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.
- package/LICENSE +21 -0
- package/README.md +137 -0
- package/dist/agent/api-client.d.ts +27 -0
- package/dist/agent/api-client.js +295 -0
- package/dist/agent/exec-service.d.ts +6 -0
- package/dist/agent/exec-service.js +126 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.js +24 -0
- package/dist/agent/llm-service.d.ts +9 -0
- package/dist/agent/llm-service.js +156 -0
- package/dist/agent/runner.d.ts +16 -0
- package/dist/agent/runner.js +187 -0
- package/dist/agent/session-handler.d.ts +28 -0
- package/dist/agent/session-handler.js +184 -0
- package/dist/agent/tmux.d.ts +29 -0
- package/dist/agent/tmux.js +157 -0
- package/dist/agent/types.d.ts +72 -0
- package/dist/agent/types.js +2 -0
- package/dist/agent/websocket.d.ts +45 -0
- package/dist/agent/websocket.js +288 -0
- package/dist/api.d.ts +31 -0
- package/dist/api.js +78 -0
- package/dist/commands/agent.d.ts +5 -0
- package/dist/commands/agent.js +19 -0
- package/dist/commands/agents.d.ts +1 -0
- package/dist/commands/agents.js +77 -0
- package/dist/commands/login.d.ts +5 -0
- package/dist/commands/login.js +41 -0
- package/dist/commands/project.d.ts +33 -0
- package/dist/commands/project.js +359 -0
- package/dist/commands/sendkeys.d.ts +3 -0
- package/dist/commands/sendkeys.js +66 -0
- package/dist/commands/sessions.d.ts +1 -0
- package/dist/commands/sessions.js +89 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +37 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +125 -0
- package/dist/project/executor.d.ts +118 -0
- package/dist/project/executor.js +893 -0
- package/dist/project/index.d.ts +4 -0
- package/dist/project/index.js +20 -0
- package/dist/project/manager.d.ts +79 -0
- package/dist/project/manager.js +397 -0
- package/dist/project/relay-client.d.ts +87 -0
- package/dist/project/relay-client.js +200 -0
- package/dist/project/types.d.ts +43 -0
- package/dist/project/types.js +3 -0
- package/package.json +59 -0
|
@@ -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 {};
|