sessioncast-cli 2.0.2 → 2.0.4

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 (45) hide show
  1. package/dist/agent/runner.js +24 -2
  2. package/dist/agent/session-handler.d.ts +3 -2
  3. package/dist/agent/session-handler.js +60 -53
  4. package/dist/agent/tmux-executor.d.ts +19 -33
  5. package/dist/agent/tmux-executor.js +51 -38
  6. package/dist/agent/tmux.d.ts +12 -6
  7. package/dist/agent/tmux.js +16 -9
  8. package/dist/agent/types.d.ts +0 -10
  9. package/dist/agent/websocket.d.ts +6 -13
  10. package/dist/agent/websocket.js +36 -37
  11. package/dist/commands/agent.js +3 -0
  12. package/dist/index.js +14 -0
  13. package/dist/sentry.d.ts +4 -0
  14. package/dist/sentry.js +87 -0
  15. package/package.json +2 -1
  16. package/dist/autopilot/index.d.ts +0 -94
  17. package/dist/autopilot/index.js +0 -322
  18. package/dist/autopilot/mission-analyzer.d.ts +0 -27
  19. package/dist/autopilot/mission-analyzer.js +0 -232
  20. package/dist/autopilot/project-detector.d.ts +0 -12
  21. package/dist/autopilot/project-detector.js +0 -326
  22. package/dist/autopilot/source-scanner.d.ts +0 -26
  23. package/dist/autopilot/source-scanner.js +0 -285
  24. package/dist/autopilot/speckit-generator.d.ts +0 -60
  25. package/dist/autopilot/speckit-generator.js +0 -511
  26. package/dist/autopilot/types.d.ts +0 -110
  27. package/dist/autopilot/types.js +0 -6
  28. package/dist/autopilot/workflow-generator.d.ts +0 -33
  29. package/dist/autopilot/workflow-generator.js +0 -278
  30. package/dist/commands/autopilot.d.ts +0 -30
  31. package/dist/commands/autopilot.js +0 -262
  32. package/dist/commands/project.d.ts +0 -33
  33. package/dist/commands/project.js +0 -350
  34. package/dist/project/executor.d.ts +0 -73
  35. package/dist/project/executor.js +0 -437
  36. package/dist/project/index.d.ts +0 -4
  37. package/dist/project/index.js +0 -20
  38. package/dist/project/manager.d.ts +0 -66
  39. package/dist/project/manager.js +0 -290
  40. package/dist/project/relay-client.d.ts +0 -37
  41. package/dist/project/relay-client.js +0 -204
  42. package/dist/project/types.d.ts +0 -48
  43. package/dist/project/types.js +0 -3
  44. package/dist/utils/fileUtils.d.ts +0 -28
  45. package/dist/utils/fileUtils.js +0 -159
@@ -1,290 +0,0 @@
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
- exports.ProjectManager = ProjectManager;
@@ -1,37 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { AgentInfo } from './types';
3
- interface ProjectRelayClientOptions {
4
- relayUrl: string;
5
- token: string;
6
- projectId: string;
7
- projectName: string;
8
- projectPath: string;
9
- autoReconnect?: boolean;
10
- }
11
- interface SourceInfo {
12
- folder: string;
13
- fileCount: number;
14
- }
15
- export declare class ProjectRelayClient extends EventEmitter {
16
- private ws;
17
- private options;
18
- private isConnected;
19
- private reconnectAttempts;
20
- private maxReconnectAttempts;
21
- private reconnectTimer;
22
- private destroyed;
23
- constructor(options: ProjectRelayClientOptions);
24
- connect(): void;
25
- private scheduleReconnect;
26
- private registerProject;
27
- private handleMessage;
28
- private send;
29
- updateStatus(status: string, meta?: Record<string, any>): boolean;
30
- updateSources(sources: SourceInfo[]): boolean;
31
- updateAgents(agents: AgentInfo[]): boolean;
32
- sendSourceAdded(folder: string, fileCount: number, files?: string[]): boolean;
33
- sendAnalysisResult(viewerSessionId: string, steps: any[], decisions: any[]): boolean;
34
- getConnected(): boolean;
35
- destroy(): void;
36
- }
37
- export {};
@@ -1,204 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ProjectRelayClient = void 0;
7
- const ws_1 = __importDefault(require("ws"));
8
- const events_1 = require("events");
9
- class ProjectRelayClient extends events_1.EventEmitter {
10
- constructor(options) {
11
- super();
12
- this.ws = null;
13
- this.isConnected = false;
14
- this.reconnectAttempts = 0;
15
- this.maxReconnectAttempts = 5;
16
- this.reconnectTimer = null;
17
- this.destroyed = false;
18
- this.options = options;
19
- }
20
- connect() {
21
- if (this.destroyed)
22
- return;
23
- try {
24
- // Add token to URL for authentication
25
- const url = new URL(this.options.relayUrl);
26
- url.searchParams.set('token', this.options.token);
27
- this.ws = new ws_1.default(url.toString());
28
- this.ws.on('open', () => {
29
- this.isConnected = true;
30
- this.reconnectAttempts = 0;
31
- console.log('[ProjectRelay] Connected to relay');
32
- this.registerProject();
33
- this.emit('connected');
34
- });
35
- this.ws.on('message', (data) => {
36
- try {
37
- const message = JSON.parse(data.toString());
38
- this.handleMessage(message);
39
- }
40
- catch (e) {
41
- console.error('[ProjectRelay] Failed to parse message:', e);
42
- }
43
- });
44
- this.ws.on('close', (code, reason) => {
45
- this.isConnected = false;
46
- console.log(`[ProjectRelay] Disconnected: code=${code}, reason=${reason.toString()}`);
47
- this.emit('disconnected', { code, reason: reason.toString() });
48
- if (this.options.autoReconnect !== false && !this.destroyed) {
49
- this.scheduleReconnect();
50
- }
51
- });
52
- this.ws.on('error', (error) => {
53
- console.error('[ProjectRelay] WebSocket error:', error.message);
54
- this.emit('error', error);
55
- });
56
- }
57
- catch (error) {
58
- console.error('[ProjectRelay] Connection error:', error);
59
- this.emit('error', error);
60
- if (this.options.autoReconnect !== false && !this.destroyed) {
61
- this.scheduleReconnect();
62
- }
63
- }
64
- }
65
- scheduleReconnect() {
66
- if (this.destroyed)
67
- return;
68
- this.reconnectAttempts++;
69
- if (this.reconnectAttempts > this.maxReconnectAttempts) {
70
- console.log('[ProjectRelay] Max reconnect attempts reached');
71
- this.emit('maxReconnectAttempts');
72
- return;
73
- }
74
- const delay = Math.min(2000 * Math.pow(2, this.reconnectAttempts - 1), 30000);
75
- console.log(`[ProjectRelay] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
76
- this.reconnectTimer = setTimeout(() => {
77
- if (!this.isConnected && !this.destroyed) {
78
- this.connect();
79
- }
80
- }, delay);
81
- }
82
- registerProject() {
83
- this.send({
84
- type: 'registerProject',
85
- meta: {
86
- projectId: this.options.projectId,
87
- name: this.options.projectName,
88
- path: this.options.projectPath,
89
- token: this.options.token,
90
- status: 'pending',
91
- createdAt: new Date().toISOString()
92
- }
93
- });
94
- }
95
- handleMessage(message) {
96
- switch (message.type) {
97
- case 'addSource':
98
- // Web UI requesting to add a source
99
- this.emit('addSource', message.meta);
100
- break;
101
- case 'analyzeMission':
102
- // Web UI requesting mission analysis
103
- this.emit('analyzeMission', message.meta);
104
- break;
105
- case 'startWorkflow':
106
- // Web UI requesting to start workflow
107
- this.emit('startWorkflow', message.meta);
108
- break;
109
- case 'projectList':
110
- // Server sent project list (acknowledgment)
111
- console.log('[ProjectRelay] Project registered successfully');
112
- break;
113
- case 'error':
114
- console.error('[ProjectRelay] Server error:', message.meta);
115
- this.emit('serverError', message.meta);
116
- break;
117
- default:
118
- console.log('[ProjectRelay] Unknown message type:', message.type);
119
- this.emit('message', message);
120
- }
121
- }
122
- send(message) {
123
- if (!this.ws || this.ws.readyState !== ws_1.default.OPEN) {
124
- return false;
125
- }
126
- try {
127
- this.ws.send(JSON.stringify(message));
128
- return true;
129
- }
130
- catch {
131
- return false;
132
- }
133
- }
134
- // Update project status
135
- updateStatus(status, meta) {
136
- return this.send({
137
- type: 'projectStatus',
138
- meta: {
139
- projectId: this.options.projectId,
140
- status,
141
- ...meta
142
- }
143
- });
144
- }
145
- // Update sources list
146
- updateSources(sources) {
147
- return this.send({
148
- type: 'projectStatus',
149
- meta: {
150
- projectId: this.options.projectId,
151
- sources: JSON.stringify(sources)
152
- }
153
- });
154
- }
155
- // Update agents list
156
- updateAgents(agents) {
157
- return this.send({
158
- type: 'projectStatus',
159
- meta: {
160
- projectId: this.options.projectId,
161
- agents: JSON.stringify(agents)
162
- }
163
- });
164
- }
165
- // Send source added result back to web
166
- sendSourceAdded(folder, fileCount, files) {
167
- return this.send({
168
- type: 'sourceAdded',
169
- meta: {
170
- projectId: this.options.projectId,
171
- folder,
172
- fileCount: String(fileCount)
173
- },
174
- payload: files ? files.join('\n') : ''
175
- });
176
- }
177
- // Send analysis result back to web
178
- sendAnalysisResult(viewerSessionId, steps, decisions) {
179
- return this.send({
180
- type: 'missionAnalysis',
181
- meta: {
182
- projectId: this.options.projectId,
183
- viewerSessionId
184
- },
185
- steps,
186
- decisions
187
- });
188
- }
189
- getConnected() {
190
- return this.isConnected;
191
- }
192
- destroy() {
193
- this.destroyed = true;
194
- if (this.reconnectTimer) {
195
- clearTimeout(this.reconnectTimer);
196
- this.reconnectTimer = null;
197
- }
198
- if (this.ws) {
199
- this.ws.close();
200
- this.ws = null;
201
- }
202
- }
203
- }
204
- exports.ProjectRelayClient = ProjectRelayClient;
@@ -1,48 +0,0 @@
1
- export interface Project {
2
- name: string;
3
- path: string;
4
- createdAt: string;
5
- status: ProjectStatus;
6
- }
7
- export type ProjectStatus = 'initializing' | 'ready' | 'running' | 'completed' | 'failed';
8
- export interface Workflow {
9
- name: string;
10
- mission: string;
11
- created: string;
12
- agents: WorkflowAgent[];
13
- }
14
- export interface WorkflowAgent {
15
- id: string;
16
- name: string;
17
- workDir: string;
18
- tasks: string[];
19
- dependsOn: string[];
20
- }
21
- export interface WorkflowStatus {
22
- workflow: string;
23
- startedAt: string;
24
- status: 'pending' | 'running' | 'completed' | 'failed';
25
- agents: Record<string, AgentStatus>;
26
- }
27
- export interface AgentStatus {
28
- status: 'pending' | 'running' | 'completed' | 'failed';
29
- startedAt?: string;
30
- completedAt?: string;
31
- progress?: string;
32
- currentTask?: string;
33
- output?: string;
34
- error?: string;
35
- retryCount?: number;
36
- }
37
- export interface ProjectConfig {
38
- projectId: string;
39
- projectPath: string;
40
- machineId: string;
41
- relay: string;
42
- token: string;
43
- }
44
- export interface AgentInfo {
45
- id: string;
46
- name: string;
47
- status: 'pending' | 'running' | 'completed' | 'failed';
48
- }
@@ -1,3 +0,0 @@
1
- "use strict";
2
- // Project Mode Types
3
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,28 +0,0 @@
1
- /**
2
- * Sanitize filename to prevent path traversal attacks
3
- */
4
- export declare function sanitizeFilename(filename: string): string;
5
- /**
6
- * Get the working directory for file uploads
7
- * Priority: SESSIONCAST_UPLOAD_DIR env var > current working directory
8
- */
9
- export declare function getUploadDirectory(): string;
10
- /**
11
- * Handle file upload chunk
12
- * Returns upload result when all chunks are received, null otherwise
13
- */
14
- export declare function handleUploadChunk(sessionId: string, meta: {
15
- filename: string;
16
- size: string;
17
- mimeType: string;
18
- chunkIndex: string;
19
- totalChunks: string;
20
- }, payload: string): Promise<{
21
- success: boolean;
22
- path?: string;
23
- error?: string;
24
- } | null>;
25
- /**
26
- * Cleanup stale uploads periodically
27
- */
28
- export declare function cleanupStaleUploads(): void;