mancode 0.2.0 → 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/README.md +78 -57
- package/README.zh-CN.md +62 -52
- package/dist/cli.js +1780 -774
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
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 {
|
|
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 /
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
81
|
+
- \`<detected-source-root>/<target-module>\` \u2014 \u6539\u4EC0\u4E48
|
|
73
82
|
|
|
74
83
|
## \u9700\u8981\u65B0\u5EFA\u7684\u6587\u4EF6
|
|
75
|
-
-
|
|
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
|
-
#
|
|
114
|
-
|
|
115
|
-
##
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
##
|
|
120
|
-
|
|
121
|
-
##
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
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
|
-
- \
|
|
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
|
-
- \
|
|
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
|
-
- \
|
|
290
|
-
- \
|
|
291
|
-
- \
|
|
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
|
|
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
|
|
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/.*: "
|
|
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
|
|
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
|
-
|
|
529
|
-
echo "
|
|
530
|
-
echo " -
|
|
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
|
|
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/.*: "
|
|
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
|
|
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\
|
|
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='
|
|
614
|
-
echo "\
|
|
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
|
-
# \
|
|
619
|
-
|
|
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. \
|
|
670
|
-
\
|
|
671
|
-
- \u4F7F\u7528\
|
|
672
|
-
- \
|
|
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. \
|
|
687
|
-
3. \
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
### \
|
|
698
|
-
\
|
|
699
|
-
- \
|
|
700
|
-
- \
|
|
701
|
-
- \
|
|
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: "
|
|
852
|
-
body: `# mancode \xB7 /man (
|
|
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\
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
809
|
+
### Step 1: Scout \u8C03\u7814
|
|
871
810
|
|
|
872
|
-
|
|
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:
|
|
813
|
+
### Step 2: \u9700\u6C42\u6F84\u6E05
|
|
884
814
|
|
|
885
|
-
|
|
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
|
-
### \
|
|
817
|
+
### Step 3: Plan Coach \u51FA\u8BA1\u5212
|
|
895
818
|
|
|
896
|
-
\u7528
|
|
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
|
-
|
|
914
|
-
|
|
915
|
-
|
|
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
|
|
828
|
+
### Step 5: \u5B9E\u65BD
|
|
918
829
|
|
|
919
|
-
|
|
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
|
|
832
|
+
### Step 6: \u81EA\u6D4B\u3001\u8BCA\u65AD\u4E0E\u56DE\u5F52
|
|
928
833
|
|
|
929
|
-
\
|
|
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
|
-
|
|
836
|
+
### Step 7: Film #1 \u4EE3\u7801\u8D28\u91CF\u5BA1\u67E5\u4E0E\u4FEE\u590D
|
|
932
837
|
|
|
933
|
-
\
|
|
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
|
|
840
|
+
### Step 8: Film #2 \u5B89\u5168\u4E0E\u8FB9\u754C\u5BA1\u67E5
|
|
936
841
|
|
|
937
|
-
\u7528
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
951
|
-
|
|
952
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
872
|
+
## 2. \u51C6\u5907\u73AF\u5883\u4E0E\u6743\u9650
|
|
973
873
|
|
|
974
|
-
|
|
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
|
-
|
|
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
|
-
\
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
890
|
+
|
|
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
|
|
1000
896
|
|
|
1001
|
-
|
|
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
|
|
1103
|
-
- \u8BC6\u522B
|
|
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
|
|
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. \
|
|
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
|
-
|
|
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\
|
|
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
|
|
1214
|
-
- \
|
|
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\
|
|
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
|
|
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-
|
|
1167
|
+
## Step 1-9: Run Progressive /man Flow
|
|
1257
1168
|
|
|
1258
|
-
\u6309 \`/man\` \u7684
|
|
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.
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
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\
|
|
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
|
|
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(
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
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
|
|
1532
|
-
|
|
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
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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,23 @@ function findMarkerLine(content, marker) {
|
|
|
1774
1818
|
}
|
|
1775
1819
|
|
|
1776
1820
|
// src/installers/mode-skills.ts
|
|
1777
|
-
import { mkdir as mkdir4, readFile as
|
|
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
|
-
"
|
|
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. -->";
|
|
1834
|
+
var MANCODE_AGENT_SKILL_MARKERS = [
|
|
1835
|
+
CODEX_SKILL_MANAGED_MARKER,
|
|
1836
|
+
ZCODE_SKILL_MANAGED_MARKER
|
|
1837
|
+
];
|
|
1788
1838
|
function renderModeSkill(mode, commandPrefix = "$") {
|
|
1789
1839
|
const meta = MODE_META[mode];
|
|
1790
1840
|
const stateMode = mode === "mansolo" ? "solo" : mode;
|
|
@@ -1815,8 +1865,9 @@ function renderModeSkill(mode, commandPrefix = "$") {
|
|
|
1815
1865
|
"For every task, consider: why this change, what already exists, and what",
|
|
1816
1866
|
"is the smallest useful diff?",
|
|
1817
1867
|
"",
|
|
1818
|
-
"
|
|
1819
|
-
"
|
|
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.",
|
|
1820
1871
|
"",
|
|
1821
1872
|
meta.workflow
|
|
1822
1873
|
].join("\n");
|
|
@@ -1826,7 +1877,8 @@ async function installCodexSkills(projectRoot, minimal) {
|
|
|
1826
1877
|
await removeCodexSkills(projectRoot);
|
|
1827
1878
|
return;
|
|
1828
1879
|
}
|
|
1829
|
-
const skillsDir = path4.join(projectRoot, ".
|
|
1880
|
+
const skillsDir = path4.join(projectRoot, ".agents", "skills");
|
|
1881
|
+
await removeLegacyManagedSkills(skillsDir, MANCODE_AGENT_SKILL_MARKERS);
|
|
1830
1882
|
for (const mode of MODE_NAMES) {
|
|
1831
1883
|
const modeDir = path4.join(skillsDir, mode);
|
|
1832
1884
|
await mkdir4(modeDir, { recursive: true });
|
|
@@ -1846,7 +1898,8 @@ async function installCodexSkills(projectRoot, minimal) {
|
|
|
1846
1898
|
path4.join(modeDir, "SKILL.md"),
|
|
1847
1899
|
content,
|
|
1848
1900
|
CODEX_SKILL_MANAGED_MARKER,
|
|
1849
|
-
"Codex"
|
|
1901
|
+
"Codex",
|
|
1902
|
+
MANCODE_AGENT_SKILL_MARKERS
|
|
1850
1903
|
);
|
|
1851
1904
|
}
|
|
1852
1905
|
}
|
|
@@ -1855,7 +1908,8 @@ async function installZcodeSkills(projectRoot, minimal) {
|
|
|
1855
1908
|
await removeZcodeSkills(projectRoot);
|
|
1856
1909
|
return;
|
|
1857
1910
|
}
|
|
1858
|
-
const skillsDir = path4.join(projectRoot, ".
|
|
1911
|
+
const skillsDir = path4.join(projectRoot, ".agents", "skills");
|
|
1912
|
+
await removeLegacyManagedSkills(skillsDir, MANCODE_AGENT_SKILL_MARKERS);
|
|
1859
1913
|
for (const mode of MODE_NAMES) {
|
|
1860
1914
|
const modeDir = path4.join(skillsDir, mode);
|
|
1861
1915
|
await mkdir4(modeDir, { recursive: true });
|
|
@@ -1875,7 +1929,8 @@ async function installZcodeSkills(projectRoot, minimal) {
|
|
|
1875
1929
|
path4.join(modeDir, "SKILL.md"),
|
|
1876
1930
|
content,
|
|
1877
1931
|
ZCODE_SKILL_MANAGED_MARKER,
|
|
1878
|
-
"ZCode"
|
|
1932
|
+
"ZCode",
|
|
1933
|
+
MANCODE_AGENT_SKILL_MARKERS
|
|
1879
1934
|
);
|
|
1880
1935
|
}
|
|
1881
1936
|
}
|
|
@@ -1886,6 +1941,7 @@ async function installCursorCommands(projectRoot, minimal) {
|
|
|
1886
1941
|
}
|
|
1887
1942
|
const commandsDir = path4.join(projectRoot, ".cursor", "commands");
|
|
1888
1943
|
await mkdir4(commandsDir, { recursive: true });
|
|
1944
|
+
await removeLegacyGeneratedModeFile(path4.join(commandsDir, "man8.md"));
|
|
1889
1945
|
for (const mode of MODE_NAMES) {
|
|
1890
1946
|
const meta = MODE_META[mode];
|
|
1891
1947
|
const content = [
|
|
@@ -1893,10 +1949,17 @@ async function installCursorCommands(projectRoot, minimal) {
|
|
|
1893
1949
|
`description: ${JSON.stringify(meta.description)}`,
|
|
1894
1950
|
"---",
|
|
1895
1951
|
"",
|
|
1952
|
+
MODE_FILE_MANAGED_MARKER,
|
|
1953
|
+
"",
|
|
1896
1954
|
renderModeSkill(mode, "/"),
|
|
1897
1955
|
""
|
|
1898
1956
|
].join("\n");
|
|
1899
|
-
await
|
|
1957
|
+
await writeManagedModeFile(
|
|
1958
|
+
path4.join(commandsDir, `${mode}.md`),
|
|
1959
|
+
content,
|
|
1960
|
+
mode,
|
|
1961
|
+
"Cursor"
|
|
1962
|
+
);
|
|
1900
1963
|
}
|
|
1901
1964
|
}
|
|
1902
1965
|
async function installCopilotPrompts(projectRoot, minimal) {
|
|
@@ -1906,6 +1969,7 @@ async function installCopilotPrompts(projectRoot, minimal) {
|
|
|
1906
1969
|
}
|
|
1907
1970
|
const promptsDir = path4.join(projectRoot, ".github", "prompts");
|
|
1908
1971
|
await mkdir4(promptsDir, { recursive: true });
|
|
1972
|
+
await removeLegacyGeneratedModeFile(path4.join(promptsDir, "man8.prompt.md"));
|
|
1909
1973
|
for (const mode of MODE_NAMES) {
|
|
1910
1974
|
const meta = MODE_META[mode];
|
|
1911
1975
|
const content = [
|
|
@@ -1914,137 +1978,232 @@ async function installCopilotPrompts(projectRoot, minimal) {
|
|
|
1914
1978
|
`description: ${JSON.stringify(meta.description)}`,
|
|
1915
1979
|
"---",
|
|
1916
1980
|
"",
|
|
1981
|
+
MODE_FILE_MANAGED_MARKER,
|
|
1982
|
+
"",
|
|
1917
1983
|
renderModeSkill(mode, ""),
|
|
1918
1984
|
""
|
|
1919
1985
|
].join("\n");
|
|
1920
|
-
await
|
|
1986
|
+
await writeManagedModeFile(
|
|
1921
1987
|
path4.join(promptsDir, `${mode}.prompt.md`),
|
|
1922
1988
|
content,
|
|
1923
|
-
|
|
1989
|
+
mode,
|
|
1990
|
+
"GitHub Copilot"
|
|
1924
1991
|
);
|
|
1925
1992
|
}
|
|
1926
1993
|
}
|
|
1927
1994
|
async function removeCodexSkills(projectRoot) {
|
|
1928
|
-
|
|
1929
|
-
|
|
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
|
+
]);
|
|
2004
|
+
}
|
|
2005
|
+
async function cleanManagedSkills(skillsDir, markers) {
|
|
2006
|
+
for (const mode of [...MODE_NAMES, ...LEGACY_MODE_NAMES]) {
|
|
1930
2007
|
const modeDir = path4.join(skillsDir, mode);
|
|
1931
2008
|
const skillPath = path4.join(modeDir, "SKILL.md");
|
|
1932
|
-
if (await
|
|
2009
|
+
if (await isManagedByAnyMarker(skillPath, markers)) {
|
|
1933
2010
|
await rm2(skillPath, { force: true });
|
|
1934
|
-
await
|
|
2011
|
+
await removeIfEmpty2(modeDir);
|
|
1935
2012
|
}
|
|
1936
2013
|
}
|
|
1937
|
-
await
|
|
2014
|
+
await removeIfEmpty2(skillsDir);
|
|
1938
2015
|
}
|
|
1939
2016
|
async function removeZcodeSkills(projectRoot) {
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
await rm2(skillPath, { force: true });
|
|
1946
|
-
await removeIfEmpty(modeDir);
|
|
1947
|
-
}
|
|
2017
|
+
if (!await platformNeedsSharedSkills(projectRoot, "codex")) {
|
|
2018
|
+
await cleanManagedSkills(
|
|
2019
|
+
path4.join(projectRoot, ".agents", "skills"),
|
|
2020
|
+
MANCODE_AGENT_SKILL_MARKERS
|
|
2021
|
+
);
|
|
1948
2022
|
}
|
|
1949
|
-
await
|
|
2023
|
+
await cleanManagedSkills(path4.join(projectRoot, ".zcode", "skills"), [
|
|
2024
|
+
ZCODE_SKILL_MANAGED_MARKER
|
|
2025
|
+
]);
|
|
1950
2026
|
}
|
|
1951
2027
|
async function removeCursorCommands(projectRoot) {
|
|
1952
|
-
for (const mode of MODE_NAMES) {
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
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);
|
|
1956
2037
|
}
|
|
1957
2038
|
}
|
|
1958
2039
|
async function removeCopilotPrompts(projectRoot) {
|
|
1959
|
-
for (const mode of MODE_NAMES) {
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
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`
|
|
1963
2046
|
);
|
|
2047
|
+
if (mode === "man8") await removeLegacyGeneratedModeFile(filePath);
|
|
2048
|
+
else await removeManagedModeFile(filePath, mode);
|
|
1964
2049
|
}
|
|
1965
2050
|
}
|
|
1966
|
-
async function
|
|
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) {
|
|
1967
2087
|
try {
|
|
1968
|
-
const entries = await
|
|
2088
|
+
const entries = await readdir2(dir);
|
|
1969
2089
|
if (entries.length === 0) {
|
|
1970
2090
|
await rm2(dir, { recursive: true, force: true });
|
|
1971
2091
|
}
|
|
1972
2092
|
} catch {
|
|
1973
2093
|
}
|
|
1974
2094
|
}
|
|
1975
|
-
async function writeManagedSkill(skillPath, content, marker, platformLabel) {
|
|
1976
|
-
const existing = await
|
|
1977
|
-
if (existing !== null && !existing.includes(
|
|
2095
|
+
async function writeManagedSkill(skillPath, content, marker, platformLabel, recognizedMarkers = [marker]) {
|
|
2096
|
+
const existing = await readTextIfExists3(skillPath);
|
|
2097
|
+
if (existing !== null && !recognizedMarkers.some((m) => existing.includes(m))) {
|
|
1978
2098
|
throw new Error(
|
|
1979
2099
|
`refusing to overwrite user-authored ${platformLabel} skill: ${skillPath}`
|
|
1980
2100
|
);
|
|
1981
2101
|
}
|
|
1982
2102
|
await writeFile4(skillPath, content, "utf-8");
|
|
1983
2103
|
}
|
|
1984
|
-
async function
|
|
1985
|
-
const content = await
|
|
1986
|
-
return content
|
|
2104
|
+
async function isManagedByAnyMarker(skillPath, markers) {
|
|
2105
|
+
const content = await readTextIfExists3(skillPath);
|
|
2106
|
+
return content !== null && markers.some((marker) => content.includes(marker));
|
|
1987
2107
|
}
|
|
1988
|
-
async function
|
|
2108
|
+
async function platformNeedsSharedSkills(projectRoot, platform) {
|
|
1989
2109
|
try {
|
|
1990
|
-
|
|
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");
|
|
1991
2131
|
} catch {
|
|
1992
2132
|
return null;
|
|
1993
2133
|
}
|
|
1994
2134
|
}
|
|
1995
2135
|
var MODE_META = {
|
|
1996
|
-
|
|
1997
|
-
description: "
|
|
1998
|
-
intro: "# mancode
|
|
2136
|
+
mamba: {
|
|
2137
|
+
description: "Diagnose bugs and validate real user flows with targeted regression checks.",
|
|
2138
|
+
intro: "# mancode mamba \u2014 Diagnosis and Real Validation",
|
|
1999
2139
|
workflow: [
|
|
2000
|
-
"##
|
|
2140
|
+
"## Five-step workflow (simulate roles in one conversation)",
|
|
2001
2141
|
"",
|
|
2002
|
-
|
|
2003
|
-
"
|
|
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.",
|
|
2004
2148
|
"",
|
|
2005
|
-
"1.
|
|
2006
|
-
"
|
|
2007
|
-
"
|
|
2008
|
-
"
|
|
2009
|
-
"3.
|
|
2010
|
-
"
|
|
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.",
|
|
2011
2156
|
"",
|
|
2012
|
-
"
|
|
2013
|
-
'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."
|
|
2014
2158
|
].join("\n")
|
|
2015
2159
|
},
|
|
2016
2160
|
man: {
|
|
2017
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.",
|
|
2018
2162
|
intro: "# mancode man \u2014 Full Review Workflow",
|
|
2019
2163
|
workflow: [
|
|
2020
|
-
"##
|
|
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.",
|
|
2021
2172
|
"",
|
|
2022
|
-
"
|
|
2023
|
-
"
|
|
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.",
|
|
2024
2182
|
"",
|
|
2025
|
-
"
|
|
2026
|
-
"2. **Head Coach**: Propose a concrete implementation plan.",
|
|
2027
|
-
"3. **Wait for approval** when the task is high-risk.",
|
|
2028
|
-
"4. **Implement** the plan and self-test.",
|
|
2029
|
-
"5. **Film Analyst Offense**: Review readability, DRY, and YAGNI.",
|
|
2030
|
-
"6. **Fix** review findings.",
|
|
2031
|
-
"7. **Film Analyst Defense**: Review auth, injection, concurrency, and",
|
|
2032
|
-
" resource risks.",
|
|
2033
|
-
"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."
|
|
2034
2184
|
].join("\n")
|
|
2035
2185
|
},
|
|
2036
2186
|
manteam: {
|
|
2037
2187
|
description: "Team-aware workflow with handoff-friendly summaries. Use when the user asks for manteam or when the task affects shared team context.",
|
|
2038
2188
|
intro: "# mancode manteam \u2014 Team Workflow",
|
|
2039
2189
|
workflow: [
|
|
2040
|
-
"##
|
|
2190
|
+
"## Team workflow",
|
|
2191
|
+
"",
|
|
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`.",
|
|
2041
2196
|
"",
|
|
2042
|
-
"
|
|
2043
|
-
"
|
|
2044
|
-
"
|
|
2045
|
-
"
|
|
2046
|
-
"
|
|
2047
|
-
"
|
|
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."
|
|
2048
2207
|
].join("\n")
|
|
2049
2208
|
},
|
|
2050
2209
|
manps: {
|
|
@@ -2069,32 +2228,39 @@ var MODE_META = {
|
|
|
2069
2228
|
"",
|
|
2070
2229
|
"You are now in solo (default) mode.",
|
|
2071
2230
|
"",
|
|
2072
|
-
|
|
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.",
|
|
2073
2235
|
"",
|
|
2074
2236
|
"- Keep the diff narrow.",
|
|
2075
2237
|
"- Reuse existing functions, components, styles, and dependencies.",
|
|
2076
|
-
"-
|
|
2238
|
+
"- Read project-profile first; for detected UI assets and UI work, read `.mancode/aesthetics/style-tokens.json`.",
|
|
2077
2239
|
"- Verify with the narrowest meaningful test, lint, build, or smoke check."
|
|
2078
2240
|
].join("\n")
|
|
2079
2241
|
}
|
|
2080
2242
|
};
|
|
2081
2243
|
|
|
2082
2244
|
// src/installers/shared-content.ts
|
|
2083
|
-
import { readFile as
|
|
2245
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
2084
2246
|
import path5 from "path";
|
|
2085
2247
|
async function generateSharedContent(projectRoot, options) {
|
|
2086
|
-
const [state, tokens] = await Promise.all([
|
|
2248
|
+
const [state, tokens, profile] = await Promise.all([
|
|
2087
2249
|
readJson(
|
|
2088
2250
|
path5.join(projectRoot, ".mancode", "state.json")
|
|
2089
2251
|
),
|
|
2090
2252
|
readJson(
|
|
2091
2253
|
path5.join(projectRoot, ".mancode", "aesthetics", "style-tokens.json")
|
|
2254
|
+
),
|
|
2255
|
+
readJson(
|
|
2256
|
+
path5.join(projectRoot, ".mancode", "project-profile.json")
|
|
2092
2257
|
)
|
|
2093
2258
|
]);
|
|
2259
|
+
const currentProfile = options.projectProfile ?? profile;
|
|
2094
2260
|
const sections = [
|
|
2095
|
-
renderProjectContext(state, tokens, options),
|
|
2261
|
+
renderProjectContext(state, tokens, profile, options),
|
|
2096
2262
|
renderPracticeRules(),
|
|
2097
|
-
renderAesthetics(tokens)
|
|
2263
|
+
currentProfile?.uiAssets === "detected" ? renderAesthetics(tokens) : ""
|
|
2098
2264
|
];
|
|
2099
2265
|
if (!options.minimal) {
|
|
2100
2266
|
sections.push(renderModes(options), renderPlatformDowngrade(options));
|
|
@@ -2102,9 +2268,10 @@ async function generateSharedContent(projectRoot, options) {
|
|
|
2102
2268
|
return `${sections.filter(Boolean).join("\n\n")}
|
|
2103
2269
|
`;
|
|
2104
2270
|
}
|
|
2105
|
-
function renderProjectContext(state, tokens, options) {
|
|
2106
|
-
const
|
|
2107
|
-
const
|
|
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";
|
|
2108
2275
|
const mode = state?.currentMode || "solo";
|
|
2109
2276
|
return [
|
|
2110
2277
|
"## mancode Project Context",
|
|
@@ -2113,8 +2280,9 @@ function renderProjectContext(state, tokens, options) {
|
|
|
2113
2280
|
`- Current mode: ${mode}`,
|
|
2114
2281
|
`- Tech stack: ${techStack}`,
|
|
2115
2282
|
`- UI library: ${uiLibrary}`,
|
|
2283
|
+
`- Project profile: ${currentProfile?.projectKind || "unknown"}; validation: ${currentProfile?.availableValidation?.join(", ") || "inspect project"}`,
|
|
2116
2284
|
"- At the start of each session, read `.mancode/state.json` to check the current mode and project context.",
|
|
2117
|
-
"-
|
|
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`."
|
|
2118
2286
|
].join("\n");
|
|
2119
2287
|
}
|
|
2120
2288
|
function renderPracticeRules() {
|
|
@@ -2166,7 +2334,7 @@ function renderAesthetics(tokens) {
|
|
|
2166
2334
|
function renderModes(options) {
|
|
2167
2335
|
let commandLabel;
|
|
2168
2336
|
if (options.capabilities.skills === "agents-skills") {
|
|
2169
|
-
commandLabel = "Invoke mode skills with `$
|
|
2337
|
+
commandLabel = "Invoke mode skills with `$man`, `$mamba`, `$manteam`, `$manps`, `$mansolo`.";
|
|
2170
2338
|
} else if (options.capabilities.slashCommands === "native") {
|
|
2171
2339
|
commandLabel = "Use the named commands directly when available.";
|
|
2172
2340
|
} else {
|
|
@@ -2176,8 +2344,8 @@ function renderModes(options) {
|
|
|
2176
2344
|
"## mancode Modes",
|
|
2177
2345
|
"",
|
|
2178
2346
|
`- solo: default lightweight mode. ${commandLabel}`,
|
|
2179
|
-
"-
|
|
2180
|
-
"-
|
|
2347
|
+
"- man: progressive governance workflow with planning and optional full execution.",
|
|
2348
|
+
"- mamba: diagnose bugs and validate real user flows or regressions.",
|
|
2181
2349
|
"- manteam: use team memory and leave handoff-friendly summaries.",
|
|
2182
2350
|
"- manps: run project health and cleanup scans before remediation.",
|
|
2183
2351
|
"- mansolo: return to solo mode."
|
|
@@ -2203,14 +2371,14 @@ function renderPlatformDowngrade(options) {
|
|
|
2203
2371
|
}
|
|
2204
2372
|
if (!options.capabilities.subagents) {
|
|
2205
2373
|
lines.push(
|
|
2206
|
-
"- 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."
|
|
2207
2375
|
);
|
|
2208
2376
|
}
|
|
2209
2377
|
return lines.join("\n");
|
|
2210
2378
|
}
|
|
2211
2379
|
async function readJson(filePath) {
|
|
2212
2380
|
try {
|
|
2213
|
-
return JSON.parse(await
|
|
2381
|
+
return JSON.parse(await readFile5(filePath, "utf-8"));
|
|
2214
2382
|
} catch {
|
|
2215
2383
|
return null;
|
|
2216
2384
|
}
|
|
@@ -2248,7 +2416,8 @@ async function installCodex(projectRoot, options) {
|
|
|
2248
2416
|
},
|
|
2249
2417
|
minimal: options.minimal,
|
|
2250
2418
|
techStack: options.techStack,
|
|
2251
|
-
uiLibrary: options.uiLibrary
|
|
2419
|
+
uiLibrary: options.uiLibrary,
|
|
2420
|
+
projectProfile: options.projectProfile
|
|
2252
2421
|
});
|
|
2253
2422
|
const block = [
|
|
2254
2423
|
DEFAULT_MANCODE_START_MARKER,
|
|
@@ -2283,7 +2452,8 @@ async function installCopilot(projectRoot, options) {
|
|
|
2283
2452
|
},
|
|
2284
2453
|
minimal: true,
|
|
2285
2454
|
techStack: options.techStack,
|
|
2286
|
-
uiLibrary: options.uiLibrary
|
|
2455
|
+
uiLibrary: options.uiLibrary,
|
|
2456
|
+
projectProfile: options.projectProfile
|
|
2287
2457
|
});
|
|
2288
2458
|
const sections = [
|
|
2289
2459
|
DEFAULT_MANCODE_START_MARKER,
|
|
@@ -2310,8 +2480,8 @@ function renderCopilotPromptConventions() {
|
|
|
2310
2480
|
"",
|
|
2311
2481
|
"GitHub Copilot does not provide native mancode slash commands, hooks, or isolated subagents. Treat these names as user prompt conventions:",
|
|
2312
2482
|
"",
|
|
2313
|
-
"-
|
|
2314
|
-
"-
|
|
2483
|
+
"- man: use progressive research, planning, implementation, verification, and review.",
|
|
2484
|
+
"- mamba: diagnose bugs and validate real user flows or regressions.",
|
|
2315
2485
|
"- manteam: read team memory and write handoff-friendly summaries.",
|
|
2316
2486
|
"- manps: prefer `mancode manps [area]` before cleanup.",
|
|
2317
2487
|
"- mansolo: exit any active mode and return to default solo behavior.",
|
|
@@ -2321,7 +2491,7 @@ function renderCopilotPromptConventions() {
|
|
|
2321
2491
|
}
|
|
2322
2492
|
|
|
2323
2493
|
// src/installers/cursor.ts
|
|
2324
|
-
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";
|
|
2325
2495
|
import path8 from "path";
|
|
2326
2496
|
var MANCODE_CURSOR_CORE_RULE_FILES = [
|
|
2327
2497
|
"mancode-context.mdc",
|
|
@@ -2329,11 +2499,12 @@ var MANCODE_CURSOR_CORE_RULE_FILES = [
|
|
|
2329
2499
|
"mancode-solo.mdc"
|
|
2330
2500
|
];
|
|
2331
2501
|
var MANCODE_CURSOR_ADVANCED_RULE_FILES = [
|
|
2332
|
-
"mancode-
|
|
2502
|
+
"mancode-mamba.mdc",
|
|
2333
2503
|
"mancode-man.mdc",
|
|
2334
2504
|
"mancode-manteam.mdc",
|
|
2335
2505
|
"mancode-manps.mdc"
|
|
2336
2506
|
];
|
|
2507
|
+
var CURSOR_RULE_MANAGED_MARKER = "<!-- Managed by mancode:cursor-rule. Do not edit this marker. -->";
|
|
2337
2508
|
var MANCODE_CURSOR_RULE_FILES = [
|
|
2338
2509
|
...MANCODE_CURSOR_CORE_RULE_FILES,
|
|
2339
2510
|
...MANCODE_CURSOR_ADVANCED_RULE_FILES
|
|
@@ -2355,7 +2526,8 @@ async function installCursor(projectRoot, options) {
|
|
|
2355
2526
|
// the mode-specific Cursor rules below.
|
|
2356
2527
|
minimal: true,
|
|
2357
2528
|
techStack: options.techStack,
|
|
2358
|
-
uiLibrary: options.uiLibrary
|
|
2529
|
+
uiLibrary: options.uiLibrary,
|
|
2530
|
+
projectProfile: options.projectProfile
|
|
2359
2531
|
});
|
|
2360
2532
|
await writeRule(
|
|
2361
2533
|
rulesDir,
|
|
@@ -2383,12 +2555,13 @@ async function installCursor(projectRoot, options) {
|
|
|
2383
2555
|
await installCursorCommands(projectRoot, true);
|
|
2384
2556
|
return;
|
|
2385
2557
|
}
|
|
2558
|
+
await removeLegacyCursorRules(projectRoot);
|
|
2386
2559
|
await writeRule(
|
|
2387
2560
|
rulesDir,
|
|
2388
|
-
"mancode-
|
|
2389
|
-
"Use
|
|
2561
|
+
"mancode-mamba.mdc",
|
|
2562
|
+
"Use for bug diagnosis and real regression testing",
|
|
2390
2563
|
false,
|
|
2391
|
-
|
|
2564
|
+
renderMambaRule()
|
|
2392
2565
|
);
|
|
2393
2566
|
await writeRule(
|
|
2394
2567
|
rulesDir,
|
|
@@ -2427,11 +2600,77 @@ async function writeRule(rulesDir, fileName, description, alwaysApply, body) {
|
|
|
2427
2600
|
|
|
2428
2601
|
${body.trim()}
|
|
2429
2602
|
`;
|
|
2430
|
-
|
|
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
|
+
);
|
|
2431
2619
|
}
|
|
2432
2620
|
async function removeAdvancedRules(rulesDir) {
|
|
2433
2621
|
for (const fileName of MANCODE_CURSOR_ADVANCED_RULE_FILES) {
|
|
2434
|
-
await
|
|
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;
|
|
2435
2674
|
}
|
|
2436
2675
|
}
|
|
2437
2676
|
function renderPracticeRule() {
|
|
@@ -2458,12 +2697,12 @@ function renderSoloRule() {
|
|
|
2458
2697
|
"",
|
|
2459
2698
|
"- Keep the diff narrow.",
|
|
2460
2699
|
"- Reuse existing functions, components, styles, and dependencies.",
|
|
2461
|
-
"- 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.",
|
|
2462
2701
|
"- Verify with the narrowest meaningful test, lint, build, or smoke check."
|
|
2463
2702
|
].join("\n");
|
|
2464
2703
|
}
|
|
2465
|
-
function
|
|
2466
|
-
return renderModeSkill("
|
|
2704
|
+
function renderMambaRule() {
|
|
2705
|
+
return renderModeSkill("mamba", "/");
|
|
2467
2706
|
}
|
|
2468
2707
|
function renderManRule() {
|
|
2469
2708
|
return renderModeSkill("man", "/");
|
|
@@ -2495,7 +2734,8 @@ async function installZcode(projectRoot, options) {
|
|
|
2495
2734
|
},
|
|
2496
2735
|
minimal: options.minimal,
|
|
2497
2736
|
techStack: options.techStack,
|
|
2498
|
-
uiLibrary: options.uiLibrary
|
|
2737
|
+
uiLibrary: options.uiLibrary,
|
|
2738
|
+
projectProfile: options.projectProfile
|
|
2499
2739
|
});
|
|
2500
2740
|
const block = [
|
|
2501
2741
|
ZCODE_MANCODE_START_MARKER,
|
|
@@ -2677,8 +2917,6 @@ async function checkRemote(cwd) {
|
|
|
2677
2917
|
|
|
2678
2918
|
// src/system/detect.ts
|
|
2679
2919
|
import { exec as exec2 } from "child_process";
|
|
2680
|
-
import { access as access2, readFile as readFile5 } from "fs/promises";
|
|
2681
|
-
import path11 from "path";
|
|
2682
2920
|
import { promisify as promisify2 } from "util";
|
|
2683
2921
|
var execAsync2 = promisify2(exec2);
|
|
2684
2922
|
async function detectSystemDeps() {
|
|
@@ -2702,62 +2940,271 @@ async function detectSystemDeps() {
|
|
|
2702
2940
|
]);
|
|
2703
2941
|
return { bash, git, jq, node };
|
|
2704
2942
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
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 {
|
|
2717
3039
|
}
|
|
2718
|
-
throw err;
|
|
2719
3040
|
}
|
|
2720
|
-
|
|
2721
|
-
const pkg = JSON.parse(raw);
|
|
2722
|
-
const allDeps = {
|
|
2723
|
-
...pkg.dependencies,
|
|
2724
|
-
...pkg.devDependencies,
|
|
2725
|
-
...pkg.peerDependencies
|
|
2726
|
-
};
|
|
2727
|
-
const deps = Object.keys(allDeps || {});
|
|
2728
|
-
const hasFrontend = deps.some((d) => ["react", "vue", "svelte", "angular"].includes(d)) || deps.some((d) => d.startsWith("@angular/"));
|
|
2729
|
-
const hasBackend = deps.some(
|
|
2730
|
-
(d) => ["express", "koa", "fastify", "hapi", "next"].includes(d)
|
|
2731
|
-
) || deps.some((d) => d.startsWith("@nestjs/"));
|
|
2732
|
-
const techStack = [];
|
|
2733
|
-
if (deps.includes("react")) techStack.push("React");
|
|
2734
|
-
if (deps.includes("vue")) techStack.push("Vue");
|
|
2735
|
-
if (deps.includes("svelte")) techStack.push("Svelte");
|
|
2736
|
-
if (deps.includes("typescript")) techStack.push("TypeScript");
|
|
2737
|
-
if (deps.includes("tailwindcss")) techStack.push("Tailwind CSS");
|
|
2738
|
-
let uiLibrary = null;
|
|
2739
|
-
const hasRadix = deps.some((d) => d.startsWith("@radix-ui/"));
|
|
2740
|
-
const uiDir = path11.join(projectRoot, "src", "components", "ui");
|
|
2741
|
-
const hasUiDir = await pathExists2(uiDir);
|
|
2742
|
-
if (hasRadix && hasUiDir) {
|
|
2743
|
-
uiLibrary = "shadcn/ui";
|
|
2744
|
-
} else if (deps.includes("@mui/material")) {
|
|
2745
|
-
uiLibrary = "MUI";
|
|
2746
|
-
} else if (deps.includes("antd")) {
|
|
2747
|
-
uiLibrary = "Ant Design";
|
|
2748
|
-
} else if (deps.includes("@headlessui/react")) {
|
|
2749
|
-
uiLibrary = "headlessUI";
|
|
2750
|
-
}
|
|
2751
|
-
return { hasFrontend, hasBackend, techStack, uiLibrary };
|
|
3041
|
+
return found;
|
|
2752
3042
|
}
|
|
2753
|
-
async function
|
|
3043
|
+
async function isDirectory(candidate) {
|
|
2754
3044
|
try {
|
|
2755
|
-
await
|
|
2756
|
-
return true;
|
|
3045
|
+
return (await stat2(candidate)).isDirectory();
|
|
2757
3046
|
} catch {
|
|
2758
3047
|
return false;
|
|
2759
3048
|
}
|
|
2760
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
|
+
}
|
|
2761
3208
|
|
|
2762
3209
|
// src/system/scan-aesthetics.ts
|
|
2763
3210
|
import { promises as fs } from "fs";
|
|
@@ -2775,7 +3222,8 @@ async function scanAesthetics(projectRoot, uiLibrary = null) {
|
|
|
2775
3222
|
if (configResult) {
|
|
2776
3223
|
sourceFiles.push(configResult.relPath);
|
|
2777
3224
|
const content = await fs.readFile(configResult.absPath, "utf-8");
|
|
2778
|
-
const
|
|
3225
|
+
const inspectableContent = stripJsComments(content);
|
|
3226
|
+
const themeBlock = findKeyBlock(inspectableContent, "theme");
|
|
2779
3227
|
if (themeBlock) {
|
|
2780
3228
|
const extendBlock = findKeyBlock(themeBlock, "extend");
|
|
2781
3229
|
const searchBlock = extendBlock ?? themeBlock;
|
|
@@ -2788,15 +3236,15 @@ async function scanAesthetics(projectRoot, uiLibrary = null) {
|
|
|
2788
3236
|
fonts = extractTopLevelArrayValues(fontsBlock);
|
|
2789
3237
|
}
|
|
2790
3238
|
}
|
|
2791
|
-
darkMode = extractDarkMode(
|
|
3239
|
+
darkMode = extractDarkMode(inspectableContent);
|
|
2792
3240
|
}
|
|
2793
3241
|
let matchLevel = "none";
|
|
2794
|
-
if (configResult) {
|
|
3242
|
+
if (configResult || components.length > 0 || Object.keys(cssScan.variables).length > 0) {
|
|
2795
3243
|
matchLevel = "high";
|
|
2796
|
-
} else if (await hasTailwindDep(projectRoot)) {
|
|
3244
|
+
} else if (await hasTailwindDep(projectRoot) || uiLibrary) {
|
|
2797
3245
|
matchLevel = "low";
|
|
2798
3246
|
}
|
|
2799
|
-
if (uiLibrary) {
|
|
3247
|
+
if (uiLibrary && await pathExists2(path12.join(projectRoot, "package.json"))) {
|
|
2800
3248
|
sourceFiles.push("package.json");
|
|
2801
3249
|
}
|
|
2802
3250
|
sourceFiles.push(...cssScan.sourceFiles);
|
|
@@ -2822,7 +3270,7 @@ async function findTailwindConfig(projectRoot) {
|
|
|
2822
3270
|
];
|
|
2823
3271
|
for (const name of candidates) {
|
|
2824
3272
|
const absPath = path12.join(projectRoot, name);
|
|
2825
|
-
if (await
|
|
3273
|
+
if (await pathExists2(absPath)) {
|
|
2826
3274
|
return { absPath, relPath: name };
|
|
2827
3275
|
}
|
|
2828
3276
|
}
|
|
@@ -3037,12 +3485,19 @@ function stripJsComments(content) {
|
|
|
3037
3485
|
return result;
|
|
3038
3486
|
}
|
|
3039
3487
|
async function scanComponents(projectRoot) {
|
|
3040
|
-
const roots = [
|
|
3488
|
+
const roots = [
|
|
3489
|
+
"src/components",
|
|
3490
|
+
"components",
|
|
3491
|
+
"app/components",
|
|
3492
|
+
"lib/widgets",
|
|
3493
|
+
"lib/screens",
|
|
3494
|
+
"Sources"
|
|
3495
|
+
];
|
|
3041
3496
|
const names = /* @__PURE__ */ new Set();
|
|
3042
3497
|
let visitedFiles = 0;
|
|
3043
3498
|
for (const relRoot of roots) {
|
|
3044
3499
|
const absRoot = path12.join(projectRoot, relRoot);
|
|
3045
|
-
if (!await
|
|
3500
|
+
if (!await pathExists2(absRoot)) continue;
|
|
3046
3501
|
visitedFiles = await collectComponentNames(absRoot, names, 0, visitedFiles);
|
|
3047
3502
|
if (visitedFiles >= MAX_COMPONENT_FILES) break;
|
|
3048
3503
|
}
|
|
@@ -3073,10 +3528,14 @@ async function collectComponentNames(dir, names, depth, visitedFiles) {
|
|
|
3073
3528
|
fileCount = await collectComponentNames(abs, names, depth + 1, fileCount);
|
|
3074
3529
|
continue;
|
|
3075
3530
|
}
|
|
3076
|
-
if (!/\.(tsx|jsx|ts|js|vue|svelte)$/.test(entry))
|
|
3531
|
+
if (!/\.(tsx|jsx|ts|js|vue|svelte|dart|swift|kt|kts)$/.test(entry))
|
|
3532
|
+
continue;
|
|
3077
3533
|
fileCount++;
|
|
3078
3534
|
if (isNonComponentFile(entry)) continue;
|
|
3079
|
-
let base = entry.replace(
|
|
3535
|
+
let base = entry.replace(
|
|
3536
|
+
/\.(tsx|jsx|ts|js|vue|svelte|dart|swift|kt|kts)$/,
|
|
3537
|
+
""
|
|
3538
|
+
);
|
|
3080
3539
|
if (base === "index") {
|
|
3081
3540
|
base = path12.basename(dir);
|
|
3082
3541
|
}
|
|
@@ -3101,7 +3560,7 @@ async function scanCssVariables(projectRoot) {
|
|
|
3101
3560
|
const sourceFiles = [];
|
|
3102
3561
|
for (const relPath of candidates) {
|
|
3103
3562
|
const absPath = path12.join(projectRoot, relPath);
|
|
3104
|
-
if (!await
|
|
3563
|
+
if (!await pathExists2(absPath)) continue;
|
|
3105
3564
|
const content = await fs.readFile(absPath, "utf-8");
|
|
3106
3565
|
const found = extractCssVariables(content);
|
|
3107
3566
|
if (Object.keys(found).length === 0) continue;
|
|
@@ -3160,7 +3619,7 @@ async function hasTailwindDep(projectRoot) {
|
|
|
3160
3619
|
function escapeRegex(s) {
|
|
3161
3620
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3162
3621
|
}
|
|
3163
|
-
async function
|
|
3622
|
+
async function pathExists2(p) {
|
|
3164
3623
|
try {
|
|
3165
3624
|
await fs.access(p);
|
|
3166
3625
|
return true;
|
|
@@ -3178,7 +3637,8 @@ var DEFAULT_INIT_PLATFORM = "claude-code";
|
|
|
3178
3637
|
async function init(rootDir = process3.cwd(), options = {}) {
|
|
3179
3638
|
const mancodeDir = path13.join(rootDir, ".mancode");
|
|
3180
3639
|
const stateFile = path13.join(mancodeDir, "state.json");
|
|
3181
|
-
const wasInitialized = await
|
|
3640
|
+
const wasInitialized = await pathExists3(stateFile);
|
|
3641
|
+
let reinstallSnapshots = [];
|
|
3182
3642
|
if (wasInitialized) {
|
|
3183
3643
|
if (!options.force) {
|
|
3184
3644
|
console.log("\u2139\uFE0F mancode already initialized.");
|
|
@@ -3186,20 +3646,37 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3186
3646
|
console.log(" Run `mancode init --force` to reinstall.");
|
|
3187
3647
|
return EXIT_ALREADY_INITIALIZED;
|
|
3188
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
|
+
}
|
|
3189
3661
|
console.log("\u26A0\uFE0F Reinstalling with --force...");
|
|
3190
3662
|
}
|
|
3191
|
-
if (!await
|
|
3663
|
+
if (!await pathExists3(rootDir)) {
|
|
3192
3664
|
console.error(`\u2717 Target directory does not exist: ${rootDir}`);
|
|
3193
3665
|
return EXIT_NOT_A_PROJECT_DIR;
|
|
3194
3666
|
}
|
|
3195
|
-
const isGitRepo = await
|
|
3196
|
-
const
|
|
3197
|
-
|
|
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) {
|
|
3198
3675
|
console.error(`\u2717 Not a project directory: ${rootDir}`);
|
|
3199
|
-
console.error(" (No .git or
|
|
3676
|
+
console.error(" (No .git or recognized project manifest found)");
|
|
3200
3677
|
console.error("");
|
|
3201
3678
|
console.error(
|
|
3202
|
-
" Run mancode init in a git repository or
|
|
3679
|
+
" Run mancode init in a git repository or a recognized project directory."
|
|
3203
3680
|
);
|
|
3204
3681
|
return EXIT_NOT_A_PROJECT_DIR;
|
|
3205
3682
|
}
|
|
@@ -3222,18 +3699,22 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3222
3699
|
console.log(" bash \u2713 git \u2713 jq \u2713");
|
|
3223
3700
|
}
|
|
3224
3701
|
console.log("\u2713 \u68C0\u6D4B\u9879\u76EE\u7C7B\u578B...");
|
|
3225
|
-
const
|
|
3226
|
-
const
|
|
3227
|
-
const
|
|
3228
|
-
|
|
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) {
|
|
3229
3708
|
console.log(` ${techStackStr}`);
|
|
3230
|
-
if (
|
|
3709
|
+
if (uiLibrary) {
|
|
3231
3710
|
console.log(` UI: ${uiLibraryStr}`);
|
|
3232
3711
|
}
|
|
3233
|
-
} else if (
|
|
3234
|
-
console.log("
|
|
3712
|
+
} else if (hasManifest) {
|
|
3713
|
+
console.log(" project manifest found, no known framework dependencies");
|
|
3235
3714
|
} else {
|
|
3236
|
-
console.log(
|
|
3715
|
+
console.log(
|
|
3716
|
+
" (No recognized manifest found, profile confidence is low)"
|
|
3717
|
+
);
|
|
3237
3718
|
}
|
|
3238
3719
|
const initPlatformName = options.platform ?? DEFAULT_INIT_PLATFORM;
|
|
3239
3720
|
const installer = getPlatformInstaller(initPlatformName);
|
|
@@ -3242,7 +3723,8 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3242
3723
|
return EXIT_INIT_FAILED;
|
|
3243
3724
|
}
|
|
3244
3725
|
const team = await detectTeamStatus(rootDir);
|
|
3245
|
-
const
|
|
3726
|
+
const existingPreferences = wasInitialized ? await readExistingInitPreferences(mancodeDir) : {};
|
|
3727
|
+
const teamModeEnabled = options.team ?? existingPreferences.forceTeamMode ?? team.isTeam;
|
|
3246
3728
|
if (options.team === true) {
|
|
3247
3729
|
console.log(" team: forced on (--team)");
|
|
3248
3730
|
} else if (options.team === false) {
|
|
@@ -3274,18 +3756,28 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3274
3756
|
const stateContent = `${JSON.stringify(state, null, 2)}
|
|
3275
3757
|
`;
|
|
3276
3758
|
await fs2.writeFile(stateFile, stateContent, "utf-8");
|
|
3277
|
-
await
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
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
|
+
);
|
|
3285
3777
|
let styleLine = " .mancode/aesthetics/ # style-tokens.json (\u7A7A)";
|
|
3286
|
-
if (
|
|
3778
|
+
if (profile.uiAssets === "detected") {
|
|
3287
3779
|
console.log("\u2713 \u626B\u63CF\u5BA1\u7F8E token...");
|
|
3288
|
-
const tokens = await scanAesthetics(rootDir,
|
|
3780
|
+
const tokens = await scanAesthetics(rootDir, uiLibrary);
|
|
3289
3781
|
const tokensPath = path13.join(
|
|
3290
3782
|
mancodeDir,
|
|
3291
3783
|
"aesthetics",
|
|
@@ -3305,7 +3797,9 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3305
3797
|
);
|
|
3306
3798
|
styleLine = ` .mancode/aesthetics/ # style-tokens.json (${colorCount} colors)`;
|
|
3307
3799
|
} else if (tokens.matchLevel === "low") {
|
|
3308
|
-
console.log(
|
|
3800
|
+
console.log(
|
|
3801
|
+
" UI assets detected, no reusable tokens found (match: low)"
|
|
3802
|
+
);
|
|
3309
3803
|
styleLine = " .mancode/aesthetics/ # style-tokens.json (low match)";
|
|
3310
3804
|
} else {
|
|
3311
3805
|
console.log(" No design tokens found (match: none)");
|
|
@@ -3314,14 +3808,17 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3314
3808
|
}
|
|
3315
3809
|
console.log(`\u2713 \u5B89\u88C5 ${installer.displayName} \u9002\u914D...`);
|
|
3316
3810
|
await installer.install(rootDir, {
|
|
3317
|
-
techStack:
|
|
3318
|
-
uiLibrary
|
|
3811
|
+
techStack: profileStack,
|
|
3812
|
+
uiLibrary,
|
|
3813
|
+
projectProfile: profile,
|
|
3814
|
+
force: options.force
|
|
3319
3815
|
});
|
|
3320
3816
|
console.log("");
|
|
3321
3817
|
console.log("\u2713 mancode initialized.");
|
|
3322
3818
|
console.log("");
|
|
3323
3819
|
console.log("Created:");
|
|
3324
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");
|
|
3325
3822
|
console.log(" .mancode/config.json # \u914D\u7F6E");
|
|
3326
3823
|
console.log(
|
|
3327
3824
|
" .mancode/hooks/ # SessionStart + UserPromptSubmit"
|
|
@@ -3337,28 +3834,90 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3337
3834
|
}
|
|
3338
3835
|
return EXIT_OK;
|
|
3339
3836
|
} catch (err) {
|
|
3340
|
-
if (
|
|
3837
|
+
if (wasInitialized) {
|
|
3838
|
+
await restoreFiles(reinstallSnapshots);
|
|
3839
|
+
} else {
|
|
3341
3840
|
await fs2.rm(stateFile, { force: true });
|
|
3841
|
+
await fs2.rm(path13.join(mancodeDir, "project-profile.json"), {
|
|
3842
|
+
force: true
|
|
3843
|
+
});
|
|
3342
3844
|
}
|
|
3343
3845
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3344
3846
|
console.error(`\u2717 mancode init failed: ${msg}`);
|
|
3345
3847
|
return EXIT_INIT_FAILED;
|
|
3346
3848
|
}
|
|
3347
3849
|
}
|
|
3348
|
-
async function updateConfigOptions(mancodeDir, patch) {
|
|
3850
|
+
async function updateConfigOptions(mancodeDir, patch, preserveInstalledPlatforms) {
|
|
3349
3851
|
const configPath = path13.join(mancodeDir, "config.json");
|
|
3350
3852
|
let config = {};
|
|
3351
3853
|
try {
|
|
3352
3854
|
config = JSON.parse(await fs2.readFile(configPath, "utf-8"));
|
|
3353
3855
|
} catch {
|
|
3354
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;
|
|
3355
3874
|
await fs2.writeFile(
|
|
3356
3875
|
configPath,
|
|
3357
|
-
`${JSON.stringify({ ...config, ...
|
|
3876
|
+
`${JSON.stringify({ ...config, ...mergedPatch }, null, 2)}
|
|
3358
3877
|
`,
|
|
3359
3878
|
"utf-8"
|
|
3360
3879
|
);
|
|
3361
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
|
+
}
|
|
3362
3921
|
function printPlatformCreatedFiles(platform) {
|
|
3363
3922
|
if (platform === "claude-code") {
|
|
3364
3923
|
console.log(" .claude/settings.json # hook \u6CE8\u518C");
|
|
@@ -3371,17 +3930,17 @@ function printPlatformCreatedFiles(platform) {
|
|
|
3371
3930
|
}
|
|
3372
3931
|
if (platform === "codex") {
|
|
3373
3932
|
console.log(" AGENTS.md # Codex managed block");
|
|
3374
|
-
console.log(" .
|
|
3933
|
+
console.log(" .agents/skills/ # Codex mode skills");
|
|
3375
3934
|
return;
|
|
3376
3935
|
}
|
|
3377
3936
|
if (platform === "zcode") {
|
|
3378
3937
|
console.log(" AGENTS.md # ZCode managed block");
|
|
3379
|
-
console.log(" .
|
|
3938
|
+
console.log(" .agents/skills/ # ZCode mode skills");
|
|
3380
3939
|
return;
|
|
3381
3940
|
}
|
|
3382
3941
|
console.log(" .github/copilot-instructions.md # Copilot instructions");
|
|
3383
3942
|
}
|
|
3384
|
-
async function
|
|
3943
|
+
async function pathExists3(p) {
|
|
3385
3944
|
try {
|
|
3386
3945
|
await fs2.access(p);
|
|
3387
3946
|
return true;
|
|
@@ -3409,29 +3968,61 @@ async function checkPlatformStatus(rootDir, platform, installed) {
|
|
|
3409
3968
|
}
|
|
3410
3969
|
async function checkPlatformReadiness(rootDir, platform) {
|
|
3411
3970
|
if (platform === "claude-code") {
|
|
3412
|
-
const hasSoloSkill = await
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
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);
|
|
3417
3985
|
return {
|
|
3418
3986
|
present,
|
|
3419
|
-
ready: present,
|
|
3987
|
+
ready: present && fullContentReady,
|
|
3420
3988
|
target: ".claude/",
|
|
3421
|
-
readyDetail: "skills and hooks
|
|
3989
|
+
readyDetail: present && fullContentReady ? minimal ? "solo skill and hooks ready" : "skills, agents, and hooks ready" : "skills, agents, hook files, or hook registration are incomplete"
|
|
3422
3990
|
};
|
|
3423
3991
|
}
|
|
3424
3992
|
if (platform === "cursor") {
|
|
3425
|
-
const
|
|
3993
|
+
const hasCoreRules = await allManagedSkills(
|
|
3426
3994
|
MANCODE_CURSOR_CORE_RULE_FILES.map(
|
|
3427
3995
|
(file) => path14.join(rootDir, ".cursor", "rules", file)
|
|
3428
|
-
)
|
|
3996
|
+
),
|
|
3997
|
+
[CURSOR_RULE_MANAGED_MARKER]
|
|
3429
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
|
+
]);
|
|
3430
4021
|
return {
|
|
3431
|
-
present,
|
|
3432
|
-
ready:
|
|
3433
|
-
target: ".cursor/rules/",
|
|
3434
|
-
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"
|
|
3435
4026
|
};
|
|
3436
4027
|
}
|
|
3437
4028
|
if (platform === "codex") {
|
|
@@ -3452,15 +4043,15 @@ async function checkPlatformReadiness(rootDir, platform) {
|
|
|
3452
4043
|
readyDetail: "managed block present"
|
|
3453
4044
|
};
|
|
3454
4045
|
}
|
|
3455
|
-
const skillsDir = path14.join(rootDir, ".
|
|
4046
|
+
const skillsDir = path14.join(rootDir, ".agents", "skills");
|
|
3456
4047
|
const hasSkills = await allManagedSkills(
|
|
3457
4048
|
MODE_NAMES.map((mode) => path14.join(skillsDir, mode, "SKILL.md")),
|
|
3458
|
-
|
|
4049
|
+
MANCODE_AGENT_SKILL_MARKERS
|
|
3459
4050
|
);
|
|
3460
4051
|
return {
|
|
3461
4052
|
present: true,
|
|
3462
4053
|
ready: hasSkills,
|
|
3463
|
-
target: "AGENTS.md + .
|
|
4054
|
+
target: "AGENTS.md + .agents/skills/",
|
|
3464
4055
|
readyDetail: hasSkills ? "managed block and skills present" : "mode skills are missing, incomplete, or user-authored"
|
|
3465
4056
|
};
|
|
3466
4057
|
}
|
|
@@ -3486,15 +4077,15 @@ async function checkPlatformReadiness(rootDir, platform) {
|
|
|
3486
4077
|
readyDetail: "managed block present"
|
|
3487
4078
|
};
|
|
3488
4079
|
}
|
|
3489
|
-
const skillsDir = path14.join(rootDir, ".
|
|
4080
|
+
const skillsDir = path14.join(rootDir, ".agents", "skills");
|
|
3490
4081
|
const hasSkills = await allManagedSkills(
|
|
3491
4082
|
MODE_NAMES.map((mode) => path14.join(skillsDir, mode, "SKILL.md")),
|
|
3492
|
-
|
|
4083
|
+
MANCODE_AGENT_SKILL_MARKERS
|
|
3493
4084
|
);
|
|
3494
4085
|
return {
|
|
3495
4086
|
present: true,
|
|
3496
4087
|
ready: hasSkills,
|
|
3497
|
-
target: "AGENTS.md + .
|
|
4088
|
+
target: "AGENTS.md + .agents/skills/",
|
|
3498
4089
|
readyDetail: hasSkills ? "managed block and skills present" : "mode skills are missing, incomplete, or user-authored"
|
|
3499
4090
|
};
|
|
3500
4091
|
}
|
|
@@ -3509,14 +4100,17 @@ async function checkPlatformReadiness(rootDir, platform) {
|
|
|
3509
4100
|
readyDetail: "managed block missing"
|
|
3510
4101
|
};
|
|
3511
4102
|
}
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
const
|
|
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
|
+
);
|
|
3515
4109
|
return {
|
|
3516
|
-
present:
|
|
3517
|
-
ready:
|
|
4110
|
+
present: true,
|
|
4111
|
+
ready: hasPrompts,
|
|
3518
4112
|
target: ".github/copilot-instructions.md + .github/prompts/",
|
|
3519
|
-
readyDetail:
|
|
4113
|
+
readyDetail: hasPrompts ? "managed block and prompts present" : "mode prompts are missing, incomplete, or user-authored"
|
|
3520
4114
|
};
|
|
3521
4115
|
}
|
|
3522
4116
|
return {
|
|
@@ -3533,20 +4127,42 @@ function describeStatus(installed, readiness) {
|
|
|
3533
4127
|
}
|
|
3534
4128
|
return "missing generated files or registration";
|
|
3535
4129
|
}
|
|
3536
|
-
async function
|
|
3537
|
-
const results = await Promise.all(
|
|
4130
|
+
async function allManagedSkills(paths, markers) {
|
|
4131
|
+
const results = await Promise.all(
|
|
4132
|
+
paths.map((filePath) => fileHasAnyMarker(filePath, markers))
|
|
4133
|
+
);
|
|
3538
4134
|
return results.every(Boolean);
|
|
3539
4135
|
}
|
|
3540
|
-
async function
|
|
3541
|
-
const
|
|
3542
|
-
|
|
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
|
+
)
|
|
3543
4148
|
);
|
|
4149
|
+
const results = await Promise.all([...skillChecks, ...agentChecks]);
|
|
3544
4150
|
return results.every(Boolean);
|
|
3545
4151
|
}
|
|
3546
|
-
async function
|
|
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
|
+
}
|
|
4162
|
+
async function fileHasAnyMarker(filePath, needles) {
|
|
3547
4163
|
try {
|
|
3548
4164
|
const content = await fs3.readFile(filePath, "utf-8");
|
|
3549
|
-
return content.includes(needle);
|
|
4165
|
+
return needles.some((needle) => content.includes(needle));
|
|
3550
4166
|
} catch {
|
|
3551
4167
|
return false;
|
|
3552
4168
|
}
|
|
@@ -3558,9 +4174,9 @@ async function isPlatformMinimal(rootDir, platform) {
|
|
|
3558
4174
|
"utf-8"
|
|
3559
4175
|
);
|
|
3560
4176
|
const config = JSON.parse(raw);
|
|
3561
|
-
if (!
|
|
4177
|
+
if (!isRecord4(config.platformOptions)) return false;
|
|
3562
4178
|
const options = config.platformOptions[platform];
|
|
3563
|
-
return
|
|
4179
|
+
return isRecord4(options) && options.minimal === true;
|
|
3564
4180
|
} catch {
|
|
3565
4181
|
return false;
|
|
3566
4182
|
}
|
|
@@ -3586,9 +4202,9 @@ async function claudeHooksRegistered(rootDir) {
|
|
|
3586
4202
|
}
|
|
3587
4203
|
}
|
|
3588
4204
|
function hooksRegistered(settings) {
|
|
3589
|
-
if (!
|
|
4205
|
+
if (!isRecord4(settings)) return false;
|
|
3590
4206
|
const hooks = settings.hooks;
|
|
3591
|
-
if (!
|
|
4207
|
+
if (!isRecord4(hooks)) return false;
|
|
3592
4208
|
return hasHookCommand(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand(
|
|
3593
4209
|
hooks.UserPromptSubmit,
|
|
3594
4210
|
".mancode/hooks/user-prompt-submit.sh"
|
|
@@ -3597,14 +4213,14 @@ function hooksRegistered(settings) {
|
|
|
3597
4213
|
function hasHookCommand(value, needle) {
|
|
3598
4214
|
if (!Array.isArray(value)) return false;
|
|
3599
4215
|
return value.some((group) => {
|
|
3600
|
-
if (!
|
|
4216
|
+
if (!isRecord4(group) || !Array.isArray(group.hooks)) return false;
|
|
3601
4217
|
return group.hooks.some((hook) => {
|
|
3602
|
-
if (!
|
|
4218
|
+
if (!isRecord4(hook) || typeof hook.command !== "string") return false;
|
|
3603
4219
|
return hook.command.includes(needle);
|
|
3604
4220
|
});
|
|
3605
4221
|
});
|
|
3606
4222
|
}
|
|
3607
|
-
async function
|
|
4223
|
+
async function pathExists4(p) {
|
|
3608
4224
|
try {
|
|
3609
4225
|
await fs3.access(p);
|
|
3610
4226
|
return true;
|
|
@@ -3612,7 +4228,7 @@ async function pathExists5(p) {
|
|
|
3612
4228
|
return false;
|
|
3613
4229
|
}
|
|
3614
4230
|
}
|
|
3615
|
-
function
|
|
4231
|
+
function isRecord4(value) {
|
|
3616
4232
|
return typeof value === "object" && value !== null;
|
|
3617
4233
|
}
|
|
3618
4234
|
|
|
@@ -3623,7 +4239,7 @@ var EXIT_UNSUPPORTED_PLATFORM = 2;
|
|
|
3623
4239
|
var EXIT_INSTALL_FAILED = 3;
|
|
3624
4240
|
async function install(rootDir = process4.cwd(), platform = "claude-code", options = {}) {
|
|
3625
4241
|
const stateFile = path15.join(rootDir, ".mancode", "state.json");
|
|
3626
|
-
if (!await
|
|
4242
|
+
if (!await pathExists5(stateFile)) {
|
|
3627
4243
|
console.error("\u2717 mancode not initialized.");
|
|
3628
4244
|
console.error(" Run `mancode init` first.");
|
|
3629
4245
|
return EXIT_NOT_INITIALIZED;
|
|
@@ -3650,6 +4266,11 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
|
|
|
3650
4266
|
await readStatePlatform(rootDir)
|
|
3651
4267
|
);
|
|
3652
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;
|
|
3653
4274
|
if (alreadyInstalled && !options.force) {
|
|
3654
4275
|
const status2 = await checkPlatformStatus(rootDir, platform, true);
|
|
3655
4276
|
if (status2.ready) {
|
|
@@ -3671,12 +4292,13 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
|
|
|
3671
4292
|
}
|
|
3672
4293
|
}
|
|
3673
4294
|
console.log(`\u2713 Installing ${formatPlatformName(platform)} adapter...`);
|
|
3674
|
-
const
|
|
4295
|
+
const profile = await detectProjectProfile(rootDir);
|
|
3675
4296
|
try {
|
|
3676
4297
|
await installer.install(rootDir, {
|
|
3677
|
-
techStack:
|
|
3678
|
-
uiLibrary:
|
|
3679
|
-
|
|
4298
|
+
techStack: [...profile.languages, ...profile.frameworks],
|
|
4299
|
+
uiLibrary: primaryUiLibrary(profile),
|
|
4300
|
+
projectProfile: profile,
|
|
4301
|
+
minimal: effectiveMinimal,
|
|
3680
4302
|
force: options.force
|
|
3681
4303
|
});
|
|
3682
4304
|
} catch (err) {
|
|
@@ -3693,11 +4315,10 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
|
|
|
3693
4315
|
await updateConfig(rootDir, {
|
|
3694
4316
|
...config,
|
|
3695
4317
|
platforms,
|
|
3696
|
-
platformOptions: updatePlatformOptions(
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
)
|
|
4318
|
+
platformOptions: updatePlatformOptions(config.platformOptions, platform, {
|
|
4319
|
+
...options,
|
|
4320
|
+
minimal: effectiveMinimal
|
|
4321
|
+
})
|
|
3701
4322
|
});
|
|
3702
4323
|
console.log(`\u2713 ${formatPlatformName(platform)} adapter installed.`);
|
|
3703
4324
|
return EXIT_OK2;
|
|
@@ -3708,13 +4329,13 @@ async function readConfig(rootDir) {
|
|
|
3708
4329
|
const raw = await fs4.readFile(configPath, "utf-8");
|
|
3709
4330
|
return { config: JSON.parse(raw), valid: true };
|
|
3710
4331
|
} catch (err) {
|
|
3711
|
-
if (
|
|
4332
|
+
if (isNodeError4(err) && err.code === "ENOENT") {
|
|
3712
4333
|
return { config: {}, valid: false };
|
|
3713
4334
|
}
|
|
3714
4335
|
throw err;
|
|
3715
4336
|
}
|
|
3716
4337
|
}
|
|
3717
|
-
function
|
|
4338
|
+
function isNodeError4(err) {
|
|
3718
4339
|
return err instanceof Error && "code" in err;
|
|
3719
4340
|
}
|
|
3720
4341
|
async function updateConfig(rootDir, config) {
|
|
@@ -3733,19 +4354,24 @@ function withDefaultConfig(config, fallbackPlatform) {
|
|
|
3733
4354
|
return {
|
|
3734
4355
|
...merged,
|
|
3735
4356
|
platforms,
|
|
3736
|
-
hooks:
|
|
3737
|
-
logging:
|
|
4357
|
+
hooks: isRecord5(config.hooks) ? config.hooks : DEFAULT_CONFIG.hooks,
|
|
4358
|
+
logging: isRecord5(config.logging) ? config.logging : DEFAULT_CONFIG.logging
|
|
3738
4359
|
};
|
|
3739
4360
|
}
|
|
3740
4361
|
function updatePlatformOptions(value, platform, options) {
|
|
3741
|
-
const platformOptions =
|
|
4362
|
+
const platformOptions = isRecord5(value) ? { ...value } : {};
|
|
3742
4363
|
const existing = platformOptions[platform];
|
|
3743
4364
|
platformOptions[platform] = {
|
|
3744
|
-
...
|
|
4365
|
+
...isRecord5(existing) ? existing : {},
|
|
3745
4366
|
minimal: options.minimal === true
|
|
3746
4367
|
};
|
|
3747
4368
|
return platformOptions;
|
|
3748
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
|
+
}
|
|
3749
4375
|
async function readStatePlatform(rootDir) {
|
|
3750
4376
|
try {
|
|
3751
4377
|
const raw = await fs4.readFile(
|
|
@@ -3758,7 +4384,7 @@ async function readStatePlatform(rootDir) {
|
|
|
3758
4384
|
return null;
|
|
3759
4385
|
}
|
|
3760
4386
|
}
|
|
3761
|
-
async function
|
|
4387
|
+
async function pathExists5(p) {
|
|
3762
4388
|
try {
|
|
3763
4389
|
await fs4.access(p);
|
|
3764
4390
|
return true;
|
|
@@ -3766,7 +4392,7 @@ async function pathExists6(p) {
|
|
|
3766
4392
|
return false;
|
|
3767
4393
|
}
|
|
3768
4394
|
}
|
|
3769
|
-
function
|
|
4395
|
+
function isRecord5(value) {
|
|
3770
4396
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3771
4397
|
}
|
|
3772
4398
|
|
|
@@ -3813,7 +4439,7 @@ function describePlatform(platform) {
|
|
|
3813
4439
|
}
|
|
3814
4440
|
|
|
3815
4441
|
// src/commands/manps.ts
|
|
3816
|
-
import { access as
|
|
4442
|
+
import { access as access3 } from "fs/promises";
|
|
3817
4443
|
import path18 from "path";
|
|
3818
4444
|
import { createInterface } from "readline/promises";
|
|
3819
4445
|
|
|
@@ -3822,11 +4448,11 @@ import { execFile } from "child_process";
|
|
|
3822
4448
|
import { createHash } from "crypto";
|
|
3823
4449
|
import { existsSync, readFileSync } from "fs";
|
|
3824
4450
|
import {
|
|
3825
|
-
access as
|
|
4451
|
+
access as access2,
|
|
3826
4452
|
lstat,
|
|
3827
4453
|
mkdir as mkdir7,
|
|
3828
|
-
readFile as
|
|
3829
|
-
readdir as
|
|
4454
|
+
readFile as readFile8,
|
|
4455
|
+
readdir as readdir4,
|
|
3830
4456
|
rename,
|
|
3831
4457
|
writeFile as writeFile9
|
|
3832
4458
|
} from "fs/promises";
|
|
@@ -4097,7 +4723,7 @@ async function walk(root, current, results, depth) {
|
|
|
4097
4723
|
if (depth > MAX_SCAN_DEPTH || results.length >= MAX_PROJECT_FILES) return;
|
|
4098
4724
|
let entries;
|
|
4099
4725
|
try {
|
|
4100
|
-
entries = await
|
|
4726
|
+
entries = await readdir4(current);
|
|
4101
4727
|
} catch {
|
|
4102
4728
|
return;
|
|
4103
4729
|
}
|
|
@@ -4122,7 +4748,7 @@ async function walk(root, current, results, depth) {
|
|
|
4122
4748
|
}
|
|
4123
4749
|
async function readPackageJson(projectRoot) {
|
|
4124
4750
|
try {
|
|
4125
|
-
const raw = await
|
|
4751
|
+
const raw = await readFile8(path17.join(projectRoot, "package.json"), "utf-8");
|
|
4126
4752
|
return JSON.parse(raw);
|
|
4127
4753
|
} catch {
|
|
4128
4754
|
return null;
|
|
@@ -4365,7 +4991,7 @@ async function hasDependencyCruiserConfig(projectRoot) {
|
|
|
4365
4991
|
"dependency-cruiser.config.mjs"
|
|
4366
4992
|
];
|
|
4367
4993
|
const results = await Promise.all(
|
|
4368
|
-
files.map((file) =>
|
|
4994
|
+
files.map((file) => pathExists6(path17.join(projectRoot, file)))
|
|
4369
4995
|
);
|
|
4370
4996
|
return results.some(Boolean);
|
|
4371
4997
|
}
|
|
@@ -4433,15 +5059,15 @@ function parseDepcruiseJson(output) {
|
|
|
4433
5059
|
}
|
|
4434
5060
|
}
|
|
4435
5061
|
function extractDepcruiseViolations(value) {
|
|
4436
|
-
if (!
|
|
4437
|
-
const rawViolations = Array.isArray(value.violations) ? value.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 : [];
|
|
4438
5064
|
return rawViolations.map((violation) => normalizeDepcruiseViolation(violation)).filter(
|
|
4439
5065
|
(violation) => violation !== null
|
|
4440
5066
|
);
|
|
4441
5067
|
}
|
|
4442
5068
|
function normalizeDepcruiseViolation(value) {
|
|
4443
|
-
if (!
|
|
4444
|
-
const rule =
|
|
5069
|
+
if (!isRecord6(value)) return null;
|
|
5070
|
+
const rule = isRecord6(value.rule) ? value.rule : {};
|
|
4445
5071
|
const from = typeof value.from === "string" ? value.from : "";
|
|
4446
5072
|
const to = typeof value.to === "string" ? value.to : void 0;
|
|
4447
5073
|
const ruleName = typeof rule.name === "string" ? rule.name : typeof value.name === "string" ? value.name : "unnamed-rule";
|
|
@@ -4515,9 +5141,9 @@ async function buildIssueDatabase(projectRoot, report) {
|
|
|
4515
5141
|
async function readIssueDatabase(issueDbPath) {
|
|
4516
5142
|
let raw;
|
|
4517
5143
|
try {
|
|
4518
|
-
raw = await
|
|
5144
|
+
raw = await readFile8(issueDbPath, "utf-8");
|
|
4519
5145
|
} catch (err) {
|
|
4520
|
-
if (
|
|
5146
|
+
if (isNodeError5(err) && err.code === "ENOENT") {
|
|
4521
5147
|
return emptyIssueDatabase();
|
|
4522
5148
|
}
|
|
4523
5149
|
throw err;
|
|
@@ -4631,7 +5257,7 @@ async function addPackageScript(projectRoot, scriptName, script) {
|
|
|
4631
5257
|
const packagePath = path17.join(projectRoot, "package.json");
|
|
4632
5258
|
let pkg;
|
|
4633
5259
|
try {
|
|
4634
|
-
pkg = JSON.parse(await
|
|
5260
|
+
pkg = JSON.parse(await readFile8(packagePath, "utf-8"));
|
|
4635
5261
|
} catch {
|
|
4636
5262
|
return false;
|
|
4637
5263
|
}
|
|
@@ -4720,18 +5346,18 @@ function readFileSyncSafe(file) {
|
|
|
4720
5346
|
function pathExistsSync(file) {
|
|
4721
5347
|
return existsSync(file);
|
|
4722
5348
|
}
|
|
4723
|
-
async function
|
|
5349
|
+
async function pathExists6(file) {
|
|
4724
5350
|
try {
|
|
4725
|
-
await
|
|
5351
|
+
await access2(file);
|
|
4726
5352
|
return true;
|
|
4727
5353
|
} catch {
|
|
4728
5354
|
return false;
|
|
4729
5355
|
}
|
|
4730
5356
|
}
|
|
4731
|
-
function
|
|
5357
|
+
function isNodeError5(err) {
|
|
4732
5358
|
return err instanceof Error && "code" in err;
|
|
4733
5359
|
}
|
|
4734
|
-
function
|
|
5360
|
+
function isRecord6(value) {
|
|
4735
5361
|
return typeof value === "object" && value !== null;
|
|
4736
5362
|
}
|
|
4737
5363
|
|
|
@@ -4741,7 +5367,7 @@ var EXIT_NOT_INITIALIZED2 = 1;
|
|
|
4741
5367
|
var EXIT_SCAN_FAILED = 2;
|
|
4742
5368
|
var EXIT_INVALID_ARG = 3;
|
|
4743
5369
|
async function manps(rootDir, area = "all", options = {}) {
|
|
4744
|
-
if (!await
|
|
5370
|
+
if (!await pathExists7(path18.join(rootDir, ".mancode", "state.json"))) {
|
|
4745
5371
|
if (options.json) {
|
|
4746
5372
|
console.log(JSON.stringify({ error: "not initialized" }, null, 2));
|
|
4747
5373
|
} else {
|
|
@@ -4883,9 +5509,9 @@ async function readStdinLines() {
|
|
|
4883
5509
|
}
|
|
4884
5510
|
return Buffer.concat(chunks).toString("utf-8").split(/\r?\n/).filter((line) => line.length > 0);
|
|
4885
5511
|
}
|
|
4886
|
-
async function
|
|
5512
|
+
async function pathExists7(p) {
|
|
4887
5513
|
try {
|
|
4888
|
-
await
|
|
5514
|
+
await access3(p);
|
|
4889
5515
|
return true;
|
|
4890
5516
|
} catch {
|
|
4891
5517
|
return false;
|
|
@@ -4900,18 +5526,35 @@ var EXIT_OK5 = 0;
|
|
|
4900
5526
|
var EXIT_NOT_INITIALIZED3 = 1;
|
|
4901
5527
|
async function refreshStyle(rootDir = process6.cwd()) {
|
|
4902
5528
|
const stateFile = path19.join(rootDir, ".mancode", "state.json");
|
|
4903
|
-
if (!await
|
|
5529
|
+
if (!await pathExists8(stateFile)) {
|
|
4904
5530
|
console.error("\u2717 mancode not initialized.");
|
|
4905
5531
|
console.error(" Run `mancode init` first.");
|
|
4906
5532
|
return EXIT_NOT_INITIALIZED3;
|
|
4907
5533
|
}
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
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);
|
|
4911
5554
|
return EXIT_OK5;
|
|
4912
5555
|
}
|
|
4913
5556
|
console.log("\u2713 \u626B\u63CF\u9879\u76EE\u8BBE\u8BA1 token...");
|
|
4914
|
-
const tokens = await scanAesthetics(rootDir,
|
|
5557
|
+
const tokens = await scanAesthetics(rootDir, uiLibraryHint);
|
|
4915
5558
|
const tokensPath = path19.join(
|
|
4916
5559
|
rootDir,
|
|
4917
5560
|
".mancode",
|
|
@@ -4928,7 +5571,9 @@ async function refreshStyle(rootDir = process6.cwd()) {
|
|
|
4928
5571
|
console.log("");
|
|
4929
5572
|
if (tokens.matchLevel === "none") {
|
|
4930
5573
|
console.log("\u672A\u68C0\u6D4B\u5230\u8BBE\u8BA1 token\u3002");
|
|
4931
|
-
console.log(
|
|
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
|
+
);
|
|
4932
5577
|
} else {
|
|
4933
5578
|
if (tokens.sourceFiles.length > 0) {
|
|
4934
5579
|
console.log("\u626B\u63CF\u6765\u6E90\uFF1A");
|
|
@@ -4961,7 +5606,9 @@ async function refreshStyle(rootDir = process6.cwd()) {
|
|
|
4961
5606
|
console.log(` \u5339\u914D\u5EA6: ${tokens.matchLevel}`);
|
|
4962
5607
|
}
|
|
4963
5608
|
console.log("");
|
|
4964
|
-
console.log(
|
|
5609
|
+
console.log(
|
|
5610
|
+
"\u5DF2\u66F4\u65B0 .mancode/project-profile.json \u548C .mancode/aesthetics/style-tokens.json"
|
|
5611
|
+
);
|
|
4965
5612
|
await printStaticPlatformRefreshHint(rootDir);
|
|
4966
5613
|
return EXIT_OK5;
|
|
4967
5614
|
}
|
|
@@ -4979,6 +5626,28 @@ async function printStaticPlatformRefreshHint(rootDir) {
|
|
|
4979
5626
|
" Run `mancode install <platform> --force` to refresh their embedded style summaries."
|
|
4980
5627
|
);
|
|
4981
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
|
+
}
|
|
4982
5651
|
async function readInstalledPlatforms2(rootDir) {
|
|
4983
5652
|
try {
|
|
4984
5653
|
const raw = await fs6.readFile(
|
|
@@ -4993,7 +5662,7 @@ async function readInstalledPlatforms2(rootDir) {
|
|
|
4993
5662
|
return [];
|
|
4994
5663
|
}
|
|
4995
5664
|
}
|
|
4996
|
-
async function
|
|
5665
|
+
async function pathExists8(p) {
|
|
4997
5666
|
try {
|
|
4998
5667
|
await fs6.access(p);
|
|
4999
5668
|
return true;
|
|
@@ -5011,10 +5680,10 @@ import process7 from "process";
|
|
|
5011
5680
|
// src/system/workflow.ts
|
|
5012
5681
|
import {
|
|
5013
5682
|
mkdir as mkdir8,
|
|
5014
|
-
readFile as
|
|
5015
|
-
readdir as
|
|
5683
|
+
readFile as readFile9,
|
|
5684
|
+
readdir as readdir5,
|
|
5016
5685
|
rm as rm4,
|
|
5017
|
-
stat as
|
|
5686
|
+
stat as stat3,
|
|
5018
5687
|
writeFile as writeFile10
|
|
5019
5688
|
} from "fs/promises";
|
|
5020
5689
|
import path20 from "path";
|
|
@@ -5033,7 +5702,8 @@ function generateTaskId(task, now = /* @__PURE__ */ new Date()) {
|
|
|
5033
5702
|
function slugify(text) {
|
|
5034
5703
|
return text.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5035
5704
|
}
|
|
5036
|
-
async function createWorkflow(projectRoot, task, mode) {
|
|
5705
|
+
async function createWorkflow(projectRoot, task, mode, options = {}) {
|
|
5706
|
+
await validateParentTask(projectRoot, mode, options.parentTaskId, true);
|
|
5037
5707
|
await mkdir8(workflowsRoot(projectRoot), { recursive: true });
|
|
5038
5708
|
const taskId = await allocateTaskId(projectRoot, generateTaskId(task));
|
|
5039
5709
|
const dir = workflowDir(projectRoot, taskId);
|
|
@@ -5046,7 +5716,9 @@ async function createWorkflow(projectRoot, task, mode) {
|
|
|
5046
5716
|
skippedSteps: [],
|
|
5047
5717
|
startedAt: now,
|
|
5048
5718
|
updatedAt: now,
|
|
5049
|
-
status: "in_progress"
|
|
5719
|
+
status: "in_progress",
|
|
5720
|
+
...options.parentTaskId ? { parentTaskId: options.parentTaskId } : {},
|
|
5721
|
+
...mode === "man" || mode === "manteam" ? { planVersion: 1 } : {}
|
|
5050
5722
|
};
|
|
5051
5723
|
await writeMetadata(dir, meta);
|
|
5052
5724
|
return meta;
|
|
@@ -5058,7 +5730,7 @@ async function allocateTaskId(projectRoot, baseTaskId) {
|
|
|
5058
5730
|
await mkdir8(workflowDir(projectRoot, taskId));
|
|
5059
5731
|
return taskId;
|
|
5060
5732
|
} catch (err) {
|
|
5061
|
-
if (
|
|
5733
|
+
if (isNodeError6(err) && err.code === "EEXIST") {
|
|
5062
5734
|
continue;
|
|
5063
5735
|
}
|
|
5064
5736
|
throw err;
|
|
@@ -5070,7 +5742,7 @@ async function readWorkflow(projectRoot, taskId) {
|
|
|
5070
5742
|
if (!isValidWorkflowTaskId(taskId)) return null;
|
|
5071
5743
|
const file = metadataPath(projectRoot, taskId);
|
|
5072
5744
|
try {
|
|
5073
|
-
const raw = await
|
|
5745
|
+
const raw = await readFile9(file, "utf-8");
|
|
5074
5746
|
return parseWorkflowMeta(raw, taskId);
|
|
5075
5747
|
} catch {
|
|
5076
5748
|
return null;
|
|
@@ -5089,13 +5761,24 @@ async function updateWorkflow(projectRoot, taskId, patch) {
|
|
|
5089
5761
|
startedAt: existing.startedAt,
|
|
5090
5762
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5091
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);
|
|
5092
5772
|
await writeMetadata(workflowDir(projectRoot, taskId), updated);
|
|
5773
|
+
if (updated.parentTaskId) {
|
|
5774
|
+
await propagateChildStatus(projectRoot, updated);
|
|
5775
|
+
}
|
|
5093
5776
|
}
|
|
5094
5777
|
async function listWorkflows(projectRoot) {
|
|
5095
5778
|
const root = workflowsRoot(projectRoot);
|
|
5096
5779
|
let entries;
|
|
5097
5780
|
try {
|
|
5098
|
-
entries = await
|
|
5781
|
+
entries = await readdir5(root);
|
|
5099
5782
|
} catch {
|
|
5100
5783
|
return [];
|
|
5101
5784
|
}
|
|
@@ -5111,10 +5794,14 @@ async function deleteWorkflow(projectRoot, taskId) {
|
|
|
5111
5794
|
if (!isValidWorkflowTaskId(taskId)) return false;
|
|
5112
5795
|
const dir = workflowDir(projectRoot, taskId);
|
|
5113
5796
|
try {
|
|
5114
|
-
await
|
|
5797
|
+
await stat3(dir);
|
|
5115
5798
|
} catch {
|
|
5116
5799
|
return false;
|
|
5117
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
|
+
}
|
|
5118
5805
|
await rm4(dir, { recursive: true, force: true });
|
|
5119
5806
|
return true;
|
|
5120
5807
|
}
|
|
@@ -5136,7 +5823,7 @@ async function writeMetadata(dir, meta) {
|
|
|
5136
5823
|
function parseWorkflowMeta(raw, taskId) {
|
|
5137
5824
|
try {
|
|
5138
5825
|
const obj = JSON.parse(raw);
|
|
5139
|
-
if (typeof obj.task !== "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)) {
|
|
5140
5827
|
return null;
|
|
5141
5828
|
}
|
|
5142
5829
|
return { ...obj, taskId };
|
|
@@ -5144,7 +5831,182 @@ function parseWorkflowMeta(raw, taskId) {
|
|
|
5144
5831
|
return null;
|
|
5145
5832
|
}
|
|
5146
5833
|
}
|
|
5147
|
-
function
|
|
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) {
|
|
5148
6010
|
return err instanceof Error && "code" in err;
|
|
5149
6011
|
}
|
|
5150
6012
|
function assertValidTaskId(taskId) {
|
|
@@ -5160,7 +6022,7 @@ var EXIT_NOT_INITIALIZED4 = 1;
|
|
|
5160
6022
|
var EXIT_CORRUPT_STATE = 2;
|
|
5161
6023
|
async function status(rootDir = process7.cwd(), options = {}) {
|
|
5162
6024
|
const stateFile = path21.join(rootDir, ".mancode", "state.json");
|
|
5163
|
-
if (!await
|
|
6025
|
+
if (!await pathExists9(stateFile)) {
|
|
5164
6026
|
console.error("\u2717 mancode not initialized.");
|
|
5165
6027
|
console.error(" Run `mancode init` to get started.");
|
|
5166
6028
|
return EXIT_NOT_INITIALIZED4;
|
|
@@ -5236,12 +6098,24 @@ async function getCurrentWorkflow(rootDir, taskId) {
|
|
|
5236
6098
|
if (!taskId) return null;
|
|
5237
6099
|
const meta = await readWorkflow(rootDir, taskId);
|
|
5238
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;
|
|
5239
6108
|
return {
|
|
5240
6109
|
taskId: meta.taskId,
|
|
5241
6110
|
task: meta.task,
|
|
5242
6111
|
mode: meta.mode,
|
|
5243
6112
|
currentStep: meta.currentStep,
|
|
5244
|
-
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 } : {}
|
|
5245
6119
|
};
|
|
5246
6120
|
}
|
|
5247
6121
|
async function getProjectName(rootDir) {
|
|
@@ -5285,8 +6159,8 @@ function getEffectiveTeamStatus(state, config, detected) {
|
|
|
5285
6159
|
}
|
|
5286
6160
|
async function checkHooks(rootDir) {
|
|
5287
6161
|
const [sessionStart, userPromptSubmit, registered] = await Promise.all([
|
|
5288
|
-
|
|
5289
|
-
|
|
6162
|
+
pathExists9(path21.join(rootDir, ".mancode", "hooks", "session-start.sh")),
|
|
6163
|
+
pathExists9(
|
|
5290
6164
|
path21.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
|
|
5291
6165
|
),
|
|
5292
6166
|
isRegistered(rootDir)
|
|
@@ -5306,9 +6180,9 @@ async function isRegistered(rootDir) {
|
|
|
5306
6180
|
}
|
|
5307
6181
|
}
|
|
5308
6182
|
function hooksRegistered2(settings) {
|
|
5309
|
-
if (!
|
|
6183
|
+
if (!isRecord7(settings)) return false;
|
|
5310
6184
|
const hooks = settings.hooks;
|
|
5311
|
-
if (!
|
|
6185
|
+
if (!isRecord7(hooks)) return false;
|
|
5312
6186
|
return hasHookCommand2(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand2(
|
|
5313
6187
|
hooks.UserPromptSubmit,
|
|
5314
6188
|
".mancode/hooks/user-prompt-submit.sh"
|
|
@@ -5317,9 +6191,9 @@ function hooksRegistered2(settings) {
|
|
|
5317
6191
|
function hasHookCommand2(value, needle) {
|
|
5318
6192
|
if (!Array.isArray(value)) return false;
|
|
5319
6193
|
return value.some((group) => {
|
|
5320
|
-
if (!
|
|
6194
|
+
if (!isRecord7(group) || !Array.isArray(group.hooks)) return false;
|
|
5321
6195
|
return group.hooks.some((hook) => {
|
|
5322
|
-
if (!
|
|
6196
|
+
if (!isRecord7(hook) || typeof hook.command !== "string") return false;
|
|
5323
6197
|
return hook.command.includes(needle);
|
|
5324
6198
|
});
|
|
5325
6199
|
});
|
|
@@ -5331,7 +6205,7 @@ async function estimateHookInjection(rootDir) {
|
|
|
5331
6205
|
"hooks",
|
|
5332
6206
|
"user-prompt-submit.sh"
|
|
5333
6207
|
);
|
|
5334
|
-
if (!await
|
|
6208
|
+
if (!await pathExists9(hookPath)) {
|
|
5335
6209
|
return { tokens: 0, cap: 800 };
|
|
5336
6210
|
}
|
|
5337
6211
|
try {
|
|
@@ -5393,6 +6267,23 @@ function printText(r) {
|
|
|
5393
6267
|
console.log(
|
|
5394
6268
|
`Workflow: ${r.currentWorkflow.taskId} (Step ${r.currentWorkflow.currentStep}/${stepMax}, ${r.currentWorkflow.status})`
|
|
5395
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
|
+
}
|
|
5396
6287
|
}
|
|
5397
6288
|
console.log("");
|
|
5398
6289
|
console.log("Installed platforms:");
|
|
@@ -5432,12 +6323,12 @@ function formatTeamStatus(team) {
|
|
|
5432
6323
|
return "solo";
|
|
5433
6324
|
}
|
|
5434
6325
|
function workflowStepMax(mode) {
|
|
5435
|
-
return mode
|
|
6326
|
+
return maxWorkflowStep(mode);
|
|
5436
6327
|
}
|
|
5437
|
-
function
|
|
6328
|
+
function isRecord7(value) {
|
|
5438
6329
|
return typeof value === "object" && value !== null;
|
|
5439
6330
|
}
|
|
5440
|
-
async function
|
|
6331
|
+
async function pathExists9(p) {
|
|
5441
6332
|
try {
|
|
5442
6333
|
await fs7.access(p);
|
|
5443
6334
|
return true;
|
|
@@ -5447,30 +6338,15 @@ async function pathExists10(p) {
|
|
|
5447
6338
|
}
|
|
5448
6339
|
|
|
5449
6340
|
// src/commands/uninstall.ts
|
|
5450
|
-
import { access as
|
|
6341
|
+
import { access as access4, readFile as readFile10, rm as rm5, writeFile as writeFile11 } from "fs/promises";
|
|
5451
6342
|
import path22 from "path";
|
|
5452
6343
|
import process8 from "process";
|
|
5453
6344
|
var EXIT_OK7 = 0;
|
|
5454
6345
|
var EXIT_NOT_INITIALIZED5 = 1;
|
|
5455
6346
|
var EXIT_UNSUPPORTED_PLATFORM2 = 2;
|
|
5456
|
-
var CLAUDE_SKILL_DIRS = [
|
|
5457
|
-
"solo",
|
|
5458
|
-
"man8",
|
|
5459
|
-
"man",
|
|
5460
|
-
"manteam",
|
|
5461
|
-
"manps",
|
|
5462
|
-
"mansolo"
|
|
5463
|
-
];
|
|
5464
|
-
var CLAUDE_AGENT_FILES = [
|
|
5465
|
-
"scout.md",
|
|
5466
|
-
"head-coach.md",
|
|
5467
|
-
"film-analyst-offense.md",
|
|
5468
|
-
"film-analyst-defense.md",
|
|
5469
|
-
"plan-coach.md"
|
|
5470
|
-
];
|
|
5471
6347
|
async function uninstall(rootDir = process8.cwd(), platform, options = {}) {
|
|
5472
6348
|
const stateFile = path22.join(rootDir, ".mancode", "state.json");
|
|
5473
|
-
if (!await
|
|
6349
|
+
if (!await pathExists10(stateFile)) {
|
|
5474
6350
|
console.error("\u2717 mancode not initialized.");
|
|
5475
6351
|
console.error(" Run `mancode init` first.");
|
|
5476
6352
|
return EXIT_NOT_INITIALIZED5;
|
|
@@ -5527,22 +6403,14 @@ async function uninstallAll(rootDir) {
|
|
|
5527
6403
|
});
|
|
5528
6404
|
}
|
|
5529
6405
|
async function uninstallClaudeCode(rootDir) {
|
|
5530
|
-
|
|
5531
|
-
const skillsDir = path22.join(claudeDir, "skills");
|
|
5532
|
-
const agentsDir = path22.join(claudeDir, "agents");
|
|
5533
|
-
for (const skillDir of CLAUDE_SKILL_DIRS) {
|
|
5534
|
-
await rm5(path22.join(skillsDir, skillDir), { recursive: true, force: true });
|
|
5535
|
-
}
|
|
5536
|
-
for (const agentFile of CLAUDE_AGENT_FILES) {
|
|
5537
|
-
await rm5(path22.join(agentsDir, agentFile), { force: true });
|
|
5538
|
-
}
|
|
6406
|
+
await removeClaudeGeneratedContent(rootDir);
|
|
5539
6407
|
await cleanClaudeSettings(rootDir);
|
|
5540
6408
|
}
|
|
5541
6409
|
async function cleanClaudeSettings(rootDir) {
|
|
5542
6410
|
const settingsPath = path22.join(rootDir, ".claude", "settings.json");
|
|
5543
6411
|
let content;
|
|
5544
6412
|
try {
|
|
5545
|
-
content = await
|
|
6413
|
+
content = await readFile10(settingsPath, "utf-8");
|
|
5546
6414
|
} catch {
|
|
5547
6415
|
return;
|
|
5548
6416
|
}
|
|
@@ -5552,28 +6420,21 @@ async function cleanClaudeSettings(rootDir) {
|
|
|
5552
6420
|
} catch {
|
|
5553
6421
|
return;
|
|
5554
6422
|
}
|
|
5555
|
-
if (!isRecord6(settings.hooks)) return;
|
|
5556
6423
|
const cleanedHooks = {};
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
}
|
|
5562
|
-
const filtered = groups.flatMap((group) => {
|
|
5563
|
-
if (!isRecord6(group) || !Array.isArray(group.hooks)) return [group];
|
|
5564
|
-
const hooks = group.hooks.filter(
|
|
5565
|
-
(hook) => !(isRecord6(hook) && typeof hook.command === "string" && hook.command.includes(".mancode/hooks/"))
|
|
5566
|
-
);
|
|
5567
|
-
return hooks.length > 0 ? [{ ...group, hooks }] : [];
|
|
5568
|
-
});
|
|
5569
|
-
if (filtered.length > 0) {
|
|
5570
|
-
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;
|
|
5571
6428
|
}
|
|
6429
|
+
settings.hooks = Object.keys(cleanedHooks).length > 0 ? cleanedHooks : void 0;
|
|
5572
6430
|
}
|
|
5573
|
-
if (
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
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;
|
|
5577
6438
|
}
|
|
5578
6439
|
await writeFile11(
|
|
5579
6440
|
settingsPath,
|
|
@@ -5582,17 +6443,42 @@ async function cleanClaudeSettings(rootDir) {
|
|
|
5582
6443
|
"utf-8"
|
|
5583
6444
|
);
|
|
5584
6445
|
}
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
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 }] : [];
|
|
5589
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);
|
|
5590
6476
|
await removeCursorCommands(rootDir);
|
|
5591
6477
|
}
|
|
5592
6478
|
async function uninstallCodex(rootDir) {
|
|
5593
6479
|
const agentsPath = path22.join(rootDir, "AGENTS.md");
|
|
5594
6480
|
try {
|
|
5595
|
-
const content = await
|
|
6481
|
+
const content = await readFile10(agentsPath, "utf-8");
|
|
5596
6482
|
const cleaned = removeManagedBlock(content);
|
|
5597
6483
|
if (cleaned.trim()) {
|
|
5598
6484
|
await writeFile11(agentsPath, `${cleaned}
|
|
@@ -5611,7 +6497,7 @@ async function uninstallCopilot(rootDir) {
|
|
|
5611
6497
|
"copilot-instructions.md"
|
|
5612
6498
|
);
|
|
5613
6499
|
try {
|
|
5614
|
-
const content = await
|
|
6500
|
+
const content = await readFile10(instructionsPath, "utf-8");
|
|
5615
6501
|
const cleaned = removeManagedBlock(content);
|
|
5616
6502
|
if (cleaned.trim()) {
|
|
5617
6503
|
await writeFile11(instructionsPath, `${cleaned}
|
|
@@ -5626,7 +6512,7 @@ async function uninstallCopilot(rootDir) {
|
|
|
5626
6512
|
async function uninstallZcode(rootDir) {
|
|
5627
6513
|
const agentsPath = path22.join(rootDir, "AGENTS.md");
|
|
5628
6514
|
try {
|
|
5629
|
-
const content = await
|
|
6515
|
+
const content = await readFile10(agentsPath, "utf-8");
|
|
5630
6516
|
const cleaned = removeManagedBlock(
|
|
5631
6517
|
content,
|
|
5632
6518
|
ZCODE_MANCODE_START_MARKER,
|
|
@@ -5645,11 +6531,11 @@ async function uninstallZcode(rootDir) {
|
|
|
5645
6531
|
async function removeFromConfig(rootDir, platform) {
|
|
5646
6532
|
const configPath = path22.join(rootDir, ".mancode", "config.json");
|
|
5647
6533
|
try {
|
|
5648
|
-
const raw = await
|
|
6534
|
+
const raw = await readFile10(configPath, "utf-8");
|
|
5649
6535
|
const config = JSON.parse(raw);
|
|
5650
6536
|
if (Array.isArray(config.platforms)) {
|
|
5651
6537
|
config.platforms = config.platforms.filter((p) => p !== platform);
|
|
5652
|
-
if (
|
|
6538
|
+
if (isRecord8(config.platformOptions)) {
|
|
5653
6539
|
const platformOptions = Object.fromEntries(
|
|
5654
6540
|
Object.entries(config.platformOptions).filter(
|
|
5655
6541
|
([name]) => name !== platform
|
|
@@ -5667,12 +6553,12 @@ async function removeFromConfig(rootDir, platform) {
|
|
|
5667
6553
|
} catch {
|
|
5668
6554
|
}
|
|
5669
6555
|
}
|
|
5670
|
-
function
|
|
6556
|
+
function isRecord8(value) {
|
|
5671
6557
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5672
6558
|
}
|
|
5673
|
-
async function
|
|
6559
|
+
async function pathExists10(p) {
|
|
5674
6560
|
try {
|
|
5675
|
-
await
|
|
6561
|
+
await access4(p);
|
|
5676
6562
|
return true;
|
|
5677
6563
|
} catch {
|
|
5678
6564
|
return false;
|
|
@@ -5690,13 +6576,13 @@ function version() {
|
|
|
5690
6576
|
}
|
|
5691
6577
|
|
|
5692
6578
|
// src/commands/workflow.ts
|
|
5693
|
-
import { access as
|
|
6579
|
+
import { access as access5 } from "fs/promises";
|
|
5694
6580
|
import path23 from "path";
|
|
5695
6581
|
var EXIT_OK8 = 0;
|
|
5696
6582
|
var EXIT_NOT_INITIALIZED6 = 1;
|
|
5697
6583
|
var EXIT_INVALID_ARG2 = 2;
|
|
5698
6584
|
async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
5699
|
-
if (!await
|
|
6585
|
+
if (!await pathExists11(path23.join(rootDir, ".mancode", "state.json"))) {
|
|
5700
6586
|
if (options.json) {
|
|
5701
6587
|
console.log(JSON.stringify({ error: "not initialized" }, null, 2));
|
|
5702
6588
|
} else {
|
|
@@ -5728,7 +6614,7 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
|
5728
6614
|
} else {
|
|
5729
6615
|
console.error(`\u2717 Invalid workflow subcommand: ${subcommand}`);
|
|
5730
6616
|
console.error(
|
|
5731
|
-
" Use: create <
|
|
6617
|
+
" Use: create <man|mamba|manteam> <task> | update <taskId> | list | show <taskId> | clean"
|
|
5732
6618
|
);
|
|
5733
6619
|
}
|
|
5734
6620
|
return EXIT_INVALID_ARG2;
|
|
@@ -5737,25 +6623,34 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
|
5737
6623
|
async function workflowCreate(rootDir, args, options) {
|
|
5738
6624
|
const mode = args[0];
|
|
5739
6625
|
const task = args.slice(1).join(" ").trim();
|
|
5740
|
-
if (mode !== "
|
|
6626
|
+
if (mode !== "man" && mode !== "mamba" && mode !== "manteam") {
|
|
5741
6627
|
return invalidArg(
|
|
5742
6628
|
options,
|
|
5743
6629
|
`invalid workflow mode: ${mode ?? ""}`,
|
|
5744
|
-
"Use: mancode workflow create <
|
|
6630
|
+
"Use: mancode workflow create <man|mamba|manteam> <task> [--parent-task <taskId>]"
|
|
5745
6631
|
);
|
|
5746
6632
|
}
|
|
5747
6633
|
if (!task) {
|
|
5748
6634
|
return invalidArg(
|
|
5749
6635
|
options,
|
|
5750
6636
|
"missing workflow task",
|
|
5751
|
-
"Use: mancode workflow create <
|
|
6637
|
+
"Use: mancode workflow create <man|mamba|manteam> <task> [--parent-task <taskId>]"
|
|
5752
6638
|
);
|
|
5753
6639
|
}
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
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
|
+
);
|
|
5759
6654
|
}
|
|
5760
6655
|
return EXIT_OK8;
|
|
5761
6656
|
}
|
|
@@ -5764,7 +6659,7 @@ async function workflowUpdate(rootDir, taskId, options) {
|
|
|
5764
6659
|
return invalidArg(
|
|
5765
6660
|
options,
|
|
5766
6661
|
"missing taskId",
|
|
5767
|
-
"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]"
|
|
5768
6663
|
);
|
|
5769
6664
|
}
|
|
5770
6665
|
if (!isValidWorkflowTaskId(taskId)) {
|
|
@@ -5776,9 +6671,9 @@ async function workflowUpdate(rootDir, taskId, options) {
|
|
|
5776
6671
|
}
|
|
5777
6672
|
const patch = {};
|
|
5778
6673
|
if (options.step !== void 0) {
|
|
5779
|
-
const currentStep =
|
|
5780
|
-
const maxStep = existing.mode
|
|
5781
|
-
if (
|
|
6674
|
+
const currentStep = parseExactPositiveInteger(options.step);
|
|
6675
|
+
const maxStep = maxWorkflowStep(existing.mode);
|
|
6676
|
+
if (currentStep === null || currentStep < 1 || currentStep > maxStep) {
|
|
5782
6677
|
return invalidArg(options, `invalid --step: ${options.step}`);
|
|
5783
6678
|
}
|
|
5784
6679
|
patch.currentStep = currentStep;
|
|
@@ -5789,14 +6684,67 @@ async function workflowUpdate(rootDir, taskId, options) {
|
|
|
5789
6684
|
}
|
|
5790
6685
|
patch.status = options.status;
|
|
5791
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
|
+
}
|
|
5792
6723
|
if (options.skipped !== void 0) {
|
|
5793
6724
|
patch.skippedSteps = options.skipped.split(",").map((item) => item.trim()).filter(Boolean);
|
|
5794
6725
|
}
|
|
5795
6726
|
if (Object.keys(patch).length === 0) {
|
|
5796
6727
|
return invalidArg(options, "missing update fields");
|
|
5797
6728
|
}
|
|
5798
|
-
|
|
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
|
+
}
|
|
5799
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
|
+
}
|
|
5800
6748
|
if (options.json) {
|
|
5801
6749
|
console.log(JSON.stringify(updated, null, 2));
|
|
5802
6750
|
} else {
|
|
@@ -5806,8 +6754,9 @@ async function workflowUpdate(rootDir, taskId, options) {
|
|
|
5806
6754
|
}
|
|
5807
6755
|
async function workflowList(rootDir, options) {
|
|
5808
6756
|
const workflows = await listWorkflows(rootDir);
|
|
6757
|
+
const views = attachActiveChildren(workflows);
|
|
5809
6758
|
if (options.json) {
|
|
5810
|
-
console.log(JSON.stringify(
|
|
6759
|
+
console.log(JSON.stringify(views, null, 2));
|
|
5811
6760
|
return EXIT_OK8;
|
|
5812
6761
|
}
|
|
5813
6762
|
if (workflows.length === 0) {
|
|
@@ -5819,7 +6768,7 @@ async function workflowList(rootDir, options) {
|
|
|
5819
6768
|
`mancode workflows (${workflows.length} total, ${inProgress} in_progress)`
|
|
5820
6769
|
);
|
|
5821
6770
|
console.log("");
|
|
5822
|
-
for (const meta of
|
|
6771
|
+
for (const meta of views) {
|
|
5823
6772
|
console.log(formatWorkflowRow(meta));
|
|
5824
6773
|
}
|
|
5825
6774
|
return EXIT_OK8;
|
|
@@ -5848,22 +6797,44 @@ async function workflowShow(rootDir, taskId, options) {
|
|
|
5848
6797
|
}
|
|
5849
6798
|
return EXIT_INVALID_ARG2;
|
|
5850
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;
|
|
5851
6803
|
if (options.json) {
|
|
5852
|
-
console.log(
|
|
6804
|
+
console.log(
|
|
6805
|
+
JSON.stringify(
|
|
6806
|
+
children === void 0 ? meta : { ...meta, activeChildren: children },
|
|
6807
|
+
null,
|
|
6808
|
+
2
|
|
6809
|
+
)
|
|
6810
|
+
);
|
|
5853
6811
|
return EXIT_OK8;
|
|
5854
6812
|
}
|
|
5855
6813
|
console.log(`Workflow: ${meta.taskId}`);
|
|
5856
6814
|
console.log(`Task: ${meta.task}`);
|
|
5857
6815
|
console.log(`Mode: ${meta.mode}`);
|
|
5858
6816
|
console.log(`Status: ${meta.status}`);
|
|
5859
|
-
console.log(
|
|
5860
|
-
`Current step:${meta.currentStep}/${meta.mode === "man" ? 8 : 3}`
|
|
5861
|
-
);
|
|
6817
|
+
console.log(`Current step:${meta.currentStep}/${maxWorkflowStep(meta.mode)}`);
|
|
5862
6818
|
console.log(
|
|
5863
6819
|
`Skipped: ${meta.skippedSteps.length > 0 ? meta.skippedSteps.join(", ") : "none"}`
|
|
5864
6820
|
);
|
|
5865
6821
|
console.log(`Started: ${meta.startedAt}`);
|
|
5866
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
|
+
}
|
|
5867
6838
|
return EXIT_OK8;
|
|
5868
6839
|
}
|
|
5869
6840
|
async function workflowClean(rootDir, options) {
|
|
@@ -5885,41 +6856,74 @@ async function workflowClean(rootDir, options) {
|
|
|
5885
6856
|
return EXIT_INVALID_ARG2;
|
|
5886
6857
|
}
|
|
5887
6858
|
const candidates = workflows.filter((meta) => {
|
|
5888
|
-
if (!
|
|
6859
|
+
if (!isTerminalWorkflowStatus(meta.status)) return false;
|
|
6860
|
+
if (!cutoff) return true;
|
|
5889
6861
|
const started = Date.parse(meta.startedAt);
|
|
5890
6862
|
return Number.isFinite(started) && started < cutoff.getTime();
|
|
5891
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
|
+
}
|
|
5892
6878
|
if (options.json) {
|
|
5893
6879
|
console.log(
|
|
5894
6880
|
JSON.stringify(
|
|
5895
6881
|
{
|
|
5896
6882
|
dryRun: Boolean(options.dryRun),
|
|
5897
|
-
count: candidates.length,
|
|
5898
|
-
workflows: candidates
|
|
6883
|
+
count: options.dryRun ? candidates.length : removed.length,
|
|
6884
|
+
workflows: options.dryRun ? candidates : removed
|
|
5899
6885
|
},
|
|
5900
6886
|
null,
|
|
5901
6887
|
2
|
|
5902
6888
|
)
|
|
5903
6889
|
);
|
|
5904
|
-
} else if (candidates.length === 0) {
|
|
6890
|
+
} else if ((options.dryRun ? candidates : removed).length === 0) {
|
|
5905
6891
|
console.log("No workflows to clean.");
|
|
5906
6892
|
} else if (options.dryRun) {
|
|
5907
6893
|
console.log(`Would remove ${candidates.length} workflow(s):`);
|
|
5908
6894
|
for (const meta of candidates) console.log(` ${meta.taskId}`);
|
|
5909
6895
|
} else {
|
|
5910
|
-
console.log(`Removed ${
|
|
5911
|
-
}
|
|
5912
|
-
if (!options.dryRun) {
|
|
5913
|
-
for (const meta of candidates) {
|
|
5914
|
-
await deleteWorkflow(rootDir, meta.taskId);
|
|
5915
|
-
}
|
|
6896
|
+
console.log(`Removed ${removed.length} workflow(s).`);
|
|
5916
6897
|
}
|
|
5917
6898
|
return EXIT_OK8;
|
|
5918
6899
|
}
|
|
5919
6900
|
function formatWorkflowRow(meta) {
|
|
5920
|
-
const stepMax = meta.mode
|
|
6901
|
+
const stepMax = maxWorkflowStep(meta.mode);
|
|
5921
6902
|
const step = meta.status === "in_progress" ? `Step ${meta.currentStep}/${stepMax}` : ago(meta.updatedAt);
|
|
5922
|
-
|
|
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;
|
|
5923
6927
|
}
|
|
5924
6928
|
function parseOlderThan(value) {
|
|
5925
6929
|
if (!value) return void 0;
|
|
@@ -5939,9 +6943,6 @@ function invalidArg(options, message, usage) {
|
|
|
5939
6943
|
}
|
|
5940
6944
|
return EXIT_INVALID_ARG2;
|
|
5941
6945
|
}
|
|
5942
|
-
function isWorkflowStatus(value) {
|
|
5943
|
-
return value === "in_progress" || value === "completed" || value === "abandoned";
|
|
5944
|
-
}
|
|
5945
6946
|
function ago(iso) {
|
|
5946
6947
|
const time = Date.parse(iso);
|
|
5947
6948
|
if (!Number.isFinite(time)) return "unknown";
|
|
@@ -5953,9 +6954,9 @@ function ago(iso) {
|
|
|
5953
6954
|
if (hours < 24) return `${hours}h ago`;
|
|
5954
6955
|
return `${Math.floor(hours / 24)}d ago`;
|
|
5955
6956
|
}
|
|
5956
|
-
async function
|
|
6957
|
+
async function pathExists11(p) {
|
|
5957
6958
|
try {
|
|
5958
|
-
await
|
|
6959
|
+
await access5(p);
|
|
5959
6960
|
return true;
|
|
5960
6961
|
} catch {
|
|
5961
6962
|
return false;
|
|
@@ -5963,7 +6964,9 @@ async function pathExists12(p) {
|
|
|
5963
6964
|
}
|
|
5964
6965
|
|
|
5965
6966
|
// src/cli.ts
|
|
5966
|
-
program.name("mancode").description(
|
|
6967
|
+
program.name("mancode").description(
|
|
6968
|
+
"AI coding agent harness. Modes: solo, /man, /mamba, /manteam, /manps."
|
|
6969
|
+
).version(VERSION);
|
|
5967
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) => {
|
|
5968
6971
|
const code = await init(process.cwd(), options);
|
|
5969
6972
|
process.exitCode = code;
|
|
@@ -5990,7 +6993,10 @@ program.command("uninstall [platform]").description("Remove platform adapter or
|
|
|
5990
6993
|
const code = await uninstall(process.cwd(), platform, options);
|
|
5991
6994
|
process.exitCode = code;
|
|
5992
6995
|
});
|
|
5993
|
-
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(
|
|
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) => {
|
|
5994
7000
|
const code = await workflow(process.cwd(), subcommand, args ?? [], options);
|
|
5995
7001
|
process.exitCode = code;
|
|
5996
7002
|
});
|
|
@@ -5998,7 +7004,7 @@ program.command("manps [area]").description("Run deterministic preseason health
|
|
|
5998
7004
|
const code = await manps(process.cwd(), area ?? "all", options);
|
|
5999
7005
|
process.exitCode = code;
|
|
6000
7006
|
});
|
|
6001
|
-
program.command("refresh-style").description("
|
|
7007
|
+
program.command("refresh-style").description("Refresh project profile and rescan applicable design tokens").action(async () => {
|
|
6002
7008
|
const code = await refreshStyle(process.cwd());
|
|
6003
7009
|
process.exitCode = code;
|
|
6004
7010
|
});
|