spets 0.1.65 → 0.1.67

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 +101 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -943,50 +943,32 @@ 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
- parts.push("The following questions have been resolved:");
969
- parts.push("");
970
- for (const entry of resolvedEntries) {
971
- parts.push(`**Q: ${entry.question.question}**`);
972
- if (entry.question.context) {
973
- parts.push(`Context: ${entry.question.context}`);
974
- }
975
- parts.push(`A: ${entry.answer.answer}`);
976
- parts.push("");
977
- }
978
- parts.push("Based on these answers, determine if there are still ambiguities that need clarification.");
979
- parts.push("Do NOT repeat questions that have already been resolved.");
946
+ const hasQAHistory = params.previousQA && params.previousQA.length > 0;
947
+ if (hasQAHistory) {
948
+ parts.push("## Previous Q&A");
949
+ parts.push("");
950
+ for (const entry of params.previousQA) {
951
+ parts.push(`**Q:** ${entry.question.question}`);
952
+ parts.push(`**A:** ${entry.answer.answer}`);
980
953
  parts.push("");
981
954
  }
955
+ parts.push("## Your Task");
956
+ parts.push("");
957
+ parts.push("For each Q&A above, judge if the question is resolved:");
958
+ parts.push("- Clear answer \u2192 resolved, do NOT re-ask");
959
+ parts.push('- "you decide" / "\uC54C\uC544\uC11C \uD574" \u2192 resolved, use your judgment');
960
+ parts.push('- "give options" / "\uC635\uC158 \uC918" \u2192 NOT resolved, re-ask WITH concrete options');
961
+ parts.push("- Confused / unclear \u2192 NOT resolved, re-ask simpler");
962
+ parts.push("");
963
+ parts.push("Only output questions that are NOT resolved. Do NOT ask new questions.");
964
+ parts.push("");
965
+ } else {
966
+ parts.push("## Consider");
967
+ parts.push("");
968
+ parts.push("1. Is the task clear enough to proceed?");
969
+ parts.push("2. Are there ambiguities that need user decision?");
970
+ parts.push("");
982
971
  }
983
- parts.push("## Consider These Questions");
984
- parts.push("");
985
- parts.push("1. Is the task description clear enough to proceed?");
986
- parts.push("2. Are there multiple valid approaches that need user decision?");
987
- parts.push("3. Are there ambiguities that could lead to wrong implementation?");
988
- parts.push("4. Are there missing requirements or constraints?");
989
- parts.push("");
990
972
  parts.push("## Output Format");
991
973
  parts.push("");
992
974
  parts.push("Output a JSON object with questions array:");
@@ -1655,30 +1637,10 @@ ${issues}`;
1655
1637
  this.saveState(state);
1656
1638
  return this.responsePhaseExecute(state);
1657
1639
  }
1658
- /**
1659
- * Check if an answer indicates user confusion
1660
- */
1661
- isConfusedAnswer(answer) {
1662
- const confusionPatterns = [
1663
- /don'?t understand/i,
1664
- /not sure/i,
1665
- /unclear/i,
1666
- /what do you mean/i,
1667
- /i'?m confused/i,
1668
- /can you explain/i,
1669
- /뭔 말/i,
1670
- /이해가 안/i,
1671
- /모르겠/i,
1672
- /무슨 말/i,
1673
- /잘 모르/i
1674
- ];
1675
- return confusionPatterns.some((p) => p.test(answer));
1676
- }
1677
1640
  /**
1678
1641
  * Human answered questions
1679
- * - Mark questions as resolved if answer is clear
1680
- * - Keep questions unresolved if user indicates confusion
1681
- * - Re-ask unresolved questions until all are resolved
1642
+ * - Store Q&A in history
1643
+ * - Loop back to clarify phase - AI will judge if answers resolved the questions
1682
1644
  */
1683
1645
  cmdClarified(taskId, answers) {
1684
1646
  const state = this.loadState(taskId);
@@ -1686,23 +1648,11 @@ ${issues}`;
1686
1648
  return this.responseError(`No workflow found: ${taskId}`, taskId);
1687
1649
  }
1688
1650
  const questions = state.questions || [];
1689
- for (const answer of answers) {
1690
- const question = questions.find((q) => q.id === answer.questionId);
1691
- if (question) {
1692
- question.resolved = !this.isConfusedAnswer(answer.answer);
1693
- }
1694
- }
1695
1651
  const newEntries = questions.map((q, i) => ({
1696
1652
  question: q,
1697
1653
  answer: answers[i] || { questionId: q.id, answer: "" }
1698
1654
  }));
1699
1655
  state.qaHistory = [...state.qaHistory || [], ...newEntries];
1700
- const unresolvedQuestions = questions.filter((q) => !q.resolved);
1701
- if (unresolvedQuestions.length > 0) {
1702
- state.questions = unresolvedQuestions;
1703
- this.saveState(state);
1704
- return this.responseCheckpointClarify(state);
1705
- }
1706
1656
  state.questions = void 0;
1707
1657
  state.answers = void 0;
1708
1658
  state.status = "phase_clarify";
@@ -4146,6 +4096,54 @@ function outputError(error) {
4146
4096
  console.log(JSON.stringify({ type: "error", error }, null, 2));
4147
4097
  process.exit(1);
4148
4098
  }
4099
+ function parseFlexibleJSON(json, options = {}) {
4100
+ const { expectArray = false, arrayKeys = [], defaultValue, allowStringFallback = false } = options;
4101
+ try {
4102
+ const parsed = JSON.parse(json);
4103
+ if (expectArray) {
4104
+ if (Array.isArray(parsed)) {
4105
+ return { success: true, data: parsed };
4106
+ }
4107
+ if (typeof parsed === "object" && parsed !== null) {
4108
+ for (const key of arrayKeys) {
4109
+ if (Array.isArray(parsed[key])) {
4110
+ return { success: true, data: parsed[key] };
4111
+ }
4112
+ }
4113
+ for (const key of ["data", "items", "results", "list"]) {
4114
+ if (Array.isArray(parsed[key])) {
4115
+ return { success: true, data: parsed[key] };
4116
+ }
4117
+ }
4118
+ const values = Object.values(parsed);
4119
+ const arrays = values.filter(Array.isArray);
4120
+ if (arrays.length === 1) {
4121
+ return { success: true, data: arrays[0] };
4122
+ }
4123
+ }
4124
+ return { success: true, data: [] };
4125
+ }
4126
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
4127
+ const keys = Object.keys(parsed);
4128
+ if (keys.length === 1) {
4129
+ const innerValue = parsed[keys[0]];
4130
+ if (typeof innerValue === "object" && innerValue !== null && !Array.isArray(innerValue)) {
4131
+ const innerKeys = Object.keys(innerValue);
4132
+ if (innerKeys.length > 1) {
4133
+ return { success: true, data: innerValue };
4134
+ }
4135
+ }
4136
+ }
4137
+ return { success: true, data: parsed };
4138
+ }
4139
+ return { success: true, data: parsed };
4140
+ } catch {
4141
+ if (allowStringFallback && defaultValue !== void 0) {
4142
+ return { success: true, data: defaultValue };
4143
+ }
4144
+ return { success: false, error: "Invalid JSON format" };
4145
+ }
4146
+ }
4149
4147
  async function orchestrateCommand(action, args) {
4150
4148
  try {
4151
4149
  const orchestrator = new Orchestrator();
@@ -4167,18 +4165,21 @@ async function orchestrateCommand(action, args) {
4167
4165
  outputError("Task ID is required for explore-done");
4168
4166
  return;
4169
4167
  }
4170
- let exploreOutput = {
4168
+ const defaultExplore = {
4171
4169
  summary: "",
4172
4170
  relevantFiles: [],
4173
4171
  patterns: [],
4174
4172
  constraints: [],
4175
4173
  dependencies: []
4176
4174
  };
4175
+ let exploreOutput = defaultExplore;
4177
4176
  if (exploreJson) {
4178
- try {
4179
- exploreOutput = JSON.parse(exploreJson);
4180
- } catch {
4181
- exploreOutput.summary = exploreJson;
4177
+ const parsed = parseFlexibleJSON(exploreJson, {
4178
+ allowStringFallback: true,
4179
+ defaultValue: { ...defaultExplore, summary: exploreJson }
4180
+ });
4181
+ if (parsed.success) {
4182
+ exploreOutput = { ...defaultExplore, ...parsed.data };
4182
4183
  }
4183
4184
  }
4184
4185
  const result = orchestrator.cmdExploreDone(taskId, exploreOutput);
@@ -4194,13 +4195,15 @@ async function orchestrateCommand(action, args) {
4194
4195
  }
4195
4196
  let questions = [];
4196
4197
  if (questionsJson) {
4197
- try {
4198
- const parsed = JSON.parse(questionsJson);
4199
- questions = Array.isArray(parsed) ? parsed : parsed.questions || [];
4200
- } catch {
4201
- outputError("Invalid JSON for questions");
4198
+ const parsed = parseFlexibleJSON(questionsJson, {
4199
+ expectArray: true,
4200
+ arrayKeys: ["questions"]
4201
+ });
4202
+ if (!parsed.success) {
4203
+ outputError(parsed.error);
4202
4204
  return;
4203
4205
  }
4206
+ questions = parsed.data;
4204
4207
  }
4205
4208
  const result = orchestrator.cmdClarifyDone(taskId, questions);
4206
4209
  outputJSON(result);
@@ -4223,7 +4226,7 @@ async function orchestrateCommand(action, args) {
4223
4226
  outputError("Task ID is required for verify-done");
4224
4227
  return;
4225
4228
  }
4226
- let verifyOutput = {
4229
+ const defaultVerify = {
4227
4230
  passed: false,
4228
4231
  score: {
4229
4232
  requirementsCoverage: 0,
@@ -4234,13 +4237,14 @@ async function orchestrateCommand(action, args) {
4234
4237
  issues: [],
4235
4238
  summary: ""
4236
4239
  };
4240
+ let verifyOutput = defaultVerify;
4237
4241
  if (verifyJson) {
4238
- try {
4239
- verifyOutput = JSON.parse(verifyJson);
4240
- } catch {
4241
- outputError("Invalid JSON for verify output");
4242
+ const parsed = parseFlexibleJSON(verifyJson, {});
4243
+ if (!parsed.success) {
4244
+ outputError(parsed.error);
4242
4245
  return;
4243
4246
  }
4247
+ verifyOutput = { ...defaultVerify, ...parsed.data };
4244
4248
  }
4245
4249
  const result = orchestrator.cmdVerifyDone(taskId, verifyOutput);
4246
4250
  outputJSON(result);
@@ -4253,14 +4257,15 @@ async function orchestrateCommand(action, args) {
4253
4257
  outputError("Task ID and answers JSON are required for clarified");
4254
4258
  return;
4255
4259
  }
4256
- let answers;
4257
- try {
4258
- answers = JSON.parse(answersJson);
4259
- } catch {
4260
- outputError("Invalid JSON for answers");
4260
+ const parsed = parseFlexibleJSON(answersJson, {
4261
+ expectArray: true,
4262
+ arrayKeys: ["answers"]
4263
+ });
4264
+ if (!parsed.success) {
4265
+ outputError(parsed.error);
4261
4266
  return;
4262
4267
  }
4263
- const result = orchestrator.cmdClarified(taskId, answers);
4268
+ const result = orchestrator.cmdClarified(taskId, parsed.data);
4264
4269
  outputJSON(result);
4265
4270
  break;
4266
4271
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spets",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "description": "Spec Driven Development Execution Framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",