spets 0.1.19 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +864 -408
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
- import { readFileSync as readFileSync9 } from "fs";
6
- import { dirname as dirname6, join as join9 } from "path";
5
+ import { readFileSync as readFileSync10 } from "fs";
6
+ import { dirname as dirname5, join as join10 } from "path";
7
7
  import { fileURLToPath as fileURLToPath2 } from "url";
8
8
 
9
9
  // src/commands/init.ts
@@ -589,7 +589,7 @@ function formatDocStatus(status) {
589
589
  }
590
590
 
591
591
  // src/commands/start.ts
592
- import { execSync as execSync3 } from "child_process";
592
+ import { execSync as execSync2 } from "child_process";
593
593
 
594
594
  // src/orchestrator/index.ts
595
595
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
@@ -671,7 +671,56 @@ var Orchestrator = class {
671
671
  // ===========================================================================
672
672
  // Protocol Response Builders
673
673
  // ===========================================================================
674
- responseStep(state) {
674
+ /**
675
+ * Phase 1: Context gathering
676
+ */
677
+ responsePhaseContext(state) {
678
+ const steps = this.getSteps();
679
+ const outputPath = this.getOutputPath();
680
+ let previousOutput;
681
+ if (state.stepIndex > 1) {
682
+ const prevStep = steps[state.stepIndex - 2];
683
+ const prevPath = join4(outputPath, state.taskId, `${prevStep}.md`);
684
+ if (existsSync4(prevPath)) {
685
+ previousOutput = prevPath;
686
+ }
687
+ }
688
+ return {
689
+ type: "phase",
690
+ phase: "context",
691
+ step: state.currentStep,
692
+ stepIndex: state.stepIndex,
693
+ totalSteps: state.totalSteps,
694
+ taskId: state.taskId,
695
+ description: state.description,
696
+ context: {
697
+ instruction: this.getStepInstructionPath(state.currentStep),
698
+ previousOutput
699
+ },
700
+ onComplete: `context-done ${state.taskId}`
701
+ };
702
+ }
703
+ /**
704
+ * Phase 2: Question generation
705
+ */
706
+ responsePhaseClarify(state) {
707
+ return {
708
+ type: "phase",
709
+ phase: "clarify",
710
+ step: state.currentStep,
711
+ taskId: state.taskId,
712
+ description: state.description,
713
+ gatheredContext: state.context || "",
714
+ context: {
715
+ instruction: this.getStepInstructionPath(state.currentStep)
716
+ },
717
+ onComplete: `clarify-done ${state.taskId}`
718
+ };
719
+ }
720
+ /**
721
+ * Phase 3: Document generation
722
+ */
723
+ responsePhaseGenerate(state) {
675
724
  const steps = this.getSteps();
676
725
  const outputPath = this.getOutputPath();
677
726
  let previousOutput;
@@ -685,12 +734,15 @@ var Orchestrator = class {
685
734
  const templatePath = this.getStepTemplatePath(state.currentStep);
686
735
  const hasTemplate = existsSync4(templatePath);
687
736
  return {
688
- type: "step",
737
+ type: "phase",
738
+ phase: "generate",
689
739
  step: state.currentStep,
690
740
  stepIndex: state.stepIndex,
691
741
  totalSteps: state.totalSteps,
692
742
  taskId: state.taskId,
693
743
  description: state.description,
744
+ gatheredContext: state.context || "",
745
+ answers: state.answers,
694
746
  context: {
695
747
  instruction: this.getStepInstructionPath(state.currentStep),
696
748
  template: hasTemplate ? templatePath : void 0,
@@ -698,24 +750,25 @@ var Orchestrator = class {
698
750
  output: join4(outputPath, state.taskId, `${state.currentStep}.md`),
699
751
  revisionFeedback: state.revisionFeedback
700
752
  },
701
- onComplete: `done ${state.taskId}`
753
+ onComplete: `generate-done ${state.taskId}`
702
754
  };
703
755
  }
704
- responseCheckpointClarify(state, questions) {
756
+ /**
757
+ * Checkpoint: Questions need answers
758
+ */
759
+ responseCheckpointClarify(state) {
705
760
  return {
706
761
  type: "checkpoint",
707
762
  checkpoint: "clarify",
708
763
  taskId: state.taskId,
709
764
  step: state.currentStep,
710
- questions: questions.map((q) => ({
711
- id: q.id,
712
- question: q.question,
713
- context: q.context,
714
- options: q.options
715
- })),
765
+ questions: state.questions || [],
716
766
  onComplete: `clarified ${state.taskId} '<answers_json>'`
717
767
  };
718
768
  }
769
+ /**
770
+ * Checkpoint: Document needs approval
771
+ */
719
772
  responseCheckpointApprove(state) {
720
773
  const outputPath = this.getOutputPath();
721
774
  return {
@@ -768,7 +821,7 @@ var Orchestrator = class {
768
821
  // Command Handlers
769
822
  // ===========================================================================
770
823
  /**
771
- * Initialize a new workflow
824
+ * Initialize a new workflow - starts at phase_context
772
825
  */
773
826
  cmdInit(description) {
774
827
  try {
@@ -780,62 +833,107 @@ var Orchestrator = class {
780
833
  currentStep: steps[0],
781
834
  stepIndex: 1,
782
835
  totalSteps: steps.length,
783
- status: "awaiting_spec",
836
+ status: "phase_context",
837
+ phase: "context",
784
838
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
785
839
  };
786
840
  this.saveState(state);
787
- return this.responseStep(state);
841
+ return this.responsePhaseContext(state);
788
842
  } catch (e) {
789
843
  return this.responseError(e.message);
790
844
  }
791
845
  }
792
846
  /**
793
- * Mark current step as done, check for questions or go to approve
847
+ * Phase 1 complete: Context gathered, move to clarify phase
794
848
  */
795
- cmdDone(taskId) {
849
+ cmdContextDone(taskId, context) {
796
850
  const state = this.loadState(taskId);
797
851
  if (!state) {
798
852
  return this.responseError(`No workflow found: ${taskId}`, taskId);
799
853
  }
800
- const specPath = this.getSpecPath(taskId, state.currentStep);
801
- if (!existsSync4(specPath)) {
802
- return this.responseError(`Spec not found: ${specPath}`, taskId, state.currentStep);
854
+ state.context = context;
855
+ state.status = "phase_clarify";
856
+ state.phase = "clarify";
857
+ this.saveState(state);
858
+ return this.responsePhaseClarify(state);
859
+ }
860
+ /**
861
+ * Phase 2 complete: Questions generated (or none)
862
+ * If questions exist → checkpoint
863
+ * If no questions → move to generate phase
864
+ */
865
+ cmdClarifyDone(taskId, questions) {
866
+ const state = this.loadState(taskId);
867
+ if (!state) {
868
+ return this.responseError(`No workflow found: ${taskId}`, taskId);
803
869
  }
804
- const questions = this.checkUnresolvedQuestions(specPath);
805
870
  if (questions.length > 0) {
806
- state.status = "awaiting_clarify";
871
+ state.questions = questions;
872
+ state.status = "clarify_pending";
873
+ state.phase = "clarify";
807
874
  this.saveState(state);
808
- return this.responseCheckpointClarify(state, questions);
875
+ return this.responseCheckpointClarify(state);
809
876
  }
810
- state.status = "awaiting_approve";
877
+ state.status = "phase_generate";
878
+ state.phase = "generate";
811
879
  this.saveState(state);
812
- return this.responseCheckpointApprove(state);
880
+ return this.responsePhaseGenerate(state);
813
881
  }
814
882
  /**
815
- * Submit clarification answers
883
+ * Human answered questions, move to generate phase
816
884
  */
817
885
  cmdClarified(taskId, answers) {
886
+ const state = this.loadState(taskId);
887
+ if (!state) {
888
+ return this.responseError(`No workflow found: ${taskId}`, taskId);
889
+ }
890
+ state.answers = answers;
891
+ state.status = "phase_generate";
892
+ state.phase = "generate";
893
+ this.saveState(state);
894
+ return this.responsePhaseGenerate(state);
895
+ }
896
+ /**
897
+ * Phase 3 complete: Document generated, move to approve checkpoint
898
+ */
899
+ cmdGenerateDone(taskId) {
818
900
  const state = this.loadState(taskId);
819
901
  if (!state) {
820
902
  return this.responseError(`No workflow found: ${taskId}`, taskId);
821
903
  }
822
904
  const specPath = this.getSpecPath(taskId, state.currentStep);
823
- if (existsSync4(specPath)) {
824
- const content = readFileSync4(specPath, "utf-8");
825
- const { content: body, data } = matter2(content);
826
- if (data.open_questions && Array.isArray(data.open_questions)) {
827
- data.open_questions = data.open_questions.map((q, i) => ({
828
- ...q,
829
- resolved: true,
830
- answer: answers.find((a) => a.questionId === `q${i + 1}`)?.answer
831
- }));
832
- }
833
- writeFileSync3(specPath, matter2.stringify(body, data));
905
+ if (!existsSync4(specPath)) {
906
+ return this.responseError(`Document not found: ${specPath}`, taskId, state.currentStep);
907
+ }
908
+ state.status = "approve_pending";
909
+ state.phase = "review";
910
+ this.saveState(state);
911
+ return this.responseCheckpointApprove(state);
912
+ }
913
+ /**
914
+ * @deprecated Use cmdContextDone, cmdClarifyDone, cmdGenerateDone instead
915
+ */
916
+ cmdDone(taskId) {
917
+ const state = this.loadState(taskId);
918
+ if (!state) {
919
+ return this.responseError(`No workflow found: ${taskId}`, taskId);
920
+ }
921
+ const specPath = this.getSpecPath(taskId, state.currentStep);
922
+ if (!existsSync4(specPath)) {
923
+ return this.responseError(`Spec not found: ${specPath}`, taskId, state.currentStep);
924
+ }
925
+ const questions = this.checkUnresolvedQuestions(specPath);
926
+ if (questions.length > 0) {
927
+ state.questions = questions;
928
+ state.status = "clarify_pending";
929
+ state.phase = "clarify";
930
+ this.saveState(state);
931
+ return this.responseCheckpointClarify(state);
834
932
  }
835
- state.status = "awaiting_spec";
836
- state.revisionFeedback = `Answers provided: ${JSON.stringify(answers)}`;
933
+ state.status = "approve_pending";
934
+ state.phase = "review";
837
935
  this.saveState(state);
838
- return this.responseStep(state);
936
+ return this.responseCheckpointApprove(state);
839
937
  }
840
938
  /**
841
939
  * Approve current step and move to next
@@ -857,10 +955,14 @@ var Orchestrator = class {
857
955
  if (state.stepIndex < state.totalSteps) {
858
956
  state.currentStep = steps[state.stepIndex];
859
957
  state.stepIndex += 1;
860
- state.status = "awaiting_spec";
958
+ state.status = "phase_context";
959
+ state.phase = "context";
960
+ state.context = void 0;
961
+ state.questions = void 0;
962
+ state.answers = void 0;
861
963
  state.revisionFeedback = void 0;
862
964
  this.saveState(state);
863
- return this.responseStep(state);
965
+ return this.responsePhaseContext(state);
864
966
  } else {
865
967
  state.status = "completed";
866
968
  this.saveState(state);
@@ -868,17 +970,18 @@ var Orchestrator = class {
868
970
  }
869
971
  }
870
972
  /**
871
- * Request revision with feedback
973
+ * Request revision with feedback - goes back to generate phase
872
974
  */
873
975
  cmdRevise(taskId, feedback) {
874
976
  const state = this.loadState(taskId);
875
977
  if (!state) {
876
978
  return this.responseError(`No workflow found: ${taskId}`, taskId);
877
979
  }
878
- state.status = "awaiting_spec";
980
+ state.status = "phase_generate";
981
+ state.phase = "generate";
879
982
  state.revisionFeedback = feedback;
880
983
  this.saveState(state);
881
- return this.responseStep(state);
984
+ return this.responsePhaseGenerate(state);
882
985
  }
883
986
  /**
884
987
  * Reject and stop workflow
@@ -921,13 +1024,15 @@ var Orchestrator = class {
921
1024
  return this.responseError(`No workflow found: ${taskId}`, taskId);
922
1025
  }
923
1026
  switch (state.status) {
924
- case "awaiting_spec":
925
- return this.responseStep(state);
926
- case "awaiting_clarify": {
927
- const questions = this.checkUnresolvedQuestions(this.getSpecPath(taskId, state.currentStep));
928
- return this.responseCheckpointClarify(state, questions);
929
- }
930
- case "awaiting_approve":
1027
+ case "phase_context":
1028
+ return this.responsePhaseContext(state);
1029
+ case "phase_clarify":
1030
+ return this.responsePhaseClarify(state);
1031
+ case "clarify_pending":
1032
+ return this.responseCheckpointClarify(state);
1033
+ case "phase_generate":
1034
+ return this.responsePhaseGenerate(state);
1035
+ case "approve_pending":
931
1036
  return this.responseCheckpointApprove(state);
932
1037
  case "completed":
933
1038
  case "stopped":
@@ -940,9 +1045,233 @@ var Orchestrator = class {
940
1045
  };
941
1046
 
942
1047
  // src/core/step-executor.ts
943
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
944
- import { join as join5 } from "path";
1048
+ import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
945
1049
  import matter3 from "gray-matter";
1050
+
1051
+ // src/core/prompt-builder.ts
1052
+ import { readFileSync as readFileSync5, existsSync as existsSync5 } from "fs";
1053
+ import { join as join5 } from "path";
1054
+ function buildContextPrompt(params) {
1055
+ const cwd = params.cwd || process.cwd();
1056
+ const stepsDir = getStepsDir(cwd);
1057
+ const isFirstStep = params.stepIndex === 1;
1058
+ const instructionPath = join5(stepsDir, params.step, "instruction.md");
1059
+ const instruction = existsSync5(instructionPath) ? readFileSync5(instructionPath, "utf-8") : "";
1060
+ const parts = [];
1061
+ parts.push("# Context Gathering Phase");
1062
+ parts.push("");
1063
+ parts.push("Your task is to gather context before generating a document.");
1064
+ parts.push("Do NOT generate the document yet - only gather information.");
1065
+ parts.push("");
1066
+ parts.push("## Task Information");
1067
+ parts.push("");
1068
+ parts.push(`- **Task ID**: ${params.taskId}`);
1069
+ parts.push(`- **Current Step**: ${params.step} (${params.stepIndex}/${params.totalSteps})`);
1070
+ parts.push("");
1071
+ if (isFirstStep) {
1072
+ parts.push("## User Request");
1073
+ parts.push("");
1074
+ parts.push(`> ${params.description}`);
1075
+ parts.push("");
1076
+ } else if (params.previousOutput) {
1077
+ parts.push("## Previous Step Output");
1078
+ parts.push("");
1079
+ if (existsSync5(params.previousOutput)) {
1080
+ parts.push(readFileSync5(params.previousOutput, "utf-8"));
1081
+ }
1082
+ parts.push("");
1083
+ }
1084
+ if (instruction) {
1085
+ parts.push("## Step Instruction (Preview)");
1086
+ parts.push("");
1087
+ parts.push("The next phase will use this instruction to generate a document:");
1088
+ parts.push("");
1089
+ parts.push("```");
1090
+ parts.push(instruction.slice(0, 500) + (instruction.length > 500 ? "..." : ""));
1091
+ parts.push("```");
1092
+ parts.push("");
1093
+ }
1094
+ parts.push("## Your Task");
1095
+ parts.push("");
1096
+ parts.push("1. **Analyze** the request/input to understand what is being asked");
1097
+ parts.push("2. **Search** the codebase for relevant files, patterns, conventions");
1098
+ parts.push("3. **Identify** key constraints, dependencies, and considerations");
1099
+ parts.push("");
1100
+ parts.push("## Output Format");
1101
+ parts.push("");
1102
+ parts.push("Output a JSON object with this structure:");
1103
+ parts.push("");
1104
+ parts.push("```json");
1105
+ parts.push("{");
1106
+ parts.push(' "summary": "Brief summary of what you understood and found",');
1107
+ parts.push(' "relevantFiles": ["path/to/file1.ts", "path/to/file2.ts"],');
1108
+ parts.push(' "keyFindings": [');
1109
+ parts.push(' "Finding 1: description",');
1110
+ parts.push(' "Finding 2: description"');
1111
+ parts.push(" ]");
1112
+ parts.push("}");
1113
+ parts.push("```");
1114
+ parts.push("");
1115
+ parts.push("**Important:** Output ONLY the JSON, no other text.");
1116
+ parts.push("");
1117
+ return parts.join("\n");
1118
+ }
1119
+ function buildClarifyPrompt(params) {
1120
+ const cwd = params.cwd || process.cwd();
1121
+ const stepsDir = getStepsDir(cwd);
1122
+ const instructionPath = join5(stepsDir, params.step, "instruction.md");
1123
+ const instruction = existsSync5(instructionPath) ? readFileSync5(instructionPath, "utf-8") : "";
1124
+ const parts = [];
1125
+ parts.push("# Clarify Phase");
1126
+ parts.push("");
1127
+ parts.push("Your task is to identify any questions that need user input before generating the document.");
1128
+ parts.push("Do NOT generate the document yet - only generate questions if needed.");
1129
+ parts.push("");
1130
+ parts.push("## Task Information");
1131
+ parts.push("");
1132
+ parts.push(`- **Task ID**: ${params.taskId}`);
1133
+ parts.push(`- **Current Step**: ${params.step}`);
1134
+ parts.push(`- **Description**: ${params.description}`);
1135
+ parts.push("");
1136
+ parts.push("## Gathered Context");
1137
+ parts.push("");
1138
+ parts.push(params.gatheredContext);
1139
+ parts.push("");
1140
+ if (instruction) {
1141
+ parts.push("## Step Instruction");
1142
+ parts.push("");
1143
+ parts.push(instruction);
1144
+ parts.push("");
1145
+ }
1146
+ parts.push("## Consider These Questions");
1147
+ parts.push("");
1148
+ parts.push("1. Is the task description clear enough to proceed?");
1149
+ parts.push("2. Are there multiple valid approaches that need user decision?");
1150
+ parts.push("3. Are there ambiguities that could lead to wrong implementation?");
1151
+ parts.push("4. Are there missing requirements or constraints?");
1152
+ parts.push("");
1153
+ parts.push("## Output Format");
1154
+ parts.push("");
1155
+ parts.push("Output a JSON object with questions array:");
1156
+ parts.push("");
1157
+ parts.push("```json");
1158
+ parts.push("{");
1159
+ parts.push(' "questions": [');
1160
+ parts.push(" {");
1161
+ parts.push(' "id": "q1",');
1162
+ parts.push(' "question": "Your specific question?",');
1163
+ parts.push(' "context": "Why this matters for the document",');
1164
+ parts.push(' "options": ["Option A", "Option B"] // optional');
1165
+ parts.push(" }");
1166
+ parts.push(" ]");
1167
+ parts.push("}");
1168
+ parts.push("```");
1169
+ parts.push("");
1170
+ parts.push('If no questions are needed, return: `{"questions": []}`');
1171
+ parts.push("");
1172
+ parts.push("**Important:** Output ONLY the JSON, no other text.");
1173
+ parts.push("");
1174
+ return parts.join("\n");
1175
+ }
1176
+ function buildGeneratePrompt(params) {
1177
+ const cwd = params.cwd || process.cwd();
1178
+ const config = loadConfig(cwd);
1179
+ const stepsDir = getStepsDir(cwd);
1180
+ const outputsDir = getOutputsDir(cwd);
1181
+ const isFirstStep = params.stepIndex === 1;
1182
+ const prevStep = params.stepIndex > 1 ? config.steps[params.stepIndex - 2] : null;
1183
+ const instructionPath = join5(stepsDir, params.step, "instruction.md");
1184
+ const templatePath = join5(stepsDir, params.step, "template.md");
1185
+ const outputPath = join5(outputsDir, params.taskId, `${params.step}.md`);
1186
+ if (!existsSync5(instructionPath)) {
1187
+ throw new Error(`Instruction not found: ${instructionPath}`);
1188
+ }
1189
+ const instruction = readFileSync5(instructionPath, "utf-8");
1190
+ const template = existsSync5(templatePath) ? readFileSync5(templatePath, "utf-8") : null;
1191
+ let previousSpec = null;
1192
+ if (prevStep) {
1193
+ const prevPath = join5(outputsDir, params.taskId, `${prevStep}.md`);
1194
+ if (existsSync5(prevPath)) {
1195
+ previousSpec = {
1196
+ step: prevStep,
1197
+ content: readFileSync5(prevPath, "utf-8")
1198
+ };
1199
+ }
1200
+ }
1201
+ const parts = [];
1202
+ parts.push("# Document Generation Phase");
1203
+ parts.push("");
1204
+ parts.push("Generate the document based on the gathered context and user answers.");
1205
+ parts.push("");
1206
+ parts.push("## Step Instruction");
1207
+ parts.push("");
1208
+ parts.push(instruction);
1209
+ parts.push("");
1210
+ if (template) {
1211
+ parts.push("## Document Template");
1212
+ parts.push("");
1213
+ parts.push("**CRITICAL: You MUST follow this template structure completely.**");
1214
+ parts.push("");
1215
+ parts.push(template);
1216
+ parts.push("");
1217
+ }
1218
+ parts.push("## Gathered Context");
1219
+ parts.push("");
1220
+ parts.push(params.gatheredContext);
1221
+ parts.push("");
1222
+ if (params.answers && params.answers.length > 0) {
1223
+ parts.push("## User Answers");
1224
+ parts.push("");
1225
+ for (const answer of params.answers) {
1226
+ parts.push(`- **${answer.questionId}**: ${answer.answer}`);
1227
+ }
1228
+ parts.push("");
1229
+ }
1230
+ if (isFirstStep) {
1231
+ parts.push("## User Request");
1232
+ parts.push("");
1233
+ parts.push(`> ${params.description}`);
1234
+ parts.push("");
1235
+ } else if (previousSpec) {
1236
+ parts.push(`## Input: ${previousSpec.step}.md`);
1237
+ parts.push("");
1238
+ parts.push(previousSpec.content);
1239
+ parts.push("");
1240
+ }
1241
+ parts.push("## Task Context");
1242
+ parts.push("");
1243
+ parts.push(`- **Task ID**: ${params.taskId}`);
1244
+ parts.push(`- **Current Step**: ${params.step} (${params.stepIndex}/${params.totalSteps})`);
1245
+ parts.push("");
1246
+ if (params.revisionFeedback) {
1247
+ parts.push("## Revision Feedback");
1248
+ parts.push("");
1249
+ parts.push("Previous version was not approved. Please revise based on this feedback:");
1250
+ parts.push("");
1251
+ parts.push(`> ${params.revisionFeedback}`);
1252
+ parts.push("");
1253
+ }
1254
+ parts.push("## Output Instructions");
1255
+ parts.push("");
1256
+ parts.push(`Generate the document and save to: \`${outputPath}\``);
1257
+ parts.push("");
1258
+ parts.push("**Required frontmatter:**");
1259
+ parts.push("```yaml");
1260
+ parts.push("---");
1261
+ parts.push(`id: ${params.taskId}`);
1262
+ parts.push(`step: ${params.step}`);
1263
+ parts.push("status: pending_approval");
1264
+ parts.push("---");
1265
+ parts.push("```");
1266
+ parts.push("");
1267
+ return {
1268
+ prompt: parts.join("\n"),
1269
+ outputPath
1270
+ };
1271
+ }
1272
+
1273
+ // src/core/step-executor.ts
1274
+ import { join as join6 } from "path";
946
1275
  var StepExecutor = class {
947
1276
  adapter;
948
1277
  config;
@@ -952,132 +1281,237 @@ var StepExecutor = class {
952
1281
  this.config = config;
953
1282
  this.cwd = cwd;
954
1283
  }
1284
+ // ==========================================================================
1285
+ // Phase 1: Context Gathering
1286
+ // ==========================================================================
1287
+ /**
1288
+ * Execute context phase - AI gathers context
1289
+ * Returns gathered context as string
1290
+ */
1291
+ async executeContextPhase(step, context) {
1292
+ this.adapter.io.notify(`Phase 1/4: Gathering context for ${step}`, "info");
1293
+ const params = {
1294
+ taskId: context.taskId,
1295
+ step,
1296
+ description: context.description,
1297
+ stepIndex: context.stepIndex,
1298
+ totalSteps: context.totalSteps,
1299
+ previousOutput: context.previousOutput,
1300
+ cwd: this.cwd
1301
+ };
1302
+ const prompt = buildContextPrompt(params);
1303
+ const response = await this.adapter.ai.execute({
1304
+ prompt,
1305
+ outputPath: ""
1306
+ // No file output needed
1307
+ });
1308
+ const contextOutput = this.parseContextOutput(response || "");
1309
+ return {
1310
+ phase: "context",
1311
+ context: JSON.stringify(contextOutput)
1312
+ };
1313
+ }
1314
+ // ==========================================================================
1315
+ // Phase 2: Clarify (Question Generation)
1316
+ // ==========================================================================
1317
+ /**
1318
+ * Execute clarify phase - AI generates questions
1319
+ * Returns questions (may be empty)
1320
+ */
1321
+ async executeClarifyPhase(step, context) {
1322
+ this.adapter.io.notify(`Phase 2/4: Generating questions for ${step}`, "info");
1323
+ if (!context.gatheredContext) {
1324
+ throw new Error("Context phase must be completed before clarify phase");
1325
+ }
1326
+ const params = {
1327
+ taskId: context.taskId,
1328
+ step,
1329
+ description: context.description,
1330
+ gatheredContext: context.gatheredContext,
1331
+ cwd: this.cwd
1332
+ };
1333
+ const prompt = buildClarifyPrompt(params);
1334
+ const response = await this.adapter.ai.execute({
1335
+ prompt,
1336
+ outputPath: ""
1337
+ // No file output needed
1338
+ });
1339
+ const clarifyOutput = this.parseClarifyOutput(response || "");
1340
+ if (clarifyOutput.questions.length > 0) {
1341
+ this.adapter.io.notify(`${clarifyOutput.questions.length} question(s) need answers`, "warning");
1342
+ } else {
1343
+ this.adapter.io.notify("No questions needed, proceeding to generate", "info");
1344
+ }
1345
+ return {
1346
+ phase: "clarify",
1347
+ questions: clarifyOutput.questions
1348
+ };
1349
+ }
1350
+ /**
1351
+ * Ask questions to human
1352
+ * Returns answers (or pending if async mode)
1353
+ */
1354
+ async askQuestions(questions) {
1355
+ const answers = await this.adapter.io.askMultiple(questions);
1356
+ if (answers.length === 0) {
1357
+ return { phase: "clarify", questions, pending: true };
1358
+ }
1359
+ return { phase: "clarify", questions: [] };
1360
+ }
1361
+ // ==========================================================================
1362
+ // Phase 3: Generate Document
1363
+ // ==========================================================================
1364
+ /**
1365
+ * Execute generate phase - AI creates document
1366
+ */
1367
+ async executeGeneratePhase(step, context) {
1368
+ this.adapter.io.notify(`Phase 3/4: Generating document for ${step}`, "info");
1369
+ if (!context.gatheredContext) {
1370
+ throw new Error("Context phase must be completed before generate phase");
1371
+ }
1372
+ const params = {
1373
+ taskId: context.taskId,
1374
+ step,
1375
+ description: context.description,
1376
+ stepIndex: context.stepIndex,
1377
+ totalSteps: context.totalSteps,
1378
+ gatheredContext: context.gatheredContext,
1379
+ answers: context.answers,
1380
+ revisionFeedback: context.revisionFeedback,
1381
+ cwd: this.cwd
1382
+ };
1383
+ const { prompt, outputPath } = buildGeneratePrompt(params);
1384
+ await this.adapter.ai.execute({ prompt, outputPath });
1385
+ if (!existsSync6(outputPath)) {
1386
+ throw new Error(`AI did not create document at ${outputPath}`);
1387
+ }
1388
+ this.adapter.io.notify(`Document created: ${outputPath}`, "success");
1389
+ return { phase: "generate" };
1390
+ }
1391
+ // ==========================================================================
1392
+ // Phase 4: Review (Approval)
1393
+ // ==========================================================================
1394
+ /**
1395
+ * Execute review phase - human approves/revises/rejects
1396
+ */
1397
+ async executeReviewPhase(step, context) {
1398
+ this.adapter.io.notify(`Phase 4/4: Review for ${step}`, "info");
1399
+ const outputsDir = getOutputsDir(this.cwd);
1400
+ const outputPath = join6(outputsDir, context.taskId, `${step}.md`);
1401
+ if (!existsSync6(outputPath)) {
1402
+ throw new Error(`Document not found: ${outputPath}`);
1403
+ }
1404
+ const approval = await this.adapter.io.approve(
1405
+ outputPath,
1406
+ step,
1407
+ context.stepIndex,
1408
+ context.totalSteps
1409
+ );
1410
+ if (approval.pending) {
1411
+ return { phase: "review", pending: true };
1412
+ }
1413
+ if (approval.action === "approve") {
1414
+ this.updateDocumentStatus(outputPath, "approved");
1415
+ this.adapter.io.notify(`Step ${step} approved`, "success");
1416
+ return { phase: "review", approved: true };
1417
+ }
1418
+ if (approval.action === "stop") {
1419
+ this.adapter.io.notify(`Workflow stopped at step ${step}`, "info");
1420
+ return { phase: "review", stopped: true };
1421
+ }
1422
+ if (approval.action === "reject") {
1423
+ this.updateDocumentStatus(outputPath, "rejected");
1424
+ this.adapter.io.notify(`Step ${step} rejected`, "error");
1425
+ return { phase: "review", rejected: true };
1426
+ }
1427
+ this.adapter.io.notify(`Revision requested for step ${step}`, "info");
1428
+ return {
1429
+ phase: "review",
1430
+ revisionFeedback: approval.feedback || "Please revise the document"
1431
+ };
1432
+ }
1433
+ // ==========================================================================
1434
+ // Full Step Execution (convenience method)
1435
+ // ==========================================================================
955
1436
  /**
956
- * Execute a single step
1437
+ * Execute all phases of a step
1438
+ * For synchronous environments (CLI) - runs through all phases
1439
+ * For async environments (GitHub) - may return pending
957
1440
  *
958
- * Flow:
959
- * 1. Generate document (AI)
960
- * 2. Check for open_questions in frontmatter
961
- * 3. If questions exist, ask user and regenerate
962
- * 4. Request approval
963
- * 5. Return result
1441
+ * @deprecated Use individual phase methods for 4-phase control
964
1442
  */
965
1443
  async execute(step, context) {
966
- const stepsDir = getStepsDir(this.cwd);
967
- const outputsDir = getOutputsDir(this.cwd);
968
- const instructionPath = join5(stepsDir, step, "instruction.md");
969
- const templatePath = join5(stepsDir, step, "template.md");
970
- const outputPath = join5(outputsDir, context.taskId, `${step}.md`);
971
- let previousOutputPath;
972
- if (context.stepIndex > 1) {
973
- const steps = this.config.steps;
974
- const prevStep = steps[context.stepIndex - 2];
975
- const prevPath = join5(outputsDir, context.taskId, `${prevStep}.md`);
976
- if (existsSync5(prevPath)) {
977
- previousOutputPath = prevPath;
978
- }
979
- }
980
- this.adapter.io.notify(`Starting step: ${step}`, "info");
981
1444
  let attempts = 0;
982
1445
  const maxAttempts = 10;
983
- let revisionFeedback = context.revisionFeedback;
1446
+ let currentContext = { ...context };
984
1447
  while (attempts < maxAttempts) {
985
1448
  attempts++;
986
- const converseParams = {
987
- instructionPath,
988
- templatePath: existsSync5(templatePath) ? templatePath : void 0,
989
- previousOutputPath,
990
- outputPath,
991
- taskId: context.taskId,
992
- description: context.description,
993
- revisionFeedback
994
- };
995
- await this.adapter.ai.converse(converseParams);
996
- if (!existsSync5(outputPath)) {
997
- throw new Error(`AI did not create document at ${outputPath}`);
998
- }
999
- const questions = this.getUnresolvedQuestions(outputPath);
1000
- if (questions.length > 0) {
1001
- this.adapter.io.notify(`${questions.length} question(s) need answers`, "warning");
1002
- const answers = await this.adapter.io.askMultiple(questions);
1003
- this.resolveQuestions(outputPath, answers);
1004
- revisionFeedback = `User answered questions:
1005
- ${answers.map((a) => `- ${a.questionId}: ${a.answer}`).join("\n")}
1006
-
1007
- Regenerate the document incorporating these answers.`;
1008
- this.adapter.io.notify("Regenerating with answers...", "info");
1009
- continue;
1449
+ if (!currentContext.gatheredContext) {
1450
+ const contextResult = await this.executeContextPhase(step, currentContext);
1451
+ currentContext.gatheredContext = contextResult.context;
1010
1452
  }
1011
- const approval = await this.adapter.io.approve(
1012
- outputPath,
1013
- step,
1014
- context.stepIndex,
1015
- context.totalSteps
1016
- );
1017
- if (approval.action === "approve") {
1018
- this.updateDocumentStatus(outputPath, "approved");
1019
- this.adapter.io.notify(`Step ${step} approved`, "success");
1020
- return { approved: true, rejected: false, stopped: false };
1453
+ const clarifyResult = await this.executeClarifyPhase(step, currentContext);
1454
+ if (clarifyResult.questions && clarifyResult.questions.length > 0) {
1455
+ const answers = await this.adapter.io.askMultiple(clarifyResult.questions);
1456
+ if (answers.length === 0) {
1457
+ return { phase: "clarify", questions: clarifyResult.questions, pending: true };
1458
+ }
1459
+ currentContext.answers = answers;
1021
1460
  }
1022
- if (approval.action === "stop") {
1023
- this.adapter.io.notify(`Workflow stopped at step ${step}`, "info");
1024
- return { approved: false, rejected: false, stopped: true };
1461
+ await this.executeGeneratePhase(step, currentContext);
1462
+ const reviewResult = await this.executeReviewPhase(step, currentContext);
1463
+ if (reviewResult.pending) {
1464
+ return reviewResult;
1025
1465
  }
1026
- if (approval.action === "reject") {
1027
- this.updateDocumentStatus(outputPath, "rejected");
1028
- this.adapter.io.notify(`Step ${step} rejected`, "error");
1029
- return { approved: false, rejected: true, stopped: false };
1466
+ if (reviewResult.approved || reviewResult.rejected || reviewResult.stopped) {
1467
+ return reviewResult;
1030
1468
  }
1031
- revisionFeedback = approval.feedback || "Please revise the document";
1469
+ currentContext.revisionFeedback = reviewResult.revisionFeedback;
1032
1470
  this.adapter.io.notify(`Revising step ${step}...`, "info");
1033
1471
  }
1034
1472
  throw new Error(`Maximum revision attempts (${maxAttempts}) exceeded for step ${step}`);
1035
1473
  }
1474
+ // ==========================================================================
1475
+ // Helpers
1476
+ // ==========================================================================
1036
1477
  /**
1037
- * Get unresolved questions from document frontmatter
1478
+ * Parse context phase output (JSON)
1038
1479
  */
1039
- getUnresolvedQuestions(docPath) {
1040
- const content = readFileSync5(docPath, "utf-8");
1041
- const { data } = matter3(content);
1042
- const questions = [];
1043
- if (data.open_questions && Array.isArray(data.open_questions)) {
1044
- for (let i = 0; i < data.open_questions.length; i++) {
1045
- const q = data.open_questions[i];
1046
- if (!q.resolved) {
1047
- questions.push({
1048
- id: `q${i + 1}`,
1049
- question: q.question,
1050
- context: q.context,
1051
- options: q.options
1052
- });
1053
- }
1480
+ parseContextOutput(response) {
1481
+ try {
1482
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
1483
+ if (jsonMatch) {
1484
+ return JSON.parse(jsonMatch[0]);
1054
1485
  }
1486
+ } catch {
1055
1487
  }
1056
- return questions;
1488
+ return {
1489
+ summary: response,
1490
+ relevantFiles: [],
1491
+ keyFindings: []
1492
+ };
1057
1493
  }
1058
1494
  /**
1059
- * Mark questions as resolved and add answers to frontmatter
1495
+ * Parse clarify phase output (JSON)
1060
1496
  */
1061
- resolveQuestions(docPath, answers) {
1062
- const content = readFileSync5(docPath, "utf-8");
1063
- const { content: body, data } = matter3(content);
1064
- if (data.open_questions && Array.isArray(data.open_questions)) {
1065
- data.open_questions = data.open_questions.map((q, i) => {
1066
- const answer = answers.find((a) => a.questionId === `q${i + 1}`);
1067
- return {
1068
- ...q,
1069
- resolved: true,
1070
- answer: answer?.answer
1071
- };
1072
- });
1497
+ parseClarifyOutput(response) {
1498
+ try {
1499
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
1500
+ if (jsonMatch) {
1501
+ const parsed = JSON.parse(jsonMatch[0]);
1502
+ if (Array.isArray(parsed.questions)) {
1503
+ return parsed;
1504
+ }
1505
+ }
1506
+ } catch {
1073
1507
  }
1074
- writeFileSync4(docPath, matter3.stringify(body, data));
1508
+ return { questions: [] };
1075
1509
  }
1076
1510
  /**
1077
1511
  * Update document status in frontmatter
1078
1512
  */
1079
1513
  updateDocumentStatus(docPath, status) {
1080
- const content = readFileSync5(docPath, "utf-8");
1514
+ const content = readFileSync6(docPath, "utf-8");
1081
1515
  const { content: body, data } = matter3(content);
1082
1516
  data.status = status;
1083
1517
  data.updated_at = (/* @__PURE__ */ new Date()).toISOString();
@@ -1087,69 +1521,26 @@ Regenerate the document incorporating these answers.`;
1087
1521
 
1088
1522
  // src/adapters/cli.ts
1089
1523
  import { spawn, spawnSync } from "child_process";
1090
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
1091
- import { dirname as dirname4 } from "path";
1524
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
1525
+ import { dirname as dirname3 } from "path";
1092
1526
  import { input, select, confirm } from "@inquirer/prompts";
1093
1527
  var CLIAIAdapter = class {
1094
1528
  claudeCommand;
1095
1529
  constructor(claudeCommand = "claude") {
1096
1530
  this.claudeCommand = claudeCommand;
1097
1531
  }
1098
- async converse(params) {
1099
- const prompt = this.buildPrompt(params);
1532
+ async execute(params) {
1100
1533
  console.log(`
1101
- \u{1F4DD} Generating document for step...`);
1102
- const response = await this.callClaude(prompt);
1103
- const outputDir = dirname4(params.outputPath);
1104
- if (!existsSync6(outputDir)) {
1105
- mkdirSync4(outputDir, { recursive: true });
1106
- }
1107
- writeFileSync5(params.outputPath, response);
1108
- }
1109
- buildPrompt(params) {
1110
- const parts = [];
1111
- const instruction = readFileSync6(params.instructionPath, "utf-8");
1112
- parts.push("# Task");
1113
- parts.push(`You are generating a document as part of a spec-driven development workflow.`);
1114
- parts.push("");
1115
- parts.push("## Task Description");
1116
- parts.push(params.description);
1117
- parts.push("");
1118
- parts.push("## Instructions");
1119
- parts.push(instruction);
1120
- parts.push("");
1121
- if (params.templatePath && existsSync6(params.templatePath)) {
1122
- const template = readFileSync6(params.templatePath, "utf-8");
1123
- parts.push("## Output Template");
1124
- parts.push("Follow this template structure:");
1125
- parts.push(template);
1126
- parts.push("");
1127
- }
1128
- if (params.previousOutputPath && existsSync6(params.previousOutputPath)) {
1129
- const previousOutput = readFileSync6(params.previousOutputPath, "utf-8");
1130
- parts.push("## Previous Step Output");
1131
- parts.push(previousOutput);
1132
- parts.push("");
1133
- }
1134
- if (params.revisionFeedback) {
1135
- parts.push("## Revision Feedback");
1136
- parts.push(params.revisionFeedback);
1137
- parts.push("");
1138
- }
1139
- parts.push("## Output Format");
1140
- parts.push("Generate the document content directly.");
1141
- parts.push("If you have clarifying questions that MUST be answered, include them in the frontmatter as:");
1142
- parts.push("```yaml");
1143
- parts.push("---");
1144
- parts.push("status: draft");
1145
- parts.push("open_questions:");
1146
- parts.push(' - question: "Your question here"');
1147
- parts.push(" resolved: false");
1148
- parts.push("---");
1149
- parts.push("```");
1150
- parts.push("");
1151
- parts.push("Only ask questions if absolutely necessary.");
1152
- return parts.join("\n");
1534
+ \u{1F4DD} Generating...`);
1535
+ const response = await this.callClaude(params.prompt);
1536
+ if (params.outputPath) {
1537
+ const outputDir = dirname3(params.outputPath);
1538
+ if (!existsSync7(outputDir)) {
1539
+ mkdirSync4(outputDir, { recursive: true });
1540
+ }
1541
+ writeFileSync5(params.outputPath, response);
1542
+ }
1543
+ return response;
1153
1544
  }
1154
1545
  async callClaude(prompt) {
1155
1546
  return new Promise((resolve, reject) => {
@@ -1216,8 +1607,8 @@ Context: ${question.context}`);
1216
1607
  return answers;
1217
1608
  }
1218
1609
  async approve(specPath, stepName, stepIndex, totalSteps) {
1219
- if (existsSync6(specPath)) {
1220
- const doc = readFileSync6(specPath, "utf-8");
1610
+ if (existsSync7(specPath)) {
1611
+ const doc = readFileSync7(specPath, "utf-8");
1221
1612
  console.log("\n" + "=".repeat(60));
1222
1613
  console.log(`\u{1F4C4} ${stepName} Document (Step ${stepIndex}/${totalSteps})`);
1223
1614
  console.log("=".repeat(60));
@@ -1262,17 +1653,17 @@ Context: ${question.context}`);
1262
1653
  };
1263
1654
  var CLISystemAdapter = class {
1264
1655
  readFile(path) {
1265
- return readFileSync6(path, "utf-8");
1656
+ return readFileSync7(path, "utf-8");
1266
1657
  }
1267
1658
  writeFile(path, content) {
1268
- const dir = dirname4(path);
1269
- if (!existsSync6(dir)) {
1659
+ const dir = dirname3(path);
1660
+ if (!existsSync7(dir)) {
1270
1661
  mkdirSync4(dir, { recursive: true });
1271
1662
  }
1272
1663
  writeFileSync5(path, content);
1273
1664
  }
1274
1665
  fileExists(path) {
1275
- return existsSync6(path);
1666
+ return existsSync7(path);
1276
1667
  }
1277
1668
  exec(command) {
1278
1669
  try {
@@ -1303,76 +1694,25 @@ function createCLIAdapter(claudeCommand = "claude") {
1303
1694
 
1304
1695
  // src/adapters/github.ts
1305
1696
  import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
1306
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
1307
- import { dirname as dirname5 } from "path";
1308
- var PauseForInputError = class extends Error {
1309
- constructor(inputType, data) {
1310
- super(`Paused waiting for ${inputType}`);
1311
- this.inputType = inputType;
1312
- this.data = data;
1313
- this.name = "PauseForInputError";
1314
- }
1315
- };
1697
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
1698
+ import { dirname as dirname4 } from "path";
1316
1699
  var GitHubAIAdapter = class {
1317
1700
  claudeCommand;
1318
1701
  constructor(claudeCommand = "claude") {
1319
1702
  this.claudeCommand = claudeCommand;
1320
1703
  }
1321
- async converse(params) {
1322
- const prompt = this.buildPrompt(params);
1704
+ async execute(params) {
1323
1705
  console.log(`
1324
- \u{1F4DD} Generating document for step...`);
1325
- const response = await this.callClaude(prompt);
1326
- const outputDir = dirname5(params.outputPath);
1327
- if (!existsSync7(outputDir)) {
1328
- mkdirSync5(outputDir, { recursive: true });
1329
- }
1330
- writeFileSync6(params.outputPath, response);
1331
- }
1332
- buildPrompt(params) {
1333
- const parts = [];
1334
- const instruction = readFileSync7(params.instructionPath, "utf-8");
1335
- parts.push("# Task");
1336
- parts.push(`You are generating a document as part of a spec-driven development workflow.`);
1337
- parts.push("");
1338
- parts.push("## Task Description");
1339
- parts.push(params.description);
1340
- parts.push("");
1341
- parts.push("## Instructions");
1342
- parts.push(instruction);
1343
- parts.push("");
1344
- if (params.templatePath && existsSync7(params.templatePath)) {
1345
- const template = readFileSync7(params.templatePath, "utf-8");
1346
- parts.push("## Output Template");
1347
- parts.push("Follow this template structure:");
1348
- parts.push(template);
1349
- parts.push("");
1350
- }
1351
- if (params.previousOutputPath && existsSync7(params.previousOutputPath)) {
1352
- const previousOutput = readFileSync7(params.previousOutputPath, "utf-8");
1353
- parts.push("## Previous Step Output");
1354
- parts.push(previousOutput);
1355
- parts.push("");
1356
- }
1357
- if (params.revisionFeedback) {
1358
- parts.push("## Revision Feedback");
1359
- parts.push(params.revisionFeedback);
1360
- parts.push("");
1361
- }
1362
- parts.push("## Output Format");
1363
- parts.push("Generate the document content directly.");
1364
- parts.push("If you have clarifying questions that MUST be answered, include them in the frontmatter as:");
1365
- parts.push("```yaml");
1366
- parts.push("---");
1367
- parts.push("status: draft");
1368
- parts.push("open_questions:");
1369
- parts.push(' - question: "Your question here"');
1370
- parts.push(" resolved: false");
1371
- parts.push("---");
1372
- parts.push("```");
1373
- parts.push("");
1374
- parts.push("Only ask questions if absolutely necessary.");
1375
- return parts.join("\n");
1706
+ \u{1F4DD} Generating...`);
1707
+ const response = await this.callClaude(params.prompt);
1708
+ if (params.outputPath) {
1709
+ const outputDir = dirname4(params.outputPath);
1710
+ if (!existsSync8(outputDir)) {
1711
+ mkdirSync5(outputDir, { recursive: true });
1712
+ }
1713
+ writeFileSync6(params.outputPath, response);
1714
+ }
1715
+ return response;
1376
1716
  }
1377
1717
  async callClaude(prompt) {
1378
1718
  return new Promise((resolve, reject) => {
@@ -1417,22 +1757,38 @@ var GitHubIOAdapter = class {
1417
1757
  this.taskId = taskId;
1418
1758
  }
1419
1759
  async ask(question) {
1420
- throw new PauseForInputError("questions", [question]);
1760
+ console.log(`\u{1F4DD} Question queued: ${question.question}`);
1761
+ return "";
1421
1762
  }
1422
1763
  async askMultiple(questions) {
1423
1764
  const comment = this.formatQuestionsComment(questions);
1424
1765
  await this.postComment(comment);
1425
- console.log("\n\u23F8\uFE0F Waiting for answers on GitHub...");
1426
- throw new PauseForInputError("questions", questions);
1766
+ console.log("\n\u23F8\uFE0F Questions posted to GitHub. Waiting for answers...");
1767
+ console.log(" Answer with /answer command on the Issue/PR.");
1768
+ this.pendingQuestions = questions;
1769
+ return [];
1427
1770
  }
1428
1771
  async approve(specPath, stepName, stepIndex, totalSteps) {
1429
- const doc = existsSync7(specPath) ? readFileSync7(specPath, "utf-8") : "";
1772
+ const doc = existsSync8(specPath) ? readFileSync8(specPath, "utf-8") : "";
1430
1773
  const comment = this.formatApprovalComment(doc, stepName, stepIndex, totalSteps, specPath);
1431
1774
  await this.postComment(comment);
1432
- console.log("\n\u23F8\uFE0F Waiting for approval on GitHub...");
1775
+ console.log("\n\u23F8\uFE0F Approval request posted to GitHub.");
1433
1776
  console.log(" Comment /approve, /revise <feedback>, or /reject on the Issue/PR.");
1434
- return { action: "stop" };
1777
+ return { action: "stop", pending: true };
1435
1778
  }
1779
+ /**
1780
+ * Check if there are pending questions awaiting external input
1781
+ */
1782
+ hasPendingQuestions() {
1783
+ return this.pendingQuestions.length > 0;
1784
+ }
1785
+ /**
1786
+ * Get pending questions for serialization
1787
+ */
1788
+ getPendingQuestions() {
1789
+ return this.pendingQuestions;
1790
+ }
1791
+ pendingQuestions = [];
1436
1792
  notify(message, type) {
1437
1793
  const icons = {
1438
1794
  info: "\u2139\uFE0F",
@@ -1527,17 +1883,17 @@ var GitHubIOAdapter = class {
1527
1883
  };
1528
1884
  var GitHubSystemAdapter = class {
1529
1885
  readFile(path) {
1530
- return readFileSync7(path, "utf-8");
1886
+ return readFileSync8(path, "utf-8");
1531
1887
  }
1532
1888
  writeFile(path, content) {
1533
- const dir = dirname5(path);
1534
- if (!existsSync7(dir)) {
1889
+ const dir = dirname4(path);
1890
+ if (!existsSync8(dir)) {
1535
1891
  mkdirSync5(dir, { recursive: true });
1536
1892
  }
1537
1893
  writeFileSync6(path, content);
1538
1894
  }
1539
1895
  fileExists(path) {
1540
- return existsSync7(path);
1896
+ return existsSync8(path);
1541
1897
  }
1542
1898
  exec(command) {
1543
1899
  try {
@@ -1573,7 +1929,7 @@ function getGitHubInfo(cwd) {
1573
1929
  return { owner: config.owner, repo: config.repo };
1574
1930
  }
1575
1931
  try {
1576
- const remoteUrl = execSync3("git remote get-url origin", { encoding: "utf-8", cwd }).trim();
1932
+ const remoteUrl = execSync2("git remote get-url origin", { encoding: "utf-8", cwd }).trim();
1577
1933
  const sshMatch = remoteUrl.match(/git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
1578
1934
  if (sshMatch) {
1579
1935
  return { owner: sshMatch[1], repo: sshMatch[2] };
@@ -1588,7 +1944,7 @@ function getGitHubInfo(cwd) {
1588
1944
  }
1589
1945
  function getCurrentBranch() {
1590
1946
  try {
1591
- return execSync3("git branch --show-current", { encoding: "utf-8" }).trim() || void 0;
1947
+ return execSync2("git branch --show-current", { encoding: "utf-8" }).trim() || void 0;
1592
1948
  } catch {
1593
1949
  return void 0;
1594
1950
  }
@@ -1643,7 +1999,7 @@ async function startCommand(query, options) {
1643
1999
  console.log(`
1644
2000
  \u{1F680} Starting workflow: ${query}
1645
2001
  `);
1646
- if (response.type === "step") {
2002
+ if (response.type === "phase" && response.phase === "context") {
1647
2003
  taskId = response.taskId;
1648
2004
  if (isGitHubMode && "io" in adapter && "setTaskId" in adapter.io) {
1649
2005
  adapter.io.setTaskId(taskId);
@@ -1651,69 +2007,87 @@ async function startCommand(query, options) {
1651
2007
  }
1652
2008
  try {
1653
2009
  while (response.type !== "complete" && response.type !== "error") {
1654
- if (response.type === "step") {
1655
- const stepResponse = response;
1656
- taskId = stepResponse.taskId;
1657
- console.log(`
1658
- --- Step ${stepResponse.stepIndex}/${stepResponse.totalSteps}: ${stepResponse.step} ---
1659
- `);
1660
- const context = {
1661
- taskId: stepResponse.taskId,
1662
- description: stepResponse.description,
1663
- stepIndex: stepResponse.stepIndex,
1664
- totalSteps: stepResponse.totalSteps,
1665
- revisionFeedback: stepResponse.context.revisionFeedback
1666
- };
1667
- try {
1668
- const result = await stepExecutor.execute(stepResponse.step, context);
1669
- if (result.approved) {
1670
- response = orchestrator.cmdApprove(taskId);
1671
- } else if (result.rejected) {
1672
- response = orchestrator.cmdReject(taskId);
1673
- } else if (result.stopped) {
1674
- response = orchestrator.cmdStop(taskId);
1675
- } else {
1676
- throw new Error("Unexpected step result");
1677
- }
1678
- } catch (error) {
1679
- if (error instanceof PauseForInputError) {
2010
+ if (response.type === "phase") {
2011
+ const phaseResponse = response;
2012
+ taskId = phaseResponse.taskId;
2013
+ if (phaseResponse.phase === "context") {
2014
+ const contextResp = phaseResponse;
2015
+ console.log(`
2016
+ --- Step ${contextResp.stepIndex}/${contextResp.totalSteps}: ${contextResp.step} ---`);
2017
+ console.log("Phase 1/4: Gathering context...\n");
2018
+ const stepContext = {
2019
+ taskId: contextResp.taskId,
2020
+ description: contextResp.description,
2021
+ stepIndex: contextResp.stepIndex,
2022
+ totalSteps: contextResp.totalSteps,
2023
+ previousOutput: contextResp.context.previousOutput
2024
+ };
2025
+ const result = await stepExecutor.executeContextPhase(contextResp.step, stepContext);
2026
+ response = orchestrator.cmdContextDone(taskId, result.context || "");
2027
+ } else if (phaseResponse.phase === "clarify") {
2028
+ const clarifyResp = phaseResponse;
2029
+ console.log("Phase 2/4: Generating questions...\n");
2030
+ const stepContext = {
2031
+ taskId: clarifyResp.taskId,
2032
+ description: clarifyResp.description,
2033
+ stepIndex: 0,
2034
+ // Not needed for clarify
2035
+ totalSteps: 0,
2036
+ gatheredContext: clarifyResp.gatheredContext
2037
+ };
2038
+ const result = await stepExecutor.executeClarifyPhase(clarifyResp.step, stepContext);
2039
+ response = orchestrator.cmdClarifyDone(taskId, result.questions || []);
2040
+ } else if (phaseResponse.phase === "generate") {
2041
+ const generateResp = phaseResponse;
2042
+ console.log("Phase 3/4: Generating document...\n");
2043
+ const stepContext = {
2044
+ taskId: generateResp.taskId,
2045
+ description: generateResp.description,
2046
+ stepIndex: generateResp.stepIndex,
2047
+ totalSteps: generateResp.totalSteps,
2048
+ gatheredContext: generateResp.gatheredContext,
2049
+ answers: generateResp.answers,
2050
+ revisionFeedback: generateResp.context.revisionFeedback
2051
+ };
2052
+ await stepExecutor.executeGeneratePhase(generateResp.step, stepContext);
2053
+ response = orchestrator.cmdGenerateDone(taskId);
2054
+ }
2055
+ } else if (response.type === "checkpoint") {
2056
+ taskId = response.taskId;
2057
+ if (response.checkpoint === "clarify") {
2058
+ const clarifyCheckpoint = response;
2059
+ console.log("Phase 2/4: Questions need answers\n");
2060
+ const answers = await adapter.io.askMultiple(clarifyCheckpoint.questions);
2061
+ if (answers.length === 0) {
1680
2062
  console.log(`
1681
2063
  \u23F8\uFE0F Workflow paused. Task ID: ${taskId}`);
1682
2064
  console.log(" Resume with: spets resume --task", taskId);
1683
2065
  return;
1684
2066
  }
1685
- throw error;
1686
- }
1687
- } else if (response.type === "checkpoint") {
1688
- taskId = response.taskId;
1689
- if (response.checkpoint === "clarify") {
1690
- adapter.io.notify("Resuming from clarify checkpoint - regenerating step", "warning");
1691
- response = orchestrator.cmdStatus(taskId);
2067
+ response = orchestrator.cmdClarified(taskId, answers);
1692
2068
  } else if (response.checkpoint === "approve") {
1693
- const approveResponse = response;
1694
- try {
1695
- const approval = await adapter.io.approve(
1696
- approveResponse.specPath,
1697
- approveResponse.step,
1698
- approveResponse.stepIndex,
1699
- approveResponse.totalSteps
1700
- );
1701
- if (approval.action === "approve") {
1702
- response = orchestrator.cmdApprove(taskId);
1703
- } else if (approval.action === "revise") {
1704
- response = orchestrator.cmdRevise(taskId, approval.feedback || "");
1705
- } else if (approval.action === "reject") {
1706
- response = orchestrator.cmdReject(taskId);
1707
- } else {
1708
- response = orchestrator.cmdStop(taskId);
1709
- }
1710
- } catch (error) {
1711
- if (error instanceof PauseForInputError) {
1712
- console.log(`
2069
+ const approveCheckpoint = response;
2070
+ console.log("Phase 4/4: Review document\n");
2071
+ const approval = await adapter.io.approve(
2072
+ approveCheckpoint.specPath,
2073
+ approveCheckpoint.step,
2074
+ approveCheckpoint.stepIndex,
2075
+ approveCheckpoint.totalSteps
2076
+ );
2077
+ if (approval.pending) {
2078
+ console.log(`
1713
2079
  \u23F8\uFE0F Workflow paused. Task ID: ${taskId}`);
1714
- return;
1715
- }
1716
- throw error;
2080
+ console.log(" Resume with: spets resume --task", taskId);
2081
+ return;
2082
+ }
2083
+ if (approval.action === "approve") {
2084
+ response = orchestrator.cmdApprove(taskId);
2085
+ } else if (approval.action === "revise") {
2086
+ response = orchestrator.cmdRevise(taskId, approval.feedback || "");
2087
+ } else if (approval.action === "reject") {
2088
+ response = orchestrator.cmdReject(taskId);
2089
+ } else {
2090
+ response = orchestrator.cmdStop(taskId);
1717
2091
  }
1718
2092
  }
1719
2093
  } else {
@@ -1752,19 +2126,19 @@ Resume with: spets resume --task ${taskId}`);
1752
2126
 
1753
2127
  // src/commands/resume.ts
1754
2128
  import { select as select2 } from "@inquirer/prompts";
1755
- import { existsSync as existsSync8, readdirSync as readdirSync2 } from "fs";
1756
- import { join as join6 } from "path";
2129
+ import { existsSync as existsSync9, readdirSync as readdirSync2 } from "fs";
2130
+ import { join as join7 } from "path";
1757
2131
  function listResumableTasks(cwd) {
1758
2132
  const outputsDir = getOutputsDir(cwd);
1759
- if (!existsSync8(outputsDir)) {
2133
+ if (!existsSync9(outputsDir)) {
1760
2134
  return [];
1761
2135
  }
1762
2136
  const tasks = [];
1763
2137
  const taskDirs = readdirSync2(outputsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
1764
2138
  const orchestrator = new Orchestrator(cwd);
1765
2139
  for (const taskId of taskDirs) {
1766
- const stateFile = join6(outputsDir, taskId, ".state.json");
1767
- if (!existsSync8(stateFile)) continue;
2140
+ const stateFile = join7(outputsDir, taskId, ".state.json");
2141
+ if (!existsSync9(stateFile)) continue;
1768
2142
  try {
1769
2143
  const response = orchestrator.cmdStatus(taskId);
1770
2144
  if (response.type === "error") continue;
@@ -1932,8 +2306,8 @@ Resume with: spets resume --task ${taskId}`);
1932
2306
  }
1933
2307
 
1934
2308
  // src/commands/plugin.ts
1935
- import { existsSync as existsSync9, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7, rmSync } from "fs";
1936
- import { join as join7 } from "path";
2309
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7, rmSync } from "fs";
2310
+ import { join as join8 } from "path";
1937
2311
  import { homedir } from "os";
1938
2312
  async function pluginCommand(action, name) {
1939
2313
  switch (action) {
@@ -1975,10 +2349,10 @@ async function installPlugin(name) {
1975
2349
  installer();
1976
2350
  }
1977
2351
  function installClaudePlugin() {
1978
- const claudeDir = join7(homedir(), ".claude");
1979
- const commandsDir = join7(claudeDir, "commands");
2352
+ const claudeDir = join8(homedir(), ".claude");
2353
+ const commandsDir = join8(claudeDir, "commands");
1980
2354
  mkdirSync6(commandsDir, { recursive: true });
1981
- const skillPath = join7(commandsDir, "spets.md");
2355
+ const skillPath = join8(commandsDir, "spets.md");
1982
2356
  writeFileSync7(skillPath, getClaudeSkillContent());
1983
2357
  console.log("Installed Claude Code plugin.");
1984
2358
  console.log(`Location: ${skillPath}`);
@@ -1991,14 +2365,14 @@ function installClaudePlugin() {
1991
2365
  }
1992
2366
  async function uninstallPlugin(name) {
1993
2367
  if (name === "claude") {
1994
- const skillPath = join7(homedir(), ".claude", "commands", "spets.md");
1995
- const legacySkillPath = join7(homedir(), ".claude", "commands", "sdd-do.md");
2368
+ const skillPath = join8(homedir(), ".claude", "commands", "spets.md");
2369
+ const legacySkillPath = join8(homedir(), ".claude", "commands", "sdd-do.md");
1996
2370
  let uninstalled = false;
1997
- if (existsSync9(skillPath)) {
2371
+ if (existsSync10(skillPath)) {
1998
2372
  rmSync(skillPath);
1999
2373
  uninstalled = true;
2000
2374
  }
2001
- if (existsSync9(legacySkillPath)) {
2375
+ if (existsSync10(legacySkillPath)) {
2002
2376
  rmSync(legacySkillPath);
2003
2377
  uninstalled = true;
2004
2378
  }
@@ -2017,9 +2391,9 @@ async function listPlugins() {
2017
2391
  console.log("");
2018
2392
  console.log(" claude - Claude Code /spets skill");
2019
2393
  console.log("");
2020
- const skillPath = join7(homedir(), ".claude", "commands", "spets.md");
2021
- const legacySkillPath = join7(homedir(), ".claude", "commands", "sdd-do.md");
2022
- const claudeInstalled = existsSync9(skillPath) || existsSync9(legacySkillPath);
2394
+ const skillPath = join8(homedir(), ".claude", "commands", "spets.md");
2395
+ const legacySkillPath = join8(homedir(), ".claude", "commands", "sdd-do.md");
2396
+ const claudeInstalled = existsSync10(skillPath) || existsSync10(legacySkillPath);
2023
2397
  console.log("Installed:");
2024
2398
  if (claudeInstalled) {
2025
2399
  console.log(" - claude");
@@ -2189,9 +2563,9 @@ request: The user's task description for the workflow
2189
2563
  }
2190
2564
 
2191
2565
  // src/commands/github.ts
2192
- import { execSync as execSync4, spawn as spawn3, spawnSync as spawnSync3 } from "child_process";
2193
- import { readdirSync as readdirSync4, readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
2194
- import { join as join8 } from "path";
2566
+ import { execSync as execSync3, spawn as spawn3, spawnSync as spawnSync3 } from "child_process";
2567
+ import { readdirSync as readdirSync4, readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
2568
+ import { join as join9 } from "path";
2195
2569
  function parseGitHubCommand(comment) {
2196
2570
  const trimmed = comment.trim();
2197
2571
  if (trimmed === "/approve") {
@@ -2231,7 +2605,7 @@ function getGitHubInfo2(cwd) {
2231
2605
  return { owner: config.owner, repo: config.repo };
2232
2606
  }
2233
2607
  try {
2234
- const remoteUrl = execSync4("git remote get-url origin", { encoding: "utf-8", cwd }).trim();
2608
+ const remoteUrl = execSync3("git remote get-url origin", { encoding: "utf-8", cwd }).trim();
2235
2609
  const sshMatch = remoteUrl.match(/git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
2236
2610
  if (sshMatch) {
2237
2611
  return { owner: sshMatch[1], repo: sshMatch[2] };
@@ -2246,7 +2620,7 @@ function getGitHubInfo2(cwd) {
2246
2620
  }
2247
2621
  function getCurrentBranch2(cwd) {
2248
2622
  try {
2249
- return execSync4("git branch --show-current", { encoding: "utf-8", cwd }).trim();
2623
+ return execSync3("git branch --show-current", { encoding: "utf-8", cwd }).trim();
2250
2624
  } catch {
2251
2625
  return void 0;
2252
2626
  }
@@ -2254,7 +2628,7 @@ function getCurrentBranch2(cwd) {
2254
2628
  function findTaskId(cwd, owner, repo, issueOrPr) {
2255
2629
  if (issueOrPr) {
2256
2630
  try {
2257
- const commentsJson = execSync4(
2631
+ const commentsJson = execSync3(
2258
2632
  `gh api repos/${owner}/${repo}/issues/${issueOrPr}/comments --jq '.[].body'`,
2259
2633
  { encoding: "utf-8" }
2260
2634
  );
@@ -2266,7 +2640,7 @@ function findTaskId(cwd, owner, repo, issueOrPr) {
2266
2640
  }
2267
2641
  }
2268
2642
  const outputsDir = getOutputsDir(cwd);
2269
- if (existsSync10(outputsDir)) {
2643
+ if (existsSync11(outputsDir)) {
2270
2644
  const taskDirs = readdirSync4(outputsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
2271
2645
  if (taskDirs.length > 0) {
2272
2646
  return taskDirs[0];
@@ -2323,15 +2697,15 @@ async function githubCommand(options) {
2323
2697
  case "approve": {
2324
2698
  console.log("Approving current step...");
2325
2699
  response = orchestrator.cmdApprove(taskId);
2326
- if (parsed.createPR && response.type !== "error") {
2700
+ if (parsed.createPR && response.type === "complete") {
2327
2701
  console.log("Creating PR with AI-generated content...");
2328
- const stepResponse = response;
2329
- await createPullRequestWithAI(githubConfig, taskId, stepResponse.description || "", cwd);
2702
+ const completeResp = response;
2703
+ await createPullRequestWithAI(githubConfig, taskId, completeResp.message || "", cwd);
2330
2704
  }
2331
- if (parsed.createIssue && response.type !== "error") {
2705
+ if (parsed.createIssue && response.type === "phase") {
2332
2706
  console.log("Creating/Updating Issue...");
2333
- const stepResponse = response;
2334
- await createOrUpdateIssue(githubConfig, taskId, stepResponse.description || "", stepResponse.step);
2707
+ const phaseResp = response;
2708
+ await createOrUpdateIssue(githubConfig, taskId, phaseResp.description || "", phaseResp.step);
2335
2709
  }
2336
2710
  break;
2337
2711
  }
@@ -2356,37 +2730,78 @@ async function githubCommand(options) {
2356
2730
  break;
2357
2731
  }
2358
2732
  }
2359
- if (response.type === "step") {
2360
- const stepResponse = response;
2361
- const stepExecutor = new StepExecutor(adapter, config, cwd);
2362
- console.log(`
2363
- --- Step ${stepResponse.stepIndex}/${stepResponse.totalSteps}: ${stepResponse.step} ---
2364
- `);
2365
- const context = {
2366
- taskId: stepResponse.taskId,
2367
- description: stepResponse.description,
2368
- stepIndex: stepResponse.stepIndex,
2369
- totalSteps: stepResponse.totalSteps,
2370
- revisionFeedback: stepResponse.context.revisionFeedback
2371
- };
2372
- try {
2373
- const result = await stepExecutor.execute(stepResponse.step, context);
2374
- if (result.approved) {
2375
- orchestrator.cmdApprove(taskId);
2376
- } else if (result.rejected) {
2377
- orchestrator.cmdReject(taskId);
2378
- } else if (result.stopped) {
2379
- orchestrator.cmdStop(taskId);
2733
+ const stepExecutor = new StepExecutor(adapter, config, cwd);
2734
+ while (response.type !== "complete" && response.type !== "error") {
2735
+ if (response.type === "phase") {
2736
+ const phaseResponse = response;
2737
+ if (phaseResponse.phase === "context") {
2738
+ const contextResp = phaseResponse;
2739
+ console.log(`
2740
+ --- Step ${contextResp.stepIndex}/${contextResp.totalSteps}: ${contextResp.step} ---`);
2741
+ console.log("Phase 1/4: Gathering context...\n");
2742
+ const stepContext = {
2743
+ taskId: contextResp.taskId,
2744
+ description: contextResp.description,
2745
+ stepIndex: contextResp.stepIndex,
2746
+ totalSteps: contextResp.totalSteps,
2747
+ previousOutput: contextResp.context.previousOutput
2748
+ };
2749
+ const result = await stepExecutor.executeContextPhase(contextResp.step, stepContext);
2750
+ response = orchestrator.cmdContextDone(taskId, result.context || "");
2751
+ } else if (phaseResponse.phase === "clarify") {
2752
+ const clarifyResp = phaseResponse;
2753
+ console.log("Phase 2/4: Generating questions...\n");
2754
+ const stepContext = {
2755
+ taskId: clarifyResp.taskId,
2756
+ description: clarifyResp.description,
2757
+ stepIndex: 0,
2758
+ totalSteps: 0,
2759
+ gatheredContext: clarifyResp.gatheredContext
2760
+ };
2761
+ const result = await stepExecutor.executeClarifyPhase(clarifyResp.step, stepContext);
2762
+ response = orchestrator.cmdClarifyDone(taskId, result.questions || []);
2763
+ } else if (phaseResponse.phase === "generate") {
2764
+ const generateResp = phaseResponse;
2765
+ console.log("Phase 3/4: Generating document...\n");
2766
+ const stepContext = {
2767
+ taskId: generateResp.taskId,
2768
+ description: generateResp.description,
2769
+ stepIndex: generateResp.stepIndex,
2770
+ totalSteps: generateResp.totalSteps,
2771
+ gatheredContext: generateResp.gatheredContext,
2772
+ answers: generateResp.answers,
2773
+ revisionFeedback: generateResp.context.revisionFeedback
2774
+ };
2775
+ await stepExecutor.executeGeneratePhase(generateResp.step, stepContext);
2776
+ response = orchestrator.cmdGenerateDone(taskId);
2380
2777
  }
2381
- } catch (error) {
2382
- if (error instanceof PauseForInputError) {
2778
+ } else if (response.type === "checkpoint") {
2779
+ if (response.checkpoint === "clarify") {
2780
+ const clarifyCheckpoint = response;
2781
+ console.log("Phase 2/4: Questions need answers\n");
2782
+ await adapter.io.askMultiple(clarifyCheckpoint.questions);
2783
+ console.log(`
2784
+ \u23F8\uFE0F Workflow paused. Waiting for /answer on GitHub.`);
2785
+ return;
2786
+ } else if (response.checkpoint === "approve") {
2787
+ const approveCheckpoint = response;
2788
+ console.log("Phase 4/4: Document ready for review\n");
2789
+ await adapter.io.approve(
2790
+ approveCheckpoint.specPath,
2791
+ approveCheckpoint.step,
2792
+ approveCheckpoint.stepIndex,
2793
+ approveCheckpoint.totalSteps
2794
+ );
2383
2795
  console.log(`
2384
- \u23F8\uFE0F Workflow paused. Waiting for input on GitHub.`);
2796
+ \u23F8\uFE0F Workflow paused. Waiting for /approve or /revise on GitHub.`);
2385
2797
  return;
2386
2798
  }
2387
- throw error;
2799
+ } else {
2800
+ console.error(`Unknown response type: ${response.type}`);
2801
+ process.exit(1);
2388
2802
  }
2389
- } else if (response.type === "complete") {
2803
+ }
2804
+ if (response.type === "complete") {
2390
2805
  const completeResponse = response;
2391
2806
  if (completeResponse.status === "completed") {
2392
2807
  console.log("\u2705 Workflow completed!");
@@ -2431,12 +2846,12 @@ async function postComment(config, body) {
2431
2846
  }
2432
2847
  async function createPullRequestWithAI(config, taskId, userQuery, cwd) {
2433
2848
  const { owner, repo, issueNumber } = config;
2434
- const outputsDir = join8(getOutputsDir(cwd), taskId);
2849
+ const outputsDir = join9(getOutputsDir(cwd), taskId);
2435
2850
  const outputs = [];
2436
- if (existsSync10(outputsDir)) {
2851
+ if (existsSync11(outputsDir)) {
2437
2852
  const files = readdirSync4(outputsDir).filter((f) => f.endsWith(".md"));
2438
2853
  for (const file of files) {
2439
- const content = readFileSync8(join8(outputsDir, file), "utf-8");
2854
+ const content = readFileSync9(join9(outputsDir, file), "utf-8");
2440
2855
  outputs.push({ step: file.replace(".md", ""), content });
2441
2856
  }
2442
2857
  }
@@ -2597,6 +3012,47 @@ async function orchestrateCommand(action, args) {
2597
3012
  outputJSON(result);
2598
3013
  break;
2599
3014
  }
3015
+ case "context-done": {
3016
+ const taskId = args[0];
3017
+ const contextJson = args[1];
3018
+ if (!taskId) {
3019
+ outputError("Task ID is required for context-done");
3020
+ return;
3021
+ }
3022
+ const result = orchestrator.cmdContextDone(taskId, contextJson || "");
3023
+ outputJSON(result);
3024
+ break;
3025
+ }
3026
+ case "clarify-done": {
3027
+ const taskId = args[0];
3028
+ const questionsJson = args[1];
3029
+ if (!taskId) {
3030
+ outputError("Task ID is required for clarify-done");
3031
+ return;
3032
+ }
3033
+ let questions = [];
3034
+ if (questionsJson) {
3035
+ try {
3036
+ questions = JSON.parse(questionsJson);
3037
+ } catch {
3038
+ outputError("Invalid JSON for questions");
3039
+ return;
3040
+ }
3041
+ }
3042
+ const result = orchestrator.cmdClarifyDone(taskId, questions);
3043
+ outputJSON(result);
3044
+ break;
3045
+ }
3046
+ case "generate-done": {
3047
+ const taskId = args[0];
3048
+ if (!taskId) {
3049
+ outputError("Task ID is required for generate-done");
3050
+ return;
3051
+ }
3052
+ const result = orchestrator.cmdGenerateDone(taskId);
3053
+ outputJSON(result);
3054
+ break;
3055
+ }
2600
3056
  case "done": {
2601
3057
  const taskId = args[0];
2602
3058
  if (!taskId) {
@@ -2685,8 +3141,8 @@ async function orchestrateCommand(action, args) {
2685
3141
  }
2686
3142
 
2687
3143
  // src/index.ts
2688
- var __dirname2 = dirname6(fileURLToPath2(import.meta.url));
2689
- var pkg = JSON.parse(readFileSync9(join9(__dirname2, "..", "package.json"), "utf-8"));
3144
+ var __dirname2 = dirname5(fileURLToPath2(import.meta.url));
3145
+ var pkg = JSON.parse(readFileSync10(join10(__dirname2, "..", "package.json"), "utf-8"));
2690
3146
  var program = new Command();
2691
3147
  program.name("spets").description("Spec Driven Development Execution Framework").version(pkg.version);
2692
3148
  program.command("init").description("Initialize spets in current directory").option("-f, --force", "Overwrite existing config").option("--github", "Add GitHub Actions workflow for PR/Issue integration").action(initCommand);