lee-spec-kit 0.7.10 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +7 -6
- package/README.md +7 -6
- package/dist/index.js +192 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/en/common/README.md +6 -4
- package/templates/en/common/agents/agents.md +8 -4
- package/templates/en/common/agents/skills/execute-task.md +5 -1
- package/templates/en/common/features/README.md +3 -2
- package/templates/en/common/features/feature-base/spec.md +1 -1
- package/templates/en/common/features/feature-base/tasks.md +6 -3
- package/templates/en/common/ideas/README.md +2 -2
- package/templates/en/common/prd/README.md +2 -2
- package/templates/ko/common/README.md +6 -4
- package/templates/ko/common/agents/agents.md +8 -4
- package/templates/ko/common/agents/skills/execute-task.md +5 -1
- package/templates/ko/common/features/README.md +3 -2
- package/templates/ko/common/features/feature-base/spec.md +1 -1
- package/templates/ko/common/features/feature-base/tasks.md +6 -3
- package/templates/ko/common/ideas/README.md +2 -2
- package/templates/ko/common/prd/README.md +2 -2
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { spawn, spawnSync, execFileSync, execSync } from 'child_process';
|
|
|
9
9
|
import os from 'os';
|
|
10
10
|
import { createHash, randomUUID } from 'crypto';
|
|
11
11
|
import fs13 from 'fs';
|
|
12
|
+
import fs20 from 'fs/promises';
|
|
12
13
|
import { Buffer as Buffer$1 } from 'buffer';
|
|
13
14
|
|
|
14
15
|
var getFilename = () => fileURLToPath(import.meta.url);
|
|
@@ -212,7 +213,7 @@ var koCli = {
|
|
|
212
213
|
"doctor.issue.tasksEmpty": "tasks.md\uC5D0 \uD0DC\uC2A4\uD06C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
213
214
|
"doctor.issue.tasksDocStatusUnset": "tasks.md\uC758 \uBB38\uC11C \uC0C1\uD0DC(Doc Status)\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (Draft/Review/Approved \uC911 \uD558\uB098\uB85C \uC124\uC815\uD558\uC138\uC694.)",
|
|
214
215
|
"doctor.issue.tasksDocStatusMissing": "tasks.md\uC5D0 \uBB38\uC11C \uC0C1\uD0DC(Doc Status) \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. `- **\uBB38\uC11C \uC0C1\uD0DC**: -`\uC640 `\uAC12: Draft | Review | Approved`\uB97C \uCD94\uAC00\uD558\uC138\uC694.",
|
|
215
|
-
"doctor.issue.tasksPrdTagUnknown": "tasks.md\uC5D0 \uC815\uC758\uB418\uC9C0 \uC54A\uC740 PRD \uD0DC\uADF8\uAC00 \uC788\uC2B5\uB2C8\uB2E4: {ids}{extra}. tasks.md\uC5D0\uC11C PRD
|
|
216
|
+
"doctor.issue.tasksPrdTagUnknown": "tasks.md\uC5D0 \uC815\uC758\uB418\uC9C0 \uC54A\uC740 PRD \uD0DC\uADF8\uAC00 \uC788\uC2B5\uB2C8\uB2E4: {ids}{extra}. tasks.md\uC5D0\uC11C `PRD-*` ID\uB97C \uC784\uC758\uB85C \uB9CC\uB4E4\uC9C0 \uB9D0\uACE0, \uBA3C\uC800 docs/prd \uB610\uB294 \uC0C1\uC704 \uC694\uAD6C\uC0AC\uD56D \uBB38\uC11C\uC5D0 ID\uB97C backfill\uD55C \uB4A4 spec.md `PRD Refs`\uC640 tasks \uD0DC\uADF8\uB97C \uD568\uAED8 \uB9DE\uCD94\uC138\uC694.",
|
|
216
217
|
"doctor.issue.unmanagedDocsEntry": "lee-spec-kit \uBB38\uC11C \uD45C\uBA74 \uBC16\uC758 \uBB38\uC11C \uC5D4\uD2B8\uB9AC\uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4: {path}. \uCD5C\uC885 SSOT\uB85C \uC4F0\uC9C0 \uB9D0\uACE0 feature-local docs\uB85C \uC815\uADDC\uD654\uD558\uAC70\uB098, \uC758\uB3C4\uB41C \uACBD\uB85C\uB77C\uBA74 `.lee-spec-kit.json`\uC758 `allowedDocsEntries`\uC5D0 \uCD94\uAC00\uD558\uC138\uC694.",
|
|
217
218
|
"doctor.issue.duplicateFeatureId": "\uC911\uBCF5 Feature ID \uAC10\uC9C0: {id} ({count}\uAC1C)",
|
|
218
219
|
"doctor.issue.missingFeatureId": "Feature \uD3F4\uB354\uBA85\uC774 F001-... \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4. (ID\uB97C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC74C)",
|
|
@@ -478,7 +479,7 @@ var koContext = {
|
|
|
478
479
|
"context.checkRequired": "[\uD655\uC778 \uD544\uC694] ",
|
|
479
480
|
"context.checkPolicyHint": "\u2139\uFE0F \uC0AC\uC6A9\uC790 \uD655\uC778 \uC815\uCC45\uC740 \uC138\uC158 \uC2DC\uC791(\uB610\uB294 context \uC555\uCD95/\uB9AC\uC14B \uC9C1\uD6C4)\uC5D0 1\uD68C \uD655\uC778\uD558\uACE0, \uC774\uD6C4\uC5D0\uB294 \uC815\uCC45/\uC124\uC815 \uBCC0\uACBD \uB610\uB294 \uC0AC\uC6A9\uC790 \uC0C8\uB85C\uACE0\uCE68 \uC694\uCCAD \uC2DC\uC5D0\uB9CC \uC7AC\uD655\uC778\uD558\uC138\uC694. (git push/merge/merge commit \uD3EC\uD568) [\uD655\uC778 \uD544\uC694]\uAC00 \uC788\uC73C\uBA74 \uB77C\uBCA8 \uD1A0\uD070 \uADDC\uCE59(`A`, `A OK`, `A \uC9C4\uD589\uD574`)\uC5D0 \uB9DE\uB294 \uC751\uB2F5\uC744 \uBC1B\uC740 \uB4A4 \uC9C4\uD589\uD558\uC138\uC694. (config: approval\uB85C \uC870\uC815 \uAC00\uB2A5)",
|
|
480
481
|
"context.actionOptionHint": "\uB77C\uBCA8 \uC751\uB2F5 \uADDC\uCE59: `A`, `A OK`, `A \uC9C4\uD589\uD574` \uC911 \uD558\uB098\uC758 \uD615\uC2DD\uC73C\uB85C \uC751\uB2F5",
|
|
481
|
-
"context.actionExplainHint": "CLI\uAC00 \uC900 \uC2B9\uC778 \uBB38\uAD6C\uB97C \
|
|
482
|
+
"context.actionExplainHint": "\uC2B9\uC778 \uB300\uAE30 \uC0C1\uD0DC\uB77C\uBA74 `matchedFeature.currentSubstate*` \uAE30\uBC18 \uD604\uC7AC \uB2E8\uACC4 \uD55C \uC904 \uC694\uC57D\uC744 \uBA3C\uC800 \uBD99\uC77C \uC218 \uC788\uACE0, \uADF8 \uB2E4\uC74C CLI\uAC00 \uC900 \uC2B9\uC778 \uBB38\uAD6C\uB97C \uADF8\uB300\uB85C \uBCF4\uC5EC\uC8FC\uC138\uC694. \uCD94\uAC00 \uC124\uBA85\uC740 \uC0AC\uC6A9\uC790\uAC00 \uBB3C\uC744 \uB54C\uB9CC \uB367\uBD99\uC774\uACE0, \uC2B9\uC778 \uBB38\uAD6C \uC790\uCCB4\uB294 \uBC14\uAFB8\uC9C0 \uB9C8\uC138\uC694.",
|
|
482
483
|
"context.finalLabelPrompt": "\uD604\uC7AC \uC120\uD0DD \uAC00\uB2A5\uD55C \uB77C\uBCA8: {labels}. \uB77C\uBCA8 \uC751\uB2F5 \uADDC\uCE59(`A`, `A OK`, `A \uC9C4\uD589\uD574`)\uC73C\uB85C \uC751\uB2F5\uD558\uC138\uC694. (\uC608: `{example}`)",
|
|
483
484
|
"context.finalLabelPromptWithRequest": "\uD604\uC7AC \uC120\uD0DD \uAC00\uB2A5\uD55C \uB77C\uBCA8: {labels}. \uB77C\uBCA8 \uC751\uB2F5 \uADDC\uCE59(`A`, `A OK`, `A \uC9C4\uD589\uD574`)\uC73C\uB85C \uC751\uB2F5\uD558\uC138\uC694. (\uC608: `{example}`) \uC694\uCCAD \uD14D\uC2A4\uD2B8\uAC00 \uD544\uC694\uD55C \uB77C\uBCA8\uC740 \uB2E4\uC74C \uD615\uC2DD\uC73C\uB85C \uC785\uB825\uD558\uC138\uC694: {requestExamples}",
|
|
484
485
|
"context.suggestionHeader": "\uCD94\uCC9C \uB2E4\uC74C \uC120\uD0DD\uC9C0",
|
|
@@ -508,6 +509,7 @@ var koContext = {
|
|
|
508
509
|
"context.actionDetail.tasksWriteCreate": "tasks.md\uB97C \uC0DD\uC131\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C Review\uB85C \uC124\uC815\uD558\uC138\uC694",
|
|
509
510
|
"context.actionDetail.tasksWriteNeedAtLeastOne": "tasks.md\uC5D0 \uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uD0DC\uC2A4\uD06C\uB97C \uCD94\uAC00\uD558\uC138\uC694",
|
|
510
511
|
"context.actionDetail.tasksWriteImprove": "tasks.md\uB97C \uBCF4\uC644\uD558\uACE0 \uBB38\uC11C \uC0C1\uD0DC\uB97C \uC815\uB82C\uD558\uC138\uC694",
|
|
512
|
+
"context.actionDetail.tasksWriteChangeSync": "\uCD94\uAC00 \uAD6C\uD604 \uC804\uC5D0 \uC0C8 \uC0AC\uC6A9\uC790 \uC694\uCCAD\uC744 tasks.md\uC5D0 \uBA3C\uC800 \uBC18\uC601\uD558\uC138\uC694",
|
|
511
513
|
"context.actionDetail.tasksApprove": "tasks.md\uB97C \uC2B9\uC778\uD569\uB2C8\uB2E4",
|
|
512
514
|
"context.actionDetail.issueCreate": "\uC774\uC288\uB97C \uC0DD\uC131\uD558\uACE0 tasks.md\uC758 \uC774\uC288 \uC815\uBCF4\uB97C \uB9DE\uCD94\uC138\uC694",
|
|
513
515
|
"context.actionDetail.issueCreateAndWrite": "\uC774\uC288 \uCD08\uC548\uC744 \uBCF4\uC644\uD558\uACE0 \uB77C\uBCA8 \uC2B9\uC778(`A` \uB610\uB294 `A OK`) \uD6C4 \uC774\uC288\uB97C \uC0DD\uC131\uD574 \uBC88\uD638\uB97C \uB3D9\uAE30\uD654\uD558\uC138\uC694",
|
|
@@ -680,6 +682,7 @@ var koMessages = {
|
|
|
680
682
|
featureScopeSplitTwo: "Feature \uBC94\uC704\uAC00 \uD07D\uB2C8\uB2E4. (tasks: {taskCount}, decisions \uC904\uC218: {decisionsLineCount}) \uAD8C\uC7A5 \uADDC\uCE59\uC0C1 40~79 \uD0DC\uC2A4\uD06C\uC774\uBA74\uC11C \uD558\uB4DC \uAE30\uC900 \uBBF8\uB9CC\uC774\uBA74 2\uAC1C \uC774\uC288 \uBD84\uD560\uC774 \uAE30\uBCF8\uC785\uB2C8\uB2E4. `{guideCommand}`\uB97C \uB530\uB77C \uACB0\uD569\uB3C4/\uD30C\uC77C\uACB9\uCE68/\uD14C\uC2A4\uD2B8/\uBC30\uD3EC \uAE30\uC900\uC73C\uB85C \uBD84\uD560\uD558\uC138\uC694. \uAC01 \uC774\uC288\uC5D0\uB294 \uB2E4\uC74C \uD15C\uD50C\uB9BF\uC744 \uAE30\uB85D\uD558\uC138\uC694: \uBAA9\uD45C, \uD3EC\uD568 \uBC94\uC704, \uC81C\uC678 \uBC94\uC704, \uC120\uD589 \uC758\uC874\uC131, PR \uC644\uB8CC \uAE30\uC900.",
|
|
681
683
|
featureScopeSplitFour: "Feature \uBC94\uC704\uAC00 \uD07D\uB2C8\uB2E4. (tasks: {taskCount}, decisions \uC904\uC218: {decisionsLineCount}) tasks >= {recommendFourTaskThreshold} \uB610\uB294 decisions \uC904\uC218 >= {recommendFourDecisionsThreshold}\uC774\uBA74 4\uAC1C \uC774\uC288 \uBD84\uD560\uC744 \uAC15\uD558\uAC8C \uAD8C\uC7A5\uD569\uB2C8\uB2E4. `{guideCommand}`\uB97C \uB530\uB77C 4\uAC1C\uC758 \uC5F0\uAD00 \uC774\uC288\uB85C \uBD84\uB9AC\uD558\uACE0 \uC758\uC874 \uC21C\uC11C\uB97C \uBA85\uC2DC\uD55C \uB4A4 PR\uC744 \uC21C\uCC28 \uBA38\uC9C0\uD558\uC138\uC694. \uAC01 \uC774\uC288 \uD15C\uD50C\uB9BF: \uBAA9\uD45C, \uD3EC\uD568 \uBC94\uC704, \uC81C\uC678 \uBC94\uC704, \uC120\uD589 \uC758\uC874\uC131, PR \uC644\uB8CC \uAE30\uC900.",
|
|
682
684
|
userRequestReplan: "\uD604\uC7AC \uB2E8\uACC4\uC640 \uBCC4\uAC1C\uB85C \uC0AC\uC6A9\uC790\uAC00 \uC81C\uC548\uD55C \uC0C8 \uC694\uAD6C\uB97C \uBA3C\uC800 \uBC18\uC601\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uC694\uAD6C\uC0AC\uD56D\uC744 \uC694\uC57D\uD574 tasks.md\uC5D0 \uCD94\uAC00\uD558\uAC70\uB098 \uBCC4\uB3C4 Feature\uB85C \uBD84\uB9AC\uD55C \uB4A4, \uBB38\uC11C \uC0C1\uD0DC\uB97C \uB9DE\uCD94\uACE0 context\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
|
|
685
|
+
changeRequestSync: 'tasks.md\uC5D0 \uC0C8 \uC0AC\uC6A9\uC790 \uC694\uCCAD\uC774 \uAE30\uB85D\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: "{request}". \uCD94\uAC00 \uAD6C\uD604 \uC804\uC5D0 \uBA3C\uC800 tasks.md\uB97C \uAC31\uC2E0\uD558\uC138\uC694. \uC601\uD5A5\uBC1B\uB294 \uD0DC\uC2A4\uD06C\uB97C \uCD94\uAC00\uD558\uAC70\uB098 \uC7AC\uD0DC\uAE45\uD558\uACE0, \uC774 \uC694\uCCAD\uC774 \uC2E4\uC81C \uC0AC\uC6A9\uC790 \uB3D9\uC791\uC774\uB098 \uBC94\uC704\uB97C \uBC14\uAFB8\uBA74 decisions.md\uC640 spec/PRD \uCC38\uC870\uB3C4 \uD568\uAED8 \uB9DE\uCD94\uC138\uC694. \uB3D9\uAE30\uD654\uAC00 \uB05D\uB098\uBA74 `\uB300\uAE30 \uC911 \uBCC0\uACBD \uC694\uCCAD` \uAC12\uC744 \uBE44\uC6B0\uACE0 context/flow\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.',
|
|
683
686
|
docsUnmanagedNormalize: "lee-spec-kit \uBB38\uC11C \uD45C\uBA74 \uBC16\uC758 \uBB38\uC11C \uC5D4\uD2B8\uB9AC\uAC00 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4: {paths}. Feature `{featureRef}`\uB97C \uACC4\uC18D \uC9C4\uD589\uD558\uAE30 \uC804\uC5D0 \uC774 \uBB38\uC11C\uB4E4\uC740 reference \uC785\uB825\uC73C\uB85C\uB9CC \uCDE8\uAE09\uD558\uC138\uC694. \uC0AC\uC6A9\uC790 \uBC94\uC704/acceptance\uB294 `spec.md`, \uC544\uD0A4\uD14D\uCC98/\uD30C\uC77C/\uD14C\uC2A4\uD2B8 \uB0B4\uC6A9\uC740 `plan.md`, \uC2E4\uD589 \uC791\uC5C5\uC740 `tasks.md`, \uADFC\uAC70/\uD2B8\uB808\uC774\uB4DC\uC624\uD504\uB294 `decisions.md`\uB85C \uC815\uADDC\uD654\uD55C \uB4A4 `npx lee-spec-kit context {featureRef}`\uB97C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.",
|
|
684
687
|
featureDone: "\uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC694\uAD6C\uC0AC\uD56D\uACFC \uB85C\uCEEC \uC815\uB9AC\uAE4C\uC9C0 \uBAA8\uB450 \uB05D\uB0AC\uC2B5\uB2C8\uB2E4. \uC774 Feature\uB294 \uC644\uC804\uD788 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
685
688
|
fallbackRerunContext: "\uC0C1\uD0DC\uB97C \uD310\uBCC4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC11C\uB97C \uD655\uC778\uD55C \uB4A4 \uB2E4\uC2DC context\uB97C \uC2E4\uD589\uD558\uC138\uC694."
|
|
@@ -789,7 +792,7 @@ var enCli = {
|
|
|
789
792
|
"doctor.issue.tasksEmpty": "tasks.md has no tasks.",
|
|
790
793
|
"doctor.issue.tasksDocStatusUnset": "tasks.md Doc Status is not set. (Set it to Draft, Review, or Approved.)",
|
|
791
794
|
"doctor.issue.tasksDocStatusMissing": "tasks.md is missing the Doc Status field. Add `- **Doc Status**: -` and `Values: Draft | Review | Approved`.",
|
|
792
|
-
"doctor.issue.tasksPrdTagUnknown": "tasks.md uses PRD tags with no matching source definition: {ids}{extra}. Do not invent IDs
|
|
795
|
+
"doctor.issue.tasksPrdTagUnknown": "tasks.md uses PRD tags with no matching source definition: {ids}{extra}. Do not invent `PRD-*` IDs in tasks.md. Backfill IDs in docs/prd or the upstream requirements doc first, then align spec.md `PRD Refs` and task tags.",
|
|
793
796
|
"doctor.issue.unmanagedDocsEntry": "Unmanaged docs entry detected outside the lee-spec-kit docs surface: {path}. Treat it as reference input only, normalize it into feature-local docs, or allowlist it in `.lee-spec-kit.json` `allowedDocsEntries`.",
|
|
794
797
|
"doctor.issue.duplicateFeatureId": "Duplicate Feature ID detected: {id} ({count})",
|
|
795
798
|
"doctor.issue.missingFeatureId": "Feature folder name is not in F001-... format. (Cannot extract ID)",
|
|
@@ -1055,7 +1058,7 @@ var enContext = {
|
|
|
1055
1058
|
"context.checkRequired": "[CHECK required] ",
|
|
1056
1059
|
"context.checkPolicyHint": "\u2139\uFE0F Check user-approval policy once at session start (or right after context compression/reset); re-check only when policy/config changes or the user explicitly requests refresh. (includes git push/merge and merge commits). If you see [CHECK required], wait for a reply that follows label-token rules (`A`, `A OK`, `A proceed`) before proceeding (config: approval can override)",
|
|
1057
1060
|
"context.actionOptionHint": "Label reply rules: answer as `A`, `A OK`, or `A proceed`",
|
|
1058
|
-
"context.actionExplainHint": "
|
|
1061
|
+
"context.actionExplainHint": "If approval is pending, you may add one brief current-stage recap from `matchedFeature.currentSubstate*`, then use the exact approval lines from the CLI. Add further explanation only if the user asks, and do not paraphrase the approval prompts.",
|
|
1059
1062
|
"context.finalLabelPrompt": "Available labels now: {labels}. Reply using label-token rules (`A`, `A OK`, `A proceed`). (e.g. `{example}`)",
|
|
1060
1063
|
"context.finalLabelPromptWithRequest": "Available labels now: {labels}. Reply using label-token rules (`A`, `A OK`, `A proceed`). (e.g. `{example}`) Labels that require a request must be replied as: {requestExamples}",
|
|
1061
1064
|
"context.suggestionHeader": "Suggested Next Options",
|
|
@@ -1085,6 +1088,7 @@ var enContext = {
|
|
|
1085
1088
|
"context.actionDetail.tasksWriteCreate": "Create tasks.md and set Doc Status to Review",
|
|
1086
1089
|
"context.actionDetail.tasksWriteNeedAtLeastOne": "Add at least one task to tasks.md",
|
|
1087
1090
|
"context.actionDetail.tasksWriteImprove": "Refine tasks.md and align Doc Status",
|
|
1091
|
+
"context.actionDetail.tasksWriteChangeSync": "Sync the new user request into tasks.md before more implementation",
|
|
1088
1092
|
"context.actionDetail.tasksApprove": "Approve tasks.md",
|
|
1089
1093
|
"context.actionDetail.issueCreate": "Create the issue and sync issue fields in tasks.md",
|
|
1090
1094
|
"context.actionDetail.issueCreateAndWrite": "Draft issue content, get explicit OK, then create and sync Issue",
|
|
@@ -1257,6 +1261,7 @@ var enMessages = {
|
|
|
1257
1261
|
featureScopeSplitTwo: "Feature scope is large (tasks: {taskCount}, decisions lines: {decisionsLineCount}). Recommendation rule: 40~79 tasks and below the hard threshold usually maps to 2 issues. Follow `{guideCommand}` and split by coupling/file-overlap/test/deploy criteria. For each new issue, document this template: Goal, Included Scope, Excluded Scope, Depends On, PR Done Criteria.",
|
|
1258
1262
|
featureScopeSplitFour: "Feature scope is large (tasks: {taskCount}, decisions lines: {decisionsLineCount}). Hard recommendation is 4 issues when tasks >= {recommendFourTaskThreshold} or decisions lines >= {recommendFourDecisionsThreshold}. Follow `{guideCommand}` and split into 4 linked issues with explicit dependency order and sequential PR merges. Use the per-issue template: Goal, Included Scope, Excluded Scope, Depends On, PR Done Criteria.",
|
|
1259
1263
|
userRequestReplan: "You can pause this step and handle a newly requested user requirement first. Summarize it, add it to tasks.md or split it into a separate Feature, then align document statuses and rerun context.",
|
|
1264
|
+
changeRequestSync: 'A pending user request is recorded in tasks.md: "{request}". Before more implementation, update tasks.md first. Add or retag the affected task, and if this changes shipped behavior or scope, sync decisions.md plus spec/PRD references as needed. Clear `Pending Change Request` after syncing, then rerun context/flow.',
|
|
1260
1265
|
docsUnmanagedNormalize: "Unmanaged docs entries were found outside the lee-spec-kit docs surface: {paths}. Before continuing feature `{featureRef}`, treat them as reference inputs only. Normalize user-facing scope and acceptance into `spec.md`, architecture/file/test details into `plan.md`, executable work into `tasks.md`, and rationale/trade-offs into `decisions.md`, then rerun `npx lee-spec-kit context {featureRef}`.",
|
|
1261
1266
|
featureDone: "All workflow requirements and local cleanup are complete. This feature is fully finished.",
|
|
1262
1267
|
fallbackRerunContext: "Cannot determine status. Check the docs and run context again."
|
|
@@ -2027,25 +2032,24 @@ Auto-run continuity (main/sub-agent orchestration):
|
|
|
2027
2032
|
3. Else run \`npx lee-spec-kit context --json-compact\` (fallback: \`--json\`) and continue from current \`actionOptions\`/\`autoRun\`.
|
|
2028
2033
|
- Pause and report to user only when:
|
|
2029
2034
|
- \`approvalRequest.required === true\`, or
|
|
2030
|
-
- \`autoRun.reasonCode\` is \`AUTO_GATE_REACHED\`, \`AUTO_DELEGATED_HANDOFF\`, or \`
|
|
2035
|
+
- \`autoRun.reasonCode\` is \`AUTO_GATE_REACHED\`, \`AUTO_DELEGATED_HANDOFF\`, \`AUTO_MANUAL_REQUIRED\`, or \`AUTO_SELECTION_REQUIRED\`, or
|
|
2031
2036
|
- command execution fails (non-zero/error), or
|
|
2032
2037
|
- user explicitly asks to pause.
|
|
2038
|
+
- Special case: if \`matchedFeature.currentSubstateId === "change_request_sync"\` or \`matchedFeature.pendingChangeRequest\` is present, treat that \`AUTO_MANUAL_REQUIRED\` state as an internal docs-sync step first, not an immediate user-facing stop. Update \`tasks.md\` first, and if shipped behavior or scope changed, sync \`decisions.md\` plus \`spec.md\` / PRD refs as needed. Clear the pending change field after syncing, then continue with fresh \`context\`/\`flow\`. Only pause/report there if approval becomes required, execution fails, or the user explicitly asked to pause.
|
|
2033
2039
|
|
|
2034
2040
|
User-facing output rule (state-aware):
|
|
2035
2041
|
|
|
2036
2042
|
- Treat approval as a separate state.
|
|
2037
|
-
- Approval-waiting state means:
|
|
2038
|
-
|
|
2039
|
-
- you are explicitly waiting for user approval before execution.
|
|
2043
|
+
- Approval-waiting state means the latest \`context --json-compact\` / \`flow --json-compact\` (fallback: \`context --json\` / \`flow --json\`) shows \`approvalRequest.required === true\`.
|
|
2044
|
+
- Do not infer approval-waiting from conversation tone, action type, or whether \`actionOptions[]\` merely exist.
|
|
2040
2045
|
|
|
2041
2046
|
In approval-waiting state:
|
|
2042
2047
|
|
|
2043
|
-
1.
|
|
2044
|
-
2.
|
|
2045
|
-
3. Do not paraphrase or omit
|
|
2046
|
-
4. Prefer \`
|
|
2047
|
-
5.
|
|
2048
|
-
6. When \`matchedFeature.currentSubstateOwner="subagent"\` and \`agentOrchestration.subAgentHandoff.required=true\` with \`mode="command"\`, call \`spawn_agent\` first and do not execute the delegated command directly from the main agent. If the delegated command is handoff-only, continue the delegated work immediately and do not re-open the same approval label.
|
|
2048
|
+
1. If \`matchedFeature.currentSubstateId\` is present, prepend one brief current-stage recap derived from \`matchedFeature.currentSubstateId/currentSubstateOwner/currentSubstatePhase\`.
|
|
2049
|
+
2. Then show \`approvalRequest.userFacingLines\` exactly as provided. If those lines are unavailable, fall back to \`actionOptions[*].approvalPrompt\` plus \`approvalRequest.finalPrompt\`.
|
|
2050
|
+
3. Do not paraphrase, reorder, or omit the CLI-provided approval lines.
|
|
2051
|
+
4. Prefer \`matchedFeature.currentSubstateOwner\` plus \`agentOrchestration.subAgentHandoff\` as the delegation SSOT.
|
|
2052
|
+
5. When \`matchedFeature.currentSubstateOwner="subagent"\` and \`agentOrchestration.subAgentHandoff.required=true\` with \`mode="command"\`, call \`spawn_agent\` first and do not execute the delegated command directly from the main agent. If the delegated command is handoff-only, continue the delegated work immediately and do not re-open the same approval label.
|
|
2049
2053
|
|
|
2050
2054
|
In non-approval state (progress updates, analysis, tool execution logs, unrelated Q&A):
|
|
2051
2055
|
|
|
@@ -2056,10 +2060,11 @@ In non-approval state (progress updates, analysis, tool execution logs, unrelate
|
|
|
2056
2060
|
If approval is still pending after answering an unrelated question:
|
|
2057
2061
|
|
|
2058
2062
|
- First answer the question.
|
|
2059
|
-
-
|
|
2060
|
-
|
|
2061
|
-
- \`
|
|
2062
|
-
-
|
|
2063
|
+
- Re-check the latest \`approvalRequest.required\`.
|
|
2064
|
+
- If it is still \`true\`, re-open approval with:
|
|
2065
|
+
- one brief current-stage recap from \`matchedFeature.currentSubstateId/currentSubstateOwner/currentSubstatePhase\` when available, then
|
|
2066
|
+
- the exact CLI-provided approval lines from \`approvalRequest.userFacingLines\` (fallback: \`actionOptions[*].approvalPrompt\` + \`approvalRequest.finalPrompt\`).
|
|
2067
|
+
- Never output \`finalPrompt\` alone without the matching \`A: ...\` prompt, and do not add custom label-summary wrappers such as \`Available labels:\`.`;
|
|
2063
2068
|
function renderManagedSegment(lang, docsRepo) {
|
|
2064
2069
|
return `${LEE_SPEC_KIT_AGENTS_BEGIN}
|
|
2065
2070
|
${CANONICAL_LEE_SPEC_KIT_AGENTS_TEXT}
|
|
@@ -4282,6 +4287,9 @@ function isCompletionChecklistDone(feature) {
|
|
|
4282
4287
|
function isTasksDocApproved(feature) {
|
|
4283
4288
|
return !feature.docs.tasksDocStatusFieldExists || feature.tasksDocStatus === "Approved";
|
|
4284
4289
|
}
|
|
4290
|
+
function hasPendingChangeRequest(feature) {
|
|
4291
|
+
return typeof feature.pendingChangeRequest === "string" && feature.pendingChangeRequest.trim().length > 0;
|
|
4292
|
+
}
|
|
4285
4293
|
function isImplementationDone(feature) {
|
|
4286
4294
|
return feature.docs.tasksExists && feature.tasks.total > 0 && feature.tasks.total === feature.tasks.done && isCompletionChecklistDone(feature) && isTasksDocApproved(feature);
|
|
4287
4295
|
}
|
|
@@ -4652,7 +4660,18 @@ function getStepDefinitions(ctx) {
|
|
|
4652
4660
|
prePrReviewPolicy,
|
|
4653
4661
|
taskCommitGatePolicy
|
|
4654
4662
|
} = resolveWorkflowRuntime(ctx.config.workflow);
|
|
4655
|
-
const isTaskExecuteCurrent = (f) => f.docs.tasksExists && f.tasks.total > 0 && (f.tasks.done < f.tasks.total || !isCompletionChecklistDone(f)) && isTasksDocApproved(f) && (!workflowPolicy.requireBranch || f.git.onExpectedBranch || f.tasks.done === f.tasks.total);
|
|
4663
|
+
const isTaskExecuteCurrent = (f) => f.docs.tasksExists && f.tasks.total > 0 && (hasPendingChangeRequest(f) || f.tasks.done < f.tasks.total || !isCompletionChecklistDone(f)) && isTasksDocApproved(f) && (!workflowPolicy.requireBranch || f.git.onExpectedBranch || f.tasks.done === f.tasks.total);
|
|
4664
|
+
const getChangeRequestSyncActions = (f) => [
|
|
4665
|
+
{
|
|
4666
|
+
type: "instruction",
|
|
4667
|
+
category: "tasks_write",
|
|
4668
|
+
requiresUserCheck: false,
|
|
4669
|
+
uiDetailKey: "context.actionDetail.tasksWriteChangeSync",
|
|
4670
|
+
message: tr(lang, "messages", "changeRequestSync", {
|
|
4671
|
+
request: f.pendingChangeRequest || "-"
|
|
4672
|
+
})
|
|
4673
|
+
}
|
|
4674
|
+
];
|
|
4656
4675
|
const isTaskExecuteWorktreeBlocked = (f) => isTaskExecuteCurrent(f) && workflowPolicy.requireWorktree && f.tasks.done < f.tasks.total && !!f.issueNumber && !f.git.projectInManagedWorktree;
|
|
4657
4676
|
const isTaskExecuteFinalize = (f) => isTaskExecuteCurrent(f) && f.tasks.total === f.tasks.done && !isCompletionChecklistDone(f);
|
|
4658
4677
|
const isTaskExecuteCommitPending = (f) => isTaskExecuteCurrent(f) && (isTaskExecuteFinalize(f) && (f.git.docsHasCommitRequiredChanges || f.git.projectHasUncommittedChanges) || !!f.nextTodoTask && (f.git.docsHasCommitRequiredChanges || f.git.projectHasUncommittedChanges));
|
|
@@ -5453,6 +5472,14 @@ ${tr(lang, "messages", "prePrReviewDecisionReconfirm", {
|
|
|
5453
5472
|
detail: (f) => f.tasks.total > 0 ? `(${f.tasks.done}/${f.tasks.total})` : ""
|
|
5454
5473
|
},
|
|
5455
5474
|
substates: [
|
|
5475
|
+
{
|
|
5476
|
+
id: "change_request_sync",
|
|
5477
|
+
phase: "ready",
|
|
5478
|
+
owner: "main",
|
|
5479
|
+
category: "tasks_write",
|
|
5480
|
+
when: (f) => isTaskExecuteCurrent(f) && hasPendingChangeRequest(f),
|
|
5481
|
+
actions: (f) => getChangeRequestSyncActions(f)
|
|
5482
|
+
},
|
|
5456
5483
|
{
|
|
5457
5484
|
id: "task_blocked",
|
|
5458
5485
|
phase: "blocked",
|
|
@@ -6364,6 +6391,15 @@ function extractFirstSpecValue(content, keys) {
|
|
|
6364
6391
|
}
|
|
6365
6392
|
return void 0;
|
|
6366
6393
|
}
|
|
6394
|
+
function parsePendingChangeRequest(value) {
|
|
6395
|
+
if (!value) return void 0;
|
|
6396
|
+
const normalized = value.trim().replace(/\s+/g, " ");
|
|
6397
|
+
if (!normalized) return void 0;
|
|
6398
|
+
if (/^(?:-|#|none|n\/a|na|없음|미정)$/i.test(normalized)) {
|
|
6399
|
+
return void 0;
|
|
6400
|
+
}
|
|
6401
|
+
return normalized;
|
|
6402
|
+
}
|
|
6367
6403
|
function parseDocStatus(value) {
|
|
6368
6404
|
if (!value) return void 0;
|
|
6369
6405
|
const trimmed = value.trim();
|
|
@@ -7190,6 +7226,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
|
|
|
7190
7226
|
let lastDoneTask;
|
|
7191
7227
|
let nextTodoTask;
|
|
7192
7228
|
let tasksDocStatus;
|
|
7229
|
+
let pendingChangeRequest;
|
|
7193
7230
|
let tasksDocStatusFieldExists = false;
|
|
7194
7231
|
let completionChecklist;
|
|
7195
7232
|
let prePrReviewStatus;
|
|
@@ -7251,6 +7288,11 @@ async function parseFeature(ctx, featurePath, type, context, options) {
|
|
|
7251
7288
|
"Doc Status"
|
|
7252
7289
|
]);
|
|
7253
7290
|
tasksDocStatus = parseDocStatus(tasksDocStatusValue);
|
|
7291
|
+
const pendingChangeRequestValue = extractFirstSpecValue(content, [
|
|
7292
|
+
"Pending Change Request",
|
|
7293
|
+
"\uB300\uAE30 \uC911 \uBCC0\uACBD \uC694\uCCAD"
|
|
7294
|
+
]);
|
|
7295
|
+
pendingChangeRequest = parsePendingChangeRequest(pendingChangeRequestValue);
|
|
7254
7296
|
const prValue = extractFirstSpecValue(content, ["PR", "Pull Request"]);
|
|
7255
7297
|
prFieldExists = hasAnySpecKey(content, ["PR", "Pull Request"]);
|
|
7256
7298
|
prLink = parsePrLink(prValue);
|
|
@@ -7657,6 +7699,7 @@ async function parseFeature(ctx, featurePath, type, context, options) {
|
|
|
7657
7699
|
folderName,
|
|
7658
7700
|
type,
|
|
7659
7701
|
path: featurePath,
|
|
7702
|
+
pendingChangeRequest,
|
|
7660
7703
|
completion: {
|
|
7661
7704
|
implementationDone,
|
|
7662
7705
|
workflowDone,
|
|
@@ -10186,6 +10229,81 @@ async function consumeApprovalTicket(config, token, expected) {
|
|
|
10186
10229
|
}
|
|
10187
10230
|
|
|
10188
10231
|
// src/services/ActionExecutor.ts
|
|
10232
|
+
var PENDING_CHANGE_REQUEST_KEYS = [
|
|
10233
|
+
"Pending Change Request",
|
|
10234
|
+
"\uB300\uAE30 \uC911 \uBCC0\uACBD \uC694\uCCAD"
|
|
10235
|
+
];
|
|
10236
|
+
function escapeRegExp5(value) {
|
|
10237
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10238
|
+
}
|
|
10239
|
+
function findSpecLineIndex(lines, keys) {
|
|
10240
|
+
const escaped = keys.map((key) => escapeRegExp5(key));
|
|
10241
|
+
const re = new RegExp(
|
|
10242
|
+
`^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:`,
|
|
10243
|
+
"i"
|
|
10244
|
+
);
|
|
10245
|
+
return lines.findIndex((line) => re.test(line));
|
|
10246
|
+
}
|
|
10247
|
+
function replaceSpecLine(line, keys, preferredKey, value) {
|
|
10248
|
+
const escaped = keys.map((key) => escapeRegExp5(key));
|
|
10249
|
+
const re = new RegExp(
|
|
10250
|
+
`^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`,
|
|
10251
|
+
"i"
|
|
10252
|
+
);
|
|
10253
|
+
if (!re.test(line)) return line;
|
|
10254
|
+
return line.replace(re, `$1${preferredKey}$2${value}`);
|
|
10255
|
+
}
|
|
10256
|
+
function computeSpecInsertIndex(lines, anchorKeys) {
|
|
10257
|
+
const anchorIndex = findSpecLineIndex(lines, anchorKeys);
|
|
10258
|
+
if (anchorIndex !== -1) {
|
|
10259
|
+
let cursor = anchorIndex + 1;
|
|
10260
|
+
while (cursor < lines.length && /^\s{2,}-\s+/.test(lines[cursor])) {
|
|
10261
|
+
cursor += 1;
|
|
10262
|
+
}
|
|
10263
|
+
return cursor;
|
|
10264
|
+
}
|
|
10265
|
+
const sectionIndex = lines.findIndex(
|
|
10266
|
+
(line) => /^\s*##\s+(Task List|태스크 목록)\s*$/.test(line)
|
|
10267
|
+
);
|
|
10268
|
+
if (sectionIndex !== -1) return sectionIndex;
|
|
10269
|
+
return lines.length;
|
|
10270
|
+
}
|
|
10271
|
+
function upsertPendingChangeRequestLine(content, requestText, lang) {
|
|
10272
|
+
const normalizedRequest = requestText.trim().replace(/\s+/g, " ");
|
|
10273
|
+
const preferredKey = lang === "ko" ? "\uB300\uAE30 \uC911 \uBCC0\uACBD \uC694\uCCAD" : "Pending Change Request";
|
|
10274
|
+
const lines = content.split("\n");
|
|
10275
|
+
const index = findSpecLineIndex(lines, PENDING_CHANGE_REQUEST_KEYS);
|
|
10276
|
+
if (index !== -1) {
|
|
10277
|
+
lines[index] = replaceSpecLine(
|
|
10278
|
+
lines[index],
|
|
10279
|
+
PENDING_CHANGE_REQUEST_KEYS,
|
|
10280
|
+
preferredKey,
|
|
10281
|
+
normalizedRequest
|
|
10282
|
+
);
|
|
10283
|
+
return lines.join("\n");
|
|
10284
|
+
}
|
|
10285
|
+
const insertAt = computeSpecInsertIndex(lines, [
|
|
10286
|
+
"Branch",
|
|
10287
|
+
"\uBE0C\uB79C\uCE58",
|
|
10288
|
+
"Issue",
|
|
10289
|
+
"Issue Number",
|
|
10290
|
+
"\uC774\uC288 \uBC88\uD638"
|
|
10291
|
+
]);
|
|
10292
|
+
lines.splice(insertAt, 0, `- **${preferredKey}**: ${normalizedRequest}`);
|
|
10293
|
+
return lines.join("\n");
|
|
10294
|
+
}
|
|
10295
|
+
async function persistPendingChangeRequest(featurePath, requestText, lang) {
|
|
10296
|
+
const tasksPath = path16.join(featurePath, "tasks.md");
|
|
10297
|
+
let content;
|
|
10298
|
+
try {
|
|
10299
|
+
content = await fs20.readFile(tasksPath, "utf-8");
|
|
10300
|
+
} catch {
|
|
10301
|
+
return;
|
|
10302
|
+
}
|
|
10303
|
+
const nextContent = upsertPendingChangeRequestLine(content, requestText, lang);
|
|
10304
|
+
if (nextContent === content) return;
|
|
10305
|
+
await fs20.writeFile(tasksPath, nextContent, "utf-8");
|
|
10306
|
+
}
|
|
10189
10307
|
function executeCommandAction(cmd, jsonMode, cwd) {
|
|
10190
10308
|
const shellPath = process.env.SHELL || (process.platform === "win32" ? process.env.ComSpec || "cmd.exe" : "/bin/sh");
|
|
10191
10309
|
if (jsonMode) {
|
|
@@ -10326,6 +10444,17 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
|
|
|
10326
10444
|
action: freshSelected.action
|
|
10327
10445
|
});
|
|
10328
10446
|
const featureRef = freshState.matchedFeature.folderName;
|
|
10447
|
+
if (selectedAction.category === "user_request_replan" && userRequest && freshState.matchedFeature.docs.tasksExists) {
|
|
10448
|
+
await withFileLock(
|
|
10449
|
+
getDocsLockPath(config.docsDir),
|
|
10450
|
+
async () => persistPendingChangeRequest(
|
|
10451
|
+
freshState.matchedFeature.path,
|
|
10452
|
+
userRequest,
|
|
10453
|
+
lang
|
|
10454
|
+
),
|
|
10455
|
+
{ owner: "context-approve:pending-change-request" }
|
|
10456
|
+
);
|
|
10457
|
+
}
|
|
10329
10458
|
if (!options.execute) {
|
|
10330
10459
|
const ticket = executeRequiresTicket ? await issueApprovalTicket(config, {
|
|
10331
10460
|
contextVersion: freshState.contextVersion,
|
|
@@ -10893,7 +11022,7 @@ async function runContext(featureName, options) {
|
|
|
10893
11022
|
approvalRequest: {
|
|
10894
11023
|
guidance: approvalGuidance.replace(
|
|
10895
11024
|
"Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user.",
|
|
10896
|
-
"
|
|
11025
|
+
"If approval is pending, you may prepend one brief current-stage recap derived from `matchedFeature.currentSubstate*`, then user-facing output must reuse the exact approval prompts (`A: ...`) and `finalPrompt`."
|
|
10897
11026
|
),
|
|
10898
11027
|
required: approvalRequired,
|
|
10899
11028
|
finalPrompt: finalApprovalPrompt,
|
|
@@ -11245,9 +11374,10 @@ function printChecklist(f, stepDefinitions) {
|
|
|
11245
11374
|
console.log(` ${mark} ${definition.step}. ${label} ${detail}`);
|
|
11246
11375
|
});
|
|
11247
11376
|
}
|
|
11248
|
-
var
|
|
11377
|
+
var PRD_REQUIREMENT_ID_EXACT_RE = /^PRD-[A-Z0-9]+(?:-[A-Z0-9]+)*$/i;
|
|
11378
|
+
var PRD_REQUIREMENT_ID_RE = /\bPRD-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/gi;
|
|
11249
11379
|
function isPrdRequirementId(value) {
|
|
11250
|
-
return
|
|
11380
|
+
return PRD_REQUIREMENT_ID_EXACT_RE.test(value.trim());
|
|
11251
11381
|
}
|
|
11252
11382
|
function isNonPrdTag(value) {
|
|
11253
11383
|
const trimmed = value.trim();
|
|
@@ -12372,8 +12502,13 @@ function toCompactStatusReport(report) {
|
|
|
12372
12502
|
};
|
|
12373
12503
|
}
|
|
12374
12504
|
function buildAgentOrchestrationPolicy2(autoRun, featureRef) {
|
|
12505
|
+
const resumableStatuses = /* @__PURE__ */ new Set([
|
|
12506
|
+
"delegated_handoff",
|
|
12507
|
+
"gate_reached",
|
|
12508
|
+
"manual_required"
|
|
12509
|
+
]);
|
|
12375
12510
|
const resumeCommand = autoRun?.run?.resumeCommand || autoRun?.resume?.flowCommand || null;
|
|
12376
|
-
const handoffRequired = !!autoRun && !!resumeCommand;
|
|
12511
|
+
const handoffRequired = !!autoRun && !!resumeCommand && resumableStatuses.has(autoRun.status);
|
|
12377
12512
|
const verifyCacheKey = handoffRequired ? `${(featureRef || "unknown").toLowerCase()}|${Buffer$1.from(
|
|
12378
12513
|
resumeCommand
|
|
12379
12514
|
).toString("base64").slice(0, 12)}` : "";
|
|
@@ -12629,7 +12764,10 @@ function resolveAutoMode(config, options, requestText) {
|
|
|
12629
12764
|
if (requestText) {
|
|
12630
12765
|
return resolveConfigDefaultAutoMode(config);
|
|
12631
12766
|
}
|
|
12632
|
-
|
|
12767
|
+
if (options.approve || options.execute || options.resume) {
|
|
12768
|
+
return null;
|
|
12769
|
+
}
|
|
12770
|
+
return resolveConfigDefaultAutoMode(config);
|
|
12633
12771
|
}
|
|
12634
12772
|
function toAutoReasonCode(status) {
|
|
12635
12773
|
const map = {
|
|
@@ -12647,8 +12785,6 @@ function toAutoReasonCode(status) {
|
|
|
12647
12785
|
}
|
|
12648
12786
|
function isAutoRunFailureStatus(status) {
|
|
12649
12787
|
return [
|
|
12650
|
-
"manual_required",
|
|
12651
|
-
"selection_required",
|
|
12652
12788
|
"no_progress",
|
|
12653
12789
|
"request_label_missing",
|
|
12654
12790
|
"request_failed",
|
|
@@ -12660,10 +12796,10 @@ function toFlowRunStatus(status) {
|
|
|
12660
12796
|
case "delegated_handoff":
|
|
12661
12797
|
case "gate_reached":
|
|
12662
12798
|
case "manual_required":
|
|
12799
|
+
case "selection_required":
|
|
12663
12800
|
return "paused";
|
|
12664
12801
|
case "no_action_options":
|
|
12665
12802
|
return "completed";
|
|
12666
|
-
case "selection_required":
|
|
12667
12803
|
case "no_progress":
|
|
12668
12804
|
case "request_label_missing":
|
|
12669
12805
|
case "request_failed":
|
|
@@ -12757,7 +12893,7 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
12757
12893
|
executions,
|
|
12758
12894
|
gate: null,
|
|
12759
12895
|
manual: null,
|
|
12760
|
-
error: "Auto-run
|
|
12896
|
+
error: "Auto-run paused because a single matched feature is required before execution can continue."
|
|
12761
12897
|
};
|
|
12762
12898
|
}
|
|
12763
12899
|
if (actionOptions.length === 0) {
|
|
@@ -13004,7 +13140,9 @@ async function runAutoUntilCategory(config, featureName, selectionOptions, until
|
|
|
13004
13140
|
|
|
13005
13141
|
// src/commands/flow.ts
|
|
13006
13142
|
function flowCommand(program2) {
|
|
13007
|
-
program2.command("flow [feature-name]").description(
|
|
13143
|
+
program2.command("flow [feature-name]").description(
|
|
13144
|
+
"Run the default workflow auto-loop and pause at selection/approval/manual/resume boundaries"
|
|
13145
|
+
).option("--json", "Output in JSON format for agents").option(
|
|
13008
13146
|
"--json-compact",
|
|
13009
13147
|
"Output compact JSON for agents (implies --json, reduced duplication)"
|
|
13010
13148
|
).option("--component <component>", "Component name for multi projects").option("--all", "Include completed features when auto-detecting").option("--done", "Show completed (workflow-done) features only").option(
|
|
@@ -13184,14 +13322,6 @@ async function runFlow(featureName, options) {
|
|
|
13184
13322
|
}
|
|
13185
13323
|
}
|
|
13186
13324
|
}
|
|
13187
|
-
if (autoMode && !featureName) {
|
|
13188
|
-
if (!resolvedFeatureName) {
|
|
13189
|
-
throw createCliError(
|
|
13190
|
-
"CONTEXT_SELECTION_REQUIRED",
|
|
13191
|
-
"Auto mode requires explicit <feature-name> (e.g. F004)."
|
|
13192
|
-
);
|
|
13193
|
-
}
|
|
13194
|
-
}
|
|
13195
13325
|
if (options.startAuto && !autoMode) {
|
|
13196
13326
|
throw createCliError(
|
|
13197
13327
|
"INVALID_ARGUMENT",
|
|
@@ -13672,13 +13802,13 @@ function parseLabels(raw, lang) {
|
|
|
13672
13802
|
}
|
|
13673
13803
|
return [...new Set(labels)];
|
|
13674
13804
|
}
|
|
13675
|
-
function
|
|
13805
|
+
function escapeRegExp6(value) {
|
|
13676
13806
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13677
13807
|
}
|
|
13678
13808
|
function extractDraftMetadataValue(content, keys) {
|
|
13679
13809
|
for (const key of keys) {
|
|
13680
13810
|
const re = new RegExp(
|
|
13681
|
-
`^\\s*-\\s*\\*\\*${
|
|
13811
|
+
`^\\s*-\\s*\\*\\*${escapeRegExp6(key)}\\*\\*\\s*:\\s*(.*?)\\s*$`,
|
|
13682
13812
|
"mi"
|
|
13683
13813
|
);
|
|
13684
13814
|
const match = content.match(re);
|
|
@@ -13797,7 +13927,7 @@ function ensureSections(body, sections, kind, lang) {
|
|
|
13797
13927
|
};
|
|
13798
13928
|
const hasMetadataField = (field) => {
|
|
13799
13929
|
const re = new RegExp(
|
|
13800
|
-
`^\\s*-\\s*\\*\\*${
|
|
13930
|
+
`^\\s*-\\s*\\*\\*${escapeRegExp6(field)}\\*\\*\\s*:`,
|
|
13801
13931
|
"m"
|
|
13802
13932
|
);
|
|
13803
13933
|
return re.test(body);
|
|
@@ -14685,7 +14815,7 @@ function stripMarkdownCodeContexts(body) {
|
|
|
14685
14815
|
function hasIssueClosingKeyword(body, issueNumber) {
|
|
14686
14816
|
if (!issueNumber) return false;
|
|
14687
14817
|
const cleaned = stripMarkdownCodeContexts(body);
|
|
14688
|
-
const issue =
|
|
14818
|
+
const issue = escapeRegExp6(issueNumber);
|
|
14689
14819
|
const closeKeywordRegex = new RegExp(
|
|
14690
14820
|
`\\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\b\\s*(?:[a-zA-Z0-9_.-]+\\/)?#\\s*${issue}\\b`,
|
|
14691
14821
|
"i"
|
|
@@ -17134,7 +17264,7 @@ var DEFAULT_EVIDENCE_FOR_ANY_MODE = {
|
|
|
17134
17264
|
residualRisks: ["none"],
|
|
17135
17265
|
commandsExecuted: []
|
|
17136
17266
|
};
|
|
17137
|
-
function
|
|
17267
|
+
function escapeRegExp7(value) {
|
|
17138
17268
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
17139
17269
|
}
|
|
17140
17270
|
function normalizeDecision(raw) {
|
|
@@ -17147,15 +17277,15 @@ function normalizeDecision(raw) {
|
|
|
17147
17277
|
if (value === "blocked" || value === "block") return "blocked";
|
|
17148
17278
|
return null;
|
|
17149
17279
|
}
|
|
17150
|
-
function
|
|
17151
|
-
const escaped = keys.map((key) =>
|
|
17280
|
+
function findSpecLineIndex2(lines, keys) {
|
|
17281
|
+
const escaped = keys.map((key) => escapeRegExp7(key));
|
|
17152
17282
|
const re = new RegExp(
|
|
17153
17283
|
`^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`
|
|
17154
17284
|
);
|
|
17155
17285
|
return lines.findIndex((line) => re.test(line));
|
|
17156
17286
|
}
|
|
17157
|
-
function
|
|
17158
|
-
const escaped = keys.map((key) =>
|
|
17287
|
+
function replaceSpecLine2(line, keys, preferredKey, value) {
|
|
17288
|
+
const escaped = keys.map((key) => escapeRegExp7(key));
|
|
17159
17289
|
const re = new RegExp(
|
|
17160
17290
|
`^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`
|
|
17161
17291
|
);
|
|
@@ -17163,7 +17293,7 @@ function replaceSpecLine(line, keys, preferredKey, value) {
|
|
|
17163
17293
|
return line.replace(re, `$1${preferredKey}$2${value}`);
|
|
17164
17294
|
}
|
|
17165
17295
|
function computeInsertIndex(lines, anchorKeys) {
|
|
17166
|
-
const anchorIndex =
|
|
17296
|
+
const anchorIndex = findSpecLineIndex2(lines, anchorKeys);
|
|
17167
17297
|
if (anchorIndex !== -1) {
|
|
17168
17298
|
let cursor = anchorIndex + 1;
|
|
17169
17299
|
while (cursor < lines.length && /^\s{2,}-\s+/.test(lines[cursor])) {
|
|
@@ -17179,9 +17309,9 @@ function computeInsertIndex(lines, anchorKeys) {
|
|
|
17179
17309
|
}
|
|
17180
17310
|
function upsertSpecLine(content, keys, preferredKey, value, anchorKeys) {
|
|
17181
17311
|
const lines = content.split("\n");
|
|
17182
|
-
const index =
|
|
17312
|
+
const index = findSpecLineIndex2(lines, keys);
|
|
17183
17313
|
if (index !== -1) {
|
|
17184
|
-
lines[index] =
|
|
17314
|
+
lines[index] = replaceSpecLine2(lines[index], keys, preferredKey, value);
|
|
17185
17315
|
return lines.join("\n");
|
|
17186
17316
|
}
|
|
17187
17317
|
const insertAt = computeInsertIndex(lines, anchorKeys);
|
|
@@ -17679,18 +17809,18 @@ async function runPrePrReview(featureName, options) {
|
|
|
17679
17809
|
}
|
|
17680
17810
|
console.log();
|
|
17681
17811
|
}
|
|
17682
|
-
function
|
|
17812
|
+
function escapeRegExp8(value) {
|
|
17683
17813
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
17684
17814
|
}
|
|
17685
|
-
function
|
|
17686
|
-
const escaped = keys.map((key) =>
|
|
17815
|
+
function findSpecLineIndex3(lines, keys) {
|
|
17816
|
+
const escaped = keys.map((key) => escapeRegExp8(key));
|
|
17687
17817
|
const re = new RegExp(
|
|
17688
17818
|
`^\\s*-\\s*\\*\\*(?:${escaped.join("|")})\\*\\*\\s*:\\s*`
|
|
17689
17819
|
);
|
|
17690
17820
|
return lines.findIndex((line) => re.test(line));
|
|
17691
17821
|
}
|
|
17692
|
-
function
|
|
17693
|
-
const escaped = keys.map((key) =>
|
|
17822
|
+
function replaceSpecLine3(line, keys, preferredKey, value) {
|
|
17823
|
+
const escaped = keys.map((key) => escapeRegExp8(key));
|
|
17694
17824
|
const re = new RegExp(
|
|
17695
17825
|
`^(\\s*-\\s*\\*\\*)(?:${escaped.join("|")})(\\*\\*\\s*:\\s*)(.*)$`
|
|
17696
17826
|
);
|
|
@@ -17698,7 +17828,7 @@ function replaceSpecLine2(line, keys, preferredKey, value) {
|
|
|
17698
17828
|
return line.replace(re, `$1${preferredKey}$2${value}`);
|
|
17699
17829
|
}
|
|
17700
17830
|
function computeInsertIndex2(lines, anchorKeys) {
|
|
17701
|
-
const anchorIndex =
|
|
17831
|
+
const anchorIndex = findSpecLineIndex3(lines, anchorKeys);
|
|
17702
17832
|
if (anchorIndex !== -1) {
|
|
17703
17833
|
let cursor = anchorIndex + 1;
|
|
17704
17834
|
while (cursor < lines.length && /^\s{2,}-\s+/.test(lines[cursor])) {
|
|
@@ -17714,9 +17844,9 @@ function computeInsertIndex2(lines, anchorKeys) {
|
|
|
17714
17844
|
}
|
|
17715
17845
|
function upsertSpecLine2(content, keys, preferredKey, value, anchorKeys) {
|
|
17716
17846
|
const lines = content.split("\n");
|
|
17717
|
-
const index =
|
|
17847
|
+
const index = findSpecLineIndex3(lines, keys);
|
|
17718
17848
|
if (index !== -1) {
|
|
17719
|
-
lines[index] =
|
|
17849
|
+
lines[index] = replaceSpecLine3(lines[index], keys, preferredKey, value);
|
|
17720
17850
|
return lines.join("\n");
|
|
17721
17851
|
}
|
|
17722
17852
|
const insertAt = computeInsertIndex2(lines, anchorKeys);
|
|
@@ -18495,7 +18625,7 @@ function normalizeTaskDetailItems(values, flagName) {
|
|
|
18495
18625
|
}
|
|
18496
18626
|
return normalized;
|
|
18497
18627
|
}
|
|
18498
|
-
function
|
|
18628
|
+
function escapeRegExp9(value) {
|
|
18499
18629
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18500
18630
|
}
|
|
18501
18631
|
function findTaskListHeadingIndex(lines) {
|
|
@@ -18530,7 +18660,7 @@ function normalizeTaskRef(value) {
|
|
|
18530
18660
|
if (!trimmed) {
|
|
18531
18661
|
throw createCliError(
|
|
18532
18662
|
"INVALID_ARGUMENT",
|
|
18533
|
-
"`--ref` is required. Use `NON-PRD` or an existing `PRD
|
|
18663
|
+
"`--ref` is required. Use `NON-PRD` or an existing `PRD-*` requirement ID."
|
|
18534
18664
|
);
|
|
18535
18665
|
}
|
|
18536
18666
|
if (isNonPrdTag(trimmed)) return "NON-PRD";
|
|
@@ -18538,14 +18668,14 @@ function normalizeTaskRef(value) {
|
|
|
18538
18668
|
if (!isPrdRequirementId(normalized)) {
|
|
18539
18669
|
throw createCliError(
|
|
18540
18670
|
"INVALID_ARGUMENT",
|
|
18541
|
-
"`--ref` must be `NON-PRD` or an existing `PRD-FR-001
|
|
18671
|
+
"`--ref` must be `NON-PRD` or an existing `PRD-*` requirement ID (for example `PRD-FR-001` or `PRD-SCOPE-V1-DESKTOP-EDITOR`)."
|
|
18542
18672
|
);
|
|
18543
18673
|
}
|
|
18544
18674
|
return normalized;
|
|
18545
18675
|
}
|
|
18546
18676
|
function getNextTaskSequence(content, featureFolderName) {
|
|
18547
18677
|
const taskIdPattern = new RegExp(
|
|
18548
|
-
`\\bT-${
|
|
18678
|
+
`\\bT-${escapeRegExp9(featureFolderName)}-(\\d+)\\b`,
|
|
18549
18679
|
"g"
|
|
18550
18680
|
);
|
|
18551
18681
|
let max = 0;
|
|
@@ -18675,7 +18805,7 @@ async function runTaskAdd(featureName, options) {
|
|
|
18675
18805
|
}
|
|
18676
18806
|
function taskCommand(program2) {
|
|
18677
18807
|
const task = program2.command("task").description("Manage tasks");
|
|
18678
|
-
task.command("add [feature-name]").description("Append a new task to the end of Task List").requiredOption("--title <title>", "Task title").requiredOption("--ref <ref>", "Requirement ref: NON-PRD or PRD
|
|
18808
|
+
task.command("add [feature-name]").description("Append a new task to the end of Task List").requiredOption("--title <title>", "Task title").requiredOption("--ref <ref>", "Requirement ref: NON-PRD or existing PRD-* key").option(
|
|
18679
18809
|
"--acceptance <text>",
|
|
18680
18810
|
"Acceptance item. Repeat to add more than one.",
|
|
18681
18811
|
collectRepeatableOption,
|