lee-spec-kit 0.7.5 → 0.7.8
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 +415 -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,69 @@ 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
|
+
"AGENTS.md",
|
|
8742
|
+
"README.md",
|
|
8743
|
+
".lee-spec-kit.json",
|
|
8744
|
+
".gitignore"
|
|
8745
|
+
];
|
|
8746
|
+
var DOC_LIKE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8747
|
+
".md",
|
|
8748
|
+
".mdx",
|
|
8749
|
+
".txt",
|
|
8750
|
+
".rst",
|
|
8751
|
+
".adoc"
|
|
8752
|
+
]);
|
|
8753
|
+
function normalizeEntryName(value) {
|
|
8754
|
+
return value.trim().toLowerCase();
|
|
8755
|
+
}
|
|
8756
|
+
function toAllowedSet(values, extras) {
|
|
8757
|
+
return new Set(
|
|
8758
|
+
[...values, ...extras || []].map((entry) => normalizeEntryName(entry)).filter(Boolean)
|
|
8759
|
+
);
|
|
8760
|
+
}
|
|
8761
|
+
function isDocLikeFile(name) {
|
|
8762
|
+
return DOC_LIKE_FILE_EXTENSIONS.has(path15.extname(name).toLowerCase());
|
|
8763
|
+
}
|
|
8764
|
+
async function collectUnmanagedDocsEntries(docsDir, allowed) {
|
|
8765
|
+
const allowedDirs = toAllowedSet(DEFAULT_MANAGED_DOC_DIRS, allowed?.dirs);
|
|
8766
|
+
const allowedFiles = toAllowedSet(DEFAULT_MANAGED_DOC_FILES, allowed?.files);
|
|
8767
|
+
const entries = await fs.readdir(docsDir, { withFileTypes: true });
|
|
8768
|
+
const unmanaged = [];
|
|
8769
|
+
for (const entry of entries) {
|
|
8770
|
+
const name = entry.name || "";
|
|
8771
|
+
if (!name) continue;
|
|
8772
|
+
if (entry.isDirectory()) {
|
|
8773
|
+
if (name.startsWith(".")) continue;
|
|
8774
|
+
if (allowedDirs.has(normalizeEntryName(name))) continue;
|
|
8775
|
+
unmanaged.push({
|
|
8776
|
+
name,
|
|
8777
|
+
kind: "dir",
|
|
8778
|
+
absPath: path15.join(docsDir, name),
|
|
8779
|
+
relPath: `docs/${name}`
|
|
8780
|
+
});
|
|
8781
|
+
continue;
|
|
8782
|
+
}
|
|
8783
|
+
if (!entry.isFile()) continue;
|
|
8784
|
+
if (allowedFiles.has(normalizeEntryName(name))) continue;
|
|
8785
|
+
if (!isDocLikeFile(name)) continue;
|
|
8786
|
+
unmanaged.push({
|
|
8787
|
+
name,
|
|
8788
|
+
kind: "file",
|
|
8789
|
+
absPath: path15.join(docsDir, name),
|
|
8790
|
+
relPath: `docs/${name}`
|
|
8791
|
+
});
|
|
8792
|
+
}
|
|
8793
|
+
return unmanaged.sort((a, b) => a.relPath.localeCompare(b.relPath));
|
|
8794
|
+
}
|
|
8769
8795
|
|
|
8770
8796
|
// src/utils/context-selection.ts
|
|
8771
8797
|
var REMOTE_ACTION_CATEGORIES = /* @__PURE__ */ new Set([
|
|
@@ -9059,6 +9085,13 @@ function toReasonCode(status) {
|
|
|
9059
9085
|
async function resolveContextSelection(ctx, featureName, options) {
|
|
9060
9086
|
const { config } = ctx;
|
|
9061
9087
|
const { features, branches, warnings } = await scanFeatures(ctx);
|
|
9088
|
+
const unmanagedDocs = await collectUnmanagedDocsEntries(
|
|
9089
|
+
config.docsDir,
|
|
9090
|
+
config.allowedDocsEntries
|
|
9091
|
+
);
|
|
9092
|
+
const unmanagedDocsWarning = unmanagedDocs.length > 0 ? tr(config.lang, "warnings", "unmanagedDocsEntries", {
|
|
9093
|
+
paths: unmanagedDocs.map((entry) => entry.relPath).join(", ")
|
|
9094
|
+
}) : "";
|
|
9062
9095
|
const selectedComponent = resolveComponentOption(options.component);
|
|
9063
9096
|
const scopedFeatures = selectedComponent ? features.filter((f) => f.type === selectedComponent) : features;
|
|
9064
9097
|
const doneFeatures = scopedFeatures.filter((f) => f.completion.workflowDone);
|
|
@@ -9129,14 +9162,36 @@ async function resolveContextSelection(ctx, featureName, options) {
|
|
|
9129
9162
|
openFeatures,
|
|
9130
9163
|
targetFeatures
|
|
9131
9164
|
);
|
|
9132
|
-
const
|
|
9165
|
+
const baseMatchedFeature = targetFeatures.length === 1 ? targetFeatures[0] : null;
|
|
9166
|
+
let matchedFeature = baseMatchedFeature;
|
|
9167
|
+
if (baseMatchedFeature && !baseMatchedFeature.completion.workflowDone && unmanagedDocs.length > 0) {
|
|
9168
|
+
const normalizeMessage = tr(config.lang, "messages", "docsUnmanagedNormalize", {
|
|
9169
|
+
paths: unmanagedDocs.map((entry) => entry.relPath).join(", "),
|
|
9170
|
+
featureRef: baseMatchedFeature.folderName
|
|
9171
|
+
});
|
|
9172
|
+
matchedFeature = {
|
|
9173
|
+
...baseMatchedFeature,
|
|
9174
|
+
currentSubstateId: "docs_unmanaged_normalize",
|
|
9175
|
+
currentSubstateOwner: "main",
|
|
9176
|
+
currentSubstatePhase: "blocked",
|
|
9177
|
+
warnings: unmanagedDocsWarning ? [...baseMatchedFeature.warnings, unmanagedDocsWarning] : [...baseMatchedFeature.warnings],
|
|
9178
|
+
actions: [
|
|
9179
|
+
{
|
|
9180
|
+
type: "instruction",
|
|
9181
|
+
category: "docs_normalize",
|
|
9182
|
+
message: normalizeMessage
|
|
9183
|
+
}
|
|
9184
|
+
],
|
|
9185
|
+
nextAction: normalizeMessage
|
|
9186
|
+
};
|
|
9187
|
+
}
|
|
9133
9188
|
const actions = annotateActions(matchedFeature?.actions ?? []);
|
|
9134
9189
|
const actionOptions = toActionOptions(actions, config.lang);
|
|
9135
9190
|
const contextVersion = getContextVersion(matchedFeature, actionOptions);
|
|
9136
9191
|
return {
|
|
9137
9192
|
features: scopedFeatures,
|
|
9138
9193
|
branches,
|
|
9139
|
-
warnings,
|
|
9194
|
+
warnings: unmanagedDocsWarning ? [...warnings, unmanagedDocsWarning] : warnings,
|
|
9140
9195
|
doneFeatures,
|
|
9141
9196
|
openFeatures,
|
|
9142
9197
|
inProgressFeatures,
|
|
@@ -9186,14 +9241,12 @@ function isTaskExecuteProjectCommitCommand(option) {
|
|
|
9186
9241
|
function shouldDelegateCurrentAction(actionOptions, currentSubstateOwner) {
|
|
9187
9242
|
const primaryOption = actionOptions[0];
|
|
9188
9243
|
const primaryCategory = primaryOption?.action?.category || null;
|
|
9189
|
-
const
|
|
9190
|
-
LEGACY_LONG_RUNNING_DELEGATION_CATEGORIES
|
|
9191
|
-
);
|
|
9244
|
+
const handoffCategories = new Set(SUBAGENT_HANDOFF_CATEGORIES);
|
|
9192
9245
|
const isCommand = primaryOption?.action?.type === "command";
|
|
9193
9246
|
const isRemoteCommand = isCommand && primaryOption?.action?.operationType === "remote";
|
|
9194
9247
|
const ownerDelegates = currentSubstateOwner === "subagent";
|
|
9195
|
-
const
|
|
9196
|
-
const shouldDelegate = (ownerDelegates ||
|
|
9248
|
+
const categoryFallbackDelegates = !currentSubstateOwner && !!primaryCategory && handoffCategories.has(primaryCategory);
|
|
9249
|
+
const shouldDelegate = (ownerDelegates || categoryFallbackDelegates) && isCommand && !isRemoteCommand && !isTaskExecuteProjectCommitCommand(primaryOption);
|
|
9197
9250
|
return {
|
|
9198
9251
|
shouldDelegate,
|
|
9199
9252
|
category: primaryCategory
|
|
@@ -9222,15 +9275,6 @@ function buildAgentOrchestrationPolicy(actionOptions, autoRunAvailable, autoRunC
|
|
|
9222
9275
|
).digest("hex").slice(0, 12) : "";
|
|
9223
9276
|
return {
|
|
9224
9277
|
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
9278
|
mainAgentResponsibilities: [
|
|
9235
9279
|
"Keep user conversation state and approval boundaries",
|
|
9236
9280
|
"Run the same execution loop directly when sub-agent is unavailable",
|
|
@@ -9376,7 +9420,7 @@ function buildDelegatedApprovalGuidance(handoffRequired, handoffMode) {
|
|
|
9376
9420
|
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
9421
|
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
9422
|
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
|
|
9423
|
+
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
9424
|
return `${base}${delegatedCommand}${nonDelegated}${orchestration}`;
|
|
9381
9425
|
}
|
|
9382
9426
|
function buildFinalApprovalPrompt(lang, actionOptions) {
|
|
@@ -9415,7 +9459,7 @@ function resolveAutoRunCategories(approval) {
|
|
|
9415
9459
|
const known = new Set(ACTION_CATEGORIES);
|
|
9416
9460
|
const unique2 = /* @__PURE__ */ new Set();
|
|
9417
9461
|
const unknown = /* @__PURE__ */ new Set();
|
|
9418
|
-
for (const raw of approval?.requireCheckCategories ??
|
|
9462
|
+
for (const raw of approval?.requireCheckCategories ?? []) {
|
|
9419
9463
|
const normalized = normalizeCategoryToken(raw);
|
|
9420
9464
|
if (!normalized) continue;
|
|
9421
9465
|
if (known.has(normalized)) {
|
|
@@ -9450,7 +9494,7 @@ function resolveAutoRunPlan(lang, state, featureName, selectedComponent, approva
|
|
|
9450
9494
|
if (state.status !== "single_matched") return base("NOT_SINGLE_MATCHED");
|
|
9451
9495
|
if (state.actionOptions.length === 0) return base("NO_ACTION_OPTIONS");
|
|
9452
9496
|
if (approvalRequired) return base("APPROVAL_REQUIRED");
|
|
9453
|
-
const mode = approval?.mode ?? "
|
|
9497
|
+
const mode = approval?.mode ?? "category";
|
|
9454
9498
|
if (mode !== "category") return base("APPROVAL_MODE_NOT_CATEGORY");
|
|
9455
9499
|
const defaultPolicy = approval?.default ?? "keep";
|
|
9456
9500
|
if (defaultPolicy !== "skip") return base("DEFAULT_NOT_SKIP");
|
|
@@ -9780,7 +9824,6 @@ function parseApprovalReply(input, validLabels) {
|
|
|
9780
9824
|
}
|
|
9781
9825
|
return null;
|
|
9782
9826
|
}
|
|
9783
|
-
var LEGACY_APPROVAL_TICKET_FILENAME = ".lee-spec-kit.approval-tickets.json";
|
|
9784
9827
|
var APPROVAL_TICKET_TTL_MS = 5 * 60 * 1e3;
|
|
9785
9828
|
function getApprovalSessionId() {
|
|
9786
9829
|
const explicit = (process.env.LEE_SPEC_KIT_SESSION_ID || "").trim();
|
|
@@ -9789,12 +9832,6 @@ function getApprovalSessionId() {
|
|
|
9789
9832
|
if (terminalSession) return terminalSession;
|
|
9790
9833
|
return "";
|
|
9791
9834
|
}
|
|
9792
|
-
function getApprovalTicketPaths(config) {
|
|
9793
|
-
return {
|
|
9794
|
-
runtimePath: getApprovalTicketStorePath(config.docsDir),
|
|
9795
|
-
legacyPath: path15.join(config.docsDir, LEGACY_APPROVAL_TICKET_FILENAME)
|
|
9796
|
-
};
|
|
9797
|
-
}
|
|
9798
9835
|
async function loadApprovalTicketStore(storePath) {
|
|
9799
9836
|
if (!await fs.pathExists(storePath)) return { tickets: [] };
|
|
9800
9837
|
try {
|
|
@@ -9817,32 +9854,11 @@ function pruneApprovalTickets(tickets, nowMs) {
|
|
|
9817
9854
|
return expiresAtMs > nowMs;
|
|
9818
9855
|
});
|
|
9819
9856
|
}
|
|
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
|
-
});
|
|
9857
|
+
async function resolveApprovalTicketStoreAndPath(config, _nowMs) {
|
|
9858
|
+
const runtimePath = getApprovalTicketStorePath(config.docsDir);
|
|
9843
9859
|
return {
|
|
9844
9860
|
storePath: runtimePath,
|
|
9845
|
-
store:
|
|
9861
|
+
store: await loadApprovalTicketStore(runtimePath)
|
|
9846
9862
|
};
|
|
9847
9863
|
}
|
|
9848
9864
|
function toApprovalActionHash(payload) {
|
|
@@ -10653,7 +10669,7 @@ async function runContext(featureName, options) {
|
|
|
10653
10669
|
oneApprovalPerAction: approvalRequired,
|
|
10654
10670
|
requireFreshContext: true,
|
|
10655
10671
|
contextVersion: state.contextVersion,
|
|
10656
|
-
config: config.approval ??
|
|
10672
|
+
config: config.approval ?? createDefaultApprovalConfig()
|
|
10657
10673
|
},
|
|
10658
10674
|
agentOrchestration,
|
|
10659
10675
|
delegatedAction,
|
|
@@ -11378,6 +11394,20 @@ async function checkDocsStructure(config, cwd) {
|
|
|
11378
11394
|
path: formatPath(cwd, configPath)
|
|
11379
11395
|
});
|
|
11380
11396
|
}
|
|
11397
|
+
const unmanagedEntries = await collectUnmanagedDocsEntries(
|
|
11398
|
+
config.docsDir,
|
|
11399
|
+
config.allowedDocsEntries
|
|
11400
|
+
);
|
|
11401
|
+
for (const entry of unmanagedEntries) {
|
|
11402
|
+
issues.push({
|
|
11403
|
+
level: "warn",
|
|
11404
|
+
code: "unmanaged_docs_entry",
|
|
11405
|
+
message: tr(config.lang, "cli", "doctor.issue.unmanagedDocsEntry", {
|
|
11406
|
+
path: entry.relPath
|
|
11407
|
+
}),
|
|
11408
|
+
path: formatPath(cwd, entry.absPath)
|
|
11409
|
+
});
|
|
11410
|
+
}
|
|
11381
11411
|
return issues;
|
|
11382
11412
|
}
|
|
11383
11413
|
async function checkFeatures(config, cwd, features, decisionsPlaceholderMode) {
|
|
@@ -11571,7 +11601,15 @@ function doctorCommand(program2) {
|
|
|
11571
11601
|
let warnings = scan.warnings;
|
|
11572
11602
|
let issues = [];
|
|
11573
11603
|
issues.push(
|
|
11574
|
-
...await checkDocsStructure(
|
|
11604
|
+
...await checkDocsStructure(
|
|
11605
|
+
{
|
|
11606
|
+
docsDir,
|
|
11607
|
+
projectType,
|
|
11608
|
+
lang,
|
|
11609
|
+
allowedDocsEntries: config.allowedDocsEntries
|
|
11610
|
+
},
|
|
11611
|
+
cwd
|
|
11612
|
+
)
|
|
11575
11613
|
);
|
|
11576
11614
|
issues.push(
|
|
11577
11615
|
...await checkFeatures(
|
|
@@ -11605,7 +11643,15 @@ function doctorCommand(program2) {
|
|
|
11605
11643
|
warnings = scan.warnings;
|
|
11606
11644
|
issues = [];
|
|
11607
11645
|
issues.push(
|
|
11608
|
-
...await checkDocsStructure(
|
|
11646
|
+
...await checkDocsStructure(
|
|
11647
|
+
{
|
|
11648
|
+
docsDir,
|
|
11649
|
+
projectType,
|
|
11650
|
+
lang,
|
|
11651
|
+
allowedDocsEntries: config.allowedDocsEntries
|
|
11652
|
+
},
|
|
11653
|
+
cwd
|
|
11654
|
+
)
|
|
11609
11655
|
);
|
|
11610
11656
|
issues.push(
|
|
11611
11657
|
...await checkFeatures(
|
|
@@ -12121,18 +12167,13 @@ function toCompactStatusReport(report) {
|
|
|
12121
12167
|
};
|
|
12122
12168
|
}
|
|
12123
12169
|
function buildAgentOrchestrationPolicy2(autoRun, featureRef) {
|
|
12124
|
-
const
|
|
12125
|
-
const handoffRequired = !!autoRun && !!
|
|
12170
|
+
const resumeCommand = autoRun?.run?.resumeCommand || autoRun?.resume?.flowCommand || null;
|
|
12171
|
+
const handoffRequired = !!autoRun && !!resumeCommand;
|
|
12126
12172
|
const verifyCacheKey = handoffRequired ? `${(featureRef || "unknown").toLowerCase()}|${Buffer$1.from(
|
|
12127
|
-
|
|
12173
|
+
resumeCommand
|
|
12128
12174
|
).toString("base64").slice(0, 12)}` : "";
|
|
12129
12175
|
return {
|
|
12130
12176
|
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
12177
|
mainAgentResponsibilities: [
|
|
12137
12178
|
"Keep user conversation state and approval boundaries",
|
|
12138
12179
|
"Run the same execution loop directly when sub-agent is unavailable",
|
|
@@ -12151,14 +12192,13 @@ function buildAgentOrchestrationPolicy2(autoRun, featureRef) {
|
|
|
12151
12192
|
"AUTO_MANUAL_REQUIRED",
|
|
12152
12193
|
"command execution error"
|
|
12153
12194
|
],
|
|
12154
|
-
preferredResumeCommand,
|
|
12155
12195
|
subAgentHandoff: {
|
|
12156
12196
|
required: handoffRequired,
|
|
12157
12197
|
mode: handoffRequired ? "auto_run" : null,
|
|
12158
12198
|
featureRef,
|
|
12159
12199
|
category: null,
|
|
12160
12200
|
cwd: handoffRequired ? process.cwd() : null,
|
|
12161
|
-
cmd: handoffRequired ?
|
|
12201
|
+
cmd: handoffRequired ? resumeCommand : null,
|
|
12162
12202
|
verify: handoffRequired ? {
|
|
12163
12203
|
runOncePerSession: true,
|
|
12164
12204
|
cacheKey: verifyCacheKey,
|
|
@@ -17818,20 +17858,25 @@ async function runRequirements(options) {
|
|
|
17818
17858
|
}
|
|
17819
17859
|
if (options.strict && issuesFound) process.exitCode = 1;
|
|
17820
17860
|
}
|
|
17821
|
-
|
|
17861
|
+
|
|
17862
|
+
// src/utils/task-lines.ts
|
|
17863
|
+
function parseTaskLine(line, index = -1) {
|
|
17822
17864
|
const match = line.match(
|
|
17823
|
-
/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]
|
|
17865
|
+
/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]((?:\[[^\]]+\])*)\s+(T-[A-Za-z0-9-]+)\s+(.+?)\s*$/
|
|
17824
17866
|
);
|
|
17825
17867
|
if (!match) return null;
|
|
17868
|
+
const tags = [...(match[2] || "").matchAll(/\[([^\]]+)\]/g)].map((entry) => (entry[1] || "").trim()).filter(Boolean);
|
|
17826
17869
|
return {
|
|
17827
|
-
index
|
|
17870
|
+
index,
|
|
17828
17871
|
raw: line,
|
|
17829
17872
|
status: match[1],
|
|
17830
|
-
|
|
17873
|
+
tags,
|
|
17831
17874
|
taskId: match[3],
|
|
17832
17875
|
title: match[4]
|
|
17833
17876
|
};
|
|
17834
17877
|
}
|
|
17878
|
+
|
|
17879
|
+
// src/commands/task-run.ts
|
|
17835
17880
|
function buildTaskRunPrompt(input) {
|
|
17836
17881
|
const shared = [
|
|
17837
17882
|
"Read `spec.md`, `plan.md`, and `tasks.md` before editing code.",
|
|
@@ -18003,19 +18048,6 @@ function taskRunCommand(program2) {
|
|
|
18003
18048
|
}
|
|
18004
18049
|
});
|
|
18005
18050
|
}
|
|
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
18051
|
function setTaskStatus2(line, nextStatus) {
|
|
18020
18052
|
return line.raw.replace(
|
|
18021
18053
|
/^\s*-\s*\[(TODO|DOING|DONE|REVIEW)\]/,
|
|
@@ -18064,7 +18096,7 @@ async function runTaskComplete(featureName, options) {
|
|
|
18064
18096
|
);
|
|
18065
18097
|
}
|
|
18066
18098
|
const resolvedTask = lines.map((line, index) => {
|
|
18067
|
-
const parsed =
|
|
18099
|
+
const parsed = parseTaskLine(line);
|
|
18068
18100
|
return parsed ? { ...parsed, index } : null;
|
|
18069
18101
|
}).find((entry) => entry?.taskId === requestedTaskId);
|
|
18070
18102
|
if (!resolvedTask) {
|
|
@@ -18143,10 +18175,204 @@ function taskCompleteCommand(program2) {
|
|
|
18143
18175
|
}
|
|
18144
18176
|
);
|
|
18145
18177
|
}
|
|
18178
|
+
function escapeRegExp8(value) {
|
|
18179
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18180
|
+
}
|
|
18181
|
+
function findTaskListHeadingIndex(lines) {
|
|
18182
|
+
return lines.findIndex((line) => /^\s*##\s+(Task List|태스크 목록)\s*$/.test(line));
|
|
18183
|
+
}
|
|
18184
|
+
function findNextSectionHeadingIndex(lines, start) {
|
|
18185
|
+
for (let i = start + 1; i < lines.length; i += 1) {
|
|
18186
|
+
if (/^\s*##\s+/.test(lines[i] || "")) return i;
|
|
18187
|
+
}
|
|
18188
|
+
return lines.length;
|
|
18189
|
+
}
|
|
18190
|
+
function findTaskInsertIndex(lines, sectionStart, sectionEnd) {
|
|
18191
|
+
let lastTaskIndex = -1;
|
|
18192
|
+
for (let i = sectionStart; i < sectionEnd; i += 1) {
|
|
18193
|
+
if (parseTaskLine(lines[i] || "", i)) lastTaskIndex = i;
|
|
18194
|
+
}
|
|
18195
|
+
if (lastTaskIndex < 0) return sectionEnd;
|
|
18196
|
+
let insertIndex = lastTaskIndex + 1;
|
|
18197
|
+
while (insertIndex < sectionEnd) {
|
|
18198
|
+
const line = lines[insertIndex] || "";
|
|
18199
|
+
if (parseTaskLine(line, insertIndex)) break;
|
|
18200
|
+
if (/^\s{2,}\S/.test(line) || /^\s*$/.test(line)) {
|
|
18201
|
+
insertIndex += 1;
|
|
18202
|
+
continue;
|
|
18203
|
+
}
|
|
18204
|
+
break;
|
|
18205
|
+
}
|
|
18206
|
+
return insertIndex;
|
|
18207
|
+
}
|
|
18208
|
+
function normalizeTaskRef(value) {
|
|
18209
|
+
const trimmed = value.trim();
|
|
18210
|
+
if (!trimmed) {
|
|
18211
|
+
throw createCliError(
|
|
18212
|
+
"INVALID_ARGUMENT",
|
|
18213
|
+
"`--ref` is required. Use `NON-PRD` or an existing `PRD-...` requirement ID."
|
|
18214
|
+
);
|
|
18215
|
+
}
|
|
18216
|
+
if (isNonPrdTag(trimmed)) return "NON-PRD";
|
|
18217
|
+
const normalized = trimmed.toUpperCase();
|
|
18218
|
+
if (!isPrdRequirementId(normalized)) {
|
|
18219
|
+
throw createCliError(
|
|
18220
|
+
"INVALID_ARGUMENT",
|
|
18221
|
+
"`--ref` must be `NON-PRD` or an existing `PRD-FR-001`-style requirement ID."
|
|
18222
|
+
);
|
|
18223
|
+
}
|
|
18224
|
+
return normalized;
|
|
18225
|
+
}
|
|
18226
|
+
function getNextTaskSequence(content, featureFolderName) {
|
|
18227
|
+
const taskIdPattern = new RegExp(
|
|
18228
|
+
`\\bT-${escapeRegExp8(featureFolderName)}-(\\d+)\\b`,
|
|
18229
|
+
"g"
|
|
18230
|
+
);
|
|
18231
|
+
let max = 0;
|
|
18232
|
+
for (const match of content.matchAll(taskIdPattern)) {
|
|
18233
|
+
const numeric = Number(match[1] || "0");
|
|
18234
|
+
if (Number.isFinite(numeric) && numeric > max) max = numeric;
|
|
18235
|
+
}
|
|
18236
|
+
return max + 1;
|
|
18237
|
+
}
|
|
18238
|
+
function formatTaskBlock(input) {
|
|
18239
|
+
return [
|
|
18240
|
+
`- [TODO][${input.ref}] ${input.taskId} ${input.title}`,
|
|
18241
|
+
` - Date: ${input.recordedAt}`,
|
|
18242
|
+
" - Acceptance:",
|
|
18243
|
+
" - -",
|
|
18244
|
+
" - Checklist:",
|
|
18245
|
+
" - [ ] -"
|
|
18246
|
+
];
|
|
18247
|
+
}
|
|
18248
|
+
async function resolveTaskFeature(featureName, component) {
|
|
18249
|
+
const ctx = await createCliContext({ cwd: process.cwd() });
|
|
18250
|
+
if (!ctx) {
|
|
18251
|
+
throw createCliError(
|
|
18252
|
+
"CONFIG_NOT_FOUND",
|
|
18253
|
+
"No lee-spec-kit config found in this workspace."
|
|
18254
|
+
);
|
|
18255
|
+
}
|
|
18256
|
+
const state = await resolveContextSelection(ctx, featureName, {
|
|
18257
|
+
component: resolveComponentOption(component)
|
|
18258
|
+
});
|
|
18259
|
+
if (state.status !== "single_matched" || !state.matchedFeature) {
|
|
18260
|
+
throw createCliError(
|
|
18261
|
+
"CONTEXT_SELECTION_REQUIRED",
|
|
18262
|
+
"task add requires a single matched feature. Pass <feature-name> explicitly."
|
|
18263
|
+
);
|
|
18264
|
+
}
|
|
18265
|
+
return {
|
|
18266
|
+
ctx,
|
|
18267
|
+
feature: state.matchedFeature
|
|
18268
|
+
};
|
|
18269
|
+
}
|
|
18270
|
+
async function runTaskAdd(featureName, options) {
|
|
18271
|
+
const { ctx, feature } = await resolveTaskFeature(featureName, options.component);
|
|
18272
|
+
const title = options.title.trim();
|
|
18273
|
+
if (!title) {
|
|
18274
|
+
throw createCliError("INVALID_ARGUMENT", "`--title` must not be empty.");
|
|
18275
|
+
}
|
|
18276
|
+
const ref = normalizeTaskRef(options.ref);
|
|
18277
|
+
if (isPrdRequirementId(ref)) {
|
|
18278
|
+
const { definitions } = await scanPrdRequirements(ctx.fs, ctx.config.docsDir);
|
|
18279
|
+
if (!definitions.has(ref)) {
|
|
18280
|
+
throw createCliError(
|
|
18281
|
+
"PRECONDITION_FAILED",
|
|
18282
|
+
`Requirement "${ref}" is not defined in docs/prd or the upstream requirements doc.`
|
|
18283
|
+
);
|
|
18284
|
+
}
|
|
18285
|
+
}
|
|
18286
|
+
const tasksPath = path15.join(feature.path, "tasks.md");
|
|
18287
|
+
if (!await fs.pathExists(tasksPath)) {
|
|
18288
|
+
throw createCliError(
|
|
18289
|
+
"PRECONDITION_FAILED",
|
|
18290
|
+
`tasks.md not found for feature: ${feature.folderName}`
|
|
18291
|
+
);
|
|
18292
|
+
}
|
|
18293
|
+
const content = await fs.readFile(tasksPath, "utf-8");
|
|
18294
|
+
const lines = content.split("\n");
|
|
18295
|
+
const taskListHeadingIndex = findTaskListHeadingIndex(lines);
|
|
18296
|
+
if (taskListHeadingIndex < 0) {
|
|
18297
|
+
throw createCliError(
|
|
18298
|
+
"PRECONDITION_FAILED",
|
|
18299
|
+
"tasks.md is missing a `Task List` section."
|
|
18300
|
+
);
|
|
18301
|
+
}
|
|
18302
|
+
const nextSectionHeadingIndex = findNextSectionHeadingIndex(
|
|
18303
|
+
lines,
|
|
18304
|
+
taskListHeadingIndex
|
|
18305
|
+
);
|
|
18306
|
+
const taskId = `T-${feature.folderName}-${String(
|
|
18307
|
+
getNextTaskSequence(content, feature.folderName)
|
|
18308
|
+
).padStart(2, "0")}`;
|
|
18309
|
+
const insertIndex = findTaskInsertIndex(
|
|
18310
|
+
lines,
|
|
18311
|
+
taskListHeadingIndex + 1,
|
|
18312
|
+
nextSectionHeadingIndex
|
|
18313
|
+
);
|
|
18314
|
+
const recordedAt = getLocalDateString();
|
|
18315
|
+
const blockLines = formatTaskBlock({ ref, taskId, title, recordedAt });
|
|
18316
|
+
const shouldPrefixBlank = insertIndex > taskListHeadingIndex + 1 && (lines[insertIndex - 1] || "").trim() !== "";
|
|
18317
|
+
const shouldSuffixBlank = insertIndex < lines.length && (lines[insertIndex] || "").trim() !== "";
|
|
18318
|
+
const insertLines = [
|
|
18319
|
+
...shouldPrefixBlank ? [""] : [],
|
|
18320
|
+
...blockLines,
|
|
18321
|
+
...shouldSuffixBlank ? [""] : []
|
|
18322
|
+
];
|
|
18323
|
+
lines.splice(insertIndex, 0, ...insertLines);
|
|
18324
|
+
await fs.writeFile(tasksPath, lines.join("\n"), "utf-8");
|
|
18325
|
+
const payload = {
|
|
18326
|
+
status: "ok",
|
|
18327
|
+
reasonCode: "TASK_ADDED",
|
|
18328
|
+
feature: feature.folderName,
|
|
18329
|
+
taskId,
|
|
18330
|
+
title,
|
|
18331
|
+
ref,
|
|
18332
|
+
tasksUpdated: true,
|
|
18333
|
+
tasksPath,
|
|
18334
|
+
recordedAt
|
|
18335
|
+
};
|
|
18336
|
+
if (options.json) {
|
|
18337
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
18338
|
+
return;
|
|
18339
|
+
}
|
|
18340
|
+
console.log(chalk9.green(`Added task ${taskId} to ${feature.folderName}.`));
|
|
18341
|
+
console.log(chalk9.gray(`- ref: ${ref}`));
|
|
18342
|
+
console.log(chalk9.gray(`- tasks.md updated: ${tasksPath}`));
|
|
18343
|
+
}
|
|
18344
|
+
function taskCommand(program2) {
|
|
18345
|
+
const task = program2.command("task").description("Manage tasks");
|
|
18346
|
+
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) => {
|
|
18347
|
+
try {
|
|
18348
|
+
await runTaskAdd(featureName, options);
|
|
18349
|
+
} catch (error) {
|
|
18350
|
+
const ctx = await createCliContext({ cwd: process.cwd() });
|
|
18351
|
+
const lang = ctx?.config?.lang ?? DEFAULT_LANG;
|
|
18352
|
+
const cliError = toCliError(error);
|
|
18353
|
+
const suggestions = getCliErrorSuggestions(cliError.code, lang);
|
|
18354
|
+
if (options.json) {
|
|
18355
|
+
console.log(
|
|
18356
|
+
JSON.stringify({
|
|
18357
|
+
status: "error",
|
|
18358
|
+
reasonCode: cliError.code,
|
|
18359
|
+
error: cliError.message,
|
|
18360
|
+
suggestions
|
|
18361
|
+
})
|
|
18362
|
+
);
|
|
18363
|
+
process.exitCode = 1;
|
|
18364
|
+
return;
|
|
18365
|
+
}
|
|
18366
|
+
console.error(chalk9.red(`[${cliError.code}] ${cliError.message}`));
|
|
18367
|
+
printCliErrorSuggestions(suggestions, lang);
|
|
18368
|
+
process.exitCode = 1;
|
|
18369
|
+
}
|
|
18370
|
+
});
|
|
18371
|
+
}
|
|
18146
18372
|
function setupCommand(program2) {
|
|
18147
18373
|
const setup = program2.command("setup").description("Developer environment setup helpers");
|
|
18148
18374
|
setup.command("codex-bootstrap").description(
|
|
18149
|
-
"Install a small Codex global bootstrap that reads ./docs/AGENTS.md"
|
|
18375
|
+
"Install a small Codex global bootstrap that reads ./AGENTS.md or ./docs/AGENTS.md"
|
|
18150
18376
|
).option(
|
|
18151
18377
|
"--remove",
|
|
18152
18378
|
"Remove the lee-spec-kit managed Codex bootstrap block"
|
|
@@ -18374,6 +18600,7 @@ prePrReviewCommand(program);
|
|
|
18374
18600
|
codeReviewRunCommand(program);
|
|
18375
18601
|
taskRunCommand(program);
|
|
18376
18602
|
taskCompleteCommand(program);
|
|
18603
|
+
taskCommand(program);
|
|
18377
18604
|
requirementsCommand(program);
|
|
18378
18605
|
setupCommand(program);
|
|
18379
18606
|
configureRootCommandSurface();
|