spets 0.1.40 → 0.1.42
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/dist/index.js +25 -826
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { readFileSync as readFileSync11 } from "fs";
|
|
6
|
-
import { dirname as
|
|
6
|
+
import { dirname as dirname5, join as join11 } from "path";
|
|
7
7
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8
8
|
|
|
9
9
|
// src/commands/init.ts
|
|
@@ -729,211 +729,6 @@ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsS
|
|
|
729
729
|
import { join as join5, dirname as dirname2 } from "path";
|
|
730
730
|
import matter2 from "gray-matter";
|
|
731
731
|
|
|
732
|
-
// src/core/section-tree.ts
|
|
733
|
-
function parseTemplateToTree(template, stepName, documentGoal, outputDir = "sections", instructionSummary = "") {
|
|
734
|
-
const lines = template.split("\n");
|
|
735
|
-
const headerRegex = /^(#{2,6})\s+(.+)$/;
|
|
736
|
-
const headers = [];
|
|
737
|
-
lines.forEach((line, index) => {
|
|
738
|
-
const match = line.match(headerRegex);
|
|
739
|
-
if (match) {
|
|
740
|
-
headers.push({
|
|
741
|
-
depth: match[1].length,
|
|
742
|
-
title: match[2].trim(),
|
|
743
|
-
fullTitle: line.trim(),
|
|
744
|
-
line: index + 1
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
});
|
|
748
|
-
if (headers.length === 0) {
|
|
749
|
-
return {
|
|
750
|
-
stepName,
|
|
751
|
-
sharedContext: {
|
|
752
|
-
documentGoal,
|
|
753
|
-
stepName,
|
|
754
|
-
stepInstruction: instructionSummary,
|
|
755
|
-
totalSections: 0,
|
|
756
|
-
treeOverview: "(no sections)"
|
|
757
|
-
},
|
|
758
|
-
leaves: [],
|
|
759
|
-
consolidateLevels: [],
|
|
760
|
-
rootOutputPath: `${outputDir}/${stepName}.md`
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
const root = {
|
|
764
|
-
id: "root",
|
|
765
|
-
title: "# Document",
|
|
766
|
-
depth: 1,
|
|
767
|
-
startLine: 0,
|
|
768
|
-
children: [],
|
|
769
|
-
isLeaf: false,
|
|
770
|
-
outputPath: `${outputDir}/${stepName}.md`,
|
|
771
|
-
position: { path: [], siblings: [], depth: 1 }
|
|
772
|
-
};
|
|
773
|
-
const stack = [root];
|
|
774
|
-
headers.forEach((header, idx) => {
|
|
775
|
-
const node = {
|
|
776
|
-
id: slugify(header.title),
|
|
777
|
-
title: header.fullTitle,
|
|
778
|
-
depth: header.depth,
|
|
779
|
-
startLine: header.line,
|
|
780
|
-
endLine: idx < headers.length - 1 ? headers[idx + 1].line : lines.length + 1,
|
|
781
|
-
children: [],
|
|
782
|
-
isLeaf: true,
|
|
783
|
-
// Will be updated when children are added
|
|
784
|
-
outputPath: "",
|
|
785
|
-
// Will be set after tree is built
|
|
786
|
-
templateContent: extractContent(lines, header.line, idx < headers.length - 1 ? headers[idx + 1].line : lines.length + 1),
|
|
787
|
-
position: { path: [], siblings: [], depth: header.depth }
|
|
788
|
-
};
|
|
789
|
-
while (stack.length > 1 && stack[stack.length - 1].depth >= header.depth) {
|
|
790
|
-
stack.pop();
|
|
791
|
-
}
|
|
792
|
-
const parent = stack[stack.length - 1];
|
|
793
|
-
parent.children.push(node);
|
|
794
|
-
parent.isLeaf = false;
|
|
795
|
-
stack.push(node);
|
|
796
|
-
});
|
|
797
|
-
calculatePositions(root, [], outputDir);
|
|
798
|
-
const leaves = [];
|
|
799
|
-
collectLeaves(root, leaves);
|
|
800
|
-
const consolidateLevels = generateConsolidateLevels(root);
|
|
801
|
-
const treeOverview = generateTreeOverview(root);
|
|
802
|
-
const totalSections = countSections(root);
|
|
803
|
-
return {
|
|
804
|
-
stepName,
|
|
805
|
-
sharedContext: {
|
|
806
|
-
documentGoal,
|
|
807
|
-
stepName,
|
|
808
|
-
stepInstruction: instructionSummary,
|
|
809
|
-
totalSections,
|
|
810
|
-
treeOverview
|
|
811
|
-
},
|
|
812
|
-
leaves,
|
|
813
|
-
consolidateLevels,
|
|
814
|
-
rootOutputPath: root.outputPath
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
function slugify(title) {
|
|
818
|
-
return title.toLowerCase().replace(/[^a-z0-9가-힣]+/g, "-").replace(/^-+|-+$/g, "");
|
|
819
|
-
}
|
|
820
|
-
function extractContent(lines, startLine, endLine) {
|
|
821
|
-
return lines.slice(startLine, endLine - 1).join("\n").trim();
|
|
822
|
-
}
|
|
823
|
-
function calculatePositions(node, parentPath, outputDir) {
|
|
824
|
-
if (node.id === "root") {
|
|
825
|
-
node.position = { path: [], siblings: [], depth: 1 };
|
|
826
|
-
node.outputPath = `${outputDir}/_root.md`;
|
|
827
|
-
}
|
|
828
|
-
const siblingTitles = node.children.map(
|
|
829
|
-
(c) => c.title.replace(/^#+\s*/, "")
|
|
830
|
-
);
|
|
831
|
-
node.children.forEach((child, idx) => {
|
|
832
|
-
const childTitle = child.title.replace(/^#+\s*/, "");
|
|
833
|
-
const currentPath = [...parentPath, childTitle];
|
|
834
|
-
child.position = {
|
|
835
|
-
path: currentPath,
|
|
836
|
-
siblings: siblingTitles.filter((_, i) => i !== idx),
|
|
837
|
-
parent: parentPath.length > 0 ? parentPath[parentPath.length - 1] : void 0,
|
|
838
|
-
depth: child.depth
|
|
839
|
-
};
|
|
840
|
-
const pathSlug = currentPath.map((p) => slugify(p)).join("/");
|
|
841
|
-
child.outputPath = `${outputDir}/${pathSlug}.md`;
|
|
842
|
-
calculatePositions(child, currentPath, outputDir);
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
|
-
function collectLeaves(node, leaves) {
|
|
846
|
-
if (node.isLeaf && node.id !== "root") {
|
|
847
|
-
leaves.push(node);
|
|
848
|
-
}
|
|
849
|
-
node.children.forEach((child) => collectLeaves(child, leaves));
|
|
850
|
-
}
|
|
851
|
-
function generateConsolidateLevels(root) {
|
|
852
|
-
const levelMap = /* @__PURE__ */ new Map();
|
|
853
|
-
function traverse(node) {
|
|
854
|
-
if (node.children.length > 0) {
|
|
855
|
-
const childIds = node.children.map((c) => c.id);
|
|
856
|
-
const group = {
|
|
857
|
-
parentId: node.id,
|
|
858
|
-
childIds,
|
|
859
|
-
outputPath: node.outputPath
|
|
860
|
-
};
|
|
861
|
-
const childDepth = Math.max(...node.children.map((c) => c.depth));
|
|
862
|
-
if (!levelMap.has(childDepth)) {
|
|
863
|
-
levelMap.set(childDepth, []);
|
|
864
|
-
}
|
|
865
|
-
levelMap.get(childDepth).push(group);
|
|
866
|
-
node.children.forEach((child) => traverse(child));
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
traverse(root);
|
|
870
|
-
const levels = [];
|
|
871
|
-
const depths = Array.from(levelMap.keys()).sort((a, b) => b - a);
|
|
872
|
-
for (const depth of depths) {
|
|
873
|
-
levels.push({
|
|
874
|
-
depth,
|
|
875
|
-
groups: levelMap.get(depth)
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
return levels;
|
|
879
|
-
}
|
|
880
|
-
function generateTreeOverview(root) {
|
|
881
|
-
const lines = [];
|
|
882
|
-
function renderNode(node, prefix, isLast, isRoot) {
|
|
883
|
-
if (!isRoot) {
|
|
884
|
-
const marker = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
885
|
-
lines.push(prefix + marker + node.title);
|
|
886
|
-
}
|
|
887
|
-
const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
|
|
888
|
-
node.children.forEach((child, idx) => {
|
|
889
|
-
const isChildLast = idx === node.children.length - 1;
|
|
890
|
-
renderNode(child, childPrefix, isChildLast, false);
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
renderNode(root, "", true, true);
|
|
894
|
-
return lines.join("\n");
|
|
895
|
-
}
|
|
896
|
-
function countSections(root) {
|
|
897
|
-
let count = 0;
|
|
898
|
-
function traverse(node) {
|
|
899
|
-
if (node.id !== "root") {
|
|
900
|
-
count++;
|
|
901
|
-
}
|
|
902
|
-
node.children.forEach((child) => traverse(child));
|
|
903
|
-
}
|
|
904
|
-
traverse(root);
|
|
905
|
-
return count;
|
|
906
|
-
}
|
|
907
|
-
function buildSectionInstruction(section, sharedContext) {
|
|
908
|
-
return `# Section: ${section.title}
|
|
909
|
-
|
|
910
|
-
## Document Context
|
|
911
|
-
**Goal:** ${sharedContext.documentGoal}
|
|
912
|
-
**Step:** ${sharedContext.stepName}
|
|
913
|
-
**Total Sections:** ${sharedContext.totalSections}
|
|
914
|
-
|
|
915
|
-
## Tree Overview
|
|
916
|
-
\`\`\`
|
|
917
|
-
${sharedContext.treeOverview}
|
|
918
|
-
\`\`\`
|
|
919
|
-
|
|
920
|
-
## Your Position
|
|
921
|
-
- **Path:** ${section.position.path.join(" > ")}
|
|
922
|
-
- **Siblings:** ${section.position.siblings.join(", ") || "None"}
|
|
923
|
-
- **Parent:** ${section.position.parent || "Root"}
|
|
924
|
-
|
|
925
|
-
## Your Task
|
|
926
|
-
Write the "${section.title.replace(/^#+\s*/, "")}" section.
|
|
927
|
-
Consider how it relates to sibling sections and contributes to the parent section.
|
|
928
|
-
|
|
929
|
-
${section.templateContent ? `## Template Guide
|
|
930
|
-
${section.templateContent}` : ""}
|
|
931
|
-
|
|
932
|
-
## Output
|
|
933
|
-
Save to: ${section.outputPath}
|
|
934
|
-
`;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
732
|
// src/core/prompt-builder.ts
|
|
938
733
|
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
939
734
|
import { join as join4 } from "path";
|
|
@@ -1255,111 +1050,6 @@ function buildGeneratePrompt(params) {
|
|
|
1255
1050
|
outputPath
|
|
1256
1051
|
};
|
|
1257
1052
|
}
|
|
1258
|
-
function buildSectionPrompt(params) {
|
|
1259
|
-
const { section, sharedContext, gatheredContext, answers } = params;
|
|
1260
|
-
const parts = [];
|
|
1261
|
-
parts.push(`# Section: ${section.title}`);
|
|
1262
|
-
parts.push("");
|
|
1263
|
-
parts.push("## Document Context");
|
|
1264
|
-
parts.push("");
|
|
1265
|
-
parts.push(`**Goal:** ${sharedContext.documentGoal}`);
|
|
1266
|
-
parts.push(`**Step:** ${sharedContext.stepName}`);
|
|
1267
|
-
parts.push(`**Total Sections:** ${sharedContext.totalSections}`);
|
|
1268
|
-
parts.push("");
|
|
1269
|
-
parts.push("## Document Structure");
|
|
1270
|
-
parts.push("");
|
|
1271
|
-
parts.push("```");
|
|
1272
|
-
parts.push(sharedContext.treeOverview);
|
|
1273
|
-
parts.push("```");
|
|
1274
|
-
parts.push("");
|
|
1275
|
-
parts.push("## Your Position");
|
|
1276
|
-
parts.push("");
|
|
1277
|
-
parts.push(`- **Path:** ${section.position.path.join(" > ")}`);
|
|
1278
|
-
parts.push(`- **Siblings:** ${section.position.siblings.join(", ") || "None"}`);
|
|
1279
|
-
parts.push(`- **Parent:** ${section.position.parent || "Root"}`);
|
|
1280
|
-
parts.push("");
|
|
1281
|
-
if (gatheredContext) {
|
|
1282
|
-
parts.push("## Gathered Context");
|
|
1283
|
-
parts.push("");
|
|
1284
|
-
parts.push(gatheredContext);
|
|
1285
|
-
parts.push("");
|
|
1286
|
-
}
|
|
1287
|
-
if (answers && answers.length > 0) {
|
|
1288
|
-
parts.push("## User Answers");
|
|
1289
|
-
parts.push("");
|
|
1290
|
-
for (const answer of answers) {
|
|
1291
|
-
parts.push(`- **${answer.questionId}**: ${answer.answer}`);
|
|
1292
|
-
}
|
|
1293
|
-
parts.push("");
|
|
1294
|
-
}
|
|
1295
|
-
if (section.templateContent) {
|
|
1296
|
-
parts.push("## Template Guide");
|
|
1297
|
-
parts.push("");
|
|
1298
|
-
parts.push(section.templateContent);
|
|
1299
|
-
parts.push("");
|
|
1300
|
-
}
|
|
1301
|
-
parts.push("## Your Task");
|
|
1302
|
-
parts.push("");
|
|
1303
|
-
parts.push(`Write the "${section.title.replace(/^#+\\s*/, "")}" section.`);
|
|
1304
|
-
parts.push("Consider how it relates to sibling sections and contributes to the parent section.");
|
|
1305
|
-
parts.push("");
|
|
1306
|
-
parts.push("## Output");
|
|
1307
|
-
parts.push("");
|
|
1308
|
-
parts.push(`Save to: \`${section.outputPath}\``);
|
|
1309
|
-
parts.push("");
|
|
1310
|
-
parts.push("Write ONLY the content for this section (including its header).");
|
|
1311
|
-
parts.push("Do NOT include content from other sections.");
|
|
1312
|
-
parts.push("");
|
|
1313
|
-
return {
|
|
1314
|
-
prompt: parts.join("\n"),
|
|
1315
|
-
outputPath: section.outputPath
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
function buildConsolidatePrompt(params) {
|
|
1319
|
-
const { group, childContents, sharedContext, parentTitle } = params;
|
|
1320
|
-
const parts = [];
|
|
1321
|
-
parts.push("# Consolidate Sections");
|
|
1322
|
-
parts.push("");
|
|
1323
|
-
parts.push("## Document Context");
|
|
1324
|
-
parts.push("");
|
|
1325
|
-
parts.push(`**Goal:** ${sharedContext.documentGoal}`);
|
|
1326
|
-
parts.push(`**Step:** ${sharedContext.stepName}`);
|
|
1327
|
-
parts.push("");
|
|
1328
|
-
parts.push("## Document Structure");
|
|
1329
|
-
parts.push("");
|
|
1330
|
-
parts.push("```");
|
|
1331
|
-
parts.push(sharedContext.treeOverview);
|
|
1332
|
-
parts.push("```");
|
|
1333
|
-
parts.push("");
|
|
1334
|
-
parts.push("## Your Task");
|
|
1335
|
-
parts.push("");
|
|
1336
|
-
parts.push(`Consolidate the following child sections into the parent section "${parentTitle || group.parentId}".`);
|
|
1337
|
-
parts.push("");
|
|
1338
|
-
parts.push("## Child Sections to Consolidate");
|
|
1339
|
-
parts.push("");
|
|
1340
|
-
for (const child of childContents) {
|
|
1341
|
-
parts.push(`### Child: ${child.id}`);
|
|
1342
|
-
parts.push("");
|
|
1343
|
-
parts.push(child.content);
|
|
1344
|
-
parts.push("");
|
|
1345
|
-
parts.push("---");
|
|
1346
|
-
parts.push("");
|
|
1347
|
-
}
|
|
1348
|
-
parts.push("## Output Instructions");
|
|
1349
|
-
parts.push("");
|
|
1350
|
-
parts.push(`Save consolidated content to: \`${group.outputPath}\``);
|
|
1351
|
-
parts.push("");
|
|
1352
|
-
parts.push("**Guidelines:**");
|
|
1353
|
-
parts.push("- Combine child sections into a coherent parent section");
|
|
1354
|
-
parts.push("- Maintain the hierarchy (parent header, then children)");
|
|
1355
|
-
parts.push("- Ensure smooth transitions between sections");
|
|
1356
|
-
parts.push("- Preserve all content from child sections");
|
|
1357
|
-
parts.push("");
|
|
1358
|
-
return {
|
|
1359
|
-
prompt: parts.join("\n"),
|
|
1360
|
-
outputPath: group.outputPath
|
|
1361
|
-
};
|
|
1362
|
-
}
|
|
1363
1053
|
function buildExecutePrompt(params) {
|
|
1364
1054
|
const cwd = params.cwd || process.cwd();
|
|
1365
1055
|
const config = loadConfig(cwd);
|
|
@@ -1910,131 +1600,6 @@ ${issues}`;
|
|
|
1910
1600
|
onComplete: `verify-done ${state.taskId}`
|
|
1911
1601
|
};
|
|
1912
1602
|
}
|
|
1913
|
-
/**
|
|
1914
|
-
* Phase 3 (section mode): Parallel section generation
|
|
1915
|
-
*/
|
|
1916
|
-
responsePhaseGenerateSections(state) {
|
|
1917
|
-
if (!state.sectionPlan) {
|
|
1918
|
-
throw new Error("Section plan not initialized");
|
|
1919
|
-
}
|
|
1920
|
-
const steps = this.getSteps();
|
|
1921
|
-
const outputPath = this.getOutputPath();
|
|
1922
|
-
let previousOutput;
|
|
1923
|
-
if (state.stepIndex > 1) {
|
|
1924
|
-
const prevStep = steps[state.stepIndex - 2];
|
|
1925
|
-
const prevPath = join5(outputPath, state.taskId, `${prevStep}.md`);
|
|
1926
|
-
if (existsSync5(prevPath)) {
|
|
1927
|
-
previousOutput = prevPath;
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
const parallelSections = state.sectionPlan.leaves.map((leaf) => ({
|
|
1931
|
-
id: leaf.id,
|
|
1932
|
-
title: leaf.title,
|
|
1933
|
-
outputPath: join5(outputPath, state.taskId, leaf.outputPath),
|
|
1934
|
-
instruction: buildSectionInstruction(leaf, state.sectionPlan.sharedContext),
|
|
1935
|
-
position: {
|
|
1936
|
-
path: leaf.position.path,
|
|
1937
|
-
siblings: leaf.position.siblings,
|
|
1938
|
-
parent: leaf.position.parent
|
|
1939
|
-
},
|
|
1940
|
-
templateContent: leaf.templateContent
|
|
1941
|
-
}));
|
|
1942
|
-
return {
|
|
1943
|
-
type: "phase",
|
|
1944
|
-
phase: "generate-sections",
|
|
1945
|
-
step: state.currentStep,
|
|
1946
|
-
stepIndex: state.stepIndex,
|
|
1947
|
-
totalSteps: state.totalSteps,
|
|
1948
|
-
taskId: state.taskId,
|
|
1949
|
-
description: state.description,
|
|
1950
|
-
sharedContext: state.sectionPlan.sharedContext,
|
|
1951
|
-
parallelSections,
|
|
1952
|
-
consolidateOrder: state.sectionPlan.consolidateLevels,
|
|
1953
|
-
context: {
|
|
1954
|
-
instruction: this.getStepInstructionPath(state.currentStep),
|
|
1955
|
-
template: this.getStepTemplatePath(state.currentStep),
|
|
1956
|
-
previousOutput,
|
|
1957
|
-
outputDir: join5(outputPath, state.taskId, "sections", state.currentStep)
|
|
1958
|
-
},
|
|
1959
|
-
onComplete: `sections-done ${state.taskId}`
|
|
1960
|
-
};
|
|
1961
|
-
}
|
|
1962
|
-
/**
|
|
1963
|
-
* Phase: Consolidate sections at a specific level
|
|
1964
|
-
*/
|
|
1965
|
-
responsePhaseConsolidate(state, level) {
|
|
1966
|
-
if (!state.sectionPlan) {
|
|
1967
|
-
throw new Error("Section plan not initialized");
|
|
1968
|
-
}
|
|
1969
|
-
const outputPath = this.getOutputPath();
|
|
1970
|
-
const parallelGroups = level.groups.map((group) => ({
|
|
1971
|
-
...group,
|
|
1972
|
-
outputPath: join5(outputPath, state.taskId, group.outputPath)
|
|
1973
|
-
}));
|
|
1974
|
-
return {
|
|
1975
|
-
type: "phase",
|
|
1976
|
-
phase: "consolidate",
|
|
1977
|
-
step: state.currentStep,
|
|
1978
|
-
taskId: state.taskId,
|
|
1979
|
-
level: level.depth,
|
|
1980
|
-
parallelGroups,
|
|
1981
|
-
sharedContext: state.sectionPlan.sharedContext,
|
|
1982
|
-
onComplete: `consolidate-level-done ${state.taskId} ${level.depth}`
|
|
1983
|
-
};
|
|
1984
|
-
}
|
|
1985
|
-
/**
|
|
1986
|
-
* Validate that all section files exist before consolidation
|
|
1987
|
-
*/
|
|
1988
|
-
validateConsolidateReady(state, level) {
|
|
1989
|
-
const outputPath = this.getOutputPath();
|
|
1990
|
-
const warnings = [];
|
|
1991
|
-
const missing = [];
|
|
1992
|
-
for (const group of level.groups) {
|
|
1993
|
-
for (const childId of group.childIds) {
|
|
1994
|
-
const findSection = (plan, id) => {
|
|
1995
|
-
for (const leaf of plan.leaves) {
|
|
1996
|
-
if (leaf.id === id) {
|
|
1997
|
-
return leaf.outputPath;
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
for (const lvl of plan.consolidateLevels) {
|
|
2001
|
-
for (const g of lvl.groups) {
|
|
2002
|
-
if (g.parentId === id) {
|
|
2003
|
-
return g.outputPath;
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
}
|
|
2007
|
-
return void 0;
|
|
2008
|
-
};
|
|
2009
|
-
const childPath = findSection(state.sectionPlan, childId);
|
|
2010
|
-
if (!childPath) {
|
|
2011
|
-
missing.push(childId);
|
|
2012
|
-
continue;
|
|
2013
|
-
}
|
|
2014
|
-
const fullPath = join5(outputPath, state.taskId, childPath);
|
|
2015
|
-
if (!existsSync5(fullPath)) {
|
|
2016
|
-
missing.push(childId);
|
|
2017
|
-
} else {
|
|
2018
|
-
const content = readFileSync5(fullPath, "utf-8");
|
|
2019
|
-
if (!content.trim()) {
|
|
2020
|
-
warnings.push(`Empty section file: ${childId}`);
|
|
2021
|
-
}
|
|
2022
|
-
if (!content.match(/^#+ /m)) {
|
|
2023
|
-
warnings.push(`Section missing header: ${childId}`);
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
if (missing.length > 0) {
|
|
2029
|
-
return {
|
|
2030
|
-
valid: false,
|
|
2031
|
-
error: `Missing section files: ${missing.join(", ")}`,
|
|
2032
|
-
warnings,
|
|
2033
|
-
missing
|
|
2034
|
-
};
|
|
2035
|
-
}
|
|
2036
|
-
return { valid: true, warnings };
|
|
2037
|
-
}
|
|
2038
1603
|
/**
|
|
2039
1604
|
* Checkpoint: Questions need answers
|
|
2040
1605
|
*/
|
|
@@ -2158,7 +1723,7 @@ ${issues}`;
|
|
|
2158
1723
|
/**
|
|
2159
1724
|
* Phase 2 complete: Questions generated (or none)
|
|
2160
1725
|
* If questions exist → checkpoint
|
|
2161
|
-
* If no questions → move to
|
|
1726
|
+
* If no questions → move to execute phase with accumulated answers
|
|
2162
1727
|
*/
|
|
2163
1728
|
cmdClarifyDone(taskId, questions) {
|
|
2164
1729
|
const state = this.loadState(taskId);
|
|
@@ -2173,15 +1738,8 @@ ${issues}`;
|
|
|
2173
1738
|
return this.responseCheckpointClarify(state);
|
|
2174
1739
|
}
|
|
2175
1740
|
state.answers = state.qaHistory?.map((entry) => entry.answer);
|
|
2176
|
-
this.initSectionPlan(state);
|
|
2177
|
-
if (state.sectionPlan) {
|
|
2178
|
-
state.status = "phase_sections";
|
|
2179
|
-
state.phase = "draft";
|
|
2180
|
-
this.saveState(state);
|
|
2181
|
-
return this.responsePhaseGenerateSections(state);
|
|
2182
|
-
}
|
|
2183
1741
|
state.status = "phase_execute";
|
|
2184
|
-
state.phase = "
|
|
1742
|
+
state.phase = "execute";
|
|
2185
1743
|
state.verifyAttempts = 0;
|
|
2186
1744
|
this.saveState(state);
|
|
2187
1745
|
return this.responsePhaseExecute(state);
|
|
@@ -2391,110 +1949,6 @@ ${issues}`;
|
|
|
2391
1949
|
this.saveState(state);
|
|
2392
1950
|
return this.responseComplete(state, "stopped");
|
|
2393
1951
|
}
|
|
2394
|
-
// ===========================================================================
|
|
2395
|
-
// Section-based Workflow Commands
|
|
2396
|
-
// ===========================================================================
|
|
2397
|
-
/**
|
|
2398
|
-
* Initialize section-based generation for current step
|
|
2399
|
-
* Called internally when template has sections
|
|
2400
|
-
*/
|
|
2401
|
-
initSectionPlan(state) {
|
|
2402
|
-
const templatePath = this.getStepTemplatePath(state.currentStep);
|
|
2403
|
-
if (!existsSync5(templatePath)) {
|
|
2404
|
-
return;
|
|
2405
|
-
}
|
|
2406
|
-
const template = readFileSync5(templatePath, "utf-8");
|
|
2407
|
-
const instructionPath = this.getStepInstructionPath(state.currentStep);
|
|
2408
|
-
const instructionSummary = existsSync5(instructionPath) ? readFileSync5(instructionPath, "utf-8").slice(0, 500) : "";
|
|
2409
|
-
const plan = parseTemplateToTree(
|
|
2410
|
-
template,
|
|
2411
|
-
state.currentStep,
|
|
2412
|
-
state.description,
|
|
2413
|
-
`sections/${state.currentStep}`,
|
|
2414
|
-
instructionSummary
|
|
2415
|
-
);
|
|
2416
|
-
if (plan.leaves.length > 1) {
|
|
2417
|
-
state.sectionPlan = plan;
|
|
2418
|
-
state.sectionStatuses = plan.leaves.map((leaf) => ({
|
|
2419
|
-
id: leaf.id,
|
|
2420
|
-
status: "pending",
|
|
2421
|
-
outputPath: leaf.outputPath
|
|
2422
|
-
}));
|
|
2423
|
-
state.consolidateProgress = {
|
|
2424
|
-
currentLevel: 0,
|
|
2425
|
-
completedLevels: [],
|
|
2426
|
-
totalLevels: plan.consolidateLevels.length
|
|
2427
|
-
};
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
/**
|
|
2431
|
-
* All sections completed, move to consolidation
|
|
2432
|
-
*/
|
|
2433
|
-
cmdSectionsDone(taskId) {
|
|
2434
|
-
const state = this.loadState(taskId);
|
|
2435
|
-
if (!state) {
|
|
2436
|
-
return this.responseError(`No workflow found: ${taskId}`, taskId);
|
|
2437
|
-
}
|
|
2438
|
-
if (!state.sectionPlan || !state.consolidateProgress) {
|
|
2439
|
-
return this.responseError("No section plan found", taskId, state.currentStep);
|
|
2440
|
-
}
|
|
2441
|
-
if (state.sectionStatuses) {
|
|
2442
|
-
state.sectionStatuses = state.sectionStatuses.map((s) => ({
|
|
2443
|
-
...s,
|
|
2444
|
-
status: "completed"
|
|
2445
|
-
}));
|
|
2446
|
-
}
|
|
2447
|
-
if (state.consolidateProgress.totalLevels > 0) {
|
|
2448
|
-
state.status = "phase_consolidate";
|
|
2449
|
-
state.consolidateProgress.currentLevel = 0;
|
|
2450
|
-
this.saveState(state);
|
|
2451
|
-
const firstLevel = state.sectionPlan.consolidateLevels[0];
|
|
2452
|
-
const validation = this.validateConsolidateReady(state, firstLevel);
|
|
2453
|
-
if (!validation.valid) {
|
|
2454
|
-
return this.responseError(
|
|
2455
|
-
validation.error || "Validation failed",
|
|
2456
|
-
taskId,
|
|
2457
|
-
state.currentStep
|
|
2458
|
-
);
|
|
2459
|
-
}
|
|
2460
|
-
return this.responsePhaseConsolidate(state, firstLevel);
|
|
2461
|
-
}
|
|
2462
|
-
state.status = "approve_pending";
|
|
2463
|
-
state.phase = "review";
|
|
2464
|
-
this.saveState(state);
|
|
2465
|
-
return this.responseCheckpointApprove(state);
|
|
2466
|
-
}
|
|
2467
|
-
/**
|
|
2468
|
-
* Consolidation level completed, move to next level or approve
|
|
2469
|
-
*/
|
|
2470
|
-
cmdConsolidateLevelDone(taskId, level) {
|
|
2471
|
-
const state = this.loadState(taskId);
|
|
2472
|
-
if (!state) {
|
|
2473
|
-
return this.responseError(`No workflow found: ${taskId}`, taskId);
|
|
2474
|
-
}
|
|
2475
|
-
if (!state.sectionPlan || !state.consolidateProgress) {
|
|
2476
|
-
return this.responseError("No section plan found", taskId, state.currentStep);
|
|
2477
|
-
}
|
|
2478
|
-
state.consolidateProgress.completedLevels.push(level);
|
|
2479
|
-
state.consolidateProgress.currentLevel++;
|
|
2480
|
-
if (state.consolidateProgress.currentLevel < state.consolidateProgress.totalLevels) {
|
|
2481
|
-
this.saveState(state);
|
|
2482
|
-
const nextLevel = state.sectionPlan.consolidateLevels[state.consolidateProgress.currentLevel];
|
|
2483
|
-
const validation = this.validateConsolidateReady(state, nextLevel);
|
|
2484
|
-
if (!validation.valid) {
|
|
2485
|
-
return this.responseError(
|
|
2486
|
-
validation.error || "Validation failed",
|
|
2487
|
-
taskId,
|
|
2488
|
-
state.currentStep
|
|
2489
|
-
);
|
|
2490
|
-
}
|
|
2491
|
-
return this.responsePhaseConsolidate(state, nextLevel);
|
|
2492
|
-
}
|
|
2493
|
-
state.status = "approve_pending";
|
|
2494
|
-
state.phase = "review";
|
|
2495
|
-
this.saveState(state);
|
|
2496
|
-
return this.responseCheckpointApprove(state);
|
|
2497
|
-
}
|
|
2498
1952
|
/**
|
|
2499
1953
|
* Get current workflow status
|
|
2500
1954
|
*/
|
|
@@ -2527,15 +1981,6 @@ ${issues}`;
|
|
|
2527
1981
|
return this.responsePhaseExecute(state);
|
|
2528
1982
|
case "phase_verify":
|
|
2529
1983
|
return this.responsePhaseVerify(state);
|
|
2530
|
-
// Section-based workflow
|
|
2531
|
-
case "phase_sections":
|
|
2532
|
-
return this.responsePhaseGenerateSections(state);
|
|
2533
|
-
case "phase_consolidate":
|
|
2534
|
-
if (state.sectionPlan && state.consolidateProgress) {
|
|
2535
|
-
const level = state.sectionPlan.consolidateLevels[state.consolidateProgress.currentLevel];
|
|
2536
|
-
return this.responsePhaseConsolidate(state, level);
|
|
2537
|
-
}
|
|
2538
|
-
return this.responseError("Invalid consolidate state", taskId);
|
|
2539
1984
|
// Checkpoints and completion
|
|
2540
1985
|
case "approve_pending":
|
|
2541
1986
|
return this.responseCheckpointApprove(state);
|
|
@@ -2557,10 +2002,9 @@ ${issues}`;
|
|
|
2557
2002
|
};
|
|
2558
2003
|
|
|
2559
2004
|
// src/core/step-executor.ts
|
|
2560
|
-
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4
|
|
2561
|
-
import {
|
|
2005
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
2006
|
+
import { join as join6 } from "path";
|
|
2562
2007
|
import matter3 from "gray-matter";
|
|
2563
|
-
var fsExistsSync = existsSync6;
|
|
2564
2008
|
var StepExecutor = class {
|
|
2565
2009
|
adapter;
|
|
2566
2010
|
config;
|
|
@@ -2802,159 +2246,6 @@ var StepExecutor = class {
|
|
|
2802
2246
|
};
|
|
2803
2247
|
}
|
|
2804
2248
|
// ==========================================================================
|
|
2805
|
-
// Phase 3 (Section Mode): Parallel Section Generation
|
|
2806
|
-
// ==========================================================================
|
|
2807
|
-
/**
|
|
2808
|
-
* Execute section generation phase - AI creates individual sections in parallel
|
|
2809
|
-
*/
|
|
2810
|
-
async executeSectionsPhase(sections, sharedContext, gatheredContext, answers) {
|
|
2811
|
-
this.adapter.io.notify(
|
|
2812
|
-
`Phase 3/4: Generating ${sections.length} sections in parallel`,
|
|
2813
|
-
"info"
|
|
2814
|
-
);
|
|
2815
|
-
const executeParams = sections.map((section) => {
|
|
2816
|
-
const params = {
|
|
2817
|
-
section: {
|
|
2818
|
-
id: section.id,
|
|
2819
|
-
title: section.title,
|
|
2820
|
-
outputPath: section.outputPath,
|
|
2821
|
-
instruction: section.instruction,
|
|
2822
|
-
position: section.position,
|
|
2823
|
-
templateContent: section.templateContent
|
|
2824
|
-
},
|
|
2825
|
-
sharedContext,
|
|
2826
|
-
gatheredContext,
|
|
2827
|
-
answers
|
|
2828
|
-
};
|
|
2829
|
-
const { prompt, outputPath } = buildSectionPrompt(params);
|
|
2830
|
-
return { id: section.id, prompt, outputPath };
|
|
2831
|
-
});
|
|
2832
|
-
let results;
|
|
2833
|
-
if (this.adapter.ai.executeParallel) {
|
|
2834
|
-
const parallelResults = await this.adapter.ai.executeParallel(executeParams);
|
|
2835
|
-
results = parallelResults.map((r) => {
|
|
2836
|
-
const param = executeParams.find((p) => p.id === r.id);
|
|
2837
|
-
if (r.success && param && !fsExistsSync(param.outputPath)) {
|
|
2838
|
-
return { id: r.id, success: false, error: `File not created: ${param.outputPath}` };
|
|
2839
|
-
}
|
|
2840
|
-
return { id: r.id, success: r.success, error: r.error };
|
|
2841
|
-
});
|
|
2842
|
-
} else {
|
|
2843
|
-
results = await Promise.all(
|
|
2844
|
-
executeParams.map(async ({ id, prompt, outputPath }) => {
|
|
2845
|
-
try {
|
|
2846
|
-
const dir = dirname3(outputPath);
|
|
2847
|
-
if (!fsExistsSync(dir)) {
|
|
2848
|
-
mkdirSync4(dir, { recursive: true });
|
|
2849
|
-
}
|
|
2850
|
-
await this.adapter.ai.execute({ prompt, outputPath });
|
|
2851
|
-
if (!fsExistsSync(outputPath)) {
|
|
2852
|
-
return { id, success: false, error: `File not created: ${outputPath}` };
|
|
2853
|
-
}
|
|
2854
|
-
this.adapter.io.notify(`Section created: ${id}`, "success");
|
|
2855
|
-
return { id, success: true };
|
|
2856
|
-
} catch (error) {
|
|
2857
|
-
return {
|
|
2858
|
-
id,
|
|
2859
|
-
success: false,
|
|
2860
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2861
|
-
};
|
|
2862
|
-
}
|
|
2863
|
-
})
|
|
2864
|
-
);
|
|
2865
|
-
}
|
|
2866
|
-
const failures = results.filter((r) => !r.success);
|
|
2867
|
-
if (failures.length > 0) {
|
|
2868
|
-
const errorMsg = failures.map((f) => `${f.id}: ${f.error}`).join(", ");
|
|
2869
|
-
throw new Error(`Section generation failed: ${errorMsg}`);
|
|
2870
|
-
}
|
|
2871
|
-
return { phase: "generate" };
|
|
2872
|
-
}
|
|
2873
|
-
/**
|
|
2874
|
-
* Execute consolidation phase - AI merges child sections into parent
|
|
2875
|
-
*/
|
|
2876
|
-
async executeConsolidatePhase(groups, sharedContext, getSectionPath) {
|
|
2877
|
-
this.adapter.io.notify(
|
|
2878
|
-
`Consolidating ${groups.length} section groups`,
|
|
2879
|
-
"info"
|
|
2880
|
-
);
|
|
2881
|
-
const executeParams = [];
|
|
2882
|
-
for (const group of groups) {
|
|
2883
|
-
const childContents = [];
|
|
2884
|
-
let hasError = false;
|
|
2885
|
-
for (const childId of group.childIds) {
|
|
2886
|
-
const childPath = getSectionPath(childId);
|
|
2887
|
-
if (!childPath || !fsExistsSync(childPath)) {
|
|
2888
|
-
executeParams.push({ id: group.parentId, error: `Child section not found: ${childId}` });
|
|
2889
|
-
hasError = true;
|
|
2890
|
-
break;
|
|
2891
|
-
}
|
|
2892
|
-
childContents.push({
|
|
2893
|
-
id: childId,
|
|
2894
|
-
content: readFileSync6(childPath, "utf-8")
|
|
2895
|
-
});
|
|
2896
|
-
}
|
|
2897
|
-
if (hasError) continue;
|
|
2898
|
-
const params = {
|
|
2899
|
-
group: {
|
|
2900
|
-
parentId: group.parentId,
|
|
2901
|
-
childIds: group.childIds,
|
|
2902
|
-
outputPath: group.outputPath
|
|
2903
|
-
},
|
|
2904
|
-
childContents,
|
|
2905
|
-
sharedContext
|
|
2906
|
-
};
|
|
2907
|
-
const { prompt, outputPath } = buildConsolidatePrompt(params);
|
|
2908
|
-
executeParams.push({ id: group.parentId, prompt, outputPath });
|
|
2909
|
-
}
|
|
2910
|
-
const earlyErrors = executeParams.filter((p) => "error" in p);
|
|
2911
|
-
if (earlyErrors.length > 0) {
|
|
2912
|
-
const errorMsg = earlyErrors.map((e) => `${e.id}: ${e.error}`).join(", ");
|
|
2913
|
-
throw new Error(`Consolidation failed: ${errorMsg}`);
|
|
2914
|
-
}
|
|
2915
|
-
const validParams = executeParams;
|
|
2916
|
-
let results;
|
|
2917
|
-
if (this.adapter.ai.executeParallel) {
|
|
2918
|
-
const parallelResults = await this.adapter.ai.executeParallel(validParams);
|
|
2919
|
-
results = parallelResults.map((r) => {
|
|
2920
|
-
const param = validParams.find((p) => p.id === r.id);
|
|
2921
|
-
if (r.success && param && !fsExistsSync(param.outputPath)) {
|
|
2922
|
-
return { id: r.id, success: false, error: `File not created: ${param.outputPath}` };
|
|
2923
|
-
}
|
|
2924
|
-
return { id: r.id, success: r.success, error: r.error };
|
|
2925
|
-
});
|
|
2926
|
-
} else {
|
|
2927
|
-
results = await Promise.all(
|
|
2928
|
-
validParams.map(async ({ id, prompt, outputPath }) => {
|
|
2929
|
-
try {
|
|
2930
|
-
const dir = dirname3(outputPath);
|
|
2931
|
-
if (!fsExistsSync(dir)) {
|
|
2932
|
-
mkdirSync4(dir, { recursive: true });
|
|
2933
|
-
}
|
|
2934
|
-
await this.adapter.ai.execute({ prompt, outputPath });
|
|
2935
|
-
if (!fsExistsSync(outputPath)) {
|
|
2936
|
-
return { id, success: false, error: `File not created: ${outputPath}` };
|
|
2937
|
-
}
|
|
2938
|
-
this.adapter.io.notify(`Consolidated: ${id}`, "success");
|
|
2939
|
-
return { id, success: true };
|
|
2940
|
-
} catch (error) {
|
|
2941
|
-
return {
|
|
2942
|
-
id,
|
|
2943
|
-
success: false,
|
|
2944
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2945
|
-
};
|
|
2946
|
-
}
|
|
2947
|
-
})
|
|
2948
|
-
);
|
|
2949
|
-
}
|
|
2950
|
-
const failures = results.filter((r) => !r.success);
|
|
2951
|
-
if (failures.length > 0) {
|
|
2952
|
-
const errorMsg = failures.map((f) => `${f.id}: ${f.error}`).join(", ");
|
|
2953
|
-
throw new Error(`Consolidation failed: ${errorMsg}`);
|
|
2954
|
-
}
|
|
2955
|
-
return { phase: "generate" };
|
|
2956
|
-
}
|
|
2957
|
-
// ==========================================================================
|
|
2958
2249
|
// Phase 4: Review (Approval)
|
|
2959
2250
|
// ==========================================================================
|
|
2960
2251
|
/**
|
|
@@ -3156,8 +2447,8 @@ var StepExecutor = class {
|
|
|
3156
2447
|
|
|
3157
2448
|
// src/adapters/cli.ts
|
|
3158
2449
|
import { spawn, spawnSync } from "child_process";
|
|
3159
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as
|
|
3160
|
-
import { dirname as
|
|
2450
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
2451
|
+
import { dirname as dirname3 } from "path";
|
|
3161
2452
|
import { input, select, confirm } from "@inquirer/prompts";
|
|
3162
2453
|
var CLIAIAdapter = class {
|
|
3163
2454
|
claudeCommand;
|
|
@@ -3168,9 +2459,9 @@ var CLIAIAdapter = class {
|
|
|
3168
2459
|
console.log(`
|
|
3169
2460
|
\u{1F4DD} Generating...`);
|
|
3170
2461
|
if (params.outputPath) {
|
|
3171
|
-
const outputDir =
|
|
2462
|
+
const outputDir = dirname3(params.outputPath);
|
|
3172
2463
|
if (!existsSync7(outputDir)) {
|
|
3173
|
-
|
|
2464
|
+
mkdirSync4(outputDir, { recursive: true });
|
|
3174
2465
|
}
|
|
3175
2466
|
}
|
|
3176
2467
|
const stdout = await this.callClaude(params.prompt);
|
|
@@ -3251,9 +2542,9 @@ var CLIAIAdapter = class {
|
|
|
3251
2542
|
const id = p.id || p.outputPath;
|
|
3252
2543
|
try {
|
|
3253
2544
|
if (p.outputPath) {
|
|
3254
|
-
const outputDir =
|
|
2545
|
+
const outputDir = dirname3(p.outputPath);
|
|
3255
2546
|
if (!existsSync7(outputDir)) {
|
|
3256
|
-
|
|
2547
|
+
mkdirSync4(outputDir, { recursive: true });
|
|
3257
2548
|
}
|
|
3258
2549
|
}
|
|
3259
2550
|
await this.callClaude(p.prompt, false);
|
|
@@ -3358,9 +2649,9 @@ var CLISystemAdapter = class {
|
|
|
3358
2649
|
return readFileSync7(path, "utf-8");
|
|
3359
2650
|
}
|
|
3360
2651
|
writeFile(path, content) {
|
|
3361
|
-
const dir =
|
|
2652
|
+
const dir = dirname3(path);
|
|
3362
2653
|
if (!existsSync7(dir)) {
|
|
3363
|
-
|
|
2654
|
+
mkdirSync4(dir, { recursive: true });
|
|
3364
2655
|
}
|
|
3365
2656
|
writeFileSync5(path, content);
|
|
3366
2657
|
}
|
|
@@ -3396,8 +2687,8 @@ function createCLIAdapter(claudeCommand = "claude") {
|
|
|
3396
2687
|
|
|
3397
2688
|
// src/adapters/github.ts
|
|
3398
2689
|
import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
3399
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as
|
|
3400
|
-
import { dirname as
|
|
2690
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
2691
|
+
import { dirname as dirname4 } from "path";
|
|
3401
2692
|
var GitHubAIAdapter = class {
|
|
3402
2693
|
claudeCommand;
|
|
3403
2694
|
constructor(claudeCommand = "claude") {
|
|
@@ -3407,9 +2698,9 @@ var GitHubAIAdapter = class {
|
|
|
3407
2698
|
console.log(`
|
|
3408
2699
|
\u{1F4DD} Generating...`);
|
|
3409
2700
|
if (params.outputPath) {
|
|
3410
|
-
const outputDir =
|
|
2701
|
+
const outputDir = dirname4(params.outputPath);
|
|
3411
2702
|
if (!existsSync8(outputDir)) {
|
|
3412
|
-
|
|
2703
|
+
mkdirSync5(outputDir, { recursive: true });
|
|
3413
2704
|
}
|
|
3414
2705
|
}
|
|
3415
2706
|
const stdout = await this.callClaude(params.prompt);
|
|
@@ -3465,9 +2756,9 @@ var GitHubAIAdapter = class {
|
|
|
3465
2756
|
const id = p.id || p.outputPath;
|
|
3466
2757
|
try {
|
|
3467
2758
|
if (p.outputPath) {
|
|
3468
|
-
const outputDir =
|
|
2759
|
+
const outputDir = dirname4(p.outputPath);
|
|
3469
2760
|
if (!existsSync8(outputDir)) {
|
|
3470
|
-
|
|
2761
|
+
mkdirSync5(outputDir, { recursive: true });
|
|
3471
2762
|
}
|
|
3472
2763
|
}
|
|
3473
2764
|
await this.callClaude(p.prompt);
|
|
@@ -3640,9 +2931,9 @@ var GitHubSystemAdapter = class {
|
|
|
3640
2931
|
return readFileSync8(path, "utf-8");
|
|
3641
2932
|
}
|
|
3642
2933
|
writeFile(path, content) {
|
|
3643
|
-
const dir =
|
|
2934
|
+
const dir = dirname4(path);
|
|
3644
2935
|
if (!existsSync8(dir)) {
|
|
3645
|
-
|
|
2936
|
+
mkdirSync5(dir, { recursive: true });
|
|
3646
2937
|
}
|
|
3647
2938
|
writeFileSync6(path, content);
|
|
3648
2939
|
}
|
|
@@ -4275,7 +3566,7 @@ Resume with: spets resume --task ${taskId}`);
|
|
|
4275
3566
|
}
|
|
4276
3567
|
|
|
4277
3568
|
// src/commands/plugin.ts
|
|
4278
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
3569
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7, rmSync } from "fs";
|
|
4279
3570
|
import { join as join8 } from "path";
|
|
4280
3571
|
import { homedir } from "os";
|
|
4281
3572
|
async function pluginCommand(action, name) {
|
|
@@ -4320,7 +3611,7 @@ async function installPlugin(name) {
|
|
|
4320
3611
|
function installClaudePlugin() {
|
|
4321
3612
|
const claudeDir = join8(homedir(), ".claude");
|
|
4322
3613
|
const commandsDir = join8(claudeDir, "commands");
|
|
4323
|
-
|
|
3614
|
+
mkdirSync6(commandsDir, { recursive: true });
|
|
4324
3615
|
const skillPath = join8(commandsDir, "spets.md");
|
|
4325
3616
|
writeFileSync7(skillPath, getClaudeSkillContent());
|
|
4326
3617
|
console.log("Installed Claude Code plugin.");
|
|
@@ -4406,8 +3697,6 @@ Parse JSON response \u2192 execute action from table below \u2192 loop until \`t
|
|
|
4406
3697
|
| \`clarify\` | [ACTION_CLARIFY](#action_clarify) |
|
|
4407
3698
|
| \`execute\` | [ACTION_EXECUTE](#action_execute) |
|
|
4408
3699
|
| \`verify\` | [ACTION_VERIFY](#action_verify) |
|
|
4409
|
-
| \`generate-sections\` | [ACTION_PARALLEL_SECTIONS](#action_parallel_sections) |
|
|
4410
|
-
| \`consolidate\` | [ACTION_PARALLEL_CONSOLIDATE](#action_parallel_consolidate) |
|
|
4411
3700
|
| \`context\` | [ACTION_CONTEXT](#action_context) (legacy) |
|
|
4412
3701
|
| \`generate\` | [ACTION_GENERATE](#action_generate) (legacy) |
|
|
4413
3702
|
|
|
@@ -4515,65 +3804,6 @@ This ensures identical behavior across CLI, Claude Code, and GitHub modes.
|
|
|
4515
3804
|
5. Parse response \u2192 next action
|
|
4516
3805
|
\`\`\`
|
|
4517
3806
|
|
|
4518
|
-
### ACTION_PARALLEL_SECTIONS
|
|
4519
|
-
|
|
4520
|
-
**PARALLEL EXECUTION (FOREGROUND)**
|
|
4521
|
-
|
|
4522
|
-
\`\`\`
|
|
4523
|
-
1. FOR EACH section IN parallelSections:
|
|
4524
|
-
Task(
|
|
4525
|
-
subagent_type: "general-purpose",
|
|
4526
|
-
prompt: "
|
|
4527
|
-
Generate section and save to file. Return ONLY the path.
|
|
4528
|
-
|
|
4529
|
-
Path: {section.outputPath}
|
|
4530
|
-
Title: {section.title}
|
|
4531
|
-
Instruction: {section.instruction}
|
|
4532
|
-
Context: {sharedContext.description}
|
|
4533
|
-
|
|
4534
|
-
1. Generate content for this section
|
|
4535
|
-
2. Write to the path using Write tool
|
|
4536
|
-
3. Return ONLY: {section.outputPath}
|
|
4537
|
-
(no explanations, no content, just the path)
|
|
4538
|
-
"
|
|
4539
|
-
)
|
|
4540
|
-
|
|
4541
|
-
2. ALL Task calls MUST be in SINGLE message (parallel execution)
|
|
4542
|
-
|
|
4543
|
-
3. Bash: npx spets orchestrate sections-done {taskId}
|
|
4544
|
-
|
|
4545
|
-
4. Parse response \u2192 next action
|
|
4546
|
-
\`\`\`
|
|
4547
|
-
|
|
4548
|
-
### ACTION_PARALLEL_CONSOLIDATE
|
|
4549
|
-
|
|
4550
|
-
**PARALLEL EXECUTION (FOREGROUND)**
|
|
4551
|
-
|
|
4552
|
-
\`\`\`
|
|
4553
|
-
1. FOR EACH group IN parallelGroups:
|
|
4554
|
-
Task(
|
|
4555
|
-
subagent_type: "general-purpose",
|
|
4556
|
-
prompt: "
|
|
4557
|
-
Merge children into parent. Return ONLY the path.
|
|
4558
|
-
|
|
4559
|
-
Output: {group.outputPath}
|
|
4560
|
-
Children: {group.childPaths}
|
|
4561
|
-
|
|
4562
|
-
1. Read each child file
|
|
4563
|
-
2. Merge preserving hierarchy
|
|
4564
|
-
3. Write to output using Write tool
|
|
4565
|
-
4. Return ONLY: {group.outputPath}
|
|
4566
|
-
(no explanations, no content, just the path)
|
|
4567
|
-
"
|
|
4568
|
-
)
|
|
4569
|
-
|
|
4570
|
-
2. ALL Task calls MUST be in SINGLE message (parallel execution)
|
|
4571
|
-
|
|
4572
|
-
3. Bash: npx spets orchestrate consolidate-level-done {taskId} {level}
|
|
4573
|
-
|
|
4574
|
-
4. Parse response \u2192 next action
|
|
4575
|
-
\`\`\`
|
|
4576
|
-
|
|
4577
3807
|
### ACTION_ASK_QUESTIONS
|
|
4578
3808
|
|
|
4579
3809
|
\`\`\`
|
|
@@ -4645,10 +3875,6 @@ npx spets orchestrate revise <taskId> "<feedback>"
|
|
|
4645
3875
|
npx spets orchestrate reject <taskId>
|
|
4646
3876
|
npx spets orchestrate stop <taskId>
|
|
4647
3877
|
|
|
4648
|
-
# Section-based workflow
|
|
4649
|
-
npx spets orchestrate sections-done <taskId>
|
|
4650
|
-
npx spets orchestrate consolidate-level-done <taskId> <level>
|
|
4651
|
-
|
|
4652
3878
|
# Legacy (backward compatible)
|
|
4653
3879
|
npx spets orchestrate context-done <taskId>
|
|
4654
3880
|
npx spets orchestrate generate-done <taskId>
|
|
@@ -5388,35 +4614,8 @@ async function orchestrateCommand(action, args) {
|
|
|
5388
4614
|
outputJSON(result);
|
|
5389
4615
|
break;
|
|
5390
4616
|
}
|
|
5391
|
-
// Section-based workflow commands
|
|
5392
|
-
case "sections-done": {
|
|
5393
|
-
const taskId = args[0];
|
|
5394
|
-
if (!taskId) {
|
|
5395
|
-
outputError("Task ID is required for sections-done");
|
|
5396
|
-
return;
|
|
5397
|
-
}
|
|
5398
|
-
const result = orchestrator.cmdSectionsDone(taskId);
|
|
5399
|
-
outputJSON(result);
|
|
5400
|
-
break;
|
|
5401
|
-
}
|
|
5402
|
-
case "consolidate-level-done": {
|
|
5403
|
-
const taskId = args[0];
|
|
5404
|
-
const levelStr = args[1];
|
|
5405
|
-
if (!taskId || !levelStr) {
|
|
5406
|
-
outputError("Task ID and level are required for consolidate-level-done");
|
|
5407
|
-
return;
|
|
5408
|
-
}
|
|
5409
|
-
const level = parseInt(levelStr, 10);
|
|
5410
|
-
if (isNaN(level)) {
|
|
5411
|
-
outputError("Level must be a number");
|
|
5412
|
-
return;
|
|
5413
|
-
}
|
|
5414
|
-
const result = orchestrator.cmdConsolidateLevelDone(taskId, level);
|
|
5415
|
-
outputJSON(result);
|
|
5416
|
-
break;
|
|
5417
|
-
}
|
|
5418
4617
|
default:
|
|
5419
|
-
outputError(`Unknown action: ${action}. Valid actions: init, explore-done, clarify-done, execute-done, verify-done, clarified, approve, revise, reject, stop, status
|
|
4618
|
+
outputError(`Unknown action: ${action}. Valid actions: init, explore-done, clarify-done, execute-done, verify-done, clarified, approve, revise, reject, stop, status (legacy: context-done, generate-done, draft-done, done)`);
|
|
5420
4619
|
}
|
|
5421
4620
|
} catch (e) {
|
|
5422
4621
|
outputError(e.message);
|
|
@@ -5424,7 +4623,7 @@ async function orchestrateCommand(action, args) {
|
|
|
5424
4623
|
}
|
|
5425
4624
|
|
|
5426
4625
|
// src/index.ts
|
|
5427
|
-
var __dirname2 =
|
|
4626
|
+
var __dirname2 = dirname5(fileURLToPath2(import.meta.url));
|
|
5428
4627
|
var pkg = JSON.parse(readFileSync11(join11(__dirname2, "..", "package.json"), "utf-8"));
|
|
5429
4628
|
var program = new Command();
|
|
5430
4629
|
program.name("spets").description("Spec Driven Development Execution Framework").version(pkg.version);
|