mancode 0.2.1 → 0.3.0

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
@@ -12,13 +12,20 @@ import path13 from "path";
12
12
  import process3 from "process";
13
13
 
14
14
  // src/installers/claude-code.ts
15
- import { access, mkdir as mkdir3, readFile as readFile2, rm, writeFile as writeFile3 } from "fs/promises";
15
+ import {
16
+ access,
17
+ mkdir as mkdir3,
18
+ readFile as readFile3,
19
+ readdir,
20
+ rm,
21
+ writeFile as writeFile3
22
+ } from "fs/promises";
16
23
  import path3 from "path";
17
24
 
18
25
  // src/templates/agents/scout.ts
19
26
  var SCOUT_AGENT = {
20
27
  name: "scout",
21
- description: "Investigates the codebase for mancode /man8 and /man workflows. Finds similar implementations, reusable modules, files to change, and risks. Does not write code.",
28
+ description: "Investigates the codebase for mancode /man workflows. Finds similar implementations, reusable modules, files to change, and risks. Does not write code.",
22
29
  tools: ["Read", "Grep", "Glob", "Bash"],
23
30
  body: `\u4F60\u662F mancode \u6559\u7EC3\u7EC4\u7684 Scout\uFF08\u7403\u63A2\uFF09\u3002
24
31
 
@@ -61,18 +68,20 @@ var SCOUT_AGENT = {
61
68
  \`\`\`markdown
62
69
  # Scout Report \xB7 <task>
63
70
 
71
+ ## \u9879\u76EE Profile
72
+ - \u9879\u76EE\u7C7B\u578B / \u8BED\u8A00 / framework / source roots / manifests / \u53EF\u7528\u9A8C\u8BC1 / UI \u8D44\u4EA7 / \u7F6E\u4FE1\u5EA6
73
+
64
74
  ## \u76F8\u4F3C\u5B9E\u73B0
65
- - \`src/foo/bar.ts:42\` \u2014 \u7B80\u8FF0\u53EF\u590D\u7528\u7684\u903B\u8F91
75
+ - \`<detected-source-root>/<relevant-file>:<line>\` \u2014 \u7B80\u8FF0\u53EF\u590D\u7528\u7684\u903B\u8F91
66
76
 
67
77
  ## \u53EF\u590D\u7528\u8D44\u6E90
68
- - \`src/components/Foo.tsx\` \u2014 \u7B80\u8FF0
69
- - \`src/utils/bar.ts\` \u2014 \u7B80\u8FF0
78
+ - \`<detected-source-root>/<existing-module>\` \u2014 \u7B80\u8FF0
70
79
 
71
80
  ## \u9700\u8981\u4FEE\u6539\u7684\u6587\u4EF6
72
- - \`src/pages/X.tsx\` \u2014 \u6539\u4EC0\u4E48
81
+ - \`<detected-source-root>/<target-module>\` \u2014 \u6539\u4EC0\u4E48
73
82
 
74
83
  ## \u9700\u8981\u65B0\u5EFA\u7684\u6587\u4EF6
75
- - \`src/components/Y.tsx\`
84
+ - \`<detected-source-root>/<new-module>\`
76
85
 
77
86
  ## \u98CE\u9669\u70B9
78
87
  - \u26A0\uFE0F \u98CE\u9669\u63CF\u8FF0\uFF08\u5F15\u7528 \`file:line\`\uFF09
@@ -110,21 +119,25 @@ var PLAN_COACH_AGENT = {
110
119
  ## \u8F93\u51FA\u683C\u5F0F
111
120
 
112
121
  \`\`\`markdown
113
- # Game Plan \xB7 <task>
114
-
115
- ## Goal
116
-
117
- ## Assumptions
118
-
119
- ## Files To Change
120
-
121
- ## Steps
122
-
123
- ## Validation
124
-
125
- ## Risks
126
-
127
- ## Questions
122
+ # Plan \xB7 <task>
123
+
124
+ ## \u9700\u6C42\u6458\u8981
125
+ ## \u4EFB\u52A1\u5206\u7EA7
126
+ - \u7B80\u5355 / \u4E2D\u7B49 / \u590D\u6742\uFF1B\u8BF4\u660E\u7406\u7531
127
+ ## \u6A21\u5757\u7D22\u5F15
128
+ ## \u6280\u672F\u4E0E\u4EA4\u4ED8\u7EA6\u675F\uFF08\u4EC5\u65B0\u9879\u76EE\u6216\u6280\u672F\u9009\u62E9\u672A\u5B9A\u65F6\uFF09
129
+ - \u5019\u9009\u65B9\u6848\uFF082\u20133 \u4E2A\uFF09\u3001\u4F18\u7F3A\u70B9\u3001\u63A8\u8350\u4E0E\u786E\u8BA4\u70B9\uFF1B\u5DF2\u6709\u9879\u76EE\u5199\u201C\u6CBF\u7528\u68C0\u6D4B\u5230\u7684\u9879\u76EE\u7EA6\u5B9A\u201D
130
+ ## \u6A21\u5757\uFF1A<\u540D\u79F0>
131
+ ### \u6539\u52A8\u6587\u4EF6 / \u65B0\u5EFA\u6587\u4EF6
132
+ ### \u590D\u7528\u8D44\u6E90\uFF08\u5F15\u7528 scout-report.md \u884C\u53F7\uFF09
133
+ ### \u6700\u5C0F\u5B9E\u73B0\u7B56\u7565
134
+ ### \u4E0D\u505A\u4EC0\u4E48
135
+ ### \u5B9E\u65BD\u6B65\u9AA4
136
+ ### \u98CE\u9669\u70B9
137
+ ### \u5B8C\u6210\u5B9A\u4E49
138
+ ## \u9A8C\u8BC1\u8BA1\u5212
139
+ - build / lint / typecheck / test / smoke test
140
+ ## \u9884\u4F30
128
141
  \`\`\`
129
142
 
130
143
  \u5982\u679C\u4EFB\u52A1\u4E0D\u5E94\u7EE7\u7EED\u5B9E\u65BD\uFF0C\u660E\u786E\u5199\u5728 Risks \u6216 Questions \u4E2D\u3002`
@@ -201,7 +214,7 @@ var HEAD_COACH_AGENT = {
201
214
 
202
215
  - **\u660E\u786E\u5047\u8BBE**\uFF1A\u591A\u79CD\u7406\u89E3\u65F6\u5217\u51FA\u6765\uFF1B\u5C0F\u9009\u62E9\uFF08\u547D\u540D\u3001\u683C\u5F0F\uFF09\u9009\u4E00\u4E2A\u5408\u7406\u7684\u5E76\u8BF4\u660E
203
216
  - **\u5148\u8BFB\u518D\u5199**\uFF1A\u5199\u65B0\u4EE3\u7801\u524D\u5148\u8BFB\u5DF2\u6709\u4EE3\u7801\uFF0C\u627E\u51C6\u8981\u6539\u7684\u6587\u4EF6\u548C\u884C
204
- - **\u9A8C\u8BC1\u4F9D\u8D56**\uFF1A\u7528\u67D0\u5E93\u524D\u786E\u8BA4 \`package.json\` / \`requirements.txt\` \u91CC\u6709
217
+ - **\u9A8C\u8BC1\u4F9D\u8D56**\uFF1A\u7528\u67D0\u5E93\u524D\u786E\u8BA4\u9879\u76EE\u5DF2\u68C0\u6D4B\u5230\u7684 manifest\u3001\u9501\u6587\u4EF6\u6216\u73B0\u6709 import \u4E2D\u786E\u5B9E\u5B58\u5728
205
218
  - **\u59D4\u6258\u5B50\u4EE3\u7406**\uFF1A\u6DF1\u5EA6\u63A2\u7D22\u3001\u5927\u8303\u56F4\u641C\u7D22 \u2192 Scout agent
206
219
 
207
220
  ---
@@ -212,7 +225,7 @@ var HEAD_COACH_AGENT = {
212
225
  - **\u7CBE\u786E\u5339\u914D**\u5DF2\u6709\u98CE\u683C\u3001\u547D\u540D\u3001\u6A21\u5F0F \u2014 \u5373\u4F7F\u4F60\u4E0D\u8BA4\u540C
213
226
  - \u6539\u52A8\u5B64\u7ACB\u4E86\u67D0\u4E2A import / \u53D8\u91CF / \u51FD\u6570 \u2192 \u79FB\u9664\u5B83
214
227
  - \u4E0D\u79FB\u9664\u6539\u52A8\u524D\u5C31\u5B58\u5728\u7684\u6B7B\u4EE3\u7801\uFF08\u63D0\u4E00\u53E5\u5C31\u884C\uFF09
215
- - \u5E94\u7528\u5BA1\u7F8E token\uFF08\u524D\u7AEF\u4EFB\u52A1\uFF0C\u4ECE \`.mancode/aesthetics/style-tokens.json\` \u8BFB\u53D6\uFF09
228
+ - \u5148\u8BFB \`.mancode/project-profile.json\`\uFF1B\u4EC5\u5728 profile \u786E\u8BA4 UI \u8D44\u4EA7\u4E14\u4EFB\u52A1\u6D89\u53CA UI \u65F6\uFF0C\u8BFB\u53D6\u5BA1\u7F8E token
216
229
 
217
230
  ---
218
231
 
@@ -277,7 +290,8 @@ var HEAD_COACH_AGENT = {
277
290
 
278
291
  **\u5B9E\u65BD\u9636\u6BB5**\uFF1A
279
292
  - \u6309 plan \u5199\u4EE3\u7801
280
- - \u5E94\u7528\u9879\u76EE\u5BA1\u7F8E token
293
+ - \u6309 profile \u4F7F\u7528\u9879\u76EE\u80FD\u529B\u4E0E\u9A8C\u8BC1\u65B9\u5F0F\uFF1B\u4E0D\u5047\u5B9A\u67D0\u8BED\u8A00\u3001\u6846\u67B6\u3001\u6D4F\u89C8\u5668\u6216 UI \u5B58\u5728
294
+ - UI \u4EFB\u52A1\u624D\u5E94\u7528\u9879\u76EE\u5BA1\u7F8E token\uFF0C\u5E76\u5BA1\u67E5\u4FE1\u606F\u5C42\u7EA7\u3001\u72B6\u6001\u53CD\u9988\u3001\u53EF\u8FBE\u6027\u4E0E\u65E2\u6709\u8BBE\u8BA1\u4E00\u81F4\u6027
281
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
282
296
 
283
297
  **\u4FEE\u590D\u9636\u6BB5**\uFF08Film Analyst \u53CD\u9988\u540E\uFF09\uFF1A
@@ -286,9 +300,15 @@ var HEAD_COACH_AGENT = {
286
300
  - \u4F18\u5148\u7EA7\uFF1A\u{1F534} \u5FC5\u4FEE > \u{1F7E1} \u5EFA\u8BAE > \u{1F7E2} \u53EF\u9009
287
301
 
288
302
  **\u6536\u5C3E\u9636\u6BB5**\uFF1A
289
- - \u5408\u5E76 worktree\uFF08\u5982\u9002\u7528\uFF09
290
- - \u6E05\u7406\u4E34\u65F6\u6587\u4EF6
291
- - \u751F\u6210 summary\uFF08\u505A\u4E86\u4EC0\u4E48 / \u9A8C\u8BC1 / \u4E0B\u4E00\u6B65\uFF09
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
306
+ - \u5C06\u5173\u952E\u51B3\u7B56\u4EA4\u7ED9\u8C03\u7528\u65B9 appendTeamDecision\uFF0C\u5E76\u66F4\u65B0 Active Plans\u3002
307
+ - \u5408\u5E76 worktree \u524D\u53D6\u5F97\u7528\u6237\u786E\u8BA4\uFF1B\u6E05\u7406\u4E34\u65F6\u6587\u4EF6\u3002
308
+
309
+ **/mamba \u4EA4\u63A5\u9636\u6BB5**\uFF1A
310
+ - \u5F53\u771F\u5B9E\u6D4F\u89C8\u5668\u9A8C\u8BC1\u3001\u590D\u6742\u590D\u73B0\u6216\u56DE\u5F52\u9700\u8981\u4E13\u95E8\u8BCA\u65AD\u65F6\uFF0C\u901A\u8FC7 \`mancode workflow create mamba ... --parent-task <taskId>\` \u521B\u5EFA\u5B50 workflow\uFF1B\u6240\u6709 metadata \u53D8\u5316\u90FD\u8D70 workflow CLI\u3002
311
+ - \u5B50\u4EFB\u52A1 fixed/verified/no_repro \u540E\u56DE\u5230\u7236 /man Step 6\uFF1B\u82E5\u7236\u66FE\u56E0\u8BE5\u5B50\u4EFB\u52A1 blocked\uFF0C\u5148\u7528 workflow CLI \u6062\u590D\u4E3A in_progress\u3002blocked \u6216 manual_test_required \u65F6\u8BA9\u7236 workflow \u540C\u6B65 blocked\uFF0C\u540E\u8005\u4E0D\u5F97\u81EA\u52A8\u6062\u590D\u3002
292
312
 
293
313
  \u4E3B\u89C1\u5F3A\uFF0C\u5FC5\u8981\u65F6\u624D\u95EE\u7528\u6237\u3002\u5F00\u5E72\u3002`
294
314
  };
@@ -296,7 +316,7 @@ var HEAD_COACH_AGENT = {
296
316
  // src/templates/agents/film-analyst-offense.ts
297
317
  var FILM_ANALYST_OFFENSE_AGENT = {
298
318
  name: "film-analyst-offense",
299
- description: "Reviews code quality for mancode /man workflows (Step 5). Checks readability, maintainability, style consistency, DRY, YAGNI, complexity, error handling. Does not write code.",
319
+ description: "Reviews code and UI experience quality for mancode /man workflows (Step 7). Checks readability, maintainability, consistency, interaction hierarchy, and error handling. Does not write code.",
300
320
  tools: ["Read", "Grep", "Glob"],
301
321
  body: `\u4F60\u662F mancode \u6559\u7EC3\u7EC4\u7684 Film Analyst #1\uFF08\u5F55\u50CF\u5206\u6790\u5E08\xB7\u8FDB\u653B\uFF09\u3002
302
322
 
@@ -328,6 +348,18 @@ var FILM_ANALYST_OFFENSE_AGENT = {
328
348
  | **\u590D\u6742\u5EA6** | \u51FD\u6570\u8FC7\u957F\uFF08>60 \u884C\uFF09\u3001\u5D4C\u5957\u8FC7\u6DF1\uFF08>3 \u5C42\uFF09\u3001\u53C2\u6570\u8FC7\u591A\uFF08>4 \u4E2A\uFF09 |
329
349
  | **\u9519\u8BEF\u5904\u7406** | \u5F02\u5E38\u5904\u7406\u662F\u5426\u5408\u7406\uFF08\u4E0D\u541E\u9519\u3001\u4E0D\u88F8 catch\uFF09 |
330
350
 
351
+ ## \u6761\u4EF6 UI \u4F53\u9A8C\u5BA1\u67E5
352
+
353
+ \u53EA\u6709\u5F53 \`.mancode/project-profile.json\` \u6807\u8BB0\u9879\u76EE\u542B UI\uFF0C\u4E14\u672C\u6B21 diff \u786E\u5B9E\u6539\u52A8\u4E86\u7528\u6237\u754C\u9762\u65F6\uFF0C\u624D\u6267\u884C\u8FD9\u4E00\u7EC4\u68C0\u67E5\uFF1B\u7EAF\u540E\u7AEF\u3001CLI \u6216\u8005\u65E0 UI \u6539\u52A8\u65F6\u5199\u660E\u201C\u4E0D\u9002\u7528\u201D\uFF0C\u4E0D\u865A\u6784\u95EE\u9898\u3002
354
+
355
+ | \u7EF4\u5EA6 | \u68C0\u67E5\u4EC0\u4E48 |
356
+ |---|---|
357
+ | **\u4FE1\u606F\u5C42\u7EA7** | \u6807\u9898\u3001\u4E3B\u64CD\u4F5C\u3001\u6B21\u64CD\u4F5C\u548C\u5371\u9669\u64CD\u4F5C\u662F\u5426\u5BB9\u6613\u533A\u5206 |
358
+ | **\u4EFB\u52A1\u8DEF\u5F84** | \u4E3B\u8981\u4EFB\u52A1\u662F\u5426\u6709\u6E05\u6670\u5165\u53E3\u3001\u53CD\u9988\u548C\u5B8C\u6210\u72B6\u6001\uFF0C\u4E0D\u589E\u52A0\u65E0\u610F\u4E49\u6B65\u9AA4 |
359
+ | **\u72B6\u6001\u8986\u76D6** | \u7A7A\u3001\u52A0\u8F7D\u3001\u5931\u8D25\u3001\u65E0\u6743\u9650\u3001\u6210\u529F\u72B6\u6001\u662F\u5426\u4E0E\u6539\u52A8\u76F8\u5339\u914D |
360
+ | **\u54CD\u5E94\u5F0F** | \u65E2\u6709\u9879\u76EE\u652F\u6301\u591A\u5C3A\u5BF8\u65F6\uFF0C\u6539\u52A8\u662F\u5426\u5728\u5173\u952E\u5BBD\u5EA6\u4E0B\u4ECD\u53EF\u7528 |
361
+ | **\u89C6\u89C9\u4E00\u81F4\u6027** | \u662F\u5426\u590D\u7528\u9879\u76EE\u73B0\u6709 token\u3001\u7EC4\u4EF6\u3001\u95F4\u8DDD\u548C\u5B57\u4F53\u5C42\u7EA7\uFF0C\u907F\u514D\u5C40\u90E8\u53E6\u9020\u8BBE\u8BA1\u7CFB\u7EDF |
362
+
331
363
  ## \u8F93\u51FA\u683C\u5F0F\uFF08\u4E25\u683C\u9075\u5B88\uFF09
332
364
 
333
365
  \`\`\`markdown
@@ -363,7 +395,7 @@ var FILM_ANALYST_OFFENSE_AGENT = {
363
395
  // src/templates/agents/film-analyst-defense.ts
364
396
  var FILM_ANALYST_DEFENSE_AGENT = {
365
397
  name: "film-analyst-defense",
366
- description: "Reviews robustness for mancode /man workflows (Step 7). Checks boundary conditions, security (XSS / SQL injection / auth / data leaks), performance, resource management, error recovery. Does not write code.",
398
+ description: "Reviews robustness and UI accessibility for mancode /man workflows (Step 8). Checks boundaries, security, performance, recovery, permissions, keyboard access, and contrast. Does not write code.",
367
399
  tools: ["Read", "Grep", "Glob"],
368
400
  body: `\u4F60\u662F mancode \u6559\u7EC3\u7EC4\u7684 Film Analyst #2\uFF08\u5F55\u50CF\u5206\u6790\u5E08\xB7\u9632\u5B88\uFF09\u3002
369
401
 
@@ -395,6 +427,17 @@ var FILM_ANALYST_DEFENSE_AGENT = {
395
427
  | **\u9519\u8BEF\u6062\u590D** | \u5931\u8D25\u540E\u80FD\u5426\u6062\u590D\u3001\u662F\u5426\u7834\u574F\u72B6\u6001\u3001\u662F\u5426\u541E\u9519 |
396
428
  | **\u4F9D\u8D56** | \u7B2C\u4E09\u65B9\u5E93\u7684\u5DF2\u77E5\u6F0F\u6D1E\uFF08\u5F15\u7528\u5373\u53EF\uFF09 |
397
429
 
430
+ ## \u6761\u4EF6 UI \u9632\u5B88\u5BA1\u67E5
431
+
432
+ \u53EA\u6709\u5F53 \`.mancode/project-profile.json\` \u6807\u8BB0\u9879\u76EE\u542B UI\uFF0C\u4E14\u672C\u6B21 diff \u786E\u5B9E\u6539\u52A8\u4E86\u7528\u6237\u754C\u9762\u65F6\uFF0C\u624D\u6267\u884C\uFF1B\u5426\u5219\u5199\u660E\u201C\u4E0D\u9002\u7528\u201D\u3002
433
+
434
+ | \u7EF4\u5EA6 | \u68C0\u67E5\u4EC0\u4E48 |
435
+ |---|---|
436
+ | **\u952E\u76D8\u53EF\u7528\u6027** | \u4E3B\u8981\u6D41\u7A0B\u3001\u5F39\u7A97\u548C\u81EA\u5B9A\u4E49\u63A7\u4EF6\u80FD\u5426\u7528\u952E\u76D8\u64CD\u4F5C\uFF0C\u7126\u70B9\u662F\u5426\u53EF\u89C1\u4E14\u987A\u5E8F\u5408\u7406 |
437
+ | **\u8BED\u4E49\u4E0E\u8F85\u52A9\u6280\u672F** | \u4EA4\u4E92\u5143\u7D20\u662F\u5426\u6709\u6B63\u786E\u8BED\u4E49\u3001\u540D\u79F0\u548C\u72B6\u6001\u901A\u77E5 |
438
+ | **\u5BF9\u6BD4\u4E0E\u975E\u989C\u8272\u7EBF\u7D22** | \u6587\u5B57\u3001\u7126\u70B9\u3001\u9519\u8BEF\u4E0E\u6210\u529F\u4FE1\u606F\u662F\u5426\u53EF\u8BFB\uFF0C\u662F\u5426\u4E0D\u53EA\u4F9D\u8D56\u989C\u8272 |
439
+ | **\u6743\u9650\u4E0E\u9519\u8BEF\u8DEF\u5F84** | \u65E0\u6743\u9650\u3001\u7F51\u7EDC\u5931\u8D25\u3001\u90E8\u5206\u6210\u529F\u3001\u91CD\u8BD5\u548C\u53D6\u6D88\u662F\u5426\u4F1A\u6CC4\u6F0F\u6570\u636E\u6216\u7834\u574F\u72B6\u6001 |
440
+
398
441
  ## \u8F93\u51FA\u683C\u5F0F\uFF08\u4E25\u683C\u9075\u5B88\uFF09
399
442
 
400
443
  \`\`\`markdown
@@ -469,6 +512,7 @@ set -uo pipefail
469
512
 
470
513
  PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
471
514
  STATE_FILE="$PROJECT_ROOT/.mancode/state.json"
515
+ PROFILE_FILE="$PROJECT_ROOT/.mancode/project-profile.json"
472
516
 
473
517
  HAS_JQ=0
474
518
  if [ "\${MANCODE_DISABLE_JQ:-0}" != "1" ]; then
@@ -481,7 +525,7 @@ json_get() {
481
525
  if [ "$HAS_JQ" = "1" ]; then
482
526
  jq -r ".$key // empty" "$file" 2>/dev/null || true
483
527
  else
484
- grep "\\"$key\\"" "$file" 2>/dev/null | sed 's/.*: "\\(.*\\)".*/\\1/' || true
528
+ grep "\\"$key\\"" "$file" 2>/dev/null | sed -E 's/.*: *"([^"]*)".*/\\1/' || true
485
529
  fi
486
530
  }
487
531
 
@@ -522,12 +566,18 @@ echo "\u4F60\u6B63\u5728\u4F7F\u7528 mancode \${MODE:-solo} \u6A21\u5F0F\u3002"
522
566
  echo ""
523
567
  echo "### \u6838\u5FC3\u539F\u5219"
524
568
  echo "1. **\u4F18\u5148\u590D\u7528\u9879\u76EE\u5DF2\u6709\u4EE3\u7801**"
525
- echo " - \u68C0\u67E5 src/ \u662F\u5426\u5DF2\u6709\u7C7B\u4F3C\u5B9E\u73B0"
569
+ echo " - \u68C0\u67E5\u5DF2\u68C0\u6D4B\u5230\u7684\u6E90\u7801\u76EE\u5F55\u548C\u5DF2\u6709\u7C7B\u4F3C\u5B9E\u73B0"
526
570
  echo " - \u590D\u7528\u73B0\u6709\u7EC4\u4EF6\u3001\u51FD\u6570\u3001\u6837\u5F0F"
527
571
  echo ""
528
- echo "2. **\u5E94\u7528\u9879\u76EE\u5BA1\u7F8E token**\uFF08\u524D\u7AEF\u4EFB\u52A1\uFF09"
529
- echo " - UI library: $(sanitize "$UI_LIBRARY")"
530
- echo " - \u4F7F\u7528\u9879\u76EE\u5DF2\u6709\u7684\u8BBE\u8BA1 token"
572
+ if [ "$(json_get "uiAssets" "$PROFILE_FILE")" = "detected" ]; then
573
+ echo "2. **\u5E94\u7528\u9879\u76EE\u5BA1\u7F8E token**\uFF08\u4EC5\u5728\u9879\u76EE profile \u786E\u8BA4\u6709 UI \u8D44\u4EA7\u4E14\u4EFB\u52A1\u6D89\u53CA UI \u65F6\uFF09"
574
+ echo " - UI library: $(sanitize "$UI_LIBRARY")"
575
+ echo " - \u4F7F\u7528\u9879\u76EE\u5DF2\u6709\u7684\u8BBE\u8BA1 token"
576
+ else
577
+ echo "2. **\u6309\u9879\u76EE\u80FD\u529B\u5DE5\u4F5C**"
578
+ echo " - \u4E0D\u5047\u5B9A\u5B58\u5728 UI\u3001\u6D4F\u89C8\u5668\u6216\u7279\u5B9A\u6280\u672F\u6808"
579
+ echo " - \u5148\u8BFB\u53D6 project-profile \u4E0E\u9879\u76EE\u73B0\u6709\u9A8C\u8BC1\u65B9\u5F0F"
580
+ fi
531
581
  echo ""
532
582
  echo "3. **\u6700\u5C0F\u6539\u52A8**"
533
583
  echo " - \u53EA\u6539\u7528\u6237\u8981\u6C42\u7684\u90E8\u5206"
@@ -543,7 +593,7 @@ fi
543
593
  `;
544
594
  var USER_PROMPT_SUBMIT_HOOK = `#!/bin/bash
545
595
  # .mancode/hooks/user-prompt-submit.sh
546
- # mancode UserPromptSubmit hook - \u6CE8\u5165 3 \u95EE\u8FFD\u95EE + \u5BA1\u7F8E token
596
+ # mancode UserPromptSubmit hook - \u6CE8\u5165 6 \u95EE\u8FFD\u95EE + \u5BA1\u7F8E token
547
597
  set -uo pipefail
548
598
 
549
599
  PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
@@ -561,7 +611,7 @@ json_get() {
561
611
  if [ "$HAS_JQ" = "1" ]; then
562
612
  jq -r ".$key // empty" "$file" 2>/dev/null || true
563
613
  else
564
- grep "\\"$key\\"" "$file" 2>/dev/null | sed 's/.*: "\\(.*\\)".*/\\1/' || true
614
+ grep "\\"$key\\"" "$file" 2>/dev/null | sed -E 's/.*: *"([^"]*)".*/\\1/' || true
565
615
  fi
566
616
  }
567
617
 
@@ -572,9 +622,9 @@ sanitize() {
572
622
 
573
623
  MODE=$(json_get "currentMode" "$STATE_FILE")
574
624
 
575
- # \u53EA\u5728 solo \u6A21\u5F0F\u4E0B\u8F93\u51FA 3 \u95EE
625
+ # \u53EA\u5728 solo \u6A21\u5F0F\u4E0B\u8F93\u51FA 6 \u95EE
576
626
  if [ "$MODE" = "solo" ]; then
577
- echo "## \u52A8\u624B\u524D\uFF0C\u5148\u60F3\u4E09\u4E2A\u95EE\u9898\uFF1A"
627
+ echo "## \u52A8\u624B\u524D\uFF0C\u5148\u60F3\u516D\u4E2A\u95EE\u9898\uFF1A"
578
628
  echo ""
579
629
  echo "1. **\u4E3A\u4EC0\u4E48\u505A\uFF1F**"
580
630
  echo " - \u8FD9\u4E2A\u6539\u52A8\u89E3\u51B3\u4EC0\u4E48\u95EE\u9898\uFF1F"
@@ -585,6 +635,14 @@ if [ "$MODE" = "solo" ]; then
585
635
  echo "3. **\u6700\u5C11\u6539\u591A\u5C11\uFF1F**"
586
636
  echo " - \u80FD\u7528\u4E00\u884C\u89E3\u51B3\u5417\uFF1F\u80FD\u590D\u7528\u73B0\u6709\u4EE3\u7801\u5417\uFF1F"
587
637
  echo ""
638
+ echo "4. **\u80FD\u4E0D\u80FD\u4E0D\u62C6\u65B0\u7CFB\u7EDF\uFF1F**"
639
+ echo " - \u4E0D\u65B0\u5EFA\u6587\u4EF6\u6216\u6A21\u5757\u80FD\u5B8C\u6210\u5417\uFF1F"
640
+ echo ""
641
+ echo "5. **\u975E\u5E73\u51E1\u903B\u8F91\u600E\u6837\u6700\u5C0F\u8FD0\u884C\u9A8C\u8BC1\uFF1F**"
642
+ echo ""
643
+ echo "6. **\u6709\u4EC0\u4E48\u6CA1\u628A\u63E1\u7684\uFF1F**"
644
+ echo " - \u5148\u81EA\u884C\u67E5\u4EE3\u7801\u6216\u6587\u6863\uFF0C\u6700\u591A 2 \u6B21\u5DE5\u5177\u8C03\u7528\uFF1B\u4ECD\u4E0D\u786E\u5B9A\u518D\u95EE\u7528\u6237\u3002"
645
+ echo ""
588
646
  fi
589
647
 
590
648
  # Claude Code \u901A\u8FC7 stdin \u4F20\u5165 JSON: {"prompt": "...", ...}
@@ -610,13 +668,18 @@ if [ "$MODE" = "solo" ] && echo "$USER_PROMPT" | grep -qiE "\u5148(\u522B|\u4E0D
610
668
  echo "## mancode \u81EA\u52A8\u8DEF\u7531"
611
669
  echo ""
612
670
  echo "\u8FD9\u4E2A\u8BF7\u6C42\u662F\u89C4\u5212/\u8C03\u7814\u7C7B\u4EFB\u52A1\u3002\u4E0D\u8981\u76F4\u63A5\u8FDB\u5165 solo \u5B9E\u65BD\u3002"
613
- echo "\u5FC5\u987B\u5148\u8C03\u7528 Skill tool\uFF0Cskill='man8'\uFF0C\u628A\u7528\u6237\u539F\u59CB\u8BF7\u6C42\u4F5C\u4E3A task\uFF0C\u6267\u884C Scout \u8C03\u7814\u548C Head Coach plan\u3002"
614
- echo "\u5982\u679C\u7528\u6237\u660E\u786E\u8981\u6C42\u4E0D\u8981\u6539\u4EE3\u7801\uFF0C\u751F\u6210 plan \u540E\u505C\u5728\u786E\u8BA4\u6B65\u9AA4\u3002"
671
+ echo "\u5FC5\u987B\u5148\u8C03\u7528 Skill tool\uFF0Cskill='man'\uFF0C\u628A\u7528\u6237\u539F\u59CB\u8BF7\u6C42\u4F5C\u4E3A task\uFF0C\u6267\u884C Scout \u8C03\u7814\u3001\u6F84\u6E05\u548C Plan Coach plan\u3002"
672
+ echo "\u7528\u6237\u53EA\u8981\u8BA1\u5212\u65F6\uFF0C\u5728 Step 4 \u9009\u62E9\u201C\u53EA\u8981\u8BA1\u5212\u201D\uFF1B\u4E0D\u8981\u5207\u5230\u53E6\u4E00\u4E2A\u547D\u4EE4\u3002"
615
673
  echo ""
616
674
  fi
617
675
 
618
- # \u524D\u7AEF\u4EFB\u52A1\u5173\u952E\u8BCD\u68C0\u6D4B\uFF08\u7528 \b \u8BCD\u8FB9\u754C\u907F\u514D\u5B50\u4E32\u8BEF\u5339\u914D\uFF09
619
- if echo "$USER_PROMPT" | grep -qiE "\\b(button|component|page|style|ui|design|layout|css|tailwind|color|font|theme|card|input|modal|dialog|header|footer|sidebar|dropdown|tooltip|toast|avatar|badge)\\b"; then
676
+ # UI token \u4EC5\u5728 profile \u786E\u8BA4\u5B58\u5728 UI \u8D44\u4EA7\u4E14\u4EFB\u52A1\u660E\u786E\u6D89\u53CA UI \u65F6\u6CE8\u5165\u3002
677
+ PROFILE_FILE="$PROJECT_ROOT/.mancode/project-profile.json"
678
+ UI_ASSETS=""
679
+ if [ -f "$PROFILE_FILE" ]; then
680
+ UI_ASSETS=$(json_get "uiAssets" "$PROFILE_FILE")
681
+ fi
682
+ if [ "$UI_ASSETS" = "detected" ] && echo "$USER_PROMPT" | grep -qiE "\\b(button|component|page|style|ui|design|layout|css|color|font|theme|card|input|modal|dialog|header|footer|sidebar|dropdown|tooltip|toast|avatar|badge)\\b|\u754C\u9762|\u9875\u9762|\u6309\u94AE|\u6837\u5F0F|\u989C\u8272|\u5B57\u4F53|\u5E03\u5C40|\u7EC4\u4EF6|\u5F39\u7A97|\u5BFC\u822A|\u5361\u7247|\u8F93\u5165\u6846|\u4E3B\u9898|\u7F8E\u5316|\u4F18\u5316.*\u754C\u9762|\u8C03\u6574.*\u6837\u5F0F"; then
620
683
  if [ -f "$AESTHETICS_FILE" ]; then
621
684
  MATCH_LEVEL=$(json_get "matchLevel" "$AESTHETICS_FILE")
622
685
 
@@ -666,11 +729,10 @@ var SOLO_SKILL = `# mancode \xB7 solo mode
666
729
  5. \u4E00\u884C\u80FD\u89E3\u51B3\uFF1F\u2192 \u4E00\u884C
667
730
  6. \u53EA\u6709\u4EE5\u4E0A\u90FD\u4E0D\u884C\uFF0C\u624D\u5199\u6700\u5C0F\u5B9E\u73B0
668
731
 
669
- ### 2. \u5BA1\u7F8E\u4E00\u81F4\u6027
670
- \u524D\u7AEF\u4EFB\u52A1\uFF1A
671
- - \u4F7F\u7528\u9879\u76EE\u5DF2\u6709\u7684\u8BBE\u8BA1 token\uFF08\u989C\u8272\u3001\u5B57\u4F53\uFF09\u2014\u2014 hook \u4F1A\u81EA\u52A8\u6CE8\u5165
672
- - \u590D\u7528\u73B0\u6709\u7EC4\u4EF6\uFF0C\u4E0D\u91CD\u65B0\u9020
673
- - \u5339\u914D\u547D\u540D\u89C4\u8303
732
+ ### 2. \u9879\u76EE\u4E00\u81F4\u6027
733
+ - \u5148\u8BFB \`.mancode/project-profile.json\`\uFF0C\u4E0D\u5047\u5B9A\u9879\u76EE\u7C7B\u578B\u3001\u8BED\u8A00\u3001UI \u6216\u6D4F\u89C8\u5668\u80FD\u529B\u3002
734
+ - \u53EA\u6709 profile \u786E\u8BA4 UI \u8D44\u4EA7\u4E14\u4EFB\u52A1\u6D89\u53CA UI \u65F6\uFF0C\u624D\u4F7F\u7528\u5DF2\u6709\u8BBE\u8BA1 token\uFF1B\u590D\u7528\u7EC4\u4EF6\uFF0C\u4E0D\u91CD\u65B0\u9020\u3002
735
+ - \u6240\u6709\u9879\u76EE\u90FD\u5339\u914D\u5DF2\u6709\u547D\u540D\u3001\u9519\u8BEF\u5904\u7406\u3001\u6D4B\u8BD5\u4E0E\u4EA4\u4ED8\u89C4\u8303\u3002
674
736
 
675
737
  ### 3. \u5916\u79D1\u624B\u672F\u5F0F\u4FEE\u6539
676
738
  - \u53EA\u6539\u7528\u6237\u8981\u6C42\u7684\u90E8\u5206
@@ -683,23 +745,22 @@ var SOLO_SKILL = `# mancode \xB7 solo mode
683
745
  ### \u8BFB\u53D6\u9879\u76EE\u4E0A\u4E0B\u6587
684
746
  \u6BCF\u6B21\u4EFB\u52A1\u524D\uFF1A
685
747
  1. \u8BFB\u53D6 \`.mancode/state.json\`\uFF08\u4E86\u89E3\u9879\u76EE\u72B6\u6001\uFF09
686
- 2. \u641C\u7D22\u76F8\u4F3C\u5B9E\u73B0\uFF08"\u8FD9\u4E2A\u9879\u76EE\u91CC\u6709\u6CA1\u6709\uFF1F"\uFF09
687
- 3. \u68C0\u67E5\u53EF\u590D\u7528\u8D44\u6E90
688
-
689
- ### \u524D\u7AEF\u4EFB\u52A1
690
- \u5982\u679C\u662F UI / \u7EC4\u4EF6 / \u6837\u5F0F\u4EFB\u52A1\uFF1A
691
- 1. UserPromptSubmit hook \u4F1A\u6CE8\u5165\u9879\u76EE\u5BA1\u7F8E token\uFF08colors/fonts/uiLibrary\uFF09
692
- 2. \u4E25\u683C\u4F7F\u7528\u6CE8\u5165\u7684\u989C\u8272\u503C\uFF0C\u4E0D\u8981\u731C
693
- 3. \u9879\u76EE\u5DF2\u6709 Button/Card/Input \u7B49\u7EC4\u4EF6 \u2192 \u5FC5\u987B\u590D\u7528
694
- 4. \u4E0D\u8981\u5F15\u5165\u9879\u76EE\u8272\u677F\u4EE5\u5916\u7684\u65B0\u989C\u8272
695
- 5. \u4E0D\u8981\u7528 inline style \u5199\u989C\u8272
696
-
697
- ### \u53CD AI slop \u68C0\u67E5
698
- \u524D\u7AEF\u8F93\u51FA\u524D\u81EA\u68C0\uFF1A
699
- - \u274C \u6240\u6709\u5361\u7247\u90FD\u662F rounded-2xl shadow-md \u2192 \u7528\u9879\u76EE\u5B9E\u9645 borderRadius
700
- - \u274C \u9ED8\u8BA4\u6DF1\u8272 + \u7D2B\u8272\u6E10\u53D8 \u2192 \u7528\u9879\u76EE\u4E3B\u8272
701
- - \u274C \u6BCF\u4E2A\u6309\u94AE\u90FD\u52A0 hover:scale-105 \u2192 \u53EA\u5728 CTA \u7528
702
- - \u274C Loading \u90FD\u7528 spinner \u2192 \u7528\u9879\u76EE\u5DF2\u6709 Skeleton
748
+ 2. \u8BFB\u53D6 \`.mancode/project-profile.json\`\uFF08\u4E86\u89E3\u68C0\u6D4B\u5230\u7684\u6E90\u7801\u76EE\u5F55\u3001\u80FD\u529B\u548C\u9A8C\u8BC1\u65B9\u5F0F\uFF09
749
+ 3. \u641C\u7D22\u76F8\u4F3C\u5B9E\u73B0\uFF08"\u8FD9\u4E2A\u9879\u76EE\u91CC\u6709\u6CA1\u6709\uFF1F"\uFF09
750
+ 4. \u68C0\u67E5\u53EF\u590D\u7528\u8D44\u6E90
751
+
752
+ ### UI \u4EFB\u52A1\uFF08\u6761\u4EF6\u6267\u884C\uFF09
753
+ \u4EC5\u5F53 project-profile \u786E\u8BA4\u6709 UI \u8D44\u4EA7\u4E14\u4EFB\u52A1\u786E\u5B9E\u6D89\u53CA\u754C\u9762\u65F6\uFF1A
754
+ 1. UserPromptSubmit hook \u4F1A\u6CE8\u5165\u9879\u76EE\u5BA1\u7F8E token\uFF08\u5982\u6709\uFF09
755
+ 2. \u4E25\u683C\u4F7F\u7528\u5DF2\u6709 token \u548C\u7EC4\u4EF6\uFF1B\u6CA1\u6709\u53EF\u9760 token \u65F6\u5148\u68C0\u67E5\u73B0\u6709\u754C\u9762\uFF0C\u518D\u63D0\u51FA\u6700\u5C0F\u4E00\u81F4\u65B9\u6848
756
+ 3. \u4E0D\u628A\u7279\u5B9A\u6846\u67B6\u3001\u7EC4\u4EF6\u540D\u3001\u8272\u677F\u6216\u4EA4\u4E92\u8303\u5F0F\u5F53\u4F5C\u9ED8\u8BA4
757
+ 4. \u4EE5\u9879\u76EE\u65E2\u6709\u7684\u65E0\u969C\u788D\u3001\u54CD\u5E94\u5F0F\u4E0E\u53CD\u9988\u65B9\u5F0F\u4E3A\u51C6
758
+
759
+ ### \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
703
764
 
704
765
  ### \u9A8C\u8BC1
705
766
  \u4EE3\u7801\u5B8C\u6210\u540E\uFF1A
@@ -708,6 +769,12 @@ var SOLO_SKILL = `# mancode \xB7 solo mode
708
769
  3. \u8FD0\u884C test\uFF08\u5982\u6709\uFF09
709
770
  4. \u786E\u8BA4\u901A\u8FC7\u540E\u624D\u58F0\u79F0\u5B8C\u6210
710
771
 
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
777
+
711
778
  ## \u4F60\u7684\u98CE\u683C
712
779
 
713
780
  - \u76F4\u63A5\u3001\u7B80\u6D01\u3001\u4E0D\u5E9F\u8BDD
@@ -716,126 +783,6 @@ var SOLO_SKILL = `# mancode \xB7 solo mode
716
783
  - \u9519\u8BEF\u4FE1\u606F\u4E5F\u8981\u7B26\u5408\u9879\u76EE\u98CE\u683C
717
784
  `;
718
785
 
719
- // src/templates/skills/man8.ts
720
- var MAN8_SKILL = {
721
- name: "man8",
722
- description: 'Automatically use for pre-implementation research and planning requests: "\u5148\u770B\u770B", "\u7ED9\u65B9\u6848", "\u600E\u4E48\u505A", "\u4E0D\u8981\u6539\u4EE3\u7801", architecture/risk/approach questions. Also available as /man8. Runs Scout, then Head Coach writes a plan before coding.',
723
- body: `# mancode \xB7 /man8 (4 AM Warmup)
724
-
725
- \u8FD9\u662F**\u8C03\u7814 + \u8BA1\u5212** skill\u3002\u5B83\u53EF\u4EE5\u88AB\u7528\u6237\u663E\u5F0F \`/man8 <task>\` \u89E6\u53D1\uFF0C\u4E5F\u5E94\u8BE5\u5728\u7528\u6237\u6CA1\u6709\u8F93\u5165\u547D\u4EE4\u4F46\u660E\u663E\u8868\u8FBE"\u5148\u8C03\u7814/\u7ED9\u65B9\u6848/\u600E\u4E48\u505A/\u522B\u6539\u4EE3\u7801/\u8BC4\u4F30\u98CE\u9669/\u8BBE\u8BA1\u65B9\u6848"\u65F6\u81EA\u52A8\u89E6\u53D1\u3002
726
-
727
- \u7528\u6237\u786E\u8BA4\u524D\u4E0D\u52A8\u4EE3\u7801\uFF1B\u7528\u6237\u9009\u62E9\u5B9E\u65BD\u540E\uFF0C\u5207\u56DE solo \u5E76\u7ACB\u5373\u6309 plan \u5F00\u59CB\u6539\u3002
728
-
729
- ## \u81EA\u52A8\u89E6\u53D1\u6761\u4EF6
730
-
731
- \u5F53\u7528\u6237\u8BF7\u6C42\u7B26\u5408\u4EE5\u4E0B\u4EFB\u4E00\u6761\u4EF6\u65F6\uFF0C\u4E3B\u52A8\u4F7F\u7528\u672C skill\uFF0C\u800C\u4E0D\u662F\u76F4\u63A5\u8FDB\u5165 solo \u5B9E\u65BD\uFF1A
732
-
733
- - \u8981\u6C42"\u5148\u770B\u770B"\u3001"\u5148\u8C03\u7814"\u3001"\u5148\u5206\u6790"\u3001"\u7ED9\u6211\u65B9\u6848"\u3001"\u600E\u4E48\u505A"\u3001"\u8BBE\u8BA1\u4E00\u4E0B"
734
- - \u660E\u786E\u8BF4"\u5148\u522B\u6539\u4EE3\u7801"\u3001"\u4E0D\u8981\u52A8\u4EE3\u7801"\u3001"\u53EA\u7ED9\u8BA1\u5212"\u3001"\u5148\u8BC4\u4F30\u98CE\u9669"
735
- - \u4EFB\u52A1\u6D89\u53CA\u672A\u77E5\u6A21\u5757\u3001\u67B6\u6784\u9009\u62E9\u3001\u96C6\u6210\u65B9\u6848\u3001\u8FC1\u79FB\u65B9\u6848\u3001\u8F83\u5927\u8303\u56F4\u6539\u52A8
736
- - \u7528\u6237\u95EE"\u8FD9\u4E2A\u529F\u80FD\u600E\u4E48\u5B9E\u73B0\u66F4\u597D"\u3001"\u5E94\u8BE5\u600E\u4E48\u62C6"\u3001"\u6709\u6CA1\u6709\u98CE\u9669"
737
-
738
- \u4E0D\u8981\u56E0\u4E3A\u666E\u901A\u5C0F\u6539\u52A8\u81EA\u52A8\u89E6\u53D1\u672C skill\u3002\u4F8B\u5982\uFF1A"\u628A README \u52A0\u4E00\u884C"\u3001"\u4FEE\u4E00\u4E2A typo"\u3001"\u628A\u6309\u94AE\u6587\u6848\u6539\u6389" \u5E94\u7EE7\u7EED\u7528 solo\u3002
739
-
740
- ## \u4F60\u9700\u8981\u4ECE\u7528\u6237\u6D88\u606F\u91CC\u63D0\u53D6\u7684
741
-
742
- 1. \u5148\u7528 Read \u8BFB\u53D6 \`.mancode/state.json\`\u3002
743
- 2. \u7ACB\u5373\u7528 Edit \u66F4\u65B0 state\uFF1A
744
- - \`lastMode\` \u2190 \u539F \`currentMode\`
745
- - \`currentMode\` \u2192 \`"man8"\`
746
- - \`currentTask\` \u2192 \`null\`
747
- - \`currentWorkflowMode\` \u2192 \`null\`
748
- - \`skippedSteps\` \u2192 \`[]\`
749
- 3. **task**\uFF1A
750
- - \u663E\u5F0F\u89E6\u53D1\uFF1A\u7528\u6237\u8F93\u5165 \`/man8 <task>\` \u65F6\uFF0Ctask \u662F \`<task>\` \u90E8\u5206\u3002\u5982 \`/man8 \u6DFB\u52A0 OAuth \u767B\u5F55\u6309\u94AE\` \u2192 task = "\u6DFB\u52A0 OAuth \u767B\u5F55\u6309\u94AE"\u3002
751
- - \u81EA\u52A8\u89E6\u53D1\uFF1A\u7528\u6237\u6CA1\u6709\u8F93\u5165 \`/man8\` \u4F46\u672C skill \u88AB\u9009\u4E2D\u65F6\uFF0C\u628A\u7528\u6237\u539F\u59CB\u8BF7\u6C42\u6574\u4F53\u4F5C\u4E3A task\u3002
752
- 4. \u53EA\u6709\u5728\u663E\u5F0F\u8F93\u5165 \`/man8\` \u4E14 task \u4E3A\u7A7A\u65F6\uFF0C\u624D\u7528 AskUserQuestion \u95EE\u7528\u6237\uFF1A"\u8981\u8C03\u7814\u4EC0\u4E48\u4EFB\u52A1\uFF1F"\u3002\u7B49\u5F85\u4EFB\u52A1\u671F\u95F4\uFF0C\u5F53\u524D\u6A21\u5F0F\u4ECD\u662F \`man8\`\uFF1B\u5982\u679C\u7528\u6237\u95EE"\u73B0\u5728\u662F\u4EC0\u4E48\u6A21\u5F0F"\uFF0C\u56DE\u7B54 man8 \u6A21\u5F0F\u3002
753
-
754
- ## 3 \u6B65\u6D41\u7A0B
755
-
756
- ### Step 1\uFF1AScout \u8C03\u7814
757
-
758
- 1. \u7528 Write \u5DE5\u5177\u521B\u5EFA workflow \u76EE\u5F55\u3002task id \u7528 \`YYYYMMDD-HHMMSS-<slug>\` \u683C\u5F0F\uFF08slug = task \u7684 kebab-case\uFF0C\u622A\u5230 30 \u5B57\u7B26\uFF09\uFF1A
759
- - \u5FC5\u987B\u5148\u7528 Bash \u6267\u884C \`date -u +"%Y%m%d-%H%M%S"\` \u83B7\u53D6\u771F\u5B9E\u65F6\u95F4\u6233\uFF1B\u4E0D\u8981\u51ED\u7A7A\u4F30\u7B97\u65E5\u671F\u65F6\u95F4
760
- - \u76EE\u5F55\uFF1A\`.mancode/workflows/<taskId>/\`
761
- - \u5199\u5165 \`metadata.json\`\uFF1A\`{"taskId":"...","task":"...","mode":"man8","currentStep":1,"skippedSteps":[],"startedAt":"<ISO>","updatedAt":"<ISO>","status":"in_progress"}\`
762
- 2. \u7528 Agent tool \u8C03\u7528 Scout\uFF1A
763
- \`\`\`
764
- Agent({
765
- description: "Scout: investigate codebase for <task>",
766
- subagent_type: "scout",
767
- prompt: "\u4EFB\u52A1\uFF1A<task>\\n\u9879\u76EE\u6808\uFF1A<\u4ECE .mancode/state.json techStack \u8BFB>\\n\u5F00\u59CB\u8C03\u7814\u3002"
768
- })
769
- \`\`\`
770
- 3. \u628A Scout \u7684\u8F93\u51FA\u7528 Write \u5DE5\u5177\u5199\u5165 \`.mancode/workflows/<taskId>/scout-report.md\`
771
- 4. \u7528 Edit \u66F4\u65B0 \`.mancode/state.json\`\uFF1A\`currentMode: "man8"\`, \`currentTask: "<taskId>"\`, \`currentWorkflowMode: "man8"\`
772
- 5. \u7528 Edit \u5DE5\u5177\u66F4\u65B0 metadata.json\uFF1A\`currentStep: 2\`
773
-
774
- ### Step 2\uFF1APlan Coach \u5199 plan
775
-
776
- 1. \u7528 Agent tool \u8C03\u7528 Plan Coach\uFF08\u53EA\u8BFB plan \u6A21\u5F0F\uFF09\uFF1A
777
- \`\`\`
778
- Agent({
779
- description: "Plan Coach: write plan for <task>",
780
- subagent_type: "plan-coach",
781
- prompt: "\u4EFB\u52A1\uFF1A<task>\\nScout Report\uFF08\u6765\u81EA .mancode/workflows/<taskId>/scout-report.md\uFF09\uFF1A\\n<\u628A scout-report.md \u5185\u5BB9\u8D34\u8FDB\u6765>\\n\\n\u53EA\u8FD4\u56DE plan markdown\uFF0C\u4E0D\u8981\u4FEE\u6539\u9879\u76EE\u6587\u4EF6\u3002"
782
- })
783
- \`\`\`
784
- 2. \u628A Plan Coach \u8F93\u51FA\u5199\u5165 \`.mancode/workflows/<taskId>/plan.md\`
785
- 3. \u66F4\u65B0 metadata.json\uFF1A\`currentStep: 3\`
786
-
787
- ### Step 3\uFF1A\u7528\u6237\u786E\u8BA4
788
-
789
- \u7528 AskUserQuestion \u5DE5\u5177\u95EE\u7528\u6237\uFF1A
790
-
791
- \`\`\`
792
- AskUserQuestion({
793
- questions: [{
794
- question: "Plan \u5DF2\u751F\u6210\uFF08\u89C1 .mancode/workflows/<taskId>/plan.md\uFF09\u3002\u600E\u4E48\u505A\uFF1F",
795
- header: "Next step",
796
- options: [
797
- { label: "\u5207 solo \u5B9E\u65BD (Recommended)", description: "\u5207\u56DE solo \u6A21\u5F0F\uFF0C\u6309 plan \u76F4\u63A5\u5F00\u53D1" },
798
- { label: "\u4FEE\u6539 plan", description: "\u91CD\u65B0\u8DD1 Step 2\uFF08Plan Coach \u91CD\u5199 plan\uFF09" },
799
- { label: "\u9000\u51FA\uFF0C\u4FDD\u7559 plan", description: "\u7ED3\u675F /man8\uFF0Cplan \u4FDD\u7559\u5728 .mancode/workflows/ \u4F9B\u65E5\u540E\u53C2\u8003" }
800
- ],
801
- multiSelect: false
802
- }]
803
- })
804
- \`\`\`
805
-
806
- \u6839\u636E\u7528\u6237\u9009\u62E9\uFF1A
807
-
808
- - **\u5207 solo \u5B9E\u65BD**\uFF1A
809
- 1. \u7528 Edit \u66F4\u65B0 \`.mancode/state.json\`\uFF1A\`currentMode: "solo"\`, \`lastMode: "man8"\`, \`currentTask: null\`, \`currentWorkflowMode: null\`, \`skippedSteps: []\`
810
- 2. \u8BFB\u53D6 \`.mancode/workflows/<taskId>/plan.md\`
811
- 3. \u7528\u5F53\u524D assistant\uFF08solo \u6A21\u5F0F\uFF09\u7ACB\u5373\u6309 plan \u5B9E\u65BD\uFF1B\u4E0D\u8981\u518D\u6B21\u8981\u6C42\u7528\u6237\u8F93\u5165"\u5F00\u59CB\u5B9E\u65BD"
812
- 4. \u5B9E\u65BD\u5B8C\u6210\u540E\u8FD0\u884C plan \u91CC\u5217\u51FA\u7684\u9A8C\u8BC1\u547D\u4EE4\uFF1B\u5982\u6CA1\u6709\u9A8C\u8BC1\u547D\u4EE4\uFF0C\u81F3\u5C11\u7528\u53EA\u8BFB\u68C0\u67E5\u786E\u8BA4\u76EE\u6807\u6587\u4EF6\u5185\u5BB9
813
- 5. \u7528 Edit \u66F4\u65B0 metadata.json\uFF1A\`status: "completed"\`, \`currentStep: 3\`
814
- 6. \u6700\u7EC8\u544A\u8BC9\u7528\u6237\uFF1A\u6539\u4E86\u54EA\u4E9B\u6587\u4EF6\u3001\u9A8C\u8BC1\u7ED3\u679C\u3001plan \u8DEF\u5F84
815
-
816
- - **\u4FEE\u6539 plan**\uFF1A
817
- 1. \u8BE2\u95EE\u7528\u6237\u5E0C\u671B\u8C03\u6574\u4EC0\u4E48
818
- 2. \u91CD\u65B0\u8DD1 Step 2\uFF08Plan Coach \u91CD\u5199 plan\uFF0C\u53EF\u9644\u7528\u6237\u7684\u4FEE\u6539\u610F\u89C1\uFF09
819
-
820
- - **\u9000\u51FA**\uFF1A
821
- 1. \u66F4\u65B0 metadata.json\uFF1A\`status: "completed"\`
822
- 2. \u7528 Edit \u66F4\u65B0 \`.mancode/state.json\`\uFF1A\`currentMode: "solo"\`, \`lastMode: "man8"\`, \`currentTask: null\`, \`currentWorkflowMode: null\`, \`skippedSteps: []\`
823
- 3. \u544A\u8BC9\u7528\u6237\uFF1A"plan \u4FDD\u7559\u5728 \`.mancode/workflows/<taskId>/plan.md\`\uFF0C\u9700\u8981\u65F6\u518D\u53EB\u6211\u3002"
824
-
825
- ## \u4E0A\u4E0B\u6587\u9884\u7B97
826
-
827
- - Scout \u7684 prompt \u2264 200 tokens
828
- - Plan Coach \u7684 prompt \u628A scout-report.md \u5168\u90E8\u8D34\u8FDB\u53BB\uFF08plan \u9636\u6BB5\u503C\u5F97\uFF09
829
- - \u4E0D\u8981 dump \u5927\u91CF\u4EE3\u7801\u5230\u5BF9\u8BDD\uFF1B\u8BA9 agent \u81EA\u5DF1\u7528 Read \u8BFB
830
-
831
- ## \u5931\u8D25\u5904\u7406
832
-
833
- - Agent \u8C03\u7528\u5931\u8D25\uFF1A\u62A5\u544A\u9519\u8BEF\uFF0C\u4E0D\u91CD\u8BD5 2 \u6B21\u4EE5\u4E0A\uFF08\u94C1\u5F8B 1.3\uFF09
834
- - metadata.json \u5199\u5165\u5931\u8D25\uFF1A\u505C\u4E0B\u6765\u8BCA\u65AD\uFF0C\u4E0D\u4F2A\u9020\u72B6\u6001
835
-
836
- \u6536\u5230 \`/man8\` \u6216\u81EA\u52A8\u89E6\u53D1\u540E\u7ACB\u5373\u5F00\u59CB Step 1\uFF0C\u4E0D\u8981\u7B49\u7528\u6237\u786E\u8BA4\u3002`
837
- };
838
-
839
786
  // src/templates/skills/principles.ts
840
787
  var CORE_CODING_PRINCIPLES = `## \u94C1\u5F8B\uFF08\u6C38\u4E0D\u8FDD\u53CD\uFF09
841
788
 
@@ -848,157 +795,115 @@ var CORE_CODING_PRINCIPLES = `## \u94C1\u5F8B\uFF08\u6C38\u4E0D\u8FDD\u53CD\uFF0
848
795
  // src/templates/skills/man.ts
849
796
  var MAN_SKILL = {
850
797
  name: "man",
851
- description: "Full 8-step playoffs workflow with coaching staff. Scout \u2192 Plan \u2192 Implement \u2192 Self-test \u2192 Film Offense \u2192 Fix \u2192 Film Defense \u2192 Wrap-up. Use for production code, complex features, critical modules.",
852
- body: `# mancode \xB7 /man (Playoffs Mode)
798
+ description: "Progressive 9-step engineering workflow: research, clarify, plan, choose plan-only or execution, implement, validate, review, and wrap up. Use for complex or high-risk changes.",
799
+ body: `# mancode \xB7 /man (Progressive Governance)
853
800
 
854
- \u7528\u6237\u7528 \`/man <task>\` \u89E6\u53D1\u4F60\u3002\u8FD9\u662F**\u5B8C\u6574 8 \u6B65\u6D41\u7A0B**\uFF0C\u6559\u7EC3\u7EC4\u5168\u5458\u4E0A\u573A\u3002
801
+ \u7528\u6237\u7528 \`/man <task>\` \u89E6\u53D1\u4F60\u3002\u7ACB\u5373\u8BFB \`.mancode/state.json\`\u3002\u82E5\u5DF2\u6709\u4E0D\u540C\u7684 active workflow\uFF0C\u4E0D\u5F97\u76F4\u63A5\u6E05\u7A7A\u6307\u9488\uFF1B\u5148\u8BA9\u7528\u6237\u9009\u62E9\u6062\u590D\u539F\u6D41\u7A0B\u6216\u7528 \`/mansolo\` \u653E\u5F03\u3002task \u4E3A\u7A7A\u65F6\u8BE2\u95EE\u4EFB\u52A1\uFF0C\u7B49\u5F85\u671F\u95F4\u4ECD\u662F \`man\` \u6A21\u5F0F\u3002
855
802
 
856
- ## \u63D0\u53D6 task
803
+ \u7528 \`mancode workflow create man "<task>" --json\` \u521B\u5EFA workflow \u5E76\u8BFB\u53D6\u8FD4\u56DE\u7684 taskId\uFF1B\u4E0D\u5F97\u76F4\u63A5\u521B\u5EFA\u6216\u6539\u5199 metadata.json\u3002\u968F\u540E\u53EA\u7528 \`mancode workflow update\` \u66F4\u65B0 step/status/planVersion/skippedSteps\u3002state \u7684 \`currentMode\`\u3001\`currentTask\` \u548C \`currentWorkflowMode\` \u6307\u5411\u672C task\u3002
857
804
 
858
- 1. \u5148\u7528 Read \u8BFB\u53D6 \`.mancode/state.json\`\u3002
859
- 2. \u7ACB\u5373\u7528 Edit \u66F4\u65B0 state\uFF1A
860
- - \`lastMode\` \u2190 \u539F \`currentMode\`
861
- - \`currentMode\` \u2192 \`"man"\`
862
- - \`currentTask\` \u2192 \`null\`
863
- - \`currentWorkflowMode\` \u2192 \`null\`
864
- - \`skippedSteps\` \u2192 \`[]\`
865
- 3. \u4ECE \`/man <task>\` \u63D0\u53D6 \`<task>\`\u3002
866
- 4. \u5982 task \u4E3A\u7A7A\uFF0C\u7528 AskUserQuestion \u95EE\u7528\u6237\uFF1A"\u8981\u7528 /man \u5904\u7406\u4EC0\u4E48\u4EFB\u52A1\uFF1F"\u3002\u7B49\u5F85\u4EFB\u52A1\u671F\u95F4\uFF0C\u5F53\u524D\u6A21\u5F0F\u4ECD\u662F \`man\`\uFF1B\u5982\u679C\u7528\u6237\u95EE"\u73B0\u5728\u662F\u4EC0\u4E48\u6A21\u5F0F"\uFF0C\u56DE\u7B54 man \u6A21\u5F0F\u3002
805
+ ## \u65B0\u9879\u76EE\u6280\u672F\u9009\u62E9\u5173\u5361\uFF08\u4EC5\u5728\u9002\u7528\u65F6\uFF09
867
806
 
868
- ## 8 \u6B65\u6D41\u7A0B
807
+ \u5148\u8BFB \`.mancode/project-profile.json\`\u3002\u5982\u679C\u7528\u6237\u8981\u65B0\u5EFA\u9879\u76EE\u3001profile \u4E3A unknown\uFF0C\u4E14\u7528\u6237\u6CA1\u6709\u6307\u5B9A\u6280\u672F\u6808\uFF1A\u5148\u6536\u96C6\u76EE\u6807\u5E73\u53F0\u3001\u7528\u6237\u89C4\u6A21\u3001\u79BB\u7EBF/\u90E8\u7F72\u3001\u56E2\u961F\u719F\u6089\u5EA6\u3001\u9884\u7B97\u4E0E\u96C6\u6210\u7EA6\u675F\uFF1B\u5217\u51FA 2\u20133 \u4E2A\u53EF\u884C\u65B9\u6848\u7684\u4F18\u7F3A\u70B9\u548C\u63A8\u8350\u7406\u7531\uFF0C\u53D6\u5F97\u7528\u6237\u786E\u8BA4\u540E\u624D\u521B\u5EFA\u811A\u624B\u67B6\u3002\u4E0D\u8981\u628A\u4EFB\u4F55\u8BED\u8A00\u3001\u6846\u67B6\u3001UI \u5E93\u6216\u6D4F\u89C8\u5668\u81EA\u52A8\u5316\u5F53\u4F5C\u9ED8\u8BA4\u3002\u5DF2\u6709\u9879\u76EE\u5219\u4EE5\u68C0\u6D4B\u5230\u7684\u4E8B\u5B9E\u548C\u4ED3\u5E93\u7EA6\u5B9A\u4E3A\u51C6\u3002
869
808
 
870
- ### Step 1: Scout Report\uFF08\u7403\u63A2\u62A5\u544A\uFF09
809
+ ### Step 1: Scout \u8C03\u7814
871
810
 
872
- 1. \u521B\u5EFA workflow \u76EE\u5F55\uFF1A\`.mancode/workflows/<YYYYMMDD-HHMMSS-slug>/\`
873
- - \u5199\u5165 \`metadata.json\`\uFF1A\`{"taskId":"...","task":"...","mode":"man","currentStep":1,"skippedSteps":[],"startedAt":"<ISO>","updatedAt":"<ISO>","status":"in_progress"}\`
874
- 2. \u8C03\u7528 Scout\uFF1A
875
- \`\`\`
876
- Agent({ subagent_type: "scout", description: "Scout: <task>",
877
- prompt: "\u4EFB\u52A1\uFF1A<task>\\n\u9879\u76EE\u6808\uFF1A<\u4ECE state.json>\\n\u5F00\u59CB\u8C03\u7814\u3002" })
878
- \`\`\`
879
- 3. Write scout \u8F93\u51FA\u5230 \`scout-report.md\`
880
- 4. \u66F4\u65B0 state.json\uFF1A\`currentMode: "man"\`, \`currentTask: "<taskId>"\`, \`currentWorkflowMode: "man"\`
881
- 5. metadata.json\uFF1A\`currentStep: 2\`
811
+ \u8C03\u7528 \`scout\`\uFF0C\u5199 \`scout-report.md\`\u3002\u62A5\u544A\u5FC5\u987B\u5305\u542B\u201C**\u4E0D\u786E\u5B9A\u7684\u5730\u65B9**\u201D\u3002\u8FD0\u884C \`mancode workflow update <taskId> --step 2\`\u3002
882
812
 
883
- ### Step 2: Game Plan
813
+ ### Step 2: \u9700\u6C42\u6F84\u6E05
884
814
 
885
- 1. \u8BFB \`scout-report.md\` \u5185\u5BB9
886
- 2. \u8C03\u7528 Plan Coach\uFF08\u53EA\u8BFB plan \u6A21\u5F0F\uFF09\uFF1A
887
- \`\`\`
888
- Agent({ subagent_type: "plan-coach", description: "Plan Coach: plan <task>",
889
- prompt: "\u4EFB\u52A1\uFF1A<task>\\nScout Report\uFF1A\\n<scout-report.md \u5185\u5BB9>\\n\\n\u53EA\u8FD4\u56DE plan markdown\uFF0C\u4E0D\u8981\u4FEE\u6539\u9879\u76EE\u6587\u4EF6\u3002" })
890
- \`\`\`
891
- 3. Write \u5230 \`plan.md\`
892
- 4. metadata.json\uFF1A\`currentStep: 3\`
815
+ \u4E3B skill \u76F4\u63A5\u57FA\u4E8E Scout \u62A5\u544A\u548C\u4EFB\u52A1\u6B67\u4E49\u63D0\u95EE\uFF1A\u5BA2\u89C2\u9898\u7528 AskUserQuestion\uFF08\u6BCF\u6B21\u6700\u591A 4 \u4E2A\uFF09\uFF0C\u4E3B\u89C2\u9898\u5F00\u653E\u63D0\u95EE\u3002\u6700\u591A\u4E24\u8F6E\uFF0C\u56DE\u7B54\u5199 \`requirements.md\`\uFF1B\u65E0\u95EE\u9898\u5219\u901A\u8FC7 CLI \u5C06 \`clarify\` \u5199\u5165\u7D2F\u8BA1 skippedSteps\u3002\u7B2C\u4E8C\u8F6E\u540E\u5C06\u5047\u8BBE\u3001\u672A\u51B3\u98CE\u9669\u548C\u4FDD\u5B88\u9ED8\u8BA4\u503C\u5199\u5165 requirements\uFF0C\u4E0D\u65E0\u9650\u8FFD\u95EE\u3002\u5B8C\u6210\u540E\u66F4\u65B0\u81F3 Step 3\u3002
893
816
 
894
- ### \u7528\u6237\u786E\u8BA4 plan
817
+ ### Step 3: Plan Coach \u51FA\u8BA1\u5212
895
818
 
896
- \u7528 AskUserQuestion\uFF1A
819
+ \u8C03\u7528\u53EA\u8BFB \`plan-coach\`\uFF0C\u8F93\u5165 task\u3001scout-report \u548C requirements\uFF1BPlan Coach \u53EA\u8FD4\u56DE\u8BA1\u5212\u6587\u672C\uFF0C\u7531\u4E3B skill \u5199\u5165 \`plan.md\`\u3002\u8BA1\u5212\u5FC5\u987B\u542B\u4EFB\u52A1\u5206\u7EA7\u3001\u6A21\u5757\u7D22\u5F15\u3001\u590D\u7528\u8D44\u6E90\u4E0E scout \u884C\u53F7\u3001\u6700\u5C0F\u7B56\u7565\u3001\u4E0D\u505A\u4EC0\u4E48\u3001\u5B8C\u6210\u5B9A\u4E49\u3001\u9A8C\u8BC1\u4E0E smoke test\u3001\u9884\u4F30\u3002\u9996\u6B21\u8BA1\u5212\u5199\u5165\u6210\u529F\u540E\u8FD0\u884C \`mancode workflow update <taskId> --step 4\`\uFF0C\u7531 CLI upsert Active Plans \u5E76\u8FDB\u5165\u8BA1\u5212\u5173\u5361\uFF1B\u91CD\u5199\u8BA1\u5212\u65F6\u4FDD\u6301\u5728 Step 4\uFF0C\u8FD0\u884C \`--plan-version <\u5F53\u524D\u7248\u672C+1>\`\uFF0C\u4E0D\u5F97\u76F4\u63A5\u7F16\u8F91 planVersion\u3002
897
820
 
898
- \`\`\`
899
- AskUserQuestion({
900
- questions: [{
901
- question: "Plan \u89C1 .mancode/workflows/<taskId>/plan.md\u3002\u600E\u4E48\u505A\uFF1F",
902
- header: "Plan review",
903
- options: [
904
- { label: "\u6309 plan \u5B9E\u65BD (Recommended)", description: "\u8FDB\u5165 Step 3" },
905
- { label: "\u4FEE\u6539 plan", description: "\u56DE Step 2 \u91CD\u5199" },
906
- { label: "\u9000\u51FA", description: "\u653E\u5F03\uFF0C\u6807\u8BB0 abandoned" }
907
- ],
908
- multiSelect: false
909
- }]
910
- })
911
- \`\`\`
821
+ ### Step 4: \u8BA1\u5212\u5173\u5361
912
822
 
913
- - "\u9000\u51FA" \u2192 metadata.json \`status: "abandoned"\`\uFF1B\u7528 Edit \u66F4\u65B0 \`.mancode/state.json\`\uFF1A\`currentMode: "solo"\`, \`lastMode: "man"\`, \`currentTask: null\`, \`currentWorkflowMode: null\`, \`skippedSteps: []\`\uFF1B\u7136\u540E\u7ED3\u675F\u3002
914
- - "\u4FEE\u6539 plan" \u2192 \u56DE Step 2\uFF0C\u9644\u7528\u6237\u4FEE\u6539\u610F\u89C1\u3002
915
- - metadata.json\uFF1A\`currentStep: 3\`
823
+ \u7528 AskUserQuestion \u8BA9\u7528\u6237\u9009\u62E9\uFF1A
824
+ 1. **\u53EA\u8981\u8BA1\u5212**\uFF1ACLI \u66F4\u65B0 \`status: planned\`\uFF0Cstate \u56DE solo \u5E76\u6E05\u7A7A workflow \u6307\u9488\uFF0C\u7ED3\u675F\u3002
825
+ 2. **\u7EE7\u7EED\u6267\u884C\uFF08\u63A8\u8350\uFF09**\uFF1ACLI \u66F4\u65B0\u81F3 Step 5\u3002
826
+ 3. **\u4FEE\u6539\u8BA1\u5212**\uFF1A\u6536\u96C6\u610F\u89C1\u5E76\u91CD\u8DD1 Step 3 \u7684 Plan Coach\uFF0Cworkflow \u6307\u9488\u4FDD\u6301 Step 4\uFF1B\u8BA1\u5212\u91CD\u5199\u5B8C\u6210\u540E\u9012\u589E planVersion\u3002
916
827
 
917
- ### Step 3: Tip-off\uFF08\u5B9E\u65BD\uFF09
828
+ ### Step 5: \u5B9E\u65BD
918
829
 
919
- 1. \u5982\u4EFB\u52A1\u89C4\u6A21\u5927\uFF08\u591A\u6587\u4EF6\u3001\u65B0\u6A21\u5757\uFF09\uFF0C\u5EFA\u8BAE\u7528 EnterWorktree \u521B\u5EFA worktree\uFF08\u53EF\u9009\uFF0C\u95EE\u7528\u6237\uFF09
920
- 2. \u8C03\u7528 Head Coach\uFF08\u5B9E\u65BD\u6A21\u5F0F\uFF09\uFF1A
921
- \`\`\`
922
- Agent({ subagent_type: "head-coach", description: "Head Coach: implement <task>",
923
- prompt: "\u4EFB\u52A1\uFF1A<task>\\nPlan\uFF1A\\n<plan.md \u5185\u5BB9>\\n\\n\u6309 plan \u5B9E\u65BD\u3002" })
924
- \`\`\`
925
- 3. metadata.json\uFF1A\`currentStep: 4\`
830
+ \u8C03\u7528 \`head-coach\` \u6309\u786E\u8BA4\u8BA1\u5212\u5B9E\u65BD\u3002\u591A\u6587\u4EF6\u3001\u65B0\u6A21\u5757\u6216\u9AD8\u98CE\u9669\u4EFB\u52A1\u53EF\u5EFA\u8BAE worktree\uFF0C\u5FC5\u987B\u5148\u83B7\u7528\u6237\u540C\u610F\u3002\u5B9E\u65BD\u5B8C\u6210\u540E\u901A\u8FC7 CLI \u66F4\u65B0\u81F3 Step 6\u3002
926
831
 
927
- ### Step 4: Head Coach \u81EA\u6D4B
832
+ ### Step 6: \u81EA\u6D4B\u3001\u8BCA\u65AD\u4E0E\u56DE\u5F52
928
833
 
929
- \u8BA9 Head Coach \u8DD1\u9A8C\u8BC1\uFF1A\`npm run build && npm run lint && npm test\`\uFF08\u6216\u9879\u76EE\u5B9E\u9645\u547D\u4EE4\uFF09\u3002
834
+ \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
930
835
 
931
- **\u94C1\u5F8B 1.3\uFF1A\u5931\u8D25\u4E24\u6B21\u5FC5\u987B\u505C\u4E0B\u8BCA\u65AD\u6839\u56E0**\u3002
836
+ ### Step 7: Film #1 \u4EE3\u7801\u8D28\u91CF\u5BA1\u67E5\u4E0E\u4FEE\u590D
932
837
 
933
- \u901A\u8FC7\u540E metadata.json\uFF1A\`currentStep: 5\`
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
934
839
 
935
- ### Step 5: Film Session #1\uFF08\u8FDB\u653B\uFF09
840
+ ### Step 8: Film #2 \u5B89\u5168\u4E0E\u8FB9\u754C\u5BA1\u67E5
936
841
 
937
- \u7528 AskUserQuestion \u95EE\u7528\u6237\uFF1A"\u51C6\u5907\u53EB\u5F55\u50CF\u5206\u6790\u5E08 #1\uFF08\u8FDB\u653B\uFF09\u4E0A\u573A\uFF0C\u9700\u8981\u5417\uFF1F"
938
- - \u8DF3\u8FC7\uFF1Ametadata.json \`skippedSteps: ["film-1"]\`\uFF0C\u76F4\u63A5 Step 7
939
- - \u6267\u884C\uFF1A
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
940
843
 
941
- \`\`\`
942
- Agent({ subagent_type: "film-analyst-offense", description: "Film #1: offense review",
943
- prompt: "\u4EFB\u52A1\uFF1A<task>\\nScout Report\uFF1A\\n<scout-report.md>\\n\\n\u672C\u6B21\u6539\u52A8\u7684 git diff\uFF1A\\n<git diff HEAD>\\n\\n\u5F00\u59CB\u5BA1\u67E5\u3002" })
944
- \`\`\`
844
+ ### Step 9: \u589E\u5F3A\u6536\u5C3E
945
845
 
946
- Write \u8F93\u51FA\u5230 \`film-report-1.md\`\u3002metadata.json\uFF1A\`currentStep: 6\`
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
947
851
 
948
- ### Step 6: Halftime \u4FEE\u590D
852
+ \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
949
853
 
950
- \u8C03\u7528 Head Coach\uFF08\u4FEE\u590D\u6A21\u5F0F\uFF09\uFF1A
951
- \`\`\`
952
- Agent({ subagent_type: "head-coach", description: "Head Coach: fix film-1",
953
- prompt: "\u4EFB\u52A1\uFF1A<task>\\nFilm Report #1\uFF1A\\n<film-report-1.md>\\n\\n\u4FEE\u590D\u6307\u51FA\u7684\u95EE\u9898\uFF08\u{1F534} \u5FC5\u4FEE > \u{1F7E1} \u5EFA\u8BAE > \u{1F7E2} \u53EF\u9009\uFF09\u3002" })
954
- \`\`\`
854
+ ${CORE_CODING_PRINCIPLES}
855
+ `
856
+ };
955
857
 
956
- \u4FEE\u590D\u540E\u91CD\u8DD1 build/lint/test\u3002metadata.json\uFF1A\`currentStep: 7\`
858
+ // src/templates/skills/mamba.ts
859
+ var MAMBA_SKILL = {
860
+ name: "mamba",
861
+ description: "Diagnose bugs and validate real user flows. Reproduce, find the root cause, run targeted regression checks, and use browser automation when the platform supports it.",
862
+ body: `# mancode \xB7 /mamba (Diagnosis & Real Validation)
957
863
 
958
- ### Step 7: Film Session #2\uFF08\u9632\u5B88\uFF09
864
+ \u7528\u6237\u7528 \`/mamba <\u95EE\u9898\u6216\u6A21\u5757>\` \u89E6\u53D1\u4F60\u3002\u76EE\u6807\u662F\u7528\u8BC1\u636E\u56DE\u7B54\uFF1A\u80FD\u5426\u590D\u73B0\u3001\u6839\u56E0\u5728\u54EA\u91CC\u3001\u4FEE\u590D\u662F\u5426\u6709\u6548\u3001\u5173\u952E\u8DEF\u5F84\u662F\u5426\u56DE\u5F52\u3002
959
865
 
960
- \u95EE\u7528\u6237\uFF1A"\u53EB\u5F55\u50CF\u5206\u6790\u5E08 #2\uFF08\u9632\u5B88\uFF09\u4E0A\u573A\uFF1F"
961
- - \u8DF3\u8FC7\uFF1Ametadata.json \`skippedSteps\` \u52A0 \`"film-2"\`\uFF0C\u76F4\u63A5 Step 8
962
- - \u6267\u884C\uFF1A
866
+ ## 1. \u5EFA\u7ACB\u4EFB\u52A1
963
867
 
964
- \`\`\`
965
- Agent({ subagent_type: "film-analyst-defense", description: "Film #2: defense review",
966
- prompt: "\u4EFB\u52A1\uFF1A<task>\\nScout Report\uFF1A\\n<scout-report.md>\\nFilm Report #1 \u5DF2\u6307\u51FA\uFF08\u4E0D\u8981\u91CD\u590D\uFF09\uFF1A\\n<film-report-1.md>\\n\\n\u672C\u6B21\u6539\u52A8\u7684 git diff\uFF1A\\n<git diff HEAD>\\n\\n\u5F00\u59CB\u9632\u5B88\u5BA1\u67E5\u3002" })
967
- \`\`\`
868
+ \u5148\u8BFB state\u3002\u82E5 \`currentTask\` \u6307\u5411 unrelated active workflow\uFF0C\u505C\u6B62\u5E76\u8BA9\u7528\u6237\u9009\u62E9\u6062\u590D\u8BE5\u6D41\u7A0B\u6216\u5148\u7528 /mansolo \u653E\u5F03\uFF1B\u4E0D\u5F97\u8986\u76D6\u65E7\u6307\u9488\u3002\u72EC\u7ACB\u4EFB\u52A1\u8FD0\u884C \`mancode workflow create mamba "<\u95EE\u9898\u6216\u6A21\u5757>" --json\`\uFF1B\u7531 /man \u6216 /manteam Step 6 \u8F6C\u5165\u65F6\u8FD0\u884C \`mancode workflow create mamba "<\u95EE\u9898\u6216\u6A21\u5757>" --parent-task <\u7236taskId> --json\`\u3002\u8BFB\u53D6\u8FD4\u56DE taskId \u540E\uFF0C\u5C06 state \u7684 currentMode/currentTask/currentWorkflowMode \u6307\u5411 mamba\u3002\u4E0D\u5F97\u76F4\u63A5\u521B\u5EFA\u6216\u6539\u5199 metadata.json\u3002
968
869
 
969
- Write \u5230 \`film-report-2.md\`\u3002
970
- metadata.json\uFF1A\`currentStep: 8\`
870
+ \u8BB0\u5F55\u73B0\u8C61\u3001\u73AF\u5883\u3001\u590D\u73B0\u6761\u4EF6\u3001\u9884\u671F\u4E0E\u5B9E\u9645\u7ED3\u679C\u3001\u4E0D\u53EF\u89E6\u78B0\u7684\u6570\u636E\u3002\u5148\u68C0\u67E5\u5DF2\u6709\u6D4B\u8BD5\u3001\u8DEF\u7531\u3001\u6A21\u5757\u5165\u53E3\u548C README\uFF1B\u4ECD\u65E0\u6CD5\u786E\u5B9A\u6700\u5C0F\u8DEF\u5F84\u65F6\u53EA\u95EE\u5173\u952E\u8DEF\u5F84\u95EE\u9898\u3002\u5B8C\u6210\u540E\u7528 CLI \u66F4\u65B0\u81F3 Step 2\u3002
971
871
 
972
- ### Step 8: Post-game \u6536\u5C3E
872
+ ## 2. \u51C6\u5907\u73AF\u5883\u4E0E\u6743\u9650
973
873
 
974
- 1. \u8C03\u7528 Head Coach\uFF08\u6536\u5C3E\u6A21\u5F0F\uFF09\u4FEE\u590D Film Report #2 \u7684 \u{1F534} \u95EE\u9898\uFF1A
975
- \`\`\`
976
- Agent({ subagent_type: "head-coach", description: "Head Coach: wrap up",
977
- prompt: "\u4EFB\u52A1\uFF1A<task>\\nFilm Report #2\uFF1A\\n<film-report-2.md>\\n\\n\u4FEE\u590D \u{1F534} \u5FC5\u4FEE\u95EE\u9898\uFF0C\u7136\u540E\u751F\u6210 summary\u3002" })
978
- \`\`\`
979
- 2. \u628A summary \u5199\u5230 \`summary.md\`
980
- 3. \u5982\u6709 worktree\uFF0C\u5408\u5E76\u56DE\u4E3B\u5206\u652F\uFF08\u7528\u6237\u786E\u8BA4\u540E\uFF09
981
- 4. \u66F4\u65B0 state.json\uFF1A\`currentMode: "solo"\`, \`lastMode: "man"\`, \`currentTask: null\`, \`currentWorkflowMode: null\`, \`skippedSteps: []\`
982
- 5. metadata.json\uFF1A\`status: "completed"\`
874
+ \u5148\u8BFB \`.mancode/project-profile.json\`\uFF0C\u53EA\u4F7F\u7528\u5DF2\u68C0\u6D4B\u5230\u7684\u9879\u76EE\u7C7B\u578B\u3001manifest \u548C\u9A8C\u8BC1\u80FD\u529B\uFF1Bprofile \u4E3A unknown \u65F6\u5148\u95EE\u5173\u952E\u7F3A\u5931\u4FE1\u606F\u3002\u4F18\u5148\u672C\u5730 \u2192 \u6D4B\u8BD5 \u2192 staging \u2192 \u751F\u4EA7\u53EA\u8BFB\u3002\u53EF\u4EE5\u542F\u52A8\u672C\u5730\u5F00\u53D1\u670D\u52A1\u548C\u505A\u53EA\u8BFB\u63A2\u6D4B\u3002\u4E0B\u8F7D\u4F9D\u8D56\u3001seed/reset\u3001\u5199\u5165\u6D4B\u8BD5\u6570\u636E\u3001\u767B\u5F55\u5916\u90E8\u7CFB\u7EDF\u3001\u901A\u77E5\u3001\u4ED8\u6B3E\u3001\u5220\u9664\u6216\u751F\u4EA7\u5199\u64CD\u4F5C\u524D\uFF0C\u8BF4\u660E\u5F71\u54CD\u5E76\u53D6\u5F97\u7528\u6237\u786E\u8BA4\uFF1B\u4E0D\u731C\u6D4B\u51ED\u636E\u6216 Token\u3002
983
875
 
984
- ## \u6700\u7EC8\u8F93\u51FA
876
+ \u5E73\u53F0\u80FD\u529B\u5206\u7EA7\uFF1A
877
+ - **\u5B8C\u6574**\uFF1A\u6709\u7EC8\u7AEF\u3001\u6D4F\u89C8\u5668\u81EA\u52A8\u5316\u548C Playwright CLI\uFF1B\u53EF\u6D4B\u8BD5\u771F\u5B9E\u6D4F\u89C8\u5668\u8DEF\u5F84\u3002
878
+ - **\u534A\u81EA\u52A8**\uFF1A\u53EA\u80FD\u6267\u884C\u547D\u4EE4\uFF1B\u8F93\u51FA\u9010\u6B65\u4EBA\u5DE5\u6D4F\u89C8\u5668\u6D4B\u8BD5\u6E05\u5355\u3002
879
+ - **\u53D7\u9650**\uFF1A\u65E0\u672C\u5730\u6267\u884C\u80FD\u529B\uFF1B\u8F93\u51FA\u73AF\u5883\u9700\u6C42\u4E0E\u4EBA\u5DE5\u6B65\u9AA4\uFF0Cmetadata \u8BB0 \`outcome: "manual_test_required"\`\uFF0C\u4E0D\u5F97\u58F0\u79F0\u5DF2\u771F\u5B9E\u6D4B\u8BD5\u3002
985
880
 
986
- \u544A\u8BC9\u7528\u6237\uFF1A
987
- - \u6539\u4E86\u54EA\u4E9B\u6587\u4EF6 / \u65B0\u5EFA\u4E86\u54EA\u4E9B\u6587\u4EF6
988
- - \u590D\u7528\u4E86\u54EA\u4E9B\u5DF2\u6709\u4EE3\u7801
989
- - \u54EA\u4E9B\u6B65\u9AA4\u88AB\u8DF3\u8FC7\uFF08\u26A0\uFE0F \u6807\u6CE8\uFF09
990
- - \u9A8C\u8BC1\u7ED3\u679C\uFF08build/lint/test \u662F\u5426\u901A\u8FC7\uFF09
991
- - summary \u8DEF\u5F84
881
+ \u4EC5\u5F53 profile \u786E\u8BA4 Web UI \u4E14\u6D4F\u89C8\u5668\u80FD\u529B\u53EF\u7528\u65F6\uFF0C\u68C0\u67E5 \`npx\` \u5E76\u4F7F\u7528\u9879\u76EE\u7684 Playwright CLI wrapper\u3002\u5176\u4ED6\u9879\u76EE\u7C7B\u578B\u4F7F\u7528\u5DF2\u68C0\u6D4B\u5230\u7684 API\u3001\u6A21\u62DF\u5668\u3001\u8BBE\u5907\u3001\u547D\u4EE4\u6216\u5951\u7EA6\u9A8C\u8BC1\u65B9\u5F0F\u3002\u6D4F\u89C8\u5668\u9A8C\u8BC1\u65F6\u5148 snapshot \u518D\u4F7F\u7528\u5143\u7D20\u5F15\u7528\uFF1B\u5BFC\u822A\u3001\u5F39\u7A97\u6216 UI \u5927\u53D8\u5316\u540E\u91CD\u65B0 snapshot\u3002\u622A\u56FE\u3001trace \u548C\u6D4F\u89C8\u5668\u65E5\u5FD7\u5199\u5165 \`.mancode/workflows/<taskId>/artifacts/playwright/\`\uFF0C\u62A5\u544A\u53EA\u5199\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u5E76\u8131\u654F Token\u3001Cookie\u3001\u4E2A\u4EBA\u6216\u4E1A\u52A1\u654F\u611F\u6570\u636E\u3002
992
882
 
993
- ## \u4E0A\u4E0B\u6587\u9884\u7B97
883
+ \u73AF\u5883\u5C31\u7EEA\u540E\u7528 CLI \u66F4\u65B0\u81F3 Step 3\uFF1B\u7F3A\u5C11\u73AF\u5883\u3001\u6743\u9650\u6216\u6570\u636E\u5219\u76F4\u63A5\u5199 blocked + blockingReason\uFF0C\u5E76\u4FDD\u7559 state \u6307\u5411\u5F53\u524D mamba \u4EE5\u4FBF\u6062\u590D\u3002
994
884
 
995
- - \u8C03\u7528 subagent \u65F6\uFF0C**\u53EA\u8D34\u5FC5\u8981\u6587\u4EF6\u5185\u5BB9**\uFF08scout-report.md \u5168\u6587\u3001film-report \u5168\u6587\uFF09
996
- - diff \u5927\u65F6\u53EA\u8D34 hunk \u6458\u8981\uFF0C\u8BA9 agent \u81EA\u5DF1\u7528 Read \u770B\u5B8C\u6574 diff
997
- - \u4E0D\u8981 dump \u6574\u4E2A\u6E90\u6587\u4EF6\u5230\u5BF9\u8BDD
885
+ ## 3. \u590D\u73B0\u4E0E\u8BCA\u65AD
998
886
 
999
- ${CORE_CODING_PRINCIPLES}
887
+ \u6309\u6700\u77ED\u7528\u6237\u8DEF\u5F84\u590D\u73B0\u3002\u8BB0\u5F55\u8BF7\u6C42/\u54CD\u5E94\u3001\u65E5\u5FD7\u3001\u9519\u8BEF\u6808\u3001\u622A\u56FE\u548C\u5173\u952E UI \u72B6\u6001\u3002\u533A\u5206\u73AF\u5883\u3001\u6570\u636E\u3001\u914D\u7F6E\u3001\u6743\u9650\u3001\u524D\u7AEF\u3001\u540E\u7AEF\u548C\u9700\u6C42\u7406\u89E3\u95EE\u9898\u3002\u5199 \`diagnosis.md\`\uFF1A\u590D\u73B0\u6B65\u9AA4\u3001\u8BC1\u636E\u3001\u6839\u56E0\u6216\u5019\u9009\u6839\u56E0\u3001\u5F71\u54CD\u9762\u548C\u7F6E\u4FE1\u5EA6\u3002\u5B8C\u6210\u540E\u7528 CLI \u66F4\u65B0\u81F3 Step 4\u3002
888
+
889
+ ## 4. \u6700\u5C0F\u4FEE\u590D\u6216\u4EC5\u9A8C\u8BC1
1000
890
 
1001
- \u6536\u5230 \`/man\` \u89E6\u53D1\u540E\u7ACB\u5373\u5F00\u59CB Step 1\u3002`
891
+ \u7528\u6237\u8981\u6C42\u4FEE\u590D\u65F6\u53EA\u505A\u6700\u5C0F\u6539\u52A8\uFF1B\u53EA\u8981\u6C42\u6D4B\u8BD5\u65F6\u4E0D\u6539\u4EE3\u7801\u3002\u65B0\u9700\u6C42\u3001\u67B6\u6784\u51B3\u7B56\u6216\u8DE8\u6A21\u5757\u6539\u9020\u5E94\u505C\u6B62\u5E76\u5EFA\u8BAE /man\u3002\u5B8C\u6210\u540E\u7528 CLI \u66F4\u65B0\u81F3 Step 5\u3002
892
+
893
+ ## 5. \u771F\u5B9E\u56DE\u5F52\u4E0E\u7ED3\u8BBA
894
+
895
+ \u91CD\u8DD1\u539F\u8DEF\u5F84\uFF0C\u8986\u76D6\u53D7\u5F71\u54CD\u7684\u5173\u952E\u6B63\u5411\u3001\u8D1F\u5411\u548C\u6743\u9650\u8FB9\u754C\u8DEF\u5F84\uFF0C\u5E76\u8FD0\u884C\u5FC5\u8981 build/lint/typecheck/test\u3002\u5199 \`mamba-report.md\`\uFF1A\u73AF\u5883\u3001\u6B65\u9AA4\u3001\u7ED3\u679C\u3001\u4EA7\u7269\u8DEF\u5F84\u3001\u56DE\u5F52\u8303\u56F4\u3001\u98CE\u9669\u4E0E\u5EFA\u8BAE\u3002
896
+
897
+ - \u5DF2\u4FEE\u590D\uFF1A\`status: "completed", outcome: "fixed"\`
898
+ - \u5DF2\u9A8C\u8BC1\u4F46\u672A\u6539\u4EE3\u7801\uFF1A\`status: "completed", outcome: "verified"\`
899
+ - \u6307\u5B9A\u73AF\u5883\u672A\u590D\u73B0\uFF1A\`status: "completed", outcome: "no_repro"\`\uFF0C\u9650\u5B9A\u7ED3\u8BBA\u8303\u56F4
900
+ - \u7F3A\u5C11\u73AF\u5883\u3001\u6743\u9650\u6216\u6570\u636E\uFF0C\u6216\u4FEE\u590D\u4ECD\u5931\u8D25\uFF1A\`status: "blocked"\` + \`blockingReason\`
901
+ - \u53D7\u9650\u5E73\u53F0\u53EA\u80FD\u751F\u6210\u624B\u6D4B\u65B9\u6848\uFF1A\`status: "completed", outcome: "manual_test_required"\`\uFF1B\u5FC5\u987B\u660E\u786E\u6CA1\u6709\u6267\u884C\u771F\u5B9E\u6D4B\u8BD5
902
+
903
+ \u7528 \`mancode workflow update <taskId> --status <status> [--outcome <outcome>] [--blocking-reason <\u539F\u56E0>]\` \u5199\u7ED3\u8BBA\u3002\u7236\u5B50\u4EFB\u52A1\u7684 blocked/manual_test_required \u4F20\u64AD\u7531 CLI \u8D1F\u8D23\u3002\u5173\u8054\u5B50\u4EFB\u52A1\u4EE5 fixed/verified/no_repro \u5B8C\u6210\u540E\uFF0C\u5148\u8BFB\u53D6\u7236 workflow\uFF1B\u82E5\u7236\u4ECD\u56E0\u672C\u5B50\u4EFB\u52A1 blocked\uFF0C\u8FD0\u884C \`mancode workflow update <parentTaskId> --status in_progress\` \u6E05\u9664\u963B\u585E\uFF0C\u518D\u628A state \u6062\u590D\u5230\u7236 workflow \u7684 Step 6\u3002manual_test_required \u4E0D\u5F97\u81EA\u52A8\u6062\u590D\u7236\u4EFB\u52A1\u3002\u72EC\u7ACB\u4EFB\u52A1\u5B8C\u6210\u540E\u56DE solo\uFF1Bblocked \u7684\u72EC\u7ACB\u4EFB\u52A1\u7EE7\u7EED\u4FDD\u6301 mamba state\u3002\u4EFB\u4F55\u60C5\u51B5\u4E0B\u90FD\u4E0D\u5F97\u76F4\u63A5\u7F16\u8F91 metadata \u7ED5\u8FC7 CLI\u3002
904
+
905
+ ${CORE_CODING_PRINCIPLES}
906
+ `
1002
907
  };
1003
908
 
1004
909
  // src/templates/skills/manps.ts
@@ -1099,8 +1004,8 @@ CLI \u6210\u529F\u540E\uFF0C\u518D\u6309\u4E0B\u9762\u7684\u624B\u52A8\u626B\u63
1099
1004
 
1100
1005
  ### 1. Project Shape
1101
1006
 
1102
- - \u8BFB\u53D6 \`package.json\`\u3001README\u3001\u6D4B\u8BD5\u914D\u7F6E\u3001lint/build \u811A\u672C
1103
- - \u8BC6\u522B tech stack\u3001\u6D4B\u8BD5\u547D\u4EE4\u3001\u6784\u5EFA\u547D\u4EE4
1007
+ - \u8BFB\u53D6\u68C0\u6D4B\u5230\u7684 manifest\u3001README\u3001\u6D4B\u8BD5\u914D\u7F6E\u3001lint/build \u811A\u672C
1008
+ - \u8BC6\u522B\u9879\u76EE\u7C7B\u578B\u3001\u8BED\u8A00\u3001\u6D4B\u8BD5\u547D\u4EE4\u4E0E\u6784\u5EFA\u547D\u4EE4\uFF1B\u4E0D\u5047\u5B9A\u7279\u5B9A\u751F\u6001
1104
1009
  - \u8BFB\u53D6 \`.mancode/state.json\` \u548C \`.mancode/aesthetics/style-tokens.json\`\uFF08\u5982\u5B58\u5728\uFF09
1105
1010
 
1106
1011
  ### 2. Tech Debt
@@ -1113,7 +1018,7 @@ CLI \u6210\u529F\u540E\uFF0C\u518D\u6309\u4E0B\u9762\u7684\u624B\u52A8\u626B\u63
1113
1018
 
1114
1019
  ### 3. Dependency Health
1115
1020
 
1116
- - \u5BF9\u7167 \`package.json\` dependencies/devDependencies \u4E0E\u6E90\u7801 import
1021
+ - \u5BF9\u7167\u5DF2\u68C0\u6D4B\u5230\u7684 manifest\u3001\u9501\u6587\u4EF6\u3001\u4F9D\u8D56\u58F0\u660E\u4E0E\u6E90\u7801 import
1117
1022
  - \u6807\u8BB0\u7591\u4F3C\u672A\u4F7F\u7528\u4F9D\u8D56\uFF0C\u4F46\u4E0D\u8981\u76F4\u63A5\u5220\u9664
1118
1023
  - \u6807\u8BB0\u7F3A\u5C11\u811A\u672C\u7684\u98CE\u9669\uFF1A\u65E0 test\u3001\u65E0 lint\u3001\u65E0 build
1119
1024
 
@@ -1172,14 +1077,15 @@ var MANSOLO_SKILL = {
1172
1077
 
1173
1078
  ## \u4F60\u8981\u505A\u7684
1174
1079
 
1175
- 1. \u7528 Read \u8BFB \`.mancode/state.json\`
1176
- 2. \u7528 Edit \u66F4\u65B0\u4EE5\u4E0B\u5B57\u6BB5\uFF1A
1080
+ 1. \u7528 Read \u8BFB \`.mancode/state.json\`\u3002\u5982\u679C \`currentTask\` \u975E\u7A7A\uFF0C\u5148\u8FD0\u884C \`mancode workflow show <taskId> --json\`\uFF0C\u4E0D\u8981\u5148\u6E05\u7A7A state\u3002
1081
+ 2. \u6309\u4E0B\u65B9\u8FB9\u754C\u89C4\u5219\u5B89\u5168\u5904\u7406 active workflow\uFF1B\u7EC8\u6001 workflow \u4E0D\u518D\u6539\u5199\u72B6\u6001\u3002
1082
+ 3. \u53EA\u6709\u4E0D\u518D\u9057\u7559 active workflow \u540E\uFF0C\u624D\u7528 Edit \u66F4\u65B0\u4EE5\u4E0B\u5B57\u6BB5\uFF1A
1177
1083
  - \`currentMode\` \u2192 \`"solo"\`
1178
1084
  - \`lastMode\` \u2190 \u539F \`currentMode\`\uFF08\u4FDD\u7559\u4E0A\u4E00\u4E2A\u6A21\u5F0F\u540D\u4F5C\u4E3A\u8BB0\u5F55\uFF09
1179
1085
  - \`currentTask\` \u2192 \`null\`
1180
1086
  - \`currentWorkflowMode\` \u2192 \`null\`
1181
1087
  - \`skippedSteps\` \u2192 \`[]\`
1182
- 3. \u4E0D\u6539 \`initializedAt\`\u3001\`techStack\`\u3001\`uiLibrary\`\u3001\`teamModeAutoDetected\`\u3001\`contributors\`
1088
+ 4. \u4E0D\u6539 \`initializedAt\`\u3001\`techStack\`\u3001\`uiLibrary\`\u3001\`teamModeAutoDetected\`\u3001\`contributors\`
1183
1089
 
1184
1090
  ## \u8F93\u51FA
1185
1091
 
@@ -1195,7 +1101,8 @@ var MANSOLO_SKILL = {
1195
1101
 
1196
1102
  - **\u6CA1\u6709 .mancode/state.json**\uFF1A\u544A\u8BC9\u7528\u6237\uFF1A"\u9879\u76EE\u672A\u521D\u59CB\u5316\uFF0C\u8FD0\u884C \`mancode init\`\u3002"
1197
1103
  - **state.json \u5DF2\u662F solo**\uFF1A\u4ECD\u6B63\u5E38\u6267\u884C\uFF0C\u544A\u77E5\u7528\u6237\u5F53\u524D\u5DF2\u662F solo\u3002
1198
- - **\u5F53\u524D\u5728 /man /man8 \u6D41\u7A0B\u4E2D**\uFF1A\u7528 AskUserQuestion \u95EE\u7528\u6237\uFF1A
1104
+ - **\u5F53\u524D task \u5DF2\u662F completed/abandoned**\uFF1A\u4E0D\u8981\u5C1D\u8BD5\u628A\u7EC8\u6001\u6539\u6210 abandoned\uFF1B\u76F4\u63A5\u6E05\u7406 state\u3002\u82E5\u5B83\u662F\u5173\u8054 mamba \u4E14\u7236\u4EFB\u52A1\u4ECD active\uFF0C\u5148\u5C55\u793A\u7236\u4EFB\u52A1\u5E76\u6309\u4E0B\u4E00\u6761\u786E\u8BA4\u662F\u5426\u4E00\u5E76\u653E\u5F03\u3002
1105
+ - **\u5F53\u524D\u5728 active /man /mamba /manteam \u6D41\u7A0B\u4E2D**\uFF1A\u7528 AskUserQuestion \u95EE\u7528\u6237\uFF1A
1199
1106
  \`\`\`
1200
1107
  AskUserQuestion({
1201
1108
  questions: [{
@@ -1210,10 +1117,12 @@ var MANSOLO_SKILL = {
1210
1117
  })
1211
1118
  \`\`\`
1212
1119
  \u7528\u6237\u786E\u8BA4\u540E\uFF1A
1213
- - \u7528 Edit \u66F4\u65B0 workflow \u7684 \`metadata.json\`\uFF1A\`status: "abandoned"\`
1214
- - \u518D\u66F4\u65B0 state.json \u5207\u56DE solo
1120
+ - \u5148\u7528 \`mancode workflow show <taskId> --json\` \u68C0\u67E5\u7236\u5B50\u5173\u7CFB\u548C activeChildren\u3002
1121
+ - \u82E5\u5B58\u5728\u6D3B\u8DC3\u5B50\u4EFB\u52A1\u6216\u5F53\u524D\u662F\u5E26 parentTaskId \u7684 mamba\uFF0C\u660E\u786E\u8BF4\u660E\u5C06\u653E\u5F03\u7684 workflow \u94FE\uFF1B\u5148\u9010\u4E2A\u7528 CLI \u6807\u8BB0\u5B50\u4EFB\u52A1 abandoned\uFF0C\u518D\u6807\u8BB0\u7236/\u5F53\u524D\u4EFB\u52A1 abandoned\u3002
1122
+ - \u6240\u6709 metadata \u72B6\u6001\u90FD\u5FC5\u987B\u901A\u8FC7 \`mancode workflow update <taskId> --status abandoned\` \u66F4\u65B0\uFF1BCLI \u62D2\u7EDD\u65F6\u505C\u6B62\uFF0C\u4E0D\u5F97\u76F4\u63A5 Edit \u7ED5\u8FC7\u3002
1123
+ - \u6210\u529F\u540E\u518D\u66F4\u65B0 state.json \u5207\u56DE solo\uFF1BCLI \u4F1A\u540C\u6B65\u6E05\u7406 Active Plans\u3002
1215
1124
 
1216
- \u6536\u5230\u89E6\u53D1\u7ACB\u5373\u6267\u884C\uFF0C\u4E0D\u95EE\u4EFB\u4F55\u989D\u5916\u95EE\u9898\u3002`
1125
+ \u6536\u5230\u89E6\u53D1\u7ACB\u5373\u68C0\u67E5\u72B6\u6001\uFF1B\u9664\u653E\u5F03 active workflow \u6240\u9700\u7684\u786E\u8BA4\u5916\uFF0C\u4E0D\u95EE\u989D\u5916\u95EE\u9898\u3002`
1217
1126
  };
1218
1127
 
1219
1128
  // src/templates/skills/manteam.ts
@@ -1222,7 +1131,9 @@ var MANTEAM_SKILL = {
1222
1131
  description: "Team workflow for shared repositories. Adds contributor context, coordination notes, commit discipline, and hand-off summaries to the full /man flow.",
1223
1132
  body: `# mancode \xB7 /manteam (Team Game)
1224
1133
 
1225
- \u7528\u6237\u7528 \`/manteam <task>\` \u89E6\u53D1\u4F60\u3002\u8FD9\u662F\u591A\u4EBA\u534F\u4F5C\u7248\u7684\u5B8C\u6574\u5DE5\u4F5C\u6D41\uFF1A\u5148\u7406\u89E3\u56E2\u961F\u4E0A\u4E0B\u6587\uFF0C\u518D\u6309 /man \u7684 8 \u6B65\u6D41\u7A0B\u63A8\u8FDB\u3002
1134
+ \u7528\u6237\u7528 \`/manteam <task>\` \u89E6\u53D1\u4F60\u3002\u8FD9\u662F\u591A\u4EBA\u534F\u4F5C\u7248\u7684\u5B8C\u6574\u5DE5\u4F5C\u6D41\uFF1A\u5148\u7406\u89E3\u56E2\u961F\u4E0A\u4E0B\u6587\uFF0C\u518D\u6309 /man \u7684 9 \u6B65\u6D41\u7A0B\u63A8\u8FDB\u3002
1135
+
1136
+ \u7528 \`mancode workflow create manteam "<task>" --json\` \u521B\u5EFA workflow\uFF0C\u5E76\u5C06 state \u6307\u5411\u8FD4\u56DE\u7684 taskId\u3002\u540E\u7EED step/status/planVersion/skippedSteps \u5168\u90E8\u4F7F\u7528 \`mancode workflow update\`\uFF1B\u4E0D\u5F97\u76F4\u63A5\u6539 metadata.json\u3002\u82E5\u5DF2\u6709 active workflow\uFF0C\u5148\u8BA9\u7528\u6237\u9009\u62E9\u6062\u590D\u6216\u653E\u5F03\uFF0C\u4E0D\u80FD\u6E05\u7A7A\u65E7\u6307\u9488\u3002
1226
1137
 
1227
1138
  ## \u9002\u7528\u573A\u666F
1228
1139
 
@@ -1253,21 +1164,21 @@ var MANTEAM_SKILL = {
1253
1164
  - files likely to touch
1254
1165
  - coordination risks
1255
1166
 
1256
- ## Step 1-8: Run Playoffs Flow
1167
+ ## Step 1-9: Run Progressive /man Flow
1257
1168
 
1258
- \u6309 \`/man\` \u7684 8 \u6B65\u6D41\u7A0B\u6267\u884C\uFF0C\u4F46\u6BCF\u4E00\u6B65\u589E\u52A0\u56E2\u961F\u7EA6\u675F\uFF1A
1169
+ \u6309 \`/man\` \u7684 9 \u6B65\u6D41\u7A0B\u6267\u884C\uFF08\u542B\u6F84\u6E05\u3001\u8BA1\u5212\u5173\u5361\u548C\u589E\u5F3A\u6536\u5C3E\uFF09\uFF0C\u4F46\u6BCF\u4E00\u6B65\u589E\u52A0\u56E2\u961F\u7EA6\u675F\uFF1A
1259
1170
 
1260
1171
  1. Scout Report\uFF1A\u5FC5\u987B\u5217\u51FA\u5171\u4EAB\u6587\u4EF6\u3001\u8FD1\u671F\u76F8\u5173\u63D0\u4EA4\u3001\u6F5C\u5728\u51B2\u7A81\u6587\u4EF6\u3002
1261
- 2. Game Plan\uFF1A\u8BA1\u5212\u91CC\u5FC5\u987B\u5305\u542B\u53D8\u66F4\u8FB9\u754C\u3001\u517C\u5BB9\u6027\u98CE\u9669\u3001\u56DE\u6EDA\u65B9\u5F0F\u3002
1262
- - Plan Coach \u5199\u8BA1\u5212\uFF1A\u53EA\u8FD4\u56DE\u8BA1\u5212\u6587\u672C\uFF0C\u7981\u6B62\u63D0\u524D\u4FEE\u6539\u4E1A\u52A1\u6587\u4EF6\u6216\u56E2\u961F memory\u3002
1263
- 3. Tip-off\uFF1A\u6539\u52A8\u524D\u518D\u6B21\u68C0\u67E5 \`git status --short\`\uFF0C\u907F\u514D\u8E29\u7528\u6237\u6216\u961F\u53CB\u6539\u52A8\u3002
1264
- 4. 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
1265
- 5. 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\u3002
1266
- 6. Halftime Fix\uFF1A\u53EA\u4FEE plan \u548C film \u6307\u51FA\u7684\u5185\u5BB9\u3002
1267
- 7. 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
1268
- 8. Post-game\uFF1A\u751F\u6210 hand-off summary\u3002
1172
+ 2. \u6F84\u6E05\uFF1A\u6700\u591A\u4E24\u8F6E\uFF1B\u628A\u5DF2\u786E\u8BA4\u7EA6\u675F\u548C\u4FDD\u5B88\u9ED8\u8BA4\u503C\u5199\u5165 requirements.md\u3002
1173
+ 3. Game Plan\uFF1A\u8BA1\u5212\u91CC\u5FC5\u987B\u5305\u542B\u53D8\u66F4\u8FB9\u754C\u3001\u517C\u5BB9\u6027\u98CE\u9669\u3001\u56DE\u6EDA\u65B9\u5F0F\uFF1BPlan Coach \u53EA\u8FD4\u56DE\u8BA1\u5212\u6587\u672C\uFF0C\u7981\u6B62\u63D0\u524D\u4FEE\u6539\u4E1A\u52A1\u6587\u4EF6\u6216\u56E2\u961F memory\u3002
1174
+ 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
+ 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
+ 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
1269
1180
 
1270
- \u5982\u679C\u7528\u6237\u5728\u786E\u8BA4\u9636\u6BB5\u9009\u62E9"\u9000\u51FA"\u6216\u653E\u5F03\u56E2\u961F workflow\uFF1A\u6807\u8BB0\u5F53\u524D workflow \`metadata.json.status = "abandoned"\`\uFF0C\u5E76\u7528 Edit \u66F4\u65B0 \`.mancode/state.json\`\uFF1A\`currentMode: "solo"\`, \`lastMode: "manteam"\`, \`currentTask: null\`, \`currentWorkflowMode: null\`, \`skippedSteps: []\`\u3002\u4E0D\u8981\u628A abandoned workflow \u7559\u5728 active state\u3002
1181
+ \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
1271
1182
 
1272
1183
  ## Team Hand-off Summary
1273
1184
 
@@ -1314,8 +1225,8 @@ ${CORE_CODING_PRINCIPLES}
1314
1225
 
1315
1226
  // src/templates/skills/index.ts
1316
1227
  var MVP2_SKILLS = [
1317
- MAN8_SKILL,
1318
1228
  MAN_SKILL,
1229
+ MAMBA_SKILL,
1319
1230
  MANTEAM_SKILL,
1320
1231
  MANPS_SKILL,
1321
1232
  MANSOLO_SKILL
@@ -1331,11 +1242,11 @@ ${spec.body}
1331
1242
  }
1332
1243
 
1333
1244
  // src/installers/common.ts
1334
- import { chmod, mkdir as mkdir2, readFile, stat, writeFile as writeFile2 } from "fs/promises";
1245
+ import { chmod, mkdir as mkdir2, readFile as readFile2, stat, writeFile as writeFile2 } from "fs/promises";
1335
1246
  import path2 from "path";
1336
1247
 
1337
1248
  // src/system/team-memory.ts
1338
- import { appendFile, mkdir, writeFile } from "fs/promises";
1249
+ import { appendFile, mkdir, readFile, writeFile } from "fs/promises";
1339
1250
  import path from "path";
1340
1251
  var MEMORY_FILES = {
1341
1252
  prd: {
@@ -1381,6 +1292,54 @@ async function ensureTeamMemory(projectRoot) {
1381
1292
  ]);
1382
1293
  return { dir, files };
1383
1294
  }
1295
+ async function upsertActivePlan(projectRoot, entry) {
1296
+ const summary = await ensureTeamMemory(projectRoot);
1297
+ const updatedAt = (entry.updatedAt ?? /* @__PURE__ */ new Date()).toISOString();
1298
+ const line = `- ${entry.taskId} | ${entry.status} | plan v${entry.planVersion} | .mancode/workflows/${entry.taskId}/plan.md | ${updatedAt}`;
1299
+ const raw = await readFile(summary.files.spec, "utf-8");
1300
+ const heading = "## Active Plans";
1301
+ const start = raw.indexOf(heading);
1302
+ const isActive = entry.status === "in_progress" || entry.status === "planned";
1303
+ if (start === -1) {
1304
+ if (!isActive) return;
1305
+ await writeFile(
1306
+ summary.files.spec,
1307
+ `${raw.trimEnd()}
1308
+
1309
+ ${heading}
1310
+
1311
+ ${line}
1312
+ `,
1313
+ "utf-8"
1314
+ );
1315
+ return;
1316
+ }
1317
+ const afterHeading = start + heading.length;
1318
+ const nextHeading = raw.indexOf("\n## ", afterHeading);
1319
+ const end = nextHeading === -1 ? raw.length : nextHeading;
1320
+ const section = raw.slice(afterHeading, end);
1321
+ const entryPattern = new RegExp(
1322
+ `^- ${escapeRegExp(entry.taskId)} \\|.*(?:\\n|$)`,
1323
+ "m"
1324
+ );
1325
+ if (!isActive) {
1326
+ await writeFile(
1327
+ summary.files.spec,
1328
+ `${raw.slice(0, afterHeading)}${section.replace(entryPattern, "")}${raw.slice(end)}`,
1329
+ "utf-8"
1330
+ );
1331
+ return;
1332
+ }
1333
+ const replaced = section.replace(entryPattern, line);
1334
+ const nextSection = replaced === section ? `${section.trimEnd()}
1335
+ ${line}
1336
+ ` : replaced;
1337
+ await writeFile(
1338
+ summary.files.spec,
1339
+ `${raw.slice(0, afterHeading)}${nextSection}${raw.slice(end)}`,
1340
+ "utf-8"
1341
+ );
1342
+ }
1384
1343
  function memoryDir(projectRoot) {
1385
1344
  return path.join(projectRoot, ".mancode", "memory");
1386
1345
  }
@@ -1397,6 +1356,9 @@ function renderMemoryFile(title, body) {
1397
1356
  ${body}
1398
1357
  `;
1399
1358
  }
1359
+ function escapeRegExp(value) {
1360
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1361
+ }
1400
1362
 
1401
1363
  // src/templates/defaults.ts
1402
1364
  var DEFAULT_CONFIG = {
@@ -1463,7 +1425,7 @@ async function installMancodeCore(projectRoot) {
1463
1425
  }
1464
1426
  async function readTextIfExists(filePath) {
1465
1427
  try {
1466
- return await readFile(filePath, "utf-8");
1428
+ return await readFile2(filePath, "utf-8");
1467
1429
  } catch (err) {
1468
1430
  if (isNodeError(err) && err.code === "ENOENT") return "";
1469
1431
  throw err;
@@ -1490,6 +1452,14 @@ function isNodeError(err) {
1490
1452
  }
1491
1453
 
1492
1454
  // src/installers/claude-code.ts
1455
+ var CLAUDE_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:claude-skill. Do not edit this marker. -->";
1456
+ var CLAUDE_AGENT_MANAGED_MARKER = "<!-- Managed by mancode:claude-agent. Do not edit this marker. -->";
1457
+ var LEGACY_CLAUDE_SKILL_SETTINGS = {
1458
+ solo: ".claude/skills/mancode-solo.md",
1459
+ man8: ".claude/skills/mancode-man8.md",
1460
+ man: ".claude/skills/mancode-man.md",
1461
+ mansolo: ".claude/skills/mancode-mansolo.md"
1462
+ };
1493
1463
  async function validateClaudeCodeSettings(projectRoot) {
1494
1464
  await readClaudeSettings(path3.join(projectRoot, ".claude"));
1495
1465
  }
@@ -1499,7 +1469,7 @@ async function installClaudeCode(projectRoot, options) {
1499
1469
  const force = options.force ?? false;
1500
1470
  await installMancodeCore(projectRoot);
1501
1471
  await mkdir3(path3.join(claudeDir, "skills"), { recursive: true });
1502
- await installSoloSkill(path3.join(claudeDir, "skills"));
1472
+ await installSoloSkill(path3.join(claudeDir, "skills"), force);
1503
1473
  if (options.minimal) {
1504
1474
  await uninstallMvp2Skills(path3.join(claudeDir, "skills"));
1505
1475
  } else {
@@ -1512,44 +1482,59 @@ async function installClaudeCode(projectRoot, options) {
1512
1482
  }
1513
1483
  await updateClaudeSettings(claudeDir, settings);
1514
1484
  }
1515
- async function installSoloSkill(skillsDir) {
1516
- await installProjectSkill(skillsDir, {
1517
- name: "solo",
1518
- description: "Default mancode solo-mode guidance for small, focused tasks.",
1519
- body: SOLO_SKILL
1520
- });
1521
- await removeLegacyFlatSkill(skillsDir, "mancode-solo");
1485
+ async function installSoloSkill(skillsDir, force) {
1486
+ await installProjectSkill(
1487
+ skillsDir,
1488
+ {
1489
+ name: "solo",
1490
+ description: "Default mancode solo-mode guidance for small, focused tasks.",
1491
+ body: SOLO_SKILL
1492
+ },
1493
+ force
1494
+ );
1495
+ await removeLegacyFlatSkill(skillsDir, "mancode-solo", "solo");
1522
1496
  }
1523
1497
  async function installMvp2Skills(skillsDir, force) {
1498
+ await removeManagedLegacyMan8Skill(skillsDir);
1524
1499
  for (const skill of MVP2_SKILLS) {
1525
1500
  await installProjectSkill(skillsDir, skill, force);
1526
- await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`);
1501
+ await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`, skill.name);
1527
1502
  }
1528
1503
  }
1504
+ async function removeManagedLegacyMan8Skill(skillsDir) {
1505
+ await removeGeneratedSkillDir(skillsDir, "man8");
1506
+ }
1529
1507
  async function uninstallMvp2Skills(skillsDir) {
1530
1508
  for (const skill of MVP2_SKILLS) {
1531
- await rm(path3.join(skillsDir, skill.name), {
1532
- recursive: true,
1533
- force: true
1534
- });
1535
- await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`);
1509
+ await removeGeneratedSkillDir(skillsDir, skill.name);
1510
+ await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`, skill.name);
1536
1511
  }
1537
1512
  }
1538
1513
  async function installProjectSkill(skillsDir, skill, force = true) {
1539
1514
  const skillDir = path3.join(skillsDir, skill.name);
1540
1515
  const skillPath = path3.join(skillDir, "SKILL.md");
1541
- if (!force) {
1542
- try {
1543
- await access(skillPath);
1544
- return;
1545
- } catch {
1546
- }
1516
+ const existing = await readTextIfExists2(skillPath);
1517
+ if (existing !== null && !force) {
1518
+ return;
1519
+ }
1520
+ if (existing !== null && force && !isGeneratedClaudeSkill(existing, skill.name)) {
1521
+ throw new Error(
1522
+ `refusing to overwrite user-authored Claude Code skill: ${skillPath}`
1523
+ );
1547
1524
  }
1548
1525
  await mkdir3(skillDir, { recursive: true });
1549
- await writeFile3(skillPath, renderSkill(skill), "utf-8");
1526
+ await writeFile3(
1527
+ skillPath,
1528
+ addManagedMarker(renderSkill(skill), CLAUDE_SKILL_MANAGED_MARKER),
1529
+ "utf-8"
1530
+ );
1550
1531
  }
1551
- async function removeLegacyFlatSkill(skillsDir, legacyName) {
1552
- await rm(path3.join(skillsDir, `${legacyName}.md`), { force: true });
1532
+ async function removeLegacyFlatSkill(skillsDir, legacyName, modeName) {
1533
+ const legacyPath = path3.join(skillsDir, `${legacyName}.md`);
1534
+ const content = await readTextIfExists2(legacyPath);
1535
+ if (content && isGeneratedClaudeSkill(content, modeName)) {
1536
+ await rm(legacyPath, { force: true });
1537
+ }
1553
1538
  }
1554
1539
  async function installAgents(agentsDir, force) {
1555
1540
  await mkdir3(agentsDir, { recursive: true });
@@ -1562,19 +1547,84 @@ async function installAgents(agentsDir, force) {
1562
1547
  } catch {
1563
1548
  }
1564
1549
  }
1565
- const content = renderAgent(agent);
1550
+ const existing = await readTextIfExists2(agentPath);
1551
+ if (existing !== null && force && !isGeneratedClaudeAgent(existing, agent.name)) {
1552
+ throw new Error(
1553
+ `refusing to overwrite user-authored Claude Code agent: ${agentPath}`
1554
+ );
1555
+ }
1556
+ const content = addManagedMarker(
1557
+ renderAgent(agent),
1558
+ CLAUDE_AGENT_MANAGED_MARKER
1559
+ );
1566
1560
  await writeFile3(agentPath, content, "utf-8");
1567
1561
  }
1568
1562
  }
1569
1563
  async function uninstallAgents(agentsDir) {
1570
1564
  for (const agent of ALL_AGENTS) {
1571
- await rm(path3.join(agentsDir, `${agent.name}.md`), { force: true });
1565
+ const agentPath = path3.join(agentsDir, `${agent.name}.md`);
1566
+ const content = await readTextIfExists2(agentPath);
1567
+ if (content && isGeneratedClaudeAgent(content, agent.name)) {
1568
+ await rm(agentPath, { force: true });
1569
+ }
1570
+ }
1571
+ }
1572
+ async function removeClaudeGeneratedContent(projectRoot) {
1573
+ const claudeDir = path3.join(projectRoot, ".claude");
1574
+ const skillsDir = path3.join(claudeDir, "skills");
1575
+ for (const modeName of [
1576
+ "solo",
1577
+ "man8",
1578
+ ...MVP2_SKILLS.map((skill) => skill.name)
1579
+ ]) {
1580
+ await removeGeneratedSkillDir(skillsDir, modeName);
1581
+ await removeLegacyFlatSkill(skillsDir, `mancode-${modeName}`, modeName);
1582
+ }
1583
+ await uninstallAgents(path3.join(claudeDir, "agents"));
1584
+ }
1585
+ async function removeGeneratedSkillDir(skillsDir, modeName) {
1586
+ const skillDir = path3.join(skillsDir, modeName);
1587
+ const skillPath = path3.join(skillDir, "SKILL.md");
1588
+ const content = await readTextIfExists2(skillPath);
1589
+ if (!content || !isGeneratedClaudeSkill(content, modeName)) return;
1590
+ await rm(skillPath, { force: true });
1591
+ await removeIfEmpty(skillDir);
1592
+ }
1593
+ function isGeneratedClaudeSkill(content, modeName) {
1594
+ if (content.includes(CLAUDE_SKILL_MANAGED_MARKER)) return true;
1595
+ if (!content.includes(`name: ${modeName}`)) return false;
1596
+ const heading = modeName === "solo" ? "# mancode \xB7 solo mode" : modeName === "man8" ? "# mancode \xB7 /man8 (4 AM Warmup)" : `# mancode \xB7 /${modeName}`;
1597
+ return content.includes(heading);
1598
+ }
1599
+ function isGeneratedClaudeAgent(content, name) {
1600
+ return content.includes(CLAUDE_AGENT_MANAGED_MARKER) || content.includes(`name: ${name}`) && content.includes("mancode \u6559\u7EC3\u7EC4");
1601
+ }
1602
+ function addManagedMarker(content, marker) {
1603
+ return content.replace("---\n\n", `---
1604
+
1605
+ ${marker}
1606
+
1607
+ `);
1608
+ }
1609
+ async function readTextIfExists2(filePath) {
1610
+ try {
1611
+ return await readFile3(filePath, "utf-8");
1612
+ } catch {
1613
+ return null;
1614
+ }
1615
+ }
1616
+ async function removeIfEmpty(dir) {
1617
+ try {
1618
+ if ((await readdir(dir)).length === 0) {
1619
+ await rm(dir, { recursive: true, force: true });
1620
+ }
1621
+ } catch {
1572
1622
  }
1573
1623
  }
1574
1624
  async function readClaudeSettings(claudeDir) {
1575
1625
  const settingsPath = path3.join(claudeDir, "settings.json");
1576
1626
  try {
1577
- const raw = await readFile2(settingsPath, "utf-8");
1627
+ const raw = await readFile3(settingsPath, "utf-8");
1578
1628
  return JSON.parse(raw);
1579
1629
  } catch (err) {
1580
1630
  if (isNodeError2(err) && err.code === "ENOENT") {
@@ -1604,16 +1654,10 @@ async function updateClaudeSettings(claudeDir, settings) {
1604
1654
  }
1605
1655
  function removeLegacyMancodeSkillSettings(settings) {
1606
1656
  if (!settings.skills) return;
1607
- const legacyNames = /* @__PURE__ */ new Set([
1608
- "solo",
1609
- "man8",
1610
- "man",
1611
- "manteam",
1612
- "manps",
1613
- "mansolo"
1614
- ]);
1615
1657
  const retained = Object.fromEntries(
1616
- Object.entries(settings.skills).filter(([name]) => !legacyNames.has(name))
1658
+ Object.entries(settings.skills).filter(
1659
+ ([name, value]) => LEGACY_CLAUDE_SKILL_SETTINGS[name] !== value
1660
+ )
1617
1661
  );
1618
1662
  settings.skills = Object.keys(retained).length > 0 ? retained : void 0;
1619
1663
  }
@@ -1774,17 +1818,19 @@ function findMarkerLine(content, marker) {
1774
1818
  }
1775
1819
 
1776
1820
  // src/installers/mode-skills.ts
1777
- import { mkdir as mkdir4, readFile as readFile3, readdir, rm as rm2, writeFile as writeFile4 } from "fs/promises";
1821
+ import { mkdir as mkdir4, readFile as readFile4, readdir as readdir2, rm as rm2, writeFile as writeFile4 } from "fs/promises";
1778
1822
  import path4 from "path";
1779
1823
  var MODE_NAMES = [
1780
- "man8",
1824
+ "mamba",
1781
1825
  "man",
1782
1826
  "manteam",
1783
1827
  "manps",
1784
1828
  "mansolo"
1785
1829
  ];
1830
+ var LEGACY_MODE_NAMES = ["man8"];
1786
1831
  var CODEX_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:codex-skill. Do not edit this file manually. -->";
1787
1832
  var ZCODE_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:zcode-skill. Do not edit this file manually. -->";
1833
+ var MODE_FILE_MANAGED_MARKER = "<!-- Managed by mancode:mode-file. Do not edit this file manually. -->";
1788
1834
  var MANCODE_AGENT_SKILL_MARKERS = [
1789
1835
  CODEX_SKILL_MANAGED_MARKER,
1790
1836
  ZCODE_SKILL_MANAGED_MARKER
@@ -1819,8 +1865,9 @@ function renderModeSkill(mode, commandPrefix = "$") {
1819
1865
  "For every task, consider: why this change, what already exists, and what",
1820
1866
  "is the smallest useful diff?",
1821
1867
  "",
1822
- "For UI tasks, read `.mancode/aesthetics/style-tokens.json` for current",
1823
- "design tokens before inventing new styles.",
1868
+ "Read `.mancode/project-profile.json` before choosing tools or validation.",
1869
+ "Only for a UI task in a profile with detected UI assets, read",
1870
+ "`.mancode/aesthetics/style-tokens.json` before inventing new styles.",
1824
1871
  "",
1825
1872
  meta.workflow
1826
1873
  ].join("\n");
@@ -1831,6 +1878,7 @@ async function installCodexSkills(projectRoot, minimal) {
1831
1878
  return;
1832
1879
  }
1833
1880
  const skillsDir = path4.join(projectRoot, ".agents", "skills");
1881
+ await removeLegacyManagedSkills(skillsDir, MANCODE_AGENT_SKILL_MARKERS);
1834
1882
  for (const mode of MODE_NAMES) {
1835
1883
  const modeDir = path4.join(skillsDir, mode);
1836
1884
  await mkdir4(modeDir, { recursive: true });
@@ -1861,6 +1909,7 @@ async function installZcodeSkills(projectRoot, minimal) {
1861
1909
  return;
1862
1910
  }
1863
1911
  const skillsDir = path4.join(projectRoot, ".agents", "skills");
1912
+ await removeLegacyManagedSkills(skillsDir, MANCODE_AGENT_SKILL_MARKERS);
1864
1913
  for (const mode of MODE_NAMES) {
1865
1914
  const modeDir = path4.join(skillsDir, mode);
1866
1915
  await mkdir4(modeDir, { recursive: true });
@@ -1892,6 +1941,7 @@ async function installCursorCommands(projectRoot, minimal) {
1892
1941
  }
1893
1942
  const commandsDir = path4.join(projectRoot, ".cursor", "commands");
1894
1943
  await mkdir4(commandsDir, { recursive: true });
1944
+ await removeLegacyGeneratedModeFile(path4.join(commandsDir, "man8.md"));
1895
1945
  for (const mode of MODE_NAMES) {
1896
1946
  const meta = MODE_META[mode];
1897
1947
  const content = [
@@ -1899,10 +1949,17 @@ async function installCursorCommands(projectRoot, minimal) {
1899
1949
  `description: ${JSON.stringify(meta.description)}`,
1900
1950
  "---",
1901
1951
  "",
1952
+ MODE_FILE_MANAGED_MARKER,
1953
+ "",
1902
1954
  renderModeSkill(mode, "/"),
1903
1955
  ""
1904
1956
  ].join("\n");
1905
- await writeFile4(path4.join(commandsDir, `${mode}.md`), content, "utf-8");
1957
+ await writeManagedModeFile(
1958
+ path4.join(commandsDir, `${mode}.md`),
1959
+ content,
1960
+ mode,
1961
+ "Cursor"
1962
+ );
1906
1963
  }
1907
1964
  }
1908
1965
  async function installCopilotPrompts(projectRoot, minimal) {
@@ -1912,6 +1969,7 @@ async function installCopilotPrompts(projectRoot, minimal) {
1912
1969
  }
1913
1970
  const promptsDir = path4.join(projectRoot, ".github", "prompts");
1914
1971
  await mkdir4(promptsDir, { recursive: true });
1972
+ await removeLegacyGeneratedModeFile(path4.join(promptsDir, "man8.prompt.md"));
1915
1973
  for (const mode of MODE_NAMES) {
1916
1974
  const meta = MODE_META[mode];
1917
1975
  const content = [
@@ -1920,64 +1978,114 @@ async function installCopilotPrompts(projectRoot, minimal) {
1920
1978
  `description: ${JSON.stringify(meta.description)}`,
1921
1979
  "---",
1922
1980
  "",
1981
+ MODE_FILE_MANAGED_MARKER,
1982
+ "",
1923
1983
  renderModeSkill(mode, ""),
1924
1984
  ""
1925
1985
  ].join("\n");
1926
- await writeFile4(
1986
+ await writeManagedModeFile(
1927
1987
  path4.join(promptsDir, `${mode}.prompt.md`),
1928
1988
  content,
1929
- "utf-8"
1989
+ mode,
1990
+ "GitHub Copilot"
1930
1991
  );
1931
1992
  }
1932
1993
  }
1933
1994
  async function removeCodexSkills(projectRoot) {
1934
- await cleanCodexManagedSkills(path4.join(projectRoot, ".agents", "skills"));
1935
- await cleanCodexManagedSkills(path4.join(projectRoot, ".codex", "skills"));
1995
+ if (!await platformNeedsSharedSkills(projectRoot, "zcode")) {
1996
+ await cleanManagedSkills(
1997
+ path4.join(projectRoot, ".agents", "skills"),
1998
+ MANCODE_AGENT_SKILL_MARKERS
1999
+ );
2000
+ }
2001
+ await cleanManagedSkills(path4.join(projectRoot, ".codex", "skills"), [
2002
+ CODEX_SKILL_MANAGED_MARKER
2003
+ ]);
1936
2004
  }
1937
- async function cleanCodexManagedSkills(skillsDir) {
1938
- for (const mode of MODE_NAMES) {
2005
+ async function cleanManagedSkills(skillsDir, markers) {
2006
+ for (const mode of [...MODE_NAMES, ...LEGACY_MODE_NAMES]) {
1939
2007
  const modeDir = path4.join(skillsDir, mode);
1940
2008
  const skillPath = path4.join(modeDir, "SKILL.md");
1941
- if (await isManagedSkillFile(skillPath, CODEX_SKILL_MANAGED_MARKER)) {
2009
+ if (await isManagedByAnyMarker(skillPath, markers)) {
1942
2010
  await rm2(skillPath, { force: true });
1943
- await removeIfEmpty(modeDir);
2011
+ await removeIfEmpty2(modeDir);
1944
2012
  }
1945
2013
  }
1946
- await removeIfEmpty(skillsDir);
2014
+ await removeIfEmpty2(skillsDir);
1947
2015
  }
1948
2016
  async function removeZcodeSkills(projectRoot) {
1949
- await cleanZcodeManagedSkills(path4.join(projectRoot, ".agents", "skills"));
1950
- await cleanZcodeManagedSkills(path4.join(projectRoot, ".zcode", "skills"));
1951
- }
1952
- async function cleanZcodeManagedSkills(skillsDir) {
1953
- for (const mode of MODE_NAMES) {
1954
- const modeDir = path4.join(skillsDir, mode);
1955
- const skillPath = path4.join(modeDir, "SKILL.md");
1956
- if (await isManagedSkillFile(skillPath, ZCODE_SKILL_MANAGED_MARKER)) {
1957
- await rm2(skillPath, { force: true });
1958
- await removeIfEmpty(modeDir);
1959
- }
2017
+ if (!await platformNeedsSharedSkills(projectRoot, "codex")) {
2018
+ await cleanManagedSkills(
2019
+ path4.join(projectRoot, ".agents", "skills"),
2020
+ MANCODE_AGENT_SKILL_MARKERS
2021
+ );
1960
2022
  }
1961
- await removeIfEmpty(skillsDir);
2023
+ await cleanManagedSkills(path4.join(projectRoot, ".zcode", "skills"), [
2024
+ ZCODE_SKILL_MANAGED_MARKER
2025
+ ]);
1962
2026
  }
1963
2027
  async function removeCursorCommands(projectRoot) {
1964
- for (const mode of MODE_NAMES) {
1965
- await rm2(path4.join(projectRoot, ".cursor", "commands", `${mode}.md`), {
1966
- force: true
1967
- });
2028
+ for (const mode of [...MODE_NAMES, ...LEGACY_MODE_NAMES]) {
2029
+ const filePath = path4.join(
2030
+ projectRoot,
2031
+ ".cursor",
2032
+ "commands",
2033
+ `${mode}.md`
2034
+ );
2035
+ if (mode === "man8") await removeLegacyGeneratedModeFile(filePath);
2036
+ else await removeManagedModeFile(filePath, mode);
1968
2037
  }
1969
2038
  }
1970
2039
  async function removeCopilotPrompts(projectRoot) {
1971
- for (const mode of MODE_NAMES) {
1972
- await rm2(
1973
- path4.join(projectRoot, ".github", "prompts", `${mode}.prompt.md`),
1974
- { force: true }
2040
+ for (const mode of [...MODE_NAMES, ...LEGACY_MODE_NAMES]) {
2041
+ const filePath = path4.join(
2042
+ projectRoot,
2043
+ ".github",
2044
+ "prompts",
2045
+ `${mode}.prompt.md`
1975
2046
  );
2047
+ if (mode === "man8") await removeLegacyGeneratedModeFile(filePath);
2048
+ else await removeManagedModeFile(filePath, mode);
1976
2049
  }
1977
2050
  }
1978
- async function removeIfEmpty(dir) {
2051
+ async function writeManagedModeFile(filePath, content, mode, platformLabel) {
2052
+ const existing = await readTextIfExists3(filePath);
2053
+ if (existing !== null && !isGeneratedModeFile(existing, mode)) {
2054
+ throw new Error(
2055
+ `refusing to overwrite user-authored ${platformLabel} mode file: ${filePath}`
2056
+ );
2057
+ }
2058
+ await writeFile4(filePath, content, "utf-8");
2059
+ }
2060
+ async function removeManagedModeFile(filePath, mode) {
2061
+ const content = await readTextIfExists3(filePath);
2062
+ if (content && isGeneratedModeFile(content, mode)) {
2063
+ await rm2(filePath, { force: true });
2064
+ }
2065
+ }
2066
+ async function removeLegacyGeneratedModeFile(filePath) {
2067
+ const content = await readTextIfExists3(filePath);
2068
+ if (content?.includes("# mancode man8 \u2014 Investigate and Plan") && content.includes("## Mode Persistence")) {
2069
+ await rm2(filePath, { force: true });
2070
+ }
2071
+ }
2072
+ function isGeneratedModeFile(content, mode) {
2073
+ return content.includes(MODE_FILE_MANAGED_MARKER) || content.includes(`# mancode ${mode}`) && content.includes("## Mode Persistence");
2074
+ }
2075
+ async function removeLegacyManagedSkills(skillsDir, markers) {
2076
+ for (const mode of LEGACY_MODE_NAMES) {
2077
+ const modeDir = path4.join(skillsDir, mode);
2078
+ const skillPath = path4.join(modeDir, "SKILL.md");
2079
+ const content = await readTextIfExists3(skillPath);
2080
+ if (content && markers.some((marker) => content.includes(marker))) {
2081
+ await rm2(skillPath, { force: true });
2082
+ await removeIfEmpty2(modeDir);
2083
+ }
2084
+ }
2085
+ }
2086
+ async function removeIfEmpty2(dir) {
1979
2087
  try {
1980
- const entries = await readdir(dir);
2088
+ const entries = await readdir2(dir);
1981
2089
  if (entries.length === 0) {
1982
2090
  await rm2(dir, { recursive: true, force: true });
1983
2091
  }
@@ -1985,7 +2093,7 @@ async function removeIfEmpty(dir) {
1985
2093
  }
1986
2094
  }
1987
2095
  async function writeManagedSkill(skillPath, content, marker, platformLabel, recognizedMarkers = [marker]) {
1988
- const existing = await readTextIfExists2(skillPath);
2096
+ const existing = await readTextIfExists3(skillPath);
1989
2097
  if (existing !== null && !recognizedMarkers.some((m) => existing.includes(m))) {
1990
2098
  throw new Error(
1991
2099
  `refusing to overwrite user-authored ${platformLabel} skill: ${skillPath}`
@@ -1993,70 +2101,109 @@ async function writeManagedSkill(skillPath, content, marker, platformLabel, reco
1993
2101
  }
1994
2102
  await writeFile4(skillPath, content, "utf-8");
1995
2103
  }
1996
- async function isManagedSkillFile(skillPath, marker) {
1997
- const content = await readTextIfExists2(skillPath);
1998
- return content?.includes(marker) ?? false;
2104
+ async function isManagedByAnyMarker(skillPath, markers) {
2105
+ const content = await readTextIfExists3(skillPath);
2106
+ return content !== null && markers.some((marker) => content.includes(marker));
1999
2107
  }
2000
- async function readTextIfExists2(filePath) {
2108
+ async function platformNeedsSharedSkills(projectRoot, platform) {
2001
2109
  try {
2002
- return await readFile3(filePath, "utf-8");
2110
+ const raw = await readFile4(
2111
+ path4.join(projectRoot, ".mancode", "config.json"),
2112
+ "utf-8"
2113
+ );
2114
+ const config = JSON.parse(raw);
2115
+ if (!Array.isArray(config.platforms) || !config.platforms.includes(platform)) {
2116
+ return false;
2117
+ }
2118
+ if (!isRecord2(config.platformOptions)) return true;
2119
+ const options = config.platformOptions[platform];
2120
+ return !isRecord2(options) || options.minimal !== true;
2121
+ } catch {
2122
+ return false;
2123
+ }
2124
+ }
2125
+ function isRecord2(value) {
2126
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2127
+ }
2128
+ async function readTextIfExists3(filePath) {
2129
+ try {
2130
+ return await readFile4(filePath, "utf-8");
2003
2131
  } catch {
2004
2132
  return null;
2005
2133
  }
2006
2134
  }
2007
2135
  var MODE_META = {
2008
- man8: {
2009
- description: "Investigate first, then produce a plan before implementation. Use when the user asks for man8, planning, architecture, migration, integration, or risk assessment.",
2010
- intro: "# mancode man8 \u2014 Investigate and Plan",
2136
+ mamba: {
2137
+ description: "Diagnose bugs and validate real user flows with targeted regression checks.",
2138
+ intro: "# mancode mamba \u2014 Diagnosis and Real Validation",
2011
2139
  workflow: [
2012
- "## Workflow (simulate in single conversation)",
2140
+ "## Five-step workflow (simulate roles in one conversation)",
2013
2141
  "",
2014
- "This platform does not provide isolated subagents. Simulate the coaching",
2015
- "staff in sequence:",
2142
+ 'Use `mancode workflow create mamba "<task>" --json`; when called from',
2143
+ "a /man or /manteam Step 6 workflow, add `--parent-task <taskId>`. Persist the returned",
2144
+ "taskId in `.mancode/state.json` as currentTask; set currentMode and",
2145
+ "currentWorkflowMode to mamba. Never edit metadata.json directly; use",
2146
+ "`mancode workflow update` for every step and final state.",
2147
+ "If state points to an unrelated active workflow, stop and ask the user to resume it or abandon it through mansolo; never overwrite its pointer.",
2016
2148
  "",
2017
- "1. **Scout**: Investigate the relevant code and summarize constraints,",
2018
- " existing patterns, and dependencies.",
2019
- "2. **Head Coach**: Write a concrete implementation plan based on the",
2020
- " Scout findings.",
2021
- "3. **Stop for user approval** before implementing. Do not write code",
2022
- " until the user confirms.",
2149
+ "1. Define the symptom, environment, expected/actual result, protected data, and success criteria; then run `mancode workflow update <taskId> --step 2`.",
2150
+ "2. Prepare the safest available environment. Full capability may use a detected Web UI and Playwright; command-only capability produces a manual browser checklist; restricted capability records manual_test_required. When ready, update to Step 3.",
2151
+ " Starting local services and read-only probes is allowed. Dependency downloads, credentials, seed/reset, external login, messages, payments, deletion, and production writes require explicit approval.",
2152
+ " If environment, permission, or data is missing, set blocked with `--blocking-reason` and keep state resumable instead of advancing.",
2153
+ "3. Reproduce the shortest path and write `diagnosis.md` with evidence, root cause or candidates, impact, and confidence; then update to Step 4.",
2154
+ "4. Apply only the requested minimum fix, or make no code change when the user requested validation only. New architecture or cross-module scope returns to /man. Then update to Step 5.",
2155
+ "5. Re-run the path and affected positive, negative, and permission boundaries; write `mamba-report.md` with environment, evidence, artifacts, regression scope, and risks.",
2023
2156
  "",
2024
- "After user approval, switch to solo mode (update state.json currentMode",
2025
- 'to "solo") and implement the plan.'
2157
+ "Finish through the CLI with exactly one result: completed+fixed, completed+verified, completed+no_repro, completed+manual_test_required, or blocked+blockingReason. Never claim real testing in restricted mode. Child blocked/manual_test_required propagation is handled by the CLI. After a child finishes with fixed/verified/no_repro, inspect its parent; if that parent is still blocked because of this child, run `mancode workflow update <parentTaskId> --status in_progress` before restoring parent state at Step 6. Never auto-resume manual_test_required."
2026
2158
  ].join("\n")
2027
2159
  },
2028
2160
  man: {
2029
2161
  description: "Full high-risk workflow with plan, implementation, verification, and review. Use when the user asks for man or a high-risk production change.",
2030
2162
  intro: "# mancode man \u2014 Full Review Workflow",
2031
2163
  workflow: [
2032
- "## Workflow (simulate in single conversation)",
2164
+ "## Progressive nine-step workflow (simulate roles in one conversation)",
2165
+ "",
2166
+ 'Create the durable workflow with `mancode workflow create man "<task>" --json`,',
2167
+ "persist its taskId in `.mancode/state.json` as currentTask, set currentMode",
2168
+ "and currentWorkflowMode to man, and use `mancode workflow update`",
2169
+ "for every step/status/plan revision. Never edit metadata.json directly.",
2170
+ "After each completed phase, advance through the CLI to the next numbered step;",
2171
+ "never advance state merely because a report was requested or an agent was called.",
2033
2172
  "",
2034
- "This platform does not provide isolated subagents. Simulate the full",
2035
- "coaching staff workflow in sequence:",
2173
+ "Step 1 \u2014 Scout: inspect the profile, existing implementation, dependencies, risks, and unknowns; write `scout-report.md`.",
2174
+ "Step 2 \u2014 Clarify: ask at most two rounds; write answers, conservative defaults, and unresolved risks to `requirements.md`.",
2175
+ "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
+ "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
+ "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.",
2036
2182
  "",
2037
- "1. **Scout**: Investigate the codebase and summarize constraints.",
2038
- "2. **Head Coach**: Propose a concrete implementation plan.",
2039
- "3. **Wait for approval** when the task is high-risk.",
2040
- "4. **Implement** the plan and self-test.",
2041
- "5. **Film Analyst Offense**: Review readability, DRY, and YAGNI.",
2042
- "6. **Fix** review findings.",
2043
- "7. **Film Analyst Defense**: Review auth, injection, concurrency, and",
2044
- " resource risks.",
2045
- "8. **Summarize** verification results and remaining risk."
2183
+ "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."
2046
2184
  ].join("\n")
2047
2185
  },
2048
2186
  manteam: {
2049
2187
  description: "Team-aware workflow with handoff-friendly summaries. Use when the user asks for manteam or when the task affects shared team context.",
2050
2188
  intro: "# mancode manteam \u2014 Team Workflow",
2051
2189
  workflow: [
2052
- "## Workflow",
2190
+ "## Team workflow",
2053
2191
  "",
2054
- "1. Read `.mancode/memory/prd.md`, `.mancode/memory/spec.md`, and",
2055
- " `.mancode/memory/decisions.md` when relevant.",
2056
- "2. Preserve decisions and leave handoff-friendly summaries.",
2057
- "3. Prefer Conventional Commit style when suggesting commits.",
2058
- "4. Simulate the coaching staff in sequence (same as man mode) but",
2059
- " with extra emphasis on documentation and handoff clarity."
2192
+ 'Create with `mancode workflow create manteam "<task>" --json`; never edit',
2193
+ "metadata.json directly. Persist its taskId as currentTask and set currentMode",
2194
+ "and currentWorkflowMode to manteam. First collect branch, dirty-worktree, contributor,",
2195
+ "shared-module, and handoff risks in `team-context.md`.",
2196
+ "",
2197
+ "Then execute every /man Step 1 through Step 9, including clarification,",
2198
+ "plan gate, validation, two review passes, conditional completion, planVersion,",
2199
+ "Active Plans, and linked mamba child behavior. Add compatibility, rollback,",
2200
+ "ownership, conflict, and handoff notes at each relevant step.",
2201
+ "When a resolved mamba child had blocked the parent, restore the parent through",
2202
+ "`workflow update --status in_progress`; never auto-resume manual_test_required.",
2203
+ "",
2204
+ "Read `.mancode/memory/prd.md`, `spec.md`, and `decisions.md`; append durable",
2205
+ "decisions only after confirmed implementation. Write `handoff.md`. Never",
2206
+ "overwrite teammate changes or commit/merge/push without explicit approval."
2060
2207
  ].join("\n")
2061
2208
  },
2062
2209
  manps: {
@@ -2081,32 +2228,39 @@ var MODE_META = {
2081
2228
  "",
2082
2229
  "You are now in solo (default) mode.",
2083
2230
  "",
2084
- 'Update `.mancode/state.json` and set `currentMode` to "solo".',
2231
+ "If state points to an active workflow, show it with `mancode workflow show",
2232
+ "<taskId> --json`, ask for confirmation, abandon active children before their",
2233
+ "parent through `mancode workflow update ... --status abandoned`, and only",
2234
+ "then update `.mancode/state.json` to solo. Never edit metadata.json directly.",
2085
2235
  "",
2086
2236
  "- Keep the diff narrow.",
2087
2237
  "- Reuse existing functions, components, styles, and dependencies.",
2088
- "- For UI work, read `.mancode/aesthetics/style-tokens.json`.",
2238
+ "- Read project-profile first; for detected UI assets and UI work, read `.mancode/aesthetics/style-tokens.json`.",
2089
2239
  "- Verify with the narrowest meaningful test, lint, build, or smoke check."
2090
2240
  ].join("\n")
2091
2241
  }
2092
2242
  };
2093
2243
 
2094
2244
  // src/installers/shared-content.ts
2095
- import { readFile as readFile4 } from "fs/promises";
2245
+ import { readFile as readFile5 } from "fs/promises";
2096
2246
  import path5 from "path";
2097
2247
  async function generateSharedContent(projectRoot, options) {
2098
- const [state, tokens] = await Promise.all([
2248
+ const [state, tokens, profile] = await Promise.all([
2099
2249
  readJson(
2100
2250
  path5.join(projectRoot, ".mancode", "state.json")
2101
2251
  ),
2102
2252
  readJson(
2103
2253
  path5.join(projectRoot, ".mancode", "aesthetics", "style-tokens.json")
2254
+ ),
2255
+ readJson(
2256
+ path5.join(projectRoot, ".mancode", "project-profile.json")
2104
2257
  )
2105
2258
  ]);
2259
+ const currentProfile = options.projectProfile ?? profile;
2106
2260
  const sections = [
2107
- renderProjectContext(state, tokens, options),
2261
+ renderProjectContext(state, tokens, profile, options),
2108
2262
  renderPracticeRules(),
2109
- renderAesthetics(tokens)
2263
+ currentProfile?.uiAssets === "detected" ? renderAesthetics(tokens) : ""
2110
2264
  ];
2111
2265
  if (!options.minimal) {
2112
2266
  sections.push(renderModes(options), renderPlatformDowngrade(options));
@@ -2114,9 +2268,10 @@ async function generateSharedContent(projectRoot, options) {
2114
2268
  return `${sections.filter(Boolean).join("\n\n")}
2115
2269
  `;
2116
2270
  }
2117
- function renderProjectContext(state, tokens, options) {
2118
- const techStack = state?.techStack || options.techStack.join(" + ") || "Unknown";
2119
- const uiLibrary = state?.uiLibrary || tokens?.uiLibrary || options.uiLibrary || "None";
2271
+ function renderProjectContext(state, tokens, profile, options) {
2272
+ const currentProfile = options.projectProfile ?? profile;
2273
+ const techStack = options.projectProfile ? options.techStack.join(" + ") || "Unknown" : state?.techStack || options.techStack.join(" + ") || "Unknown";
2274
+ const uiLibrary = options.projectProfile ? options.uiLibrary || "None" : state?.uiLibrary || tokens?.uiLibrary || options.uiLibrary || "None";
2120
2275
  const mode = state?.currentMode || "solo";
2121
2276
  return [
2122
2277
  "## mancode Project Context",
@@ -2125,8 +2280,9 @@ function renderProjectContext(state, tokens, options) {
2125
2280
  `- Current mode: ${mode}`,
2126
2281
  `- Tech stack: ${techStack}`,
2127
2282
  `- UI library: ${uiLibrary}`,
2283
+ `- Project profile: ${currentProfile?.projectKind || "unknown"}; validation: ${currentProfile?.availableValidation?.join(", ") || "inspect project"}`,
2128
2284
  "- At the start of each session, read `.mancode/state.json` to check the current mode and project context.",
2129
- "- For UI tasks, read `.mancode/aesthetics/style-tokens.json` for current design tokens."
2285
+ "- Read `.mancode/project-profile.json` before choosing tools or validation. Only for a UI task in a profile with detected UI assets, read `.mancode/aesthetics/style-tokens.json`."
2130
2286
  ].join("\n");
2131
2287
  }
2132
2288
  function renderPracticeRules() {
@@ -2178,7 +2334,7 @@ function renderAesthetics(tokens) {
2178
2334
  function renderModes(options) {
2179
2335
  let commandLabel;
2180
2336
  if (options.capabilities.skills === "agents-skills") {
2181
- commandLabel = "Invoke mode skills with `$man8`, `$man`, `$manteam`, `$manps`, `$mansolo`.";
2337
+ commandLabel = "Invoke mode skills with `$man`, `$mamba`, `$manteam`, `$manps`, `$mansolo`.";
2182
2338
  } else if (options.capabilities.slashCommands === "native") {
2183
2339
  commandLabel = "Use the named commands directly when available.";
2184
2340
  } else {
@@ -2188,8 +2344,8 @@ function renderModes(options) {
2188
2344
  "## mancode Modes",
2189
2345
  "",
2190
2346
  `- solo: default lightweight mode. ${commandLabel}`,
2191
- "- man8: investigate first, then produce a plan before implementation.",
2192
- "- man: full high-risk workflow with plan, implementation, verification, and review.",
2347
+ "- man: progressive governance workflow with planning and optional full execution.",
2348
+ "- mamba: diagnose bugs and validate real user flows or regressions.",
2193
2349
  "- manteam: use team memory and leave handoff-friendly summaries.",
2194
2350
  "- manps: run project health and cleanup scans before remediation.",
2195
2351
  "- mansolo: return to solo mode."
@@ -2215,14 +2371,14 @@ function renderPlatformDowngrade(options) {
2215
2371
  }
2216
2372
  if (!options.capabilities.subagents) {
2217
2373
  lines.push(
2218
- "- Simulate the coaching staff in sequence inside the same conversation: Scout, Head Coach, Film Analyst Offense, Film Analyst Defense."
2374
+ "- Simulate the coaching staff in sequence inside the same conversation: Scout, Plan Coach, Head Coach, Film Analyst Offense, Film Analyst Defense."
2219
2375
  );
2220
2376
  }
2221
2377
  return lines.join("\n");
2222
2378
  }
2223
2379
  async function readJson(filePath) {
2224
2380
  try {
2225
- return JSON.parse(await readFile4(filePath, "utf-8"));
2381
+ return JSON.parse(await readFile5(filePath, "utf-8"));
2226
2382
  } catch {
2227
2383
  return null;
2228
2384
  }
@@ -2260,7 +2416,8 @@ async function installCodex(projectRoot, options) {
2260
2416
  },
2261
2417
  minimal: options.minimal,
2262
2418
  techStack: options.techStack,
2263
- uiLibrary: options.uiLibrary
2419
+ uiLibrary: options.uiLibrary,
2420
+ projectProfile: options.projectProfile
2264
2421
  });
2265
2422
  const block = [
2266
2423
  DEFAULT_MANCODE_START_MARKER,
@@ -2295,7 +2452,8 @@ async function installCopilot(projectRoot, options) {
2295
2452
  },
2296
2453
  minimal: true,
2297
2454
  techStack: options.techStack,
2298
- uiLibrary: options.uiLibrary
2455
+ uiLibrary: options.uiLibrary,
2456
+ projectProfile: options.projectProfile
2299
2457
  });
2300
2458
  const sections = [
2301
2459
  DEFAULT_MANCODE_START_MARKER,
@@ -2322,8 +2480,8 @@ function renderCopilotPromptConventions() {
2322
2480
  "",
2323
2481
  "GitHub Copilot does not provide native mancode slash commands, hooks, or isolated subagents. Treat these names as user prompt conventions:",
2324
2482
  "",
2325
- "- man8: investigate first and produce a plan before implementation.",
2326
- "- man: use a careful plan, implementation, verification, and review loop.",
2483
+ "- man: use progressive research, planning, implementation, verification, and review.",
2484
+ "- mamba: diagnose bugs and validate real user flows or regressions.",
2327
2485
  "- manteam: read team memory and write handoff-friendly summaries.",
2328
2486
  "- manps: prefer `mancode manps [area]` before cleanup.",
2329
2487
  "- mansolo: exit any active mode and return to default solo behavior.",
@@ -2333,7 +2491,7 @@ function renderCopilotPromptConventions() {
2333
2491
  }
2334
2492
 
2335
2493
  // src/installers/cursor.ts
2336
- import { mkdir as mkdir6, rm as rm3, writeFile as writeFile7 } from "fs/promises";
2494
+ import { mkdir as mkdir6, readFile as readFile6, rm as rm3, writeFile as writeFile7 } from "fs/promises";
2337
2495
  import path8 from "path";
2338
2496
  var MANCODE_CURSOR_CORE_RULE_FILES = [
2339
2497
  "mancode-context.mdc",
@@ -2341,11 +2499,12 @@ var MANCODE_CURSOR_CORE_RULE_FILES = [
2341
2499
  "mancode-solo.mdc"
2342
2500
  ];
2343
2501
  var MANCODE_CURSOR_ADVANCED_RULE_FILES = [
2344
- "mancode-man8.mdc",
2502
+ "mancode-mamba.mdc",
2345
2503
  "mancode-man.mdc",
2346
2504
  "mancode-manteam.mdc",
2347
2505
  "mancode-manps.mdc"
2348
2506
  ];
2507
+ var CURSOR_RULE_MANAGED_MARKER = "<!-- Managed by mancode:cursor-rule. Do not edit this marker. -->";
2349
2508
  var MANCODE_CURSOR_RULE_FILES = [
2350
2509
  ...MANCODE_CURSOR_CORE_RULE_FILES,
2351
2510
  ...MANCODE_CURSOR_ADVANCED_RULE_FILES
@@ -2367,7 +2526,8 @@ async function installCursor(projectRoot, options) {
2367
2526
  // the mode-specific Cursor rules below.
2368
2527
  minimal: true,
2369
2528
  techStack: options.techStack,
2370
- uiLibrary: options.uiLibrary
2529
+ uiLibrary: options.uiLibrary,
2530
+ projectProfile: options.projectProfile
2371
2531
  });
2372
2532
  await writeRule(
2373
2533
  rulesDir,
@@ -2395,12 +2555,13 @@ async function installCursor(projectRoot, options) {
2395
2555
  await installCursorCommands(projectRoot, true);
2396
2556
  return;
2397
2557
  }
2558
+ await removeLegacyCursorRules(projectRoot);
2398
2559
  await writeRule(
2399
2560
  rulesDir,
2400
- "mancode-man8.mdc",
2401
- "Use when the user invokes /man8 or asks for investigation and planning before implementation",
2561
+ "mancode-mamba.mdc",
2562
+ "Use for bug diagnosis and real regression testing",
2402
2563
  false,
2403
- renderMan8Rule()
2564
+ renderMambaRule()
2404
2565
  );
2405
2566
  await writeRule(
2406
2567
  rulesDir,
@@ -2439,11 +2600,77 @@ async function writeRule(rulesDir, fileName, description, alwaysApply, body) {
2439
2600
 
2440
2601
  ${body.trim()}
2441
2602
  `;
2442
- await writeFile7(path8.join(rulesDir, fileName), content, "utf-8");
2603
+ const rulePath = path8.join(rulesDir, fileName);
2604
+ const existing = await readTextIfExists4(rulePath);
2605
+ if (existing !== null && !isGeneratedCursorRule(existing, fileName)) {
2606
+ throw new Error(
2607
+ `refusing to overwrite user-authored Cursor rule: ${rulePath}`
2608
+ );
2609
+ }
2610
+ await writeFile7(
2611
+ rulePath,
2612
+ content.replace("---\n\n", `---
2613
+
2614
+ ${CURSOR_RULE_MANAGED_MARKER}
2615
+
2616
+ `),
2617
+ "utf-8"
2618
+ );
2443
2619
  }
2444
2620
  async function removeAdvancedRules(rulesDir) {
2445
2621
  for (const fileName of MANCODE_CURSOR_ADVANCED_RULE_FILES) {
2446
- await rm3(path8.join(rulesDir, fileName), { force: true });
2622
+ await removeGeneratedCursorRule(rulesDir, fileName);
2623
+ }
2624
+ await removeLegacyCursorRules(path8.dirname(path8.dirname(rulesDir)));
2625
+ }
2626
+ async function removeCursorGeneratedRules(projectRoot) {
2627
+ const rulesDir = path8.join(projectRoot, ".cursor", "rules");
2628
+ for (const fileName of MANCODE_CURSOR_RULE_FILES) {
2629
+ await removeGeneratedCursorRule(rulesDir, fileName);
2630
+ }
2631
+ await removeLegacyCursorRules(projectRoot);
2632
+ }
2633
+ async function removeLegacyCursorRules(projectRoot) {
2634
+ const legacyPath = path8.join(
2635
+ projectRoot,
2636
+ ".cursor",
2637
+ "rules",
2638
+ "mancode-man8.mdc"
2639
+ );
2640
+ try {
2641
+ const content = await readFile6(legacyPath, "utf-8");
2642
+ if (content.includes("# mancode man8 \u2014 Investigate and Plan") && content.includes("## Mode Persistence")) {
2643
+ await rm3(legacyPath, { force: true });
2644
+ }
2645
+ } catch {
2646
+ }
2647
+ }
2648
+ async function removeGeneratedCursorRule(rulesDir, fileName) {
2649
+ const rulePath = path8.join(rulesDir, fileName);
2650
+ const content = await readTextIfExists4(rulePath);
2651
+ if (content && isGeneratedCursorRule(content, fileName)) {
2652
+ await rm3(rulePath, { force: true });
2653
+ }
2654
+ }
2655
+ function isGeneratedCursorRule(content, fileName) {
2656
+ if (content.includes(CURSOR_RULE_MANAGED_MARKER)) return true;
2657
+ if (fileName === "mancode-context.mdc") {
2658
+ return content.includes("# mancode Configuration") && content.includes("Platform adapter: Cursor");
2659
+ }
2660
+ if (fileName === "mancode-practice.mdc") {
2661
+ return content.includes("# mancode Practice") && content.includes("YAGNI ladder");
2662
+ }
2663
+ if (fileName === "mancode-solo.mdc") {
2664
+ return content.includes("# mancode solo") && content.includes("Use solo mode");
2665
+ }
2666
+ const mode = fileName.replace(/^mancode-/, "").replace(/\.mdc$/, "");
2667
+ return content.includes(`# mancode ${mode} \u2014`) && content.includes("## Mode Persistence");
2668
+ }
2669
+ async function readTextIfExists4(filePath) {
2670
+ try {
2671
+ return await readFile6(filePath, "utf-8");
2672
+ } catch {
2673
+ return null;
2447
2674
  }
2448
2675
  }
2449
2676
  function renderPracticeRule() {
@@ -2470,12 +2697,12 @@ function renderSoloRule() {
2470
2697
  "",
2471
2698
  "- Keep the diff narrow.",
2472
2699
  "- Reuse existing functions, components, styles, and dependencies.",
2473
- "- For UI work, read `.mancode/aesthetics/style-tokens.json` and inspect existing components before inventing new styles.",
2700
+ "- 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.",
2474
2701
  "- Verify with the narrowest meaningful test, lint, build, or smoke check."
2475
2702
  ].join("\n");
2476
2703
  }
2477
- function renderMan8Rule() {
2478
- return renderModeSkill("man8", "/");
2704
+ function renderMambaRule() {
2705
+ return renderModeSkill("mamba", "/");
2479
2706
  }
2480
2707
  function renderManRule() {
2481
2708
  return renderModeSkill("man", "/");
@@ -2507,7 +2734,8 @@ async function installZcode(projectRoot, options) {
2507
2734
  },
2508
2735
  minimal: options.minimal,
2509
2736
  techStack: options.techStack,
2510
- uiLibrary: options.uiLibrary
2737
+ uiLibrary: options.uiLibrary,
2738
+ projectProfile: options.projectProfile
2511
2739
  });
2512
2740
  const block = [
2513
2741
  ZCODE_MANCODE_START_MARKER,
@@ -2689,8 +2917,6 @@ async function checkRemote(cwd) {
2689
2917
 
2690
2918
  // src/system/detect.ts
2691
2919
  import { exec as exec2 } from "child_process";
2692
- import { access as access2, readFile as readFile5 } from "fs/promises";
2693
- import path11 from "path";
2694
2920
  import { promisify as promisify2 } from "util";
2695
2921
  var execAsync2 = promisify2(exec2);
2696
2922
  async function detectSystemDeps() {
@@ -2714,62 +2940,271 @@ async function detectSystemDeps() {
2714
2940
  ]);
2715
2941
  return { bash, git, jq, node };
2716
2942
  }
2717
- async function detectProjectType(projectRoot) {
2718
- const pkgPath = path11.join(projectRoot, "package.json");
2719
- try {
2720
- await access2(pkgPath);
2721
- } catch (err) {
2722
- if (err.code === "ENOENT") {
2723
- return {
2724
- hasFrontend: false,
2725
- hasBackend: false,
2726
- techStack: [],
2727
- uiLibrary: null
2728
- };
2943
+
2944
+ // src/system/project-profile.ts
2945
+ import { readFile as readFile7, readdir as readdir3, stat as stat2 } from "fs/promises";
2946
+ import path11 from "path";
2947
+ var PROJECT_MANIFESTS = [
2948
+ "package.json",
2949
+ "pyproject.toml",
2950
+ "requirements.txt",
2951
+ "go.mod",
2952
+ "Cargo.toml",
2953
+ "pom.xml",
2954
+ "build.gradle",
2955
+ "build.gradle.kts",
2956
+ "Package.swift",
2957
+ "pubspec.yaml"
2958
+ ];
2959
+ async function detectProjectProfile(projectRoot) {
2960
+ const entries = await readdir3(projectRoot).catch(() => []);
2961
+ const manifests = PROJECT_MANIFESTS.filter((file) => entries.includes(file));
2962
+ const sourceRoots = await existingDirs(projectRoot, [
2963
+ "src",
2964
+ "app",
2965
+ "apps",
2966
+ "packages",
2967
+ "web",
2968
+ "lib",
2969
+ "Sources",
2970
+ "cmd",
2971
+ "server",
2972
+ "backend",
2973
+ "mobile",
2974
+ "ios",
2975
+ "android",
2976
+ "data",
2977
+ "notebooks"
2978
+ ]);
2979
+ const packageJson = await readJson2(path11.join(projectRoot, "package.json"));
2980
+ const [pubspec, pythonManifests] = await Promise.all([
2981
+ readText(path11.join(projectRoot, "pubspec.yaml")),
2982
+ Promise.all([
2983
+ readText(path11.join(projectRoot, "pyproject.toml")),
2984
+ readText(path11.join(projectRoot, "requirements.txt"))
2985
+ ]).then((parts) => parts.filter(Boolean).join("\n"))
2986
+ ]);
2987
+ const deps = Object.keys({
2988
+ ...packageJson?.dependencies,
2989
+ ...packageJson?.devDependencies,
2990
+ ...packageJson?.peerDependencies
2991
+ });
2992
+ const flutter = isFlutterProject(pubspec);
2993
+ const shadcn = entries.includes("components.json") || deps.some((dep) => dep.startsWith("@radix-ui/")) && await isDirectory(path11.join(projectRoot, "src", "components", "ui"));
2994
+ const languages = inferLanguages(manifests, entries);
2995
+ const frameworks = inferFrameworks(
2996
+ deps,
2997
+ manifests,
2998
+ pythonManifests,
2999
+ flutter,
3000
+ shadcn
3001
+ );
3002
+ const uiAssets = hasUiAssets(deps, entries, flutter) ? "detected" : "none";
3003
+ const projectKind = inferKind(
3004
+ deps,
3005
+ manifests,
3006
+ entries,
3007
+ packageJson,
3008
+ uiAssets,
3009
+ flutter,
3010
+ pythonManifests
3011
+ );
3012
+ const availableValidation = inferValidation(
3013
+ packageJson,
3014
+ manifests,
3015
+ entries,
3016
+ flutter,
3017
+ pythonManifests
3018
+ );
3019
+ return {
3020
+ version: "1.0",
3021
+ projectKind,
3022
+ languages,
3023
+ frameworks,
3024
+ sourceRoots,
3025
+ manifests,
3026
+ availableValidation,
3027
+ uiAssets,
3028
+ browserAutomation: deps.some((dep) => dep.includes("playwright")) || entries.some((entry) => /^playwright\.config\./.test(entry)) ? "available" : projectKind === "web" || projectKind === "mixed" ? "unknown" : "unavailable",
3029
+ confidence: projectKind === "unknown" ? "low" : manifests.length > 0 ? "high" : "medium"
3030
+ };
3031
+ }
3032
+ async function existingDirs(root, candidates) {
3033
+ const found = [];
3034
+ for (const candidate of candidates) {
3035
+ try {
3036
+ if ((await stat2(path11.join(root, candidate))).isDirectory())
3037
+ found.push(candidate);
3038
+ } catch {
2729
3039
  }
2730
- throw err;
2731
3040
  }
2732
- const raw = await readFile5(pkgPath, "utf-8");
2733
- const pkg = JSON.parse(raw);
2734
- const allDeps = {
2735
- ...pkg.dependencies,
2736
- ...pkg.devDependencies,
2737
- ...pkg.peerDependencies
2738
- };
2739
- const deps = Object.keys(allDeps || {});
2740
- const hasFrontend = deps.some((d) => ["react", "vue", "svelte", "angular"].includes(d)) || deps.some((d) => d.startsWith("@angular/"));
2741
- const hasBackend = deps.some(
2742
- (d) => ["express", "koa", "fastify", "hapi", "next"].includes(d)
2743
- ) || deps.some((d) => d.startsWith("@nestjs/"));
2744
- const techStack = [];
2745
- if (deps.includes("react")) techStack.push("React");
2746
- if (deps.includes("vue")) techStack.push("Vue");
2747
- if (deps.includes("svelte")) techStack.push("Svelte");
2748
- if (deps.includes("typescript")) techStack.push("TypeScript");
2749
- if (deps.includes("tailwindcss")) techStack.push("Tailwind CSS");
2750
- let uiLibrary = null;
2751
- const hasRadix = deps.some((d) => d.startsWith("@radix-ui/"));
2752
- const uiDir = path11.join(projectRoot, "src", "components", "ui");
2753
- const hasUiDir = await pathExists2(uiDir);
2754
- if (hasRadix && hasUiDir) {
2755
- uiLibrary = "shadcn/ui";
2756
- } else if (deps.includes("@mui/material")) {
2757
- uiLibrary = "MUI";
2758
- } else if (deps.includes("antd")) {
2759
- uiLibrary = "Ant Design";
2760
- } else if (deps.includes("@headlessui/react")) {
2761
- uiLibrary = "headlessUI";
2762
- }
2763
- return { hasFrontend, hasBackend, techStack, uiLibrary };
3041
+ return found;
2764
3042
  }
2765
- async function pathExists2(p) {
3043
+ async function isDirectory(candidate) {
2766
3044
  try {
2767
- await access2(p);
2768
- return true;
3045
+ return (await stat2(candidate)).isDirectory();
2769
3046
  } catch {
2770
3047
  return false;
2771
3048
  }
2772
3049
  }
3050
+ async function readJson2(file) {
3051
+ try {
3052
+ return JSON.parse(await readFile7(file, "utf-8"));
3053
+ } catch {
3054
+ return null;
3055
+ }
3056
+ }
3057
+ async function readText(file) {
3058
+ try {
3059
+ return await readFile7(file, "utf-8");
3060
+ } catch {
3061
+ return "";
3062
+ }
3063
+ }
3064
+ function inferLanguages(manifests, entries) {
3065
+ const languages = [];
3066
+ if (manifests.includes("package.json"))
3067
+ languages.push("JavaScript/TypeScript");
3068
+ if (manifests.includes("pyproject.toml") || manifests.includes("requirements.txt"))
3069
+ languages.push("Python");
3070
+ if (manifests.includes("go.mod")) languages.push("Go");
3071
+ if (manifests.includes("Cargo.toml")) languages.push("Rust");
3072
+ if (manifests.includes("pom.xml") || manifests.includes("build.gradle") || manifests.includes("build.gradle.kts"))
3073
+ languages.push("JVM");
3074
+ if (manifests.includes("Package.swift")) languages.push("Swift");
3075
+ if (manifests.includes("pubspec.yaml")) languages.push("Dart");
3076
+ if (entries.includes("Dockerfile") && languages.length === 0)
3077
+ languages.push("Containerized");
3078
+ return languages;
3079
+ }
3080
+ function inferFrameworks(deps, manifests, pythonManifests, flutter, shadcn) {
3081
+ const result = [];
3082
+ for (const [dep, name] of [
3083
+ ["react", "React"],
3084
+ ["vue", "Vue"],
3085
+ ["svelte", "Svelte"],
3086
+ ["next", "Next.js"],
3087
+ ["nuxt", "Nuxt"],
3088
+ ["astro", "Astro"],
3089
+ ["solid-js", "SolidJS"],
3090
+ ["preact", "Preact"],
3091
+ ["@angular/core", "Angular"],
3092
+ ["express", "Express"],
3093
+ ["fastify", "Fastify"],
3094
+ ["electron", "Electron"],
3095
+ ["@tauri-apps/api", "Tauri"],
3096
+ ["tailwindcss", "Tailwind CSS"],
3097
+ ["@mui/material", "MUI"],
3098
+ ["antd", "Ant Design"],
3099
+ ["@headlessui/react", "Headless UI"]
3100
+ ]) {
3101
+ if (deps.includes(dep)) result.push(name);
3102
+ }
3103
+ if (manifests.includes("go.mod")) result.push("Go modules");
3104
+ if (manifests.includes("Cargo.toml")) result.push("Cargo");
3105
+ if (flutter) result.push("Flutter");
3106
+ if (shadcn) result.push("shadcn/ui");
3107
+ else if (deps.some((dep) => dep.startsWith("@radix-ui/")))
3108
+ result.push("Radix UI");
3109
+ for (const [pattern, name] of [
3110
+ [/\bdjango\b/i, "Django"],
3111
+ [/\bfastapi\b/i, "FastAPI"],
3112
+ [/\bflask\b/i, "Flask"]
3113
+ ]) {
3114
+ if (pattern.test(pythonManifests)) result.push(name);
3115
+ }
3116
+ return result;
3117
+ }
3118
+ function hasUiAssets(deps, entries, flutter) {
3119
+ return deps.some(
3120
+ (dep) => [
3121
+ "react",
3122
+ "vue",
3123
+ "svelte",
3124
+ "solid-js",
3125
+ "preact",
3126
+ "@angular/core",
3127
+ "tailwindcss",
3128
+ "@mui/material",
3129
+ "antd",
3130
+ "@headlessui/react",
3131
+ "electron",
3132
+ "@tauri-apps/api"
3133
+ ].includes(dep) || dep.startsWith("@radix-ui/")
3134
+ ) || entries.includes("index.html") || entries.some((entry) => /^tailwind\.config\./.test(entry)) || entries.includes("ios") || entries.includes("android") || flutter;
3135
+ }
3136
+ function primaryUiLibrary(profile) {
3137
+ const preferred = [
3138
+ "shadcn/ui",
3139
+ "MUI",
3140
+ "Ant Design",
3141
+ "Headless UI",
3142
+ "Radix UI",
3143
+ "Tailwind CSS",
3144
+ "Flutter"
3145
+ ];
3146
+ return preferred.find((name) => profile.frameworks.includes(name)) ?? null;
3147
+ }
3148
+ function inferKind(deps, manifests, entries, packageJson, uiAssets, flutter, pythonManifests) {
3149
+ const hasBackendFramework = deps.includes("express") || deps.includes("fastify") || /\b(django|fastapi|flask)\b/i.test(pythonManifests);
3150
+ const hasBackendLayout = entries.includes("server") || entries.includes("backend");
3151
+ const hasPackageLibraryContract = manifests.includes("package.json") && ["main", "module", "exports", "types"].some(
3152
+ (field) => Object.hasOwn(packageJson ?? {}, field)
3153
+ );
3154
+ const hasCliSignal = entries.includes("bin") || entries.includes("cmd") || typeof packageJson?.bin === "string" || typeof packageJson?.bin === "object" && packageJson.bin !== null;
3155
+ const hasWebApplicationSignal = entries.some(
3156
+ (entry) => ["app", "pages", "public", "web", "index.html"].includes(entry)
3157
+ ) || deps.some((dep) => ["next", "nuxt", "astro"].includes(dep));
3158
+ const hasNonWebManifest = manifests.some(
3159
+ (manifest) => [
3160
+ "pyproject.toml",
3161
+ "requirements.txt",
3162
+ "go.mod",
3163
+ "Cargo.toml",
3164
+ "pom.xml",
3165
+ "build.gradle",
3166
+ "build.gradle.kts"
3167
+ ].includes(manifest)
3168
+ );
3169
+ if (entries.includes("ios") || entries.includes("android") || flutter)
3170
+ return hasBackendFramework || hasBackendLayout ? "mixed" : "mobile";
3171
+ if (deps.some((dep) => ["electron", "@tauri-apps/api"].includes(dep)))
3172
+ return hasBackendFramework || hasBackendLayout ? "mixed" : "desktop";
3173
+ if (hasCliSignal && !hasWebApplicationSignal)
3174
+ return hasBackendFramework || hasBackendLayout ? "mixed" : "cli";
3175
+ if (hasPackageLibraryContract && !hasWebApplicationSignal && !hasBackendFramework && !hasBackendLayout && !hasNonWebManifest)
3176
+ return "library";
3177
+ if (uiAssets === "detected")
3178
+ return hasBackendFramework || hasBackendLayout || hasNonWebManifest ? "mixed" : "web";
3179
+ if (entries.includes("data") || entries.includes("notebooks")) return "data";
3180
+ if (hasPackageLibraryContract) return "library";
3181
+ if (manifests.includes("pubspec.yaml") && entries.includes("lib"))
3182
+ return "library";
3183
+ if (hasBackendFramework || hasBackendLayout) return "backend";
3184
+ if (manifests.filter((manifest) => manifest !== "requirements.txt").length > 1)
3185
+ return "mixed";
3186
+ return "unknown";
3187
+ }
3188
+ function inferValidation(pkg, manifests, entries, flutter, pythonManifests) {
3189
+ const scripts = pkg?.scripts ?? {};
3190
+ const packageRunner = entries.includes("pnpm-lock.yaml") ? "pnpm" : entries.includes("yarn.lock") ? "yarn" : entries.includes("bun.lock") || entries.includes("bun.lockb") ? "bun" : "npm";
3191
+ return Array.from(
3192
+ new Set(
3193
+ ["build", "lint", "test", "typecheck"].filter((name) => typeof scripts[name] === "string").map((name) => `${packageRunner} run ${name}`).concat(
3194
+ manifests.includes("go.mod") ? ["go test ./..."] : [],
3195
+ manifests.includes("Cargo.toml") ? ["cargo test"] : [],
3196
+ /\bpytest\b/i.test(pythonManifests) || entries.includes("pytest.ini") ? ["python -m pytest"] : [],
3197
+ manifests.includes("pom.xml") ? [entries.includes("mvnw") ? "./mvnw test" : "mvn test"] : [],
3198
+ manifests.includes("build.gradle") || manifests.includes("build.gradle.kts") ? [entries.includes("gradlew") ? "./gradlew test" : "gradle test"] : [],
3199
+ manifests.includes("Package.swift") ? ["swift test"] : [],
3200
+ manifests.includes("pubspec.yaml") ? [flutter ? "flutter test" : "dart test"] : []
3201
+ )
3202
+ )
3203
+ );
3204
+ }
3205
+ function isFlutterProject(pubspec) {
3206
+ return /(^|\n)\s*sdk\s*:\s*flutter\s*($|\n)/m.test(pubspec);
3207
+ }
2773
3208
 
2774
3209
  // src/system/scan-aesthetics.ts
2775
3210
  import { promises as fs } from "fs";
@@ -2787,7 +3222,8 @@ async function scanAesthetics(projectRoot, uiLibrary = null) {
2787
3222
  if (configResult) {
2788
3223
  sourceFiles.push(configResult.relPath);
2789
3224
  const content = await fs.readFile(configResult.absPath, "utf-8");
2790
- const themeBlock = findKeyBlock(content, "theme");
3225
+ const inspectableContent = stripJsComments(content);
3226
+ const themeBlock = findKeyBlock(inspectableContent, "theme");
2791
3227
  if (themeBlock) {
2792
3228
  const extendBlock = findKeyBlock(themeBlock, "extend");
2793
3229
  const searchBlock = extendBlock ?? themeBlock;
@@ -2800,15 +3236,15 @@ async function scanAesthetics(projectRoot, uiLibrary = null) {
2800
3236
  fonts = extractTopLevelArrayValues(fontsBlock);
2801
3237
  }
2802
3238
  }
2803
- darkMode = extractDarkMode(stripJsComments(content));
3239
+ darkMode = extractDarkMode(inspectableContent);
2804
3240
  }
2805
3241
  let matchLevel = "none";
2806
- if (configResult) {
3242
+ if (configResult || components.length > 0 || Object.keys(cssScan.variables).length > 0) {
2807
3243
  matchLevel = "high";
2808
- } else if (await hasTailwindDep(projectRoot)) {
3244
+ } else if (await hasTailwindDep(projectRoot) || uiLibrary) {
2809
3245
  matchLevel = "low";
2810
3246
  }
2811
- if (uiLibrary) {
3247
+ if (uiLibrary && await pathExists2(path12.join(projectRoot, "package.json"))) {
2812
3248
  sourceFiles.push("package.json");
2813
3249
  }
2814
3250
  sourceFiles.push(...cssScan.sourceFiles);
@@ -2834,7 +3270,7 @@ async function findTailwindConfig(projectRoot) {
2834
3270
  ];
2835
3271
  for (const name of candidates) {
2836
3272
  const absPath = path12.join(projectRoot, name);
2837
- if (await pathExists3(absPath)) {
3273
+ if (await pathExists2(absPath)) {
2838
3274
  return { absPath, relPath: name };
2839
3275
  }
2840
3276
  }
@@ -3049,12 +3485,19 @@ function stripJsComments(content) {
3049
3485
  return result;
3050
3486
  }
3051
3487
  async function scanComponents(projectRoot) {
3052
- const roots = ["src/components", "components", "app/components"];
3488
+ const roots = [
3489
+ "src/components",
3490
+ "components",
3491
+ "app/components",
3492
+ "lib/widgets",
3493
+ "lib/screens",
3494
+ "Sources"
3495
+ ];
3053
3496
  const names = /* @__PURE__ */ new Set();
3054
3497
  let visitedFiles = 0;
3055
3498
  for (const relRoot of roots) {
3056
3499
  const absRoot = path12.join(projectRoot, relRoot);
3057
- if (!await pathExists3(absRoot)) continue;
3500
+ if (!await pathExists2(absRoot)) continue;
3058
3501
  visitedFiles = await collectComponentNames(absRoot, names, 0, visitedFiles);
3059
3502
  if (visitedFiles >= MAX_COMPONENT_FILES) break;
3060
3503
  }
@@ -3085,10 +3528,14 @@ async function collectComponentNames(dir, names, depth, visitedFiles) {
3085
3528
  fileCount = await collectComponentNames(abs, names, depth + 1, fileCount);
3086
3529
  continue;
3087
3530
  }
3088
- if (!/\.(tsx|jsx|ts|js|vue|svelte)$/.test(entry)) continue;
3531
+ if (!/\.(tsx|jsx|ts|js|vue|svelte|dart|swift|kt|kts)$/.test(entry))
3532
+ continue;
3089
3533
  fileCount++;
3090
3534
  if (isNonComponentFile(entry)) continue;
3091
- let base = entry.replace(/\.(tsx|jsx|ts|js|vue|svelte)$/, "");
3535
+ let base = entry.replace(
3536
+ /\.(tsx|jsx|ts|js|vue|svelte|dart|swift|kt|kts)$/,
3537
+ ""
3538
+ );
3092
3539
  if (base === "index") {
3093
3540
  base = path12.basename(dir);
3094
3541
  }
@@ -3113,7 +3560,7 @@ async function scanCssVariables(projectRoot) {
3113
3560
  const sourceFiles = [];
3114
3561
  for (const relPath of candidates) {
3115
3562
  const absPath = path12.join(projectRoot, relPath);
3116
- if (!await pathExists3(absPath)) continue;
3563
+ if (!await pathExists2(absPath)) continue;
3117
3564
  const content = await fs.readFile(absPath, "utf-8");
3118
3565
  const found = extractCssVariables(content);
3119
3566
  if (Object.keys(found).length === 0) continue;
@@ -3172,7 +3619,7 @@ async function hasTailwindDep(projectRoot) {
3172
3619
  function escapeRegex(s) {
3173
3620
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3174
3621
  }
3175
- async function pathExists3(p) {
3622
+ async function pathExists2(p) {
3176
3623
  try {
3177
3624
  await fs.access(p);
3178
3625
  return true;
@@ -3190,7 +3637,8 @@ var DEFAULT_INIT_PLATFORM = "claude-code";
3190
3637
  async function init(rootDir = process3.cwd(), options = {}) {
3191
3638
  const mancodeDir = path13.join(rootDir, ".mancode");
3192
3639
  const stateFile = path13.join(mancodeDir, "state.json");
3193
- const wasInitialized = await pathExists4(stateFile);
3640
+ const wasInitialized = await pathExists3(stateFile);
3641
+ let reinstallSnapshots = [];
3194
3642
  if (wasInitialized) {
3195
3643
  if (!options.force) {
3196
3644
  console.log("\u2139\uFE0F mancode already initialized.");
@@ -3198,20 +3646,37 @@ async function init(rootDir = process3.cwd(), options = {}) {
3198
3646
  console.log(" Run `mancode init --force` to reinstall.");
3199
3647
  return EXIT_ALREADY_INITIALIZED;
3200
3648
  }
3649
+ try {
3650
+ reinstallSnapshots = await snapshotFiles([
3651
+ stateFile,
3652
+ path13.join(mancodeDir, "config.json"),
3653
+ path13.join(mancodeDir, "project-profile.json"),
3654
+ path13.join(mancodeDir, "aesthetics", "style-tokens.json")
3655
+ ]);
3656
+ } catch (error) {
3657
+ const message = error instanceof Error ? error.message : String(error);
3658
+ console.error(`\u2717 Cannot snapshot existing mancode state: ${message}`);
3659
+ return EXIT_INIT_FAILED;
3660
+ }
3201
3661
  console.log("\u26A0\uFE0F Reinstalling with --force...");
3202
3662
  }
3203
- if (!await pathExists4(rootDir)) {
3663
+ if (!await pathExists3(rootDir)) {
3204
3664
  console.error(`\u2717 Target directory does not exist: ${rootDir}`);
3205
3665
  return EXIT_NOT_A_PROJECT_DIR;
3206
3666
  }
3207
- const isGitRepo = await pathExists4(path13.join(rootDir, ".git"));
3208
- const hasPackageJson = await pathExists4(path13.join(rootDir, "package.json"));
3209
- if (!isGitRepo && !hasPackageJson) {
3667
+ const isGitRepo = await pathExists3(path13.join(rootDir, ".git"));
3668
+ const hasManifest = await Promise.any(
3669
+ PROJECT_MANIFESTS.map(async (name) => {
3670
+ if (await pathExists3(path13.join(rootDir, name))) return true;
3671
+ throw new Error("manifest missing");
3672
+ })
3673
+ ).catch(() => false);
3674
+ if (!isGitRepo && !hasManifest) {
3210
3675
  console.error(`\u2717 Not a project directory: ${rootDir}`);
3211
- console.error(" (No .git or package.json found)");
3676
+ console.error(" (No .git or recognized project manifest found)");
3212
3677
  console.error("");
3213
3678
  console.error(
3214
- " Run mancode init in a git repository or Node.js project."
3679
+ " Run mancode init in a git repository or a recognized project directory."
3215
3680
  );
3216
3681
  return EXIT_NOT_A_PROJECT_DIR;
3217
3682
  }
@@ -3234,18 +3699,22 @@ async function init(rootDir = process3.cwd(), options = {}) {
3234
3699
  console.log(" bash \u2713 git \u2713 jq \u2713");
3235
3700
  }
3236
3701
  console.log("\u2713 \u68C0\u6D4B\u9879\u76EE\u7C7B\u578B...");
3237
- const project = await detectProjectType(rootDir);
3238
- const techStackStr = project.techStack.join(" + ") || "Unknown";
3239
- const uiLibraryStr = project.uiLibrary || "None";
3240
- if (project.techStack.length > 0) {
3702
+ const profile = await detectProjectProfile(rootDir);
3703
+ const profileStack = [...profile.languages, ...profile.frameworks];
3704
+ const techStackStr = profileStack.join(" + ") || "Unknown";
3705
+ const uiLibrary = primaryUiLibrary(profile);
3706
+ const uiLibraryStr = uiLibrary || "None";
3707
+ if (profileStack.length > 0) {
3241
3708
  console.log(` ${techStackStr}`);
3242
- if (project.uiLibrary) {
3709
+ if (uiLibrary) {
3243
3710
  console.log(` UI: ${uiLibraryStr}`);
3244
3711
  }
3245
- } else if (hasPackageJson) {
3246
- console.log(" package.json found, no known framework dependencies");
3712
+ } else if (hasManifest) {
3713
+ console.log(" project manifest found, no known framework dependencies");
3247
3714
  } else {
3248
- console.log(" (No package.json found, skipping tech detection)");
3715
+ console.log(
3716
+ " (No recognized manifest found, profile confidence is low)"
3717
+ );
3249
3718
  }
3250
3719
  const initPlatformName = options.platform ?? DEFAULT_INIT_PLATFORM;
3251
3720
  const installer = getPlatformInstaller(initPlatformName);
@@ -3254,7 +3723,8 @@ async function init(rootDir = process3.cwd(), options = {}) {
3254
3723
  return EXIT_INIT_FAILED;
3255
3724
  }
3256
3725
  const team = await detectTeamStatus(rootDir);
3257
- const teamModeEnabled = options.team ?? team.isTeam;
3726
+ const existingPreferences = wasInitialized ? await readExistingInitPreferences(mancodeDir) : {};
3727
+ const teamModeEnabled = options.team ?? existingPreferences.forceTeamMode ?? team.isTeam;
3258
3728
  if (options.team === true) {
3259
3729
  console.log(" team: forced on (--team)");
3260
3730
  } else if (options.team === false) {
@@ -3286,18 +3756,28 @@ async function init(rootDir = process3.cwd(), options = {}) {
3286
3756
  const stateContent = `${JSON.stringify(state, null, 2)}
3287
3757
  `;
3288
3758
  await fs2.writeFile(stateFile, stateContent, "utf-8");
3289
- await updateConfigOptions(mancodeDir, {
3290
- forceTeamMode: options.team === true,
3291
- defaultStyle: options.style ?? null,
3292
- platforms: [installer.name],
3293
- platformOptions: {
3294
- [installer.name]: { minimal: false }
3295
- }
3296
- });
3759
+ await fs2.writeFile(
3760
+ path13.join(mancodeDir, "project-profile.json"),
3761
+ `${JSON.stringify(profile, null, 2)}
3762
+ `,
3763
+ "utf-8"
3764
+ );
3765
+ await updateConfigOptions(
3766
+ mancodeDir,
3767
+ {
3768
+ forceTeamMode: options.team === void 0 && wasInitialized ? void 0 : options.team === true,
3769
+ defaultStyle: options.style === void 0 && wasInitialized ? void 0 : options.style ?? null,
3770
+ platforms: [installer.name],
3771
+ platformOptions: {
3772
+ [installer.name]: { minimal: false }
3773
+ }
3774
+ },
3775
+ wasInitialized
3776
+ );
3297
3777
  let styleLine = " .mancode/aesthetics/ # style-tokens.json (\u7A7A)";
3298
- if (project.hasFrontend) {
3778
+ if (profile.uiAssets === "detected") {
3299
3779
  console.log("\u2713 \u626B\u63CF\u5BA1\u7F8E token...");
3300
- const tokens = await scanAesthetics(rootDir, project.uiLibrary);
3780
+ const tokens = await scanAesthetics(rootDir, uiLibrary);
3301
3781
  const tokensPath = path13.join(
3302
3782
  mancodeDir,
3303
3783
  "aesthetics",
@@ -3317,7 +3797,9 @@ async function init(rootDir = process3.cwd(), options = {}) {
3317
3797
  );
3318
3798
  styleLine = ` .mancode/aesthetics/ # style-tokens.json (${colorCount} colors)`;
3319
3799
  } else if (tokens.matchLevel === "low") {
3320
- console.log(" Tailwind detected, no config found (match: low)");
3800
+ console.log(
3801
+ " UI assets detected, no reusable tokens found (match: low)"
3802
+ );
3321
3803
  styleLine = " .mancode/aesthetics/ # style-tokens.json (low match)";
3322
3804
  } else {
3323
3805
  console.log(" No design tokens found (match: none)");
@@ -3326,14 +3808,17 @@ async function init(rootDir = process3.cwd(), options = {}) {
3326
3808
  }
3327
3809
  console.log(`\u2713 \u5B89\u88C5 ${installer.displayName} \u9002\u914D...`);
3328
3810
  await installer.install(rootDir, {
3329
- techStack: project.techStack,
3330
- uiLibrary: project.uiLibrary
3811
+ techStack: profileStack,
3812
+ uiLibrary,
3813
+ projectProfile: profile,
3814
+ force: options.force
3331
3815
  });
3332
3816
  console.log("");
3333
3817
  console.log("\u2713 mancode initialized.");
3334
3818
  console.log("");
3335
3819
  console.log("Created:");
3336
3820
  console.log(" .mancode/state.json # \u9879\u76EE\u72B6\u6001");
3821
+ console.log(" .mancode/project-profile.json # \u68C0\u6D4B\u5230\u7684\u9879\u76EE\u4E8B\u5B9E");
3337
3822
  console.log(" .mancode/config.json # \u914D\u7F6E");
3338
3823
  console.log(
3339
3824
  " .mancode/hooks/ # SessionStart + UserPromptSubmit"
@@ -3349,28 +3834,90 @@ async function init(rootDir = process3.cwd(), options = {}) {
3349
3834
  }
3350
3835
  return EXIT_OK;
3351
3836
  } catch (err) {
3352
- if (!wasInitialized) {
3837
+ if (wasInitialized) {
3838
+ await restoreFiles(reinstallSnapshots);
3839
+ } else {
3353
3840
  await fs2.rm(stateFile, { force: true });
3841
+ await fs2.rm(path13.join(mancodeDir, "project-profile.json"), {
3842
+ force: true
3843
+ });
3354
3844
  }
3355
3845
  const msg = err instanceof Error ? err.message : String(err);
3356
3846
  console.error(`\u2717 mancode init failed: ${msg}`);
3357
3847
  return EXIT_INIT_FAILED;
3358
3848
  }
3359
3849
  }
3360
- async function updateConfigOptions(mancodeDir, patch) {
3850
+ async function updateConfigOptions(mancodeDir, patch, preserveInstalledPlatforms) {
3361
3851
  const configPath = path13.join(mancodeDir, "config.json");
3362
3852
  let config = {};
3363
3853
  try {
3364
3854
  config = JSON.parse(await fs2.readFile(configPath, "utf-8"));
3365
3855
  } catch {
3366
3856
  }
3857
+ const existingPlatforms = Array.isArray(config.platforms) ? config.platforms.filter(
3858
+ (platform) => typeof platform === "string"
3859
+ ) : [];
3860
+ const existingPlatformOptions = isRecord3(config.platformOptions) ? config.platformOptions : {};
3861
+ const definedPatch = Object.fromEntries(
3862
+ Object.entries(patch).filter(([, value]) => value !== void 0)
3863
+ );
3864
+ const mergedPatch = preserveInstalledPlatforms ? {
3865
+ ...definedPatch,
3866
+ platforms: Array.from(
3867
+ /* @__PURE__ */ new Set([...existingPlatforms, ...patch.platforms])
3868
+ ),
3869
+ platformOptions: {
3870
+ ...existingPlatformOptions,
3871
+ ...patch.platformOptions
3872
+ }
3873
+ } : definedPatch;
3367
3874
  await fs2.writeFile(
3368
3875
  configPath,
3369
- `${JSON.stringify({ ...config, ...patch }, null, 2)}
3876
+ `${JSON.stringify({ ...config, ...mergedPatch }, null, 2)}
3370
3877
  `,
3371
3878
  "utf-8"
3372
3879
  );
3373
3880
  }
3881
+ async function readExistingInitPreferences(mancodeDir) {
3882
+ try {
3883
+ const config = JSON.parse(
3884
+ await fs2.readFile(path13.join(mancodeDir, "config.json"), "utf-8")
3885
+ );
3886
+ return typeof config.forceTeamMode === "boolean" ? { forceTeamMode: config.forceTeamMode } : {};
3887
+ } catch {
3888
+ return {};
3889
+ }
3890
+ }
3891
+ async function snapshotFiles(filePaths) {
3892
+ return Promise.all(
3893
+ filePaths.map(async (filePath) => {
3894
+ try {
3895
+ return { filePath, content: await fs2.readFile(filePath, "utf-8") };
3896
+ } catch (error) {
3897
+ if (isNodeError3(error) && error.code === "ENOENT") {
3898
+ return { filePath, content: null };
3899
+ }
3900
+ throw error;
3901
+ }
3902
+ })
3903
+ );
3904
+ }
3905
+ async function restoreFiles(snapshots) {
3906
+ for (const snapshot of snapshots) {
3907
+ if (snapshot.content === null) {
3908
+ await fs2.rm(snapshot.filePath, { force: true });
3909
+ continue;
3910
+ }
3911
+ await fs2.mkdir(path13.dirname(snapshot.filePath), { recursive: true });
3912
+ await fs2.writeFile(snapshot.filePath, snapshot.content, "utf-8");
3913
+ }
3914
+ }
3915
+ function isRecord3(value) {
3916
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3917
+ }
3918
+ function isNodeError3(error) {
3919
+ return error instanceof Error && "code" in error;
3920
+ }
3374
3921
  function printPlatformCreatedFiles(platform) {
3375
3922
  if (platform === "claude-code") {
3376
3923
  console.log(" .claude/settings.json # hook \u6CE8\u518C");
@@ -3393,7 +3940,7 @@ function printPlatformCreatedFiles(platform) {
3393
3940
  }
3394
3941
  console.log(" .github/copilot-instructions.md # Copilot instructions");
3395
3942
  }
3396
- async function pathExists4(p) {
3943
+ async function pathExists3(p) {
3397
3944
  try {
3398
3945
  await fs2.access(p);
3399
3946
  return true;
@@ -3421,29 +3968,61 @@ async function checkPlatformStatus(rootDir, platform, installed) {
3421
3968
  }
3422
3969
  async function checkPlatformReadiness(rootDir, platform) {
3423
3970
  if (platform === "claude-code") {
3424
- const hasSoloSkill = await pathExists5(
3425
- path14.join(rootDir, ".claude", "skills", "solo", "SKILL.md")
3426
- );
3427
- const registered = await claudeHooksRegistered(rootDir);
3428
- const present = hasSoloSkill && registered;
3971
+ const [hasSoloSkill, registered, hasHookFiles] = await Promise.all([
3972
+ fileMatches(
3973
+ path14.join(rootDir, ".claude", "skills", "solo", "SKILL.md"),
3974
+ (content) => isGeneratedClaudeSkill(content, "solo")
3975
+ ),
3976
+ claudeHooksRegistered(rootDir),
3977
+ pathsExist([
3978
+ path14.join(rootDir, ".mancode", "hooks", "session-start.sh"),
3979
+ path14.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
3980
+ ])
3981
+ ]);
3982
+ const present = hasSoloSkill && registered && hasHookFiles;
3983
+ const minimal = await isPlatformMinimal(rootDir, "claude-code");
3984
+ const fullContentReady = minimal ? true : await claudeFullContentReady(rootDir);
3429
3985
  return {
3430
3986
  present,
3431
- ready: present,
3987
+ ready: present && fullContentReady,
3432
3988
  target: ".claude/",
3433
- readyDetail: "skills and hooks registered"
3989
+ readyDetail: present && fullContentReady ? minimal ? "solo skill and hooks ready" : "skills, agents, and hooks ready" : "skills, agents, hook files, or hook registration are incomplete"
3434
3990
  };
3435
3991
  }
3436
3992
  if (platform === "cursor") {
3437
- const present = await allPathsExist(
3993
+ const hasCoreRules = await allManagedSkills(
3438
3994
  MANCODE_CURSOR_CORE_RULE_FILES.map(
3439
3995
  (file) => path14.join(rootDir, ".cursor", "rules", file)
3440
- )
3996
+ ),
3997
+ [CURSOR_RULE_MANAGED_MARKER]
3441
3998
  );
3999
+ if (await isPlatformMinimal(rootDir, "cursor")) {
4000
+ return {
4001
+ present: hasCoreRules,
4002
+ ready: hasCoreRules,
4003
+ target: ".cursor/rules/",
4004
+ readyDetail: "mancode core rules present"
4005
+ };
4006
+ }
4007
+ const [hasRules, hasCommands] = await Promise.all([
4008
+ allManagedSkills(
4009
+ MANCODE_CURSOR_RULE_FILES.map(
4010
+ (file) => path14.join(rootDir, ".cursor", "rules", file)
4011
+ ),
4012
+ [CURSOR_RULE_MANAGED_MARKER]
4013
+ ),
4014
+ allManagedSkills(
4015
+ MODE_NAMES.map(
4016
+ (mode) => path14.join(rootDir, ".cursor", "commands", `${mode}.md`)
4017
+ ),
4018
+ [MODE_FILE_MANAGED_MARKER]
4019
+ )
4020
+ ]);
3442
4021
  return {
3443
- present,
3444
- ready: present,
3445
- target: ".cursor/rules/",
3446
- readyDetail: "mancode rules present"
4022
+ present: hasCoreRules,
4023
+ ready: hasRules && hasCommands,
4024
+ target: ".cursor/rules/ + .cursor/commands/",
4025
+ readyDetail: hasRules && hasCommands ? "mancode rules and commands present" : "mode rules or commands are missing, incomplete, or user-authored"
3447
4026
  };
3448
4027
  }
3449
4028
  if (platform === "codex") {
@@ -3521,14 +4100,17 @@ async function checkPlatformReadiness(rootDir, platform) {
3521
4100
  readyDetail: "managed block missing"
3522
4101
  };
3523
4102
  }
3524
- const promptsDir = path14.join(rootDir, ".github", "prompts");
3525
- if (await pathExists5(promptsDir)) {
3526
- const hasPrompt = await pathExists5(path14.join(promptsDir, "man8.prompt.md"));
4103
+ if (!await isPlatformMinimal(rootDir, "copilot")) {
4104
+ const promptsDir = path14.join(rootDir, ".github", "prompts");
4105
+ const hasPrompts = await allManagedSkills(
4106
+ MODE_NAMES.map((mode) => path14.join(promptsDir, `${mode}.prompt.md`)),
4107
+ [MODE_FILE_MANAGED_MARKER]
4108
+ );
3527
4109
  return {
3528
- present: hasPrompt,
3529
- ready: hasPrompt,
4110
+ present: true,
4111
+ ready: hasPrompts,
3530
4112
  target: ".github/copilot-instructions.md + .github/prompts/",
3531
- readyDetail: hasPrompt ? "managed block and prompts present" : "prompts directory exists but man8 prompt missing"
4113
+ readyDetail: hasPrompts ? "managed block and prompts present" : "mode prompts are missing, incomplete, or user-authored"
3532
4114
  };
3533
4115
  }
3534
4116
  return {
@@ -3545,16 +4127,38 @@ function describeStatus(installed, readiness) {
3545
4127
  }
3546
4128
  return "missing generated files or registration";
3547
4129
  }
3548
- async function allPathsExist(paths) {
3549
- const results = await Promise.all(paths.map(pathExists5));
3550
- return results.every(Boolean);
3551
- }
3552
4130
  async function allManagedSkills(paths, markers) {
3553
4131
  const results = await Promise.all(
3554
4132
  paths.map((filePath) => fileHasAnyMarker(filePath, markers))
3555
4133
  );
3556
4134
  return results.every(Boolean);
3557
4135
  }
4136
+ async function claudeFullContentReady(rootDir) {
4137
+ const skillChecks = MVP2_SKILLS.map(
4138
+ (skill) => fileMatches(
4139
+ path14.join(rootDir, ".claude", "skills", skill.name, "SKILL.md"),
4140
+ (content) => isGeneratedClaudeSkill(content, skill.name)
4141
+ )
4142
+ );
4143
+ const agentChecks = ALL_AGENTS.map(
4144
+ (agent) => fileMatches(
4145
+ path14.join(rootDir, ".claude", "agents", `${agent.name}.md`),
4146
+ (content) => isGeneratedClaudeAgent(content, agent.name)
4147
+ )
4148
+ );
4149
+ const results = await Promise.all([...skillChecks, ...agentChecks]);
4150
+ return results.every(Boolean);
4151
+ }
4152
+ async function fileMatches(filePath, predicate) {
4153
+ try {
4154
+ return predicate(await fs3.readFile(filePath, "utf-8"));
4155
+ } catch {
4156
+ return false;
4157
+ }
4158
+ }
4159
+ async function pathsExist(paths) {
4160
+ return (await Promise.all(paths.map(pathExists4))).every(Boolean);
4161
+ }
3558
4162
  async function fileHasAnyMarker(filePath, needles) {
3559
4163
  try {
3560
4164
  const content = await fs3.readFile(filePath, "utf-8");
@@ -3570,9 +4174,9 @@ async function isPlatformMinimal(rootDir, platform) {
3570
4174
  "utf-8"
3571
4175
  );
3572
4176
  const config = JSON.parse(raw);
3573
- if (!isRecord2(config.platformOptions)) return false;
4177
+ if (!isRecord4(config.platformOptions)) return false;
3574
4178
  const options = config.platformOptions[platform];
3575
- return isRecord2(options) && options.minimal === true;
4179
+ return isRecord4(options) && options.minimal === true;
3576
4180
  } catch {
3577
4181
  return false;
3578
4182
  }
@@ -3598,9 +4202,9 @@ async function claudeHooksRegistered(rootDir) {
3598
4202
  }
3599
4203
  }
3600
4204
  function hooksRegistered(settings) {
3601
- if (!isRecord2(settings)) return false;
4205
+ if (!isRecord4(settings)) return false;
3602
4206
  const hooks = settings.hooks;
3603
- if (!isRecord2(hooks)) return false;
4207
+ if (!isRecord4(hooks)) return false;
3604
4208
  return hasHookCommand(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand(
3605
4209
  hooks.UserPromptSubmit,
3606
4210
  ".mancode/hooks/user-prompt-submit.sh"
@@ -3609,14 +4213,14 @@ function hooksRegistered(settings) {
3609
4213
  function hasHookCommand(value, needle) {
3610
4214
  if (!Array.isArray(value)) return false;
3611
4215
  return value.some((group) => {
3612
- if (!isRecord2(group) || !Array.isArray(group.hooks)) return false;
4216
+ if (!isRecord4(group) || !Array.isArray(group.hooks)) return false;
3613
4217
  return group.hooks.some((hook) => {
3614
- if (!isRecord2(hook) || typeof hook.command !== "string") return false;
4218
+ if (!isRecord4(hook) || typeof hook.command !== "string") return false;
3615
4219
  return hook.command.includes(needle);
3616
4220
  });
3617
4221
  });
3618
4222
  }
3619
- async function pathExists5(p) {
4223
+ async function pathExists4(p) {
3620
4224
  try {
3621
4225
  await fs3.access(p);
3622
4226
  return true;
@@ -3624,7 +4228,7 @@ async function pathExists5(p) {
3624
4228
  return false;
3625
4229
  }
3626
4230
  }
3627
- function isRecord2(value) {
4231
+ function isRecord4(value) {
3628
4232
  return typeof value === "object" && value !== null;
3629
4233
  }
3630
4234
 
@@ -3635,7 +4239,7 @@ var EXIT_UNSUPPORTED_PLATFORM = 2;
3635
4239
  var EXIT_INSTALL_FAILED = 3;
3636
4240
  async function install(rootDir = process4.cwd(), platform = "claude-code", options = {}) {
3637
4241
  const stateFile = path15.join(rootDir, ".mancode", "state.json");
3638
- if (!await pathExists6(stateFile)) {
4242
+ if (!await pathExists5(stateFile)) {
3639
4243
  console.error("\u2717 mancode not initialized.");
3640
4244
  console.error(" Run `mancode init` first.");
3641
4245
  return EXIT_NOT_INITIALIZED;
@@ -3662,6 +4266,11 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
3662
4266
  await readStatePlatform(rootDir)
3663
4267
  );
3664
4268
  const alreadyInstalled = configResult.valid && config.platforms && Array.isArray(config.platforms) ? config.platforms.includes(platform) : false;
4269
+ const configuredMinimal = readConfiguredMinimal(
4270
+ config.platformOptions,
4271
+ platform
4272
+ );
4273
+ const effectiveMinimal = options.minimal === true || !options.force && alreadyInstalled && configuredMinimal;
3665
4274
  if (alreadyInstalled && !options.force) {
3666
4275
  const status2 = await checkPlatformStatus(rootDir, platform, true);
3667
4276
  if (status2.ready) {
@@ -3683,12 +4292,13 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
3683
4292
  }
3684
4293
  }
3685
4294
  console.log(`\u2713 Installing ${formatPlatformName(platform)} adapter...`);
3686
- const project = await detectProjectType(rootDir);
4295
+ const profile = await detectProjectProfile(rootDir);
3687
4296
  try {
3688
4297
  await installer.install(rootDir, {
3689
- techStack: project.techStack,
3690
- uiLibrary: project.uiLibrary,
3691
- minimal: options.minimal,
4298
+ techStack: [...profile.languages, ...profile.frameworks],
4299
+ uiLibrary: primaryUiLibrary(profile),
4300
+ projectProfile: profile,
4301
+ minimal: effectiveMinimal,
3692
4302
  force: options.force
3693
4303
  });
3694
4304
  } catch (err) {
@@ -3705,11 +4315,10 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
3705
4315
  await updateConfig(rootDir, {
3706
4316
  ...config,
3707
4317
  platforms,
3708
- platformOptions: updatePlatformOptions(
3709
- config.platformOptions,
3710
- platform,
3711
- options
3712
- )
4318
+ platformOptions: updatePlatformOptions(config.platformOptions, platform, {
4319
+ ...options,
4320
+ minimal: effectiveMinimal
4321
+ })
3713
4322
  });
3714
4323
  console.log(`\u2713 ${formatPlatformName(platform)} adapter installed.`);
3715
4324
  return EXIT_OK2;
@@ -3720,13 +4329,13 @@ async function readConfig(rootDir) {
3720
4329
  const raw = await fs4.readFile(configPath, "utf-8");
3721
4330
  return { config: JSON.parse(raw), valid: true };
3722
4331
  } catch (err) {
3723
- if (isNodeError3(err) && err.code === "ENOENT") {
4332
+ if (isNodeError4(err) && err.code === "ENOENT") {
3724
4333
  return { config: {}, valid: false };
3725
4334
  }
3726
4335
  throw err;
3727
4336
  }
3728
4337
  }
3729
- function isNodeError3(err) {
4338
+ function isNodeError4(err) {
3730
4339
  return err instanceof Error && "code" in err;
3731
4340
  }
3732
4341
  async function updateConfig(rootDir, config) {
@@ -3745,19 +4354,24 @@ function withDefaultConfig(config, fallbackPlatform) {
3745
4354
  return {
3746
4355
  ...merged,
3747
4356
  platforms,
3748
- hooks: isRecord3(config.hooks) ? config.hooks : DEFAULT_CONFIG.hooks,
3749
- logging: isRecord3(config.logging) ? config.logging : DEFAULT_CONFIG.logging
4357
+ hooks: isRecord5(config.hooks) ? config.hooks : DEFAULT_CONFIG.hooks,
4358
+ logging: isRecord5(config.logging) ? config.logging : DEFAULT_CONFIG.logging
3750
4359
  };
3751
4360
  }
3752
4361
  function updatePlatformOptions(value, platform, options) {
3753
- const platformOptions = isRecord3(value) ? { ...value } : {};
4362
+ const platformOptions = isRecord5(value) ? { ...value } : {};
3754
4363
  const existing = platformOptions[platform];
3755
4364
  platformOptions[platform] = {
3756
- ...isRecord3(existing) ? existing : {},
4365
+ ...isRecord5(existing) ? existing : {},
3757
4366
  minimal: options.minimal === true
3758
4367
  };
3759
4368
  return platformOptions;
3760
4369
  }
4370
+ function readConfiguredMinimal(value, platform) {
4371
+ if (!isRecord5(value)) return false;
4372
+ const platformOptions = value[platform];
4373
+ return isRecord5(platformOptions) && platformOptions.minimal === true;
4374
+ }
3761
4375
  async function readStatePlatform(rootDir) {
3762
4376
  try {
3763
4377
  const raw = await fs4.readFile(
@@ -3770,7 +4384,7 @@ async function readStatePlatform(rootDir) {
3770
4384
  return null;
3771
4385
  }
3772
4386
  }
3773
- async function pathExists6(p) {
4387
+ async function pathExists5(p) {
3774
4388
  try {
3775
4389
  await fs4.access(p);
3776
4390
  return true;
@@ -3778,7 +4392,7 @@ async function pathExists6(p) {
3778
4392
  return false;
3779
4393
  }
3780
4394
  }
3781
- function isRecord3(value) {
4395
+ function isRecord5(value) {
3782
4396
  return typeof value === "object" && value !== null && !Array.isArray(value);
3783
4397
  }
3784
4398
 
@@ -3825,7 +4439,7 @@ function describePlatform(platform) {
3825
4439
  }
3826
4440
 
3827
4441
  // src/commands/manps.ts
3828
- import { access as access4 } from "fs/promises";
4442
+ import { access as access3 } from "fs/promises";
3829
4443
  import path18 from "path";
3830
4444
  import { createInterface } from "readline/promises";
3831
4445
 
@@ -3834,11 +4448,11 @@ import { execFile } from "child_process";
3834
4448
  import { createHash } from "crypto";
3835
4449
  import { existsSync, readFileSync } from "fs";
3836
4450
  import {
3837
- access as access3,
4451
+ access as access2,
3838
4452
  lstat,
3839
4453
  mkdir as mkdir7,
3840
- readFile as readFile6,
3841
- readdir as readdir2,
4454
+ readFile as readFile8,
4455
+ readdir as readdir4,
3842
4456
  rename,
3843
4457
  writeFile as writeFile9
3844
4458
  } from "fs/promises";
@@ -4109,7 +4723,7 @@ async function walk(root, current, results, depth) {
4109
4723
  if (depth > MAX_SCAN_DEPTH || results.length >= MAX_PROJECT_FILES) return;
4110
4724
  let entries;
4111
4725
  try {
4112
- entries = await readdir2(current);
4726
+ entries = await readdir4(current);
4113
4727
  } catch {
4114
4728
  return;
4115
4729
  }
@@ -4134,7 +4748,7 @@ async function walk(root, current, results, depth) {
4134
4748
  }
4135
4749
  async function readPackageJson(projectRoot) {
4136
4750
  try {
4137
- const raw = await readFile6(path17.join(projectRoot, "package.json"), "utf-8");
4751
+ const raw = await readFile8(path17.join(projectRoot, "package.json"), "utf-8");
4138
4752
  return JSON.parse(raw);
4139
4753
  } catch {
4140
4754
  return null;
@@ -4377,7 +4991,7 @@ async function hasDependencyCruiserConfig(projectRoot) {
4377
4991
  "dependency-cruiser.config.mjs"
4378
4992
  ];
4379
4993
  const results = await Promise.all(
4380
- files.map((file) => pathExists7(path17.join(projectRoot, file)))
4994
+ files.map((file) => pathExists6(path17.join(projectRoot, file)))
4381
4995
  );
4382
4996
  return results.some(Boolean);
4383
4997
  }
@@ -4445,15 +5059,15 @@ function parseDepcruiseJson(output) {
4445
5059
  }
4446
5060
  }
4447
5061
  function extractDepcruiseViolations(value) {
4448
- if (!isRecord4(value)) return [];
4449
- const rawViolations = Array.isArray(value.violations) ? value.violations : isRecord4(value.summary) && Array.isArray(value.summary.violations) ? value.summary.violations : [];
5062
+ if (!isRecord6(value)) return [];
5063
+ const rawViolations = Array.isArray(value.violations) ? value.violations : isRecord6(value.summary) && Array.isArray(value.summary.violations) ? value.summary.violations : [];
4450
5064
  return rawViolations.map((violation) => normalizeDepcruiseViolation(violation)).filter(
4451
5065
  (violation) => violation !== null
4452
5066
  );
4453
5067
  }
4454
5068
  function normalizeDepcruiseViolation(value) {
4455
- if (!isRecord4(value)) return null;
4456
- const rule = isRecord4(value.rule) ? value.rule : {};
5069
+ if (!isRecord6(value)) return null;
5070
+ const rule = isRecord6(value.rule) ? value.rule : {};
4457
5071
  const from = typeof value.from === "string" ? value.from : "";
4458
5072
  const to = typeof value.to === "string" ? value.to : void 0;
4459
5073
  const ruleName = typeof rule.name === "string" ? rule.name : typeof value.name === "string" ? value.name : "unnamed-rule";
@@ -4527,9 +5141,9 @@ async function buildIssueDatabase(projectRoot, report) {
4527
5141
  async function readIssueDatabase(issueDbPath) {
4528
5142
  let raw;
4529
5143
  try {
4530
- raw = await readFile6(issueDbPath, "utf-8");
5144
+ raw = await readFile8(issueDbPath, "utf-8");
4531
5145
  } catch (err) {
4532
- if (isNodeError4(err) && err.code === "ENOENT") {
5146
+ if (isNodeError5(err) && err.code === "ENOENT") {
4533
5147
  return emptyIssueDatabase();
4534
5148
  }
4535
5149
  throw err;
@@ -4643,7 +5257,7 @@ async function addPackageScript(projectRoot, scriptName, script) {
4643
5257
  const packagePath = path17.join(projectRoot, "package.json");
4644
5258
  let pkg;
4645
5259
  try {
4646
- pkg = JSON.parse(await readFile6(packagePath, "utf-8"));
5260
+ pkg = JSON.parse(await readFile8(packagePath, "utf-8"));
4647
5261
  } catch {
4648
5262
  return false;
4649
5263
  }
@@ -4732,18 +5346,18 @@ function readFileSyncSafe(file) {
4732
5346
  function pathExistsSync(file) {
4733
5347
  return existsSync(file);
4734
5348
  }
4735
- async function pathExists7(file) {
5349
+ async function pathExists6(file) {
4736
5350
  try {
4737
- await access3(file);
5351
+ await access2(file);
4738
5352
  return true;
4739
5353
  } catch {
4740
5354
  return false;
4741
5355
  }
4742
5356
  }
4743
- function isNodeError4(err) {
5357
+ function isNodeError5(err) {
4744
5358
  return err instanceof Error && "code" in err;
4745
5359
  }
4746
- function isRecord4(value) {
5360
+ function isRecord6(value) {
4747
5361
  return typeof value === "object" && value !== null;
4748
5362
  }
4749
5363
 
@@ -4753,7 +5367,7 @@ var EXIT_NOT_INITIALIZED2 = 1;
4753
5367
  var EXIT_SCAN_FAILED = 2;
4754
5368
  var EXIT_INVALID_ARG = 3;
4755
5369
  async function manps(rootDir, area = "all", options = {}) {
4756
- if (!await pathExists8(path18.join(rootDir, ".mancode", "state.json"))) {
5370
+ if (!await pathExists7(path18.join(rootDir, ".mancode", "state.json"))) {
4757
5371
  if (options.json) {
4758
5372
  console.log(JSON.stringify({ error: "not initialized" }, null, 2));
4759
5373
  } else {
@@ -4895,9 +5509,9 @@ async function readStdinLines() {
4895
5509
  }
4896
5510
  return Buffer.concat(chunks).toString("utf-8").split(/\r?\n/).filter((line) => line.length > 0);
4897
5511
  }
4898
- async function pathExists8(p) {
5512
+ async function pathExists7(p) {
4899
5513
  try {
4900
- await access4(p);
5514
+ await access3(p);
4901
5515
  return true;
4902
5516
  } catch {
4903
5517
  return false;
@@ -4912,18 +5526,35 @@ var EXIT_OK5 = 0;
4912
5526
  var EXIT_NOT_INITIALIZED3 = 1;
4913
5527
  async function refreshStyle(rootDir = process6.cwd()) {
4914
5528
  const stateFile = path19.join(rootDir, ".mancode", "state.json");
4915
- if (!await pathExists9(stateFile)) {
5529
+ if (!await pathExists8(stateFile)) {
4916
5530
  console.error("\u2717 mancode not initialized.");
4917
5531
  console.error(" Run `mancode init` first.");
4918
5532
  return EXIT_NOT_INITIALIZED3;
4919
5533
  }
4920
- const project = await detectProjectType(rootDir);
4921
- if (!project.hasFrontend) {
4922
- console.log("\u2139\uFE0F No frontend framework detected. Nothing to scan.");
5534
+ console.log("\u2713 \u5237\u65B0\u9879\u76EE profile...");
5535
+ const profile = await detectProjectProfile(rootDir);
5536
+ const profilePath = path19.join(rootDir, ".mancode", "project-profile.json");
5537
+ await fs6.writeFile(
5538
+ profilePath,
5539
+ `${JSON.stringify(profile, null, 2)}
5540
+ `,
5541
+ "utf-8"
5542
+ );
5543
+ const uiLibraryHint = primaryUiLibrary(profile);
5544
+ await refreshLegacyStateContext(rootDir, profile, uiLibraryHint);
5545
+ console.log(
5546
+ ` \u7C7B\u578B: ${profile.projectKind} | UI: ${profile.uiAssets} | \u6D4F\u89C8\u5668: ${profile.browserAutomation}`
5547
+ );
5548
+ if (profile.uiAssets !== "detected") {
5549
+ console.log(
5550
+ "\u2139\uFE0F No UI assets detected in project profile. Skipping style scan."
5551
+ );
5552
+ console.log(" Updated .mancode/project-profile.json.");
5553
+ await printStaticPlatformRefreshHint(rootDir);
4923
5554
  return EXIT_OK5;
4924
5555
  }
4925
5556
  console.log("\u2713 \u626B\u63CF\u9879\u76EE\u8BBE\u8BA1 token...");
4926
- const tokens = await scanAesthetics(rootDir, project.uiLibrary);
5557
+ const tokens = await scanAesthetics(rootDir, uiLibraryHint);
4927
5558
  const tokensPath = path19.join(
4928
5559
  rootDir,
4929
5560
  ".mancode",
@@ -4940,7 +5571,9 @@ async function refreshStyle(rootDir = process6.cwd()) {
4940
5571
  console.log("");
4941
5572
  if (tokens.matchLevel === "none") {
4942
5573
  console.log("\u672A\u68C0\u6D4B\u5230\u8BBE\u8BA1 token\u3002");
4943
- console.log(" \u5EFA\u8BAE\uFF1A\u6DFB\u52A0 tailwind.config.js \u6216\u624B\u52A8\u7F16\u8F91 style-tokens.json");
5574
+ console.log(
5575
+ " \u5EFA\u8BAE\uFF1A\u68C0\u67E5\u9879\u76EE\u73B0\u6709\u4E3B\u9898\u3001\u7EC4\u4EF6\u6216 token \u6587\u4EF6\uFF0C\u5FC5\u8981\u65F6\u624B\u52A8\u7EF4\u62A4 style-tokens.json"
5576
+ );
4944
5577
  } else {
4945
5578
  if (tokens.sourceFiles.length > 0) {
4946
5579
  console.log("\u626B\u63CF\u6765\u6E90\uFF1A");
@@ -4973,7 +5606,9 @@ async function refreshStyle(rootDir = process6.cwd()) {
4973
5606
  console.log(` \u5339\u914D\u5EA6: ${tokens.matchLevel}`);
4974
5607
  }
4975
5608
  console.log("");
4976
- console.log("\u5DF2\u66F4\u65B0 .mancode/aesthetics/style-tokens.json");
5609
+ console.log(
5610
+ "\u5DF2\u66F4\u65B0 .mancode/project-profile.json \u548C .mancode/aesthetics/style-tokens.json"
5611
+ );
4977
5612
  await printStaticPlatformRefreshHint(rootDir);
4978
5613
  return EXIT_OK5;
4979
5614
  }
@@ -4991,6 +5626,28 @@ async function printStaticPlatformRefreshHint(rootDir) {
4991
5626
  " Run `mancode install <platform> --force` to refresh their embedded style summaries."
4992
5627
  );
4993
5628
  }
5629
+ async function refreshLegacyStateContext(rootDir, profile, uiLibrary) {
5630
+ const statePath = path19.join(rootDir, ".mancode", "state.json");
5631
+ try {
5632
+ const state = JSON.parse(await fs6.readFile(statePath, "utf-8"));
5633
+ const stack = [...profile.languages, ...profile.frameworks];
5634
+ await fs6.writeFile(
5635
+ statePath,
5636
+ `${JSON.stringify(
5637
+ {
5638
+ ...state,
5639
+ techStack: stack.join(" + ") || profile.projectKind,
5640
+ uiLibrary: uiLibrary ?? "None"
5641
+ },
5642
+ null,
5643
+ 2
5644
+ )}
5645
+ `,
5646
+ "utf-8"
5647
+ );
5648
+ } catch {
5649
+ }
5650
+ }
4994
5651
  async function readInstalledPlatforms2(rootDir) {
4995
5652
  try {
4996
5653
  const raw = await fs6.readFile(
@@ -5005,7 +5662,7 @@ async function readInstalledPlatforms2(rootDir) {
5005
5662
  return [];
5006
5663
  }
5007
5664
  }
5008
- async function pathExists9(p) {
5665
+ async function pathExists8(p) {
5009
5666
  try {
5010
5667
  await fs6.access(p);
5011
5668
  return true;
@@ -5023,10 +5680,10 @@ import process7 from "process";
5023
5680
  // src/system/workflow.ts
5024
5681
  import {
5025
5682
  mkdir as mkdir8,
5026
- readFile as readFile7,
5027
- readdir as readdir3,
5683
+ readFile as readFile9,
5684
+ readdir as readdir5,
5028
5685
  rm as rm4,
5029
- stat as stat2,
5686
+ stat as stat3,
5030
5687
  writeFile as writeFile10
5031
5688
  } from "fs/promises";
5032
5689
  import path20 from "path";
@@ -5045,7 +5702,8 @@ function generateTaskId(task, now = /* @__PURE__ */ new Date()) {
5045
5702
  function slugify(text) {
5046
5703
  return text.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
5047
5704
  }
5048
- async function createWorkflow(projectRoot, task, mode) {
5705
+ async function createWorkflow(projectRoot, task, mode, options = {}) {
5706
+ await validateParentTask(projectRoot, mode, options.parentTaskId, true);
5049
5707
  await mkdir8(workflowsRoot(projectRoot), { recursive: true });
5050
5708
  const taskId = await allocateTaskId(projectRoot, generateTaskId(task));
5051
5709
  const dir = workflowDir(projectRoot, taskId);
@@ -5058,7 +5716,9 @@ async function createWorkflow(projectRoot, task, mode) {
5058
5716
  skippedSteps: [],
5059
5717
  startedAt: now,
5060
5718
  updatedAt: now,
5061
- status: "in_progress"
5719
+ status: "in_progress",
5720
+ ...options.parentTaskId ? { parentTaskId: options.parentTaskId } : {},
5721
+ ...mode === "man" || mode === "manteam" ? { planVersion: 1 } : {}
5062
5722
  };
5063
5723
  await writeMetadata(dir, meta);
5064
5724
  return meta;
@@ -5070,7 +5730,7 @@ async function allocateTaskId(projectRoot, baseTaskId) {
5070
5730
  await mkdir8(workflowDir(projectRoot, taskId));
5071
5731
  return taskId;
5072
5732
  } catch (err) {
5073
- if (isNodeError5(err) && err.code === "EEXIST") {
5733
+ if (isNodeError6(err) && err.code === "EEXIST") {
5074
5734
  continue;
5075
5735
  }
5076
5736
  throw err;
@@ -5082,7 +5742,7 @@ async function readWorkflow(projectRoot, taskId) {
5082
5742
  if (!isValidWorkflowTaskId(taskId)) return null;
5083
5743
  const file = metadataPath(projectRoot, taskId);
5084
5744
  try {
5085
- const raw = await readFile7(file, "utf-8");
5745
+ const raw = await readFile9(file, "utf-8");
5086
5746
  return parseWorkflowMeta(raw, taskId);
5087
5747
  } catch {
5088
5748
  return null;
@@ -5101,13 +5761,24 @@ async function updateWorkflow(projectRoot, taskId, patch) {
5101
5761
  startedAt: existing.startedAt,
5102
5762
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
5103
5763
  };
5764
+ const explicitlyPatchedBlockingReason = Object.hasOwn(
5765
+ patch,
5766
+ "blockingReason"
5767
+ );
5768
+ if (updated.status !== "blocked" && !explicitlyPatchedBlockingReason) {
5769
+ updated.blockingReason = void 0;
5770
+ }
5771
+ await validateWorkflowMeta(projectRoot, updated, existing);
5104
5772
  await writeMetadata(workflowDir(projectRoot, taskId), updated);
5773
+ if (updated.parentTaskId) {
5774
+ await propagateChildStatus(projectRoot, updated);
5775
+ }
5105
5776
  }
5106
5777
  async function listWorkflows(projectRoot) {
5107
5778
  const root = workflowsRoot(projectRoot);
5108
5779
  let entries;
5109
5780
  try {
5110
- entries = await readdir3(root);
5781
+ entries = await readdir5(root);
5111
5782
  } catch {
5112
5783
  return [];
5113
5784
  }
@@ -5123,10 +5794,14 @@ async function deleteWorkflow(projectRoot, taskId) {
5123
5794
  if (!isValidWorkflowTaskId(taskId)) return false;
5124
5795
  const dir = workflowDir(projectRoot, taskId);
5125
5796
  try {
5126
- await stat2(dir);
5797
+ await stat3(dir);
5127
5798
  } catch {
5128
5799
  return false;
5129
5800
  }
5801
+ const existing = await readWorkflow(projectRoot, taskId);
5802
+ if (existing && (existing.mode === "man" || existing.mode === "manteam") && (await listMambaChildren(projectRoot, taskId)).length > 0) {
5803
+ return false;
5804
+ }
5130
5805
  await rm4(dir, { recursive: true, force: true });
5131
5806
  return true;
5132
5807
  }
@@ -5148,7 +5823,7 @@ async function writeMetadata(dir, meta) {
5148
5823
  function parseWorkflowMeta(raw, taskId) {
5149
5824
  try {
5150
5825
  const obj = JSON.parse(raw);
5151
- if (typeof obj.task !== "string" || typeof obj.mode !== "string" || typeof obj.currentStep !== "number" || !Array.isArray(obj.skippedSteps) || typeof obj.startedAt !== "string" || typeof obj.updatedAt !== "string" || typeof obj.status !== "string") {
5826
+ if (typeof obj.task !== "string" || !isWorkflowMode(obj.mode) || typeof obj.currentStep !== "number" || !Array.isArray(obj.skippedSteps) || typeof obj.startedAt !== "string" || typeof obj.updatedAt !== "string" || !isWorkflowStatus(obj.status) || !isWorkflowShape(obj)) {
5152
5827
  return null;
5153
5828
  }
5154
5829
  return { ...obj, taskId };
@@ -5156,7 +5831,182 @@ function parseWorkflowMeta(raw, taskId) {
5156
5831
  return null;
5157
5832
  }
5158
5833
  }
5159
- function isNodeError5(err) {
5834
+ function maxWorkflowStep(mode) {
5835
+ return mode === "mamba" ? 5 : 9;
5836
+ }
5837
+ function isWorkflowMode(value) {
5838
+ return value === "man" || value === "mamba" || value === "manteam";
5839
+ }
5840
+ function isWorkflowStatus(value) {
5841
+ return value === "in_progress" || value === "planned" || value === "completed" || value === "blocked" || value === "abandoned";
5842
+ }
5843
+ function isWorkflowOutcome(value) {
5844
+ return value === "fixed" || value === "verified" || value === "no_repro" || value === "manual_test_required";
5845
+ }
5846
+ async function validateParentTask(projectRoot, mode, parentTaskId, requireStepSix = false) {
5847
+ if (!parentTaskId) return;
5848
+ if (mode !== "mamba") {
5849
+ throw new Error("only mamba workflows can have a parent task");
5850
+ }
5851
+ const parent = await readWorkflow(projectRoot, parentTaskId);
5852
+ if (!parent || parent.mode !== "man" && parent.mode !== "manteam") {
5853
+ throw new Error(`invalid parent workflow: ${parentTaskId}`);
5854
+ }
5855
+ if (parent.status !== "in_progress" && parent.status !== "blocked" || requireStepSix && (parent.status !== "in_progress" || parent.currentStep !== 6)) {
5856
+ throw new Error(
5857
+ `parent workflow must be in_progress at step 6: ${parentTaskId}`
5858
+ );
5859
+ }
5860
+ }
5861
+ async function validateWorkflowMeta(projectRoot, updated, existing) {
5862
+ if (updated.mode !== existing.mode) {
5863
+ throw new Error("workflow mode cannot be changed");
5864
+ }
5865
+ if (updated.task !== existing.task) {
5866
+ throw new Error("workflow task cannot be changed");
5867
+ }
5868
+ if (updated.parentTaskId !== existing.parentTaskId) {
5869
+ throw new Error("workflow parent task cannot be changed");
5870
+ }
5871
+ if (!Number.isInteger(updated.currentStep) || updated.currentStep < 1 || updated.currentStep > maxWorkflowStep(updated.mode)) {
5872
+ throw new Error(`invalid workflow step: ${updated.currentStep}`);
5873
+ }
5874
+ if (!isWorkflowStatus(updated.status)) {
5875
+ throw new Error(`invalid workflow status: ${String(updated.status)}`);
5876
+ }
5877
+ if (!Array.isArray(updated.skippedSteps) || updated.skippedSteps.some((step) => typeof step !== "string")) {
5878
+ throw new Error("workflow skipped steps must be strings");
5879
+ }
5880
+ if (!canTransition(existing.status, updated.status)) {
5881
+ throw new Error(
5882
+ `invalid workflow status transition: ${existing.status} -> ${updated.status}`
5883
+ );
5884
+ }
5885
+ if (updated.status === "planned" && updated.mode === "mamba") {
5886
+ throw new Error("mamba workflows cannot be planned");
5887
+ }
5888
+ if (updated.status === "planned" && updated.currentStep !== 4) {
5889
+ throw new Error("planned workflows must be at step 4");
5890
+ }
5891
+ if (updated.status === "completed" && updated.currentStep !== maxWorkflowStep(updated.mode)) {
5892
+ throw new Error(
5893
+ `completed ${updated.mode} workflows must be at step ${maxWorkflowStep(updated.mode)}`
5894
+ );
5895
+ }
5896
+ if (updated.blockingReason !== void 0 && typeof updated.blockingReason !== "string") {
5897
+ throw new Error("workflow blocking reason must be a string");
5898
+ }
5899
+ if (updated.status === "blocked") {
5900
+ if (!updated.blockingReason?.trim()) {
5901
+ throw new Error("blocked workflows require a blocking reason");
5902
+ }
5903
+ } else if (updated.blockingReason !== void 0) {
5904
+ throw new Error("only blocked workflows can have a blocking reason");
5905
+ }
5906
+ if (updated.mode !== "mamba" && updated.outcome !== void 0) {
5907
+ throw new Error("only mamba workflows can have an outcome");
5908
+ }
5909
+ if (updated.outcome !== void 0 && !isWorkflowOutcome(updated.outcome)) {
5910
+ throw new Error(`invalid workflow outcome: ${updated.outcome}`);
5911
+ }
5912
+ if (updated.outcome !== void 0 && updated.status !== "completed") {
5913
+ throw new Error("mamba outcomes can only be set on completed workflows");
5914
+ }
5915
+ if (updated.mode === "mamba" && updated.status === "completed" && !updated.outcome) {
5916
+ throw new Error("completed mamba workflows require an outcome");
5917
+ }
5918
+ if (updated.mode === "mamba" && updated.planVersion !== void 0) {
5919
+ throw new Error("mamba workflows cannot have a plan version");
5920
+ }
5921
+ if (updated.planVersion !== void 0 && (!Number.isInteger(updated.planVersion) || updated.planVersion < 1)) {
5922
+ throw new Error("workflow plan version must be a positive integer");
5923
+ }
5924
+ if (updated.planVersion !== existing.planVersion && updated.planVersion !== void 0 && updated.planVersion !== (existing.planVersion ?? 1) + 1) {
5925
+ throw new Error("workflow plan version must increase exactly once");
5926
+ }
5927
+ if (updated.planVersion !== existing.planVersion && updated.currentStep !== 4) {
5928
+ throw new Error("workflow plan version can only change at step 4");
5929
+ }
5930
+ await validateParentTask(projectRoot, updated.mode, updated.parentTaskId);
5931
+ if ((isTerminalWorkflowStatus(updated.status) || updated.status === "planned") && (await listActiveMambaChildren(projectRoot, updated.taskId)).length > 0) {
5932
+ throw new Error("cannot finish workflow with an active mamba child");
5933
+ }
5934
+ }
5935
+ function canTransition(from, to) {
5936
+ if (from === to) return true;
5937
+ if (from === "in_progress") return true;
5938
+ if (from === "planned" || from === "blocked") {
5939
+ return to === "in_progress" || to === "abandoned";
5940
+ }
5941
+ return false;
5942
+ }
5943
+ function isTerminalWorkflowStatus(status2) {
5944
+ return status2 === "completed" || status2 === "abandoned";
5945
+ }
5946
+ function isWorkflowShape(obj) {
5947
+ const { currentStep, mode, status: status2 } = obj;
5948
+ if (!Number.isInteger(currentStep) || !mode || currentStep === void 0 || currentStep < 1 || currentStep > maxWorkflowStep(mode)) {
5949
+ return false;
5950
+ }
5951
+ if (!status2 || !Array.isArray(obj.skippedSteps)) return false;
5952
+ if (obj.skippedSteps.some((step) => typeof step !== "string")) return false;
5953
+ if (obj.blockingReason !== void 0 && typeof obj.blockingReason !== "string")
5954
+ return false;
5955
+ if (obj.parentTaskId !== void 0 && typeof obj.parentTaskId !== "string")
5956
+ return false;
5957
+ if (obj.outcome !== void 0 && !isWorkflowOutcome(obj.outcome))
5958
+ return false;
5959
+ if (obj.planVersion !== void 0 && (!Number.isInteger(obj.planVersion) || obj.planVersion < 1))
5960
+ return false;
5961
+ if (status2 === "blocked") {
5962
+ if (!obj.blockingReason?.trim()) return false;
5963
+ } else if (obj.blockingReason !== void 0) {
5964
+ return false;
5965
+ }
5966
+ if (mode === "mamba") {
5967
+ if (status2 === "planned" || obj.planVersion !== void 0) return false;
5968
+ if (obj.parentTaskId !== void 0 && !isValidWorkflowTaskId(obj.parentTaskId))
5969
+ return false;
5970
+ if (obj.outcome !== void 0 && status2 !== "completed") return false;
5971
+ if (status2 === "completed" && obj.outcome === void 0) return false;
5972
+ } else if (obj.parentTaskId !== void 0 || obj.outcome !== void 0) {
5973
+ return false;
5974
+ }
5975
+ return true;
5976
+ }
5977
+ async function listActiveMambaChildren(projectRoot, parentTaskId) {
5978
+ return (await listMambaChildren(projectRoot, parentTaskId)).filter(
5979
+ (workflow2) => workflow2.status === "in_progress" || workflow2.status === "blocked"
5980
+ );
5981
+ }
5982
+ async function listMambaChildren(projectRoot, parentTaskId) {
5983
+ return (await listWorkflows(projectRoot)).filter(
5984
+ (workflow2) => workflow2.mode === "mamba" && workflow2.parentTaskId === parentTaskId
5985
+ );
5986
+ }
5987
+ async function propagateChildStatus(projectRoot, child) {
5988
+ const shouldBlockParent = child.status === "blocked" || child.status === "completed" && child.outcome === "manual_test_required";
5989
+ if (!shouldBlockParent) return;
5990
+ const parentTaskId = child.parentTaskId;
5991
+ if (!parentTaskId) return;
5992
+ const parent = await readWorkflow(projectRoot, parentTaskId);
5993
+ if (!parent || parent.status !== "in_progress") return;
5994
+ const reason = child.status === "blocked" ? `mamba child ${child.taskId} is blocked: ${child.blockingReason ?? "unknown reason"}` : `mamba child ${child.taskId} requires manual testing`;
5995
+ await writeMetadata(workflowDir(projectRoot, parent.taskId), {
5996
+ ...parent,
5997
+ status: "blocked",
5998
+ blockingReason: reason,
5999
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
6000
+ });
6001
+ if (parent.mode === "man" || parent.mode === "manteam") {
6002
+ await upsertActivePlan(projectRoot, {
6003
+ taskId: parent.taskId,
6004
+ status: "blocked",
6005
+ planVersion: parent.planVersion ?? 1
6006
+ });
6007
+ }
6008
+ }
6009
+ function isNodeError6(err) {
5160
6010
  return err instanceof Error && "code" in err;
5161
6011
  }
5162
6012
  function assertValidTaskId(taskId) {
@@ -5172,7 +6022,7 @@ var EXIT_NOT_INITIALIZED4 = 1;
5172
6022
  var EXIT_CORRUPT_STATE = 2;
5173
6023
  async function status(rootDir = process7.cwd(), options = {}) {
5174
6024
  const stateFile = path21.join(rootDir, ".mancode", "state.json");
5175
- if (!await pathExists10(stateFile)) {
6025
+ if (!await pathExists9(stateFile)) {
5176
6026
  console.error("\u2717 mancode not initialized.");
5177
6027
  console.error(" Run `mancode init` to get started.");
5178
6028
  return EXIT_NOT_INITIALIZED4;
@@ -5248,12 +6098,24 @@ async function getCurrentWorkflow(rootDir, taskId) {
5248
6098
  if (!taskId) return null;
5249
6099
  const meta = await readWorkflow(rootDir, taskId);
5250
6100
  if (!meta) return null;
6101
+ const activeChildren = meta.mode === "man" || meta.mode === "manteam" ? (await listWorkflows(rootDir)).filter(
6102
+ (candidate) => candidate.mode === "mamba" && candidate.parentTaskId === meta.taskId && (candidate.status === "in_progress" || candidate.status === "blocked")
6103
+ ).map((candidate) => ({
6104
+ taskId: candidate.taskId,
6105
+ currentStep: candidate.currentStep,
6106
+ status: candidate.status
6107
+ })) : void 0;
5251
6108
  return {
5252
6109
  taskId: meta.taskId,
5253
6110
  task: meta.task,
5254
6111
  mode: meta.mode,
5255
6112
  currentStep: meta.currentStep,
5256
- status: meta.status
6113
+ status: meta.status,
6114
+ ...meta.blockingReason ? { blockingReason: meta.blockingReason } : {},
6115
+ ...meta.parentTaskId ? { parentTaskId: meta.parentTaskId } : {},
6116
+ ...meta.outcome ? { outcome: meta.outcome } : {},
6117
+ ...meta.planVersion ? { planVersion: meta.planVersion } : {},
6118
+ ...activeChildren ? { activeChildren } : {}
5257
6119
  };
5258
6120
  }
5259
6121
  async function getProjectName(rootDir) {
@@ -5297,8 +6159,8 @@ function getEffectiveTeamStatus(state, config, detected) {
5297
6159
  }
5298
6160
  async function checkHooks(rootDir) {
5299
6161
  const [sessionStart, userPromptSubmit, registered] = await Promise.all([
5300
- pathExists10(path21.join(rootDir, ".mancode", "hooks", "session-start.sh")),
5301
- pathExists10(
6162
+ pathExists9(path21.join(rootDir, ".mancode", "hooks", "session-start.sh")),
6163
+ pathExists9(
5302
6164
  path21.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
5303
6165
  ),
5304
6166
  isRegistered(rootDir)
@@ -5318,9 +6180,9 @@ async function isRegistered(rootDir) {
5318
6180
  }
5319
6181
  }
5320
6182
  function hooksRegistered2(settings) {
5321
- if (!isRecord5(settings)) return false;
6183
+ if (!isRecord7(settings)) return false;
5322
6184
  const hooks = settings.hooks;
5323
- if (!isRecord5(hooks)) return false;
6185
+ if (!isRecord7(hooks)) return false;
5324
6186
  return hasHookCommand2(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand2(
5325
6187
  hooks.UserPromptSubmit,
5326
6188
  ".mancode/hooks/user-prompt-submit.sh"
@@ -5329,9 +6191,9 @@ function hooksRegistered2(settings) {
5329
6191
  function hasHookCommand2(value, needle) {
5330
6192
  if (!Array.isArray(value)) return false;
5331
6193
  return value.some((group) => {
5332
- if (!isRecord5(group) || !Array.isArray(group.hooks)) return false;
6194
+ if (!isRecord7(group) || !Array.isArray(group.hooks)) return false;
5333
6195
  return group.hooks.some((hook) => {
5334
- if (!isRecord5(hook) || typeof hook.command !== "string") return false;
6196
+ if (!isRecord7(hook) || typeof hook.command !== "string") return false;
5335
6197
  return hook.command.includes(needle);
5336
6198
  });
5337
6199
  });
@@ -5343,7 +6205,7 @@ async function estimateHookInjection(rootDir) {
5343
6205
  "hooks",
5344
6206
  "user-prompt-submit.sh"
5345
6207
  );
5346
- if (!await pathExists10(hookPath)) {
6208
+ if (!await pathExists9(hookPath)) {
5347
6209
  return { tokens: 0, cap: 800 };
5348
6210
  }
5349
6211
  try {
@@ -5405,6 +6267,23 @@ function printText(r) {
5405
6267
  console.log(
5406
6268
  `Workflow: ${r.currentWorkflow.taskId} (Step ${r.currentWorkflow.currentStep}/${stepMax}, ${r.currentWorkflow.status})`
5407
6269
  );
6270
+ if (r.currentWorkflow.parentTaskId) {
6271
+ console.log(`Parent: ${r.currentWorkflow.parentTaskId}`);
6272
+ }
6273
+ if (r.currentWorkflow.planVersion !== void 0) {
6274
+ console.log(`Plan version: ${r.currentWorkflow.planVersion}`);
6275
+ }
6276
+ if (r.currentWorkflow.outcome) {
6277
+ console.log(`Outcome: ${r.currentWorkflow.outcome}`);
6278
+ }
6279
+ if (r.currentWorkflow.blockingReason) {
6280
+ console.log(`Blocked: ${r.currentWorkflow.blockingReason}`);
6281
+ }
6282
+ if (r.currentWorkflow.activeChildren?.length) {
6283
+ console.log(
6284
+ `Children: ${r.currentWorkflow.activeChildren.map((child) => `${child.taskId} (${child.status})`).join(", ")}`
6285
+ );
6286
+ }
5408
6287
  }
5409
6288
  console.log("");
5410
6289
  console.log("Installed platforms:");
@@ -5444,12 +6323,12 @@ function formatTeamStatus(team) {
5444
6323
  return "solo";
5445
6324
  }
5446
6325
  function workflowStepMax(mode) {
5447
- return mode === "man8" ? 3 : 8;
6326
+ return maxWorkflowStep(mode);
5448
6327
  }
5449
- function isRecord5(value) {
6328
+ function isRecord7(value) {
5450
6329
  return typeof value === "object" && value !== null;
5451
6330
  }
5452
- async function pathExists10(p) {
6331
+ async function pathExists9(p) {
5453
6332
  try {
5454
6333
  await fs7.access(p);
5455
6334
  return true;
@@ -5459,30 +6338,15 @@ async function pathExists10(p) {
5459
6338
  }
5460
6339
 
5461
6340
  // src/commands/uninstall.ts
5462
- import { access as access5, readFile as readFile8, rm as rm5, writeFile as writeFile11 } from "fs/promises";
6341
+ import { access as access4, readFile as readFile10, rm as rm5, writeFile as writeFile11 } from "fs/promises";
5463
6342
  import path22 from "path";
5464
6343
  import process8 from "process";
5465
6344
  var EXIT_OK7 = 0;
5466
6345
  var EXIT_NOT_INITIALIZED5 = 1;
5467
6346
  var EXIT_UNSUPPORTED_PLATFORM2 = 2;
5468
- var CLAUDE_SKILL_DIRS = [
5469
- "solo",
5470
- "man8",
5471
- "man",
5472
- "manteam",
5473
- "manps",
5474
- "mansolo"
5475
- ];
5476
- var CLAUDE_AGENT_FILES = [
5477
- "scout.md",
5478
- "head-coach.md",
5479
- "film-analyst-offense.md",
5480
- "film-analyst-defense.md",
5481
- "plan-coach.md"
5482
- ];
5483
6347
  async function uninstall(rootDir = process8.cwd(), platform, options = {}) {
5484
6348
  const stateFile = path22.join(rootDir, ".mancode", "state.json");
5485
- if (!await pathExists11(stateFile)) {
6349
+ if (!await pathExists10(stateFile)) {
5486
6350
  console.error("\u2717 mancode not initialized.");
5487
6351
  console.error(" Run `mancode init` first.");
5488
6352
  return EXIT_NOT_INITIALIZED5;
@@ -5539,22 +6403,14 @@ async function uninstallAll(rootDir) {
5539
6403
  });
5540
6404
  }
5541
6405
  async function uninstallClaudeCode(rootDir) {
5542
- const claudeDir = path22.join(rootDir, ".claude");
5543
- const skillsDir = path22.join(claudeDir, "skills");
5544
- const agentsDir = path22.join(claudeDir, "agents");
5545
- for (const skillDir of CLAUDE_SKILL_DIRS) {
5546
- await rm5(path22.join(skillsDir, skillDir), { recursive: true, force: true });
5547
- }
5548
- for (const agentFile of CLAUDE_AGENT_FILES) {
5549
- await rm5(path22.join(agentsDir, agentFile), { force: true });
5550
- }
6406
+ await removeClaudeGeneratedContent(rootDir);
5551
6407
  await cleanClaudeSettings(rootDir);
5552
6408
  }
5553
6409
  async function cleanClaudeSettings(rootDir) {
5554
6410
  const settingsPath = path22.join(rootDir, ".claude", "settings.json");
5555
6411
  let content;
5556
6412
  try {
5557
- content = await readFile8(settingsPath, "utf-8");
6413
+ content = await readFile10(settingsPath, "utf-8");
5558
6414
  } catch {
5559
6415
  return;
5560
6416
  }
@@ -5564,28 +6420,21 @@ async function cleanClaudeSettings(rootDir) {
5564
6420
  } catch {
5565
6421
  return;
5566
6422
  }
5567
- if (!isRecord6(settings.hooks)) return;
5568
6423
  const cleanedHooks = {};
5569
- for (const [event, groups] of Object.entries(settings.hooks)) {
5570
- if (!Array.isArray(groups)) {
5571
- cleanedHooks[event] = groups;
5572
- continue;
5573
- }
5574
- const filtered = groups.flatMap((group) => {
5575
- if (!isRecord6(group) || !Array.isArray(group.hooks)) return [group];
5576
- const hooks = group.hooks.filter(
5577
- (hook) => !(isRecord6(hook) && typeof hook.command === "string" && hook.command.includes(".mancode/hooks/"))
5578
- );
5579
- return hooks.length > 0 ? [{ ...group, hooks }] : [];
5580
- });
5581
- if (filtered.length > 0) {
5582
- cleanedHooks[event] = filtered;
6424
+ if (isRecord8(settings.hooks)) {
6425
+ for (const [event, value] of Object.entries(settings.hooks)) {
6426
+ const cleaned = cleanClaudeHookValue(value);
6427
+ if (cleaned !== void 0) cleanedHooks[event] = cleaned;
5583
6428
  }
6429
+ settings.hooks = Object.keys(cleanedHooks).length > 0 ? cleanedHooks : void 0;
5584
6430
  }
5585
- if (Object.keys(cleanedHooks).length > 0) {
5586
- settings.hooks = cleanedHooks;
5587
- } else {
5588
- settings.hooks = void 0;
6431
+ if (isRecord8(settings.skills)) {
6432
+ const retainedSkills = Object.fromEntries(
6433
+ Object.entries(settings.skills).filter(
6434
+ ([name, value]) => LEGACY_CLAUDE_SKILL_SETTINGS[name] !== value
6435
+ )
6436
+ );
6437
+ settings.skills = Object.keys(retainedSkills).length > 0 ? retainedSkills : void 0;
5589
6438
  }
5590
6439
  await writeFile11(
5591
6440
  settingsPath,
@@ -5594,17 +6443,42 @@ async function cleanClaudeSettings(rootDir) {
5594
6443
  "utf-8"
5595
6444
  );
5596
6445
  }
5597
- async function uninstallCursor(rootDir) {
5598
- const rulesDir = path22.join(rootDir, ".cursor", "rules");
5599
- for (const file of MANCODE_CURSOR_RULE_FILES) {
5600
- await rm5(path22.join(rulesDir, file), { force: true });
6446
+ function cleanClaudeHookValue(value) {
6447
+ if (Array.isArray(value)) {
6448
+ const entries2 = value.flatMap(cleanClaudeHookEntry);
6449
+ return entries2.length > 0 ? entries2 : void 0;
6450
+ }
6451
+ if (!isRecord8(value)) return value;
6452
+ if (Array.isArray(value.hooks) || typeof value.command === "string") {
6453
+ const entries2 = cleanClaudeHookEntry(value);
6454
+ return entries2.length > 0 ? entries2 : void 0;
6455
+ }
6456
+ const entries = Object.entries(value).flatMap(([key, item]) => {
6457
+ const cleaned = cleanClaudeHookValue(item);
6458
+ return cleaned === void 0 ? [] : [[key, cleaned]];
6459
+ });
6460
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
6461
+ }
6462
+ function cleanClaudeHookEntry(entry) {
6463
+ if (Array.isArray(entry)) return entry.flatMap(cleanClaudeHookEntry);
6464
+ if (!isRecord8(entry)) return [entry];
6465
+ if (Array.isArray(entry.hooks)) {
6466
+ const hooks = entry.hooks.filter((hook) => !isMancodeHookEntry(hook));
6467
+ return hooks.length > 0 ? [{ ...entry, hooks }] : [];
5601
6468
  }
6469
+ return isMancodeHookEntry(entry) ? [] : [entry];
6470
+ }
6471
+ function isMancodeHookEntry(value) {
6472
+ return isRecord8(value) && typeof value.command === "string" && value.command.includes(".mancode/hooks/");
6473
+ }
6474
+ async function uninstallCursor(rootDir) {
6475
+ await removeCursorGeneratedRules(rootDir);
5602
6476
  await removeCursorCommands(rootDir);
5603
6477
  }
5604
6478
  async function uninstallCodex(rootDir) {
5605
6479
  const agentsPath = path22.join(rootDir, "AGENTS.md");
5606
6480
  try {
5607
- const content = await readFile8(agentsPath, "utf-8");
6481
+ const content = await readFile10(agentsPath, "utf-8");
5608
6482
  const cleaned = removeManagedBlock(content);
5609
6483
  if (cleaned.trim()) {
5610
6484
  await writeFile11(agentsPath, `${cleaned}
@@ -5614,9 +6488,7 @@ async function uninstallCodex(rootDir) {
5614
6488
  }
5615
6489
  } catch {
5616
6490
  }
5617
- if (!await otherAgentSkillsPlatformActive(rootDir, "codex")) {
5618
- await removeCodexSkills(rootDir);
5619
- }
6491
+ await removeCodexSkills(rootDir);
5620
6492
  }
5621
6493
  async function uninstallCopilot(rootDir) {
5622
6494
  const instructionsPath = path22.join(
@@ -5625,7 +6497,7 @@ async function uninstallCopilot(rootDir) {
5625
6497
  "copilot-instructions.md"
5626
6498
  );
5627
6499
  try {
5628
- const content = await readFile8(instructionsPath, "utf-8");
6500
+ const content = await readFile10(instructionsPath, "utf-8");
5629
6501
  const cleaned = removeManagedBlock(content);
5630
6502
  if (cleaned.trim()) {
5631
6503
  await writeFile11(instructionsPath, `${cleaned}
@@ -5640,7 +6512,7 @@ async function uninstallCopilot(rootDir) {
5640
6512
  async function uninstallZcode(rootDir) {
5641
6513
  const agentsPath = path22.join(rootDir, "AGENTS.md");
5642
6514
  try {
5643
- const content = await readFile8(agentsPath, "utf-8");
6515
+ const content = await readFile10(agentsPath, "utf-8");
5644
6516
  const cleaned = removeManagedBlock(
5645
6517
  content,
5646
6518
  ZCODE_MANCODE_START_MARKER,
@@ -5654,18 +6526,16 @@ async function uninstallZcode(rootDir) {
5654
6526
  }
5655
6527
  } catch {
5656
6528
  }
5657
- if (!await otherAgentSkillsPlatformActive(rootDir, "zcode")) {
5658
- await removeZcodeSkills(rootDir);
5659
- }
6529
+ await removeZcodeSkills(rootDir);
5660
6530
  }
5661
6531
  async function removeFromConfig(rootDir, platform) {
5662
6532
  const configPath = path22.join(rootDir, ".mancode", "config.json");
5663
6533
  try {
5664
- const raw = await readFile8(configPath, "utf-8");
6534
+ const raw = await readFile10(configPath, "utf-8");
5665
6535
  const config = JSON.parse(raw);
5666
6536
  if (Array.isArray(config.platforms)) {
5667
6537
  config.platforms = config.platforms.filter((p) => p !== platform);
5668
- if (isRecord6(config.platformOptions)) {
6538
+ if (isRecord8(config.platformOptions)) {
5669
6539
  const platformOptions = Object.fromEntries(
5670
6540
  Object.entries(config.platformOptions).filter(
5671
6541
  ([name]) => name !== platform
@@ -5683,31 +6553,17 @@ async function removeFromConfig(rootDir, platform) {
5683
6553
  } catch {
5684
6554
  }
5685
6555
  }
5686
- function isRecord6(value) {
6556
+ function isRecord8(value) {
5687
6557
  return typeof value === "object" && value !== null && !Array.isArray(value);
5688
6558
  }
5689
- async function pathExists11(p) {
6559
+ async function pathExists10(p) {
5690
6560
  try {
5691
- await access5(p);
6561
+ await access4(p);
5692
6562
  return true;
5693
6563
  } catch {
5694
6564
  return false;
5695
6565
  }
5696
6566
  }
5697
- async function otherAgentSkillsPlatformActive(rootDir, excluding) {
5698
- try {
5699
- const raw = await readFile8(
5700
- path22.join(rootDir, ".mancode", "config.json"),
5701
- "utf-8"
5702
- );
5703
- const config = JSON.parse(raw);
5704
- const platforms = Array.isArray(config.platforms) ? config.platforms : [];
5705
- const peers = ["codex", "zcode"].filter((p) => p !== excluding);
5706
- return platforms.some((p) => peers.includes(p));
5707
- } catch {
5708
- return false;
5709
- }
5710
- }
5711
6567
 
5712
6568
  // src/commands/version.ts
5713
6569
  import process9 from "process";
@@ -5720,13 +6576,13 @@ function version() {
5720
6576
  }
5721
6577
 
5722
6578
  // src/commands/workflow.ts
5723
- import { access as access6 } from "fs/promises";
6579
+ import { access as access5 } from "fs/promises";
5724
6580
  import path23 from "path";
5725
6581
  var EXIT_OK8 = 0;
5726
6582
  var EXIT_NOT_INITIALIZED6 = 1;
5727
6583
  var EXIT_INVALID_ARG2 = 2;
5728
6584
  async function workflow(rootDir, subcommand, args = [], options = {}) {
5729
- if (!await pathExists12(path23.join(rootDir, ".mancode", "state.json"))) {
6585
+ if (!await pathExists11(path23.join(rootDir, ".mancode", "state.json"))) {
5730
6586
  if (options.json) {
5731
6587
  console.log(JSON.stringify({ error: "not initialized" }, null, 2));
5732
6588
  } else {
@@ -5758,7 +6614,7 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
5758
6614
  } else {
5759
6615
  console.error(`\u2717 Invalid workflow subcommand: ${subcommand}`);
5760
6616
  console.error(
5761
- " Use: create <man8|man> <task> | update <taskId> | list | show <taskId> | clean"
6617
+ " Use: create <man|mamba|manteam> <task> | update <taskId> | list | show <taskId> | clean"
5762
6618
  );
5763
6619
  }
5764
6620
  return EXIT_INVALID_ARG2;
@@ -5767,25 +6623,34 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
5767
6623
  async function workflowCreate(rootDir, args, options) {
5768
6624
  const mode = args[0];
5769
6625
  const task = args.slice(1).join(" ").trim();
5770
- if (mode !== "man8" && mode !== "man") {
6626
+ if (mode !== "man" && mode !== "mamba" && mode !== "manteam") {
5771
6627
  return invalidArg(
5772
6628
  options,
5773
6629
  `invalid workflow mode: ${mode ?? ""}`,
5774
- "Use: mancode workflow create <man8|man> <task>"
6630
+ "Use: mancode workflow create <man|mamba|manteam> <task> [--parent-task <taskId>]"
5775
6631
  );
5776
6632
  }
5777
6633
  if (!task) {
5778
6634
  return invalidArg(
5779
6635
  options,
5780
6636
  "missing workflow task",
5781
- "Use: mancode workflow create <man8|man> <task>"
6637
+ "Use: mancode workflow create <man|mamba|manteam> <task> [--parent-task <taskId>]"
5782
6638
  );
5783
6639
  }
5784
- const meta = await createWorkflow(rootDir, task, mode);
5785
- if (options.json) {
5786
- console.log(JSON.stringify(meta, null, 2));
5787
- } else {
5788
- console.log(`Created workflow: ${meta.taskId}`);
6640
+ try {
6641
+ const meta = await createWorkflow(rootDir, task, mode, {
6642
+ parentTaskId: options.parentTask
6643
+ });
6644
+ if (options.json) {
6645
+ console.log(JSON.stringify(meta, null, 2));
6646
+ } else {
6647
+ console.log(`Created workflow: ${meta.taskId}`);
6648
+ }
6649
+ } catch (error) {
6650
+ return invalidArg(
6651
+ options,
6652
+ error instanceof Error ? error.message : "unable to create workflow"
6653
+ );
5789
6654
  }
5790
6655
  return EXIT_OK8;
5791
6656
  }
@@ -5794,7 +6659,7 @@ async function workflowUpdate(rootDir, taskId, options) {
5794
6659
  return invalidArg(
5795
6660
  options,
5796
6661
  "missing taskId",
5797
- "Use: mancode workflow update <taskId> [--step N] [--status in_progress|completed|abandoned] [--skipped a,b]"
6662
+ "Use: mancode workflow update <taskId> [--step N] [--status in_progress|planned|completed|blocked|abandoned] [--blocking-reason <reason>] [--outcome <outcome>] [--plan-version N] [--skipped a,b]"
5798
6663
  );
5799
6664
  }
5800
6665
  if (!isValidWorkflowTaskId(taskId)) {
@@ -5806,9 +6671,9 @@ async function workflowUpdate(rootDir, taskId, options) {
5806
6671
  }
5807
6672
  const patch = {};
5808
6673
  if (options.step !== void 0) {
5809
- const currentStep = Number.parseInt(options.step, 10);
5810
- const maxStep = existing.mode === "man" ? 8 : 3;
5811
- if (!Number.isInteger(currentStep) || currentStep < 1 || currentStep > maxStep) {
6674
+ const currentStep = parseExactPositiveInteger(options.step);
6675
+ const maxStep = maxWorkflowStep(existing.mode);
6676
+ if (currentStep === null || currentStep < 1 || currentStep > maxStep) {
5812
6677
  return invalidArg(options, `invalid --step: ${options.step}`);
5813
6678
  }
5814
6679
  patch.currentStep = currentStep;
@@ -5819,14 +6684,67 @@ async function workflowUpdate(rootDir, taskId, options) {
5819
6684
  }
5820
6685
  patch.status = options.status;
5821
6686
  }
6687
+ const nextStatus = patch.status ?? existing.status;
6688
+ if (options.blockingReason !== void 0) {
6689
+ if (nextStatus !== "blocked") {
6690
+ return invalidArg(
6691
+ options,
6692
+ "--blocking-reason requires --status blocked or an already blocked workflow"
6693
+ );
6694
+ }
6695
+ patch.blockingReason = options.blockingReason;
6696
+ }
6697
+ if (options.status === "in_progress" && existing.blockingReason) {
6698
+ patch.blockingReason = void 0;
6699
+ }
6700
+ if (options.outcome !== void 0) {
6701
+ if (!isWorkflowOutcome(options.outcome)) {
6702
+ return invalidArg(options, `invalid --outcome: ${options.outcome}`);
6703
+ }
6704
+ if (existing.mode !== "mamba" || nextStatus !== "completed") {
6705
+ return invalidArg(
6706
+ options,
6707
+ "--outcome is only valid when completing a mamba workflow"
6708
+ );
6709
+ }
6710
+ patch.outcome = options.outcome;
6711
+ }
6712
+ if (options.planVersion !== void 0) {
6713
+ const planVersion = parseExactPositiveInteger(options.planVersion);
6714
+ const currentVersion = existing.planVersion ?? 1;
6715
+ if (planVersion === null || existing.mode !== "man" && existing.mode !== "manteam" || planVersion !== currentVersion + 1 || (patch.currentStep ?? existing.currentStep) !== 4) {
6716
+ return invalidArg(
6717
+ options,
6718
+ `invalid --plan-version: ${options.planVersion}; expected ${currentVersion + 1} at step 4 for ${existing.mode}`
6719
+ );
6720
+ }
6721
+ patch.planVersion = planVersion;
6722
+ }
5822
6723
  if (options.skipped !== void 0) {
5823
6724
  patch.skippedSteps = options.skipped.split(",").map((item) => item.trim()).filter(Boolean);
5824
6725
  }
5825
6726
  if (Object.keys(patch).length === 0) {
5826
6727
  return invalidArg(options, "missing update fields");
5827
6728
  }
5828
- await updateWorkflow(rootDir, taskId, patch);
6729
+ try {
6730
+ await updateWorkflow(rootDir, taskId, patch);
6731
+ } catch (error) {
6732
+ return invalidArg(
6733
+ options,
6734
+ error instanceof Error ? error.message : "unable to update workflow"
6735
+ );
6736
+ }
5829
6737
  const updated = await readWorkflow(rootDir, taskId);
6738
+ if (updated && (updated.mode === "man" || updated.mode === "manteam")) {
6739
+ const shouldSyncPlan = updated.currentStep >= 4 || updated.status !== "in_progress";
6740
+ if (shouldSyncPlan) {
6741
+ await upsertActivePlan(rootDir, {
6742
+ taskId: updated.taskId,
6743
+ status: updated.status,
6744
+ planVersion: updated.planVersion ?? 1
6745
+ });
6746
+ }
6747
+ }
5830
6748
  if (options.json) {
5831
6749
  console.log(JSON.stringify(updated, null, 2));
5832
6750
  } else {
@@ -5836,8 +6754,9 @@ async function workflowUpdate(rootDir, taskId, options) {
5836
6754
  }
5837
6755
  async function workflowList(rootDir, options) {
5838
6756
  const workflows = await listWorkflows(rootDir);
6757
+ const views = attachActiveChildren(workflows);
5839
6758
  if (options.json) {
5840
- console.log(JSON.stringify(workflows, null, 2));
6759
+ console.log(JSON.stringify(views, null, 2));
5841
6760
  return EXIT_OK8;
5842
6761
  }
5843
6762
  if (workflows.length === 0) {
@@ -5849,7 +6768,7 @@ async function workflowList(rootDir, options) {
5849
6768
  `mancode workflows (${workflows.length} total, ${inProgress} in_progress)`
5850
6769
  );
5851
6770
  console.log("");
5852
- for (const meta of workflows) {
6771
+ for (const meta of views) {
5853
6772
  console.log(formatWorkflowRow(meta));
5854
6773
  }
5855
6774
  return EXIT_OK8;
@@ -5878,22 +6797,44 @@ async function workflowShow(rootDir, taskId, options) {
5878
6797
  }
5879
6798
  return EXIT_INVALID_ARG2;
5880
6799
  }
6800
+ const children = meta.mode === "man" || meta.mode === "manteam" ? attachActiveChildren(await listWorkflows(rootDir)).find(
6801
+ (workflow2) => workflow2.taskId === meta.taskId
6802
+ )?.activeChildren ?? [] : void 0;
5881
6803
  if (options.json) {
5882
- console.log(JSON.stringify(meta, null, 2));
6804
+ console.log(
6805
+ JSON.stringify(
6806
+ children === void 0 ? meta : { ...meta, activeChildren: children },
6807
+ null,
6808
+ 2
6809
+ )
6810
+ );
5883
6811
  return EXIT_OK8;
5884
6812
  }
5885
6813
  console.log(`Workflow: ${meta.taskId}`);
5886
6814
  console.log(`Task: ${meta.task}`);
5887
6815
  console.log(`Mode: ${meta.mode}`);
5888
6816
  console.log(`Status: ${meta.status}`);
5889
- console.log(
5890
- `Current step:${meta.currentStep}/${meta.mode === "man" ? 8 : 3}`
5891
- );
6817
+ console.log(`Current step:${meta.currentStep}/${maxWorkflowStep(meta.mode)}`);
5892
6818
  console.log(
5893
6819
  `Skipped: ${meta.skippedSteps.length > 0 ? meta.skippedSteps.join(", ") : "none"}`
5894
6820
  );
5895
6821
  console.log(`Started: ${meta.startedAt}`);
5896
6822
  console.log(`Updated: ${meta.updatedAt}`);
6823
+ if (meta.parentTaskId) console.log(`Parent: ${meta.parentTaskId}`);
6824
+ if (meta.planVersion !== void 0)
6825
+ console.log(`Plan version: ${meta.planVersion}`);
6826
+ if (meta.outcome) console.log(`Outcome: ${meta.outcome}`);
6827
+ if (meta.blockingReason) console.log(`Blocked: ${meta.blockingReason}`);
6828
+ if (children !== void 0) {
6829
+ if (children.length > 0) {
6830
+ console.log(`Children: ${children.length} active mamba workflow(s)`);
6831
+ for (const child of children) {
6832
+ console.log(
6833
+ ` - ${child.taskId} (Step ${child.currentStep}/5, ${child.status})`
6834
+ );
6835
+ }
6836
+ }
6837
+ }
5897
6838
  return EXIT_OK8;
5898
6839
  }
5899
6840
  async function workflowClean(rootDir, options) {
@@ -5915,41 +6856,74 @@ async function workflowClean(rootDir, options) {
5915
6856
  return EXIT_INVALID_ARG2;
5916
6857
  }
5917
6858
  const candidates = workflows.filter((meta) => {
5918
- if (!cutoff) return meta.status !== "in_progress";
6859
+ if (!isTerminalWorkflowStatus(meta.status)) return false;
6860
+ if (!cutoff) return true;
5919
6861
  const started = Date.parse(meta.startedAt);
5920
6862
  return Number.isFinite(started) && started < cutoff.getTime();
5921
6863
  });
6864
+ const removed = [];
6865
+ if (!options.dryRun) {
6866
+ for (const meta of candidates) {
6867
+ const didRemove = await deleteWorkflow(rootDir, meta.taskId);
6868
+ if (didRemove) removed.push(meta);
6869
+ if (didRemove && (meta.mode === "man" || meta.mode === "manteam")) {
6870
+ await upsertActivePlan(rootDir, {
6871
+ taskId: meta.taskId,
6872
+ status: meta.status,
6873
+ planVersion: meta.planVersion ?? 1
6874
+ });
6875
+ }
6876
+ }
6877
+ }
5922
6878
  if (options.json) {
5923
6879
  console.log(
5924
6880
  JSON.stringify(
5925
6881
  {
5926
6882
  dryRun: Boolean(options.dryRun),
5927
- count: candidates.length,
5928
- workflows: candidates
6883
+ count: options.dryRun ? candidates.length : removed.length,
6884
+ workflows: options.dryRun ? candidates : removed
5929
6885
  },
5930
6886
  null,
5931
6887
  2
5932
6888
  )
5933
6889
  );
5934
- } else if (candidates.length === 0) {
6890
+ } else if ((options.dryRun ? candidates : removed).length === 0) {
5935
6891
  console.log("No workflows to clean.");
5936
6892
  } else if (options.dryRun) {
5937
6893
  console.log(`Would remove ${candidates.length} workflow(s):`);
5938
6894
  for (const meta of candidates) console.log(` ${meta.taskId}`);
5939
6895
  } else {
5940
- console.log(`Removed ${candidates.length} workflow(s).`);
5941
- }
5942
- if (!options.dryRun) {
5943
- for (const meta of candidates) {
5944
- await deleteWorkflow(rootDir, meta.taskId);
5945
- }
6896
+ console.log(`Removed ${removed.length} workflow(s).`);
5946
6897
  }
5947
6898
  return EXIT_OK8;
5948
6899
  }
5949
6900
  function formatWorkflowRow(meta) {
5950
- const stepMax = meta.mode === "man" ? 8 : 3;
6901
+ const stepMax = maxWorkflowStep(meta.mode);
5951
6902
  const step = meta.status === "in_progress" ? `Step ${meta.currentStep}/${stepMax}` : ago(meta.updatedAt);
5952
- return `${meta.taskId.padEnd(42)} ${meta.mode.padEnd(5)} ${meta.status.padEnd(11)} ${step}`;
6903
+ const details = [
6904
+ meta.planVersion !== void 0 ? `plan=v${meta.planVersion}` : "",
6905
+ meta.parentTaskId ? `parent=${meta.parentTaskId}` : "",
6906
+ meta.outcome ? `outcome=${meta.outcome}` : "",
6907
+ meta.blockingReason ? `blocked=${meta.blockingReason}` : "",
6908
+ meta.activeChildren && meta.activeChildren.length > 0 ? `children=${meta.activeChildren.map((child) => child.taskId).join(",")}` : ""
6909
+ ].filter(Boolean);
6910
+ return `${meta.taskId.padEnd(42)} ${meta.mode.padEnd(7)} ${meta.status.padEnd(11)} ${step}${details.length > 0 ? ` | ${details.join(" | ")}` : ""}`;
6911
+ }
6912
+ function attachActiveChildren(workflows) {
6913
+ return workflows.map((meta) => {
6914
+ if (meta.mode !== "man" && meta.mode !== "manteam") return meta;
6915
+ return {
6916
+ ...meta,
6917
+ activeChildren: workflows.filter(
6918
+ (candidate) => candidate.mode === "mamba" && candidate.parentTaskId === meta.taskId && (candidate.status === "in_progress" || candidate.status === "blocked")
6919
+ )
6920
+ };
6921
+ });
6922
+ }
6923
+ function parseExactPositiveInteger(value) {
6924
+ if (!/^\d+$/.test(value)) return null;
6925
+ const parsed = Number(value);
6926
+ return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
5953
6927
  }
5954
6928
  function parseOlderThan(value) {
5955
6929
  if (!value) return void 0;
@@ -5969,9 +6943,6 @@ function invalidArg(options, message, usage) {
5969
6943
  }
5970
6944
  return EXIT_INVALID_ARG2;
5971
6945
  }
5972
- function isWorkflowStatus(value) {
5973
- return value === "in_progress" || value === "completed" || value === "abandoned";
5974
- }
5975
6946
  function ago(iso) {
5976
6947
  const time = Date.parse(iso);
5977
6948
  if (!Number.isFinite(time)) return "unknown";
@@ -5983,9 +6954,9 @@ function ago(iso) {
5983
6954
  if (hours < 24) return `${hours}h ago`;
5984
6955
  return `${Math.floor(hours / 24)}d ago`;
5985
6956
  }
5986
- async function pathExists12(p) {
6957
+ async function pathExists11(p) {
5987
6958
  try {
5988
- await access6(p);
6959
+ await access5(p);
5989
6960
  return true;
5990
6961
  } catch {
5991
6962
  return false;
@@ -5993,7 +6964,9 @@ async function pathExists12(p) {
5993
6964
  }
5994
6965
 
5995
6966
  // src/cli.ts
5996
- program.name("mancode").description("AI coding agent harness. Five modes: practice to playoffs.").version(VERSION);
6967
+ program.name("mancode").description(
6968
+ "AI coding agent harness. Modes: solo, /man, /mamba, /manteam, /manps."
6969
+ ).version(VERSION);
5997
6970
  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) => {
5998
6971
  const code = await init(process.cwd(), options);
5999
6972
  process.exitCode = code;
@@ -6020,7 +6993,10 @@ program.command("uninstall [platform]").description("Remove platform adapter or
6020
6993
  const code = await uninstall(process.cwd(), platform, options);
6021
6994
  process.exitCode = code;
6022
6995
  });
6023
- 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("--skipped <steps>", "Update skipped steps as comma-separated values").option("--json", "Output as JSON (for scripts)").action(async (subcommand, args, options) => {
6996
+ 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
+ "--parent-task <taskId>",
6998
+ "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) => {
6024
7000
  const code = await workflow(process.cwd(), subcommand, args ?? [], options);
6025
7001
  process.exitCode = code;
6026
7002
  });
@@ -6028,7 +7004,7 @@ program.command("manps [area]").description("Run deterministic preseason health
6028
7004
  const code = await manps(process.cwd(), area ?? "all", options);
6029
7005
  process.exitCode = code;
6030
7006
  });
6031
- program.command("refresh-style").description("Rescan project design tokens and update style-tokens.json").action(async () => {
7007
+ program.command("refresh-style").description("Refresh project profile and rescan applicable design tokens").action(async () => {
6032
7008
  const code = await refreshStyle(process.cwd());
6033
7009
  process.exitCode = code;
6034
7010
  });