mancode 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -43
- package/README.zh-CN.md +79 -39
- package/dist/cli.js +502 -113
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -295,14 +295,15 @@ var HEAD_COACH_AGENT = {
|
|
|
295
295
|
- \u5E94\u7528 YAGNI \u539F\u5219\uFF1A\u5DF2\u5B58\u5728 \u2192 \u590D\u7528 \u2192 \u6807\u51C6\u5E93 \u2192 \u5DF2\u88C5\u4F9D\u8D56 \u2192 \u4E00\u884C \u2192 \u6700\u5C0F\u5B9E\u73B0
|
|
296
296
|
|
|
297
297
|
**\u4FEE\u590D\u9636\u6BB5**\uFF08Film Analyst \u53CD\u9988\u540E\uFF09\uFF1A
|
|
298
|
-
- \u53EA\u4FEE\u590D\
|
|
298
|
+
- \u6C47\u603B\u6240\u9700\u5BA1\u67E5\u9886\u57DF\u7684 blocker\uFF0C\u53EA\u505A\u4E00\u8F6E\u4FEE\u590D\u5E76\u901A\u8FC7 review ledger \u8BB0\u5F55
|
|
299
|
+
- \u53EA\u4FEE\u590D\u53CD\u9988\u6307\u51FA\u7684 blocker\uFF1B\u{1F7E1}/\u{1F7E2} \u53EA\u8BB0\u5F55\u6743\u8861\uFF0C\u4E0D\u81EA\u52A8\u6269\u5927 diff
|
|
299
300
|
- **\u4E0D\u5728\u539F\u4EE3\u7801\u4E0A\u8FC7\u5EA6\u4FEE\u590D**
|
|
300
301
|
- \u4F18\u5148\u7EA7\uFF1A\u{1F534} \u5FC5\u4FEE > \u{1F7E1} \u5EFA\u8BAE > \u{1F7E2} \u53EF\u9009
|
|
301
302
|
|
|
302
303
|
**\u6536\u5C3E\u9636\u6BB5**\uFF1A
|
|
303
|
-
- \u4FEE\u590D
|
|
304
|
-
- \u751F\u6210 summary\uFF1A\u6539\u52A8/\u65B0\u5EFA\u6587\u4EF6\u3001\u590D\u7528\u8D44\u6E90\u3001\u9A8C\u8BC1\u7ED3\u679C\u3001\
|
|
305
|
-
- \u53EA\u6709\u9A8C\u8BC1\u901A\u8FC7\u4E14
|
|
304
|
+
- \u4E00\u8F6E\u4FEE\u590D\u540E\u91CD\u8DD1\u53D7\u5F71\u54CD\u7684 build/lint/typecheck/test \u548C\u5FC5\u8981 smoke test\uFF0C\u4E0D\u91CD\u590D\u8FD0\u884C\u5DF2\u5B8C\u6210 reviewer\u3002
|
|
305
|
+
- \u751F\u6210 summary\uFF1A\u6539\u52A8/\u65B0\u5EFA\u6587\u4EF6\u3001\u590D\u7528\u8D44\u6E90\u3001\u9A8C\u8BC1\u7ED3\u679C\u3001\u5BA1\u67E5\u6DF1\u5EA6\u3001\u95EE\u9898\u5904\u7F6E\u3001\u8DF3\u8FC7\u6B65\u9AA4\u548C\u6B8B\u4F59\u98CE\u9669\u3002
|
|
306
|
+
- \u53EA\u6709\u9A8C\u8BC1\u901A\u8FC7\u3001\u6240\u9700\u5BA1\u67E5\u9886\u57DF\u5B8C\u6210\u4E14 blocker \u6E05\u96F6\u624D\u5EFA\u8BAE \`completed\`\uFF1B\u5426\u5219\u5199 \`blocked\` \u4E0E\u660E\u786E blockingReason\u3002
|
|
306
307
|
- \u5C06\u5173\u952E\u51B3\u7B56\u4EA4\u7ED9\u8C03\u7528\u65B9 appendTeamDecision\uFF0C\u5E76\u66F4\u65B0 Active Plans\u3002
|
|
307
308
|
- \u5408\u5E76 worktree \u524D\u53D6\u5F97\u7528\u6237\u786E\u8BA4\uFF1B\u6E05\u7406\u4E34\u65F6\u6587\u4EF6\u3002
|
|
308
309
|
|
|
@@ -331,6 +332,8 @@ var FILM_ANALYST_OFFENSE_AGENT = {
|
|
|
331
332
|
- \u{1F7E1} \u5EFA\u8BAE\uFF08\u5E94\u8BE5\u4FEE\uFF09
|
|
332
333
|
- \u{1F7E2} \u53EF\u9009\uFF08\u53EF\u4EE5\u4E0D\u4FEE\uFF09
|
|
333
334
|
- \u6700\u540E\u7ED9\u8BC4\u5206\uFF081-10\uFF09+ \u901A\u8FC7 / \u4E0D\u901A\u8FC7
|
|
335
|
+
- \u53EA\u5BA1\u67E5\u672C\u6B21 diff\u3001\u7528\u6237\u9700\u6C42\u548C\u76F4\u63A5\u53D7\u5F71\u54CD\u8DEF\u5F84\uFF1B\u6700\u591A 3 \u4E2A\u65B0\u95EE\u9898
|
|
336
|
+
- \u6BCF\u4E2A\u95EE\u9898\u5FC5\u987B\u5305\u542B\u6539\u52A8\u884C\u3001\u53EF\u6838\u67E5\u8BC1\u636E\u548C\u7528\u6237\u5F71\u54CD\u3002\u6CA1\u6709\u8BC1\u636E\u7684 checklist \u9879\u4E0D\u8F93\u51FA
|
|
334
337
|
|
|
335
338
|
## \u4F60\u4E0D\u5199\u4EE3\u7801
|
|
336
339
|
|
|
@@ -388,6 +391,8 @@ var FILM_ANALYST_OFFENSE_AGENT = {
|
|
|
388
391
|
- \u4E0D\u8981\u5EFA\u8BAE**\u4E0E\u9879\u76EE\u98CE\u683C\u4E0D\u7B26**\u7684\u6539\u52A8
|
|
389
392
|
- \u4E0D\u8981\u5EFA\u8BAE**\u63A8\u6D4B\u6027\u529F\u80FD**\u6216**\u8FC7\u5EA6\u62BD\u8C61**
|
|
390
393
|
- \u4E0D\u8981"\u987A\u4FBF\u5EFA\u8BAE"\u548C\u5F53\u524D\u4EFB\u52A1\u65E0\u5173\u7684\u4E8B
|
|
394
|
+
- \u4E0D\u8981\u56E0\u4E3A\u7F3A\u5C11\u901A\u7528 loading\u3001\u9519\u8BEF\u683C\u5F0F\u6216\u56DE\u5F52\u6D4B\u8BD5\u800C\u673A\u68B0\u8BC4\u8BBA\uFF1B\u53EA\u6709\u672C\u6B21\u884C\u4E3A\u786E\u5B9E\u5F15\u5165\u76F8\u5E94\u8DEF\u5F84\u4E14\u6709\u8BC1\u636E\u65F6\u624D\u62A5\u544A
|
|
395
|
+
- \u540C\u4E00\u6839\u56E0\u53EA\u62A5\u4E00\u6B21\uFF1B\u{1F534} \u4F7F\u7528\u7A33\u5B9A ID\uFF08Q1\u3001Q2\u2026\u2026\uFF09\uFF0C\u4F9B review ledger \u53BB\u91CD\u548C\u6536\u5C3E
|
|
391
396
|
|
|
392
397
|
\u6536\u5230\u4EE3\u7801\u540E\u7ACB\u5373\u5F00\u59CB\u5BA1\u67E5\u3002\u62A5\u544A\u5B8C\u6574\u5373\u53EF\u3002`
|
|
393
398
|
};
|
|
@@ -411,6 +416,8 @@ var FILM_ANALYST_DEFENSE_AGENT = {
|
|
|
411
416
|
- \u{1F7E2} \u53EF\u9009\uFF08\u52A0\u56FA\u5EFA\u8BAE\uFF09
|
|
412
417
|
- **\u5B89\u5168\u95EE\u9898\u4E00\u5F8B \u{1F534}**
|
|
413
418
|
- \u6700\u540E\u7ED9\u8BC4\u5206\uFF081-10\uFF09+ \u901A\u8FC7 / \u4E0D\u901A\u8FC7
|
|
419
|
+
- \u53EA\u5BA1\u67E5\u672C\u6B21 diff\u3001\u7528\u6237\u9700\u6C42\u548C\u76F4\u63A5\u53D7\u5F71\u54CD\u8DEF\u5F84\uFF1B\u6700\u591A 3 \u4E2A\u65B0\u95EE\u9898
|
|
420
|
+
- \u6BCF\u4E2A\u95EE\u9898\u5FC5\u987B\u5305\u542B\u6539\u52A8\u884C\u3001\u53EF\u6838\u67E5\u8BC1\u636E\u548C\u7528\u6237\u5F71\u54CD
|
|
414
421
|
|
|
415
422
|
## \u4F60\u4E0D\u5199\u4EE3\u7801
|
|
416
423
|
|
|
@@ -462,8 +469,7 @@ var FILM_ANALYST_DEFENSE_AGENT = {
|
|
|
462
469
|
|
|
463
470
|
## \u4E0D\u8981\u91CD\u590D Film Analyst #1 \u7684\u53CD\u9988
|
|
464
471
|
|
|
465
|
-
\u5982\u679C #1 \u5DF2\u7ECF\u6307\u51FA\
|
|
466
|
-
\u4F60\u4E13\u6CE8\u8FB9\u754C\u3001\u5B89\u5168\u3001\u6027\u80FD\u3001\u8D44\u6E90\u3002
|
|
472
|
+
\u5F00\u59CB\u524D\u5FC5\u987B\u8BFB\u53D6 \`review-scope.md\` \u548C \`film-report-1.md\`\u3002\u5982\u679C #1 \u5DF2\u7ECF\u6307\u51FA\u76F8\u540C\u6839\u56E0\uFF0C\u6807\u8BB0\u4E3A \`duplicate\` \u5E76\u4E14\u4E0D\u8981\u91CD\u65B0\u8F93\u51FA\u4E3A finding\u3002\u4F60\u53EA\u4E13\u6CE8\u8FB9\u754C\u3001\u5B89\u5168\u3001\u6027\u80FD\u3001\u8D44\u6E90\uFF1B\u{1F534} \u4F7F\u7528\u7A33\u5B9A ID\uFF08D1\u3001D2\u2026\u2026\uFF09\u3002
|
|
467
473
|
|
|
468
474
|
## \u7EA2\u7EBF
|
|
469
475
|
|
|
@@ -757,23 +763,19 @@ var SOLO_SKILL = `# mancode \xB7 solo mode
|
|
|
757
763
|
4. \u4EE5\u9879\u76EE\u65E2\u6709\u7684\u65E0\u969C\u788D\u3001\u54CD\u5E94\u5F0F\u4E0E\u53CD\u9988\u65B9\u5F0F\u4E3A\u51C6
|
|
758
764
|
|
|
759
765
|
### \u754C\u9762\u8D28\u91CF\u68C0\u67E5\uFF08\u6761\u4EF6\u6267\u884C\uFF09
|
|
760
|
-
|
|
761
|
-
- \u662F\u5426\u590D\u7528\u4E86\u9879\u76EE\u5B9E\u9645\u7684\u5C42\u7EA7\u3001\u95F4\u8DDD\u3001\u989C\u8272\u3001\u5B57\u4F53\u4E0E\u4EA4\u4E92\u53CD\u9988\uFF1F
|
|
762
|
-
- \u4FE1\u606F\u5C42\u7EA7\u3001\u7A7A/\u52A0\u8F7D/\u9519\u8BEF\u72B6\u6001\u548C\u952E\u76D8/\u8BFB\u5C4F\u53EF\u8FBE\u6027\u662F\u5426\u6E05\u695A\uFF1F
|
|
763
|
-
- \u662F\u5426\u4E3A\u4E86\u201C\u597D\u770B\u201D\u5F15\u5165\u4E0E\u73B0\u6709\u4EA7\u54C1\u65E0\u5173\u7684\u88C5\u9970\u3001\u52A8\u753B\u6216\u7EC4\u4EF6\uFF1F
|
|
766
|
+
\u53EA\u68C0\u67E5\u672C\u6B21 diff \u65B0\u589E\u6216\u6539\u53D8\u7684\u754C\u9762\u884C\u4E3A\uFF1A\u590D\u7528\u5DF2\u6709\u5C42\u7EA7\u3001token \u548C\u7EC4\u4EF6\uFF1B\u65B0\u589E\u5F02\u6B65\u6216\u4EA4\u4E92\u8DEF\u5F84\u65F6\u624D\u68C0\u67E5\u5BF9\u5E94\u7684\u52A0\u8F7D\u3001\u5931\u8D25\u548C\u53EF\u8FBE\u6027\u72B6\u6001\u3002\u4E0D\u8981\u501F UI \u81EA\u68C0\u5DE1\u67E5\u672A\u6539\u52A8\u9875\u9762\u6216\u8865\u9F50\u63A8\u6D4B\u6027\u72B6\u6001\u3002
|
|
764
767
|
|
|
765
768
|
### \u9A8C\u8BC1
|
|
766
769
|
\u4EE3\u7801\u5B8C\u6210\u540E\uFF1A
|
|
767
|
-
1. \
|
|
768
|
-
2. \u8FD0\u884C
|
|
769
|
-
3. \
|
|
770
|
-
4. \u786E\u8BA4\u901A\u8FC7\u540E\u624D\u58F0\u79F0\u5B8C\u6210
|
|
770
|
+
1. \u9009\u62E9\u4E0E\u672C\u6B21\u6539\u52A8\u76F4\u63A5\u76F8\u5173\u7684\u6700\u7A84 test\u3001lint\u3001typecheck\u3001build \u6216 smoke check
|
|
771
|
+
2. \u6587\u6848\u3001\u6CE8\u91CA\u6216\u7EAF\u9759\u6001\u8D44\u6E90\u6539\u52A8\u4E0D\u5F3A\u5236\u8FD0\u884C\u5B8C\u6574\u6D4B\u8BD5\u77E9\u9635
|
|
772
|
+
3. \u786E\u8BA4\u5B9E\u9645\u6267\u884C\u7ED3\u679C\u540E\u624D\u58F0\u79F0\u5B8C\u6210
|
|
771
773
|
|
|
772
|
-
### \u5B8C\u6210\u540E\
|
|
773
|
-
- \
|
|
774
|
-
- \
|
|
775
|
-
- \
|
|
776
|
-
- \
|
|
774
|
+
### \u5B8C\u6210\u540E\u7684\u4E00\u6B21\u53D7\u9650\u81EA\u68C0
|
|
775
|
+
- \u53EA\u505A\u4E00\u6B21\uFF0C\u53EA\u770B\u672C\u6B21 diff\u3001\u9700\u6C42\u548C\u76F4\u63A5\u53D7\u5F71\u54CD\u8DEF\u5F84\uFF1B\u4E0D\u8C03\u7528\u989D\u5916 reviewer\uFF0C\u4E0D\u751F\u6210\u5BA1\u67E5\u62A5\u544A\uFF0C\u4E0D\u91CD\u590D\u5DE1\u68C0\u3002
|
|
776
|
+
- \u9A8C\u8BC1\u5931\u8D25\u3001\u884C\u4E3A\u56DE\u5F52\u6216\u9057\u7559 debug \u53EF\u76F4\u63A5\u4FEE\u590D\u5E76\u590D\u9A8C\uFF1B\u590D\u9A8C\u4E0D\u662F\u65B0\u4E00\u8F6E review\u3002
|
|
777
|
+
- \u547D\u540D\u3001\u53EF\u8BFB\u6027\u3001DRY\u3001loading/error \u5F62\u5F0F\u7B49\u5EFA\u8BAE\u4E0D\u81EA\u52A8\u6269\u5927\u6539\u52A8\uFF1B\u4E0E\u9700\u6C42\u65E0\u5173\u65F6\u4E0D\u8F93\u51FA\u3002
|
|
778
|
+
- \u53EA\u6709\u9274\u6743\u3001\u652F\u4ED8\u3001\u654F\u611F\u6570\u636E\u3001\u8FC1\u79FB/\u5220\u9664\u3001\u516C\u5F00 API\u3001\u672A\u53EF\u4FE1\u8F93\u5165\u3001\u5E76\u53D1\u6216\u57FA\u7840\u8BBE\u65BD\u7B49\u786C\u98CE\u9669\u51FA\u73B0\u65F6\uFF0C\u624D\u7528\u4E00\u53E5\u8BDD\u5EFA\u8BAE \`/man\`\uFF1B\u7528\u6237\u8BF4\u201C\u7EE7\u7EED solo\u201D\u5373\u53EF\u7EE7\u7EED\u3002
|
|
777
779
|
|
|
778
780
|
## \u4F60\u7684\u98CE\u683C
|
|
779
781
|
|
|
@@ -833,21 +835,24 @@ var MAN_SKILL = {
|
|
|
833
835
|
|
|
834
836
|
\u8FD0\u884C\u5B9E\u9645 build/lint/typecheck/test \u548C smoke test\u3002\u76F8\u540C\u4EE3\u7801\u3001\u73AF\u5883\u3001\u547D\u4EE4\u4E0B\u76F8\u540C\u9519\u8BEF\u7B7E\u540D\u5931\u8D25\u4E24\u6B21\uFF0C\u505C\u6B62\u76F2\u8BD5\u5E76\u8BCA\u65AD\u6839\u56E0\u3002\u9700\u8981\u771F\u5B9E\u6D4F\u89C8\u5668\u3001\u590D\u6742\u590D\u73B0\u6216\u56DE\u5F52\u65F6\uFF0C\u7528 \`mancode workflow create mamba "<\u95EE\u9898>" --parent-task <taskId> --json\` \u521B\u5EFA\u5B50 workflow\uFF1B\u7236\u4EFB\u52A1\u4FDD\u6301 Step 6\u3002\u5B50\u4EFB\u52A1 fixed/verified/no_repro \u540E\u6062\u590D\u672C\u4EFB\u52A1\uFF1B\u82E5\u7236\u66FE\u56E0\u8BE5\u5B50\u4EFB\u52A1 blocked\uFF0C\u5148\u901A\u8FC7 \`workflow update --status in_progress\` \u6062\u590D\uFF0C\u518D\u66F4\u65B0\u81F3 Step 7\u3002blocked \u6216 manual_test_required \u4F1A\u7531 CLI \u81EA\u52A8\u963B\u585E\u7236\u4EFB\u52A1\uFF0C\u4E0D\u5F97\u624B\u6539\u7236 metadata\uFF0C\u4E5F\u4E0D\u5F97\u81EA\u52A8\u8D8A\u8FC7\u4EBA\u5DE5\u9A8C\u8BC1\u8981\u6C42\u3002
|
|
835
837
|
|
|
838
|
+
\u9A8C\u8BC1\u540E\u57FA\u4E8E**\u5B9E\u9645 diff**\u5199 \`review-scope.md\`\uFF1Abase\u3001\u6539\u52A8\u6587\u4EF6\u3001\u9700\u6C42\u3001\u5DF2\u8DD1\u9A8C\u8BC1\u3001\u786C\u98CE\u9669\u548C\u5BA1\u67E5\u6DF1\u5EA6\u3002\u9274\u6743\u3001\u652F\u4ED8\u3001\u654F\u611F\u6570\u636E\u3001\u8FC1\u79FB/\u5220\u9664\u3001\u516C\u5F00 API\u3001\u672A\u53EF\u4FE1\u8F93\u5165\u3001\u5E76\u53D1\u3001\u8DE8\u670D\u52A1\u6216\u57FA\u7840\u8BBE\u65BD\u547D\u4E2D\u4EFB\u4E00\u9879\u65F6\u7528\u5B8C\u6574\u5BA1\u67E5 \`full\`\uFF1B\u5426\u5219\u7528\u5B9A\u5411\u5BA1\u67E5 \`targeted\`\u3002\u8FD0\u884C \`mancode workflow review <taskId> init --review-depth targeted --review-domain quality\` \u6216 \`--review-depth full\`\u3002\u7528\u6237\u660E\u786E\u8DF3\u8FC7\u5BA1\u67E5\u65F6\u624D\u628A \`review\` \u5199\u5165\u7D2F\u8BA1 skippedSteps\uFF0C\u5E76\u8BB0\u5F55\u6B8B\u4F59\u98CE\u9669\u3002
|
|
839
|
+
|
|
836
840
|
### Step 7: Film #1 \u4EE3\u7801\u8D28\u91CF\u5BA1\u67E5\u4E0E\u4FEE\u590D
|
|
837
841
|
|
|
838
|
-
\u8C03\u7528 \`film-analyst-offense
|
|
842
|
+
\u672A\u8DF3\u8FC7 review \u65F6\u8C03\u7528 \`film-analyst-offense\`\uFF0C\u53EA\u5BA1\u67E5\u672C\u6B21 diff \u7684\u884C\u4E3A\u6B63\u786E\u6027\u3001\u590D\u7528\u3001\u590D\u6742\u5EA6\u548C\u6D4B\u8BD5\uFF0C\u5199 \`film-report-1.md\`\u3002\u6BCF\u6761 finding \u5FC5\u987B\u5F15\u7528\u6539\u52A8\u884C\u3001\u7ED9\u51FA\u8BC1\u636E\u548C\u7528\u6237\u5F71\u54CD\uFF1B\u6700\u591A 3 \u4E2A\u65B0 finding\u3002\u7528\u7A33\u5B9A ID\uFF08\u5982 Q1\uFF09\u6807\u8BB0 \u{1F534} blocker\uFF0C\u5E76\u8FD0\u884C \`mancode workflow review <taskId> complete --review-domain quality --report film-report-1.md --blockers Q1,Q2\`\uFF1B\u6CA1\u6709 blocker \u65F6\u4F20\u7A7A\u5B57\u7B26\u4E32\u3002\u6B64\u65F6\u4E0D\u4FEE\u590D\uFF0C\u5148\u5B8C\u6210\u6240\u9700\u5BA1\u67E5\u9886\u57DF\uFF0C\u518D\u66F4\u65B0\u81F3 Step 8\u3002
|
|
839
843
|
|
|
840
844
|
### Step 8: Film #2 \u5B89\u5168\u4E0E\u8FB9\u754C\u5BA1\u67E5
|
|
841
845
|
|
|
842
|
-
\u8C03\u7528 \`film-analyst-defense\` \u5BA1\u67E5\u5B89\u5168\u3001\u6743\u9650\u3001\u9519\u8BEF\u8DEF\u5F84\u3001\u517C\u5BB9\u6027\u548C\u8FB9\u754C\uFF0C\u5199 \`film-report-2.md
|
|
846
|
+
\`full\` \u624D\u8C03\u7528 \`film-analyst-defense\`\uFF1B\u5B83\u5FC5\u987B\u5148\u8BFB \`review-scope.md\` \u548C \`film-report-1.md\`\uFF0C\u53EA\u5BA1\u67E5\u5B89\u5168\u3001\u6743\u9650\u3001\u9519\u8BEF\u8DEF\u5F84\u3001\u517C\u5BB9\u6027\u3001\u8D44\u6E90\u548C\u8FB9\u754C\u3002\u76F8\u540C\u6839\u56E0\u6807\u8BB0 \`duplicate\`\uFF0C\u4E0D\u5F97\u91CD\u65B0\u62A5\u544A\u3002\u5199 \`film-report-2.md\` \u540E\u4EE5 D1 \u7B49\u7A33\u5B9A blocker ID \u8FD0\u884C \`mancode workflow review <taskId> complete --review-domain security --report film-report-2.md --blockers D1\`\u3002\`targeted\` \u4E0D\u6267\u884C\u7B2C\u4E8C\u5BA1\uFF0C\u4E5F\u4E0D\u628A\u5B83\u63CF\u8FF0\u6210\u201C\u8DF3\u8FC7\u201D\uFF1B\u5B8C\u6210\u540E\u66F4\u65B0\u81F3 Step 9\u3002
|
|
843
847
|
|
|
844
848
|
### Step 9: \u589E\u5F3A\u6536\u5C3E
|
|
845
849
|
|
|
846
|
-
1. \u4FEE\u590D
|
|
847
|
-
2. \
|
|
848
|
-
3. \u9A8C\u8BC1\
|
|
849
|
-
4. \
|
|
850
|
-
5.
|
|
850
|
+
1. \u5B58\u5728 open blocker \u65F6\uFF0CHead Coach \u4E00\u6B21\u6027\u4FEE\u590D\u5168\u90E8 blocker\uFF0C\u5E76\u7528 \`mancode workflow review <taskId> remediate --resolved Q1,D1\` \u8BB0\u5F55\u552F\u4E00\u4E00\u8F6E\u4FEE\u590D\uFF1B\u6CA1\u6709 blocker \u65F6\u4E0D\u8FD0\u884C remediate\u3002\u4E0D\u8981\u4E3A \u{1F7E1}/\u{1F7E2} \u6269\u5927\u6539\u52A8\u3002
|
|
851
|
+
2. \u91CD\u8DD1\u53D7\u5F71\u54CD\u9A8C\u8BC1\uFF0C\u4E0D\u91CD\u65B0\u8FD0\u884C\u5DF2\u5B8C\u6210\u7684 reviewer\u3002\u4FEE\u590D\u82E5\u5F15\u5165\u65B0\u7684\u9AD8\u98CE\u9669\u9762\u5219\u6807\u8BB0 blocked\uFF0C\u4E0D\u80FD\u5F00\u542F\u65E0\u754C review \u5FAA\u73AF\u3002
|
|
852
|
+
3. \u5199 \`summary.md\`\uFF1A\u6539\u52A8\u3001\u65B0\u5EFA\u3001\u590D\u7528\u3001\u9A8C\u8BC1\u3001\u5BA1\u67E5\u6DF1\u5EA6\u3001findings \u5904\u7F6E\u3001\u8DF3\u8FC7\u6B65\u9AA4\u548C\u6B8B\u4F59\u98CE\u9669\u3002
|
|
853
|
+
4. CLI \u786E\u8BA4\u6240\u9700\u5BA1\u67E5\u9886\u57DF\u5B8C\u6210\u4E14 blocker \u6E05\u96F6\u540E\u624D\u5199 \`completed\`\uFF1B\u5426\u5219\u7528 \`--status blocked --blocking-reason "<\u539F\u56E0>"\`\u3002
|
|
854
|
+
5. \u5173\u952E\u51B3\u7B56 appendTeamDecision \u5230 \`decisions.md\`\uFF0C\u66F4\u65B0 Active Plans\u3002
|
|
855
|
+
6. worktree \u5408\u5E76\u524D\u53D6\u5F97\u7528\u6237\u786E\u8BA4\uFF1B\u7EC8\u6001\u5199\u5165\u6210\u529F\u540E state \u56DE solo \u5E76\u6E05\u7A7A workflow \u6307\u9488\u3002
|
|
851
856
|
|
|
852
857
|
\u4EFB\u4F55 step/status/outcome/planVersion \u53D8\u5316\u90FD\u5FC5\u987B\u7ECF\u8FC7 workflow CLI\uFF1BCLI \u62D2\u7EDD\u65F6\u4FDD\u7559\u5F53\u524D state \u5E76\u62A5\u544A\u539F\u56E0\uFF0C\u4E0D\u53EF\u7ED5\u8FC7\u6821\u9A8C\u76F4\u63A5\u6539 metadata.json\u3002
|
|
853
858
|
|
|
@@ -1174,9 +1179,9 @@ var MANTEAM_SKILL = {
|
|
|
1174
1179
|
4. \u8BA1\u5212\u5173\u5361\uFF1A\u7528\u6237\u53EF\u53EA\u4FDD\u7559\u8BA1\u5212\u3001\u7EE7\u7EED\u6267\u884C\u6216\u91CD\u5199\u8BA1\u5212\uFF1BActive Plans \u6309 taskId \u66F4\u65B0\u3002
|
|
1175
1180
|
5. \u5B9E\u65BD\uFF1A\u6539\u52A8\u524D\u518D\u6B21\u68C0\u67E5 \`git status --short\`\uFF0C\u907F\u514D\u8E29\u7528\u6237\u6216\u961F\u53CB\u6539\u52A8\u3002
|
|
1176
1181
|
6. Self-test\uFF1A\u4F18\u5148\u8DD1\u9879\u76EE\u5DF2\u6709\u9A8C\u8BC1\u547D\u4EE4\uFF1B\u5931\u8D25\u4E24\u6B21\u505C\u4E0B\u8BCA\u65AD\u6839\u56E0\u3002\u9700\u8981\u771F\u5B9E\u9A8C\u8BC1\u65F6\u521B\u5EFA\u5E26 parentTaskId \u7684 /mamba \u5B50\u4EFB\u52A1\u3002\u5B50\u4EFB\u52A1\u4FEE\u590D\u540E\u82E5\u7236\u4EFB\u52A1\u66FE\u56E0\u6B64 blocked\uFF0C\u5148\u7528 workflow CLI \u6062\u590D\u7236\u4EFB\u52A1\u4E3A in_progress\uFF1Bmanual_test_required \u4E0D\u5F97\u81EA\u52A8\u6062\u590D\u3002
|
|
1177
|
-
7. Film #1\uFF1A\u91CD\u70B9\u5BA1\u67E5\u53EF\u7EF4\u62A4\u6027\u3001\
|
|
1178
|
-
8. Film #2\uFF1A\u91CD\
|
|
1179
|
-
9. Post-game\uFF1A\u590D\u9A8C\
|
|
1182
|
+
7. Review scope + Film #1\uFF1A\u57FA\u4E8E\u5B9E\u9645 diff \u5199 \`review-scope.md\`\uFF0C\u7528 \`workflow review ... init\` \u9009\u62E9 targeted \u6216 full\uFF1B\u91CD\u70B9\u5BA1\u67E5\u884C\u4E3A\u3001\u53EF\u7EF4\u62A4\u6027\u3001\u56E2\u961F\u98CE\u683C\u4E0E\u6D4B\u8BD5\uFF0Cfinding \u5FC5\u987B\u6709\u8BC1\u636E\u548C\u7A33\u5B9A ID\u3002
|
|
1183
|
+
8. Film #2\uFF1A\u4EC5 full \u6267\u884C\uFF0C\u5148\u8BFB Film #1 \u62A5\u544A\u5E76\u53BB\u91CD\uFF0C\u53EA\u5BA1\u67E5\u8FB9\u754C\u3001\u5B89\u5168\u3001\u6027\u80FD\u3001\u5E76\u53D1\u548C\u517C\u5BB9\u6027\u3002\u7528\u6237\u660E\u786E\u8981\u6C42\u624D\u53EF\u8DF3\u8FC7\u5168\u90E8 review\uFF0C\u5E76\u5199\u5165 \`skippedSteps\` \u548C\u6B8B\u4F59\u98CE\u9669\u3002
|
|
1184
|
+
9. Post-game\uFF1A\u6C47\u603B blocker\uFF0C\u53EA\u505A\u4E00\u8F6E\u4FEE\u590D\u5E76\u7528 \`workflow review ... remediate\` \u8BB0\u5F55\uFF1B\u590D\u9A8C\u540E\u5199 summary \u4E0E hand-off\u3002\u9A8C\u8BC1\u5931\u8D25\u3001\u5BA1\u67E5\u4E0D\u5B8C\u6574\u6216\u4ECD\u6709 blocker \u65F6\u6807\u8BB0 blocked\uFF0C\u4E0D\u5F97\u6807 completed\u3002
|
|
1180
1185
|
|
|
1181
1186
|
\u5982\u679C\u7528\u6237\u5728\u786E\u8BA4\u9636\u6BB5\u9009\u62E9"\u9000\u51FA"\u6216\u653E\u5F03\u56E2\u961F workflow\uFF1A\u7528 \`mancode workflow update <taskId> --status abandoned\` \u66F4\u65B0\u5E76\u6E05\u7406 Active Plans\uFF1B\u82E5\u6709\u6D3B\u8DC3 mamba \u5B50\u4EFB\u52A1\uFF0C\u5148\u53D6\u5F97\u786E\u8BA4\u5E76\u9010\u4E2A abandoned\u3002\u6210\u529F\u540E\u518D\u7528 Edit \u66F4\u65B0 state \u56DE solo\u3002\u4E0D\u8981\u76F4\u63A5\u6539 metadata\uFF0C\u4E5F\u4E0D\u8981\u628A abandoned workflow \u7559\u5728 active state\u3002
|
|
1182
1187
|
|
|
@@ -1454,6 +1459,14 @@ function isNodeError(err) {
|
|
|
1454
1459
|
// src/installers/claude-code.ts
|
|
1455
1460
|
var CLAUDE_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:claude-skill. Do not edit this marker. -->";
|
|
1456
1461
|
var CLAUDE_AGENT_MANAGED_MARKER = "<!-- Managed by mancode:claude-agent. Do not edit this marker. -->";
|
|
1462
|
+
var MANCODE_HOOK_COMMANDS = [
|
|
1463
|
+
"bash .mancode/hooks/session-start.sh",
|
|
1464
|
+
"bash .mancode/hooks/user-prompt-submit.sh"
|
|
1465
|
+
];
|
|
1466
|
+
function isGeneratedMancodeHookCommand(command) {
|
|
1467
|
+
const normalized = command.trim();
|
|
1468
|
+
return MANCODE_HOOK_COMMANDS.some((item) => item === normalized);
|
|
1469
|
+
}
|
|
1457
1470
|
var LEGACY_CLAUDE_SKILL_SETTINGS = {
|
|
1458
1471
|
solo: ".claude/skills/mancode-solo.md",
|
|
1459
1472
|
man8: ".claude/skills/mancode-man8.md",
|
|
@@ -1641,11 +1654,11 @@ async function updateClaudeSettings(claudeDir, settings) {
|
|
|
1641
1654
|
settings.hooks = settings.hooks || {};
|
|
1642
1655
|
settings.hooks.SessionStart = [
|
|
1643
1656
|
...normalizeHookGroups(settings.hooks.SessionStart),
|
|
1644
|
-
createCommandGroup(
|
|
1657
|
+
createCommandGroup(MANCODE_HOOK_COMMANDS[0])
|
|
1645
1658
|
];
|
|
1646
1659
|
settings.hooks.UserPromptSubmit = [
|
|
1647
1660
|
...normalizeHookGroups(settings.hooks.UserPromptSubmit),
|
|
1648
|
-
createCommandGroup(
|
|
1661
|
+
createCommandGroup(MANCODE_HOOK_COMMANDS[1])
|
|
1649
1662
|
];
|
|
1650
1663
|
removeLegacyMancodeSkillSettings(settings);
|
|
1651
1664
|
const content = `${JSON.stringify(settings, null, 2)}
|
|
@@ -1676,7 +1689,10 @@ function normalizeHookGroups(value) {
|
|
|
1676
1689
|
return value.flatMap(normalizeHookGroupEntry);
|
|
1677
1690
|
}
|
|
1678
1691
|
if (isRecord(value)) {
|
|
1679
|
-
return Object.
|
|
1692
|
+
return Object.entries(value).flatMap(([key, entry]) => {
|
|
1693
|
+
if (key === "mancode" && containsLegacyMancodeHookPath(entry)) return [];
|
|
1694
|
+
return normalizeHookGroupEntry(entry);
|
|
1695
|
+
});
|
|
1680
1696
|
}
|
|
1681
1697
|
return [];
|
|
1682
1698
|
}
|
|
@@ -1714,7 +1730,15 @@ function normalizeHookItem(value) {
|
|
|
1714
1730
|
};
|
|
1715
1731
|
}
|
|
1716
1732
|
function isMancodeHook(hook) {
|
|
1717
|
-
return hook.command
|
|
1733
|
+
return isGeneratedMancodeHookCommand(hook.command);
|
|
1734
|
+
}
|
|
1735
|
+
function containsLegacyMancodeHookPath(value) {
|
|
1736
|
+
if (Array.isArray(value)) return value.some(containsLegacyMancodeHookPath);
|
|
1737
|
+
if (!isRecord(value)) return false;
|
|
1738
|
+
if (typeof value.command === "string" && value.command.includes(".mancode/hooks/")) {
|
|
1739
|
+
return true;
|
|
1740
|
+
}
|
|
1741
|
+
return Object.values(value).some(containsLegacyMancodeHookPath);
|
|
1718
1742
|
}
|
|
1719
1743
|
function isRecord(value) {
|
|
1720
1744
|
return typeof value === "object" && value !== null;
|
|
@@ -2175,10 +2199,10 @@ var MODE_META = {
|
|
|
2175
2199
|
"Step 3 \u2014 Plan: write `plan.md` with scope, reuse, exclusions, risks, completion criteria, and real validation. First plan is v1; revisions use `--plan-version <current+1>` and Active Plans is updated by the CLI.",
|
|
2176
2200
|
"Step 4 \u2014 Plan gate: the user chooses plan-only (`status planned`), continue to execution, or revise the plan. Do not implement before this choice.",
|
|
2177
2201
|
"Step 5 \u2014 Implement the confirmed minimum change.",
|
|
2178
|
-
"Step 6 \u2014 Run detected build/lint/typecheck/test and smoke checks. A real or complex diagnosis creates a linked mamba child while the parent remains at Step 6. After a resolved child, restore a parent blocked by that child with `workflow update --status in_progress` before Step 7; never auto-resume manual_test_required.",
|
|
2179
|
-
"Step 7 \u2014
|
|
2180
|
-
"Step 8 \u2014
|
|
2181
|
-
"Step 9 \u2014
|
|
2202
|
+
"Step 6 \u2014 Run detected build/lint/typecheck/test and smoke checks. A real or complex diagnosis creates a linked mamba child while the parent remains at Step 6. After a resolved child, restore a parent blocked by that child with `workflow update --status in_progress` before Step 7; never auto-resume manual_test_required. Then write `review-scope.md` from the actual diff, verification evidence, and hard-risk triggers. Initialize a targeted review for routine governed changes or a full review for auth, payment, sensitive data, migrations/deletion, public APIs, untrusted input, concurrency, cross-service, or infrastructure changes using `mancode workflow review <taskId> init ...`.",
|
|
2203
|
+
"Step 7 \u2014 Run one quality review limited to the changed diff and direct impact. Findings require changed-line evidence and user impact, with at most three new findings. Record stable blocker IDs through `workflow review ... complete`; do not fix yet.",
|
|
2204
|
+
"Step 8 \u2014 Only full review runs the security/boundary reviewer. It must read Film #1, mark the same root cause duplicate, and stay within security, permissions, recovery, resources, and boundaries. A targeted review ends after Film #1.",
|
|
2205
|
+
"Step 9 \u2014 If open blockers exist, fix them in one remediation round and record resolved IDs through `workflow review ... remediate`; with no blockers, do not create a remediation round. Re-run affected verification without re-running completed reviewers. Write `summary.md` and set completed only when required domains are complete and blockers are zero; otherwise set blocked+blockingReason.",
|
|
2182
2206
|
"",
|
|
2183
2207
|
"After planned/completed/abandoned, return state to solo. On blocked, preserve enough state to resume. A CLI rejection is a hard stop, never a reason to bypass metadata validation."
|
|
2184
2208
|
].join("\n")
|
|
@@ -2195,7 +2219,7 @@ var MODE_META = {
|
|
|
2195
2219
|
"shared-module, and handoff risks in `team-context.md`.",
|
|
2196
2220
|
"",
|
|
2197
2221
|
"Then execute every /man Step 1 through Step 9, including clarification,",
|
|
2198
|
-
"plan gate, validation,
|
|
2222
|
+
"plan gate, validation, bounded risk-based review, conditional completion, planVersion,",
|
|
2199
2223
|
"Active Plans, and linked mamba child behavior. Add compatibility, rollback,",
|
|
2200
2224
|
"ownership, conflict, and handoff notes at each relevant step.",
|
|
2201
2225
|
"When a resolved mamba child had blocked the parent, restore the parent through",
|
|
@@ -2298,7 +2322,9 @@ function renderPracticeRules() {
|
|
|
2298
2322
|
"5. Prefer a one-line fix when it is enough.",
|
|
2299
2323
|
"6. Only then write the smallest new implementation.",
|
|
2300
2324
|
"",
|
|
2301
|
-
"For every task, consider: why this change, what already exists, and what is the smallest useful diff?"
|
|
2325
|
+
"For every task, consider: why this change, what already exists, and what is the smallest useful diff?",
|
|
2326
|
+
"",
|
|
2327
|
+
"In solo mode, use the narrowest meaningful validation and one bounded self-check limited to the current diff. Do not start another reviewer or repeat the review. Only recommend man for auth, payment, sensitive data, migrations/deletion, public APIs, untrusted input, concurrency, or infrastructure risk."
|
|
2302
2328
|
].join("\n");
|
|
2303
2329
|
}
|
|
2304
2330
|
function renderAesthetics(tokens) {
|
|
@@ -2407,7 +2433,7 @@ async function installCodex(projectRoot, options) {
|
|
|
2407
2433
|
const existing = await readTextIfExists(agentsPath);
|
|
2408
2434
|
const sharedContent = await generateSharedContent(projectRoot, {
|
|
2409
2435
|
platform: "codex",
|
|
2410
|
-
displayName: "Codex CLI",
|
|
2436
|
+
displayName: "Codex (ChatGPT desktop/CLI)",
|
|
2411
2437
|
capabilities: {
|
|
2412
2438
|
slashCommands: "partial",
|
|
2413
2439
|
subagents: false,
|
|
@@ -2480,7 +2506,7 @@ function renderCopilotPromptConventions() {
|
|
|
2480
2506
|
"",
|
|
2481
2507
|
"GitHub Copilot does not provide native mancode slash commands, hooks, or isolated subagents. Treat these names as user prompt conventions:",
|
|
2482
2508
|
"",
|
|
2483
|
-
"- man: use progressive research, planning, implementation, verification, and review.",
|
|
2509
|
+
"- man: use progressive research, planning, implementation, verification, and bounded risk-based review.",
|
|
2484
2510
|
"- mamba: diagnose bugs and validate real user flows or regressions.",
|
|
2485
2511
|
"- manteam: read team memory and write handoff-friendly summaries.",
|
|
2486
2512
|
"- manps: prefer `mancode manps [area]` before cleanup.",
|
|
@@ -2698,7 +2724,9 @@ function renderSoloRule() {
|
|
|
2698
2724
|
"- Keep the diff narrow.",
|
|
2699
2725
|
"- Reuse existing functions, components, styles, and dependencies.",
|
|
2700
2726
|
"- Read `.mancode/project-profile.json` before choosing tools or validation. For detected UI assets and UI work, read `.mancode/aesthetics/style-tokens.json` and inspect existing components before inventing new styles.",
|
|
2701
|
-
"- Verify with the narrowest meaningful test, lint, build, or smoke check."
|
|
2727
|
+
"- Verify with the narrowest meaningful test, lint, build, or smoke check.",
|
|
2728
|
+
"- Perform one bounded self-check limited to the current diff. Do not start another reviewer, create a review artifact, or repeat the review.",
|
|
2729
|
+
"- Recommend /man only for auth, payment, sensitive data, migrations/deletion, public APIs, untrusted input, concurrency, or infrastructure risk."
|
|
2702
2730
|
].join("\n");
|
|
2703
2731
|
}
|
|
2704
2732
|
function renderMambaRule() {
|
|
@@ -2785,7 +2813,7 @@ var PLATFORM_INSTALLERS = {
|
|
|
2785
2813
|
},
|
|
2786
2814
|
codex: {
|
|
2787
2815
|
name: "codex",
|
|
2788
|
-
displayName: "Codex CLI",
|
|
2816
|
+
displayName: "Codex (ChatGPT desktop/CLI)",
|
|
2789
2817
|
capabilities: {
|
|
2790
2818
|
slashCommands: "partial",
|
|
2791
2819
|
subagents: false,
|
|
@@ -3716,14 +3744,16 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3716
3744
|
" (No recognized manifest found, profile confidence is low)"
|
|
3717
3745
|
);
|
|
3718
3746
|
}
|
|
3719
|
-
const
|
|
3747
|
+
const existingPlatform = wasInitialized ? await readExistingInitPlatform(stateFile) : null;
|
|
3748
|
+
const initPlatformName = options.platform ?? existingPlatform ?? DEFAULT_INIT_PLATFORM;
|
|
3720
3749
|
const installer = getPlatformInstaller(initPlatformName);
|
|
3721
3750
|
if (!installer) {
|
|
3722
3751
|
console.error(`\u2717 Unsupported platform: ${initPlatformName}`);
|
|
3723
3752
|
return EXIT_INIT_FAILED;
|
|
3724
3753
|
}
|
|
3725
3754
|
const team = await detectTeamStatus(rootDir);
|
|
3726
|
-
const existingPreferences = wasInitialized ? await readExistingInitPreferences(mancodeDir) : {};
|
|
3755
|
+
const existingPreferences = wasInitialized ? await readExistingInitPreferences(mancodeDir, installer.name) : {};
|
|
3756
|
+
const initialMinimal = existingPreferences.minimal ?? false;
|
|
3727
3757
|
const teamModeEnabled = options.team ?? existingPreferences.forceTeamMode ?? team.isTeam;
|
|
3728
3758
|
if (options.team === true) {
|
|
3729
3759
|
console.log(" team: forced on (--team)");
|
|
@@ -3769,7 +3799,7 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3769
3799
|
defaultStyle: options.style === void 0 && wasInitialized ? void 0 : options.style ?? null,
|
|
3770
3800
|
platforms: [installer.name],
|
|
3771
3801
|
platformOptions: {
|
|
3772
|
-
[installer.name]: { minimal:
|
|
3802
|
+
[installer.name]: { minimal: initialMinimal }
|
|
3773
3803
|
}
|
|
3774
3804
|
},
|
|
3775
3805
|
wasInitialized
|
|
@@ -3811,6 +3841,7 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3811
3841
|
techStack: profileStack,
|
|
3812
3842
|
uiLibrary,
|
|
3813
3843
|
projectProfile: profile,
|
|
3844
|
+
minimal: initialMinimal,
|
|
3814
3845
|
force: options.force
|
|
3815
3846
|
});
|
|
3816
3847
|
console.log("");
|
|
@@ -3831,6 +3862,10 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3831
3862
|
console.log(" mancode status # Show project state");
|
|
3832
3863
|
if (installer.name === "claude-code") {
|
|
3833
3864
|
console.log(" (Restart Claude Code to load hooks)");
|
|
3865
|
+
} else if (installer.name === "codex") {
|
|
3866
|
+
console.log(
|
|
3867
|
+
" (If skills do not appear, restart the ChatGPT desktop app or Codex session)"
|
|
3868
|
+
);
|
|
3834
3869
|
}
|
|
3835
3870
|
return EXIT_OK;
|
|
3836
3871
|
} catch (err) {
|
|
@@ -3878,16 +3913,34 @@ async function updateConfigOptions(mancodeDir, patch, preserveInstalledPlatforms
|
|
|
3878
3913
|
"utf-8"
|
|
3879
3914
|
);
|
|
3880
3915
|
}
|
|
3881
|
-
async function readExistingInitPreferences(mancodeDir) {
|
|
3916
|
+
async function readExistingInitPreferences(mancodeDir, platform) {
|
|
3882
3917
|
try {
|
|
3883
3918
|
const config = JSON.parse(
|
|
3884
3919
|
await fs2.readFile(path13.join(mancodeDir, "config.json"), "utf-8")
|
|
3885
3920
|
);
|
|
3886
|
-
|
|
3921
|
+
const preferences = {};
|
|
3922
|
+
if (typeof config.forceTeamMode === "boolean") {
|
|
3923
|
+
preferences.forceTeamMode = config.forceTeamMode;
|
|
3924
|
+
}
|
|
3925
|
+
if (Array.isArray(config.platforms) && config.platforms.includes(platform) && isRecord3(config.platformOptions)) {
|
|
3926
|
+
const platformOptions = config.platformOptions[platform];
|
|
3927
|
+
if (isRecord3(platformOptions) && platformOptions.minimal === true) {
|
|
3928
|
+
preferences.minimal = true;
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
return preferences;
|
|
3887
3932
|
} catch {
|
|
3888
3933
|
return {};
|
|
3889
3934
|
}
|
|
3890
3935
|
}
|
|
3936
|
+
async function readExistingInitPlatform(stateFile) {
|
|
3937
|
+
try {
|
|
3938
|
+
const state = JSON.parse(await fs2.readFile(stateFile, "utf-8"));
|
|
3939
|
+
return typeof state.platform === "string" && getPlatformInstaller(state.platform) ? state.platform : null;
|
|
3940
|
+
} catch {
|
|
3941
|
+
return null;
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3891
3944
|
async function snapshotFiles(filePaths) {
|
|
3892
3945
|
return Promise.all(
|
|
3893
3946
|
filePaths.map(async (filePath) => {
|
|
@@ -5674,24 +5727,220 @@ async function pathExists8(p) {
|
|
|
5674
5727
|
// src/commands/status.ts
|
|
5675
5728
|
import { spawn } from "child_process";
|
|
5676
5729
|
import { promises as fs7 } from "fs";
|
|
5677
|
-
import
|
|
5730
|
+
import path22 from "path";
|
|
5678
5731
|
import process7 from "process";
|
|
5679
5732
|
|
|
5680
5733
|
// src/system/workflow.ts
|
|
5681
5734
|
import {
|
|
5682
|
-
mkdir as
|
|
5683
|
-
readFile as
|
|
5735
|
+
mkdir as mkdir9,
|
|
5736
|
+
readFile as readFile10,
|
|
5684
5737
|
readdir as readdir5,
|
|
5685
5738
|
rm as rm4,
|
|
5686
|
-
stat as
|
|
5687
|
-
writeFile as
|
|
5739
|
+
stat as stat4,
|
|
5740
|
+
writeFile as writeFile11
|
|
5688
5741
|
} from "fs/promises";
|
|
5742
|
+
import path21 from "path";
|
|
5743
|
+
|
|
5744
|
+
// src/system/review-ledger.ts
|
|
5745
|
+
import { mkdir as mkdir8, readFile as readFile9, stat as stat3, writeFile as writeFile10 } from "fs/promises";
|
|
5689
5746
|
import path20 from "path";
|
|
5747
|
+
var REVIEW_FILE = "review-ledger.json";
|
|
5748
|
+
var TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
5749
|
+
var BLOCKER_ID_PATTERN = /^[A-Z][A-Z0-9-]{0,31}$/;
|
|
5750
|
+
function isReviewDepth(value) {
|
|
5751
|
+
return value === "targeted" || value === "full";
|
|
5752
|
+
}
|
|
5753
|
+
function isReviewDomain(value) {
|
|
5754
|
+
return value === "quality" || value === "security";
|
|
5755
|
+
}
|
|
5756
|
+
async function initializeReview(projectRoot, taskId, depth, targetDomain) {
|
|
5757
|
+
assertValidTaskId(taskId);
|
|
5758
|
+
const existing = await readReviewLedger(projectRoot, taskId);
|
|
5759
|
+
if (existing) throw new Error(`review already initialized: ${taskId}`);
|
|
5760
|
+
if (depth === "targeted" && !targetDomain) {
|
|
5761
|
+
throw new Error("targeted review requires a review domain");
|
|
5762
|
+
}
|
|
5763
|
+
const ledger = {
|
|
5764
|
+
version: "1.0",
|
|
5765
|
+
depth,
|
|
5766
|
+
requiredDomains: depth === "full" ? ["quality", "security"] : [targetDomain],
|
|
5767
|
+
completedDomains: [],
|
|
5768
|
+
reports: {},
|
|
5769
|
+
blockers: [],
|
|
5770
|
+
remediationRounds: 0
|
|
5771
|
+
};
|
|
5772
|
+
await writeReviewLedger(projectRoot, taskId, ledger);
|
|
5773
|
+
return ledger;
|
|
5774
|
+
}
|
|
5775
|
+
async function completeReviewDomain(projectRoot, taskId, domain, report, blockerIds) {
|
|
5776
|
+
const ledger = await requireReviewLedger(projectRoot, taskId);
|
|
5777
|
+
if (!ledger.requiredDomains.includes(domain)) {
|
|
5778
|
+
throw new Error(`review domain is not required: ${domain}`);
|
|
5779
|
+
}
|
|
5780
|
+
if (ledger.completedDomains.includes(domain)) {
|
|
5781
|
+
throw new Error(`review domain already completed: ${domain}`);
|
|
5782
|
+
}
|
|
5783
|
+
if (domain === "security" && !ledger.completedDomains.includes("quality")) {
|
|
5784
|
+
throw new Error("quality review must complete first");
|
|
5785
|
+
}
|
|
5786
|
+
assertSafeReportPath(report);
|
|
5787
|
+
await assertReportExists(projectRoot, taskId, report);
|
|
5788
|
+
const uniqueIds = new Set(blockerIds);
|
|
5789
|
+
if (uniqueIds.size !== blockerIds.length) {
|
|
5790
|
+
throw new Error("review blocker ids must be unique");
|
|
5791
|
+
}
|
|
5792
|
+
for (const id of blockerIds) {
|
|
5793
|
+
if (!BLOCKER_ID_PATTERN.test(id)) {
|
|
5794
|
+
throw new Error(`invalid review blocker id: ${id}`);
|
|
5795
|
+
}
|
|
5796
|
+
if (ledger.blockers.some((blocker) => blocker.id === id)) {
|
|
5797
|
+
throw new Error(`duplicate review blocker id: ${id}`);
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
const updated = {
|
|
5801
|
+
...ledger,
|
|
5802
|
+
completedDomains: [...ledger.completedDomains, domain],
|
|
5803
|
+
reports: { ...ledger.reports, [domain]: report },
|
|
5804
|
+
blockers: [
|
|
5805
|
+
...ledger.blockers,
|
|
5806
|
+
...blockerIds.map((id) => ({
|
|
5807
|
+
id,
|
|
5808
|
+
domain,
|
|
5809
|
+
status: "open"
|
|
5810
|
+
}))
|
|
5811
|
+
]
|
|
5812
|
+
};
|
|
5813
|
+
await writeReviewLedger(projectRoot, taskId, updated);
|
|
5814
|
+
return updated;
|
|
5815
|
+
}
|
|
5816
|
+
async function remediateReviewBlockers(projectRoot, taskId, resolvedIds) {
|
|
5817
|
+
const ledger = await requireReviewLedger(projectRoot, taskId);
|
|
5818
|
+
if (ledger.remediationRounds >= 1) {
|
|
5819
|
+
throw new Error("review allows only one remediation round");
|
|
5820
|
+
}
|
|
5821
|
+
if (resolvedIds.length === 0) {
|
|
5822
|
+
throw new Error("review remediation requires resolved blocker ids");
|
|
5823
|
+
}
|
|
5824
|
+
const uniqueIds = new Set(resolvedIds);
|
|
5825
|
+
if (uniqueIds.size !== resolvedIds.length) {
|
|
5826
|
+
throw new Error("resolved review blocker ids must be unique");
|
|
5827
|
+
}
|
|
5828
|
+
for (const id of resolvedIds) {
|
|
5829
|
+
const blocker = ledger.blockers.find((item) => item.id === id);
|
|
5830
|
+
if (!blocker || blocker.status !== "open") {
|
|
5831
|
+
throw new Error(`review blocker is not open: ${id}`);
|
|
5832
|
+
}
|
|
5833
|
+
}
|
|
5834
|
+
const updated = {
|
|
5835
|
+
...ledger,
|
|
5836
|
+
remediationRounds: ledger.remediationRounds + 1,
|
|
5837
|
+
blockers: ledger.blockers.map(
|
|
5838
|
+
(blocker) => uniqueIds.has(blocker.id) ? { ...blocker, status: "resolved" } : blocker
|
|
5839
|
+
)
|
|
5840
|
+
};
|
|
5841
|
+
await writeReviewLedger(projectRoot, taskId, updated);
|
|
5842
|
+
return updated;
|
|
5843
|
+
}
|
|
5844
|
+
async function reviewCanComplete(projectRoot, taskId) {
|
|
5845
|
+
const ledger = await readReviewLedger(projectRoot, taskId);
|
|
5846
|
+
if (!ledger) return false;
|
|
5847
|
+
return ledger.requiredDomains.every(
|
|
5848
|
+
(domain) => ledger.completedDomains.includes(domain)
|
|
5849
|
+
) && ledger.blockers.every((blocker) => blocker.status === "resolved");
|
|
5850
|
+
}
|
|
5851
|
+
async function readReviewLedger(projectRoot, taskId) {
|
|
5852
|
+
if (!TASK_ID_PATTERN.test(taskId)) return null;
|
|
5853
|
+
try {
|
|
5854
|
+
const raw = await readFile9(reviewPath(projectRoot, taskId), "utf-8");
|
|
5855
|
+
const value = JSON.parse(raw);
|
|
5856
|
+
return isReviewLedger(value) ? value : null;
|
|
5857
|
+
} catch {
|
|
5858
|
+
return null;
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
function isReviewLedger(value) {
|
|
5862
|
+
if (!isRecord7(value)) return false;
|
|
5863
|
+
if (value.version !== "1.0" || !isReviewDepth(value.depth) || !isDomainArray(value.requiredDomains) || !isDomainArray(value.completedDomains) || !isRecord7(value.reports) || !Array.isArray(value.blockers) || value.remediationRounds !== 0 && value.remediationRounds !== 1) {
|
|
5864
|
+
return false;
|
|
5865
|
+
}
|
|
5866
|
+
const requiredDomains = value.requiredDomains;
|
|
5867
|
+
const completedDomains = value.completedDomains;
|
|
5868
|
+
const reports = value.reports;
|
|
5869
|
+
const blockers = value.blockers;
|
|
5870
|
+
const expectedDomains = value.depth === "full" ? ["quality", "security"] : requiredDomains;
|
|
5871
|
+
if (value.depth === "targeted" && requiredDomains.length !== 1 || requiredDomains.length !== expectedDomains.length || !expectedDomains.every((domain) => requiredDomains.includes(domain)) || new Set(completedDomains).size !== completedDomains.length || completedDomains.some((domain) => !requiredDomains.includes(domain)) || completedDomains.some((domain) => {
|
|
5872
|
+
const report = reports[domain];
|
|
5873
|
+
return typeof report !== "string" || !isSafeReportPath(report);
|
|
5874
|
+
})) {
|
|
5875
|
+
return false;
|
|
5876
|
+
}
|
|
5877
|
+
const blockerIds = /* @__PURE__ */ new Set();
|
|
5878
|
+
return blockers.every(
|
|
5879
|
+
(blocker) => isRecord7(blocker) && typeof blocker.id === "string" && BLOCKER_ID_PATTERN.test(blocker.id) && !blockerIds.has(blocker.id) && blockerIds.add(blocker.id) && isReviewDomain(blocker.domain) && completedDomains.includes(blocker.domain) && (blocker.status === "open" || blocker.status === "resolved")
|
|
5880
|
+
);
|
|
5881
|
+
}
|
|
5882
|
+
function isDomainArray(value) {
|
|
5883
|
+
return Array.isArray(value) && value.every(isReviewDomain);
|
|
5884
|
+
}
|
|
5885
|
+
function isRecord7(value) {
|
|
5886
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5887
|
+
}
|
|
5888
|
+
async function requireReviewLedger(projectRoot, taskId) {
|
|
5889
|
+
assertValidTaskId(taskId);
|
|
5890
|
+
const ledger = await readReviewLedger(projectRoot, taskId);
|
|
5891
|
+
if (!ledger) throw new Error(`review not initialized: ${taskId}`);
|
|
5892
|
+
return ledger;
|
|
5893
|
+
}
|
|
5894
|
+
async function writeReviewLedger(projectRoot, taskId, ledger) {
|
|
5895
|
+
const dir = path20.join(projectRoot, ".mancode", "workflows", taskId);
|
|
5896
|
+
await mkdir8(dir, { recursive: true });
|
|
5897
|
+
await writeFile10(
|
|
5898
|
+
path20.join(dir, REVIEW_FILE),
|
|
5899
|
+
`${JSON.stringify(ledger, null, 2)}
|
|
5900
|
+
`,
|
|
5901
|
+
"utf-8"
|
|
5902
|
+
);
|
|
5903
|
+
}
|
|
5904
|
+
function reviewPath(projectRoot, taskId) {
|
|
5905
|
+
return path20.join(projectRoot, ".mancode", "workflows", taskId, REVIEW_FILE);
|
|
5906
|
+
}
|
|
5907
|
+
function assertValidTaskId(taskId) {
|
|
5908
|
+
if (!TASK_ID_PATTERN.test(taskId)) {
|
|
5909
|
+
throw new Error(`invalid workflow taskId: ${taskId}`);
|
|
5910
|
+
}
|
|
5911
|
+
}
|
|
5912
|
+
function assertSafeReportPath(report) {
|
|
5913
|
+
if (!isSafeReportPath(report)) {
|
|
5914
|
+
throw new Error(`invalid review report path: ${report}`);
|
|
5915
|
+
}
|
|
5916
|
+
}
|
|
5917
|
+
async function assertReportExists(projectRoot, taskId, report) {
|
|
5918
|
+
const reportPath = path20.join(
|
|
5919
|
+
projectRoot,
|
|
5920
|
+
".mancode",
|
|
5921
|
+
"workflows",
|
|
5922
|
+
taskId,
|
|
5923
|
+
report
|
|
5924
|
+
);
|
|
5925
|
+
try {
|
|
5926
|
+
if ((await stat3(reportPath)).isFile()) return;
|
|
5927
|
+
} catch {
|
|
5928
|
+
}
|
|
5929
|
+
throw new Error(`review report not found: ${report}`);
|
|
5930
|
+
}
|
|
5931
|
+
function isSafeReportPath(report) {
|
|
5932
|
+
const normalized = path20.posix.normalize(report.replaceAll("\\", "/"));
|
|
5933
|
+
return Boolean(
|
|
5934
|
+
report.trim() && !path20.isAbsolute(report) && normalized !== ".." && !normalized.startsWith("../") && normalized.endsWith(".md")
|
|
5935
|
+
);
|
|
5936
|
+
}
|
|
5937
|
+
|
|
5938
|
+
// src/system/workflow.ts
|
|
5690
5939
|
var METADATA_FILE = "metadata.json";
|
|
5691
5940
|
var SLUG_MAX = 30;
|
|
5692
|
-
var
|
|
5941
|
+
var TASK_ID_PATTERN2 = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
5693
5942
|
function isValidWorkflowTaskId(taskId) {
|
|
5694
|
-
return
|
|
5943
|
+
return TASK_ID_PATTERN2.test(taskId);
|
|
5695
5944
|
}
|
|
5696
5945
|
function generateTaskId(task, now = /* @__PURE__ */ new Date()) {
|
|
5697
5946
|
const slug = slugify(task).slice(0, SLUG_MAX) || "task";
|
|
@@ -5704,7 +5953,7 @@ function slugify(text) {
|
|
|
5704
5953
|
}
|
|
5705
5954
|
async function createWorkflow(projectRoot, task, mode, options = {}) {
|
|
5706
5955
|
await validateParentTask(projectRoot, mode, options.parentTaskId, true);
|
|
5707
|
-
await
|
|
5956
|
+
await mkdir9(workflowsRoot(projectRoot), { recursive: true });
|
|
5708
5957
|
const taskId = await allocateTaskId(projectRoot, generateTaskId(task));
|
|
5709
5958
|
const dir = workflowDir(projectRoot, taskId);
|
|
5710
5959
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -5718,7 +5967,7 @@ async function createWorkflow(projectRoot, task, mode, options = {}) {
|
|
|
5718
5967
|
updatedAt: now,
|
|
5719
5968
|
status: "in_progress",
|
|
5720
5969
|
...options.parentTaskId ? { parentTaskId: options.parentTaskId } : {},
|
|
5721
|
-
...mode === "man" || mode === "manteam" ? { planVersion: 1 } : {}
|
|
5970
|
+
...mode === "man" || mode === "manteam" ? { planVersion: 1, reviewPolicyVersion: 1 } : {}
|
|
5722
5971
|
};
|
|
5723
5972
|
await writeMetadata(dir, meta);
|
|
5724
5973
|
return meta;
|
|
@@ -5727,7 +5976,7 @@ async function allocateTaskId(projectRoot, baseTaskId) {
|
|
|
5727
5976
|
for (let attempt = 0; attempt < 1e3; attempt++) {
|
|
5728
5977
|
const taskId = attempt === 0 ? baseTaskId : `${baseTaskId}-${attempt + 1}`;
|
|
5729
5978
|
try {
|
|
5730
|
-
await
|
|
5979
|
+
await mkdir9(workflowDir(projectRoot, taskId));
|
|
5731
5980
|
return taskId;
|
|
5732
5981
|
} catch (err) {
|
|
5733
5982
|
if (isNodeError6(err) && err.code === "EEXIST") {
|
|
@@ -5742,14 +5991,14 @@ async function readWorkflow(projectRoot, taskId) {
|
|
|
5742
5991
|
if (!isValidWorkflowTaskId(taskId)) return null;
|
|
5743
5992
|
const file = metadataPath(projectRoot, taskId);
|
|
5744
5993
|
try {
|
|
5745
|
-
const raw = await
|
|
5994
|
+
const raw = await readFile10(file, "utf-8");
|
|
5746
5995
|
return parseWorkflowMeta(raw, taskId);
|
|
5747
5996
|
} catch {
|
|
5748
5997
|
return null;
|
|
5749
5998
|
}
|
|
5750
5999
|
}
|
|
5751
6000
|
async function updateWorkflow(projectRoot, taskId, patch) {
|
|
5752
|
-
|
|
6001
|
+
assertValidTaskId2(taskId);
|
|
5753
6002
|
const existing = await readWorkflow(projectRoot, taskId);
|
|
5754
6003
|
if (!existing) {
|
|
5755
6004
|
throw new Error(`workflow not found: ${taskId}`);
|
|
@@ -5794,7 +6043,7 @@ async function deleteWorkflow(projectRoot, taskId) {
|
|
|
5794
6043
|
if (!isValidWorkflowTaskId(taskId)) return false;
|
|
5795
6044
|
const dir = workflowDir(projectRoot, taskId);
|
|
5796
6045
|
try {
|
|
5797
|
-
await
|
|
6046
|
+
await stat4(dir);
|
|
5798
6047
|
} catch {
|
|
5799
6048
|
return false;
|
|
5800
6049
|
}
|
|
@@ -5806,19 +6055,19 @@ async function deleteWorkflow(projectRoot, taskId) {
|
|
|
5806
6055
|
return true;
|
|
5807
6056
|
}
|
|
5808
6057
|
function workflowsRoot(projectRoot) {
|
|
5809
|
-
return
|
|
6058
|
+
return path21.join(projectRoot, ".mancode", "workflows");
|
|
5810
6059
|
}
|
|
5811
6060
|
function workflowDir(projectRoot, taskId) {
|
|
5812
|
-
|
|
5813
|
-
return
|
|
6061
|
+
assertValidTaskId2(taskId);
|
|
6062
|
+
return path21.join(workflowsRoot(projectRoot), taskId);
|
|
5814
6063
|
}
|
|
5815
6064
|
function metadataPath(projectRoot, taskId) {
|
|
5816
|
-
return
|
|
6065
|
+
return path21.join(workflowDir(projectRoot, taskId), METADATA_FILE);
|
|
5817
6066
|
}
|
|
5818
6067
|
async function writeMetadata(dir, meta) {
|
|
5819
6068
|
const content = `${JSON.stringify(meta, null, 2)}
|
|
5820
6069
|
`;
|
|
5821
|
-
await
|
|
6070
|
+
await writeFile11(path21.join(dir, METADATA_FILE), content, "utf-8");
|
|
5822
6071
|
}
|
|
5823
6072
|
function parseWorkflowMeta(raw, taskId) {
|
|
5824
6073
|
try {
|
|
@@ -5868,6 +6117,9 @@ async function validateWorkflowMeta(projectRoot, updated, existing) {
|
|
|
5868
6117
|
if (updated.parentTaskId !== existing.parentTaskId) {
|
|
5869
6118
|
throw new Error("workflow parent task cannot be changed");
|
|
5870
6119
|
}
|
|
6120
|
+
if (updated.reviewPolicyVersion !== existing.reviewPolicyVersion) {
|
|
6121
|
+
throw new Error("workflow review policy version cannot be changed");
|
|
6122
|
+
}
|
|
5871
6123
|
if (!Number.isInteger(updated.currentStep) || updated.currentStep < 1 || updated.currentStep > maxWorkflowStep(updated.mode)) {
|
|
5872
6124
|
throw new Error(`invalid workflow step: ${updated.currentStep}`);
|
|
5873
6125
|
}
|
|
@@ -5918,6 +6170,12 @@ async function validateWorkflowMeta(projectRoot, updated, existing) {
|
|
|
5918
6170
|
if (updated.mode === "mamba" && updated.planVersion !== void 0) {
|
|
5919
6171
|
throw new Error("mamba workflows cannot have a plan version");
|
|
5920
6172
|
}
|
|
6173
|
+
if (updated.reviewPolicyVersion !== void 0 && updated.reviewPolicyVersion !== 1) {
|
|
6174
|
+
throw new Error("invalid workflow review policy version");
|
|
6175
|
+
}
|
|
6176
|
+
if (updated.mode === "mamba" && updated.reviewPolicyVersion !== void 0) {
|
|
6177
|
+
throw new Error("mamba workflows cannot have a review policy version");
|
|
6178
|
+
}
|
|
5921
6179
|
if (updated.planVersion !== void 0 && (!Number.isInteger(updated.planVersion) || updated.planVersion < 1)) {
|
|
5922
6180
|
throw new Error("workflow plan version must be a positive integer");
|
|
5923
6181
|
}
|
|
@@ -5931,6 +6189,9 @@ async function validateWorkflowMeta(projectRoot, updated, existing) {
|
|
|
5931
6189
|
if ((isTerminalWorkflowStatus(updated.status) || updated.status === "planned") && (await listActiveMambaChildren(projectRoot, updated.taskId)).length > 0) {
|
|
5932
6190
|
throw new Error("cannot finish workflow with an active mamba child");
|
|
5933
6191
|
}
|
|
6192
|
+
if (updated.status === "completed" && updated.reviewPolicyVersion === 1 && !reviewWasExplicitlySkipped(updated) && !await reviewCanComplete(projectRoot, updated.taskId)) {
|
|
6193
|
+
throw new Error("workflow review is incomplete or still has open blockers");
|
|
6194
|
+
}
|
|
5934
6195
|
}
|
|
5935
6196
|
function canTransition(from, to) {
|
|
5936
6197
|
if (from === to) return true;
|
|
@@ -5958,13 +6219,16 @@ function isWorkflowShape(obj) {
|
|
|
5958
6219
|
return false;
|
|
5959
6220
|
if (obj.planVersion !== void 0 && (!Number.isInteger(obj.planVersion) || obj.planVersion < 1))
|
|
5960
6221
|
return false;
|
|
6222
|
+
if (obj.reviewPolicyVersion !== void 0 && obj.reviewPolicyVersion !== 1)
|
|
6223
|
+
return false;
|
|
5961
6224
|
if (status2 === "blocked") {
|
|
5962
6225
|
if (!obj.blockingReason?.trim()) return false;
|
|
5963
6226
|
} else if (obj.blockingReason !== void 0) {
|
|
5964
6227
|
return false;
|
|
5965
6228
|
}
|
|
5966
6229
|
if (mode === "mamba") {
|
|
5967
|
-
if (status2 === "planned" || obj.planVersion !== void 0
|
|
6230
|
+
if (status2 === "planned" || obj.planVersion !== void 0 || obj.reviewPolicyVersion !== void 0)
|
|
6231
|
+
return false;
|
|
5968
6232
|
if (obj.parentTaskId !== void 0 && !isValidWorkflowTaskId(obj.parentTaskId))
|
|
5969
6233
|
return false;
|
|
5970
6234
|
if (obj.outcome !== void 0 && status2 !== "completed") return false;
|
|
@@ -5974,6 +6238,9 @@ function isWorkflowShape(obj) {
|
|
|
5974
6238
|
}
|
|
5975
6239
|
return true;
|
|
5976
6240
|
}
|
|
6241
|
+
function reviewWasExplicitlySkipped(meta) {
|
|
6242
|
+
return meta.skippedSteps.includes("review") || meta.skippedSteps.includes("film-1") && meta.skippedSteps.includes("film-2");
|
|
6243
|
+
}
|
|
5977
6244
|
async function listActiveMambaChildren(projectRoot, parentTaskId) {
|
|
5978
6245
|
return (await listMambaChildren(projectRoot, parentTaskId)).filter(
|
|
5979
6246
|
(workflow2) => workflow2.status === "in_progress" || workflow2.status === "blocked"
|
|
@@ -6009,7 +6276,7 @@ async function propagateChildStatus(projectRoot, child) {
|
|
|
6009
6276
|
function isNodeError6(err) {
|
|
6010
6277
|
return err instanceof Error && "code" in err;
|
|
6011
6278
|
}
|
|
6012
|
-
function
|
|
6279
|
+
function assertValidTaskId2(taskId) {
|
|
6013
6280
|
if (!isValidWorkflowTaskId(taskId)) {
|
|
6014
6281
|
throw new Error(`invalid workflow taskId: ${taskId}`);
|
|
6015
6282
|
}
|
|
@@ -6021,7 +6288,7 @@ var EXIT_OK6 = 0;
|
|
|
6021
6288
|
var EXIT_NOT_INITIALIZED4 = 1;
|
|
6022
6289
|
var EXIT_CORRUPT_STATE = 2;
|
|
6023
6290
|
async function status(rootDir = process7.cwd(), options = {}) {
|
|
6024
|
-
const stateFile =
|
|
6291
|
+
const stateFile = path22.join(rootDir, ".mancode", "state.json");
|
|
6025
6292
|
if (!await pathExists9(stateFile)) {
|
|
6026
6293
|
console.error("\u2717 mancode not initialized.");
|
|
6027
6294
|
console.error(" Run `mancode init` to get started.");
|
|
@@ -6120,19 +6387,19 @@ async function getCurrentWorkflow(rootDir, taskId) {
|
|
|
6120
6387
|
}
|
|
6121
6388
|
async function getProjectName(rootDir) {
|
|
6122
6389
|
try {
|
|
6123
|
-
const raw = await fs7.readFile(
|
|
6390
|
+
const raw = await fs7.readFile(path22.join(rootDir, "package.json"), "utf-8");
|
|
6124
6391
|
const pkg = JSON.parse(raw);
|
|
6125
6392
|
if (pkg.name && typeof pkg.name === "string") {
|
|
6126
6393
|
return pkg.name;
|
|
6127
6394
|
}
|
|
6128
6395
|
} catch {
|
|
6129
6396
|
}
|
|
6130
|
-
return
|
|
6397
|
+
return path22.basename(rootDir);
|
|
6131
6398
|
}
|
|
6132
6399
|
async function readConfig2(rootDir) {
|
|
6133
6400
|
try {
|
|
6134
6401
|
const raw = await fs7.readFile(
|
|
6135
|
-
|
|
6402
|
+
path22.join(rootDir, ".mancode", "config.json"),
|
|
6136
6403
|
"utf-8"
|
|
6137
6404
|
);
|
|
6138
6405
|
return JSON.parse(raw);
|
|
@@ -6159,9 +6426,9 @@ function getEffectiveTeamStatus(state, config, detected) {
|
|
|
6159
6426
|
}
|
|
6160
6427
|
async function checkHooks(rootDir) {
|
|
6161
6428
|
const [sessionStart, userPromptSubmit, registered] = await Promise.all([
|
|
6162
|
-
pathExists9(
|
|
6429
|
+
pathExists9(path22.join(rootDir, ".mancode", "hooks", "session-start.sh")),
|
|
6163
6430
|
pathExists9(
|
|
6164
|
-
|
|
6431
|
+
path22.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
|
|
6165
6432
|
),
|
|
6166
6433
|
isRegistered(rootDir)
|
|
6167
6434
|
]);
|
|
@@ -6170,7 +6437,7 @@ async function checkHooks(rootDir) {
|
|
|
6170
6437
|
async function isRegistered(rootDir) {
|
|
6171
6438
|
try {
|
|
6172
6439
|
const raw = await fs7.readFile(
|
|
6173
|
-
|
|
6440
|
+
path22.join(rootDir, ".claude", "settings.json"),
|
|
6174
6441
|
"utf-8"
|
|
6175
6442
|
);
|
|
6176
6443
|
const settings = JSON.parse(raw);
|
|
@@ -6180,9 +6447,9 @@ async function isRegistered(rootDir) {
|
|
|
6180
6447
|
}
|
|
6181
6448
|
}
|
|
6182
6449
|
function hooksRegistered2(settings) {
|
|
6183
|
-
if (!
|
|
6450
|
+
if (!isRecord8(settings)) return false;
|
|
6184
6451
|
const hooks = settings.hooks;
|
|
6185
|
-
if (!
|
|
6452
|
+
if (!isRecord8(hooks)) return false;
|
|
6186
6453
|
return hasHookCommand2(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand2(
|
|
6187
6454
|
hooks.UserPromptSubmit,
|
|
6188
6455
|
".mancode/hooks/user-prompt-submit.sh"
|
|
@@ -6191,15 +6458,15 @@ function hooksRegistered2(settings) {
|
|
|
6191
6458
|
function hasHookCommand2(value, needle) {
|
|
6192
6459
|
if (!Array.isArray(value)) return false;
|
|
6193
6460
|
return value.some((group) => {
|
|
6194
|
-
if (!
|
|
6461
|
+
if (!isRecord8(group) || !Array.isArray(group.hooks)) return false;
|
|
6195
6462
|
return group.hooks.some((hook) => {
|
|
6196
|
-
if (!
|
|
6463
|
+
if (!isRecord8(hook) || typeof hook.command !== "string") return false;
|
|
6197
6464
|
return hook.command.includes(needle);
|
|
6198
6465
|
});
|
|
6199
6466
|
});
|
|
6200
6467
|
}
|
|
6201
6468
|
async function estimateHookInjection(rootDir) {
|
|
6202
|
-
const hookPath =
|
|
6469
|
+
const hookPath = path22.join(
|
|
6203
6470
|
rootDir,
|
|
6204
6471
|
".mancode",
|
|
6205
6472
|
"hooks",
|
|
@@ -6325,7 +6592,7 @@ function formatTeamStatus(team) {
|
|
|
6325
6592
|
function workflowStepMax(mode) {
|
|
6326
6593
|
return maxWorkflowStep(mode);
|
|
6327
6594
|
}
|
|
6328
|
-
function
|
|
6595
|
+
function isRecord8(value) {
|
|
6329
6596
|
return typeof value === "object" && value !== null;
|
|
6330
6597
|
}
|
|
6331
6598
|
async function pathExists9(p) {
|
|
@@ -6338,14 +6605,14 @@ async function pathExists9(p) {
|
|
|
6338
6605
|
}
|
|
6339
6606
|
|
|
6340
6607
|
// src/commands/uninstall.ts
|
|
6341
|
-
import { access as access4, readFile as
|
|
6342
|
-
import
|
|
6608
|
+
import { access as access4, readFile as readFile11, rm as rm5, writeFile as writeFile12 } from "fs/promises";
|
|
6609
|
+
import path23 from "path";
|
|
6343
6610
|
import process8 from "process";
|
|
6344
6611
|
var EXIT_OK7 = 0;
|
|
6345
6612
|
var EXIT_NOT_INITIALIZED5 = 1;
|
|
6346
6613
|
var EXIT_UNSUPPORTED_PLATFORM2 = 2;
|
|
6347
6614
|
async function uninstall(rootDir = process8.cwd(), platform, options = {}) {
|
|
6348
|
-
const stateFile =
|
|
6615
|
+
const stateFile = path23.join(rootDir, ".mancode", "state.json");
|
|
6349
6616
|
if (!await pathExists10(stateFile)) {
|
|
6350
6617
|
console.error("\u2717 mancode not initialized.");
|
|
6351
6618
|
console.error(" Run `mancode init` first.");
|
|
@@ -6397,7 +6664,7 @@ async function uninstallAll(rootDir) {
|
|
|
6397
6664
|
await uninstallPlatform(rootDir, p);
|
|
6398
6665
|
}
|
|
6399
6666
|
console.log("\u2713 Removing .mancode/ directory...");
|
|
6400
|
-
await rm5(
|
|
6667
|
+
await rm5(path23.join(rootDir, ".mancode"), {
|
|
6401
6668
|
recursive: true,
|
|
6402
6669
|
force: true
|
|
6403
6670
|
});
|
|
@@ -6407,10 +6674,10 @@ async function uninstallClaudeCode(rootDir) {
|
|
|
6407
6674
|
await cleanClaudeSettings(rootDir);
|
|
6408
6675
|
}
|
|
6409
6676
|
async function cleanClaudeSettings(rootDir) {
|
|
6410
|
-
const settingsPath =
|
|
6677
|
+
const settingsPath = path23.join(rootDir, ".claude", "settings.json");
|
|
6411
6678
|
let content;
|
|
6412
6679
|
try {
|
|
6413
|
-
content = await
|
|
6680
|
+
content = await readFile11(settingsPath, "utf-8");
|
|
6414
6681
|
} catch {
|
|
6415
6682
|
return;
|
|
6416
6683
|
}
|
|
@@ -6421,14 +6688,14 @@ async function cleanClaudeSettings(rootDir) {
|
|
|
6421
6688
|
return;
|
|
6422
6689
|
}
|
|
6423
6690
|
const cleanedHooks = {};
|
|
6424
|
-
if (
|
|
6691
|
+
if (isRecord9(settings.hooks)) {
|
|
6425
6692
|
for (const [event, value] of Object.entries(settings.hooks)) {
|
|
6426
6693
|
const cleaned = cleanClaudeHookValue(value);
|
|
6427
6694
|
if (cleaned !== void 0) cleanedHooks[event] = cleaned;
|
|
6428
6695
|
}
|
|
6429
6696
|
settings.hooks = Object.keys(cleanedHooks).length > 0 ? cleanedHooks : void 0;
|
|
6430
6697
|
}
|
|
6431
|
-
if (
|
|
6698
|
+
if (isRecord9(settings.skills)) {
|
|
6432
6699
|
const retainedSkills = Object.fromEntries(
|
|
6433
6700
|
Object.entries(settings.skills).filter(
|
|
6434
6701
|
([name, value]) => LEGACY_CLAUDE_SKILL_SETTINGS[name] !== value
|
|
@@ -6436,7 +6703,7 @@ async function cleanClaudeSettings(rootDir) {
|
|
|
6436
6703
|
);
|
|
6437
6704
|
settings.skills = Object.keys(retainedSkills).length > 0 ? retainedSkills : void 0;
|
|
6438
6705
|
}
|
|
6439
|
-
await
|
|
6706
|
+
await writeFile12(
|
|
6440
6707
|
settingsPath,
|
|
6441
6708
|
`${JSON.stringify(settings, null, 2)}
|
|
6442
6709
|
`,
|
|
@@ -6448,12 +6715,13 @@ function cleanClaudeHookValue(value) {
|
|
|
6448
6715
|
const entries2 = value.flatMap(cleanClaudeHookEntry);
|
|
6449
6716
|
return entries2.length > 0 ? entries2 : void 0;
|
|
6450
6717
|
}
|
|
6451
|
-
if (!
|
|
6718
|
+
if (!isRecord9(value)) return value;
|
|
6452
6719
|
if (Array.isArray(value.hooks) || typeof value.command === "string") {
|
|
6453
6720
|
const entries2 = cleanClaudeHookEntry(value);
|
|
6454
6721
|
return entries2.length > 0 ? entries2 : void 0;
|
|
6455
6722
|
}
|
|
6456
6723
|
const entries = Object.entries(value).flatMap(([key, item]) => {
|
|
6724
|
+
if (key === "mancode" && containsLegacyMancodeHookPath2(item)) return [];
|
|
6457
6725
|
const cleaned = cleanClaudeHookValue(item);
|
|
6458
6726
|
return cleaned === void 0 ? [] : [[key, cleaned]];
|
|
6459
6727
|
});
|
|
@@ -6461,7 +6729,7 @@ function cleanClaudeHookValue(value) {
|
|
|
6461
6729
|
}
|
|
6462
6730
|
function cleanClaudeHookEntry(entry) {
|
|
6463
6731
|
if (Array.isArray(entry)) return entry.flatMap(cleanClaudeHookEntry);
|
|
6464
|
-
if (!
|
|
6732
|
+
if (!isRecord9(entry)) return [entry];
|
|
6465
6733
|
if (Array.isArray(entry.hooks)) {
|
|
6466
6734
|
const hooks = entry.hooks.filter((hook) => !isMancodeHookEntry(hook));
|
|
6467
6735
|
return hooks.length > 0 ? [{ ...entry, hooks }] : [];
|
|
@@ -6469,19 +6737,27 @@ function cleanClaudeHookEntry(entry) {
|
|
|
6469
6737
|
return isMancodeHookEntry(entry) ? [] : [entry];
|
|
6470
6738
|
}
|
|
6471
6739
|
function isMancodeHookEntry(value) {
|
|
6472
|
-
return
|
|
6740
|
+
return isRecord9(value) && typeof value.command === "string" && isGeneratedMancodeHookCommand(value.command);
|
|
6741
|
+
}
|
|
6742
|
+
function containsLegacyMancodeHookPath2(value) {
|
|
6743
|
+
if (Array.isArray(value)) return value.some(containsLegacyMancodeHookPath2);
|
|
6744
|
+
if (!isRecord9(value)) return false;
|
|
6745
|
+
if (typeof value.command === "string" && value.command.includes(".mancode/hooks/")) {
|
|
6746
|
+
return true;
|
|
6747
|
+
}
|
|
6748
|
+
return Object.values(value).some(containsLegacyMancodeHookPath2);
|
|
6473
6749
|
}
|
|
6474
6750
|
async function uninstallCursor(rootDir) {
|
|
6475
6751
|
await removeCursorGeneratedRules(rootDir);
|
|
6476
6752
|
await removeCursorCommands(rootDir);
|
|
6477
6753
|
}
|
|
6478
6754
|
async function uninstallCodex(rootDir) {
|
|
6479
|
-
const agentsPath =
|
|
6755
|
+
const agentsPath = path23.join(rootDir, "AGENTS.md");
|
|
6480
6756
|
try {
|
|
6481
|
-
const content = await
|
|
6757
|
+
const content = await readFile11(agentsPath, "utf-8");
|
|
6482
6758
|
const cleaned = removeManagedBlock(content);
|
|
6483
6759
|
if (cleaned.trim()) {
|
|
6484
|
-
await
|
|
6760
|
+
await writeFile12(agentsPath, `${cleaned}
|
|
6485
6761
|
`, "utf-8");
|
|
6486
6762
|
} else {
|
|
6487
6763
|
await rm5(agentsPath, { force: true });
|
|
@@ -6491,16 +6767,16 @@ async function uninstallCodex(rootDir) {
|
|
|
6491
6767
|
await removeCodexSkills(rootDir);
|
|
6492
6768
|
}
|
|
6493
6769
|
async function uninstallCopilot(rootDir) {
|
|
6494
|
-
const instructionsPath =
|
|
6770
|
+
const instructionsPath = path23.join(
|
|
6495
6771
|
rootDir,
|
|
6496
6772
|
".github",
|
|
6497
6773
|
"copilot-instructions.md"
|
|
6498
6774
|
);
|
|
6499
6775
|
try {
|
|
6500
|
-
const content = await
|
|
6776
|
+
const content = await readFile11(instructionsPath, "utf-8");
|
|
6501
6777
|
const cleaned = removeManagedBlock(content);
|
|
6502
6778
|
if (cleaned.trim()) {
|
|
6503
|
-
await
|
|
6779
|
+
await writeFile12(instructionsPath, `${cleaned}
|
|
6504
6780
|
`, "utf-8");
|
|
6505
6781
|
} else {
|
|
6506
6782
|
await rm5(instructionsPath, { force: true });
|
|
@@ -6510,16 +6786,16 @@ async function uninstallCopilot(rootDir) {
|
|
|
6510
6786
|
await removeCopilotPrompts(rootDir);
|
|
6511
6787
|
}
|
|
6512
6788
|
async function uninstallZcode(rootDir) {
|
|
6513
|
-
const agentsPath =
|
|
6789
|
+
const agentsPath = path23.join(rootDir, "AGENTS.md");
|
|
6514
6790
|
try {
|
|
6515
|
-
const content = await
|
|
6791
|
+
const content = await readFile11(agentsPath, "utf-8");
|
|
6516
6792
|
const cleaned = removeManagedBlock(
|
|
6517
6793
|
content,
|
|
6518
6794
|
ZCODE_MANCODE_START_MARKER,
|
|
6519
6795
|
ZCODE_MANCODE_END_MARKER
|
|
6520
6796
|
);
|
|
6521
6797
|
if (cleaned.trim()) {
|
|
6522
|
-
await
|
|
6798
|
+
await writeFile12(agentsPath, `${cleaned}
|
|
6523
6799
|
`, "utf-8");
|
|
6524
6800
|
} else {
|
|
6525
6801
|
await rm5(agentsPath, { force: true });
|
|
@@ -6529,13 +6805,13 @@ async function uninstallZcode(rootDir) {
|
|
|
6529
6805
|
await removeZcodeSkills(rootDir);
|
|
6530
6806
|
}
|
|
6531
6807
|
async function removeFromConfig(rootDir, platform) {
|
|
6532
|
-
const configPath =
|
|
6808
|
+
const configPath = path23.join(rootDir, ".mancode", "config.json");
|
|
6533
6809
|
try {
|
|
6534
|
-
const raw = await
|
|
6810
|
+
const raw = await readFile11(configPath, "utf-8");
|
|
6535
6811
|
const config = JSON.parse(raw);
|
|
6536
6812
|
if (Array.isArray(config.platforms)) {
|
|
6537
6813
|
config.platforms = config.platforms.filter((p) => p !== platform);
|
|
6538
|
-
if (
|
|
6814
|
+
if (isRecord9(config.platformOptions)) {
|
|
6539
6815
|
const platformOptions = Object.fromEntries(
|
|
6540
6816
|
Object.entries(config.platformOptions).filter(
|
|
6541
6817
|
([name]) => name !== platform
|
|
@@ -6543,7 +6819,7 @@ async function removeFromConfig(rootDir, platform) {
|
|
|
6543
6819
|
);
|
|
6544
6820
|
config.platformOptions = Object.keys(platformOptions).length > 0 ? platformOptions : void 0;
|
|
6545
6821
|
}
|
|
6546
|
-
await
|
|
6822
|
+
await writeFile12(
|
|
6547
6823
|
configPath,
|
|
6548
6824
|
`${JSON.stringify(config, null, 2)}
|
|
6549
6825
|
`,
|
|
@@ -6553,7 +6829,7 @@ async function removeFromConfig(rootDir, platform) {
|
|
|
6553
6829
|
} catch {
|
|
6554
6830
|
}
|
|
6555
6831
|
}
|
|
6556
|
-
function
|
|
6832
|
+
function isRecord9(value) {
|
|
6557
6833
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6558
6834
|
}
|
|
6559
6835
|
async function pathExists10(p) {
|
|
@@ -6577,12 +6853,12 @@ function version() {
|
|
|
6577
6853
|
|
|
6578
6854
|
// src/commands/workflow.ts
|
|
6579
6855
|
import { access as access5 } from "fs/promises";
|
|
6580
|
-
import
|
|
6856
|
+
import path24 from "path";
|
|
6581
6857
|
var EXIT_OK8 = 0;
|
|
6582
6858
|
var EXIT_NOT_INITIALIZED6 = 1;
|
|
6583
6859
|
var EXIT_INVALID_ARG2 = 2;
|
|
6584
6860
|
async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
6585
|
-
if (!await pathExists11(
|
|
6861
|
+
if (!await pathExists11(path24.join(rootDir, ".mancode", "state.json"))) {
|
|
6586
6862
|
if (options.json) {
|
|
6587
6863
|
console.log(JSON.stringify({ error: "not initialized" }, null, 2));
|
|
6588
6864
|
} else {
|
|
@@ -6600,6 +6876,8 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
|
6600
6876
|
return workflowList(rootDir, options);
|
|
6601
6877
|
case "show":
|
|
6602
6878
|
return workflowShow(rootDir, args[0], options);
|
|
6879
|
+
case "review":
|
|
6880
|
+
return workflowReview(rootDir, args, options);
|
|
6603
6881
|
case "clean":
|
|
6604
6882
|
return workflowClean(rootDir, options);
|
|
6605
6883
|
default:
|
|
@@ -6614,12 +6892,109 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
|
6614
6892
|
} else {
|
|
6615
6893
|
console.error(`\u2717 Invalid workflow subcommand: ${subcommand}`);
|
|
6616
6894
|
console.error(
|
|
6617
|
-
" Use: create <man|mamba|manteam> <task> | update <taskId> | list | show <taskId> | clean"
|
|
6895
|
+
" Use: create <man|mamba|manteam> <task> | update <taskId> | review <taskId> <init|complete|remediate|show> | list | show <taskId> | clean"
|
|
6618
6896
|
);
|
|
6619
6897
|
}
|
|
6620
6898
|
return EXIT_INVALID_ARG2;
|
|
6621
6899
|
}
|
|
6622
6900
|
}
|
|
6901
|
+
async function workflowReview(rootDir, args, options) {
|
|
6902
|
+
const taskId = args[0];
|
|
6903
|
+
const action = args[1];
|
|
6904
|
+
if (!taskId || !isValidWorkflowTaskId(taskId)) {
|
|
6905
|
+
return invalidArg(options, `invalid taskId: ${taskId ?? ""}`);
|
|
6906
|
+
}
|
|
6907
|
+
const meta = await readWorkflow(rootDir, taskId);
|
|
6908
|
+
if (!meta) return invalidArg(options, `workflow not found: ${taskId}`);
|
|
6909
|
+
if (meta.mode !== "man" && meta.mode !== "manteam") {
|
|
6910
|
+
return invalidArg(options, "review is only valid for man or manteam");
|
|
6911
|
+
}
|
|
6912
|
+
if (action === "show") {
|
|
6913
|
+
const ledger = await readReviewLedger(rootDir, taskId);
|
|
6914
|
+
if (!ledger)
|
|
6915
|
+
return invalidArg(options, `review not initialized: ${taskId}`);
|
|
6916
|
+
outputReviewLedger(ledger, options);
|
|
6917
|
+
return EXIT_OK8;
|
|
6918
|
+
}
|
|
6919
|
+
if (meta.status !== "in_progress" || meta.currentStep < 6) {
|
|
6920
|
+
return invalidArg(
|
|
6921
|
+
options,
|
|
6922
|
+
"review requires an in_progress workflow at step 6 or later"
|
|
6923
|
+
);
|
|
6924
|
+
}
|
|
6925
|
+
try {
|
|
6926
|
+
let ledger;
|
|
6927
|
+
if (action === "init") {
|
|
6928
|
+
if (!isReviewDepth(options.reviewDepth)) {
|
|
6929
|
+
return invalidArg(
|
|
6930
|
+
options,
|
|
6931
|
+
`invalid --review-depth: ${options.reviewDepth ?? ""}`
|
|
6932
|
+
);
|
|
6933
|
+
}
|
|
6934
|
+
if (options.reviewDomain !== void 0 && !isReviewDomain(options.reviewDomain)) {
|
|
6935
|
+
return invalidArg(
|
|
6936
|
+
options,
|
|
6937
|
+
`invalid --review-domain: ${options.reviewDomain}`
|
|
6938
|
+
);
|
|
6939
|
+
}
|
|
6940
|
+
ledger = await initializeReview(
|
|
6941
|
+
rootDir,
|
|
6942
|
+
taskId,
|
|
6943
|
+
options.reviewDepth,
|
|
6944
|
+
isReviewDomain(options.reviewDomain) ? options.reviewDomain : void 0
|
|
6945
|
+
);
|
|
6946
|
+
} else if (action === "complete") {
|
|
6947
|
+
if (!isReviewDomain(options.reviewDomain) || !options.report) {
|
|
6948
|
+
return invalidArg(
|
|
6949
|
+
options,
|
|
6950
|
+
"review complete requires --review-domain and --report"
|
|
6951
|
+
);
|
|
6952
|
+
}
|
|
6953
|
+
ledger = await completeReviewDomain(
|
|
6954
|
+
rootDir,
|
|
6955
|
+
taskId,
|
|
6956
|
+
options.reviewDomain,
|
|
6957
|
+
options.report,
|
|
6958
|
+
parseCsv(options.blockers)
|
|
6959
|
+
);
|
|
6960
|
+
} else if (action === "remediate") {
|
|
6961
|
+
ledger = await remediateReviewBlockers(
|
|
6962
|
+
rootDir,
|
|
6963
|
+
taskId,
|
|
6964
|
+
parseCsv(options.resolved)
|
|
6965
|
+
);
|
|
6966
|
+
} else {
|
|
6967
|
+
return invalidArg(
|
|
6968
|
+
options,
|
|
6969
|
+
`invalid review action: ${action ?? ""}`,
|
|
6970
|
+
"Use: mancode workflow review <taskId> <init|complete|remediate|show>"
|
|
6971
|
+
);
|
|
6972
|
+
}
|
|
6973
|
+
outputReviewLedger(ledger, options);
|
|
6974
|
+
return EXIT_OK8;
|
|
6975
|
+
} catch (error) {
|
|
6976
|
+
return invalidArg(
|
|
6977
|
+
options,
|
|
6978
|
+
error instanceof Error ? error.message : "unable to update review"
|
|
6979
|
+
);
|
|
6980
|
+
}
|
|
6981
|
+
}
|
|
6982
|
+
function outputReviewLedger(ledger, options) {
|
|
6983
|
+
if (options.json) {
|
|
6984
|
+
console.log(JSON.stringify(ledger, null, 2));
|
|
6985
|
+
return;
|
|
6986
|
+
}
|
|
6987
|
+
if (!ledger) return;
|
|
6988
|
+
const openBlockers = ledger.blockers.filter(
|
|
6989
|
+
(blocker) => blocker.status === "open"
|
|
6990
|
+
);
|
|
6991
|
+
console.log(
|
|
6992
|
+
`Review: ${ledger.depth}; domains ${ledger.completedDomains.length}/${ledger.requiredDomains.length}; open blockers ${openBlockers.length}; remediation ${ledger.remediationRounds}/1`
|
|
6993
|
+
);
|
|
6994
|
+
}
|
|
6995
|
+
function parseCsv(value) {
|
|
6996
|
+
return (value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
6997
|
+
}
|
|
6623
6998
|
async function workflowCreate(rootDir, args, options) {
|
|
6624
6999
|
const mode = args[0];
|
|
6625
7000
|
const task = args.slice(1).join(" ").trim();
|
|
@@ -6855,12 +7230,20 @@ async function workflowClean(rootDir, options) {
|
|
|
6855
7230
|
}
|
|
6856
7231
|
return EXIT_INVALID_ARG2;
|
|
6857
7232
|
}
|
|
6858
|
-
const
|
|
7233
|
+
const eligible = workflows.filter((meta) => {
|
|
6859
7234
|
if (!isTerminalWorkflowStatus(meta.status)) return false;
|
|
6860
7235
|
if (!cutoff) return true;
|
|
6861
7236
|
const started = Date.parse(meta.startedAt);
|
|
6862
7237
|
return Number.isFinite(started) && started < cutoff.getTime();
|
|
6863
7238
|
});
|
|
7239
|
+
const eligibleIds = new Set(eligible.map((meta) => meta.taskId));
|
|
7240
|
+
const candidates = eligible.filter((meta) => {
|
|
7241
|
+
if (meta.mode !== "man" && meta.mode !== "manteam") return true;
|
|
7242
|
+
const children = workflows.filter(
|
|
7243
|
+
(candidate) => candidate.mode === "mamba" && candidate.parentTaskId === meta.taskId
|
|
7244
|
+
);
|
|
7245
|
+
return children.every((child) => eligibleIds.has(child.taskId));
|
|
7246
|
+
}).sort((a, b) => Number(a.mode !== "mamba") - Number(b.mode !== "mamba"));
|
|
6864
7247
|
const removed = [];
|
|
6865
7248
|
if (!options.dryRun) {
|
|
6866
7249
|
for (const meta of candidates) {
|
|
@@ -6965,7 +7348,7 @@ async function pathExists11(p) {
|
|
|
6965
7348
|
|
|
6966
7349
|
// src/cli.ts
|
|
6967
7350
|
program.name("mancode").description(
|
|
6968
|
-
"AI coding agent harness. Modes: solo,
|
|
7351
|
+
"AI coding agent harness. Modes: solo, man, mamba, manteam, manps."
|
|
6969
7352
|
).version(VERSION);
|
|
6970
7353
|
program.command("init").description("Initialize mancode in the current project").option("--force", "Reinstall even if already initialized").option("--yes", "Skip all confirmations (CI mode)").option("--team", "Force enable team mode (MVP-2)").option("--no-team", "Force disable team mode (MVP-2)").option("--style <name>", "Specify aesthetic style (MVP-2)").option("--platform <platform>", "Initial adapter platform (MVP-3)").action(async (options) => {
|
|
6971
7354
|
const code = await init(process.cwd(), options);
|
|
@@ -6996,7 +7379,13 @@ program.command("uninstall [platform]").description("Remove platform adapter or
|
|
|
6996
7379
|
program.command("workflow <subcommand> [args...]").description("Manage mancode workflows").option("--dry-run", "Preview clean without deleting").option("--older-than <duration>", "Clean workflows older than (e.g. 30d)").option("--step <n>", "Update workflow current step").option("--status <status>", "Update workflow status").option(
|
|
6997
7380
|
"--parent-task <taskId>",
|
|
6998
7381
|
"Parent /man or /manteam workflow for mamba"
|
|
6999
|
-
).option("--blocking-reason <reason>", "Explain why a workflow is blocked").option("--outcome <outcome>", "Set mamba outcome").option("--plan-version <n>", "Set the next man/manteam plan revision").option("--skipped <steps>", "Update skipped steps as comma-separated values").option("--
|
|
7382
|
+
).option("--blocking-reason <reason>", "Explain why a workflow is blocked").option("--outcome <outcome>", "Set mamba outcome").option("--plan-version <n>", "Set the next man/manteam plan revision").option("--skipped <steps>", "Update skipped steps as comma-separated values").option("--review-depth <depth>", "Review depth: targeted or full").option("--review-domain <domain>", "Review domain: quality or security").option(
|
|
7383
|
+
"--report <path>",
|
|
7384
|
+
"Relative Markdown report path for a review domain"
|
|
7385
|
+
).option("--blockers <ids>", "Comma-separated blocker ids found by a review").option(
|
|
7386
|
+
"--resolved <ids>",
|
|
7387
|
+
"Comma-separated blocker ids resolved in remediation"
|
|
7388
|
+
).option("--json", "Output as JSON (for scripts)").action(async (subcommand, args, options) => {
|
|
7000
7389
|
const code = await workflow(process.cwd(), subcommand, args ?? [], options);
|
|
7001
7390
|
process.exitCode = code;
|
|
7002
7391
|
});
|