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/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\u53CD\u9988\u6307\u51FA\u7684\u95EE\u9898
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 Film #2 \u7684\u5168\u90E8 \u{1F534} \u95EE\u9898\uFF0C\u518D\u91CD\u8DD1 build/lint/typecheck/test \u548C\u5FC5\u8981 smoke test\u3002
304
- - \u751F\u6210 summary\uFF1A\u6539\u52A8/\u65B0\u5EFA\u6587\u4EF6\u3001\u590D\u7528\u8D44\u6E90\u3001\u9A8C\u8BC1\u7ED3\u679C\u3001\u53CC\u5BA1\u95EE\u9898\u5904\u7F6E\u3001\u8DF3\u8FC7\u6B65\u9AA4\u548C\u6B8B\u4F59\u98CE\u9669\u3002
305
- - \u53EA\u6709\u9A8C\u8BC1\u901A\u8FC7\u4E14 \u{1F534} \u6E05\u96F6\u624D\u5EFA\u8BAE \`completed\`\uFF1B\u5426\u5219\u5199 \`blocked\` \u4E0E\u660E\u786E blockingReason\u3002
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\u7684\u53EF\u8BFB\u6027 / DRY / \u590D\u6742\u5EA6\u95EE\u9898\uFF0C**\u4E0D\u8981\u518D\u8BF4**\u3002
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
- UI \u8F93\u51FA\u524D\u81EA\u68C0\uFF1A
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. \u8FD0\u884C build\uFF08\u5982\u6709\uFF09
768
- 2. \u8FD0\u884C lint\uFF08\u5982\u6709\uFF09
769
- 3. \u8FD0\u884C test\uFF08\u5982\u6709\uFF09
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\u81EA\u5BA1\u4E0E\u6A21\u5F0F\u63A8\u8350
773
- - \u9A8C\u8BC1\u7C7B\u95EE\u9898\uFF08build/lint/test \u5931\u8D25\u3001\u9057\u7559 debug\uFF09\u76F4\u63A5\u4FEE\u590D\u5E76\u590D\u9A8C\u3002
774
- - \u98CE\u683C\u7C7B\u95EE\u9898\uFF08\u547D\u540D\u3001\u53EF\u8BFB\u6027\u3001DRY\uFF09\u53EA\u63D0\u793A\uFF0C\u4E0D\u4E3A\u6B64\u6269\u5927\u65E0\u5173\u6539\u52A8\u3002
775
- - \u52A8\u624B\u524D\u82E5\u6D89\u53CA 3 \u4E2A\u4EE5\u4E0A\u6587\u4EF6\u3001\u65B0\u6A21\u5757\u6216\u67B6\u6784\u53D8\u52A8\uFF0C\u7528\u4E00\u53E5\u8BDD\u5EFA\u8BAE\u7528\u6237\u5148\u7528 \`/man\`\uFF1B\u7528\u6237\u8BF4\u201C\u7EE7\u7EED solo\u201D\u5373\u53EF\u8DF3\u8FC7\u3002
776
- - \u81EA\u5BA1\u53D1\u73B0 3 \u4E2A\u4EE5\u4E0A\u95EE\u9898\u65F6\uFF0C\u5EFA\u8BAE \`/manps\` \u6216 \`/man\`\u3002
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\` \u5BA1\u67E5\u8D28\u91CF\u3001\u590D\u7528\u3001\u590D\u6742\u5EA6\u548C\u6D4B\u8BD5\uFF0C\u5199 \`film-report-1.md\`\uFF1BHead Coach \u4FEE\u590D \u{1F534}\uFF0C\u5BF9 \u{1F7E1} \u5199\u4FEE\u590D\u6216\u6743\u8861\u3002\u5B8C\u6210\u540E\u66F4\u65B0\u81F3 Step 8\u3002
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\`\u3002\u53CC\u5BA1\u9ED8\u8BA4\u6267\u884C\uFF1B\u7528\u6237\u660E\u786E\u8981\u6C42\u624D\u53EF\u8DF3\u8FC7\uFF0C\u901A\u8FC7 CLI \u5199\u5165\u7D2F\u8BA1 skippedSteps \u548C\u6B8B\u4F59\u98CE\u9669\u3002\u5B8C\u6210\u540E\u66F4\u65B0\u81F3 Step 9\u3002
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 Film #2 \u7684 \u{1F534} \u5E76\u91CD\u8DD1\u9A8C\u8BC1\u3002
847
- 2. \u5199 \`summary.md\`\uFF1A\u6539\u52A8\u3001\u65B0\u5EFA\u3001\u590D\u7528\u3001\u9A8C\u8BC1\u3001\u53CC\u5BA1\u548C\u8DF3\u8FC7\u6B65\u9AA4\u3002
848
- 3. \u9A8C\u8BC1\u901A\u8FC7\u4E14 \u{1F534} \u6E05\u96F6\u624D\u7528 CLI \u5199 \`completed\`\uFF1B\u5426\u5219\u7528 \`--status blocked --blocking-reason "<\u539F\u56E0>"\`\u3002
849
- 4. \u5173\u952E\u51B3\u7B56 appendTeamDecision \u5230 \`decisions.md\`\uFF0C\u66F4\u65B0 Active Plans\u3002
850
- 5. 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
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\u547D\u540D\u4E00\u81F4\u6027\u3001\u56E2\u961F\u98CE\u683C\u4E00\u81F4\u6027\uFF0C\u5E76\u4FEE\u590D \u{1F534}\u3002
1178
- 8. Film #2\uFF1A\u91CD\u70B9\u5BA1\u67E5\u8FB9\u754C\u6761\u4EF6\u3001\u5B89\u5168\u3001\u6027\u80FD\u3001\u5E76\u53D1\u3001\u517C\u5BB9\u6027\u3002\u53CC\u5BA1\u9ED8\u8BA4\u6267\u884C\uFF1B\u7528\u6237\u660E\u786E\u8981\u6C42\u624D\u53EF\u8DF3\u8FC7\uFF0C\u5199\u5165 \`skippedSteps\` \u548C\u6B8B\u4F59\u98CE\u9669\uFF1B\u8DF3\u8FC7\u4E0D\u963B\u6B62 completed\uFF0C\u4F46\u4E0D\u5F97\u58F0\u79F0"\u5DF2\u5B8C\u6210\u53CC\u5BA1"\u3002
1179
- 9. Post-game\uFF1A\u590D\u9A8C\uFF0C\u5199 summary \u4E0E hand-off\uFF1B\u9A8C\u8BC1\u5931\u8D25\u6216\u672A\u4FEE\u590D \u{1F534} \u65F6\u6807\u8BB0 blocked\uFF0C\u4E0D\u5F97\u6807 completed\u3002
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("bash .mancode/hooks/session-start.sh")
1657
+ createCommandGroup(MANCODE_HOOK_COMMANDS[0])
1645
1658
  ];
1646
1659
  settings.hooks.UserPromptSubmit = [
1647
1660
  ...normalizeHookGroups(settings.hooks.UserPromptSubmit),
1648
- createCommandGroup("bash .mancode/hooks/user-prompt-submit.sh")
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.values(value).flatMap(normalizeHookGroupEntry);
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.includes(".mancode/hooks/");
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 Film #1 code-quality review; fix all required findings and explain suggested-item tradeoffs.",
2180
- "Step 8 \u2014 Film #2 security/boundary review; keep it independent from Film #1. The two review passes are skipped only on explicit user instruction and the residual risk is recorded.",
2181
- "Step 9 \u2014 Fix, re-verify, write `summary.md`, archive decisions, and set completed only when verification passes and required findings are zero; otherwise set blocked+blockingReason.",
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, two review passes, conditional completion, planVersion,",
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 initPlatformName = options.platform ?? DEFAULT_INIT_PLATFORM;
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: false }
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
- return typeof config.forceTeamMode === "boolean" ? { forceTeamMode: config.forceTeamMode } : {};
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 path21 from "path";
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 mkdir8,
5683
- readFile as readFile9,
5735
+ mkdir as mkdir9,
5736
+ readFile as readFile10,
5684
5737
  readdir as readdir5,
5685
5738
  rm as rm4,
5686
- stat as stat3,
5687
- writeFile as writeFile10
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 TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
5941
+ var TASK_ID_PATTERN2 = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
5693
5942
  function isValidWorkflowTaskId(taskId) {
5694
- return TASK_ID_PATTERN.test(taskId);
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 mkdir8(workflowsRoot(projectRoot), { recursive: true });
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 mkdir8(workflowDir(projectRoot, taskId));
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 readFile9(file, "utf-8");
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
- assertValidTaskId(taskId);
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 stat3(dir);
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 path20.join(projectRoot, ".mancode", "workflows");
6058
+ return path21.join(projectRoot, ".mancode", "workflows");
5810
6059
  }
5811
6060
  function workflowDir(projectRoot, taskId) {
5812
- assertValidTaskId(taskId);
5813
- return path20.join(workflowsRoot(projectRoot), taskId);
6061
+ assertValidTaskId2(taskId);
6062
+ return path21.join(workflowsRoot(projectRoot), taskId);
5814
6063
  }
5815
6064
  function metadataPath(projectRoot, taskId) {
5816
- return path20.join(workflowDir(projectRoot, taskId), METADATA_FILE);
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 writeFile10(path20.join(dir, METADATA_FILE), content, "utf-8");
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) return false;
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 assertValidTaskId(taskId) {
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 = path21.join(rootDir, ".mancode", "state.json");
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(path21.join(rootDir, "package.json"), "utf-8");
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 path21.basename(rootDir);
6397
+ return path22.basename(rootDir);
6131
6398
  }
6132
6399
  async function readConfig2(rootDir) {
6133
6400
  try {
6134
6401
  const raw = await fs7.readFile(
6135
- path21.join(rootDir, ".mancode", "config.json"),
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(path21.join(rootDir, ".mancode", "hooks", "session-start.sh")),
6429
+ pathExists9(path22.join(rootDir, ".mancode", "hooks", "session-start.sh")),
6163
6430
  pathExists9(
6164
- path21.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
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
- path21.join(rootDir, ".claude", "settings.json"),
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 (!isRecord7(settings)) return false;
6450
+ if (!isRecord8(settings)) return false;
6184
6451
  const hooks = settings.hooks;
6185
- if (!isRecord7(hooks)) return false;
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 (!isRecord7(group) || !Array.isArray(group.hooks)) return false;
6461
+ if (!isRecord8(group) || !Array.isArray(group.hooks)) return false;
6195
6462
  return group.hooks.some((hook) => {
6196
- if (!isRecord7(hook) || typeof hook.command !== "string") return false;
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 = path21.join(
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 isRecord7(value) {
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 readFile10, rm as rm5, writeFile as writeFile11 } from "fs/promises";
6342
- import path22 from "path";
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 = path22.join(rootDir, ".mancode", "state.json");
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(path22.join(rootDir, ".mancode"), {
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 = path22.join(rootDir, ".claude", "settings.json");
6677
+ const settingsPath = path23.join(rootDir, ".claude", "settings.json");
6411
6678
  let content;
6412
6679
  try {
6413
- content = await readFile10(settingsPath, "utf-8");
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 (isRecord8(settings.hooks)) {
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 (isRecord8(settings.skills)) {
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 writeFile11(
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 (!isRecord8(value)) return value;
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 (!isRecord8(entry)) return [entry];
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 isRecord8(value) && typeof value.command === "string" && value.command.includes(".mancode/hooks/");
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 = path22.join(rootDir, "AGENTS.md");
6755
+ const agentsPath = path23.join(rootDir, "AGENTS.md");
6480
6756
  try {
6481
- const content = await readFile10(agentsPath, "utf-8");
6757
+ const content = await readFile11(agentsPath, "utf-8");
6482
6758
  const cleaned = removeManagedBlock(content);
6483
6759
  if (cleaned.trim()) {
6484
- await writeFile11(agentsPath, `${cleaned}
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 = path22.join(
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 readFile10(instructionsPath, "utf-8");
6776
+ const content = await readFile11(instructionsPath, "utf-8");
6501
6777
  const cleaned = removeManagedBlock(content);
6502
6778
  if (cleaned.trim()) {
6503
- await writeFile11(instructionsPath, `${cleaned}
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 = path22.join(rootDir, "AGENTS.md");
6789
+ const agentsPath = path23.join(rootDir, "AGENTS.md");
6514
6790
  try {
6515
- const content = await readFile10(agentsPath, "utf-8");
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 writeFile11(agentsPath, `${cleaned}
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 = path22.join(rootDir, ".mancode", "config.json");
6808
+ const configPath = path23.join(rootDir, ".mancode", "config.json");
6533
6809
  try {
6534
- const raw = await readFile10(configPath, "utf-8");
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 (isRecord8(config.platformOptions)) {
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 writeFile11(
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 isRecord8(value) {
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 path23 from "path";
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(path23.join(rootDir, ".mancode", "state.json"))) {
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 candidates = workflows.filter((meta) => {
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, /man, /mamba, /manteam, /manps."
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("--json", "Output as JSON (for scripts)").action(async (subcommand, args, options) => {
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
  });