sessioncast-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/dist/agent/api-client.d.ts +27 -0
  4. package/dist/agent/api-client.js +295 -0
  5. package/dist/agent/exec-service.d.ts +6 -0
  6. package/dist/agent/exec-service.js +126 -0
  7. package/dist/agent/index.d.ts +8 -0
  8. package/dist/agent/index.js +24 -0
  9. package/dist/agent/llm-service.d.ts +9 -0
  10. package/dist/agent/llm-service.js +156 -0
  11. package/dist/agent/runner.d.ts +16 -0
  12. package/dist/agent/runner.js +187 -0
  13. package/dist/agent/session-handler.d.ts +28 -0
  14. package/dist/agent/session-handler.js +184 -0
  15. package/dist/agent/tmux.d.ts +29 -0
  16. package/dist/agent/tmux.js +157 -0
  17. package/dist/agent/types.d.ts +72 -0
  18. package/dist/agent/types.js +2 -0
  19. package/dist/agent/websocket.d.ts +45 -0
  20. package/dist/agent/websocket.js +288 -0
  21. package/dist/api.d.ts +31 -0
  22. package/dist/api.js +78 -0
  23. package/dist/commands/agent.d.ts +5 -0
  24. package/dist/commands/agent.js +19 -0
  25. package/dist/commands/agents.d.ts +1 -0
  26. package/dist/commands/agents.js +77 -0
  27. package/dist/commands/login.d.ts +5 -0
  28. package/dist/commands/login.js +41 -0
  29. package/dist/commands/project.d.ts +33 -0
  30. package/dist/commands/project.js +359 -0
  31. package/dist/commands/sendkeys.d.ts +3 -0
  32. package/dist/commands/sendkeys.js +66 -0
  33. package/dist/commands/sessions.d.ts +1 -0
  34. package/dist/commands/sessions.js +89 -0
  35. package/dist/config.d.ts +13 -0
  36. package/dist/config.js +37 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +125 -0
  39. package/dist/project/executor.d.ts +118 -0
  40. package/dist/project/executor.js +893 -0
  41. package/dist/project/index.d.ts +4 -0
  42. package/dist/project/index.js +20 -0
  43. package/dist/project/manager.d.ts +79 -0
  44. package/dist/project/manager.js +397 -0
  45. package/dist/project/relay-client.d.ts +87 -0
  46. package/dist/project/relay-client.js +200 -0
  47. package/dist/project/types.d.ts +43 -0
  48. package/dist/project/types.js +3 -0
  49. package/package.json +59 -0
@@ -0,0 +1,4 @@
1
+ export * from './types';
2
+ export * from './manager';
3
+ export * from './executor';
4
+ export * from './relay-client';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./manager"), exports);
19
+ __exportStar(require("./executor"), exports);
20
+ __exportStar(require("./relay-client"), exports);
@@ -0,0 +1,79 @@
1
+ import { Project, Workflow, WorkflowStatus, ProjectConfig, AgentStatus } from './types';
2
+ export declare class ProjectManager {
3
+ private projectPath;
4
+ private config;
5
+ constructor(projectPath: string, config?: Partial<ProjectConfig>);
6
+ /**
7
+ * Initialize a new project with folder structure
8
+ */
9
+ init(projectName?: string): Project;
10
+ private copyTemplateClaude;
11
+ private createInitialFiles;
12
+ private saveProjectConfig;
13
+ /**
14
+ * Load existing project
15
+ */
16
+ load(): Project | null;
17
+ /**
18
+ * Load workflow definition
19
+ */
20
+ loadWorkflow(): Workflow | null;
21
+ /**
22
+ * Save workflow definition
23
+ */
24
+ saveWorkflow(workflow: Workflow): void;
25
+ /**
26
+ * Load workflow status
27
+ */
28
+ loadStatus(): WorkflowStatus | null;
29
+ /**
30
+ * Save workflow status
31
+ */
32
+ saveStatus(status: WorkflowStatus): void;
33
+ /**
34
+ * Update agent status
35
+ */
36
+ updateAgentStatus(agentId: string, agentStatus: Partial<AgentStatus>): void;
37
+ /**
38
+ * Create work agent folder with CLAUDE.md
39
+ */
40
+ createWorkAgent(agentId: string, agentName: string, tasks: string[]): void;
41
+ /**
42
+ * Get tmux session name for an agent
43
+ * Note: tmux doesn't allow colons in session names, so we use underscores
44
+ */
45
+ getTmuxSessionName(agentId: string): string;
46
+ /**
47
+ * Check if agent is completed (DONE file exists)
48
+ */
49
+ isAgentCompleted(agentId: string): boolean;
50
+ /**
51
+ * Get agent output
52
+ */
53
+ getAgentOutput(agentId: string): string | null;
54
+ /**
55
+ * Append to shared context
56
+ */
57
+ appendToContext(content: string): void;
58
+ /**
59
+ * Get project path
60
+ */
61
+ getProjectPath(): string;
62
+ /**
63
+ * Get project ID
64
+ */
65
+ getProjectId(): string;
66
+ /**
67
+ * Get sources (folders) in the work directory
68
+ * Returns array of { folder, fileCount }
69
+ */
70
+ getSources(): Array<{
71
+ folder: string;
72
+ fileCount: number;
73
+ }>;
74
+ /**
75
+ * Get project structure for Claude analysis
76
+ * Returns a summary of the project's folder structure and key files
77
+ */
78
+ getProjectStructure(): string;
79
+ }
@@ -0,0 +1,397 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ProjectManager = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const yaml = __importStar(require("js-yaml"));
40
+ class ProjectManager {
41
+ constructor(projectPath, config = {}) {
42
+ this.projectPath = path.resolve(projectPath);
43
+ this.config = {
44
+ projectId: path.basename(this.projectPath),
45
+ projectPath: this.projectPath,
46
+ machineId: config.machineId || 'local',
47
+ relay: config.relay || '',
48
+ token: config.token || ''
49
+ };
50
+ }
51
+ /**
52
+ * Initialize a new project with folder structure
53
+ */
54
+ init(projectName) {
55
+ const name = projectName || path.basename(this.projectPath);
56
+ // Create folder structure
57
+ const folders = [
58
+ 'mission',
59
+ 'flow',
60
+ 'shared',
61
+ 'tools/mission-refiner',
62
+ 'tools/pm-agent',
63
+ 'work'
64
+ ];
65
+ for (const folder of folders) {
66
+ const folderPath = path.join(this.projectPath, folder);
67
+ if (!fs.existsSync(folderPath)) {
68
+ fs.mkdirSync(folderPath, { recursive: true });
69
+ }
70
+ }
71
+ // Copy template CLAUDE.md files
72
+ this.copyTemplateClaude('mission-refiner');
73
+ this.copyTemplateClaude('pm-agent');
74
+ // Create initial files
75
+ this.createInitialFiles(name);
76
+ const project = {
77
+ name,
78
+ path: this.projectPath,
79
+ createdAt: new Date().toISOString(),
80
+ status: 'initializing'
81
+ };
82
+ // Save project config
83
+ this.saveProjectConfig(project);
84
+ return project;
85
+ }
86
+ copyTemplateClaude(agent) {
87
+ const templateDir = path.join(__dirname, '../../sample-project/tools', agent);
88
+ const targetDir = path.join(this.projectPath, 'tools', agent);
89
+ const claudePath = path.join(templateDir, 'CLAUDE.md');
90
+ if (fs.existsSync(claudePath)) {
91
+ fs.copyFileSync(claudePath, path.join(targetDir, 'CLAUDE.md'));
92
+ }
93
+ }
94
+ createInitialFiles(projectName) {
95
+ // Create mission/README.md
96
+ const missionReadme = `# Mission
97
+
98
+ Project: ${projectName}
99
+
100
+ ## Current Mission
101
+ (미션이 정제되면 여기에 기록됩니다)
102
+
103
+ ## History
104
+ (대화 히스토리가 여기에 기록됩니다)
105
+ `;
106
+ fs.writeFileSync(path.join(this.projectPath, 'mission', 'README.md'), missionReadme);
107
+ // Create shared/context.md
108
+ const sharedContext = `# Shared Context
109
+
110
+ 이 파일은 에이전트 간 공유되는 컨텍스트를 저장합니다.
111
+
112
+ ## Project Info
113
+ - Name: ${projectName}
114
+ - Created: ${new Date().toISOString()}
115
+
116
+ ## Agent Outputs
117
+ (각 에이전트의 결과가 여기에 추가됩니다)
118
+ `;
119
+ fs.writeFileSync(path.join(this.projectPath, 'shared', 'context.md'), sharedContext);
120
+ // Create .gitignore
121
+ const gitignore = `# Node
122
+ node_modules/
123
+
124
+ # Temp files
125
+ *.log
126
+ .DS_Store
127
+
128
+ # Agent state
129
+ work/**/DONE
130
+ flow/status.yml
131
+ `;
132
+ fs.writeFileSync(path.join(this.projectPath, '.gitignore'), gitignore);
133
+ }
134
+ saveProjectConfig(project) {
135
+ const configPath = path.join(this.projectPath, '.sessioncast-project.yml');
136
+ fs.writeFileSync(configPath, yaml.dump(project));
137
+ }
138
+ /**
139
+ * Load existing project
140
+ */
141
+ load() {
142
+ const configPath = path.join(this.projectPath, '.sessioncast-project.yml');
143
+ if (!fs.existsSync(configPath)) {
144
+ return null;
145
+ }
146
+ return yaml.load(fs.readFileSync(configPath, 'utf-8'));
147
+ }
148
+ /**
149
+ * Load workflow definition
150
+ */
151
+ loadWorkflow() {
152
+ const workflowPath = path.join(this.projectPath, 'flow', 'workflow.yml');
153
+ if (!fs.existsSync(workflowPath)) {
154
+ return null;
155
+ }
156
+ return yaml.load(fs.readFileSync(workflowPath, 'utf-8'));
157
+ }
158
+ /**
159
+ * Save workflow definition
160
+ */
161
+ saveWorkflow(workflow) {
162
+ const workflowPath = path.join(this.projectPath, 'flow', 'workflow.yml');
163
+ fs.writeFileSync(workflowPath, yaml.dump(workflow));
164
+ }
165
+ /**
166
+ * Load workflow status
167
+ */
168
+ loadStatus() {
169
+ const statusPath = path.join(this.projectPath, 'flow', 'status.yml');
170
+ if (!fs.existsSync(statusPath)) {
171
+ return null;
172
+ }
173
+ return yaml.load(fs.readFileSync(statusPath, 'utf-8'));
174
+ }
175
+ /**
176
+ * Save workflow status
177
+ */
178
+ saveStatus(status) {
179
+ const statusPath = path.join(this.projectPath, 'flow', 'status.yml');
180
+ fs.writeFileSync(statusPath, yaml.dump(status));
181
+ }
182
+ /**
183
+ * Update agent status
184
+ */
185
+ updateAgentStatus(agentId, agentStatus) {
186
+ let status = this.loadStatus();
187
+ if (!status) {
188
+ const workflow = this.loadWorkflow();
189
+ status = {
190
+ workflow: workflow?.name || 'unknown',
191
+ startedAt: new Date().toISOString(),
192
+ status: 'running',
193
+ agents: {}
194
+ };
195
+ }
196
+ status.agents[agentId] = {
197
+ ...status.agents[agentId],
198
+ ...agentStatus
199
+ };
200
+ // Update overall status
201
+ const agentStatuses = Object.values(status.agents);
202
+ if (agentStatuses.every(a => a.status === 'completed')) {
203
+ status.status = 'completed';
204
+ }
205
+ else if (agentStatuses.some(a => a.status === 'failed')) {
206
+ status.status = 'failed';
207
+ }
208
+ else if (agentStatuses.some(a => a.status === 'running')) {
209
+ status.status = 'running';
210
+ }
211
+ this.saveStatus(status);
212
+ }
213
+ /**
214
+ * Create work agent folder with CLAUDE.md
215
+ */
216
+ createWorkAgent(agentId, agentName, tasks) {
217
+ const agentPath = path.join(this.projectPath, 'work', agentId);
218
+ if (!fs.existsSync(agentPath)) {
219
+ fs.mkdirSync(agentPath, { recursive: true });
220
+ }
221
+ const claudeContent = `# ${agentName}
222
+
223
+ ## Role
224
+ ${agentName} 작업을 수행합니다.
225
+
226
+ ## Tasks
227
+ ${tasks.map(t => `- ${t}`).join('\n')}
228
+
229
+ ## Context
230
+ - 프로젝트 경로: ${this.projectPath}
231
+ - 공유 컨텍스트: ../shared/context.md
232
+
233
+ ## Completion
234
+ 작업 완료 시:
235
+ 1. 결과를 output.md에 기록
236
+ 2. DONE 파일 생성: \`touch DONE\`
237
+
238
+ ## Important
239
+ - 모든 작업은 이 폴더 내에서 수행
240
+ - 다른 에이전트의 결과는 ../shared/context.md 참조
241
+ - 에러 발생 시 error.log에 기록
242
+ `;
243
+ fs.writeFileSync(path.join(agentPath, 'CLAUDE.md'), claudeContent);
244
+ }
245
+ /**
246
+ * Get tmux session name for an agent
247
+ * Note: tmux doesn't allow colons in session names, so we use underscores
248
+ */
249
+ getTmuxSessionName(agentId) {
250
+ const sanitizedProjectId = this.config.projectId.replace(/[^a-zA-Z0-9_-]/g, '_');
251
+ return `proj_${sanitizedProjectId}_${agentId}`;
252
+ }
253
+ /**
254
+ * Check if agent is completed (DONE file exists)
255
+ */
256
+ isAgentCompleted(agentId) {
257
+ const donePath = path.join(this.projectPath, 'work', agentId, 'DONE');
258
+ return fs.existsSync(donePath);
259
+ }
260
+ /**
261
+ * Get agent output
262
+ */
263
+ getAgentOutput(agentId) {
264
+ const outputPath = path.join(this.projectPath, 'work', agentId, 'output.md');
265
+ if (fs.existsSync(outputPath)) {
266
+ return fs.readFileSync(outputPath, 'utf-8');
267
+ }
268
+ return null;
269
+ }
270
+ /**
271
+ * Append to shared context
272
+ */
273
+ appendToContext(content) {
274
+ const contextPath = path.join(this.projectPath, 'shared', 'context.md');
275
+ fs.appendFileSync(contextPath, '\n' + content);
276
+ }
277
+ /**
278
+ * Get project path
279
+ */
280
+ getProjectPath() {
281
+ return this.projectPath;
282
+ }
283
+ /**
284
+ * Get project ID
285
+ */
286
+ getProjectId() {
287
+ return this.config.projectId;
288
+ }
289
+ /**
290
+ * Get sources (folders) in the work directory
291
+ * Returns array of { folder, fileCount }
292
+ */
293
+ getSources() {
294
+ const sources = [];
295
+ const workPath = path.join(this.projectPath, 'work');
296
+ if (!fs.existsSync(workPath)) {
297
+ return sources;
298
+ }
299
+ const items = fs.readdirSync(workPath);
300
+ for (const item of items) {
301
+ const itemPath = path.join(workPath, item);
302
+ if (fs.statSync(itemPath).isDirectory()) {
303
+ // Count files in the folder
304
+ let fileCount = 0;
305
+ try {
306
+ const countFiles = (dir) => {
307
+ let count = 0;
308
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
309
+ for (const entry of entries) {
310
+ if (entry.name.startsWith('.'))
311
+ continue; // Skip hidden files
312
+ if (entry.isDirectory()) {
313
+ count += countFiles(path.join(dir, entry.name));
314
+ }
315
+ else {
316
+ count++;
317
+ }
318
+ }
319
+ return count;
320
+ };
321
+ fileCount = countFiles(itemPath);
322
+ }
323
+ catch {
324
+ fileCount = 0;
325
+ }
326
+ sources.push({ folder: item, fileCount });
327
+ }
328
+ }
329
+ return sources;
330
+ }
331
+ /**
332
+ * Get project structure for Claude analysis
333
+ * Returns a summary of the project's folder structure and key files
334
+ */
335
+ getProjectStructure() {
336
+ const lines = [];
337
+ lines.push(`# Project Structure: ${this.config.projectId}`);
338
+ lines.push(`Path: ${this.projectPath}`);
339
+ lines.push('');
340
+ // List top-level folders
341
+ const topLevelItems = fs.readdirSync(this.projectPath);
342
+ lines.push('## Folders:');
343
+ for (const item of topLevelItems) {
344
+ const itemPath = path.join(this.projectPath, item);
345
+ if (fs.statSync(itemPath).isDirectory() && !item.startsWith('.')) {
346
+ lines.push(`- ${item}/`);
347
+ }
348
+ }
349
+ lines.push('');
350
+ // List work folder contents (existing agents)
351
+ const workPath = path.join(this.projectPath, 'work');
352
+ if (fs.existsSync(workPath)) {
353
+ lines.push('## Work Agents:');
354
+ const workItems = fs.readdirSync(workPath);
355
+ if (workItems.length === 0) {
356
+ lines.push('- (none yet)');
357
+ }
358
+ else {
359
+ for (const agent of workItems) {
360
+ const agentPath = path.join(workPath, agent);
361
+ if (fs.statSync(agentPath).isDirectory()) {
362
+ lines.push(`- work/${agent}/`);
363
+ // Check for key files
364
+ const files = fs.readdirSync(agentPath);
365
+ for (const file of files) {
366
+ lines.push(` - ${file}`);
367
+ }
368
+ }
369
+ }
370
+ }
371
+ lines.push('');
372
+ }
373
+ // Include shared context summary if exists
374
+ const contextPath = path.join(this.projectPath, 'shared', 'context.md');
375
+ if (fs.existsSync(contextPath)) {
376
+ const contextContent = fs.readFileSync(contextPath, 'utf-8');
377
+ const preview = contextContent.substring(0, 500);
378
+ lines.push('## Shared Context (preview):');
379
+ lines.push('```');
380
+ lines.push(preview + (contextContent.length > 500 ? '...' : ''));
381
+ lines.push('```');
382
+ lines.push('');
383
+ }
384
+ // Include mission info if exists
385
+ const missionPath = path.join(this.projectPath, 'mission', 'README.md');
386
+ if (fs.existsSync(missionPath)) {
387
+ const missionContent = fs.readFileSync(missionPath, 'utf-8');
388
+ const preview = missionContent.substring(0, 300);
389
+ lines.push('## Mission Info (preview):');
390
+ lines.push('```');
391
+ lines.push(preview + (missionContent.length > 300 ? '...' : ''));
392
+ lines.push('```');
393
+ }
394
+ return lines.join('\n');
395
+ }
396
+ }
397
+ exports.ProjectManager = ProjectManager;
@@ -0,0 +1,87 @@
1
+ import { EventEmitter } from 'events';
2
+ interface SourceInfo {
3
+ folder: string;
4
+ fileCount: number;
5
+ }
6
+ interface ProjectRelayClientOptions {
7
+ url: string;
8
+ token: string;
9
+ machineId: string;
10
+ projectId: string;
11
+ projectName: string;
12
+ mission?: string;
13
+ sources?: SourceInfo[];
14
+ }
15
+ export interface MissionStep {
16
+ id: string;
17
+ title: string;
18
+ description: string;
19
+ agent: string;
20
+ estimatedTime?: string;
21
+ dependsOn: string[];
22
+ }
23
+ export interface Decision {
24
+ id: string;
25
+ question: string;
26
+ options: string[];
27
+ selected?: string;
28
+ required?: boolean;
29
+ }
30
+ export interface AnalysisResult {
31
+ steps: MissionStep[];
32
+ decisions: Decision[];
33
+ }
34
+ export interface Message {
35
+ type: string;
36
+ meta?: Record<string, string>;
37
+ payload?: string;
38
+ requestId?: string;
39
+ error?: string;
40
+ steps?: MissionStep[];
41
+ decisions?: Decision[];
42
+ }
43
+ export declare class ProjectRelayClient extends EventEmitter {
44
+ private ws;
45
+ private options;
46
+ private isConnected;
47
+ private destroyed;
48
+ private reconnectTimer;
49
+ private pingTimer;
50
+ constructor(options: ProjectRelayClientOptions);
51
+ private startPingInterval;
52
+ private stopPingInterval;
53
+ connect(): Promise<void>;
54
+ private registerProject;
55
+ /**
56
+ * Update sources list on server
57
+ */
58
+ updateSources(sources: SourceInfo[]): void;
59
+ private handleMessage;
60
+ private scheduleReconnect;
61
+ private send;
62
+ updateStatus(status: string, agents?: Record<string, {
63
+ status: string;
64
+ currentTask?: string;
65
+ }>): void;
66
+ /**
67
+ * Send mission analysis response back to server
68
+ */
69
+ sendAnalysisResponse(requestId: string, steps: MissionStep[], decisions?: Decision[]): void;
70
+ /**
71
+ * Send mission analysis error back to server
72
+ */
73
+ sendAnalysisError(requestId: string, error: string): void;
74
+ /**
75
+ * Send addSource result back to server
76
+ */
77
+ sendAddSourceResult(requestId: string, result: {
78
+ folder: string;
79
+ files: string[];
80
+ }): void;
81
+ /**
82
+ * Send addSource error back to server
83
+ */
84
+ sendAddSourceError(requestId: string, error: string): void;
85
+ destroy(): void;
86
+ }
87
+ export {};