spets 0.1.7 → 0.1.9
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.
|
@@ -10,6 +10,9 @@ import { parse as parseYaml } from "yaml";
|
|
|
10
10
|
var SPETS_DIR = ".spets";
|
|
11
11
|
var CONFIG_FILE = "config.yml";
|
|
12
12
|
var STEPS_DIR = "steps";
|
|
13
|
+
var configCache = null;
|
|
14
|
+
var configCachePath = null;
|
|
15
|
+
var stepDefinitionCache = /* @__PURE__ */ new Map();
|
|
13
16
|
function getSpetsDir(cwd = process.cwd()) {
|
|
14
17
|
return join(cwd, SPETS_DIR);
|
|
15
18
|
}
|
|
@@ -27,6 +30,9 @@ function spetsExists(cwd = process.cwd()) {
|
|
|
27
30
|
}
|
|
28
31
|
function loadConfig(cwd = process.cwd()) {
|
|
29
32
|
const configPath = getConfigPath(cwd);
|
|
33
|
+
if (configCache && configCachePath === configPath) {
|
|
34
|
+
return configCache;
|
|
35
|
+
}
|
|
30
36
|
if (!existsSync(configPath)) {
|
|
31
37
|
throw new Error(`Spets config not found. Run 'spets init' first.`);
|
|
32
38
|
}
|
|
@@ -35,9 +41,15 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
35
41
|
if (!config.steps || config.steps.length === 0) {
|
|
36
42
|
throw new Error("Config must define at least one step");
|
|
37
43
|
}
|
|
44
|
+
configCache = config;
|
|
45
|
+
configCachePath = configPath;
|
|
38
46
|
return config;
|
|
39
47
|
}
|
|
40
48
|
function loadStepDefinition(stepName, cwd = process.cwd()) {
|
|
49
|
+
const cacheKey = `${cwd}:${stepName}`;
|
|
50
|
+
if (stepDefinitionCache.has(cacheKey)) {
|
|
51
|
+
return stepDefinitionCache.get(cacheKey);
|
|
52
|
+
}
|
|
41
53
|
const stepDir = join(getStepsDir(cwd), stepName);
|
|
42
54
|
const instructionPath = join(stepDir, "instruction.md");
|
|
43
55
|
const templatePath = join(stepDir, "template.md");
|
|
@@ -46,19 +58,58 @@ function loadStepDefinition(stepName, cwd = process.cwd()) {
|
|
|
46
58
|
}
|
|
47
59
|
const instruction = readFileSync(instructionPath, "utf-8");
|
|
48
60
|
const template = existsSync(templatePath) ? readFileSync(templatePath, "utf-8") : void 0;
|
|
49
|
-
|
|
61
|
+
const stepDef = {
|
|
50
62
|
name: stepName,
|
|
51
63
|
instruction,
|
|
52
64
|
template
|
|
53
65
|
};
|
|
66
|
+
stepDefinitionCache.set(cacheKey, stepDef);
|
|
67
|
+
return stepDef;
|
|
54
68
|
}
|
|
55
69
|
function getGitHubConfig(cwd = process.cwd()) {
|
|
56
70
|
const config = loadConfig(cwd);
|
|
57
71
|
return config.github;
|
|
58
72
|
}
|
|
59
73
|
|
|
74
|
+
// src/core/slug.ts
|
|
75
|
+
function generateRandomSuffix() {
|
|
76
|
+
return Math.random().toString(36).substring(2, 6);
|
|
77
|
+
}
|
|
78
|
+
function generateDatePrefix() {
|
|
79
|
+
const now = /* @__PURE__ */ new Date();
|
|
80
|
+
const year = now.getFullYear();
|
|
81
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
82
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
83
|
+
return `${year}-${month}-${day}`;
|
|
84
|
+
}
|
|
85
|
+
function sanitizeDescription(description) {
|
|
86
|
+
let sanitized = description.toLowerCase();
|
|
87
|
+
sanitized = sanitized.replace(/[^\x00-\x7F]/g, "");
|
|
88
|
+
sanitized = sanitized.replace(/[^a-z0-9\s]/g, "");
|
|
89
|
+
sanitized = sanitized.trim();
|
|
90
|
+
sanitized = sanitized.replace(/\s+/g, " ");
|
|
91
|
+
sanitized = sanitized.replace(/\s+/g, "-");
|
|
92
|
+
if (sanitized.length > 10) {
|
|
93
|
+
sanitized = sanitized.substring(0, 10);
|
|
94
|
+
}
|
|
95
|
+
sanitized = sanitized.replace(/-+$/, "");
|
|
96
|
+
if (sanitized === "") {
|
|
97
|
+
sanitized = "task";
|
|
98
|
+
}
|
|
99
|
+
return sanitized;
|
|
100
|
+
}
|
|
101
|
+
function generateSlug(description) {
|
|
102
|
+
const datePrefix = generateDatePrefix();
|
|
103
|
+
const meaningfulName = sanitizeDescription(description);
|
|
104
|
+
const randomSuffix = generateRandomSuffix();
|
|
105
|
+
return `${datePrefix}-${meaningfulName}-${randomSuffix}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
60
108
|
// src/core/state.ts
|
|
61
|
-
function generateTaskId() {
|
|
109
|
+
function generateTaskId(description) {
|
|
110
|
+
if (description) {
|
|
111
|
+
return generateSlug(description);
|
|
112
|
+
}
|
|
62
113
|
const timestamp = Date.now().toString(36);
|
|
63
114
|
const random = Math.random().toString(36).substring(2, 6);
|
|
64
115
|
return `${timestamp}-${random}`;
|
|
@@ -132,11 +183,68 @@ function listTasks(cwd = process.cwd()) {
|
|
|
132
183
|
}
|
|
133
184
|
return readdirSync(outputsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
|
|
134
185
|
}
|
|
186
|
+
function getStateCachePath(taskId, cwd = process.cwd()) {
|
|
187
|
+
return join2(getTaskDir(taskId, cwd), ".state-cache.json");
|
|
188
|
+
}
|
|
189
|
+
function loadStateCache(taskId, cwd = process.cwd()) {
|
|
190
|
+
const cachePath = getStateCachePath(taskId, cwd);
|
|
191
|
+
if (!existsSync2(cachePath)) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
const cached = JSON.parse(readFileSync2(cachePath, "utf-8"));
|
|
196
|
+
return cached;
|
|
197
|
+
} catch {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function saveStateCache(taskId, state, cwd = process.cwd()) {
|
|
202
|
+
const cachePath = getStateCachePath(taskId, cwd);
|
|
203
|
+
const stepStatuses = {};
|
|
204
|
+
for (const [stepName, output] of state.outputs.entries()) {
|
|
205
|
+
stepStatuses[stepName] = output.status;
|
|
206
|
+
}
|
|
207
|
+
const cached = {
|
|
208
|
+
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
209
|
+
taskId: state.taskId,
|
|
210
|
+
userQuery: state.userQuery,
|
|
211
|
+
currentStepIndex: state.currentStepIndex,
|
|
212
|
+
currentStepName: state.currentStepName,
|
|
213
|
+
status: state.status,
|
|
214
|
+
stepStatuses
|
|
215
|
+
};
|
|
216
|
+
try {
|
|
217
|
+
writeFileSync(cachePath, JSON.stringify(cached, null, 2));
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function isStateCacheValid(cached, maxAgeMs = 5e3) {
|
|
222
|
+
const age = Date.now() - new Date(cached.lastUpdated).getTime();
|
|
223
|
+
return age < maxAgeMs;
|
|
224
|
+
}
|
|
135
225
|
function getWorkflowState(taskId, config, cwd = process.cwd()) {
|
|
136
226
|
const taskDir = getTaskDir(taskId, cwd);
|
|
137
227
|
if (!existsSync2(taskDir)) {
|
|
138
228
|
return null;
|
|
139
229
|
}
|
|
230
|
+
const cached = loadStateCache(taskId, cwd);
|
|
231
|
+
if (cached && isStateCacheValid(cached)) {
|
|
232
|
+
const outputs2 = /* @__PURE__ */ new Map();
|
|
233
|
+
for (const [stepName, status] of Object.entries(cached.stepStatuses)) {
|
|
234
|
+
outputs2.set(stepName, {
|
|
235
|
+
path: getOutputPath(taskId, stepName, cwd),
|
|
236
|
+
status
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
taskId: cached.taskId,
|
|
241
|
+
userQuery: cached.userQuery,
|
|
242
|
+
currentStepIndex: cached.currentStepIndex,
|
|
243
|
+
currentStepName: cached.currentStepName,
|
|
244
|
+
status: cached.status,
|
|
245
|
+
outputs: outputs2
|
|
246
|
+
};
|
|
247
|
+
}
|
|
140
248
|
const outputs = /* @__PURE__ */ new Map();
|
|
141
249
|
let lastApprovedIndex = -1;
|
|
142
250
|
let currentStatus = "in_progress";
|
|
@@ -171,7 +279,7 @@ function getWorkflowState(taskId, config, cwd = process.cwd()) {
|
|
|
171
279
|
if (currentDoc?.frontmatter.status === "draft" && currentDoc.frontmatter.open_questions?.length) {
|
|
172
280
|
currentStatus = "paused";
|
|
173
281
|
}
|
|
174
|
-
|
|
282
|
+
const state = {
|
|
175
283
|
taskId,
|
|
176
284
|
userQuery,
|
|
177
285
|
currentStepIndex,
|
|
@@ -179,6 +287,8 @@ function getWorkflowState(taskId, config, cwd = process.cwd()) {
|
|
|
179
287
|
status: currentStatus,
|
|
180
288
|
outputs
|
|
181
289
|
};
|
|
290
|
+
saveStateCache(taskId, state, cwd);
|
|
291
|
+
return state;
|
|
182
292
|
}
|
|
183
293
|
function saveTaskMetadata(taskId, userQuery, cwd = process.cwd()) {
|
|
184
294
|
const taskDir = ensureTaskDir(taskId, cwd);
|
|
@@ -192,6 +302,21 @@ function loadTaskMetadata(taskId, cwd = process.cwd()) {
|
|
|
192
302
|
}
|
|
193
303
|
return JSON.parse(readFileSync2(metaPath, "utf-8"));
|
|
194
304
|
}
|
|
305
|
+
function extractPlanSummary(taskId, cwd = process.cwd()) {
|
|
306
|
+
const doc = loadDocument(taskId, "01-plan", cwd);
|
|
307
|
+
if (!doc) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
const summaryMatch = doc.content.match(/## Summary\n\n(.+?)(?=\n\n##|\n\n$|$)/s);
|
|
311
|
+
if (summaryMatch) {
|
|
312
|
+
return summaryMatch[1].trim();
|
|
313
|
+
}
|
|
314
|
+
const goalMatch = doc.content.match(/## Goal\n\n(.+?)(?=\n\n##|\n\n$|$)/s);
|
|
315
|
+
if (goalMatch) {
|
|
316
|
+
return goalMatch[1].trim();
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
195
320
|
|
|
196
321
|
export {
|
|
197
322
|
getSpetsDir,
|
|
@@ -213,5 +338,6 @@ export {
|
|
|
213
338
|
listTasks,
|
|
214
339
|
getWorkflowState,
|
|
215
340
|
saveTaskMetadata,
|
|
216
|
-
loadTaskMetadata
|
|
341
|
+
loadTaskMetadata,
|
|
342
|
+
extractPlanSummary
|
|
217
343
|
};
|
package/dist/index.js
CHANGED
|
@@ -16,13 +16,13 @@ import {
|
|
|
16
16
|
saveTaskMetadata,
|
|
17
17
|
spetsExists,
|
|
18
18
|
updateDocumentStatus
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-NOS3N4GT.js";
|
|
20
20
|
|
|
21
21
|
// src/index.ts
|
|
22
22
|
import { Command } from "commander";
|
|
23
23
|
|
|
24
24
|
// src/commands/init.ts
|
|
25
|
-
import { mkdirSync, writeFileSync } from "fs";
|
|
25
|
+
import { mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
26
26
|
import { join, dirname } from "path";
|
|
27
27
|
import { fileURLToPath } from "url";
|
|
28
28
|
import { execSync } from "child_process";
|
|
@@ -121,155 +121,20 @@ function createDefaultSteps(spetsDir) {
|
|
|
121
121
|
writeFileSync(join(implementDir, "template.md"), getImplementTemplate());
|
|
122
122
|
}
|
|
123
123
|
function getPlanInstruction() {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
You are creating a technical plan for the given task.
|
|
127
|
-
|
|
128
|
-
## Your Goal
|
|
129
|
-
|
|
130
|
-
Analyze the user's request and create a detailed implementation plan.
|
|
131
|
-
|
|
132
|
-
## Process
|
|
133
|
-
|
|
134
|
-
1. **Understand the Request**
|
|
135
|
-
- Parse the user's query to identify the core requirements
|
|
136
|
-
- Identify any ambiguities or missing information
|
|
137
|
-
|
|
138
|
-
2. **Ask Clarifying Questions** (if needed)
|
|
139
|
-
- If requirements are unclear, list questions in the \`open_questions\` section
|
|
140
|
-
- Questions should be specific and actionable
|
|
141
|
-
|
|
142
|
-
3. **Create the Plan**
|
|
143
|
-
- Break down the task into concrete steps
|
|
144
|
-
- Identify files to create/modify
|
|
145
|
-
- Consider edge cases and potential issues
|
|
146
|
-
|
|
147
|
-
## Output Format
|
|
148
|
-
|
|
149
|
-
Follow the template provided. Include:
|
|
150
|
-
- Summary of what will be built
|
|
151
|
-
- Step-by-step implementation plan
|
|
152
|
-
- Files to be created/modified
|
|
153
|
-
- Any open questions (if requirements are unclear)
|
|
154
|
-
`;
|
|
124
|
+
const fullTemplate = readFileSync(join(__dirname, "..", ".spets", "steps", "01-plan", "instruction.md"), "utf-8");
|
|
125
|
+
return fullTemplate;
|
|
155
126
|
}
|
|
156
127
|
function getPlanTemplate() {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
## Summary
|
|
160
|
-
|
|
161
|
-
Brief description of what will be implemented.
|
|
162
|
-
|
|
163
|
-
## Requirements
|
|
164
|
-
|
|
165
|
-
- Requirement 1
|
|
166
|
-
- Requirement 2
|
|
167
|
-
|
|
168
|
-
## Implementation Steps
|
|
169
|
-
|
|
170
|
-
### Step 1: [Description]
|
|
171
|
-
|
|
172
|
-
Details...
|
|
173
|
-
|
|
174
|
-
### Step 2: [Description]
|
|
175
|
-
|
|
176
|
-
Details...
|
|
177
|
-
|
|
178
|
-
## Files to Modify
|
|
179
|
-
|
|
180
|
-
| File | Action | Description |
|
|
181
|
-
|------|--------|-------------|
|
|
182
|
-
| path/to/file | Create/Modify | What changes |
|
|
183
|
-
|
|
184
|
-
## Open Questions
|
|
185
|
-
|
|
186
|
-
<!-- Remove this section if no questions -->
|
|
187
|
-
|
|
188
|
-
- Question 1?
|
|
189
|
-
- Question 2?
|
|
190
|
-
|
|
191
|
-
## Risks & Considerations
|
|
192
|
-
|
|
193
|
-
- Risk 1
|
|
194
|
-
- Risk 2
|
|
195
|
-
`;
|
|
128
|
+
const fullTemplate = readFileSync(join(__dirname, "..", ".spets", "steps", "01-plan", "template.md"), "utf-8");
|
|
129
|
+
return fullTemplate;
|
|
196
130
|
}
|
|
197
131
|
function getImplementInstruction() {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
You are implementing the plan from the previous step.
|
|
201
|
-
|
|
202
|
-
## Your Goal
|
|
203
|
-
|
|
204
|
-
Write the actual code based on the approved plan.
|
|
205
|
-
|
|
206
|
-
## Process
|
|
207
|
-
|
|
208
|
-
1. **Review the Plan**
|
|
209
|
-
- Read the approved plan document carefully
|
|
210
|
-
- Understand all requirements and steps
|
|
211
|
-
|
|
212
|
-
2. **Implement**
|
|
213
|
-
- Follow the plan step by step
|
|
214
|
-
- Write clean, well-documented code
|
|
215
|
-
- Handle edge cases identified in the plan
|
|
216
|
-
|
|
217
|
-
3. **Document Changes**
|
|
218
|
-
- List all files created/modified
|
|
219
|
-
- Explain key decisions made during implementation
|
|
220
|
-
|
|
221
|
-
## Output Format
|
|
222
|
-
|
|
223
|
-
Follow the template provided. Include:
|
|
224
|
-
- Summary of implementation
|
|
225
|
-
- List of all changes made
|
|
226
|
-
- Any deviations from the plan (with justification)
|
|
227
|
-
- Testing notes
|
|
228
|
-
`;
|
|
132
|
+
const fullTemplate = readFileSync(join(__dirname, "..", ".spets", "steps", "02-implement", "instruction.md"), "utf-8");
|
|
133
|
+
return fullTemplate;
|
|
229
134
|
}
|
|
230
135
|
function getImplementTemplate() {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
## Summary
|
|
234
|
-
|
|
235
|
-
Brief description of what was implemented.
|
|
236
|
-
|
|
237
|
-
## Changes Made
|
|
238
|
-
|
|
239
|
-
### New Files
|
|
240
|
-
|
|
241
|
-
| File | Description |
|
|
242
|
-
|------|-------------|
|
|
243
|
-
| path/to/file | What it does |
|
|
244
|
-
|
|
245
|
-
### Modified Files
|
|
246
|
-
|
|
247
|
-
| File | Changes |
|
|
248
|
-
|------|---------|
|
|
249
|
-
| path/to/file | What changed |
|
|
250
|
-
|
|
251
|
-
## Key Decisions
|
|
252
|
-
|
|
253
|
-
- Decision 1: Explanation
|
|
254
|
-
- Decision 2: Explanation
|
|
255
|
-
|
|
256
|
-
## Deviations from Plan
|
|
257
|
-
|
|
258
|
-
<!-- Remove if no deviations -->
|
|
259
|
-
|
|
260
|
-
None / List any deviations with justification.
|
|
261
|
-
|
|
262
|
-
## Testing
|
|
263
|
-
|
|
264
|
-
- [ ] Manual testing completed
|
|
265
|
-
- [ ] Unit tests added
|
|
266
|
-
- [ ] Integration tests pass
|
|
267
|
-
|
|
268
|
-
## Next Steps
|
|
269
|
-
|
|
270
|
-
- Follow-up task 1
|
|
271
|
-
- Follow-up task 2
|
|
272
|
-
`;
|
|
136
|
+
const fullTemplate = readFileSync(join(__dirname, "..", ".spets", "steps", "02-implement", "template.md"), "utf-8");
|
|
137
|
+
return fullTemplate;
|
|
273
138
|
}
|
|
274
139
|
function createClaudeCommand(cwd) {
|
|
275
140
|
const commandDir = join(cwd, ".claude", "commands");
|
|
@@ -702,6 +567,16 @@ var Executor = class {
|
|
|
702
567
|
this.config = options.config;
|
|
703
568
|
this.cwd = options.cwd || process.cwd();
|
|
704
569
|
}
|
|
570
|
+
/**
|
|
571
|
+
* Preload all step definitions into cache for better performance
|
|
572
|
+
*/
|
|
573
|
+
async preloadSteps() {
|
|
574
|
+
await Promise.all(
|
|
575
|
+
this.config.steps.map(
|
|
576
|
+
(stepName) => Promise.resolve(loadStepDefinition(stepName, this.cwd))
|
|
577
|
+
)
|
|
578
|
+
);
|
|
579
|
+
}
|
|
705
580
|
async executeWorkflow(taskId, userQuery, startIndex = 0, feedback) {
|
|
706
581
|
let previousOutput;
|
|
707
582
|
if (startIndex > 0) {
|
|
@@ -954,7 +829,6 @@ var CliPlatform = class extends BasePlatform {
|
|
|
954
829
|
async callClaude(prompt) {
|
|
955
830
|
return new Promise((resolve, reject) => {
|
|
956
831
|
const proc = spawn2(this.claudeCommand, [
|
|
957
|
-
"--print",
|
|
958
832
|
"--permission-mode",
|
|
959
833
|
"bypassPermissions"
|
|
960
834
|
], {
|
|
@@ -1024,6 +898,21 @@ var CliPlatform = class extends BasePlatform {
|
|
|
1024
898
|
|
|
1025
899
|
// src/platform/github.ts
|
|
1026
900
|
import { spawn as spawn3 } from "child_process";
|
|
901
|
+
async function retryWithBackoff(fn, maxRetries = 3, initialDelayMs = 1e3) {
|
|
902
|
+
let lastError = null;
|
|
903
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
904
|
+
try {
|
|
905
|
+
return await fn();
|
|
906
|
+
} catch (error) {
|
|
907
|
+
lastError = error;
|
|
908
|
+
if (i === maxRetries - 1) break;
|
|
909
|
+
const delay = initialDelayMs * Math.pow(2, i);
|
|
910
|
+
console.log(`Retry ${i + 1}/${maxRetries} after ${delay}ms...`);
|
|
911
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
throw lastError || new Error("Retry failed");
|
|
915
|
+
}
|
|
1027
916
|
var PauseForInputError = class extends Error {
|
|
1028
917
|
constructor(inputType, data) {
|
|
1029
918
|
super(`Paused waiting for ${inputType}`);
|
|
@@ -1211,26 +1100,39 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1211
1100
|
const { owner, repo, issueNumber } = this.config;
|
|
1212
1101
|
await this.runGh(["issue", "comment", String(issueNumber), "--body", body, "-R", `${owner}/${repo}`]);
|
|
1213
1102
|
}
|
|
1214
|
-
async runGh(args) {
|
|
1215
|
-
return
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1103
|
+
async runGh(args, timeoutMs = 3e4) {
|
|
1104
|
+
return retryWithBackoff(async () => {
|
|
1105
|
+
return new Promise((resolve, reject) => {
|
|
1106
|
+
const proc = spawn3("gh", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
1107
|
+
let stdout = "";
|
|
1108
|
+
let stderr = "";
|
|
1109
|
+
let isTimedOut = false;
|
|
1110
|
+
const timeoutId = setTimeout(() => {
|
|
1111
|
+
isTimedOut = true;
|
|
1112
|
+
proc.kill();
|
|
1113
|
+
reject(new Error(`gh command timed out after ${timeoutMs}ms`));
|
|
1114
|
+
}, timeoutMs);
|
|
1115
|
+
proc.stdout.on("data", (data) => {
|
|
1116
|
+
stdout += data.toString();
|
|
1117
|
+
});
|
|
1118
|
+
proc.stderr.on("data", (data) => {
|
|
1119
|
+
stderr += data.toString();
|
|
1120
|
+
});
|
|
1121
|
+
proc.on("close", (code) => {
|
|
1122
|
+
clearTimeout(timeoutId);
|
|
1123
|
+
if (isTimedOut) return;
|
|
1124
|
+
if (code !== 0) {
|
|
1125
|
+
reject(new Error(`gh command failed: ${stderr}`));
|
|
1126
|
+
} else {
|
|
1127
|
+
resolve(stdout);
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
proc.on("error", (err) => {
|
|
1131
|
+
clearTimeout(timeoutId);
|
|
1132
|
+
if (!isTimedOut) {
|
|
1133
|
+
reject(new Error(`Failed to run gh: ${err.message}`));
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1234
1136
|
});
|
|
1235
1137
|
});
|
|
1236
1138
|
}
|
|
@@ -1264,11 +1166,9 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1264
1166
|
async callClaude(prompt) {
|
|
1265
1167
|
return new Promise((resolve, reject) => {
|
|
1266
1168
|
const proc = spawn3("claude", [
|
|
1267
|
-
"-p",
|
|
1268
|
-
prompt,
|
|
1269
1169
|
"--permission-mode",
|
|
1270
1170
|
"bypassPermissions"
|
|
1271
|
-
], { stdio: ["
|
|
1171
|
+
], { stdio: ["pipe", "pipe", "pipe"] });
|
|
1272
1172
|
let stdout = "";
|
|
1273
1173
|
let stderr = "";
|
|
1274
1174
|
proc.stdout.on("data", (data) => {
|
|
@@ -1277,6 +1177,8 @@ var GitHubPlatform = class extends BasePlatform {
|
|
|
1277
1177
|
proc.stderr.on("data", (data) => {
|
|
1278
1178
|
stderr += data.toString();
|
|
1279
1179
|
});
|
|
1180
|
+
proc.stdin.write(prompt);
|
|
1181
|
+
proc.stdin.end();
|
|
1280
1182
|
proc.on("close", (code) => {
|
|
1281
1183
|
if (code !== 0) {
|
|
1282
1184
|
reject(new Error(`Claude CLI failed (code ${code}): stderr=${stderr}, stdout=${stdout}`));
|
|
@@ -1402,7 +1304,7 @@ async function startCommand(query, options) {
|
|
|
1402
1304
|
process.exit(1);
|
|
1403
1305
|
}
|
|
1404
1306
|
const config = loadConfig(cwd);
|
|
1405
|
-
const taskId = generateTaskId();
|
|
1307
|
+
const taskId = generateTaskId(query);
|
|
1406
1308
|
saveTaskMetadata(taskId, query, cwd);
|
|
1407
1309
|
let platform;
|
|
1408
1310
|
if (options.github || options.issue !== void 0 || options.pr !== void 0) {
|
|
@@ -1893,7 +1795,7 @@ async function githubCommand(options) {
|
|
|
1893
1795
|
}
|
|
1894
1796
|
if (!taskId) {
|
|
1895
1797
|
const config2 = loadConfig(cwd);
|
|
1896
|
-
const { listTasks: listTasks2 } = await import("./state-
|
|
1798
|
+
const { listTasks: listTasks2 } = await import("./state-54IS3PZH.js");
|
|
1897
1799
|
const tasks = listTasks2(cwd);
|
|
1898
1800
|
for (const tid of tasks) {
|
|
1899
1801
|
const state2 = getWorkflowState(tid, config2, cwd);
|
|
@@ -2072,7 +1974,7 @@ _Updated by [Spets](https://github.com/eatnug/spets)_`;
|
|
|
2072
1974
|
}
|
|
2073
1975
|
|
|
2074
1976
|
// src/orchestrator/index.ts
|
|
2075
|
-
import { readFileSync, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
1977
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
2076
1978
|
import { join as join4, dirname as dirname2 } from "path";
|
|
2077
1979
|
import matter from "gray-matter";
|
|
2078
1980
|
var Orchestrator = class {
|
|
@@ -2110,7 +2012,7 @@ var Orchestrator = class {
|
|
|
2110
2012
|
if (!existsSync4(statePath)) {
|
|
2111
2013
|
return null;
|
|
2112
2014
|
}
|
|
2113
|
-
const data = JSON.parse(
|
|
2015
|
+
const data = JSON.parse(readFileSync2(statePath, "utf-8"));
|
|
2114
2016
|
return data;
|
|
2115
2017
|
}
|
|
2116
2018
|
saveState(state) {
|
|
@@ -2129,7 +2031,7 @@ var Orchestrator = class {
|
|
|
2129
2031
|
if (!existsSync4(specPath)) {
|
|
2130
2032
|
return [];
|
|
2131
2033
|
}
|
|
2132
|
-
const content =
|
|
2034
|
+
const content = readFileSync2(specPath, "utf-8");
|
|
2133
2035
|
const { data } = matter(content);
|
|
2134
2036
|
const questions = [];
|
|
2135
2037
|
if (data.open_questions && Array.isArray(data.open_questions)) {
|
|
@@ -2148,11 +2050,6 @@ var Orchestrator = class {
|
|
|
2148
2050
|
}
|
|
2149
2051
|
return questions;
|
|
2150
2052
|
}
|
|
2151
|
-
generateTaskId(description) {
|
|
2152
|
-
const timestamp = Date.now().toString(36);
|
|
2153
|
-
const random = Math.random().toString(36).substring(2, 6);
|
|
2154
|
-
return `${timestamp}-${random}`;
|
|
2155
|
-
}
|
|
2156
2053
|
// ===========================================================================
|
|
2157
2054
|
// Protocol Response Builders
|
|
2158
2055
|
// ===========================================================================
|
|
@@ -2258,7 +2155,7 @@ var Orchestrator = class {
|
|
|
2258
2155
|
cmdInit(description) {
|
|
2259
2156
|
try {
|
|
2260
2157
|
const steps = this.getSteps();
|
|
2261
|
-
const taskId =
|
|
2158
|
+
const taskId = generateTaskId(description);
|
|
2262
2159
|
const state = {
|
|
2263
2160
|
taskId,
|
|
2264
2161
|
description,
|
|
@@ -2306,7 +2203,7 @@ var Orchestrator = class {
|
|
|
2306
2203
|
}
|
|
2307
2204
|
const specPath = this.getSpecPath(taskId, state.currentStep);
|
|
2308
2205
|
if (existsSync4(specPath)) {
|
|
2309
|
-
const content =
|
|
2206
|
+
const content = readFileSync2(specPath, "utf-8");
|
|
2310
2207
|
const { content: body, data } = matter(content);
|
|
2311
2208
|
if (data.open_questions && Array.isArray(data.open_questions)) {
|
|
2312
2209
|
data.open_questions = data.open_questions.map((q, i) => ({
|
|
@@ -2333,7 +2230,7 @@ var Orchestrator = class {
|
|
|
2333
2230
|
const steps = this.getSteps();
|
|
2334
2231
|
const specPath = this.getSpecPath(taskId, state.currentStep);
|
|
2335
2232
|
if (existsSync4(specPath)) {
|
|
2336
|
-
const content =
|
|
2233
|
+
const content = readFileSync2(specPath, "utf-8");
|
|
2337
2234
|
const { content: body, data } = matter(content);
|
|
2338
2235
|
data.status = "approved";
|
|
2339
2236
|
data.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2375,7 +2272,7 @@ var Orchestrator = class {
|
|
|
2375
2272
|
}
|
|
2376
2273
|
const specPath = this.getSpecPath(taskId, state.currentStep);
|
|
2377
2274
|
if (existsSync4(specPath)) {
|
|
2378
|
-
const content =
|
|
2275
|
+
const content = readFileSync2(specPath, "utf-8");
|
|
2379
2276
|
const { content: body, data } = matter(content);
|
|
2380
2277
|
data.status = "rejected";
|
|
2381
2278
|
data.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createDocument,
|
|
3
3
|
ensureTaskDir,
|
|
4
|
+
extractPlanSummary,
|
|
4
5
|
generateTaskId,
|
|
5
6
|
getOutputPath,
|
|
6
7
|
getTaskDir,
|
|
@@ -12,10 +13,11 @@ import {
|
|
|
12
13
|
saveDocument,
|
|
13
14
|
saveTaskMetadata,
|
|
14
15
|
updateDocumentStatus
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-NOS3N4GT.js";
|
|
16
17
|
export {
|
|
17
18
|
createDocument,
|
|
18
19
|
ensureTaskDir,
|
|
20
|
+
extractPlanSummary,
|
|
19
21
|
generateTaskId,
|
|
20
22
|
getOutputPath,
|
|
21
23
|
getTaskDir,
|