spets 0.1.66 → 0.1.68

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 +87 -98
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -930,8 +930,8 @@ function buildClarifyPrompt(params) {
930
930
  const parts = [];
931
931
  parts.push("# Clarify Phase");
932
932
  parts.push("");
933
- parts.push("Your task is to identify any questions that need user input before generating the document.");
934
- parts.push("Do NOT generate the document yet - only generate questions if needed.");
933
+ parts.push("Your task is to identify decisions that need user input before proceeding.");
934
+ parts.push("Do NOT generate the document yet - only identify decision points.");
935
935
  parts.push("");
936
936
  parts.push("## Task Information");
937
937
  parts.push("");
@@ -943,71 +943,63 @@ function buildClarifyPrompt(params) {
943
943
  parts.push("");
944
944
  parts.push(params.gatheredContext);
945
945
  parts.push("");
946
- if (params.previousQA && params.previousQA.length > 0) {
947
- const unresolvedEntries = params.previousQA.filter((e) => !e.question.resolved);
948
- const resolvedEntries = params.previousQA.filter((e) => e.question.resolved);
949
- if (unresolvedEntries.length > 0) {
950
- parts.push("## Unresolved Questions");
951
- parts.push("");
952
- parts.push("The user indicated confusion on these questions. Re-ask with a clearer explanation:");
953
- parts.push("");
954
- for (const entry of unresolvedEntries) {
955
- parts.push(`**Original Question:** ${entry.question.question}`);
956
- if (entry.question.context) {
957
- parts.push(`Context: ${entry.question.context}`);
958
- }
959
- parts.push(`**User Response:** "${entry.answer.answer}"`);
960
- parts.push("");
961
- parts.push("\u2192 Re-ask this question with simpler language, concrete examples, or more context.");
962
- parts.push("");
963
- }
964
- }
965
- if (resolvedEntries.length > 0) {
966
- parts.push("## Previous Questions & Answers");
967
- parts.push("");
968
- for (const entry of resolvedEntries) {
969
- parts.push(`**Q: ${entry.question.question}**`);
970
- if (entry.question.context) {
971
- parts.push(`Context: ${entry.question.context}`);
972
- }
973
- parts.push(`A: ${entry.answer.answer}`);
974
- parts.push("");
946
+ const hasPreviousDecisions = params.previousDecisions && params.previousDecisions.length > 0;
947
+ if (hasPreviousDecisions) {
948
+ parts.push("## Previous Decisions");
949
+ parts.push("");
950
+ for (const entry of params.previousDecisions) {
951
+ const selectedOption = entry.decision.options.find((o) => o.id === entry.answer.selectedOptionId);
952
+ const label = entry.answer.selectedOptionId === "ai" ? `AI recommendation (${entry.decision.aiRecommendation?.optionId})` : selectedOption?.label || entry.answer.selectedOptionId;
953
+ parts.push(`**${entry.decision.decision}**: ${label}`);
954
+ if (entry.answer.customInput) {
955
+ parts.push(` Custom input: ${entry.answer.customInput}`);
975
956
  }
976
- parts.push("**Rules:**");
977
- parts.push('- If user said "you decide" or "\uC54C\uC544\uC11C \uD574" \u2192 use your judgment and move on');
978
- parts.push("- If user asked for options \u2192 provide options for that specific topic only");
979
- parts.push("- Do NOT ask about topics already answered, even with different wording");
980
- parts.push("- When in doubt, proceed rather than ask again");
981
957
  parts.push("");
982
958
  }
959
+ parts.push("Based on these decisions, identify any remaining decision points.");
960
+ parts.push("Do NOT re-ask about decisions already made.");
961
+ parts.push("");
962
+ } else {
963
+ parts.push("## Your Task");
964
+ parts.push("");
965
+ parts.push("Identify decision points where:");
966
+ parts.push("1. Multiple valid approaches exist");
967
+ parts.push("2. User preference matters");
968
+ parts.push("3. Tradeoffs need to be considered");
969
+ parts.push("");
983
970
  }
984
- parts.push("## Consider These Questions");
985
- parts.push("");
986
- parts.push("1. Is the task description clear enough to proceed?");
987
- parts.push("2. Are there multiple valid approaches that need user decision?");
988
- parts.push("3. Are there ambiguities that could lead to wrong implementation?");
989
- parts.push("4. Are there missing requirements or constraints?");
990
- parts.push("");
991
971
  parts.push("## Output Format");
992
972
  parts.push("");
993
- parts.push("Output a JSON object with questions array:");
973
+ parts.push("For each decision, provide options with descriptions and tradeoffs.");
974
+ parts.push('Always include "ai" as the last option with your recommendation.');
994
975
  parts.push("");
995
976
  parts.push("```json");
996
977
  parts.push("{");
997
- parts.push(' "questions": [');
978
+ parts.push(' "ready": false,');
979
+ parts.push(' "decisions": [');
998
980
  parts.push(" {");
999
- parts.push(' "id": "q1",');
1000
- parts.push(' "question": "Your specific question?",');
1001
- parts.push(' "context": "Why this matters for the document",');
1002
- parts.push(' "options": ["Option A", "Option B"] // optional');
981
+ parts.push(' "id": "d1",');
982
+ parts.push(' "decision": "What needs to be decided",');
983
+ parts.push(' "why": "Why this decision matters",');
984
+ parts.push(' "options": [');
985
+ parts.push(' { "id": "opt1", "label": "Option A", "description": "...", "tradeoffs": "..." },');
986
+ parts.push(' { "id": "opt2", "label": "Option B", "description": "...", "tradeoffs": "..." },');
987
+ parts.push(' { "id": "ai", "label": "Let AI decide", "description": "AI chooses based on context", "recommendation": "opt1", "reason": "..." }');
988
+ parts.push(" ]");
1003
989
  parts.push(" }");
1004
990
  parts.push(" ]");
1005
991
  parts.push("}");
1006
992
  parts.push("```");
1007
993
  parts.push("");
1008
- parts.push('If no questions are needed, return: `{"questions": []}`');
994
+ parts.push("If all clear to proceed:");
995
+ parts.push("```json");
996
+ parts.push('{ "ready": true, "decisions": [], "summary": "How we will proceed" }');
997
+ parts.push("```");
1009
998
  parts.push("");
1010
- parts.push("**Important:** Output ONLY the JSON, no other text.");
999
+ parts.push("**Important:**");
1000
+ parts.push("- Output ONLY the JSON, no other text.");
1001
+ parts.push("- Every decision MUST have at least 2 concrete options with descriptions and tradeoffs.");
1002
+ parts.push('- Every decision MUST include "ai" option as the last choice.');
1011
1003
  parts.push("");
1012
1004
  return parts.join("\n");
1013
1005
  }
@@ -1406,7 +1398,7 @@ var Orchestrator = class {
1406
1398
  };
1407
1399
  }
1408
1400
  /**
1409
- * Phase 2: Question generation
1401
+ * Phase 2: Decision identification
1410
1402
  */
1411
1403
  responsePhaseClarify(state) {
1412
1404
  const prompt = buildClarifyPrompt({
@@ -1414,7 +1406,9 @@ var Orchestrator = class {
1414
1406
  step: state.currentStep,
1415
1407
  description: state.description,
1416
1408
  gatheredContext: state.exploreOutput ? JSON.stringify(state.exploreOutput) : "",
1409
+ previousDecisions: state.decisionHistory,
1417
1410
  previousQA: state.qaHistory,
1411
+ // Legacy support
1418
1412
  cwd: this.cwd
1419
1413
  });
1420
1414
  return {
@@ -1425,7 +1419,9 @@ var Orchestrator = class {
1425
1419
  description: state.description,
1426
1420
  prompt,
1427
1421
  gatheredContext: state.exploreOutput ? JSON.stringify(state.exploreOutput) : "",
1422
+ previousDecisions: state.decisionHistory,
1428
1423
  previousQA: state.qaHistory,
1424
+ // Legacy support
1429
1425
  onComplete: `npx spets orchestrate clarify-done ${state.taskId}`
1430
1426
  };
1431
1427
  }
@@ -1529,7 +1525,7 @@ ${issues}`;
1529
1525
  };
1530
1526
  }
1531
1527
  /**
1532
- * Checkpoint: Questions need answers
1528
+ * Checkpoint: Decisions need user input
1533
1529
  */
1534
1530
  responseCheckpointClarify(state) {
1535
1531
  return {
@@ -1537,7 +1533,9 @@ ${issues}`;
1537
1533
  checkpoint: "clarify",
1538
1534
  taskId: state.taskId,
1539
1535
  step: state.currentStep,
1536
+ decisions: state.decisions || [],
1540
1537
  questions: state.questions || [],
1538
+ // Legacy support
1541
1539
  onComplete: `npx spets orchestrate clarified ${state.taskId} '<answers_json>'`
1542
1540
  };
1543
1541
  }
@@ -1633,23 +1631,23 @@ ${issues}`;
1633
1631
  return this.responsePhaseClarify(state);
1634
1632
  }
1635
1633
  /**
1636
- * Phase 2 complete: Questions generated (or none)
1637
- * If questions exist → checkpoint
1638
- * If no questions → move to execute phase with accumulated answers
1634
+ * Phase 2 complete: Decisions identified (or ready to proceed)
1635
+ * If decisions exist → checkpoint for user input
1636
+ * If ready → move to execute phase
1639
1637
  */
1640
- cmdClarifyDone(taskId, questions) {
1638
+ cmdClarifyDone(taskId, clarifyOutput) {
1641
1639
  const state = this.loadState(taskId);
1642
1640
  if (!state) {
1643
1641
  return this.responseError(`No workflow found: ${taskId}`, taskId);
1644
1642
  }
1645
- if (questions.length > 0) {
1646
- state.questions = questions;
1643
+ if (!clarifyOutput.ready && clarifyOutput.decisions.length > 0) {
1644
+ state.decisions = clarifyOutput.decisions;
1647
1645
  state.status = "clarify_pending";
1648
1646
  state.phase = "clarify";
1649
1647
  this.saveState(state);
1650
1648
  return this.responseCheckpointClarify(state);
1651
1649
  }
1652
- state.answers = state.qaHistory?.map((entry) => entry.answer);
1650
+ state.answers = state.decisionHistory?.map((entry) => entry.answer);
1653
1651
  state.status = "phase_execute";
1654
1652
  state.phase = "execute";
1655
1653
  state.verifyAttempts = 0;
@@ -1657,54 +1655,45 @@ ${issues}`;
1657
1655
  return this.responsePhaseExecute(state);
1658
1656
  }
1659
1657
  /**
1660
- * Check if an answer indicates user confusion
1658
+ * Legacy support: Questions-based clarify done
1659
+ * @deprecated Use decision-based cmdClarifyDone instead
1661
1660
  */
1662
- isConfusedAnswer(answer) {
1663
- const confusionPatterns = [
1664
- /don'?t understand/i,
1665
- /not sure/i,
1666
- /unclear/i,
1667
- /what do you mean/i,
1668
- /i'?m confused/i,
1669
- /can you explain/i,
1670
- /뭔 말/i,
1671
- /이해가 안/i,
1672
- /모르겠/i,
1673
- /무슨 말/i,
1674
- /잘 모르/i
1675
- ];
1676
- return confusionPatterns.some((p) => p.test(answer));
1661
+ cmdClarifyDoneLegacy(taskId, questions) {
1662
+ const state = this.loadState(taskId);
1663
+ if (!state) {
1664
+ return this.responseError(`No workflow found: ${taskId}`, taskId);
1665
+ }
1666
+ if (questions.length > 0) {
1667
+ state.questions = questions;
1668
+ state.status = "clarify_pending";
1669
+ state.phase = "clarify";
1670
+ this.saveState(state);
1671
+ return this.responseCheckpointClarify(state);
1672
+ }
1673
+ state.answers = state.qaHistory?.map((entry) => entry.answer);
1674
+ state.status = "phase_execute";
1675
+ state.phase = "execute";
1676
+ state.verifyAttempts = 0;
1677
+ this.saveState(state);
1678
+ return this.responsePhaseExecute(state);
1677
1679
  }
1678
1680
  /**
1679
- * Human answered questions
1680
- * - Mark questions as resolved if answer is clear
1681
- * - Keep questions unresolved if user indicates confusion
1682
- * - Re-ask unresolved questions until all are resolved
1681
+ * Human made decisions
1682
+ * - Store decisions in history
1683
+ * - Loop back to clarify phase to check for remaining decisions
1683
1684
  */
1684
1685
  cmdClarified(taskId, answers) {
1685
1686
  const state = this.loadState(taskId);
1686
1687
  if (!state) {
1687
1688
  return this.responseError(`No workflow found: ${taskId}`, taskId);
1688
1689
  }
1689
- const questions = state.questions || [];
1690
- for (const answer of answers) {
1691
- const question = questions.find((q) => q.id === answer.questionId);
1692
- if (question) {
1693
- question.resolved = !this.isConfusedAnswer(answer.answer);
1694
- }
1695
- }
1696
- const newEntries = questions.map((q, i) => ({
1697
- question: q,
1698
- answer: answers[i] || { questionId: q.id, answer: "" }
1690
+ const decisions = state.decisions || [];
1691
+ const newEntries = decisions.map((d, i) => ({
1692
+ decision: d,
1693
+ answer: answers[i] || { decisionId: d.id, selectedOptionId: "ai" }
1699
1694
  }));
1700
- state.qaHistory = [...state.qaHistory || [], ...newEntries];
1701
- const unresolvedQuestions = questions.filter((q) => !q.resolved);
1702
- if (unresolvedQuestions.length > 0) {
1703
- state.questions = unresolvedQuestions;
1704
- this.saveState(state);
1705
- return this.responseCheckpointClarify(state);
1706
- }
1707
- state.questions = void 0;
1695
+ state.decisionHistory = [...state.decisionHistory || [], ...newEntries];
1696
+ state.decisions = void 0;
1708
1697
  state.answers = void 0;
1709
1698
  state.status = "phase_clarify";
1710
1699
  state.phase = "clarify";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spets",
3
- "version": "0.1.66",
3
+ "version": "0.1.68",
4
4
  "description": "Spec Driven Development Execution Framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",