Skip to content
Merged
31 changes: 31 additions & 0 deletions src/debug/jtag/api/data-seed/RoomDataSeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,37 @@ export class RoomDataSeed {
canvas.tags = ['canvas', 'art', 'drawing', 'vision'];
rooms.push(canvas);

// Outreach room - social media strategy and community building
const outreach = new RoomEntity();
outreach.uniqueId = ROOM_UNIQUE_IDS.OUTREACH;
outreach.name = 'outreach';
outreach.displayName = 'Outreach';
outreach.description = 'Social media strategy, community building, and external engagement';
outreach.topic = 'Discuss what to post, share interesting finds, coordinate outreach';
outreach.type = 'public';
outreach.status = 'active';
outreach.ownerId = humanUserId;
outreach.lastMessageAt = now;
outreach.recipeId = 'general-chat';
outreach.privacy = {
isPublic: true,
requiresInvite: false,
allowGuestAccess: false,
searchable: true
};
outreach.settings = {
allowThreads: true,
allowReactions: true,
allowFileSharing: true,
messageRetentionDays: 365,
slowMode: 0
};
outreach.members = [
{ userId: humanUserId, role: 'owner', joinedAt: now }
];
outreach.tags = ['social', 'outreach', 'community', 'moltbook'];
rooms.push(outreach);

return {
rooms: rooms as readonly RoomEntity[],
totalCount: rooms.length,
Expand Down
32 changes: 31 additions & 1 deletion src/debug/jtag/browser/generated.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Browser Structure Registry - Auto-generated
*
* Contains 11 daemons and 152 commands and 2 adapters and 27 widgets.
* Contains 11 daemons and 157 commands and 2 adapters and 27 widgets.
* Generated by scripts/generate-structure.ts - DO NOT EDIT MANUALLY
*/

Expand Down Expand Up @@ -141,6 +141,11 @@ import { SessionCreateBrowserCommand } from './../commands/session/create/browse
import { SessionDestroyBrowserCommand } from './../commands/session/destroy/browser/SessionDestroyBrowserCommand';
import { SessionGetIdBrowserCommand } from './../commands/session/get-id/browser/SessionGetIdBrowserCommand';
import { SessionGetUserBrowserCommand } from './../commands/session/get-user/browser/SessionGetUserBrowserCommand';
import { SocialCommentBrowserCommand } from './../commands/social/comment/browser/SocialCommentBrowserCommand';
import { SocialFeedBrowserCommand } from './../commands/social/feed/browser/SocialFeedBrowserCommand';
import { SocialNotificationsBrowserCommand } from './../commands/social/notifications/browser/SocialNotificationsBrowserCommand';
import { SocialPostBrowserCommand } from './../commands/social/post/browser/SocialPostBrowserCommand';
import { SocialSignupBrowserCommand } from './../commands/social/signup/browser/SocialSignupBrowserCommand';
import { StateContentCloseBrowserCommand } from './../commands/state/content/close/browser/StateContentCloseBrowserCommand';
import { StateContentSwitchBrowserCommand } from './../commands/state/content/switch/browser/StateContentSwitchBrowserCommand';
import { StateCreateBrowserCommand } from './../commands/state/create/browser/StateCreateBrowserCommand';
Expand Down Expand Up @@ -883,6 +888,31 @@ export const BROWSER_COMMANDS: CommandEntry[] = [
className: 'SessionGetUserBrowserCommand',
commandClass: SessionGetUserBrowserCommand
},
{
name: 'social/comment',
className: 'SocialCommentBrowserCommand',
commandClass: SocialCommentBrowserCommand
},
{
name: 'social/feed',
className: 'SocialFeedBrowserCommand',
commandClass: SocialFeedBrowserCommand
},
{
name: 'social/notifications',
className: 'SocialNotificationsBrowserCommand',
commandClass: SocialNotificationsBrowserCommand
},
{
name: 'social/post',
className: 'SocialPostBrowserCommand',
commandClass: SocialPostBrowserCommand
},
{
name: 'social/signup',
className: 'SocialSignupBrowserCommand',
commandClass: SocialSignupBrowserCommand
},
{
name: 'state/content/close',
className: 'StateContentCloseBrowserCommand',
Expand Down
20 changes: 20 additions & 0 deletions src/debug/jtag/commands/social/comment/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Development files
.eslintrc*
tsconfig*.json
vitest.config.ts

# Build artifacts
*.js.map
*.d.ts.map

# IDE
.vscode/
.idea/

# Logs
*.log
npm-debug.log*

# OS files
.DS_Store
Thumbs.db
164 changes: 164 additions & 0 deletions src/debug/jtag/commands/social/comment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Social Comment Command

Comment on a post or reply to a comment on a social media platform. Supports threaded replies.

## Table of Contents

- [Usage](#usage)
- [CLI Usage](#cli-usage)
- [Tool Usage](#tool-usage)
- [Parameters](#parameters)
- [Result](#result)
- [Examples](#examples)
- [Testing](#testing)
- [Unit Tests](#unit-tests)
- [Integration Tests](#integration-tests)
- [Getting Help](#getting-help)
- [Access Level](#access-level)
- [Implementation Notes](#implementation-notes)

## Usage

### CLI Usage

From the command line using the jtag CLI:

```bash
./jtag social/comment --platform=<value> --postId=<value> --content=<value>
```

### Tool Usage

From Persona tools or programmatic access using `Commands.execute()`:

```typescript
import { Commands } from '@system/core/shared/Commands';

const result = await Commands.execute('social/comment', {
// your parameters here
});
```

## Parameters

- **platform** (required): `string` - Platform (e.g., 'moltbook')
- **postId** (required): `string` - Post ID to comment on
- **content** (required): `string` - Comment text
- **parentId** (optional): `string` - Parent comment ID for threaded replies
- **personaId** (optional): `UUID` - Persona user ID (auto-detected if not provided)

## Result

Returns `SocialCommentResult` with:

Returns CommandResult with:
- **message**: `string` - Human-readable result message
- **comment**: `SocialCommentData` - Created comment details

## Examples

### Comment on a post

```bash
./jtag social/comment --platform=moltbook --postId=abc123 --content="Great insight!"
```

**Expected result:**
{ success: true, comment: { id: '...' } }

### Reply to a comment (threaded)

```bash
./jtag social/comment --platform=moltbook --postId=abc123 --content="Agreed" --parentId=def456
```

## Getting Help

### Using the Help Tool

Get detailed usage information for this command:

**CLI:**
```bash
./jtag help social/comment
```

**Tool:**
```typescript
// Use your help tool with command name 'social/comment'
```

### Using the README Tool

Access this README programmatically:

**CLI:**
```bash
./jtag readme social/comment
```

**Tool:**
```typescript
// Use your readme tool with command name 'social/comment'
```

## Testing

### Unit Tests

Test command logic in isolation using mock dependencies:

```bash
# Run unit tests (no server required)
npx tsx commands/Social Comment/test/unit/SocialCommentCommand.test.ts
```

**What's tested:**
- Command structure and parameter validation
- Mock command execution patterns
- Required parameter validation (throws ValidationError)
- Optional parameter handling (sensible defaults)
- Performance requirements
- Assertion utility helpers

**TDD Workflow:**
1. Write/modify unit test first (test-driven development)
2. Run test, see it fail
3. Implement feature
4. Run test, see it pass
5. Refactor if needed

### Integration Tests

Test command with real client connections and system integration:

```bash
# Prerequisites: Server must be running
npm start # Wait 90+ seconds for deployment

# Run integration tests
npx tsx commands/Social Comment/test/integration/SocialCommentIntegration.test.ts
```

**What's tested:**
- Client connection to live system
- Real command execution via WebSocket
- ValidationError handling for missing params
- Optional parameter defaults
- Performance under load
- Various parameter combinations

**Best Practice:**
Run unit tests frequently during development (fast feedback). Run integration tests before committing (verify system integration).

## Access Level

**ai-safe** - Safe for AI personas to call autonomously

## Implementation Notes

- **Shared Logic**: Core business logic in `shared/SocialCommentTypes.ts`
- **Browser**: Browser-specific implementation in `browser/SocialCommentBrowserCommand.ts`
- **Server**: Server-specific implementation in `server/SocialCommentServerCommand.ts`
- **Unit Tests**: Isolated testing in `test/unit/SocialCommentCommand.test.ts`
- **Integration Tests**: System testing in `test/integration/SocialCommentIntegration.test.ts`
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Social Comment Command - Browser Implementation
* Delegates to server
*/

import type { JTAGContext } from '@system/core/types/JTAGTypes';
import type { ICommandDaemon } from '@daemons/command-daemon/shared/CommandBase';
import { SocialCommentBaseCommand } from '../shared/SocialCommentCommand';
import type { SocialCommentParams, SocialCommentResult } from '../shared/SocialCommentTypes';

export class SocialCommentBrowserCommand extends SocialCommentBaseCommand {

constructor(context: JTAGContext, subpath: string, commander: ICommandDaemon) {
super(context, subpath, commander);
}

protected async executeSocialComment(params: SocialCommentParams): Promise<SocialCommentResult> {
return await this.remoteExecute(params);
}
}
35 changes: 35 additions & 0 deletions src/debug/jtag/commands/social/comment/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@jtag-commands/social/comment",
"version": "1.0.0",
"description": "Comment on a post or reply to a comment on a social media platform. Supports threaded replies.",
"main": "server/SocialCommentServerCommand.ts",
"types": "shared/SocialCommentTypes.ts",
"scripts": {
"test": "npm run test:unit && npm run test:integration",
"test:unit": "npx vitest run test/unit/*.test.ts",
"test:integration": "npx tsx test/integration/SocialCommentIntegration.test.ts",
"lint": "npx eslint **/*.ts",
"typecheck": "npx tsc --noEmit"
},
"peerDependencies": {
"@jtag/core": "*"
},
"files": [
"shared/**/*.ts",
"browser/**/*.ts",
"server/**/*.ts",
"test/**/*.ts",
"README.md"
],
"keywords": [
"jtag",
"command",
"social/comment"
],
"license": "MIT",
"author": "",
"repository": {
"type": "git",
"url": ""
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Social Comment Command - Server Implementation
*
* Creates a comment on a post or replies to an existing comment (threaded).
*/

import type { JTAGContext } from '@system/core/types/JTAGTypes';
import { transformPayload } from '@system/core/types/JTAGTypes';
import type { ICommandDaemon } from '@daemons/command-daemon/shared/CommandBase';
import { SocialCommentBaseCommand } from '../shared/SocialCommentCommand';
import type { SocialCommentParams, SocialCommentResult } from '../shared/SocialCommentTypes';
import { loadSocialContext } from '@system/social/server/SocialCommandHelper';

export class SocialCommentServerCommand extends SocialCommentBaseCommand {

constructor(context: JTAGContext, subpath: string, commander: ICommandDaemon) {
super(context, subpath, commander);
}

protected async executeSocialComment(params: SocialCommentParams): Promise<SocialCommentResult> {
const { platform, postId } = params;
const action = params.action ?? 'create';

if (!platform) throw new Error('platform is required');
if (!postId) throw new Error('postId is required');

const ctx = await loadSocialContext(platform, params.personaId, params);

if (action === 'list') {
const comments = await ctx.provider.getComments(postId, params.sort);
return transformPayload(params, {
success: true,
message: `Fetched ${comments.length} comments from ${postId} on ${platform}`,
comments,
});
}

// action === 'create'
if (!params.content) throw new Error('content is required for creating a comment');

const rateCheck = ctx.provider.checkRateLimit('comment');
if (!rateCheck.allowed) {
return transformPayload(params, {
success: false,
message: rateCheck.message ?? 'Rate limited for comments',
});
}

const comment = await ctx.provider.createComment({
postId,
content: params.content,
parentId: params.parentId,
});

const verb = params.parentId ? 'Replied to comment' : 'Commented on post';
return transformPayload(params, {
success: true,
message: `${verb} ${postId} on ${platform}`,
comment,
});
}
}
Loading
Loading