lee-spec-kit 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +414 -188
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/templates/en/common/README.md +15 -4
- package/templates/en/common/agents/agents.md +3 -1
- package/templates/en/common/agents/skills/execute-task.md +3 -2
- package/templates/en/common/features/README.md +9 -4
- package/templates/en/common/features/feature-base/decisions.md +1 -1
- package/templates/en/common/features/feature-base/plan.md +1 -1
- package/templates/en/common/features/feature-base/tasks.md +8 -21
- package/templates/ko/common/README.md +15 -4
- package/templates/ko/common/agents/agents.md +3 -1
- package/templates/ko/common/agents/skills/execute-task.md +3 -2
- package/templates/ko/common/features/README.md +9 -4
- package/templates/ko/common/features/feature-base/decisions.md +1 -1
- package/templates/ko/common/features/feature-base/plan.md +1 -1
- package/templates/ko/common/features/feature-base/tasks.md +8 -21
package/dist/index.js
CHANGED
|
@@ -188,12 +188,7 @@ var koCli = {
|
|
|
188
188
|
"update.langLabel": "\uC5B8\uC5B4",
|
|
189
189
|
"update.typeLabel": "\uD0C0\uC785",
|
|
190
190
|
"update.updatingAgents": "\u{1F4C1} agents/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911...",
|
|
191
|
-
"update.updatingSkills": "\u{1F4C1} agents/skills \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911...",
|
|
192
191
|
"update.agentsUpdated": "agents/ \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC",
|
|
193
|
-
"update.skillsUpdated": "agents/skills \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC",
|
|
194
|
-
"update.updatingFeatureBase": "\u{1F4C1} features/feature-base/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911...",
|
|
195
|
-
"update.engineManagedSkillsBuiltin": "agents/skills\uB294 CLI \uB0B4\uC7A5 \uADDC\uCE59\uC73C\uB85C \uAD00\uB9AC\uB418\uC5B4 docs\uB85C \uB3D9\uAE30\uD654\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
196
|
-
"update.engineManagedFeatureBaseBuiltin": "features/feature-base\uB294 CLI \uB0B4\uC7A5 \uD15C\uD50C\uB9BF\uC73C\uB85C \uAD00\uB9AC\uB418\uC5B4 docs\uB85C \uB3D9\uAE30\uD654\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
197
192
|
"update.engineManagedPruned": "docs\uC5D0\uC11C CLI \uAD00\uB9AC \uBB38\uC11C {count}\uAC1C\uB97C \uC815\uB9AC\uD588\uC2B5\uB2C8\uB2E4.",
|
|
198
193
|
"update.filesUpdated": "{count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC",
|
|
199
194
|
"update.updatedTotal": "\uCD1D {count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!",
|
|
@@ -218,6 +213,7 @@ var koCli = {
|
|
|
218
213
|
"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.)",
|
|
219
214
|
"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.",
|
|
220
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-FR-001 \uAC19\uC740 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
|
+
"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.",
|
|
221
217
|
"doctor.issue.duplicateFeatureId": "\uC911\uBCF5 Feature ID \uAC10\uC9C0: {id} ({count}\uAC1C)",
|
|
222
218
|
"doctor.issue.missingFeatureId": "Feature \uD3F4\uB354\uBA85\uC774 F001-... \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4. (ID\uB97C \uCD94\uCD9C\uD560 \uC218 \uC5C6\uC74C)",
|
|
223
219
|
"init.selectLangPrompt": "\uBB38\uC11C \uC5B8\uC5B4\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
|
|
@@ -684,6 +680,7 @@ var koMessages = {
|
|
|
684
680
|
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.",
|
|
685
681
|
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.",
|
|
686
682
|
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.",
|
|
683
|
+
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.",
|
|
687
684
|
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.",
|
|
688
685
|
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."
|
|
689
686
|
};
|
|
@@ -695,6 +692,7 @@ var koWarnings = {
|
|
|
695
692
|
workflowWorktreeRequired: "`workflow.requireWorktree=true` \uC124\uC815\uC73C\uB85C \uC778\uD574 \uD0DC\uC2A4\uD06C \uC2E4\uD589\uC740 `.worktrees/*` \uACBD\uB85C\uC5D0\uC11C\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4.",
|
|
696
693
|
docsGitUnavailable: "docs \uB808\uD3EC\uC758 git \uC0C1\uD0DC\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (\uB808\uD3EC \uC704\uCE58 / git init \uD655\uC778)",
|
|
697
694
|
docsPathIgnored: "\uD604\uC7AC Feature \uBB38\uC11C \uACBD\uB85C\uAC00 git ignore \uB300\uC0C1\uC785\uB2C8\uB2E4: {path} (docs \uCEE4\uBC0B \uAC10\uC9C0\uAC00 \uC81C\uD55C\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.)",
|
|
695
|
+
unmanagedDocsEntries: "lee-spec-kit \uBB38\uC11C \uD45C\uBA74 \uBC16\uC758 \uBB38\uC11C \uC5D4\uD2B8\uB9AC\uAC00 \uC788\uC2B5\uB2C8\uB2E4: {paths}. reference \uC785\uB825\uC73C\uB85C\uB9CC \uCDE8\uAE09\uD558\uACE0, \uACC4\uC18D \uC9C4\uD589\uD558\uAE30 \uC804\uC5D0 feature-local docs\uB85C \uC815\uADDC\uD654\uD558\uC138\uC694.",
|
|
698
696
|
docsUncommittedChanges: "\uBB38\uC11C \uBCC0\uACBD\uC0AC\uD56D\uC774 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uCD94\uAC00 \uBB38\uC11C \uCEE4\uBC0B \uD544\uC694) \uCEE4\uBC0B \uBA54\uC2DC\uC9C0 \uADDC\uCE59\uC740 git-workflow \uAC00\uC774\uB4DC\uB97C \uAE30\uC900\uC73C\uB85C \uD655\uC778\uD558\uC138\uC694.",
|
|
699
697
|
projectUncommittedChanges: "\uD504\uB85C\uC81D\uD2B8 \uCF54\uB4DC \uBCC0\uACBD\uC0AC\uD56D\uC774 \uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (\uCD94\uAC00 \uCF54\uB4DC \uCEE4\uBC0B \uD544\uC694)",
|
|
700
698
|
featureScopeSplitSuggested: "Feature \uBC94\uC704\uAC00 \uB2E8\uC77C \uC774\uC288\uB85C \uCC98\uB9AC\uD558\uAE30\uC5D0 \uD07D\uB2C8\uB2E4. (tasks: {taskCount}, decisions \uC904\uC218: {decisionsLineCount}; \uBD84\uD560 \uC81C\uC548 \uAE30\uC900: tasks {taskThreshold}\uAC1C \uB610\uB294 decisions {decisionsThreshold}\uC904) \uD604\uC7AC \uAD8C\uC7A5 \uBD84\uD560: {recommendedIssues}\uAC1C \uC774\uC288 (4\uBD84\uD560 \uD558\uB4DC \uAE30\uC900: tasks >= {recommendFourTaskThreshold} \uB610\uB294 decisions \uC904\uC218 >= {recommendFourDecisionsThreshold}).",
|
|
@@ -767,12 +765,7 @@ var enCli = {
|
|
|
767
765
|
"update.langLabel": "Lang",
|
|
768
766
|
"update.typeLabel": "Type",
|
|
769
767
|
"update.updatingAgents": "\u{1F4C1} Updating agents/ folder...",
|
|
770
|
-
"update.updatingSkills": "\u{1F4C1} Updating agents/skills folder...",
|
|
771
768
|
"update.agentsUpdated": "agents/ updated",
|
|
772
|
-
"update.skillsUpdated": "agents/skills updated",
|
|
773
|
-
"update.updatingFeatureBase": "\u{1F4C1} Updating features/feature-base/ folder...",
|
|
774
|
-
"update.engineManagedSkillsBuiltin": "agents/skills is CLI-managed and is not synced into docs.",
|
|
775
|
-
"update.engineManagedFeatureBaseBuiltin": "features/feature-base is CLI-managed and is not synced into docs.",
|
|
776
769
|
"update.engineManagedPruned": "Removed {count} CLI-managed docs entries from this docs tree.",
|
|
777
770
|
"update.filesUpdated": "{count} files updated",
|
|
778
771
|
"update.updatedTotal": "Updated {count} files!",
|
|
@@ -797,6 +790,7 @@ var enCli = {
|
|
|
797
790
|
"doctor.issue.tasksDocStatusUnset": "tasks.md Doc Status is not set. (Set it to Draft, Review, or Approved.)",
|
|
798
791
|
"doctor.issue.tasksDocStatusMissing": "tasks.md is missing the Doc Status field. Add `- **Doc Status**: -` and `Values: Draft | Review | Approved`.",
|
|
799
792
|
"doctor.issue.tasksPrdTagUnknown": "tasks.md uses PRD tags with no matching source definition: {ids}{extra}. Do not invent IDs like PRD-FR-001 in tasks.md. Backfill IDs in docs/prd or the upstream requirements doc first, then align spec.md `PRD Refs` and task tags.",
|
|
793
|
+
"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`.",
|
|
800
794
|
"doctor.issue.duplicateFeatureId": "Duplicate Feature ID detected: {id} ({count})",
|
|
801
795
|
"doctor.issue.missingFeatureId": "Feature folder name is not in F001-... format. (Cannot extract ID)",
|
|
802
796
|
"init.selectLangPrompt": "Select docs language:",
|
|
@@ -1263,6 +1257,7 @@ var enMessages = {
|
|
|
1263
1257
|
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.",
|
|
1264
1258
|
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.",
|
|
1265
1259
|
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.",
|
|
1260
|
+
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}`.",
|
|
1266
1261
|
featureDone: "All workflow requirements and local cleanup are complete. This feature is fully finished.",
|
|
1267
1262
|
fallbackRerunContext: "Cannot determine status. Check the docs and run context again."
|
|
1268
1263
|
};
|
|
@@ -1274,6 +1269,7 @@ var enWarnings = {
|
|
|
1274
1269
|
workflowWorktreeRequired: "With `workflow.requireWorktree=true`, task execution is allowed only from `.worktrees/*` paths.",
|
|
1275
1270
|
docsGitUnavailable: "Cannot read git status for the docs repo. (Check repo location / git init.)",
|
|
1276
1271
|
docsPathIgnored: "Current feature docs path is ignored by git: {path} (docs commit detection may be limited).",
|
|
1272
|
+
unmanagedDocsEntries: "Unmanaged docs entries are present outside the lee-spec-kit docs surface: {paths}. Treat them as reference inputs only and normalize them into feature-local docs before continuing.",
|
|
1277
1273
|
docsUncommittedChanges: "Docs changes are not committed. (Additional docs commit needed.) Check commit message rules against the git-workflow guide.",
|
|
1278
1274
|
projectUncommittedChanges: "Project code changes are not committed. (Additional code commit needed.)",
|
|
1279
1275
|
featureScopeSplitSuggested: "Feature scope may be too large for one issue (tasks: {taskCount}, decisions lines: {decisionsLineCount}; suggest split at {taskThreshold} tasks or {decisionsThreshold} decision lines). Current recommendation: split into {recommendedIssues} issue units (hard 4-way rule: tasks >= {recommendFourTaskThreshold} or decisions lines >= {recommendFourDecisionsThreshold}).",
|
|
@@ -2048,7 +2044,7 @@ In approval-waiting state:
|
|
|
2048
2044
|
2. End with \`approvalRequest.finalPrompt\` exactly as provided.
|
|
2049
2045
|
3. Do not paraphrase or omit these lines.
|
|
2050
2046
|
4. Prefer \`approvalRequest.userFacingLines\` as the source for user-facing approval text.
|
|
2051
|
-
5. Prefer \`matchedFeature.currentSubstateOwner\` plus \`agentOrchestration.subAgentHandoff\` as the delegation SSOT.
|
|
2047
|
+
5. Prefer \`matchedFeature.currentSubstateOwner\` plus \`agentOrchestration.subAgentHandoff\` as the delegation SSOT.
|
|
2052
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.
|
|
2053
2049
|
|
|
2054
2050
|
In non-approval state (progress updates, analysis, tool execution logs, unrelated Q&A):
|
|
@@ -2103,15 +2099,20 @@ async function upsertLeeSpecKitAgentsMd(filePath, options) {
|
|
|
2103
2099
|
}
|
|
2104
2100
|
var LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN = "# lee-spec-kit:codex-bootstrap:begin";
|
|
2105
2101
|
var LEE_SPEC_KIT_CODEX_BOOTSTRAP_END = "# lee-spec-kit:codex-bootstrap:end";
|
|
2106
|
-
var
|
|
2102
|
+
var REQUIRED_FALLBACKS = ["AGENTS.md", "docs/AGENTS.md"];
|
|
2107
2103
|
var REQUIRED_COMPACT_LINES = [
|
|
2104
|
+
"Preserve any instructions loaded from ./AGENTS.md or ./docs/AGENTS.md in the compacted summary.",
|
|
2105
|
+
"After context compression/reset, read ./AGENTS.md or ./docs/AGENTS.md again before resuming project-specific work."
|
|
2106
|
+
];
|
|
2107
|
+
var LEGACY_FALLBACKS = ["docs/AGENTS.md"];
|
|
2108
|
+
var LEGACY_COMPACT_LINES = [
|
|
2108
2109
|
"Preserve any instructions loaded from ./docs/AGENTS.md in the compacted summary.",
|
|
2109
2110
|
"After context compression/reset, read ./docs/AGENTS.md again before resuming project-specific work."
|
|
2110
2111
|
];
|
|
2111
2112
|
function renderManagedSegment2() {
|
|
2112
2113
|
return [
|
|
2113
2114
|
LEE_SPEC_KIT_CODEX_BOOTSTRAP_BEGIN,
|
|
2114
|
-
`project_doc_fallback_filenames = ["${
|
|
2115
|
+
`project_doc_fallback_filenames = ["${REQUIRED_FALLBACKS.join('", "')}"]`,
|
|
2115
2116
|
'compact_prompt = """',
|
|
2116
2117
|
...REQUIRED_COMPACT_LINES,
|
|
2117
2118
|
'"""',
|
|
@@ -2132,7 +2133,9 @@ function getCodexConfigPath() {
|
|
|
2132
2133
|
return path15.join(getCodexHome(), "config.toml");
|
|
2133
2134
|
}
|
|
2134
2135
|
function contentIncludesRequiredBootstrap(content) {
|
|
2135
|
-
|
|
2136
|
+
const matchesCurrent = REQUIRED_FALLBACKS.every((value) => content.includes(value)) && REQUIRED_COMPACT_LINES.every((line) => content.includes(line));
|
|
2137
|
+
const matchesLegacy = LEGACY_FALLBACKS.every((value) => content.includes(value)) && LEGACY_COMPACT_LINES.every((line) => content.includes(line));
|
|
2138
|
+
return matchesCurrent || matchesLegacy;
|
|
2136
2139
|
}
|
|
2137
2140
|
function hasConflictingTopLevelKey(content, key) {
|
|
2138
2141
|
const keyPattern = new RegExp(`^\\s*${key}\\s*=`, "m");
|
|
@@ -2367,6 +2370,7 @@ async function getConfig(cwd) {
|
|
|
2367
2370
|
pushDocs: configFile.pushDocs,
|
|
2368
2371
|
docsRemote: configFile.docsRemote,
|
|
2369
2372
|
projectRoot: configFile.projectRoot,
|
|
2373
|
+
allowedDocsEntries: configFile.allowedDocsEntries,
|
|
2370
2374
|
pr: configFile.pr,
|
|
2371
2375
|
workflow: configFile.workflow,
|
|
2372
2376
|
approval: configFile.approval
|
|
@@ -3863,6 +3867,7 @@ var ACTION_CATEGORIES = [
|
|
|
3863
3867
|
"plan_approve",
|
|
3864
3868
|
"tasks_write",
|
|
3865
3869
|
"tasks_approve",
|
|
3870
|
+
"docs_normalize",
|
|
3866
3871
|
"docs_commit",
|
|
3867
3872
|
"issue_create",
|
|
3868
3873
|
"branch_create",
|
|
@@ -3882,7 +3887,7 @@ var ACTION_CATEGORIES = [
|
|
|
3882
3887
|
"feature_done",
|
|
3883
3888
|
"fallback"
|
|
3884
3889
|
];
|
|
3885
|
-
var
|
|
3890
|
+
var SUBAGENT_HANDOFF_CATEGORIES = [
|
|
3886
3891
|
"task_execute",
|
|
3887
3892
|
"code_review_run",
|
|
3888
3893
|
"code_review",
|
|
@@ -4346,7 +4351,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
|
|
|
4346
4351
|
if (!diff.trim()) return 0;
|
|
4347
4352
|
const removedByTask = /* @__PURE__ */ new Map();
|
|
4348
4353
|
const addedByTask = /* @__PURE__ */ new Map();
|
|
4349
|
-
const
|
|
4354
|
+
const parseTaskLine2 = (line) => {
|
|
4350
4355
|
const match = line.match(
|
|
4351
4356
|
/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\s+(.+?)\s*$/i
|
|
4352
4357
|
);
|
|
@@ -4361,7 +4366,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
|
|
|
4361
4366
|
for (const line of diff.split("\n")) {
|
|
4362
4367
|
if (line.startsWith("---") || line.startsWith("+++")) continue;
|
|
4363
4368
|
if (line.startsWith("-")) {
|
|
4364
|
-
const parsed =
|
|
4369
|
+
const parsed = parseTaskLine2(line.slice(1));
|
|
4365
4370
|
if (!parsed) continue;
|
|
4366
4371
|
const existing = removedByTask.get(parsed.key) || /* @__PURE__ */ new Set();
|
|
4367
4372
|
existing.add(parsed.status);
|
|
@@ -4369,7 +4374,7 @@ function countDoneTransitionsInLatestTasksCommit(ctx, feature) {
|
|
|
4369
4374
|
continue;
|
|
4370
4375
|
}
|
|
4371
4376
|
if (line.startsWith("+")) {
|
|
4372
|
-
const parsed =
|
|
4377
|
+
const parsed = parseTaskLine2(line.slice(1));
|
|
4373
4378
|
if (!parsed) continue;
|
|
4374
4379
|
const existing = addedByTask.get(parsed.key) || /* @__PURE__ */ new Set();
|
|
4375
4380
|
existing.add(parsed.status);
|
|
@@ -5663,35 +5668,12 @@ function normalizeApprovalToken(value) {
|
|
|
5663
5668
|
return (value ?? "").trim().toLowerCase();
|
|
5664
5669
|
}
|
|
5665
5670
|
function applyApprovalPolicy(step, actions, approval, currentSubstatePhase) {
|
|
5666
|
-
const
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
...action,
|
|
5670
|
-
requiresUserCheck: applyTaskExecutePhaseCheck(
|
|
5671
|
-
action,
|
|
5672
|
-
Boolean(action.requiresUserCheck),
|
|
5673
|
-
taskExecuteCheckPolicy,
|
|
5674
|
-
false,
|
|
5675
|
-
currentSubstatePhase
|
|
5676
|
-
)
|
|
5677
|
-
}));
|
|
5678
|
-
}
|
|
5679
|
-
const mode = approval.mode ?? "builtin";
|
|
5680
|
-
if (mode === "builtin") {
|
|
5681
|
-
return actions.map((action) => ({
|
|
5682
|
-
...action,
|
|
5683
|
-
requiresUserCheck: applyTaskExecutePhaseCheck(
|
|
5684
|
-
action,
|
|
5685
|
-
Boolean(action.requiresUserCheck),
|
|
5686
|
-
taskExecuteCheckPolicy,
|
|
5687
|
-
false,
|
|
5688
|
-
currentSubstatePhase
|
|
5689
|
-
)
|
|
5690
|
-
}));
|
|
5691
|
-
}
|
|
5671
|
+
const effectiveApproval = approval ?? createDefaultApprovalConfig();
|
|
5672
|
+
const taskExecuteCheckPolicy = effectiveApproval.taskExecuteCheck === "start_only" ? "start_only" : "both";
|
|
5673
|
+
const mode = effectiveApproval.mode ?? "category";
|
|
5692
5674
|
if (mode === "steps") {
|
|
5693
5675
|
const required = new Set(
|
|
5694
|
-
(
|
|
5676
|
+
(effectiveApproval.requireCheckSteps ?? []).map((n) => typeof n === "number" ? n : Number(n)).filter((n) => Number.isFinite(n))
|
|
5695
5677
|
);
|
|
5696
5678
|
const requiresUserCheck = required.has(step);
|
|
5697
5679
|
return actions.map((action) => ({
|
|
@@ -5706,12 +5688,12 @@ function applyApprovalPolicy(step, actions, approval, currentSubstatePhase) {
|
|
|
5706
5688
|
}));
|
|
5707
5689
|
}
|
|
5708
5690
|
const requiredCategories = new Set(
|
|
5709
|
-
(
|
|
5691
|
+
(effectiveApproval.requireCheckCategories ?? []).map((c) => normalizeApprovalToken(c)).filter(Boolean)
|
|
5710
5692
|
);
|
|
5711
5693
|
const skippedCategories = new Set(
|
|
5712
|
-
(
|
|
5694
|
+
(effectiveApproval.skipCheckCategories ?? []).map((c) => normalizeApprovalToken(c)).filter(Boolean)
|
|
5713
5695
|
);
|
|
5714
|
-
const defaultPolicy =
|
|
5696
|
+
const defaultPolicy = effectiveApproval.default ?? "keep";
|
|
5715
5697
|
return actions.map((a) => {
|
|
5716
5698
|
const builtin = Boolean(a.requiresUserCheck);
|
|
5717
5699
|
const category = normalizeApprovalToken(a.category ?? "uncategorized");
|
|
@@ -7976,21 +7958,18 @@ function hasOwnKey(value, key) {
|
|
|
7976
7958
|
}
|
|
7977
7959
|
function isLegacyGeneratedApprovalConfig(approval) {
|
|
7978
7960
|
const mode = typeof approval.mode === "string" ? approval.mode : "";
|
|
7979
|
-
if (mode && mode !== "
|
|
7961
|
+
if (mode && mode !== "category" && mode !== "steps") return false;
|
|
7980
7962
|
const overrideKeys = [
|
|
7981
7963
|
"default",
|
|
7982
7964
|
"requireCheckSteps",
|
|
7983
|
-
"requireOkSteps",
|
|
7984
7965
|
"requireCheckCategories",
|
|
7985
|
-
"requireOkCategories",
|
|
7986
7966
|
"skipCheckCategories",
|
|
7987
|
-
"skipOkCategories",
|
|
7988
7967
|
"taskExecuteCheck"
|
|
7989
7968
|
];
|
|
7990
7969
|
return !overrideKeys.some((key) => hasOwnKey(approval, key));
|
|
7991
7970
|
}
|
|
7992
7971
|
function updateCommand(program2) {
|
|
7993
|
-
program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--agents-md", "Sync project-scoped AGENTS.md entrypoint").option(
|
|
7972
|
+
program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--agents-md", "Sync project-scoped AGENTS.md entrypoint").option(
|
|
7994
7973
|
"-f, --force",
|
|
7995
7974
|
"Force overwrite even if docs has uncommitted changes"
|
|
7996
7975
|
).action(async (options) => {
|
|
@@ -8033,11 +8012,9 @@ async function runUpdate(options) {
|
|
|
8033
8012
|
const docsLockPath = getDocsLockPath(docsDir);
|
|
8034
8013
|
const forceOverwrite = !!options.force || await isDocsWorktreeCleanOrThrow(docsDir, lang, [docsLockPath]);
|
|
8035
8014
|
const configBackfill = await backfillMissingConfigDefaults(docsDir);
|
|
8036
|
-
const hasExplicitSelection = !!(options.agents || options.agentsMd
|
|
8037
|
-
const updateAgents = options.agents ||
|
|
8015
|
+
const hasExplicitSelection = !!(options.agents || options.agentsMd);
|
|
8016
|
+
const updateAgents = options.agents || !hasExplicitSelection;
|
|
8038
8017
|
const updateAgentsMd = options.agentsMd || !hasExplicitSelection;
|
|
8039
|
-
const updateTemplates = options.templates || !hasExplicitSelection;
|
|
8040
|
-
const agentsMode = options.skills && !options.agents ? "skills" : "all";
|
|
8041
8018
|
console.log(chalk9.blue(tr(lang, "cli", "update.start")));
|
|
8042
8019
|
console.log(chalk9.gray(` - ${tr(lang, "cli", "update.langLabel")}: ${lang}`));
|
|
8043
8020
|
console.log(
|
|
@@ -8046,50 +8023,40 @@ async function runUpdate(options) {
|
|
|
8046
8023
|
console.log();
|
|
8047
8024
|
let updatedCount = 0;
|
|
8048
8025
|
if (updateAgents) {
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
const
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
lang,
|
|
8076
|
-
{
|
|
8077
|
-
protectedFiles: /* @__PURE__ */ new Set([
|
|
8078
|
-
"custom.md",
|
|
8079
|
-
"constitution.md",
|
|
8080
|
-
...ENGINE_MANAGED_AGENT_FILES
|
|
8081
|
-
]),
|
|
8082
|
-
skipDirectories: new Set(ENGINE_MANAGED_AGENT_DIRS)
|
|
8083
|
-
}
|
|
8084
|
-
);
|
|
8085
|
-
updatedCount += count;
|
|
8086
|
-
}
|
|
8087
|
-
console.log(
|
|
8088
|
-
chalk9.green(
|
|
8089
|
-
` \u2705 ${tr(lang, "cli", "update.agentsUpdated")}`
|
|
8090
|
-
)
|
|
8026
|
+
console.log(chalk9.blue(tr(lang, "cli", "update.updatingAgents")));
|
|
8027
|
+
const commonAgentsBase = path15.join(templatesDir, lang, "common", "agents");
|
|
8028
|
+
const targetAgentsBase = path15.join(docsDir, "agents");
|
|
8029
|
+
const commonAgents = commonAgentsBase;
|
|
8030
|
+
const targetAgents = targetAgentsBase;
|
|
8031
|
+
const featurePath = projectType === "multi" ? "docs/features/{component}" : "docs/features";
|
|
8032
|
+
const projectName = config.projectName ?? "{{projectName}}";
|
|
8033
|
+
const commonReplacements = {
|
|
8034
|
+
"{{projectName}}": projectName,
|
|
8035
|
+
"{{featurePath}}": featurePath
|
|
8036
|
+
};
|
|
8037
|
+
if (await fs.pathExists(commonAgents)) {
|
|
8038
|
+
const count = await updateFolder(
|
|
8039
|
+
commonAgents,
|
|
8040
|
+
targetAgents,
|
|
8041
|
+
forceOverwrite,
|
|
8042
|
+
commonReplacements,
|
|
8043
|
+
lang,
|
|
8044
|
+
{
|
|
8045
|
+
protectedFiles: /* @__PURE__ */ new Set([
|
|
8046
|
+
"custom.md",
|
|
8047
|
+
"constitution.md",
|
|
8048
|
+
...ENGINE_MANAGED_AGENT_FILES
|
|
8049
|
+
]),
|
|
8050
|
+
skipDirectories: new Set(ENGINE_MANAGED_AGENT_DIRS)
|
|
8051
|
+
}
|
|
8091
8052
|
);
|
|
8053
|
+
updatedCount += count;
|
|
8092
8054
|
}
|
|
8055
|
+
console.log(
|
|
8056
|
+
chalk9.green(
|
|
8057
|
+
` \u2705 ${tr(lang, "cli", "update.agentsUpdated")}`
|
|
8058
|
+
)
|
|
8059
|
+
);
|
|
8093
8060
|
}
|
|
8094
8061
|
if (updateAgentsMd) {
|
|
8095
8062
|
const agentsMdTargets = await collectAgentsMdTargets(cwd, config);
|
|
@@ -8103,10 +8070,6 @@ async function runUpdate(options) {
|
|
|
8103
8070
|
}
|
|
8104
8071
|
}
|
|
8105
8072
|
}
|
|
8106
|
-
if (updateTemplates) {
|
|
8107
|
-
console.log(chalk9.blue(tr(lang, "cli", "update.updatingFeatureBase")));
|
|
8108
|
-
console.log(chalk9.gray(tr(lang, "cli", "update.engineManagedFeatureBaseBuiltin")));
|
|
8109
|
-
}
|
|
8110
8073
|
const pruned = await pruneEngineManagedDocs(docsDir);
|
|
8111
8074
|
if (pruned.length > 0) {
|
|
8112
8075
|
console.log(
|
|
@@ -8696,15 +8659,15 @@ function getBuiltinDocIds() {
|
|
|
8696
8659
|
function normalizeBuiltinDocId(input) {
|
|
8697
8660
|
const normalized = input.trim().toLowerCase().replace(/_/g, "-");
|
|
8698
8661
|
if (normalized === "git-workflow") return "git-workflow";
|
|
8699
|
-
if (normalized === "issue-doc"
|
|
8700
|
-
if (normalized === "pr-doc"
|
|
8662
|
+
if (normalized === "issue-doc") return "issue-doc";
|
|
8663
|
+
if (normalized === "pr-doc") return "pr-doc";
|
|
8701
8664
|
if (normalized === "issue-template") return "issue-doc";
|
|
8702
8665
|
if (normalized === "pr-template") return "pr-doc";
|
|
8703
8666
|
if (normalized === "create-feature") return "create-feature";
|
|
8704
8667
|
if (normalized === "execute-task") return "execute-task";
|
|
8705
8668
|
if (normalized === "create-issue") return "create-issue";
|
|
8706
8669
|
if (normalized === "create-pr") return "create-pr";
|
|
8707
|
-
if (normalized === "split-feature"
|
|
8670
|
+
if (normalized === "split-feature") {
|
|
8708
8671
|
return "split-feature";
|
|
8709
8672
|
}
|
|
8710
8673
|
if (normalized === "agents") return "agents";
|
|
@@ -8766,6 +8729,68 @@ function resolveComponentOption(value) {
|
|
|
8766
8729
|
const component = (value || "").trim().toLowerCase();
|
|
8767
8730
|
return component || void 0;
|
|
8768
8731
|
}
|
|
8732
|
+
var DEFAULT_MANAGED_DOC_DIRS = [
|
|
8733
|
+
"agents",
|
|
8734
|
+
"designs",
|
|
8735
|
+
"features",
|
|
8736
|
+
"ideas",
|
|
8737
|
+
"prd",
|
|
8738
|
+
"scripts"
|
|
8739
|
+
];
|
|
8740
|
+
var DEFAULT_MANAGED_DOC_FILES = [
|
|
8741
|
+
"README.md",
|
|
8742
|
+
".lee-spec-kit.json",
|
|
8743
|
+
".gitignore"
|
|
8744
|
+
];
|
|
8745
|
+
var DOC_LIKE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8746
|
+
".md",
|
|
8747
|
+
".mdx",
|
|
8748
|
+
".txt",
|
|
8749
|
+
".rst",
|
|
8750
|
+
".adoc"
|
|
8751
|
+
]);
|
|
8752
|
+
function normalizeEntryName(value) {
|
|
8753
|
+
return value.trim().toLowerCase();
|
|
8754
|
+
}
|
|
8755
|
+
function toAllowedSet(values, extras) {
|
|
8756
|
+
return new Set(
|
|
8757
|
+
[...values, ...extras || []].map((entry) => normalizeEntryName(entry)).filter(Boolean)
|
|
8758
|
+
);
|
|
8759
|
+
}
|
|
8760
|
+
function isDocLikeFile(name) {
|
|
8761
|
+
return DOC_LIKE_FILE_EXTENSIONS.has(path15.extname(name).toLowerCase());
|
|
8762
|
+
}
|
|
8763
|
+
async function collectUnmanagedDocsEntries(docsDir, allowed) {
|
|
8764
|
+
const allowedDirs = toAllowedSet(DEFAULT_MANAGED_DOC_DIRS, allowed?.dirs);
|
|
8765
|
+
const allowedFiles = toAllowedSet(DEFAULT_MANAGED_DOC_FILES, allowed?.files);
|
|
8766
|
+
const entries = await fs.readdir(docsDir, { withFileTypes: true });
|
|
8767
|
+
const unmanaged = [];
|
|
8768
|
+
for (const entry of entries) {
|
|
8769
|
+
const name = entry.name || "";
|
|
8770
|
+
if (!name) continue;
|
|
8771
|
+
if (entry.isDirectory()) {
|
|
8772
|
+
if (name.startsWith(".")) continue;
|
|
8773
|
+
if (allowedDirs.has(normalizeEntryName(name))) continue;
|
|
8774
|
+
unmanaged.push({
|
|
8775
|
+
name,
|
|
8776
|
+
kind: "dir",
|
|
8777
|
+
absPath: path15.join(docsDir, name),
|
|
8778
|
+
relPath: `docs/${name}`
|
|
8779
|
+
});
|
|
8780
|
+
continue;
|
|
8781
|
+
}
|
|
8782
|
+
if (!entry.isFile()) continue;
|
|
8783
|
+
if (allowedFiles.has(normalizeEntryName(name))) continue;
|
|
8784
|
+
if (!isDocLikeFile(name)) continue;
|
|
8785
|
+
unmanaged.push({
|
|
8786
|
+
name,
|
|
8787
|
+
kind: "file",
|
|
8788
|
+
absPath: path15.join(docsDir, name),
|
|
8789
|
+
relPath: `docs/${name}`
|
|
8790
|
+
});
|
|
8791
|
+
}
|
|
8792
|
+
return unmanaged.sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
8793
|
+
}
|
|
8769
8794
|
|
|
8770
8795
|
// src/utils/context-selection.ts
|
|
8771
8796
|
var REMOTE_ACTION_CATEGORIES = /* @__PURE__ */ new Set([
|
|
@@ -9059,6 +9084,13 @@ function toReasonCode(status) {
|
|
|
9059
9084
|
async function resolveContextSelection(ctx, featureName, options) {
|
|
9060
9085
|
const { config } = ctx;
|
|
9061
9086
|
const { features, branches, warnings } = await scanFeatures(ctx);
|
|
9087
|
+
const unmanagedDocs = await collectUnmanagedDocsEntries(
|
|
9088
|
+
config.docsDir,
|
|
9089
|
+
config.allowedDocsEntries
|
|
9090
|
+
);
|
|
9091
|
+
const unmanagedDocsWarning = unmanagedDocs.length > 0 ? tr(config.lang, "warnings", "unmanagedDocsEntries", {
|
|
9092
|
+
paths: unmanagedDocs.map((entry) => entry.relPath).join(", ")
|
|
9093
|
+
}) : "";
|
|
9062
9094
|
const selectedComponent = resolveComponentOption(options.component);
|
|
9063
9095
|
const scopedFeatures = selectedComponent ? features.filter((f) => f.type === selectedComponent) : features;
|
|
9064
9096
|
const doneFeatures = scopedFeatures.filter((f) => f.completion.workflowDone);
|
|
@@ -9129,14 +9161,36 @@ async function resolveContextSelection(ctx, featureName, options) {
|
|
|
9129
9161
|
openFeatures,
|
|
9130
9162
|
targetFeatures
|
|
9131
9163
|
);
|
|
9132
|
-
const
|
|
9164
|
+
const baseMatchedFeature = targetFeatures.length === 1 ? targetFeatures[0] : null;
|
|
9165
|
+
let matchedFeature = baseMatchedFeature;
|
|
9166
|
+
if (baseMatchedFeature && !baseMatchedFeature.completion.workflowDone && unmanagedDocs.length > 0) {
|
|
9167
|
+
const normalizeMessage = tr(config.lang, "messages", "docsUnmanagedNormalize", {
|
|
9168
|
+
paths: unmanagedDocs.map((entry) => entry.relPath).join(", "),
|
|
9169
|
+
featureRef: baseMatchedFeature.folderName
|
|
9170
|
+
});
|
|
9171
|
+
matchedFeature = {
|
|
9172
|
+
...baseMatchedFeature,
|
|
9173
|
+
currentSubstateId: "docs_unmanaged_normalize",
|
|
9174
|
+
currentSubstateOwner: "main",
|
|
9175
|
+
currentSubstatePhase: "blocked",
|
|
9176
|
+
warnings: unmanagedDocsWarning ? [...baseMatchedFeature.warnings, unmanagedDocsWarning] : [...baseMatchedFeature.warnings],
|
|
9177
|
+
actions: [
|
|
9178
|
+
{
|
|
9179
|
+
type: "instruction",
|
|
9180
|
+
category: "docs_normalize",
|
|
9181
|
+
message: normalizeMessage
|
|
9182
|
+
}
|
|
9183
|
+
],
|
|
9184
|
+
nextAction: normalizeMessage
|
|
9185
|
+
};
|
|
9186
|
+
}
|
|
9133
9187
|
const actions = annotateActions(matchedFeature?.actions ?? []);
|
|
9134
9188
|
const actionOptions = toActionOptions(actions, config.lang);
|
|
9135
9189
|
const contextVersion = getContextVersion(matchedFeature, actionOptions);
|
|
9136
9190
|
return {
|
|
9137
9191
|
features: scopedFeatures,
|
|
9138
9192
|
branches,
|
|
9139
|
-
warnings,
|
|
9193
|
+
warnings: unmanagedDocsWarning ? [...warnings, unmanagedDocsWarning] : warnings,
|
|
9140
9194
|
doneFeatures,
|
|
9141
9195
|
openFeatures,
|
|
9142
9196
|
inProgressFeatures,
|
|
@@ -9186,14 +9240,12 @@ function isTaskExecuteProjectCommitCommand(option) {
|
|
|
9186
9240
|
function shouldDelegateCurrentAction(actionOptions, currentSubstateOwner) {
|
|
9187
9241
|
const primaryOption = actionOptions[0];
|
|
9188
9242
|
const primaryCategory = primaryOption?.action?.category || null;
|
|
9189
|
-
const
|
|
9190
|
-
LEGACY_LONG_RUNNING_DELEGATION_CATEGORIES
|
|
9191
|
-
);
|
|
9243
|
+
const handoffCategories = new Set(SUBAGENT_HANDOFF_CATEGORIES);
|
|
9192
9244
|
const isCommand = primaryOption?.action?.type === "command";
|
|
9193
9245
|
const isRemoteCommand = isCommand && primaryOption?.action?.operationType === "remote";
|
|
9194
9246
|
const ownerDelegates = currentSubstateOwner === "subagent";
|
|
9195
|
-
const
|
|
9196
|
-
const shouldDelegate = (ownerDelegates ||
|
|
9247
|
+
const categoryFallbackDelegates = !currentSubstateOwner && !!primaryCategory && handoffCategories.has(primaryCategory);
|
|
9248
|
+
const shouldDelegate = (ownerDelegates || categoryFallbackDelegates) && isCommand && !isRemoteCommand && !isTaskExecuteProjectCommitCommand(primaryOption);
|
|
9197
9249
|
return {
|
|
9198
9250
|
shouldDelegate,
|
|
9199
9251
|
category: primaryCategory
|
|
@@ -9222,15 +9274,6 @@ function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable, autoRunC
|
|
|
9222
9274
|
).digest("hex").slice(0, 12) : "";
|
|
9223
9275
|
return {
|
|
9224
9276
|
mode: "main_orchestrates_subagent_execution",
|
|
9225
|
-
delegationPolicy: "prefer_main_delegate_long_running_fallback_main",
|
|
9226
|
-
delegateCommandExecution: "long_running_only",
|
|
9227
|
-
delegateAutoRunExecution: true,
|
|
9228
|
-
fallbackToMainAgentWhenSubAgentUnavailable: true,
|
|
9229
|
-
longRunningCategories: [...LEGACY_LONG_RUNNING_DELEGATION_CATEGORIES],
|
|
9230
|
-
currentActionShouldDelegate: delegation.shouldDelegate,
|
|
9231
|
-
autoRunDelegationAvailable: autoRunAvailable,
|
|
9232
|
-
autoRunShouldDelegate: shouldDelegateAutoRunNow,
|
|
9233
|
-
currentActionCategory: delegation.category,
|
|
9234
9277
|
mainAgentResponsibilities: [
|
|
9235
9278
|
"Keep user conversation state and approval boundaries",
|
|
9236
9279
|
"Run the same execution loop directly when sub-agent is unavailable",
|
|
@@ -9376,7 +9419,7 @@ function buildDelegatedApprovalGuidance(handoffRequired, handoffMode) {
|
|
|
9376
9419
|
const base = "Before asking for approval, show only `actionOptions[].approvalPrompt` lines and `approvalRequest.finalPrompt` to the user. Keep `requiredDocs`, `checkPolicy`, and raw execution commands as internal guidance. For commit actions, include scope (`docs`/`project`) and commit message in the visible prompt. User replies should include the label token (e.g. `A`, `A OK`, `A proceed`, `A \uC9C4\uD589\uD574`).";
|
|
9377
9420
|
const delegatedCommand = handoffRequired && handoffMode === "command" ? ' 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, `--execute` only prepares the handoff; continue the delegated work immediately and do not re-approve the same label.' : "";
|
|
9378
9421
|
const nonDelegated = " For non-delegated command actions, prefer one-shot `npx lee-spec-kit flow <featureRef> --approve <LABEL> --execute` to avoid session mismatch after context compression/reset. Use ticket-based `context --execute --ticket` only when explicitly needed.";
|
|
9379
|
-
const orchestration = ' Use main-agent orchestration: keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT
|
|
9422
|
+
const orchestration = ' Use main-agent orchestration: keep short steps in main agent. Prefer `matchedFeature.currentSubstateOwner` + `agentOrchestration.subAgentHandoff` as the delegation SSOT. Delegate auto-run only when `agentOrchestration.subAgentHandoff.required=true` with `mode="auto_run"`.';
|
|
9380
9423
|
return `${base}${delegatedCommand}${nonDelegated}${orchestration}`;
|
|
9381
9424
|
}
|
|
9382
9425
|
function buildFinalApprovalPrompt(lang, actionOptions) {
|
|
@@ -9415,7 +9458,7 @@ function resolveAutoRunCategories(approval) {
|
|
|
9415
9458
|
const known = new Set(ACTION_CATEGORIES);
|
|
9416
9459
|
const unique2 = /* @__PURE__ */ new Set();
|
|
9417
9460
|
const unknown = /* @__PURE__ */ new Set();
|
|
9418
|
-
for (const raw of approval?.requireCheckCategories ??
|
|
9461
|
+
for (const raw of approval?.requireCheckCategories ?? []) {
|
|
9419
9462
|
const normalized = normalizeCategoryToken(raw);
|
|
9420
9463
|
if (!normalized) continue;
|
|
9421
9464
|
if (known.has(normalized)) {
|
|
@@ -9450,7 +9493,7 @@ function resolveAutoRunPlan(lang, state, featureName, selectedComponent, approva
|
|
|
9450
9493
|
if (state.status !== "single_matched") return base("NOT_SINGLE_MATCHED");
|
|
9451
9494
|
if (state.actionOptions.length === 0) return base("NO_ACTION_OPTIONS");
|
|
9452
9495
|
if (approvalRequired) return base("APPROVAL_REQUIRED");
|
|
9453
|
-
const mode = approval?.mode ?? "
|
|
9496
|
+
const mode = approval?.mode ?? "category";
|
|
9454
9497
|
if (mode !== "category") return base("APPROVAL_MODE_NOT_CATEGORY");
|
|
9455
9498
|
const defaultPolicy = approval?.default ?? "keep";
|
|
9456
9499
|
if (defaultPolicy !== "skip") return base("DEFAULT_NOT_SKIP");
|
|
@@ -9780,7 +9823,6 @@ function parseApprovalReply(input, validLabels) {
|
|
|
9780
9823
|
}
|
|
9781
9824
|
return null;
|
|
9782
9825
|
}
|
|
9783
|
-
var LEGACY_APPROVAL_TICKET_FILENAME = ".lee-spec-kit.approval-tickets.json";
|
|
9784
9826
|
var APPROVAL_TICKET_TTL_MS = 5 * 60 * 1e3;
|
|
9785
9827
|
function getApprovalSessionId() {
|
|
9786
9828
|
const explicit = (process.env.LEE_SPEC_KIT_SESSION_ID || "").trim();
|
|
@@ -9789,12 +9831,6 @@ function getApprovalSessionId() {
|
|
|
9789
9831
|
if (terminalSession) return terminalSession;
|
|
9790
9832
|
return "";
|
|
9791
9833
|
}
|
|
9792
|
-
function getApprovalTicketPaths(config) {
|
|
9793
|
-
return {
|
|
9794
|
-
runtimePath: getApprovalTicketStorePath(config.docsDir),
|
|
9795
|
-
legacyPath: path15.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
|
|
9796
|
-
};
|
|
9797
|
-
}
|
|
9798
9834
|
async function loadApprovalTicketStore(storePath) {
|
|
9799
9835
|
if (!await fs.pathExists(storePath)) return { tickets: [] };
|
|
9800
9836
|
try {
|
|
@@ -9817,32 +9853,11 @@ function pruneApprovalTickets(tickets, nowMs) {
|
|
|
9817
9853
|
return expiresAtMs > nowMs;
|
|
9818
9854
|
});
|
|
9819
9855
|
}
|
|
9820
|
-
async function resolveApprovalTicketStoreAndPath(config,
|
|
9821
|
-
const
|
|
9822
|
-
if (await fs.pathExists(runtimePath)) {
|
|
9823
|
-
return {
|
|
9824
|
-
storePath: runtimePath,
|
|
9825
|
-
store: await loadApprovalTicketStore(runtimePath)
|
|
9826
|
-
};
|
|
9827
|
-
}
|
|
9828
|
-
if (!await fs.pathExists(legacyPath)) {
|
|
9829
|
-
return {
|
|
9830
|
-
storePath: runtimePath,
|
|
9831
|
-
store: { tickets: [] }
|
|
9832
|
-
};
|
|
9833
|
-
}
|
|
9834
|
-
const legacyStore = await loadApprovalTicketStore(legacyPath);
|
|
9835
|
-
const migrated = pruneApprovalTickets(legacyStore.tickets, nowMs);
|
|
9836
|
-
await saveApprovalTicketStore(runtimePath, {
|
|
9837
|
-
tickets: migrated,
|
|
9838
|
-
updatedAt: new Date(nowMs).toISOString(),
|
|
9839
|
-
migratedFrom: legacyPath
|
|
9840
|
-
});
|
|
9841
|
-
await fs.remove(legacyPath).catch(() => {
|
|
9842
|
-
});
|
|
9856
|
+
async function resolveApprovalTicketStoreAndPath(config, _nowMs) {
|
|
9857
|
+
const runtimePath = getApprovalTicketStorePath(config.docsDir);
|
|
9843
9858
|
return {
|
|
9844
9859
|
storePath: runtimePath,
|
|
9845
|
-
store:
|
|
9860
|
+
store: await loadApprovalTicketStore(runtimePath)
|
|
9846
9861
|
};
|
|
9847
9862
|
}
|
|
9848
9863
|
function toApprovalActionHash(payload) {
|
|
@@ -10653,7 +10668,7 @@ async function runContext(featureName, options) {
|
|
|
10653
10668
|
oneApprovalPerAction: approvalRequired,
|
|
10654
10669
|
requireFreshContext: true,
|
|
10655
10670
|
contextVersion: state.contextVersion,
|
|
10656
|
-
config: config.approval ??
|
|
10671
|
+
config: config.approval ?? createDefaultApprovalConfig()
|
|
10657
10672
|
},
|
|
10658
10673
|
agentOrchestration,
|
|
10659
10674
|
delegatedAction,
|
|
@@ -11378,6 +11393,20 @@ async function checkDocsStructure(config, cwd) {
|
|
|
11378
11393
|
path: formatPath(cwd, configPath)
|
|
11379
11394
|
});
|
|
11380
11395
|
}
|
|
11396
|
+
const unmanagedEntries = await collectUnmanagedDocsEntries(
|
|
11397
|
+
config.docsDir,
|
|
11398
|
+
config.allowedDocsEntries
|
|
11399
|
+
);
|
|
11400
|
+
for (const entry of unmanagedEntries) {
|
|
11401
|
+
issues.push({
|
|
11402
|
+
level: "warn",
|
|
11403
|
+
code: "unmanaged_docs_entry",
|
|
11404
|
+
message: tr(config.lang, "cli", "doctor.issue.unmanagedDocsEntry", {
|
|
11405
|
+
path: entry.relPath
|
|
11406
|
+
}),
|
|
11407
|
+
path: formatPath(cwd, entry.absPath)
|
|
11408
|
+
});
|
|
11409
|
+
}
|
|
11381
11410
|
return issues;
|
|
11382
11411
|
}
|
|
11383
11412
|
async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
@@ -11571,7 +11600,15 @@ function doctorCommand(program2) {
|
|
|
11571
11600
|
let warnings = scan.warnings;
|
|
11572
11601
|
let issues = [];
|
|
11573
11602
|
issues.push(
|
|
11574
|
-
...await checkDocsStructure(
|
|
11603
|
+
...await checkDocsStructure(
|
|
11604
|
+
{
|
|
11605
|
+
docsDir,
|
|
11606
|
+
projectType,
|
|
11607
|
+
lang,
|
|
11608
|
+
allowedDocsEntries: config.allowedDocsEntries
|
|
11609
|
+
},
|
|
11610
|
+
cwd
|
|
11611
|
+
)
|
|
11575
11612
|
);
|
|
11576
11613
|
issues.push(
|
|
11577
11614
|
...await checkFeatures(
|
|
@@ -11605,7 +11642,15 @@ function doctorCommand(program2) {
|
|
|
11605
11642
|
warnings = scan.warnings;
|
|
11606
11643
|
issues = [];
|
|
11607
11644
|
issues.push(
|
|
11608
|
-
...await checkDocsStructure(
|
|
11645
|
+
...await checkDocsStructure(
|
|
11646
|
+
{
|
|
11647
|
+
docsDir,
|
|
11648
|
+
projectType,
|
|
11649
|
+
lang,
|
|
11650
|
+
allowedDocsEntries: config.allowedDocsEntries
|
|
11651
|
+
},
|
|
11652
|
+
cwd
|
|
11653
|
+
)
|
|
11609
11654
|
);
|
|
11610
11655
|
issues.push(
|
|
11611
11656
|
...await checkFeatures(
|
|
@@ -12121,18 +12166,13 @@ function toCompactStatusReport(report) {
|
|
|
12121
12166
|
};
|
|
12122
12167
|
}
|
|
12123
12168
|
function buildAgentOrchestrationPolicy2(autoRun, featureRef) {
|
|
12124
|
-
const
|
|
12125
|
-
const handoffRequired = !!autoRun && !!
|
|
12169
|
+
const resumeCommand = autoRun?.run?.resumeCommand || autoRun?.resume?.flowCommand || null;
|
|
12170
|
+
const handoffRequired = !!autoRun && !!resumeCommand;
|
|
12126
12171
|
const verifyCacheKey = handoffRequired ? `${(featureRef || "unknown").toLowerCase()}|${Buffer$1.from(
|
|
12127
|
-
|
|
12172
|
+
resumeCommand
|
|
12128
12173
|
).toString("base64").slice(0, 12)}` : "";
|
|
12129
12174
|
return {
|
|
12130
12175
|
mode: "main_orchestrates_subagent_execution",
|
|
12131
|
-
delegationPolicy: "prefer_main_delegate_long_running_fallback_main",
|
|
12132
|
-
delegateCommandExecution: "long_running_only",
|
|
12133
|
-
delegateAutoRunExecution: true,
|
|
12134
|
-
fallbackToMainAgentWhenSubAgentUnavailable: true,
|
|
12135
|
-
longRunningCategories: [...LEGACY_LONG_RUNNING_DELEGATION_CATEGORIES],
|
|
12136
12176
|
mainAgentResponsibilities: [
|
|
12137
12177
|
"Keep user conversation state and approval boundaries",
|
|
12138
12178
|
"Run the same execution loop directly when sub-agent is unavailable",
|
|
@@ -12151,14 +12191,13 @@ function buildAgentOrchestrationPolicy2(autoRun, featureRef) {
|
|
|
12151
12191
|
"AUTO_MANUAL_REQUIRED",
|
|
12152
12192
|
"command execution error"
|
|
12153
12193
|
],
|
|
12154
|
-
preferredResumeCommand,
|
|
12155
12194
|
subAgentHandoff: {
|
|
12156
12195
|
required: handoffRequired,
|
|
12157
12196
|
mode: handoffRequired ? "auto_run" : null,
|
|
12158
12197
|
featureRef,
|
|
12159
12198
|
category: null,
|
|
12160
12199
|
cwd: handoffRequired ? process.cwd() : null,
|
|
12161
|
-
cmd: handoffRequired ?
|
|
12200
|
+
cmd: handoffRequired ? resumeCommand : null,
|
|
12162
12201
|
verify: handoffRequired ? {
|
|
12163
12202
|
runOncePerSession: true,
|
|
12164
12203
|
cacheKey: verifyCacheKey,
|
|
@@ -17818,20 +17857,25 @@ async function runRequirements(options) {
|
|
|
17818
17857
|
}
|
|
17819
17858
|
if (options.strict && issuesFound) process.exitCode = 1;
|
|
17820
17859
|
}
|
|
17821
|
-
|
|
17860
|
+
|
|
17861
|
+
// src/utils/task-lines.ts
|
|
17862
|
+
function parseTaskLine(line, index = -1) {
|
|
17822
17863
|
const match = line.match(
|
|
17823
|
-
/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]
|
|
17864
|
+
/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]((?:\[[^\]]+\])*)\s+(T-[A-Za-z0-9-]+)\s+(.+?)\s*$/
|
|
17824
17865
|
);
|
|
17825
17866
|
if (!match) return null;
|
|
17867
|
+
const tags = [...(match[2] || "").matchAll(/\[([^\]]+)\]/g)].map((entry) => (entry[1] || "").trim()).filter(Boolean);
|
|
17826
17868
|
return {
|
|
17827
|
-
index
|
|
17869
|
+
index,
|
|
17828
17870
|
raw: line,
|
|
17829
17871
|
status: match[1],
|
|
17830
|
-
|
|
17872
|
+
tags,
|
|
17831
17873
|
taskId: match[3],
|
|
17832
17874
|
title: match[4]
|
|
17833
17875
|
};
|
|
17834
17876
|
}
|
|
17877
|
+
|
|
17878
|
+
// src/commands/task-run.ts
|
|
17835
17879
|
function buildTaskRunPrompt(input) {
|
|
17836
17880
|
const shared = [
|
|
17837
17881
|
"Read `spec.md`, `plan.md`, and `tasks.md` before editing code.",
|
|
@@ -18003,19 +18047,6 @@ function taskRunCommand(program2) {
|
|
|
18003
18047
|
}
|
|
18004
18048
|
});
|
|
18005
18049
|
}
|
|
18006
|
-
function parseTaskLine2(line) {
|
|
18007
|
-
const match = line.match(
|
|
18008
|
-
/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]\[[^\]]+\](?:\[[^\]]+\])*\s+(T-[A-Za-z0-9-]+)\s+(.+?)\s*$/
|
|
18009
|
-
);
|
|
18010
|
-
if (!match) return null;
|
|
18011
|
-
return {
|
|
18012
|
-
index: -1,
|
|
18013
|
-
raw: line,
|
|
18014
|
-
status: match[1],
|
|
18015
|
-
taskId: match[2],
|
|
18016
|
-
title: match[3]
|
|
18017
|
-
};
|
|
18018
|
-
}
|
|
18019
18050
|
function setTaskStatus2(line, nextStatus) {
|
|
18020
18051
|
return line.raw.replace(
|
|
18021
18052
|
/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]/,
|
|
@@ -18064,7 +18095,7 @@ async function runTaskComplete(featureName, options) {
|
|
|
18064
18095
|
);
|
|
18065
18096
|
}
|
|
18066
18097
|
const resolvedTask = lines.map((line, index) => {
|
|
18067
|
-
const parsed =
|
|
18098
|
+
const parsed = parseTaskLine(line);
|
|
18068
18099
|
return parsed ? { ...parsed, index } : null;
|
|
18069
18100
|
}).find((entry) => entry?.taskId === requestedTaskId);
|
|
18070
18101
|
if (!resolvedTask) {
|
|
@@ -18143,10 +18174,204 @@ function taskCompleteCommand(program2) {
|
|
|
18143
18174
|
}
|
|
18144
18175
|
);
|
|
18145
18176
|
}
|
|
18177
|
+
function escapeRegExp8(value) {
|
|
18178
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18179
|
+
}
|
|
18180
|
+
function findTaskListHeadingIndex(lines) {
|
|
18181
|
+
return lines.findIndex((line) => /^\s*##\s+(Task List|태스크 목록)\s*$/.test(line));
|
|
18182
|
+
}
|
|
18183
|
+
function findNextSectionHeadingIndex(lines, start) {
|
|
18184
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
18185
|
+
if (/^\s*##\s+/.test(lines[i] || "")) return i;
|
|
18186
|
+
}
|
|
18187
|
+
return lines.length;
|
|
18188
|
+
}
|
|
18189
|
+
function findTaskInsertIndex(lines, sectionStart, sectionEnd) {
|
|
18190
|
+
let lastTaskIndex = -1;
|
|
18191
|
+
for (let i = sectionStart; i < sectionEnd; i += 1) {
|
|
18192
|
+
if (parseTaskLine(lines[i] || "", i)) lastTaskIndex = i;
|
|
18193
|
+
}
|
|
18194
|
+
if (lastTaskIndex < 0) return sectionEnd;
|
|
18195
|
+
let insertIndex = lastTaskIndex + 1;
|
|
18196
|
+
while (insertIndex < sectionEnd) {
|
|
18197
|
+
const line = lines[insertIndex] || "";
|
|
18198
|
+
if (parseTaskLine(line, insertIndex)) break;
|
|
18199
|
+
if (/^\s{2,}\S/.test(line) || /^\s*$/.test(line)) {
|
|
18200
|
+
insertIndex += 1;
|
|
18201
|
+
continue;
|
|
18202
|
+
}
|
|
18203
|
+
break;
|
|
18204
|
+
}
|
|
18205
|
+
return insertIndex;
|
|
18206
|
+
}
|
|
18207
|
+
function normalizeTaskRef(value) {
|
|
18208
|
+
const trimmed = value.trim();
|
|
18209
|
+
if (!trimmed) {
|
|
18210
|
+
throw createCliError(
|
|
18211
|
+
"INVALID_ARGUMENT",
|
|
18212
|
+
"`--ref` is required. Use `NON-PRD` or an existing `PRD-...` requirement ID."
|
|
18213
|
+
);
|
|
18214
|
+
}
|
|
18215
|
+
if (isNonPrdTag(trimmed)) return "NON-PRD";
|
|
18216
|
+
const normalized = trimmed.toUpperCase();
|
|
18217
|
+
if (!isPrdRequirementId(normalized)) {
|
|
18218
|
+
throw createCliError(
|
|
18219
|
+
"INVALID_ARGUMENT",
|
|
18220
|
+
"`--ref` must be `NON-PRD` or an existing `PRD-FR-001`-style requirement ID."
|
|
18221
|
+
);
|
|
18222
|
+
}
|
|
18223
|
+
return normalized;
|
|
18224
|
+
}
|
|
18225
|
+
function getNextTaskSequence(content, featureFolderName) {
|
|
18226
|
+
const taskIdPattern = new RegExp(
|
|
18227
|
+
`\\bT-${escapeRegExp8(featureFolderName)}-(\\d+)\\b`,
|
|
18228
|
+
"g"
|
|
18229
|
+
);
|
|
18230
|
+
let max = 0;
|
|
18231
|
+
for (const match of content.matchAll(taskIdPattern)) {
|
|
18232
|
+
const numeric = Number(match[1] || "0");
|
|
18233
|
+
if (Number.isFinite(numeric) && numeric > max) max = numeric;
|
|
18234
|
+
}
|
|
18235
|
+
return max + 1;
|
|
18236
|
+
}
|
|
18237
|
+
function formatTaskBlock(input) {
|
|
18238
|
+
return [
|
|
18239
|
+
`- [TODO][${input.ref}] ${input.taskId} ${input.title}`,
|
|
18240
|
+
` - Date: ${input.recordedAt}`,
|
|
18241
|
+
" - Acceptance:",
|
|
18242
|
+
" - -",
|
|
18243
|
+
" - Checklist:",
|
|
18244
|
+
" - [ ] -"
|
|
18245
|
+
];
|
|
18246
|
+
}
|
|
18247
|
+
async function resolveTaskFeature(featureName, component) {
|
|
18248
|
+
const ctx = await createCliContext({ cwd: process.cwd() });
|
|
18249
|
+
if (!ctx) {
|
|
18250
|
+
throw createCliError(
|
|
18251
|
+
"CONFIG_NOT_FOUND",
|
|
18252
|
+
"No lee-spec-kit config found in this workspace."
|
|
18253
|
+
);
|
|
18254
|
+
}
|
|
18255
|
+
const state = await resolveContextSelection(ctx, featureName, {
|
|
18256
|
+
component: resolveComponentOption(component)
|
|
18257
|
+
});
|
|
18258
|
+
if (state.status !== "single_matched" || !state.matchedFeature) {
|
|
18259
|
+
throw createCliError(
|
|
18260
|
+
"CONTEXT_SELECTION_REQUIRED",
|
|
18261
|
+
"task add requires a single matched feature. Pass <feature-name> explicitly."
|
|
18262
|
+
);
|
|
18263
|
+
}
|
|
18264
|
+
return {
|
|
18265
|
+
ctx,
|
|
18266
|
+
feature: state.matchedFeature
|
|
18267
|
+
};
|
|
18268
|
+
}
|
|
18269
|
+
async function runTaskAdd(featureName, options) {
|
|
18270
|
+
const { ctx, feature } = await resolveTaskFeature(featureName, options.component);
|
|
18271
|
+
const title = options.title.trim();
|
|
18272
|
+
if (!title) {
|
|
18273
|
+
throw createCliError("INVALID_ARGUMENT", "`--title` must not be empty.");
|
|
18274
|
+
}
|
|
18275
|
+
const ref = normalizeTaskRef(options.ref);
|
|
18276
|
+
if (isPrdRequirementId(ref)) {
|
|
18277
|
+
const { definitions } = await scanPrdRequirements(ctx.fs, ctx.config.docsDir);
|
|
18278
|
+
if (!definitions.has(ref)) {
|
|
18279
|
+
throw createCliError(
|
|
18280
|
+
"PRECONDITION_FAILED",
|
|
18281
|
+
`Requirement "${ref}" is not defined in docs/prd or the upstream requirements doc.`
|
|
18282
|
+
);
|
|
18283
|
+
}
|
|
18284
|
+
}
|
|
18285
|
+
const tasksPath = path15.join(feature.path, "tasks.md");
|
|
18286
|
+
if (!await fs.pathExists(tasksPath)) {
|
|
18287
|
+
throw createCliError(
|
|
18288
|
+
"PRECONDITION_FAILED",
|
|
18289
|
+
`tasks.md not found for feature: ${feature.folderName}`
|
|
18290
|
+
);
|
|
18291
|
+
}
|
|
18292
|
+
const content = await fs.readFile(tasksPath, "utf-8");
|
|
18293
|
+
const lines = content.split("\n");
|
|
18294
|
+
const taskListHeadingIndex = findTaskListHeadingIndex(lines);
|
|
18295
|
+
if (taskListHeadingIndex < 0) {
|
|
18296
|
+
throw createCliError(
|
|
18297
|
+
"PRECONDITION_FAILED",
|
|
18298
|
+
"tasks.md is missing a `Task List` section."
|
|
18299
|
+
);
|
|
18300
|
+
}
|
|
18301
|
+
const nextSectionHeadingIndex = findNextSectionHeadingIndex(
|
|
18302
|
+
lines,
|
|
18303
|
+
taskListHeadingIndex
|
|
18304
|
+
);
|
|
18305
|
+
const taskId = `T-${feature.folderName}-${String(
|
|
18306
|
+
getNextTaskSequence(content, feature.folderName)
|
|
18307
|
+
).padStart(2, "0")}`;
|
|
18308
|
+
const insertIndex = findTaskInsertIndex(
|
|
18309
|
+
lines,
|
|
18310
|
+
taskListHeadingIndex + 1,
|
|
18311
|
+
nextSectionHeadingIndex
|
|
18312
|
+
);
|
|
18313
|
+
const recordedAt = getLocalDateString();
|
|
18314
|
+
const blockLines = formatTaskBlock({ ref, taskId, title, recordedAt });
|
|
18315
|
+
const shouldPrefixBlank = insertIndex > taskListHeadingIndex + 1 && (lines[insertIndex - 1] || "").trim() !== "";
|
|
18316
|
+
const shouldSuffixBlank = insertIndex < lines.length && (lines[insertIndex] || "").trim() !== "";
|
|
18317
|
+
const insertLines = [
|
|
18318
|
+
...shouldPrefixBlank ? [""] : [],
|
|
18319
|
+
...blockLines,
|
|
18320
|
+
...shouldSuffixBlank ? [""] : []
|
|
18321
|
+
];
|
|
18322
|
+
lines.splice(insertIndex, 0, ...insertLines);
|
|
18323
|
+
await fs.writeFile(tasksPath, lines.join("\n"), "utf-8");
|
|
18324
|
+
const payload = {
|
|
18325
|
+
status: "ok",
|
|
18326
|
+
reasonCode: "TASK_ADDED",
|
|
18327
|
+
feature: feature.folderName,
|
|
18328
|
+
taskId,
|
|
18329
|
+
title,
|
|
18330
|
+
ref,
|
|
18331
|
+
tasksUpdated: true,
|
|
18332
|
+
tasksPath,
|
|
18333
|
+
recordedAt
|
|
18334
|
+
};
|
|
18335
|
+
if (options.json) {
|
|
18336
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
18337
|
+
return;
|
|
18338
|
+
}
|
|
18339
|
+
console.log(chalk9.green(`Added task ${taskId} to ${feature.folderName}.`));
|
|
18340
|
+
console.log(chalk9.gray(`- ref: ${ref}`));
|
|
18341
|
+
console.log(chalk9.gray(`- tasks.md updated: ${tasksPath}`));
|
|
18342
|
+
}
|
|
18343
|
+
function taskCommand(program2) {
|
|
18344
|
+
const task = program2.command("task").description("Manage tasks");
|
|
18345
|
+
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-FR-001").option("--component <component>", "Component name for multi projects").option("--json", "Output JSON").action(async (featureName, options) => {
|
|
18346
|
+
try {
|
|
18347
|
+
await runTaskAdd(featureName, options);
|
|
18348
|
+
} catch (error) {
|
|
18349
|
+
const ctx = await createCliContext({ cwd: process.cwd() });
|
|
18350
|
+
const lang = ctx?.config?.lang ?? DEFAULT_LANG;
|
|
18351
|
+
const cliError = toCliError(error);
|
|
18352
|
+
const suggestions = getCliErrorSuggestions(cliError.code, lang);
|
|
18353
|
+
if (options.json) {
|
|
18354
|
+
console.log(
|
|
18355
|
+
JSON.stringify({
|
|
18356
|
+
status: "error",
|
|
18357
|
+
reasonCode: cliError.code,
|
|
18358
|
+
error: cliError.message,
|
|
18359
|
+
suggestions
|
|
18360
|
+
})
|
|
18361
|
+
);
|
|
18362
|
+
process.exitCode = 1;
|
|
18363
|
+
return;
|
|
18364
|
+
}
|
|
18365
|
+
console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
|
|
18366
|
+
printCliErrorSuggestions(suggestions, lang);
|
|
18367
|
+
process.exitCode = 1;
|
|
18368
|
+
}
|
|
18369
|
+
});
|
|
18370
|
+
}
|
|
18146
18371
|
function setupCommand(program2) {
|
|
18147
18372
|
const setup = program2.command("setup").description("Developer environment setup helpers");
|
|
18148
18373
|
setup.command("codex-bootstrap").description(
|
|
18149
|
-
"Install a small Codex global bootstrap that reads ./docs/AGENTS.md"
|
|
18374
|
+
"Install a small Codex global bootstrap that reads ./AGENTS.md or ./docs/AGENTS.md"
|
|
18150
18375
|
).option(
|
|
18151
18376
|
"--remove",
|
|
18152
18377
|
"Remove the lee-spec-kit managed Codex bootstrap block"
|
|
@@ -18374,6 +18599,7 @@ prePrReviewCommand(program);
|
|
|
18374
18599
|
codeReviewRunCommand(program);
|
|
18375
18600
|
taskRunCommand(program);
|
|
18376
18601
|
taskCompleteCommand(program);
|
|
18602
|
+
taskCommand(program);
|
|
18377
18603
|
requirementsCommand(program);
|
|
18378
18604
|
setupCommand(program);
|
|
18379
18605
|
configureRootCommandSurface();
|