mancode 0.2.1 → 0.3.1
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 +124 -81
- package/README.zh-CN.md +104 -73
- package/dist/cli.js +1827 -790
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
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
|
|
896
|
+
|
|
897
|
+
- \u5DF2\u4FEE\u590D\uFF1A\`status: "completed", outcome: "fixed"\`
|
|
898
|
+
- \u5DF2\u9A8C\u8BC1\u4F46\u672A\u6539\u4EE3\u7801\uFF1A\`status: "completed", outcome: "verified"\`
|
|
899
|
+
- \u6307\u5B9A\u73AF\u5883\u672A\u590D\u73B0\uFF1A\`status: "completed", outcome: "no_repro"\`\uFF0C\u9650\u5B9A\u7ED3\u8BBA\u8303\u56F4
|
|
900
|
+
- \u7F3A\u5C11\u73AF\u5883\u3001\u6743\u9650\u6216\u6570\u636E\uFF0C\u6216\u4FEE\u590D\u4ECD\u5931\u8D25\uFF1A\`status: "blocked"\` + \`blockingReason\`
|
|
901
|
+
- \u53D7\u9650\u5E73\u53F0\u53EA\u80FD\u751F\u6210\u624B\u6D4B\u65B9\u6848\uFF1A\`status: "completed", outcome: "manual_test_required"\`\uFF1B\u5FC5\u987B\u660E\u786E\u6CA1\u6709\u6267\u884C\u771F\u5B9E\u6D4B\u8BD5
|
|
902
|
+
|
|
903
|
+
\u7528 \`mancode workflow update <taskId> --status <status> [--outcome <outcome>] [--blocking-reason <\u539F\u56E0>]\` \u5199\u7ED3\u8BBA\u3002\u7236\u5B50\u4EFB\u52A1\u7684 blocked/manual_test_required \u4F20\u64AD\u7531 CLI \u8D1F\u8D23\u3002\u5173\u8054\u5B50\u4EFB\u52A1\u4EE5 fixed/verified/no_repro \u5B8C\u6210\u540E\uFF0C\u5148\u8BFB\u53D6\u7236 workflow\uFF1B\u82E5\u7236\u4ECD\u56E0\u672C\u5B50\u4EFB\u52A1 blocked\uFF0C\u8FD0\u884C \`mancode workflow update <parentTaskId> --status in_progress\` \u6E05\u9664\u963B\u585E\uFF0C\u518D\u628A state \u6062\u590D\u5230\u7236 workflow \u7684 Step 6\u3002manual_test_required \u4E0D\u5F97\u81EA\u52A8\u6062\u590D\u7236\u4EFB\u52A1\u3002\u72EC\u7ACB\u4EFB\u52A1\u5B8C\u6210\u540E\u56DE solo\uFF1Bblocked \u7684\u72EC\u7ACB\u4EFB\u52A1\u7EE7\u7EED\u4FDD\u6301 mamba state\u3002\u4EFB\u4F55\u60C5\u51B5\u4E0B\u90FD\u4E0D\u5F97\u76F4\u63A5\u7F16\u8F91 metadata \u7ED5\u8FC7 CLI\u3002
|
|
1000
904
|
|
|
1001
|
-
|
|
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,22 @@ 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 MANCODE_HOOK_COMMANDS = [
|
|
1458
|
+
"bash .mancode/hooks/session-start.sh",
|
|
1459
|
+
"bash .mancode/hooks/user-prompt-submit.sh"
|
|
1460
|
+
];
|
|
1461
|
+
function isGeneratedMancodeHookCommand(command) {
|
|
1462
|
+
const normalized = command.trim();
|
|
1463
|
+
return MANCODE_HOOK_COMMANDS.some((item) => item === normalized);
|
|
1464
|
+
}
|
|
1465
|
+
var LEGACY_CLAUDE_SKILL_SETTINGS = {
|
|
1466
|
+
solo: ".claude/skills/mancode-solo.md",
|
|
1467
|
+
man8: ".claude/skills/mancode-man8.md",
|
|
1468
|
+
man: ".claude/skills/mancode-man.md",
|
|
1469
|
+
mansolo: ".claude/skills/mancode-mansolo.md"
|
|
1470
|
+
};
|
|
1493
1471
|
async function validateClaudeCodeSettings(projectRoot) {
|
|
1494
1472
|
await readClaudeSettings(path3.join(projectRoot, ".claude"));
|
|
1495
1473
|
}
|
|
@@ -1499,7 +1477,7 @@ async function installClaudeCode(projectRoot, options) {
|
|
|
1499
1477
|
const force = options.force ?? false;
|
|
1500
1478
|
await installMancodeCore(projectRoot);
|
|
1501
1479
|
await mkdir3(path3.join(claudeDir, "skills"), { recursive: true });
|
|
1502
|
-
await installSoloSkill(path3.join(claudeDir, "skills"));
|
|
1480
|
+
await installSoloSkill(path3.join(claudeDir, "skills"), force);
|
|
1503
1481
|
if (options.minimal) {
|
|
1504
1482
|
await uninstallMvp2Skills(path3.join(claudeDir, "skills"));
|
|
1505
1483
|
} else {
|
|
@@ -1512,44 +1490,59 @@ async function installClaudeCode(projectRoot, options) {
|
|
|
1512
1490
|
}
|
|
1513
1491
|
await updateClaudeSettings(claudeDir, settings);
|
|
1514
1492
|
}
|
|
1515
|
-
async function installSoloSkill(skillsDir) {
|
|
1516
|
-
await installProjectSkill(
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1493
|
+
async function installSoloSkill(skillsDir, force) {
|
|
1494
|
+
await installProjectSkill(
|
|
1495
|
+
skillsDir,
|
|
1496
|
+
{
|
|
1497
|
+
name: "solo",
|
|
1498
|
+
description: "Default mancode solo-mode guidance for small, focused tasks.",
|
|
1499
|
+
body: SOLO_SKILL
|
|
1500
|
+
},
|
|
1501
|
+
force
|
|
1502
|
+
);
|
|
1503
|
+
await removeLegacyFlatSkill(skillsDir, "mancode-solo", "solo");
|
|
1522
1504
|
}
|
|
1523
1505
|
async function installMvp2Skills(skillsDir, force) {
|
|
1506
|
+
await removeManagedLegacyMan8Skill(skillsDir);
|
|
1524
1507
|
for (const skill of MVP2_SKILLS) {
|
|
1525
1508
|
await installProjectSkill(skillsDir, skill, force);
|
|
1526
|
-
await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}
|
|
1509
|
+
await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`, skill.name);
|
|
1527
1510
|
}
|
|
1528
1511
|
}
|
|
1512
|
+
async function removeManagedLegacyMan8Skill(skillsDir) {
|
|
1513
|
+
await removeGeneratedSkillDir(skillsDir, "man8");
|
|
1514
|
+
}
|
|
1529
1515
|
async function uninstallMvp2Skills(skillsDir) {
|
|
1530
1516
|
for (const skill of MVP2_SKILLS) {
|
|
1531
|
-
await
|
|
1532
|
-
|
|
1533
|
-
force: true
|
|
1534
|
-
});
|
|
1535
|
-
await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`);
|
|
1517
|
+
await removeGeneratedSkillDir(skillsDir, skill.name);
|
|
1518
|
+
await removeLegacyFlatSkill(skillsDir, `mancode-${skill.name}`, skill.name);
|
|
1536
1519
|
}
|
|
1537
1520
|
}
|
|
1538
1521
|
async function installProjectSkill(skillsDir, skill, force = true) {
|
|
1539
1522
|
const skillDir = path3.join(skillsDir, skill.name);
|
|
1540
1523
|
const skillPath = path3.join(skillDir, "SKILL.md");
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1524
|
+
const existing = await readTextIfExists2(skillPath);
|
|
1525
|
+
if (existing !== null && !force) {
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
if (existing !== null && force && !isGeneratedClaudeSkill(existing, skill.name)) {
|
|
1529
|
+
throw new Error(
|
|
1530
|
+
`refusing to overwrite user-authored Claude Code skill: ${skillPath}`
|
|
1531
|
+
);
|
|
1547
1532
|
}
|
|
1548
1533
|
await mkdir3(skillDir, { recursive: true });
|
|
1549
|
-
await writeFile3(
|
|
1534
|
+
await writeFile3(
|
|
1535
|
+
skillPath,
|
|
1536
|
+
addManagedMarker(renderSkill(skill), CLAUDE_SKILL_MANAGED_MARKER),
|
|
1537
|
+
"utf-8"
|
|
1538
|
+
);
|
|
1550
1539
|
}
|
|
1551
|
-
async function removeLegacyFlatSkill(skillsDir, legacyName) {
|
|
1552
|
-
|
|
1540
|
+
async function removeLegacyFlatSkill(skillsDir, legacyName, modeName) {
|
|
1541
|
+
const legacyPath = path3.join(skillsDir, `${legacyName}.md`);
|
|
1542
|
+
const content = await readTextIfExists2(legacyPath);
|
|
1543
|
+
if (content && isGeneratedClaudeSkill(content, modeName)) {
|
|
1544
|
+
await rm(legacyPath, { force: true });
|
|
1545
|
+
}
|
|
1553
1546
|
}
|
|
1554
1547
|
async function installAgents(agentsDir, force) {
|
|
1555
1548
|
await mkdir3(agentsDir, { recursive: true });
|
|
@@ -1562,19 +1555,84 @@ async function installAgents(agentsDir, force) {
|
|
|
1562
1555
|
} catch {
|
|
1563
1556
|
}
|
|
1564
1557
|
}
|
|
1565
|
-
const
|
|
1558
|
+
const existing = await readTextIfExists2(agentPath);
|
|
1559
|
+
if (existing !== null && force && !isGeneratedClaudeAgent(existing, agent.name)) {
|
|
1560
|
+
throw new Error(
|
|
1561
|
+
`refusing to overwrite user-authored Claude Code agent: ${agentPath}`
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
const content = addManagedMarker(
|
|
1565
|
+
renderAgent(agent),
|
|
1566
|
+
CLAUDE_AGENT_MANAGED_MARKER
|
|
1567
|
+
);
|
|
1566
1568
|
await writeFile3(agentPath, content, "utf-8");
|
|
1567
1569
|
}
|
|
1568
1570
|
}
|
|
1569
1571
|
async function uninstallAgents(agentsDir) {
|
|
1570
1572
|
for (const agent of ALL_AGENTS) {
|
|
1571
|
-
|
|
1573
|
+
const agentPath = path3.join(agentsDir, `${agent.name}.md`);
|
|
1574
|
+
const content = await readTextIfExists2(agentPath);
|
|
1575
|
+
if (content && isGeneratedClaudeAgent(content, agent.name)) {
|
|
1576
|
+
await rm(agentPath, { force: true });
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
async function removeClaudeGeneratedContent(projectRoot) {
|
|
1581
|
+
const claudeDir = path3.join(projectRoot, ".claude");
|
|
1582
|
+
const skillsDir = path3.join(claudeDir, "skills");
|
|
1583
|
+
for (const modeName of [
|
|
1584
|
+
"solo",
|
|
1585
|
+
"man8",
|
|
1586
|
+
...MVP2_SKILLS.map((skill) => skill.name)
|
|
1587
|
+
]) {
|
|
1588
|
+
await removeGeneratedSkillDir(skillsDir, modeName);
|
|
1589
|
+
await removeLegacyFlatSkill(skillsDir, `mancode-${modeName}`, modeName);
|
|
1590
|
+
}
|
|
1591
|
+
await uninstallAgents(path3.join(claudeDir, "agents"));
|
|
1592
|
+
}
|
|
1593
|
+
async function removeGeneratedSkillDir(skillsDir, modeName) {
|
|
1594
|
+
const skillDir = path3.join(skillsDir, modeName);
|
|
1595
|
+
const skillPath = path3.join(skillDir, "SKILL.md");
|
|
1596
|
+
const content = await readTextIfExists2(skillPath);
|
|
1597
|
+
if (!content || !isGeneratedClaudeSkill(content, modeName)) return;
|
|
1598
|
+
await rm(skillPath, { force: true });
|
|
1599
|
+
await removeIfEmpty(skillDir);
|
|
1600
|
+
}
|
|
1601
|
+
function isGeneratedClaudeSkill(content, modeName) {
|
|
1602
|
+
if (content.includes(CLAUDE_SKILL_MANAGED_MARKER)) return true;
|
|
1603
|
+
if (!content.includes(`name: ${modeName}`)) return false;
|
|
1604
|
+
const heading = modeName === "solo" ? "# mancode \xB7 solo mode" : modeName === "man8" ? "# mancode \xB7 /man8 (4 AM Warmup)" : `# mancode \xB7 /${modeName}`;
|
|
1605
|
+
return content.includes(heading);
|
|
1606
|
+
}
|
|
1607
|
+
function isGeneratedClaudeAgent(content, name) {
|
|
1608
|
+
return content.includes(CLAUDE_AGENT_MANAGED_MARKER) || content.includes(`name: ${name}`) && content.includes("mancode \u6559\u7EC3\u7EC4");
|
|
1609
|
+
}
|
|
1610
|
+
function addManagedMarker(content, marker) {
|
|
1611
|
+
return content.replace("---\n\n", `---
|
|
1612
|
+
|
|
1613
|
+
${marker}
|
|
1614
|
+
|
|
1615
|
+
`);
|
|
1616
|
+
}
|
|
1617
|
+
async function readTextIfExists2(filePath) {
|
|
1618
|
+
try {
|
|
1619
|
+
return await readFile3(filePath, "utf-8");
|
|
1620
|
+
} catch {
|
|
1621
|
+
return null;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
async function removeIfEmpty(dir) {
|
|
1625
|
+
try {
|
|
1626
|
+
if ((await readdir(dir)).length === 0) {
|
|
1627
|
+
await rm(dir, { recursive: true, force: true });
|
|
1628
|
+
}
|
|
1629
|
+
} catch {
|
|
1572
1630
|
}
|
|
1573
1631
|
}
|
|
1574
1632
|
async function readClaudeSettings(claudeDir) {
|
|
1575
1633
|
const settingsPath = path3.join(claudeDir, "settings.json");
|
|
1576
1634
|
try {
|
|
1577
|
-
const raw = await
|
|
1635
|
+
const raw = await readFile3(settingsPath, "utf-8");
|
|
1578
1636
|
return JSON.parse(raw);
|
|
1579
1637
|
} catch (err) {
|
|
1580
1638
|
if (isNodeError2(err) && err.code === "ENOENT") {
|
|
@@ -1591,11 +1649,11 @@ async function updateClaudeSettings(claudeDir, settings) {
|
|
|
1591
1649
|
settings.hooks = settings.hooks || {};
|
|
1592
1650
|
settings.hooks.SessionStart = [
|
|
1593
1651
|
...normalizeHookGroups(settings.hooks.SessionStart),
|
|
1594
|
-
createCommandGroup(
|
|
1652
|
+
createCommandGroup(MANCODE_HOOK_COMMANDS[0])
|
|
1595
1653
|
];
|
|
1596
1654
|
settings.hooks.UserPromptSubmit = [
|
|
1597
1655
|
...normalizeHookGroups(settings.hooks.UserPromptSubmit),
|
|
1598
|
-
createCommandGroup(
|
|
1656
|
+
createCommandGroup(MANCODE_HOOK_COMMANDS[1])
|
|
1599
1657
|
];
|
|
1600
1658
|
removeLegacyMancodeSkillSettings(settings);
|
|
1601
1659
|
const content = `${JSON.stringify(settings, null, 2)}
|
|
@@ -1604,16 +1662,10 @@ async function updateClaudeSettings(claudeDir, settings) {
|
|
|
1604
1662
|
}
|
|
1605
1663
|
function removeLegacyMancodeSkillSettings(settings) {
|
|
1606
1664
|
if (!settings.skills) return;
|
|
1607
|
-
const legacyNames = /* @__PURE__ */ new Set([
|
|
1608
|
-
"solo",
|
|
1609
|
-
"man8",
|
|
1610
|
-
"man",
|
|
1611
|
-
"manteam",
|
|
1612
|
-
"manps",
|
|
1613
|
-
"mansolo"
|
|
1614
|
-
]);
|
|
1615
1665
|
const retained = Object.fromEntries(
|
|
1616
|
-
Object.entries(settings.skills).filter(
|
|
1666
|
+
Object.entries(settings.skills).filter(
|
|
1667
|
+
([name, value]) => LEGACY_CLAUDE_SKILL_SETTINGS[name] !== value
|
|
1668
|
+
)
|
|
1617
1669
|
);
|
|
1618
1670
|
settings.skills = Object.keys(retained).length > 0 ? retained : void 0;
|
|
1619
1671
|
}
|
|
@@ -1632,7 +1684,10 @@ function normalizeHookGroups(value) {
|
|
|
1632
1684
|
return value.flatMap(normalizeHookGroupEntry);
|
|
1633
1685
|
}
|
|
1634
1686
|
if (isRecord(value)) {
|
|
1635
|
-
return Object.
|
|
1687
|
+
return Object.entries(value).flatMap(([key, entry]) => {
|
|
1688
|
+
if (key === "mancode" && containsLegacyMancodeHookPath(entry)) return [];
|
|
1689
|
+
return normalizeHookGroupEntry(entry);
|
|
1690
|
+
});
|
|
1636
1691
|
}
|
|
1637
1692
|
return [];
|
|
1638
1693
|
}
|
|
@@ -1670,7 +1725,15 @@ function normalizeHookItem(value) {
|
|
|
1670
1725
|
};
|
|
1671
1726
|
}
|
|
1672
1727
|
function isMancodeHook(hook) {
|
|
1673
|
-
return hook.command
|
|
1728
|
+
return isGeneratedMancodeHookCommand(hook.command);
|
|
1729
|
+
}
|
|
1730
|
+
function containsLegacyMancodeHookPath(value) {
|
|
1731
|
+
if (Array.isArray(value)) return value.some(containsLegacyMancodeHookPath);
|
|
1732
|
+
if (!isRecord(value)) return false;
|
|
1733
|
+
if (typeof value.command === "string" && value.command.includes(".mancode/hooks/")) {
|
|
1734
|
+
return true;
|
|
1735
|
+
}
|
|
1736
|
+
return Object.values(value).some(containsLegacyMancodeHookPath);
|
|
1674
1737
|
}
|
|
1675
1738
|
function isRecord(value) {
|
|
1676
1739
|
return typeof value === "object" && value !== null;
|
|
@@ -1774,17 +1837,19 @@ function findMarkerLine(content, marker) {
|
|
|
1774
1837
|
}
|
|
1775
1838
|
|
|
1776
1839
|
// src/installers/mode-skills.ts
|
|
1777
|
-
import { mkdir as mkdir4, readFile as
|
|
1840
|
+
import { mkdir as mkdir4, readFile as readFile4, readdir as readdir2, rm as rm2, writeFile as writeFile4 } from "fs/promises";
|
|
1778
1841
|
import path4 from "path";
|
|
1779
1842
|
var MODE_NAMES = [
|
|
1780
|
-
"
|
|
1843
|
+
"mamba",
|
|
1781
1844
|
"man",
|
|
1782
1845
|
"manteam",
|
|
1783
1846
|
"manps",
|
|
1784
1847
|
"mansolo"
|
|
1785
1848
|
];
|
|
1849
|
+
var LEGACY_MODE_NAMES = ["man8"];
|
|
1786
1850
|
var CODEX_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:codex-skill. Do not edit this file manually. -->";
|
|
1787
1851
|
var ZCODE_SKILL_MANAGED_MARKER = "<!-- Managed by mancode:zcode-skill. Do not edit this file manually. -->";
|
|
1852
|
+
var MODE_FILE_MANAGED_MARKER = "<!-- Managed by mancode:mode-file. Do not edit this file manually. -->";
|
|
1788
1853
|
var MANCODE_AGENT_SKILL_MARKERS = [
|
|
1789
1854
|
CODEX_SKILL_MANAGED_MARKER,
|
|
1790
1855
|
ZCODE_SKILL_MANAGED_MARKER
|
|
@@ -1819,8 +1884,9 @@ function renderModeSkill(mode, commandPrefix = "$") {
|
|
|
1819
1884
|
"For every task, consider: why this change, what already exists, and what",
|
|
1820
1885
|
"is the smallest useful diff?",
|
|
1821
1886
|
"",
|
|
1822
|
-
"
|
|
1823
|
-
"
|
|
1887
|
+
"Read `.mancode/project-profile.json` before choosing tools or validation.",
|
|
1888
|
+
"Only for a UI task in a profile with detected UI assets, read",
|
|
1889
|
+
"`.mancode/aesthetics/style-tokens.json` before inventing new styles.",
|
|
1824
1890
|
"",
|
|
1825
1891
|
meta.workflow
|
|
1826
1892
|
].join("\n");
|
|
@@ -1831,6 +1897,7 @@ async function installCodexSkills(projectRoot, minimal) {
|
|
|
1831
1897
|
return;
|
|
1832
1898
|
}
|
|
1833
1899
|
const skillsDir = path4.join(projectRoot, ".agents", "skills");
|
|
1900
|
+
await removeLegacyManagedSkills(skillsDir, MANCODE_AGENT_SKILL_MARKERS);
|
|
1834
1901
|
for (const mode of MODE_NAMES) {
|
|
1835
1902
|
const modeDir = path4.join(skillsDir, mode);
|
|
1836
1903
|
await mkdir4(modeDir, { recursive: true });
|
|
@@ -1861,6 +1928,7 @@ async function installZcodeSkills(projectRoot, minimal) {
|
|
|
1861
1928
|
return;
|
|
1862
1929
|
}
|
|
1863
1930
|
const skillsDir = path4.join(projectRoot, ".agents", "skills");
|
|
1931
|
+
await removeLegacyManagedSkills(skillsDir, MANCODE_AGENT_SKILL_MARKERS);
|
|
1864
1932
|
for (const mode of MODE_NAMES) {
|
|
1865
1933
|
const modeDir = path4.join(skillsDir, mode);
|
|
1866
1934
|
await mkdir4(modeDir, { recursive: true });
|
|
@@ -1892,6 +1960,7 @@ async function installCursorCommands(projectRoot, minimal) {
|
|
|
1892
1960
|
}
|
|
1893
1961
|
const commandsDir = path4.join(projectRoot, ".cursor", "commands");
|
|
1894
1962
|
await mkdir4(commandsDir, { recursive: true });
|
|
1963
|
+
await removeLegacyGeneratedModeFile(path4.join(commandsDir, "man8.md"));
|
|
1895
1964
|
for (const mode of MODE_NAMES) {
|
|
1896
1965
|
const meta = MODE_META[mode];
|
|
1897
1966
|
const content = [
|
|
@@ -1899,10 +1968,17 @@ async function installCursorCommands(projectRoot, minimal) {
|
|
|
1899
1968
|
`description: ${JSON.stringify(meta.description)}`,
|
|
1900
1969
|
"---",
|
|
1901
1970
|
"",
|
|
1971
|
+
MODE_FILE_MANAGED_MARKER,
|
|
1972
|
+
"",
|
|
1902
1973
|
renderModeSkill(mode, "/"),
|
|
1903
1974
|
""
|
|
1904
1975
|
].join("\n");
|
|
1905
|
-
await
|
|
1976
|
+
await writeManagedModeFile(
|
|
1977
|
+
path4.join(commandsDir, `${mode}.md`),
|
|
1978
|
+
content,
|
|
1979
|
+
mode,
|
|
1980
|
+
"Cursor"
|
|
1981
|
+
);
|
|
1906
1982
|
}
|
|
1907
1983
|
}
|
|
1908
1984
|
async function installCopilotPrompts(projectRoot, minimal) {
|
|
@@ -1912,6 +1988,7 @@ async function installCopilotPrompts(projectRoot, minimal) {
|
|
|
1912
1988
|
}
|
|
1913
1989
|
const promptsDir = path4.join(projectRoot, ".github", "prompts");
|
|
1914
1990
|
await mkdir4(promptsDir, { recursive: true });
|
|
1991
|
+
await removeLegacyGeneratedModeFile(path4.join(promptsDir, "man8.prompt.md"));
|
|
1915
1992
|
for (const mode of MODE_NAMES) {
|
|
1916
1993
|
const meta = MODE_META[mode];
|
|
1917
1994
|
const content = [
|
|
@@ -1920,64 +1997,114 @@ async function installCopilotPrompts(projectRoot, minimal) {
|
|
|
1920
1997
|
`description: ${JSON.stringify(meta.description)}`,
|
|
1921
1998
|
"---",
|
|
1922
1999
|
"",
|
|
2000
|
+
MODE_FILE_MANAGED_MARKER,
|
|
2001
|
+
"",
|
|
1923
2002
|
renderModeSkill(mode, ""),
|
|
1924
2003
|
""
|
|
1925
2004
|
].join("\n");
|
|
1926
|
-
await
|
|
2005
|
+
await writeManagedModeFile(
|
|
1927
2006
|
path4.join(promptsDir, `${mode}.prompt.md`),
|
|
1928
2007
|
content,
|
|
1929
|
-
|
|
2008
|
+
mode,
|
|
2009
|
+
"GitHub Copilot"
|
|
1930
2010
|
);
|
|
1931
2011
|
}
|
|
1932
2012
|
}
|
|
1933
2013
|
async function removeCodexSkills(projectRoot) {
|
|
1934
|
-
await
|
|
1935
|
-
|
|
2014
|
+
if (!await platformNeedsSharedSkills(projectRoot, "zcode")) {
|
|
2015
|
+
await cleanManagedSkills(
|
|
2016
|
+
path4.join(projectRoot, ".agents", "skills"),
|
|
2017
|
+
MANCODE_AGENT_SKILL_MARKERS
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
2020
|
+
await cleanManagedSkills(path4.join(projectRoot, ".codex", "skills"), [
|
|
2021
|
+
CODEX_SKILL_MANAGED_MARKER
|
|
2022
|
+
]);
|
|
1936
2023
|
}
|
|
1937
|
-
async function
|
|
1938
|
-
for (const mode of MODE_NAMES) {
|
|
2024
|
+
async function cleanManagedSkills(skillsDir, markers) {
|
|
2025
|
+
for (const mode of [...MODE_NAMES, ...LEGACY_MODE_NAMES]) {
|
|
1939
2026
|
const modeDir = path4.join(skillsDir, mode);
|
|
1940
2027
|
const skillPath = path4.join(modeDir, "SKILL.md");
|
|
1941
|
-
if (await
|
|
2028
|
+
if (await isManagedByAnyMarker(skillPath, markers)) {
|
|
1942
2029
|
await rm2(skillPath, { force: true });
|
|
1943
|
-
await
|
|
2030
|
+
await removeIfEmpty2(modeDir);
|
|
1944
2031
|
}
|
|
1945
2032
|
}
|
|
1946
|
-
await
|
|
2033
|
+
await removeIfEmpty2(skillsDir);
|
|
1947
2034
|
}
|
|
1948
2035
|
async function removeZcodeSkills(projectRoot) {
|
|
1949
|
-
await
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
const modeDir = path4.join(skillsDir, mode);
|
|
1955
|
-
const skillPath = path4.join(modeDir, "SKILL.md");
|
|
1956
|
-
if (await isManagedSkillFile(skillPath, ZCODE_SKILL_MANAGED_MARKER)) {
|
|
1957
|
-
await rm2(skillPath, { force: true });
|
|
1958
|
-
await removeIfEmpty(modeDir);
|
|
1959
|
-
}
|
|
2036
|
+
if (!await platformNeedsSharedSkills(projectRoot, "codex")) {
|
|
2037
|
+
await cleanManagedSkills(
|
|
2038
|
+
path4.join(projectRoot, ".agents", "skills"),
|
|
2039
|
+
MANCODE_AGENT_SKILL_MARKERS
|
|
2040
|
+
);
|
|
1960
2041
|
}
|
|
1961
|
-
await
|
|
2042
|
+
await cleanManagedSkills(path4.join(projectRoot, ".zcode", "skills"), [
|
|
2043
|
+
ZCODE_SKILL_MANAGED_MARKER
|
|
2044
|
+
]);
|
|
1962
2045
|
}
|
|
1963
2046
|
async function removeCursorCommands(projectRoot) {
|
|
1964
|
-
for (const mode of MODE_NAMES) {
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
2047
|
+
for (const mode of [...MODE_NAMES, ...LEGACY_MODE_NAMES]) {
|
|
2048
|
+
const filePath = path4.join(
|
|
2049
|
+
projectRoot,
|
|
2050
|
+
".cursor",
|
|
2051
|
+
"commands",
|
|
2052
|
+
`${mode}.md`
|
|
2053
|
+
);
|
|
2054
|
+
if (mode === "man8") await removeLegacyGeneratedModeFile(filePath);
|
|
2055
|
+
else await removeManagedModeFile(filePath, mode);
|
|
1968
2056
|
}
|
|
1969
2057
|
}
|
|
1970
2058
|
async function removeCopilotPrompts(projectRoot) {
|
|
1971
|
-
for (const mode of MODE_NAMES) {
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2059
|
+
for (const mode of [...MODE_NAMES, ...LEGACY_MODE_NAMES]) {
|
|
2060
|
+
const filePath = path4.join(
|
|
2061
|
+
projectRoot,
|
|
2062
|
+
".github",
|
|
2063
|
+
"prompts",
|
|
2064
|
+
`${mode}.prompt.md`
|
|
1975
2065
|
);
|
|
2066
|
+
if (mode === "man8") await removeLegacyGeneratedModeFile(filePath);
|
|
2067
|
+
else await removeManagedModeFile(filePath, mode);
|
|
1976
2068
|
}
|
|
1977
2069
|
}
|
|
1978
|
-
async function
|
|
2070
|
+
async function writeManagedModeFile(filePath, content, mode, platformLabel) {
|
|
2071
|
+
const existing = await readTextIfExists3(filePath);
|
|
2072
|
+
if (existing !== null && !isGeneratedModeFile(existing, mode)) {
|
|
2073
|
+
throw new Error(
|
|
2074
|
+
`refusing to overwrite user-authored ${platformLabel} mode file: ${filePath}`
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
await writeFile4(filePath, content, "utf-8");
|
|
2078
|
+
}
|
|
2079
|
+
async function removeManagedModeFile(filePath, mode) {
|
|
2080
|
+
const content = await readTextIfExists3(filePath);
|
|
2081
|
+
if (content && isGeneratedModeFile(content, mode)) {
|
|
2082
|
+
await rm2(filePath, { force: true });
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
async function removeLegacyGeneratedModeFile(filePath) {
|
|
2086
|
+
const content = await readTextIfExists3(filePath);
|
|
2087
|
+
if (content?.includes("# mancode man8 \u2014 Investigate and Plan") && content.includes("## Mode Persistence")) {
|
|
2088
|
+
await rm2(filePath, { force: true });
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
function isGeneratedModeFile(content, mode) {
|
|
2092
|
+
return content.includes(MODE_FILE_MANAGED_MARKER) || content.includes(`# mancode ${mode}`) && content.includes("## Mode Persistence");
|
|
2093
|
+
}
|
|
2094
|
+
async function removeLegacyManagedSkills(skillsDir, markers) {
|
|
2095
|
+
for (const mode of LEGACY_MODE_NAMES) {
|
|
2096
|
+
const modeDir = path4.join(skillsDir, mode);
|
|
2097
|
+
const skillPath = path4.join(modeDir, "SKILL.md");
|
|
2098
|
+
const content = await readTextIfExists3(skillPath);
|
|
2099
|
+
if (content && markers.some((marker) => content.includes(marker))) {
|
|
2100
|
+
await rm2(skillPath, { force: true });
|
|
2101
|
+
await removeIfEmpty2(modeDir);
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
async function removeIfEmpty2(dir) {
|
|
1979
2106
|
try {
|
|
1980
|
-
const entries = await
|
|
2107
|
+
const entries = await readdir2(dir);
|
|
1981
2108
|
if (entries.length === 0) {
|
|
1982
2109
|
await rm2(dir, { recursive: true, force: true });
|
|
1983
2110
|
}
|
|
@@ -1985,7 +2112,7 @@ async function removeIfEmpty(dir) {
|
|
|
1985
2112
|
}
|
|
1986
2113
|
}
|
|
1987
2114
|
async function writeManagedSkill(skillPath, content, marker, platformLabel, recognizedMarkers = [marker]) {
|
|
1988
|
-
const existing = await
|
|
2115
|
+
const existing = await readTextIfExists3(skillPath);
|
|
1989
2116
|
if (existing !== null && !recognizedMarkers.some((m) => existing.includes(m))) {
|
|
1990
2117
|
throw new Error(
|
|
1991
2118
|
`refusing to overwrite user-authored ${platformLabel} skill: ${skillPath}`
|
|
@@ -1993,70 +2120,109 @@ async function writeManagedSkill(skillPath, content, marker, platformLabel, reco
|
|
|
1993
2120
|
}
|
|
1994
2121
|
await writeFile4(skillPath, content, "utf-8");
|
|
1995
2122
|
}
|
|
1996
|
-
async function
|
|
1997
|
-
const content = await
|
|
1998
|
-
return content
|
|
2123
|
+
async function isManagedByAnyMarker(skillPath, markers) {
|
|
2124
|
+
const content = await readTextIfExists3(skillPath);
|
|
2125
|
+
return content !== null && markers.some((marker) => content.includes(marker));
|
|
1999
2126
|
}
|
|
2000
|
-
async function
|
|
2127
|
+
async function platformNeedsSharedSkills(projectRoot, platform) {
|
|
2001
2128
|
try {
|
|
2002
|
-
|
|
2129
|
+
const raw = await readFile4(
|
|
2130
|
+
path4.join(projectRoot, ".mancode", "config.json"),
|
|
2131
|
+
"utf-8"
|
|
2132
|
+
);
|
|
2133
|
+
const config = JSON.parse(raw);
|
|
2134
|
+
if (!Array.isArray(config.platforms) || !config.platforms.includes(platform)) {
|
|
2135
|
+
return false;
|
|
2136
|
+
}
|
|
2137
|
+
if (!isRecord2(config.platformOptions)) return true;
|
|
2138
|
+
const options = config.platformOptions[platform];
|
|
2139
|
+
return !isRecord2(options) || options.minimal !== true;
|
|
2140
|
+
} catch {
|
|
2141
|
+
return false;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
function isRecord2(value) {
|
|
2145
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2146
|
+
}
|
|
2147
|
+
async function readTextIfExists3(filePath) {
|
|
2148
|
+
try {
|
|
2149
|
+
return await readFile4(filePath, "utf-8");
|
|
2003
2150
|
} catch {
|
|
2004
2151
|
return null;
|
|
2005
2152
|
}
|
|
2006
2153
|
}
|
|
2007
2154
|
var MODE_META = {
|
|
2008
|
-
|
|
2009
|
-
description: "
|
|
2010
|
-
intro: "# mancode
|
|
2155
|
+
mamba: {
|
|
2156
|
+
description: "Diagnose bugs and validate real user flows with targeted regression checks.",
|
|
2157
|
+
intro: "# mancode mamba \u2014 Diagnosis and Real Validation",
|
|
2011
2158
|
workflow: [
|
|
2012
|
-
"##
|
|
2159
|
+
"## Five-step workflow (simulate roles in one conversation)",
|
|
2013
2160
|
"",
|
|
2014
|
-
|
|
2015
|
-
"
|
|
2161
|
+
'Use `mancode workflow create mamba "<task>" --json`; when called from',
|
|
2162
|
+
"a /man or /manteam Step 6 workflow, add `--parent-task <taskId>`. Persist the returned",
|
|
2163
|
+
"taskId in `.mancode/state.json` as currentTask; set currentMode and",
|
|
2164
|
+
"currentWorkflowMode to mamba. Never edit metadata.json directly; use",
|
|
2165
|
+
"`mancode workflow update` for every step and final state.",
|
|
2166
|
+
"If state points to an unrelated active workflow, stop and ask the user to resume it or abandon it through mansolo; never overwrite its pointer.",
|
|
2016
2167
|
"",
|
|
2017
|
-
"1.
|
|
2018
|
-
"
|
|
2019
|
-
"
|
|
2020
|
-
"
|
|
2021
|
-
"3.
|
|
2022
|
-
"
|
|
2168
|
+
"1. Define the symptom, environment, expected/actual result, protected data, and success criteria; then run `mancode workflow update <taskId> --step 2`.",
|
|
2169
|
+
"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.",
|
|
2170
|
+
" 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.",
|
|
2171
|
+
" If environment, permission, or data is missing, set blocked with `--blocking-reason` and keep state resumable instead of advancing.",
|
|
2172
|
+
"3. Reproduce the shortest path and write `diagnosis.md` with evidence, root cause or candidates, impact, and confidence; then update to Step 4.",
|
|
2173
|
+
"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.",
|
|
2174
|
+
"5. Re-run the path and affected positive, negative, and permission boundaries; write `mamba-report.md` with environment, evidence, artifacts, regression scope, and risks.",
|
|
2023
2175
|
"",
|
|
2024
|
-
"
|
|
2025
|
-
'to "solo") and implement the plan.'
|
|
2176
|
+
"Finish through the CLI with exactly one result: completed+fixed, completed+verified, completed+no_repro, completed+manual_test_required, or blocked+blockingReason. Never claim real testing in restricted mode. Child blocked/manual_test_required propagation is handled by the CLI. After a child finishes with fixed/verified/no_repro, inspect its parent; if that parent is still blocked because of this child, run `mancode workflow update <parentTaskId> --status in_progress` before restoring parent state at Step 6. Never auto-resume manual_test_required."
|
|
2026
2177
|
].join("\n")
|
|
2027
2178
|
},
|
|
2028
2179
|
man: {
|
|
2029
2180
|
description: "Full high-risk workflow with plan, implementation, verification, and review. Use when the user asks for man or a high-risk production change.",
|
|
2030
2181
|
intro: "# mancode man \u2014 Full Review Workflow",
|
|
2031
2182
|
workflow: [
|
|
2032
|
-
"##
|
|
2183
|
+
"## Progressive nine-step workflow (simulate roles in one conversation)",
|
|
2033
2184
|
"",
|
|
2034
|
-
|
|
2035
|
-
"
|
|
2185
|
+
'Create the durable workflow with `mancode workflow create man "<task>" --json`,',
|
|
2186
|
+
"persist its taskId in `.mancode/state.json` as currentTask, set currentMode",
|
|
2187
|
+
"and currentWorkflowMode to man, and use `mancode workflow update`",
|
|
2188
|
+
"for every step/status/plan revision. Never edit metadata.json directly.",
|
|
2189
|
+
"After each completed phase, advance through the CLI to the next numbered step;",
|
|
2190
|
+
"never advance state merely because a report was requested or an agent was called.",
|
|
2036
2191
|
"",
|
|
2037
|
-
"1
|
|
2038
|
-
"2
|
|
2039
|
-
"3.
|
|
2040
|
-
"4
|
|
2041
|
-
"5
|
|
2042
|
-
"6.
|
|
2043
|
-
"7
|
|
2044
|
-
"
|
|
2045
|
-
"
|
|
2192
|
+
"Step 1 \u2014 Scout: inspect the profile, existing implementation, dependencies, risks, and unknowns; write `scout-report.md`.",
|
|
2193
|
+
"Step 2 \u2014 Clarify: ask at most two rounds; write answers, conservative defaults, and unresolved risks to `requirements.md`.",
|
|
2194
|
+
"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.",
|
|
2195
|
+
"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.",
|
|
2196
|
+
"Step 5 \u2014 Implement the confirmed minimum change.",
|
|
2197
|
+
"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.",
|
|
2198
|
+
"Step 7 \u2014 Film #1 code-quality review; fix all required findings and explain suggested-item tradeoffs.",
|
|
2199
|
+
"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.",
|
|
2200
|
+
"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.",
|
|
2201
|
+
"",
|
|
2202
|
+
"After planned/completed/abandoned, return state to solo. On blocked, preserve enough state to resume. A CLI rejection is a hard stop, never a reason to bypass metadata validation."
|
|
2046
2203
|
].join("\n")
|
|
2047
2204
|
},
|
|
2048
2205
|
manteam: {
|
|
2049
2206
|
description: "Team-aware workflow with handoff-friendly summaries. Use when the user asks for manteam or when the task affects shared team context.",
|
|
2050
2207
|
intro: "# mancode manteam \u2014 Team Workflow",
|
|
2051
2208
|
workflow: [
|
|
2052
|
-
"##
|
|
2209
|
+
"## Team workflow",
|
|
2210
|
+
"",
|
|
2211
|
+
'Create with `mancode workflow create manteam "<task>" --json`; never edit',
|
|
2212
|
+
"metadata.json directly. Persist its taskId as currentTask and set currentMode",
|
|
2213
|
+
"and currentWorkflowMode to manteam. First collect branch, dirty-worktree, contributor,",
|
|
2214
|
+
"shared-module, and handoff risks in `team-context.md`.",
|
|
2053
2215
|
"",
|
|
2054
|
-
"
|
|
2055
|
-
"
|
|
2056
|
-
"
|
|
2057
|
-
"
|
|
2058
|
-
"
|
|
2059
|
-
"
|
|
2216
|
+
"Then execute every /man Step 1 through Step 9, including clarification,",
|
|
2217
|
+
"plan gate, validation, two review passes, conditional completion, planVersion,",
|
|
2218
|
+
"Active Plans, and linked mamba child behavior. Add compatibility, rollback,",
|
|
2219
|
+
"ownership, conflict, and handoff notes at each relevant step.",
|
|
2220
|
+
"When a resolved mamba child had blocked the parent, restore the parent through",
|
|
2221
|
+
"`workflow update --status in_progress`; never auto-resume manual_test_required.",
|
|
2222
|
+
"",
|
|
2223
|
+
"Read `.mancode/memory/prd.md`, `spec.md`, and `decisions.md`; append durable",
|
|
2224
|
+
"decisions only after confirmed implementation. Write `handoff.md`. Never",
|
|
2225
|
+
"overwrite teammate changes or commit/merge/push without explicit approval."
|
|
2060
2226
|
].join("\n")
|
|
2061
2227
|
},
|
|
2062
2228
|
manps: {
|
|
@@ -2081,32 +2247,39 @@ var MODE_META = {
|
|
|
2081
2247
|
"",
|
|
2082
2248
|
"You are now in solo (default) mode.",
|
|
2083
2249
|
"",
|
|
2084
|
-
|
|
2250
|
+
"If state points to an active workflow, show it with `mancode workflow show",
|
|
2251
|
+
"<taskId> --json`, ask for confirmation, abandon active children before their",
|
|
2252
|
+
"parent through `mancode workflow update ... --status abandoned`, and only",
|
|
2253
|
+
"then update `.mancode/state.json` to solo. Never edit metadata.json directly.",
|
|
2085
2254
|
"",
|
|
2086
2255
|
"- Keep the diff narrow.",
|
|
2087
2256
|
"- Reuse existing functions, components, styles, and dependencies.",
|
|
2088
|
-
"-
|
|
2257
|
+
"- Read project-profile first; for detected UI assets and UI work, read `.mancode/aesthetics/style-tokens.json`.",
|
|
2089
2258
|
"- Verify with the narrowest meaningful test, lint, build, or smoke check."
|
|
2090
2259
|
].join("\n")
|
|
2091
2260
|
}
|
|
2092
2261
|
};
|
|
2093
2262
|
|
|
2094
2263
|
// src/installers/shared-content.ts
|
|
2095
|
-
import { readFile as
|
|
2264
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
2096
2265
|
import path5 from "path";
|
|
2097
2266
|
async function generateSharedContent(projectRoot, options) {
|
|
2098
|
-
const [state, tokens] = await Promise.all([
|
|
2267
|
+
const [state, tokens, profile] = await Promise.all([
|
|
2099
2268
|
readJson(
|
|
2100
2269
|
path5.join(projectRoot, ".mancode", "state.json")
|
|
2101
2270
|
),
|
|
2102
2271
|
readJson(
|
|
2103
2272
|
path5.join(projectRoot, ".mancode", "aesthetics", "style-tokens.json")
|
|
2273
|
+
),
|
|
2274
|
+
readJson(
|
|
2275
|
+
path5.join(projectRoot, ".mancode", "project-profile.json")
|
|
2104
2276
|
)
|
|
2105
2277
|
]);
|
|
2278
|
+
const currentProfile = options.projectProfile ?? profile;
|
|
2106
2279
|
const sections = [
|
|
2107
|
-
renderProjectContext(state, tokens, options),
|
|
2280
|
+
renderProjectContext(state, tokens, profile, options),
|
|
2108
2281
|
renderPracticeRules(),
|
|
2109
|
-
renderAesthetics(tokens)
|
|
2282
|
+
currentProfile?.uiAssets === "detected" ? renderAesthetics(tokens) : ""
|
|
2110
2283
|
];
|
|
2111
2284
|
if (!options.minimal) {
|
|
2112
2285
|
sections.push(renderModes(options), renderPlatformDowngrade(options));
|
|
@@ -2114,9 +2287,10 @@ async function generateSharedContent(projectRoot, options) {
|
|
|
2114
2287
|
return `${sections.filter(Boolean).join("\n\n")}
|
|
2115
2288
|
`;
|
|
2116
2289
|
}
|
|
2117
|
-
function renderProjectContext(state, tokens, options) {
|
|
2118
|
-
const
|
|
2119
|
-
const
|
|
2290
|
+
function renderProjectContext(state, tokens, profile, options) {
|
|
2291
|
+
const currentProfile = options.projectProfile ?? profile;
|
|
2292
|
+
const techStack = options.projectProfile ? options.techStack.join(" + ") || "Unknown" : state?.techStack || options.techStack.join(" + ") || "Unknown";
|
|
2293
|
+
const uiLibrary = options.projectProfile ? options.uiLibrary || "None" : state?.uiLibrary || tokens?.uiLibrary || options.uiLibrary || "None";
|
|
2120
2294
|
const mode = state?.currentMode || "solo";
|
|
2121
2295
|
return [
|
|
2122
2296
|
"## mancode Project Context",
|
|
@@ -2125,8 +2299,9 @@ function renderProjectContext(state, tokens, options) {
|
|
|
2125
2299
|
`- Current mode: ${mode}`,
|
|
2126
2300
|
`- Tech stack: ${techStack}`,
|
|
2127
2301
|
`- UI library: ${uiLibrary}`,
|
|
2302
|
+
`- Project profile: ${currentProfile?.projectKind || "unknown"}; validation: ${currentProfile?.availableValidation?.join(", ") || "inspect project"}`,
|
|
2128
2303
|
"- At the start of each session, read `.mancode/state.json` to check the current mode and project context.",
|
|
2129
|
-
"-
|
|
2304
|
+
"- Read `.mancode/project-profile.json` before choosing tools or validation. Only for a UI task in a profile with detected UI assets, read `.mancode/aesthetics/style-tokens.json`."
|
|
2130
2305
|
].join("\n");
|
|
2131
2306
|
}
|
|
2132
2307
|
function renderPracticeRules() {
|
|
@@ -2178,7 +2353,7 @@ function renderAesthetics(tokens) {
|
|
|
2178
2353
|
function renderModes(options) {
|
|
2179
2354
|
let commandLabel;
|
|
2180
2355
|
if (options.capabilities.skills === "agents-skills") {
|
|
2181
|
-
commandLabel = "Invoke mode skills with `$
|
|
2356
|
+
commandLabel = "Invoke mode skills with `$man`, `$mamba`, `$manteam`, `$manps`, `$mansolo`.";
|
|
2182
2357
|
} else if (options.capabilities.slashCommands === "native") {
|
|
2183
2358
|
commandLabel = "Use the named commands directly when available.";
|
|
2184
2359
|
} else {
|
|
@@ -2188,8 +2363,8 @@ function renderModes(options) {
|
|
|
2188
2363
|
"## mancode Modes",
|
|
2189
2364
|
"",
|
|
2190
2365
|
`- solo: default lightweight mode. ${commandLabel}`,
|
|
2191
|
-
"-
|
|
2192
|
-
"-
|
|
2366
|
+
"- man: progressive governance workflow with planning and optional full execution.",
|
|
2367
|
+
"- mamba: diagnose bugs and validate real user flows or regressions.",
|
|
2193
2368
|
"- manteam: use team memory and leave handoff-friendly summaries.",
|
|
2194
2369
|
"- manps: run project health and cleanup scans before remediation.",
|
|
2195
2370
|
"- mansolo: return to solo mode."
|
|
@@ -2215,14 +2390,14 @@ function renderPlatformDowngrade(options) {
|
|
|
2215
2390
|
}
|
|
2216
2391
|
if (!options.capabilities.subagents) {
|
|
2217
2392
|
lines.push(
|
|
2218
|
-
"- Simulate the coaching staff in sequence inside the same conversation: Scout, Head Coach, Film Analyst Offense, Film Analyst Defense."
|
|
2393
|
+
"- Simulate the coaching staff in sequence inside the same conversation: Scout, Plan Coach, Head Coach, Film Analyst Offense, Film Analyst Defense."
|
|
2219
2394
|
);
|
|
2220
2395
|
}
|
|
2221
2396
|
return lines.join("\n");
|
|
2222
2397
|
}
|
|
2223
2398
|
async function readJson(filePath) {
|
|
2224
2399
|
try {
|
|
2225
|
-
return JSON.parse(await
|
|
2400
|
+
return JSON.parse(await readFile5(filePath, "utf-8"));
|
|
2226
2401
|
} catch {
|
|
2227
2402
|
return null;
|
|
2228
2403
|
}
|
|
@@ -2251,7 +2426,7 @@ async function installCodex(projectRoot, options) {
|
|
|
2251
2426
|
const existing = await readTextIfExists(agentsPath);
|
|
2252
2427
|
const sharedContent = await generateSharedContent(projectRoot, {
|
|
2253
2428
|
platform: "codex",
|
|
2254
|
-
displayName: "Codex CLI",
|
|
2429
|
+
displayName: "Codex (ChatGPT desktop/CLI)",
|
|
2255
2430
|
capabilities: {
|
|
2256
2431
|
slashCommands: "partial",
|
|
2257
2432
|
subagents: false,
|
|
@@ -2260,7 +2435,8 @@ async function installCodex(projectRoot, options) {
|
|
|
2260
2435
|
},
|
|
2261
2436
|
minimal: options.minimal,
|
|
2262
2437
|
techStack: options.techStack,
|
|
2263
|
-
uiLibrary: options.uiLibrary
|
|
2438
|
+
uiLibrary: options.uiLibrary,
|
|
2439
|
+
projectProfile: options.projectProfile
|
|
2264
2440
|
});
|
|
2265
2441
|
const block = [
|
|
2266
2442
|
DEFAULT_MANCODE_START_MARKER,
|
|
@@ -2295,7 +2471,8 @@ async function installCopilot(projectRoot, options) {
|
|
|
2295
2471
|
},
|
|
2296
2472
|
minimal: true,
|
|
2297
2473
|
techStack: options.techStack,
|
|
2298
|
-
uiLibrary: options.uiLibrary
|
|
2474
|
+
uiLibrary: options.uiLibrary,
|
|
2475
|
+
projectProfile: options.projectProfile
|
|
2299
2476
|
});
|
|
2300
2477
|
const sections = [
|
|
2301
2478
|
DEFAULT_MANCODE_START_MARKER,
|
|
@@ -2322,8 +2499,8 @@ function renderCopilotPromptConventions() {
|
|
|
2322
2499
|
"",
|
|
2323
2500
|
"GitHub Copilot does not provide native mancode slash commands, hooks, or isolated subagents. Treat these names as user prompt conventions:",
|
|
2324
2501
|
"",
|
|
2325
|
-
"-
|
|
2326
|
-
"-
|
|
2502
|
+
"- man: use progressive research, planning, implementation, verification, and review.",
|
|
2503
|
+
"- mamba: diagnose bugs and validate real user flows or regressions.",
|
|
2327
2504
|
"- manteam: read team memory and write handoff-friendly summaries.",
|
|
2328
2505
|
"- manps: prefer `mancode manps [area]` before cleanup.",
|
|
2329
2506
|
"- mansolo: exit any active mode and return to default solo behavior.",
|
|
@@ -2333,7 +2510,7 @@ function renderCopilotPromptConventions() {
|
|
|
2333
2510
|
}
|
|
2334
2511
|
|
|
2335
2512
|
// src/installers/cursor.ts
|
|
2336
|
-
import { mkdir as mkdir6, rm as rm3, writeFile as writeFile7 } from "fs/promises";
|
|
2513
|
+
import { mkdir as mkdir6, readFile as readFile6, rm as rm3, writeFile as writeFile7 } from "fs/promises";
|
|
2337
2514
|
import path8 from "path";
|
|
2338
2515
|
var MANCODE_CURSOR_CORE_RULE_FILES = [
|
|
2339
2516
|
"mancode-context.mdc",
|
|
@@ -2341,11 +2518,12 @@ var MANCODE_CURSOR_CORE_RULE_FILES = [
|
|
|
2341
2518
|
"mancode-solo.mdc"
|
|
2342
2519
|
];
|
|
2343
2520
|
var MANCODE_CURSOR_ADVANCED_RULE_FILES = [
|
|
2344
|
-
"mancode-
|
|
2521
|
+
"mancode-mamba.mdc",
|
|
2345
2522
|
"mancode-man.mdc",
|
|
2346
2523
|
"mancode-manteam.mdc",
|
|
2347
2524
|
"mancode-manps.mdc"
|
|
2348
2525
|
];
|
|
2526
|
+
var CURSOR_RULE_MANAGED_MARKER = "<!-- Managed by mancode:cursor-rule. Do not edit this marker. -->";
|
|
2349
2527
|
var MANCODE_CURSOR_RULE_FILES = [
|
|
2350
2528
|
...MANCODE_CURSOR_CORE_RULE_FILES,
|
|
2351
2529
|
...MANCODE_CURSOR_ADVANCED_RULE_FILES
|
|
@@ -2367,7 +2545,8 @@ async function installCursor(projectRoot, options) {
|
|
|
2367
2545
|
// the mode-specific Cursor rules below.
|
|
2368
2546
|
minimal: true,
|
|
2369
2547
|
techStack: options.techStack,
|
|
2370
|
-
uiLibrary: options.uiLibrary
|
|
2548
|
+
uiLibrary: options.uiLibrary,
|
|
2549
|
+
projectProfile: options.projectProfile
|
|
2371
2550
|
});
|
|
2372
2551
|
await writeRule(
|
|
2373
2552
|
rulesDir,
|
|
@@ -2395,12 +2574,13 @@ async function installCursor(projectRoot, options) {
|
|
|
2395
2574
|
await installCursorCommands(projectRoot, true);
|
|
2396
2575
|
return;
|
|
2397
2576
|
}
|
|
2577
|
+
await removeLegacyCursorRules(projectRoot);
|
|
2398
2578
|
await writeRule(
|
|
2399
2579
|
rulesDir,
|
|
2400
|
-
"mancode-
|
|
2401
|
-
"Use
|
|
2580
|
+
"mancode-mamba.mdc",
|
|
2581
|
+
"Use for bug diagnosis and real regression testing",
|
|
2402
2582
|
false,
|
|
2403
|
-
|
|
2583
|
+
renderMambaRule()
|
|
2404
2584
|
);
|
|
2405
2585
|
await writeRule(
|
|
2406
2586
|
rulesDir,
|
|
@@ -2439,11 +2619,77 @@ async function writeRule(rulesDir, fileName, description, alwaysApply, body) {
|
|
|
2439
2619
|
|
|
2440
2620
|
${body.trim()}
|
|
2441
2621
|
`;
|
|
2442
|
-
|
|
2622
|
+
const rulePath = path8.join(rulesDir, fileName);
|
|
2623
|
+
const existing = await readTextIfExists4(rulePath);
|
|
2624
|
+
if (existing !== null && !isGeneratedCursorRule(existing, fileName)) {
|
|
2625
|
+
throw new Error(
|
|
2626
|
+
`refusing to overwrite user-authored Cursor rule: ${rulePath}`
|
|
2627
|
+
);
|
|
2628
|
+
}
|
|
2629
|
+
await writeFile7(
|
|
2630
|
+
rulePath,
|
|
2631
|
+
content.replace("---\n\n", `---
|
|
2632
|
+
|
|
2633
|
+
${CURSOR_RULE_MANAGED_MARKER}
|
|
2634
|
+
|
|
2635
|
+
`),
|
|
2636
|
+
"utf-8"
|
|
2637
|
+
);
|
|
2443
2638
|
}
|
|
2444
2639
|
async function removeAdvancedRules(rulesDir) {
|
|
2445
2640
|
for (const fileName of MANCODE_CURSOR_ADVANCED_RULE_FILES) {
|
|
2446
|
-
await
|
|
2641
|
+
await removeGeneratedCursorRule(rulesDir, fileName);
|
|
2642
|
+
}
|
|
2643
|
+
await removeLegacyCursorRules(path8.dirname(path8.dirname(rulesDir)));
|
|
2644
|
+
}
|
|
2645
|
+
async function removeCursorGeneratedRules(projectRoot) {
|
|
2646
|
+
const rulesDir = path8.join(projectRoot, ".cursor", "rules");
|
|
2647
|
+
for (const fileName of MANCODE_CURSOR_RULE_FILES) {
|
|
2648
|
+
await removeGeneratedCursorRule(rulesDir, fileName);
|
|
2649
|
+
}
|
|
2650
|
+
await removeLegacyCursorRules(projectRoot);
|
|
2651
|
+
}
|
|
2652
|
+
async function removeLegacyCursorRules(projectRoot) {
|
|
2653
|
+
const legacyPath = path8.join(
|
|
2654
|
+
projectRoot,
|
|
2655
|
+
".cursor",
|
|
2656
|
+
"rules",
|
|
2657
|
+
"mancode-man8.mdc"
|
|
2658
|
+
);
|
|
2659
|
+
try {
|
|
2660
|
+
const content = await readFile6(legacyPath, "utf-8");
|
|
2661
|
+
if (content.includes("# mancode man8 \u2014 Investigate and Plan") && content.includes("## Mode Persistence")) {
|
|
2662
|
+
await rm3(legacyPath, { force: true });
|
|
2663
|
+
}
|
|
2664
|
+
} catch {
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
async function removeGeneratedCursorRule(rulesDir, fileName) {
|
|
2668
|
+
const rulePath = path8.join(rulesDir, fileName);
|
|
2669
|
+
const content = await readTextIfExists4(rulePath);
|
|
2670
|
+
if (content && isGeneratedCursorRule(content, fileName)) {
|
|
2671
|
+
await rm3(rulePath, { force: true });
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
function isGeneratedCursorRule(content, fileName) {
|
|
2675
|
+
if (content.includes(CURSOR_RULE_MANAGED_MARKER)) return true;
|
|
2676
|
+
if (fileName === "mancode-context.mdc") {
|
|
2677
|
+
return content.includes("# mancode Configuration") && content.includes("Platform adapter: Cursor");
|
|
2678
|
+
}
|
|
2679
|
+
if (fileName === "mancode-practice.mdc") {
|
|
2680
|
+
return content.includes("# mancode Practice") && content.includes("YAGNI ladder");
|
|
2681
|
+
}
|
|
2682
|
+
if (fileName === "mancode-solo.mdc") {
|
|
2683
|
+
return content.includes("# mancode solo") && content.includes("Use solo mode");
|
|
2684
|
+
}
|
|
2685
|
+
const mode = fileName.replace(/^mancode-/, "").replace(/\.mdc$/, "");
|
|
2686
|
+
return content.includes(`# mancode ${mode} \u2014`) && content.includes("## Mode Persistence");
|
|
2687
|
+
}
|
|
2688
|
+
async function readTextIfExists4(filePath) {
|
|
2689
|
+
try {
|
|
2690
|
+
return await readFile6(filePath, "utf-8");
|
|
2691
|
+
} catch {
|
|
2692
|
+
return null;
|
|
2447
2693
|
}
|
|
2448
2694
|
}
|
|
2449
2695
|
function renderPracticeRule() {
|
|
@@ -2470,12 +2716,12 @@ function renderSoloRule() {
|
|
|
2470
2716
|
"",
|
|
2471
2717
|
"- Keep the diff narrow.",
|
|
2472
2718
|
"- Reuse existing functions, components, styles, and dependencies.",
|
|
2473
|
-
"- For UI work, read `.mancode/aesthetics/style-tokens.json` and inspect existing components before inventing new styles.",
|
|
2719
|
+
"- Read `.mancode/project-profile.json` before choosing tools or validation. For detected UI assets and UI work, read `.mancode/aesthetics/style-tokens.json` and inspect existing components before inventing new styles.",
|
|
2474
2720
|
"- Verify with the narrowest meaningful test, lint, build, or smoke check."
|
|
2475
2721
|
].join("\n");
|
|
2476
2722
|
}
|
|
2477
|
-
function
|
|
2478
|
-
return renderModeSkill("
|
|
2723
|
+
function renderMambaRule() {
|
|
2724
|
+
return renderModeSkill("mamba", "/");
|
|
2479
2725
|
}
|
|
2480
2726
|
function renderManRule() {
|
|
2481
2727
|
return renderModeSkill("man", "/");
|
|
@@ -2507,7 +2753,8 @@ async function installZcode(projectRoot, options) {
|
|
|
2507
2753
|
},
|
|
2508
2754
|
minimal: options.minimal,
|
|
2509
2755
|
techStack: options.techStack,
|
|
2510
|
-
uiLibrary: options.uiLibrary
|
|
2756
|
+
uiLibrary: options.uiLibrary,
|
|
2757
|
+
projectProfile: options.projectProfile
|
|
2511
2758
|
});
|
|
2512
2759
|
const block = [
|
|
2513
2760
|
ZCODE_MANCODE_START_MARKER,
|
|
@@ -2557,7 +2804,7 @@ var PLATFORM_INSTALLERS = {
|
|
|
2557
2804
|
},
|
|
2558
2805
|
codex: {
|
|
2559
2806
|
name: "codex",
|
|
2560
|
-
displayName: "Codex CLI",
|
|
2807
|
+
displayName: "Codex (ChatGPT desktop/CLI)",
|
|
2561
2808
|
capabilities: {
|
|
2562
2809
|
slashCommands: "partial",
|
|
2563
2810
|
subagents: false,
|
|
@@ -2689,8 +2936,6 @@ async function checkRemote(cwd) {
|
|
|
2689
2936
|
|
|
2690
2937
|
// src/system/detect.ts
|
|
2691
2938
|
import { exec as exec2 } from "child_process";
|
|
2692
|
-
import { access as access2, readFile as readFile5 } from "fs/promises";
|
|
2693
|
-
import path11 from "path";
|
|
2694
2939
|
import { promisify as promisify2 } from "util";
|
|
2695
2940
|
var execAsync2 = promisify2(exec2);
|
|
2696
2941
|
async function detectSystemDeps() {
|
|
@@ -2714,62 +2959,271 @@ async function detectSystemDeps() {
|
|
|
2714
2959
|
]);
|
|
2715
2960
|
return { bash, git, jq, node };
|
|
2716
2961
|
}
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2962
|
+
|
|
2963
|
+
// src/system/project-profile.ts
|
|
2964
|
+
import { readFile as readFile7, readdir as readdir3, stat as stat2 } from "fs/promises";
|
|
2965
|
+
import path11 from "path";
|
|
2966
|
+
var PROJECT_MANIFESTS = [
|
|
2967
|
+
"package.json",
|
|
2968
|
+
"pyproject.toml",
|
|
2969
|
+
"requirements.txt",
|
|
2970
|
+
"go.mod",
|
|
2971
|
+
"Cargo.toml",
|
|
2972
|
+
"pom.xml",
|
|
2973
|
+
"build.gradle",
|
|
2974
|
+
"build.gradle.kts",
|
|
2975
|
+
"Package.swift",
|
|
2976
|
+
"pubspec.yaml"
|
|
2977
|
+
];
|
|
2978
|
+
async function detectProjectProfile(projectRoot) {
|
|
2979
|
+
const entries = await readdir3(projectRoot).catch(() => []);
|
|
2980
|
+
const manifests = PROJECT_MANIFESTS.filter((file) => entries.includes(file));
|
|
2981
|
+
const sourceRoots = await existingDirs(projectRoot, [
|
|
2982
|
+
"src",
|
|
2983
|
+
"app",
|
|
2984
|
+
"apps",
|
|
2985
|
+
"packages",
|
|
2986
|
+
"web",
|
|
2987
|
+
"lib",
|
|
2988
|
+
"Sources",
|
|
2989
|
+
"cmd",
|
|
2990
|
+
"server",
|
|
2991
|
+
"backend",
|
|
2992
|
+
"mobile",
|
|
2993
|
+
"ios",
|
|
2994
|
+
"android",
|
|
2995
|
+
"data",
|
|
2996
|
+
"notebooks"
|
|
2997
|
+
]);
|
|
2998
|
+
const packageJson = await readJson2(path11.join(projectRoot, "package.json"));
|
|
2999
|
+
const [pubspec, pythonManifests] = await Promise.all([
|
|
3000
|
+
readText(path11.join(projectRoot, "pubspec.yaml")),
|
|
3001
|
+
Promise.all([
|
|
3002
|
+
readText(path11.join(projectRoot, "pyproject.toml")),
|
|
3003
|
+
readText(path11.join(projectRoot, "requirements.txt"))
|
|
3004
|
+
]).then((parts) => parts.filter(Boolean).join("\n"))
|
|
3005
|
+
]);
|
|
3006
|
+
const deps = Object.keys({
|
|
3007
|
+
...packageJson?.dependencies,
|
|
3008
|
+
...packageJson?.devDependencies,
|
|
3009
|
+
...packageJson?.peerDependencies
|
|
3010
|
+
});
|
|
3011
|
+
const flutter = isFlutterProject(pubspec);
|
|
3012
|
+
const shadcn = entries.includes("components.json") || deps.some((dep) => dep.startsWith("@radix-ui/")) && await isDirectory(path11.join(projectRoot, "src", "components", "ui"));
|
|
3013
|
+
const languages = inferLanguages(manifests, entries);
|
|
3014
|
+
const frameworks = inferFrameworks(
|
|
3015
|
+
deps,
|
|
3016
|
+
manifests,
|
|
3017
|
+
pythonManifests,
|
|
3018
|
+
flutter,
|
|
3019
|
+
shadcn
|
|
3020
|
+
);
|
|
3021
|
+
const uiAssets = hasUiAssets(deps, entries, flutter) ? "detected" : "none";
|
|
3022
|
+
const projectKind = inferKind(
|
|
3023
|
+
deps,
|
|
3024
|
+
manifests,
|
|
3025
|
+
entries,
|
|
3026
|
+
packageJson,
|
|
3027
|
+
uiAssets,
|
|
3028
|
+
flutter,
|
|
3029
|
+
pythonManifests
|
|
3030
|
+
);
|
|
3031
|
+
const availableValidation = inferValidation(
|
|
3032
|
+
packageJson,
|
|
3033
|
+
manifests,
|
|
3034
|
+
entries,
|
|
3035
|
+
flutter,
|
|
3036
|
+
pythonManifests
|
|
3037
|
+
);
|
|
3038
|
+
return {
|
|
3039
|
+
version: "1.0",
|
|
3040
|
+
projectKind,
|
|
3041
|
+
languages,
|
|
3042
|
+
frameworks,
|
|
3043
|
+
sourceRoots,
|
|
3044
|
+
manifests,
|
|
3045
|
+
availableValidation,
|
|
3046
|
+
uiAssets,
|
|
3047
|
+
browserAutomation: deps.some((dep) => dep.includes("playwright")) || entries.some((entry) => /^playwright\.config\./.test(entry)) ? "available" : projectKind === "web" || projectKind === "mixed" ? "unknown" : "unavailable",
|
|
3048
|
+
confidence: projectKind === "unknown" ? "low" : manifests.length > 0 ? "high" : "medium"
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
async function existingDirs(root, candidates) {
|
|
3052
|
+
const found = [];
|
|
3053
|
+
for (const candidate of candidates) {
|
|
3054
|
+
try {
|
|
3055
|
+
if ((await stat2(path11.join(root, candidate))).isDirectory())
|
|
3056
|
+
found.push(candidate);
|
|
3057
|
+
} catch {
|
|
2729
3058
|
}
|
|
2730
|
-
throw err;
|
|
2731
3059
|
}
|
|
2732
|
-
|
|
2733
|
-
const pkg = JSON.parse(raw);
|
|
2734
|
-
const allDeps = {
|
|
2735
|
-
...pkg.dependencies,
|
|
2736
|
-
...pkg.devDependencies,
|
|
2737
|
-
...pkg.peerDependencies
|
|
2738
|
-
};
|
|
2739
|
-
const deps = Object.keys(allDeps || {});
|
|
2740
|
-
const hasFrontend = deps.some((d) => ["react", "vue", "svelte", "angular"].includes(d)) || deps.some((d) => d.startsWith("@angular/"));
|
|
2741
|
-
const hasBackend = deps.some(
|
|
2742
|
-
(d) => ["express", "koa", "fastify", "hapi", "next"].includes(d)
|
|
2743
|
-
) || deps.some((d) => d.startsWith("@nestjs/"));
|
|
2744
|
-
const techStack = [];
|
|
2745
|
-
if (deps.includes("react")) techStack.push("React");
|
|
2746
|
-
if (deps.includes("vue")) techStack.push("Vue");
|
|
2747
|
-
if (deps.includes("svelte")) techStack.push("Svelte");
|
|
2748
|
-
if (deps.includes("typescript")) techStack.push("TypeScript");
|
|
2749
|
-
if (deps.includes("tailwindcss")) techStack.push("Tailwind CSS");
|
|
2750
|
-
let uiLibrary = null;
|
|
2751
|
-
const hasRadix = deps.some((d) => d.startsWith("@radix-ui/"));
|
|
2752
|
-
const uiDir = path11.join(projectRoot, "src", "components", "ui");
|
|
2753
|
-
const hasUiDir = await pathExists2(uiDir);
|
|
2754
|
-
if (hasRadix && hasUiDir) {
|
|
2755
|
-
uiLibrary = "shadcn/ui";
|
|
2756
|
-
} else if (deps.includes("@mui/material")) {
|
|
2757
|
-
uiLibrary = "MUI";
|
|
2758
|
-
} else if (deps.includes("antd")) {
|
|
2759
|
-
uiLibrary = "Ant Design";
|
|
2760
|
-
} else if (deps.includes("@headlessui/react")) {
|
|
2761
|
-
uiLibrary = "headlessUI";
|
|
2762
|
-
}
|
|
2763
|
-
return { hasFrontend, hasBackend, techStack, uiLibrary };
|
|
3060
|
+
return found;
|
|
2764
3061
|
}
|
|
2765
|
-
async function
|
|
3062
|
+
async function isDirectory(candidate) {
|
|
2766
3063
|
try {
|
|
2767
|
-
await
|
|
2768
|
-
return true;
|
|
3064
|
+
return (await stat2(candidate)).isDirectory();
|
|
2769
3065
|
} catch {
|
|
2770
3066
|
return false;
|
|
2771
3067
|
}
|
|
2772
3068
|
}
|
|
3069
|
+
async function readJson2(file) {
|
|
3070
|
+
try {
|
|
3071
|
+
return JSON.parse(await readFile7(file, "utf-8"));
|
|
3072
|
+
} catch {
|
|
3073
|
+
return null;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
async function readText(file) {
|
|
3077
|
+
try {
|
|
3078
|
+
return await readFile7(file, "utf-8");
|
|
3079
|
+
} catch {
|
|
3080
|
+
return "";
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
function inferLanguages(manifests, entries) {
|
|
3084
|
+
const languages = [];
|
|
3085
|
+
if (manifests.includes("package.json"))
|
|
3086
|
+
languages.push("JavaScript/TypeScript");
|
|
3087
|
+
if (manifests.includes("pyproject.toml") || manifests.includes("requirements.txt"))
|
|
3088
|
+
languages.push("Python");
|
|
3089
|
+
if (manifests.includes("go.mod")) languages.push("Go");
|
|
3090
|
+
if (manifests.includes("Cargo.toml")) languages.push("Rust");
|
|
3091
|
+
if (manifests.includes("pom.xml") || manifests.includes("build.gradle") || manifests.includes("build.gradle.kts"))
|
|
3092
|
+
languages.push("JVM");
|
|
3093
|
+
if (manifests.includes("Package.swift")) languages.push("Swift");
|
|
3094
|
+
if (manifests.includes("pubspec.yaml")) languages.push("Dart");
|
|
3095
|
+
if (entries.includes("Dockerfile") && languages.length === 0)
|
|
3096
|
+
languages.push("Containerized");
|
|
3097
|
+
return languages;
|
|
3098
|
+
}
|
|
3099
|
+
function inferFrameworks(deps, manifests, pythonManifests, flutter, shadcn) {
|
|
3100
|
+
const result = [];
|
|
3101
|
+
for (const [dep, name] of [
|
|
3102
|
+
["react", "React"],
|
|
3103
|
+
["vue", "Vue"],
|
|
3104
|
+
["svelte", "Svelte"],
|
|
3105
|
+
["next", "Next.js"],
|
|
3106
|
+
["nuxt", "Nuxt"],
|
|
3107
|
+
["astro", "Astro"],
|
|
3108
|
+
["solid-js", "SolidJS"],
|
|
3109
|
+
["preact", "Preact"],
|
|
3110
|
+
["@angular/core", "Angular"],
|
|
3111
|
+
["express", "Express"],
|
|
3112
|
+
["fastify", "Fastify"],
|
|
3113
|
+
["electron", "Electron"],
|
|
3114
|
+
["@tauri-apps/api", "Tauri"],
|
|
3115
|
+
["tailwindcss", "Tailwind CSS"],
|
|
3116
|
+
["@mui/material", "MUI"],
|
|
3117
|
+
["antd", "Ant Design"],
|
|
3118
|
+
["@headlessui/react", "Headless UI"]
|
|
3119
|
+
]) {
|
|
3120
|
+
if (deps.includes(dep)) result.push(name);
|
|
3121
|
+
}
|
|
3122
|
+
if (manifests.includes("go.mod")) result.push("Go modules");
|
|
3123
|
+
if (manifests.includes("Cargo.toml")) result.push("Cargo");
|
|
3124
|
+
if (flutter) result.push("Flutter");
|
|
3125
|
+
if (shadcn) result.push("shadcn/ui");
|
|
3126
|
+
else if (deps.some((dep) => dep.startsWith("@radix-ui/")))
|
|
3127
|
+
result.push("Radix UI");
|
|
3128
|
+
for (const [pattern, name] of [
|
|
3129
|
+
[/\bdjango\b/i, "Django"],
|
|
3130
|
+
[/\bfastapi\b/i, "FastAPI"],
|
|
3131
|
+
[/\bflask\b/i, "Flask"]
|
|
3132
|
+
]) {
|
|
3133
|
+
if (pattern.test(pythonManifests)) result.push(name);
|
|
3134
|
+
}
|
|
3135
|
+
return result;
|
|
3136
|
+
}
|
|
3137
|
+
function hasUiAssets(deps, entries, flutter) {
|
|
3138
|
+
return deps.some(
|
|
3139
|
+
(dep) => [
|
|
3140
|
+
"react",
|
|
3141
|
+
"vue",
|
|
3142
|
+
"svelte",
|
|
3143
|
+
"solid-js",
|
|
3144
|
+
"preact",
|
|
3145
|
+
"@angular/core",
|
|
3146
|
+
"tailwindcss",
|
|
3147
|
+
"@mui/material",
|
|
3148
|
+
"antd",
|
|
3149
|
+
"@headlessui/react",
|
|
3150
|
+
"electron",
|
|
3151
|
+
"@tauri-apps/api"
|
|
3152
|
+
].includes(dep) || dep.startsWith("@radix-ui/")
|
|
3153
|
+
) || entries.includes("index.html") || entries.some((entry) => /^tailwind\.config\./.test(entry)) || entries.includes("ios") || entries.includes("android") || flutter;
|
|
3154
|
+
}
|
|
3155
|
+
function primaryUiLibrary(profile) {
|
|
3156
|
+
const preferred = [
|
|
3157
|
+
"shadcn/ui",
|
|
3158
|
+
"MUI",
|
|
3159
|
+
"Ant Design",
|
|
3160
|
+
"Headless UI",
|
|
3161
|
+
"Radix UI",
|
|
3162
|
+
"Tailwind CSS",
|
|
3163
|
+
"Flutter"
|
|
3164
|
+
];
|
|
3165
|
+
return preferred.find((name) => profile.frameworks.includes(name)) ?? null;
|
|
3166
|
+
}
|
|
3167
|
+
function inferKind(deps, manifests, entries, packageJson, uiAssets, flutter, pythonManifests) {
|
|
3168
|
+
const hasBackendFramework = deps.includes("express") || deps.includes("fastify") || /\b(django|fastapi|flask)\b/i.test(pythonManifests);
|
|
3169
|
+
const hasBackendLayout = entries.includes("server") || entries.includes("backend");
|
|
3170
|
+
const hasPackageLibraryContract = manifests.includes("package.json") && ["main", "module", "exports", "types"].some(
|
|
3171
|
+
(field) => Object.hasOwn(packageJson ?? {}, field)
|
|
3172
|
+
);
|
|
3173
|
+
const hasCliSignal = entries.includes("bin") || entries.includes("cmd") || typeof packageJson?.bin === "string" || typeof packageJson?.bin === "object" && packageJson.bin !== null;
|
|
3174
|
+
const hasWebApplicationSignal = entries.some(
|
|
3175
|
+
(entry) => ["app", "pages", "public", "web", "index.html"].includes(entry)
|
|
3176
|
+
) || deps.some((dep) => ["next", "nuxt", "astro"].includes(dep));
|
|
3177
|
+
const hasNonWebManifest = manifests.some(
|
|
3178
|
+
(manifest) => [
|
|
3179
|
+
"pyproject.toml",
|
|
3180
|
+
"requirements.txt",
|
|
3181
|
+
"go.mod",
|
|
3182
|
+
"Cargo.toml",
|
|
3183
|
+
"pom.xml",
|
|
3184
|
+
"build.gradle",
|
|
3185
|
+
"build.gradle.kts"
|
|
3186
|
+
].includes(manifest)
|
|
3187
|
+
);
|
|
3188
|
+
if (entries.includes("ios") || entries.includes("android") || flutter)
|
|
3189
|
+
return hasBackendFramework || hasBackendLayout ? "mixed" : "mobile";
|
|
3190
|
+
if (deps.some((dep) => ["electron", "@tauri-apps/api"].includes(dep)))
|
|
3191
|
+
return hasBackendFramework || hasBackendLayout ? "mixed" : "desktop";
|
|
3192
|
+
if (hasCliSignal && !hasWebApplicationSignal)
|
|
3193
|
+
return hasBackendFramework || hasBackendLayout ? "mixed" : "cli";
|
|
3194
|
+
if (hasPackageLibraryContract && !hasWebApplicationSignal && !hasBackendFramework && !hasBackendLayout && !hasNonWebManifest)
|
|
3195
|
+
return "library";
|
|
3196
|
+
if (uiAssets === "detected")
|
|
3197
|
+
return hasBackendFramework || hasBackendLayout || hasNonWebManifest ? "mixed" : "web";
|
|
3198
|
+
if (entries.includes("data") || entries.includes("notebooks")) return "data";
|
|
3199
|
+
if (hasPackageLibraryContract) return "library";
|
|
3200
|
+
if (manifests.includes("pubspec.yaml") && entries.includes("lib"))
|
|
3201
|
+
return "library";
|
|
3202
|
+
if (hasBackendFramework || hasBackendLayout) return "backend";
|
|
3203
|
+
if (manifests.filter((manifest) => manifest !== "requirements.txt").length > 1)
|
|
3204
|
+
return "mixed";
|
|
3205
|
+
return "unknown";
|
|
3206
|
+
}
|
|
3207
|
+
function inferValidation(pkg, manifests, entries, flutter, pythonManifests) {
|
|
3208
|
+
const scripts = pkg?.scripts ?? {};
|
|
3209
|
+
const packageRunner = entries.includes("pnpm-lock.yaml") ? "pnpm" : entries.includes("yarn.lock") ? "yarn" : entries.includes("bun.lock") || entries.includes("bun.lockb") ? "bun" : "npm";
|
|
3210
|
+
return Array.from(
|
|
3211
|
+
new Set(
|
|
3212
|
+
["build", "lint", "test", "typecheck"].filter((name) => typeof scripts[name] === "string").map((name) => `${packageRunner} run ${name}`).concat(
|
|
3213
|
+
manifests.includes("go.mod") ? ["go test ./..."] : [],
|
|
3214
|
+
manifests.includes("Cargo.toml") ? ["cargo test"] : [],
|
|
3215
|
+
/\bpytest\b/i.test(pythonManifests) || entries.includes("pytest.ini") ? ["python -m pytest"] : [],
|
|
3216
|
+
manifests.includes("pom.xml") ? [entries.includes("mvnw") ? "./mvnw test" : "mvn test"] : [],
|
|
3217
|
+
manifests.includes("build.gradle") || manifests.includes("build.gradle.kts") ? [entries.includes("gradlew") ? "./gradlew test" : "gradle test"] : [],
|
|
3218
|
+
manifests.includes("Package.swift") ? ["swift test"] : [],
|
|
3219
|
+
manifests.includes("pubspec.yaml") ? [flutter ? "flutter test" : "dart test"] : []
|
|
3220
|
+
)
|
|
3221
|
+
)
|
|
3222
|
+
);
|
|
3223
|
+
}
|
|
3224
|
+
function isFlutterProject(pubspec) {
|
|
3225
|
+
return /(^|\n)\s*sdk\s*:\s*flutter\s*($|\n)/m.test(pubspec);
|
|
3226
|
+
}
|
|
2773
3227
|
|
|
2774
3228
|
// src/system/scan-aesthetics.ts
|
|
2775
3229
|
import { promises as fs } from "fs";
|
|
@@ -2787,7 +3241,8 @@ async function scanAesthetics(projectRoot, uiLibrary = null) {
|
|
|
2787
3241
|
if (configResult) {
|
|
2788
3242
|
sourceFiles.push(configResult.relPath);
|
|
2789
3243
|
const content = await fs.readFile(configResult.absPath, "utf-8");
|
|
2790
|
-
const
|
|
3244
|
+
const inspectableContent = stripJsComments(content);
|
|
3245
|
+
const themeBlock = findKeyBlock(inspectableContent, "theme");
|
|
2791
3246
|
if (themeBlock) {
|
|
2792
3247
|
const extendBlock = findKeyBlock(themeBlock, "extend");
|
|
2793
3248
|
const searchBlock = extendBlock ?? themeBlock;
|
|
@@ -2800,15 +3255,15 @@ async function scanAesthetics(projectRoot, uiLibrary = null) {
|
|
|
2800
3255
|
fonts = extractTopLevelArrayValues(fontsBlock);
|
|
2801
3256
|
}
|
|
2802
3257
|
}
|
|
2803
|
-
darkMode = extractDarkMode(
|
|
3258
|
+
darkMode = extractDarkMode(inspectableContent);
|
|
2804
3259
|
}
|
|
2805
3260
|
let matchLevel = "none";
|
|
2806
|
-
if (configResult) {
|
|
3261
|
+
if (configResult || components.length > 0 || Object.keys(cssScan.variables).length > 0) {
|
|
2807
3262
|
matchLevel = "high";
|
|
2808
|
-
} else if (await hasTailwindDep(projectRoot)) {
|
|
3263
|
+
} else if (await hasTailwindDep(projectRoot) || uiLibrary) {
|
|
2809
3264
|
matchLevel = "low";
|
|
2810
3265
|
}
|
|
2811
|
-
if (uiLibrary) {
|
|
3266
|
+
if (uiLibrary && await pathExists2(path12.join(projectRoot, "package.json"))) {
|
|
2812
3267
|
sourceFiles.push("package.json");
|
|
2813
3268
|
}
|
|
2814
3269
|
sourceFiles.push(...cssScan.sourceFiles);
|
|
@@ -2834,7 +3289,7 @@ async function findTailwindConfig(projectRoot) {
|
|
|
2834
3289
|
];
|
|
2835
3290
|
for (const name of candidates) {
|
|
2836
3291
|
const absPath = path12.join(projectRoot, name);
|
|
2837
|
-
if (await
|
|
3292
|
+
if (await pathExists2(absPath)) {
|
|
2838
3293
|
return { absPath, relPath: name };
|
|
2839
3294
|
}
|
|
2840
3295
|
}
|
|
@@ -3049,12 +3504,19 @@ function stripJsComments(content) {
|
|
|
3049
3504
|
return result;
|
|
3050
3505
|
}
|
|
3051
3506
|
async function scanComponents(projectRoot) {
|
|
3052
|
-
const roots = [
|
|
3507
|
+
const roots = [
|
|
3508
|
+
"src/components",
|
|
3509
|
+
"components",
|
|
3510
|
+
"app/components",
|
|
3511
|
+
"lib/widgets",
|
|
3512
|
+
"lib/screens",
|
|
3513
|
+
"Sources"
|
|
3514
|
+
];
|
|
3053
3515
|
const names = /* @__PURE__ */ new Set();
|
|
3054
3516
|
let visitedFiles = 0;
|
|
3055
3517
|
for (const relRoot of roots) {
|
|
3056
3518
|
const absRoot = path12.join(projectRoot, relRoot);
|
|
3057
|
-
if (!await
|
|
3519
|
+
if (!await pathExists2(absRoot)) continue;
|
|
3058
3520
|
visitedFiles = await collectComponentNames(absRoot, names, 0, visitedFiles);
|
|
3059
3521
|
if (visitedFiles >= MAX_COMPONENT_FILES) break;
|
|
3060
3522
|
}
|
|
@@ -3085,10 +3547,14 @@ async function collectComponentNames(dir, names, depth, visitedFiles) {
|
|
|
3085
3547
|
fileCount = await collectComponentNames(abs, names, depth + 1, fileCount);
|
|
3086
3548
|
continue;
|
|
3087
3549
|
}
|
|
3088
|
-
if (!/\.(tsx|jsx|ts|js|vue|svelte)$/.test(entry))
|
|
3550
|
+
if (!/\.(tsx|jsx|ts|js|vue|svelte|dart|swift|kt|kts)$/.test(entry))
|
|
3551
|
+
continue;
|
|
3089
3552
|
fileCount++;
|
|
3090
3553
|
if (isNonComponentFile(entry)) continue;
|
|
3091
|
-
let base = entry.replace(
|
|
3554
|
+
let base = entry.replace(
|
|
3555
|
+
/\.(tsx|jsx|ts|js|vue|svelte|dart|swift|kt|kts)$/,
|
|
3556
|
+
""
|
|
3557
|
+
);
|
|
3092
3558
|
if (base === "index") {
|
|
3093
3559
|
base = path12.basename(dir);
|
|
3094
3560
|
}
|
|
@@ -3113,7 +3579,7 @@ async function scanCssVariables(projectRoot) {
|
|
|
3113
3579
|
const sourceFiles = [];
|
|
3114
3580
|
for (const relPath of candidates) {
|
|
3115
3581
|
const absPath = path12.join(projectRoot, relPath);
|
|
3116
|
-
if (!await
|
|
3582
|
+
if (!await pathExists2(absPath)) continue;
|
|
3117
3583
|
const content = await fs.readFile(absPath, "utf-8");
|
|
3118
3584
|
const found = extractCssVariables(content);
|
|
3119
3585
|
if (Object.keys(found).length === 0) continue;
|
|
@@ -3172,7 +3638,7 @@ async function hasTailwindDep(projectRoot) {
|
|
|
3172
3638
|
function escapeRegex(s) {
|
|
3173
3639
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3174
3640
|
}
|
|
3175
|
-
async function
|
|
3641
|
+
async function pathExists2(p) {
|
|
3176
3642
|
try {
|
|
3177
3643
|
await fs.access(p);
|
|
3178
3644
|
return true;
|
|
@@ -3190,7 +3656,8 @@ var DEFAULT_INIT_PLATFORM = "claude-code";
|
|
|
3190
3656
|
async function init(rootDir = process3.cwd(), options = {}) {
|
|
3191
3657
|
const mancodeDir = path13.join(rootDir, ".mancode");
|
|
3192
3658
|
const stateFile = path13.join(mancodeDir, "state.json");
|
|
3193
|
-
const wasInitialized = await
|
|
3659
|
+
const wasInitialized = await pathExists3(stateFile);
|
|
3660
|
+
let reinstallSnapshots = [];
|
|
3194
3661
|
if (wasInitialized) {
|
|
3195
3662
|
if (!options.force) {
|
|
3196
3663
|
console.log("\u2139\uFE0F mancode already initialized.");
|
|
@@ -3198,20 +3665,37 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3198
3665
|
console.log(" Run `mancode init --force` to reinstall.");
|
|
3199
3666
|
return EXIT_ALREADY_INITIALIZED;
|
|
3200
3667
|
}
|
|
3668
|
+
try {
|
|
3669
|
+
reinstallSnapshots = await snapshotFiles([
|
|
3670
|
+
stateFile,
|
|
3671
|
+
path13.join(mancodeDir, "config.json"),
|
|
3672
|
+
path13.join(mancodeDir, "project-profile.json"),
|
|
3673
|
+
path13.join(mancodeDir, "aesthetics", "style-tokens.json")
|
|
3674
|
+
]);
|
|
3675
|
+
} catch (error) {
|
|
3676
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3677
|
+
console.error(`\u2717 Cannot snapshot existing mancode state: ${message}`);
|
|
3678
|
+
return EXIT_INIT_FAILED;
|
|
3679
|
+
}
|
|
3201
3680
|
console.log("\u26A0\uFE0F Reinstalling with --force...");
|
|
3202
3681
|
}
|
|
3203
|
-
if (!await
|
|
3682
|
+
if (!await pathExists3(rootDir)) {
|
|
3204
3683
|
console.error(`\u2717 Target directory does not exist: ${rootDir}`);
|
|
3205
3684
|
return EXIT_NOT_A_PROJECT_DIR;
|
|
3206
3685
|
}
|
|
3207
|
-
const isGitRepo = await
|
|
3208
|
-
const
|
|
3209
|
-
|
|
3686
|
+
const isGitRepo = await pathExists3(path13.join(rootDir, ".git"));
|
|
3687
|
+
const hasManifest = await Promise.any(
|
|
3688
|
+
PROJECT_MANIFESTS.map(async (name) => {
|
|
3689
|
+
if (await pathExists3(path13.join(rootDir, name))) return true;
|
|
3690
|
+
throw new Error("manifest missing");
|
|
3691
|
+
})
|
|
3692
|
+
).catch(() => false);
|
|
3693
|
+
if (!isGitRepo && !hasManifest) {
|
|
3210
3694
|
console.error(`\u2717 Not a project directory: ${rootDir}`);
|
|
3211
|
-
console.error(" (No .git or
|
|
3695
|
+
console.error(" (No .git or recognized project manifest found)");
|
|
3212
3696
|
console.error("");
|
|
3213
3697
|
console.error(
|
|
3214
|
-
" Run mancode init in a git repository or
|
|
3698
|
+
" Run mancode init in a git repository or a recognized project directory."
|
|
3215
3699
|
);
|
|
3216
3700
|
return EXIT_NOT_A_PROJECT_DIR;
|
|
3217
3701
|
}
|
|
@@ -3234,27 +3718,34 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3234
3718
|
console.log(" bash \u2713 git \u2713 jq \u2713");
|
|
3235
3719
|
}
|
|
3236
3720
|
console.log("\u2713 \u68C0\u6D4B\u9879\u76EE\u7C7B\u578B...");
|
|
3237
|
-
const
|
|
3238
|
-
const
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3721
|
+
const profile = await detectProjectProfile(rootDir);
|
|
3722
|
+
const profileStack = [...profile.languages, ...profile.frameworks];
|
|
3723
|
+
const techStackStr = profileStack.join(" + ") || "Unknown";
|
|
3724
|
+
const uiLibrary = primaryUiLibrary(profile);
|
|
3725
|
+
const uiLibraryStr = uiLibrary || "None";
|
|
3726
|
+
if (profileStack.length > 0) {
|
|
3241
3727
|
console.log(` ${techStackStr}`);
|
|
3242
|
-
if (
|
|
3728
|
+
if (uiLibrary) {
|
|
3243
3729
|
console.log(` UI: ${uiLibraryStr}`);
|
|
3244
3730
|
}
|
|
3245
|
-
} else if (
|
|
3246
|
-
console.log("
|
|
3731
|
+
} else if (hasManifest) {
|
|
3732
|
+
console.log(" project manifest found, no known framework dependencies");
|
|
3247
3733
|
} else {
|
|
3248
|
-
console.log(
|
|
3734
|
+
console.log(
|
|
3735
|
+
" (No recognized manifest found, profile confidence is low)"
|
|
3736
|
+
);
|
|
3249
3737
|
}
|
|
3250
|
-
const
|
|
3738
|
+
const existingPlatform = wasInitialized ? await readExistingInitPlatform(stateFile) : null;
|
|
3739
|
+
const initPlatformName = options.platform ?? existingPlatform ?? DEFAULT_INIT_PLATFORM;
|
|
3251
3740
|
const installer = getPlatformInstaller(initPlatformName);
|
|
3252
3741
|
if (!installer) {
|
|
3253
3742
|
console.error(`\u2717 Unsupported platform: ${initPlatformName}`);
|
|
3254
3743
|
return EXIT_INIT_FAILED;
|
|
3255
3744
|
}
|
|
3256
3745
|
const team = await detectTeamStatus(rootDir);
|
|
3257
|
-
const
|
|
3746
|
+
const existingPreferences = wasInitialized ? await readExistingInitPreferences(mancodeDir, installer.name) : {};
|
|
3747
|
+
const initialMinimal = existingPreferences.minimal ?? false;
|
|
3748
|
+
const teamModeEnabled = options.team ?? existingPreferences.forceTeamMode ?? team.isTeam;
|
|
3258
3749
|
if (options.team === true) {
|
|
3259
3750
|
console.log(" team: forced on (--team)");
|
|
3260
3751
|
} else if (options.team === false) {
|
|
@@ -3286,18 +3777,28 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3286
3777
|
const stateContent = `${JSON.stringify(state, null, 2)}
|
|
3287
3778
|
`;
|
|
3288
3779
|
await fs2.writeFile(stateFile, stateContent, "utf-8");
|
|
3289
|
-
await
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3780
|
+
await fs2.writeFile(
|
|
3781
|
+
path13.join(mancodeDir, "project-profile.json"),
|
|
3782
|
+
`${JSON.stringify(profile, null, 2)}
|
|
3783
|
+
`,
|
|
3784
|
+
"utf-8"
|
|
3785
|
+
);
|
|
3786
|
+
await updateConfigOptions(
|
|
3787
|
+
mancodeDir,
|
|
3788
|
+
{
|
|
3789
|
+
forceTeamMode: options.team === void 0 && wasInitialized ? void 0 : options.team === true,
|
|
3790
|
+
defaultStyle: options.style === void 0 && wasInitialized ? void 0 : options.style ?? null,
|
|
3791
|
+
platforms: [installer.name],
|
|
3792
|
+
platformOptions: {
|
|
3793
|
+
[installer.name]: { minimal: initialMinimal }
|
|
3794
|
+
}
|
|
3795
|
+
},
|
|
3796
|
+
wasInitialized
|
|
3797
|
+
);
|
|
3297
3798
|
let styleLine = " .mancode/aesthetics/ # style-tokens.json (\u7A7A)";
|
|
3298
|
-
if (
|
|
3799
|
+
if (profile.uiAssets === "detected") {
|
|
3299
3800
|
console.log("\u2713 \u626B\u63CF\u5BA1\u7F8E token...");
|
|
3300
|
-
const tokens = await scanAesthetics(rootDir,
|
|
3801
|
+
const tokens = await scanAesthetics(rootDir, uiLibrary);
|
|
3301
3802
|
const tokensPath = path13.join(
|
|
3302
3803
|
mancodeDir,
|
|
3303
3804
|
"aesthetics",
|
|
@@ -3317,7 +3818,9 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3317
3818
|
);
|
|
3318
3819
|
styleLine = ` .mancode/aesthetics/ # style-tokens.json (${colorCount} colors)`;
|
|
3319
3820
|
} else if (tokens.matchLevel === "low") {
|
|
3320
|
-
console.log(
|
|
3821
|
+
console.log(
|
|
3822
|
+
" UI assets detected, no reusable tokens found (match: low)"
|
|
3823
|
+
);
|
|
3321
3824
|
styleLine = " .mancode/aesthetics/ # style-tokens.json (low match)";
|
|
3322
3825
|
} else {
|
|
3323
3826
|
console.log(" No design tokens found (match: none)");
|
|
@@ -3326,14 +3829,18 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3326
3829
|
}
|
|
3327
3830
|
console.log(`\u2713 \u5B89\u88C5 ${installer.displayName} \u9002\u914D...`);
|
|
3328
3831
|
await installer.install(rootDir, {
|
|
3329
|
-
techStack:
|
|
3330
|
-
uiLibrary
|
|
3832
|
+
techStack: profileStack,
|
|
3833
|
+
uiLibrary,
|
|
3834
|
+
projectProfile: profile,
|
|
3835
|
+
minimal: initialMinimal,
|
|
3836
|
+
force: options.force
|
|
3331
3837
|
});
|
|
3332
3838
|
console.log("");
|
|
3333
3839
|
console.log("\u2713 mancode initialized.");
|
|
3334
3840
|
console.log("");
|
|
3335
3841
|
console.log("Created:");
|
|
3336
3842
|
console.log(" .mancode/state.json # \u9879\u76EE\u72B6\u6001");
|
|
3843
|
+
console.log(" .mancode/project-profile.json # \u68C0\u6D4B\u5230\u7684\u9879\u76EE\u4E8B\u5B9E");
|
|
3337
3844
|
console.log(" .mancode/config.json # \u914D\u7F6E");
|
|
3338
3845
|
console.log(
|
|
3339
3846
|
" .mancode/hooks/ # SessionStart + UserPromptSubmit"
|
|
@@ -3346,31 +3853,115 @@ async function init(rootDir = process3.cwd(), options = {}) {
|
|
|
3346
3853
|
console.log(" mancode status # Show project state");
|
|
3347
3854
|
if (installer.name === "claude-code") {
|
|
3348
3855
|
console.log(" (Restart Claude Code to load hooks)");
|
|
3856
|
+
} else if (installer.name === "codex") {
|
|
3857
|
+
console.log(
|
|
3858
|
+
" (If skills do not appear, restart the ChatGPT desktop app or Codex session)"
|
|
3859
|
+
);
|
|
3349
3860
|
}
|
|
3350
3861
|
return EXIT_OK;
|
|
3351
3862
|
} catch (err) {
|
|
3352
|
-
if (
|
|
3863
|
+
if (wasInitialized) {
|
|
3864
|
+
await restoreFiles(reinstallSnapshots);
|
|
3865
|
+
} else {
|
|
3353
3866
|
await fs2.rm(stateFile, { force: true });
|
|
3867
|
+
await fs2.rm(path13.join(mancodeDir, "project-profile.json"), {
|
|
3868
|
+
force: true
|
|
3869
|
+
});
|
|
3354
3870
|
}
|
|
3355
3871
|
const msg = err instanceof Error ? err.message : String(err);
|
|
3356
3872
|
console.error(`\u2717 mancode init failed: ${msg}`);
|
|
3357
3873
|
return EXIT_INIT_FAILED;
|
|
3358
3874
|
}
|
|
3359
3875
|
}
|
|
3360
|
-
async function updateConfigOptions(mancodeDir, patch) {
|
|
3876
|
+
async function updateConfigOptions(mancodeDir, patch, preserveInstalledPlatforms) {
|
|
3361
3877
|
const configPath = path13.join(mancodeDir, "config.json");
|
|
3362
3878
|
let config = {};
|
|
3363
3879
|
try {
|
|
3364
3880
|
config = JSON.parse(await fs2.readFile(configPath, "utf-8"));
|
|
3365
3881
|
} catch {
|
|
3366
3882
|
}
|
|
3883
|
+
const existingPlatforms = Array.isArray(config.platforms) ? config.platforms.filter(
|
|
3884
|
+
(platform) => typeof platform === "string"
|
|
3885
|
+
) : [];
|
|
3886
|
+
const existingPlatformOptions = isRecord3(config.platformOptions) ? config.platformOptions : {};
|
|
3887
|
+
const definedPatch = Object.fromEntries(
|
|
3888
|
+
Object.entries(patch).filter(([, value]) => value !== void 0)
|
|
3889
|
+
);
|
|
3890
|
+
const mergedPatch = preserveInstalledPlatforms ? {
|
|
3891
|
+
...definedPatch,
|
|
3892
|
+
platforms: Array.from(
|
|
3893
|
+
/* @__PURE__ */ new Set([...existingPlatforms, ...patch.platforms])
|
|
3894
|
+
),
|
|
3895
|
+
platformOptions: {
|
|
3896
|
+
...existingPlatformOptions,
|
|
3897
|
+
...patch.platformOptions
|
|
3898
|
+
}
|
|
3899
|
+
} : definedPatch;
|
|
3367
3900
|
await fs2.writeFile(
|
|
3368
3901
|
configPath,
|
|
3369
|
-
`${JSON.stringify({ ...config, ...
|
|
3902
|
+
`${JSON.stringify({ ...config, ...mergedPatch }, null, 2)}
|
|
3370
3903
|
`,
|
|
3371
3904
|
"utf-8"
|
|
3372
3905
|
);
|
|
3373
3906
|
}
|
|
3907
|
+
async function readExistingInitPreferences(mancodeDir, platform) {
|
|
3908
|
+
try {
|
|
3909
|
+
const config = JSON.parse(
|
|
3910
|
+
await fs2.readFile(path13.join(mancodeDir, "config.json"), "utf-8")
|
|
3911
|
+
);
|
|
3912
|
+
const preferences = {};
|
|
3913
|
+
if (typeof config.forceTeamMode === "boolean") {
|
|
3914
|
+
preferences.forceTeamMode = config.forceTeamMode;
|
|
3915
|
+
}
|
|
3916
|
+
if (Array.isArray(config.platforms) && config.platforms.includes(platform) && isRecord3(config.platformOptions)) {
|
|
3917
|
+
const platformOptions = config.platformOptions[platform];
|
|
3918
|
+
if (isRecord3(platformOptions) && platformOptions.minimal === true) {
|
|
3919
|
+
preferences.minimal = true;
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
return preferences;
|
|
3923
|
+
} catch {
|
|
3924
|
+
return {};
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
async function readExistingInitPlatform(stateFile) {
|
|
3928
|
+
try {
|
|
3929
|
+
const state = JSON.parse(await fs2.readFile(stateFile, "utf-8"));
|
|
3930
|
+
return typeof state.platform === "string" && getPlatformInstaller(state.platform) ? state.platform : null;
|
|
3931
|
+
} catch {
|
|
3932
|
+
return null;
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
async function snapshotFiles(filePaths) {
|
|
3936
|
+
return Promise.all(
|
|
3937
|
+
filePaths.map(async (filePath) => {
|
|
3938
|
+
try {
|
|
3939
|
+
return { filePath, content: await fs2.readFile(filePath, "utf-8") };
|
|
3940
|
+
} catch (error) {
|
|
3941
|
+
if (isNodeError3(error) && error.code === "ENOENT") {
|
|
3942
|
+
return { filePath, content: null };
|
|
3943
|
+
}
|
|
3944
|
+
throw error;
|
|
3945
|
+
}
|
|
3946
|
+
})
|
|
3947
|
+
);
|
|
3948
|
+
}
|
|
3949
|
+
async function restoreFiles(snapshots) {
|
|
3950
|
+
for (const snapshot of snapshots) {
|
|
3951
|
+
if (snapshot.content === null) {
|
|
3952
|
+
await fs2.rm(snapshot.filePath, { force: true });
|
|
3953
|
+
continue;
|
|
3954
|
+
}
|
|
3955
|
+
await fs2.mkdir(path13.dirname(snapshot.filePath), { recursive: true });
|
|
3956
|
+
await fs2.writeFile(snapshot.filePath, snapshot.content, "utf-8");
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
function isRecord3(value) {
|
|
3960
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3961
|
+
}
|
|
3962
|
+
function isNodeError3(error) {
|
|
3963
|
+
return error instanceof Error && "code" in error;
|
|
3964
|
+
}
|
|
3374
3965
|
function printPlatformCreatedFiles(platform) {
|
|
3375
3966
|
if (platform === "claude-code") {
|
|
3376
3967
|
console.log(" .claude/settings.json # hook \u6CE8\u518C");
|
|
@@ -3393,7 +3984,7 @@ function printPlatformCreatedFiles(platform) {
|
|
|
3393
3984
|
}
|
|
3394
3985
|
console.log(" .github/copilot-instructions.md # Copilot instructions");
|
|
3395
3986
|
}
|
|
3396
|
-
async function
|
|
3987
|
+
async function pathExists3(p) {
|
|
3397
3988
|
try {
|
|
3398
3989
|
await fs2.access(p);
|
|
3399
3990
|
return true;
|
|
@@ -3421,29 +4012,61 @@ async function checkPlatformStatus(rootDir, platform, installed) {
|
|
|
3421
4012
|
}
|
|
3422
4013
|
async function checkPlatformReadiness(rootDir, platform) {
|
|
3423
4014
|
if (platform === "claude-code") {
|
|
3424
|
-
const hasSoloSkill = await
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
4015
|
+
const [hasSoloSkill, registered, hasHookFiles] = await Promise.all([
|
|
4016
|
+
fileMatches(
|
|
4017
|
+
path14.join(rootDir, ".claude", "skills", "solo", "SKILL.md"),
|
|
4018
|
+
(content) => isGeneratedClaudeSkill(content, "solo")
|
|
4019
|
+
),
|
|
4020
|
+
claudeHooksRegistered(rootDir),
|
|
4021
|
+
pathsExist([
|
|
4022
|
+
path14.join(rootDir, ".mancode", "hooks", "session-start.sh"),
|
|
4023
|
+
path14.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
|
|
4024
|
+
])
|
|
4025
|
+
]);
|
|
4026
|
+
const present = hasSoloSkill && registered && hasHookFiles;
|
|
4027
|
+
const minimal = await isPlatformMinimal(rootDir, "claude-code");
|
|
4028
|
+
const fullContentReady = minimal ? true : await claudeFullContentReady(rootDir);
|
|
3429
4029
|
return {
|
|
3430
4030
|
present,
|
|
3431
|
-
ready: present,
|
|
4031
|
+
ready: present && fullContentReady,
|
|
3432
4032
|
target: ".claude/",
|
|
3433
|
-
readyDetail: "skills and hooks
|
|
4033
|
+
readyDetail: present && fullContentReady ? minimal ? "solo skill and hooks ready" : "skills, agents, and hooks ready" : "skills, agents, hook files, or hook registration are incomplete"
|
|
3434
4034
|
};
|
|
3435
4035
|
}
|
|
3436
4036
|
if (platform === "cursor") {
|
|
3437
|
-
const
|
|
4037
|
+
const hasCoreRules = await allManagedSkills(
|
|
3438
4038
|
MANCODE_CURSOR_CORE_RULE_FILES.map(
|
|
3439
4039
|
(file) => path14.join(rootDir, ".cursor", "rules", file)
|
|
3440
|
-
)
|
|
4040
|
+
),
|
|
4041
|
+
[CURSOR_RULE_MANAGED_MARKER]
|
|
3441
4042
|
);
|
|
4043
|
+
if (await isPlatformMinimal(rootDir, "cursor")) {
|
|
4044
|
+
return {
|
|
4045
|
+
present: hasCoreRules,
|
|
4046
|
+
ready: hasCoreRules,
|
|
4047
|
+
target: ".cursor/rules/",
|
|
4048
|
+
readyDetail: "mancode core rules present"
|
|
4049
|
+
};
|
|
4050
|
+
}
|
|
4051
|
+
const [hasRules, hasCommands] = await Promise.all([
|
|
4052
|
+
allManagedSkills(
|
|
4053
|
+
MANCODE_CURSOR_RULE_FILES.map(
|
|
4054
|
+
(file) => path14.join(rootDir, ".cursor", "rules", file)
|
|
4055
|
+
),
|
|
4056
|
+
[CURSOR_RULE_MANAGED_MARKER]
|
|
4057
|
+
),
|
|
4058
|
+
allManagedSkills(
|
|
4059
|
+
MODE_NAMES.map(
|
|
4060
|
+
(mode) => path14.join(rootDir, ".cursor", "commands", `${mode}.md`)
|
|
4061
|
+
),
|
|
4062
|
+
[MODE_FILE_MANAGED_MARKER]
|
|
4063
|
+
)
|
|
4064
|
+
]);
|
|
3442
4065
|
return {
|
|
3443
|
-
present,
|
|
3444
|
-
ready:
|
|
3445
|
-
target: ".cursor/rules/",
|
|
3446
|
-
readyDetail: "mancode rules present"
|
|
4066
|
+
present: hasCoreRules,
|
|
4067
|
+
ready: hasRules && hasCommands,
|
|
4068
|
+
target: ".cursor/rules/ + .cursor/commands/",
|
|
4069
|
+
readyDetail: hasRules && hasCommands ? "mancode rules and commands present" : "mode rules or commands are missing, incomplete, or user-authored"
|
|
3447
4070
|
};
|
|
3448
4071
|
}
|
|
3449
4072
|
if (platform === "codex") {
|
|
@@ -3521,14 +4144,17 @@ async function checkPlatformReadiness(rootDir, platform) {
|
|
|
3521
4144
|
readyDetail: "managed block missing"
|
|
3522
4145
|
};
|
|
3523
4146
|
}
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
const
|
|
4147
|
+
if (!await isPlatformMinimal(rootDir, "copilot")) {
|
|
4148
|
+
const promptsDir = path14.join(rootDir, ".github", "prompts");
|
|
4149
|
+
const hasPrompts = await allManagedSkills(
|
|
4150
|
+
MODE_NAMES.map((mode) => path14.join(promptsDir, `${mode}.prompt.md`)),
|
|
4151
|
+
[MODE_FILE_MANAGED_MARKER]
|
|
4152
|
+
);
|
|
3527
4153
|
return {
|
|
3528
|
-
present:
|
|
3529
|
-
ready:
|
|
4154
|
+
present: true,
|
|
4155
|
+
ready: hasPrompts,
|
|
3530
4156
|
target: ".github/copilot-instructions.md + .github/prompts/",
|
|
3531
|
-
readyDetail:
|
|
4157
|
+
readyDetail: hasPrompts ? "managed block and prompts present" : "mode prompts are missing, incomplete, or user-authored"
|
|
3532
4158
|
};
|
|
3533
4159
|
}
|
|
3534
4160
|
return {
|
|
@@ -3545,16 +4171,38 @@ function describeStatus(installed, readiness) {
|
|
|
3545
4171
|
}
|
|
3546
4172
|
return "missing generated files or registration";
|
|
3547
4173
|
}
|
|
3548
|
-
async function allPathsExist(paths) {
|
|
3549
|
-
const results = await Promise.all(paths.map(pathExists5));
|
|
3550
|
-
return results.every(Boolean);
|
|
3551
|
-
}
|
|
3552
4174
|
async function allManagedSkills(paths, markers) {
|
|
3553
4175
|
const results = await Promise.all(
|
|
3554
4176
|
paths.map((filePath) => fileHasAnyMarker(filePath, markers))
|
|
3555
4177
|
);
|
|
3556
4178
|
return results.every(Boolean);
|
|
3557
4179
|
}
|
|
4180
|
+
async function claudeFullContentReady(rootDir) {
|
|
4181
|
+
const skillChecks = MVP2_SKILLS.map(
|
|
4182
|
+
(skill) => fileMatches(
|
|
4183
|
+
path14.join(rootDir, ".claude", "skills", skill.name, "SKILL.md"),
|
|
4184
|
+
(content) => isGeneratedClaudeSkill(content, skill.name)
|
|
4185
|
+
)
|
|
4186
|
+
);
|
|
4187
|
+
const agentChecks = ALL_AGENTS.map(
|
|
4188
|
+
(agent) => fileMatches(
|
|
4189
|
+
path14.join(rootDir, ".claude", "agents", `${agent.name}.md`),
|
|
4190
|
+
(content) => isGeneratedClaudeAgent(content, agent.name)
|
|
4191
|
+
)
|
|
4192
|
+
);
|
|
4193
|
+
const results = await Promise.all([...skillChecks, ...agentChecks]);
|
|
4194
|
+
return results.every(Boolean);
|
|
4195
|
+
}
|
|
4196
|
+
async function fileMatches(filePath, predicate) {
|
|
4197
|
+
try {
|
|
4198
|
+
return predicate(await fs3.readFile(filePath, "utf-8"));
|
|
4199
|
+
} catch {
|
|
4200
|
+
return false;
|
|
4201
|
+
}
|
|
4202
|
+
}
|
|
4203
|
+
async function pathsExist(paths) {
|
|
4204
|
+
return (await Promise.all(paths.map(pathExists4))).every(Boolean);
|
|
4205
|
+
}
|
|
3558
4206
|
async function fileHasAnyMarker(filePath, needles) {
|
|
3559
4207
|
try {
|
|
3560
4208
|
const content = await fs3.readFile(filePath, "utf-8");
|
|
@@ -3570,9 +4218,9 @@ async function isPlatformMinimal(rootDir, platform) {
|
|
|
3570
4218
|
"utf-8"
|
|
3571
4219
|
);
|
|
3572
4220
|
const config = JSON.parse(raw);
|
|
3573
|
-
if (!
|
|
4221
|
+
if (!isRecord4(config.platformOptions)) return false;
|
|
3574
4222
|
const options = config.platformOptions[platform];
|
|
3575
|
-
return
|
|
4223
|
+
return isRecord4(options) && options.minimal === true;
|
|
3576
4224
|
} catch {
|
|
3577
4225
|
return false;
|
|
3578
4226
|
}
|
|
@@ -3598,9 +4246,9 @@ async function claudeHooksRegistered(rootDir) {
|
|
|
3598
4246
|
}
|
|
3599
4247
|
}
|
|
3600
4248
|
function hooksRegistered(settings) {
|
|
3601
|
-
if (!
|
|
4249
|
+
if (!isRecord4(settings)) return false;
|
|
3602
4250
|
const hooks = settings.hooks;
|
|
3603
|
-
if (!
|
|
4251
|
+
if (!isRecord4(hooks)) return false;
|
|
3604
4252
|
return hasHookCommand(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand(
|
|
3605
4253
|
hooks.UserPromptSubmit,
|
|
3606
4254
|
".mancode/hooks/user-prompt-submit.sh"
|
|
@@ -3609,14 +4257,14 @@ function hooksRegistered(settings) {
|
|
|
3609
4257
|
function hasHookCommand(value, needle) {
|
|
3610
4258
|
if (!Array.isArray(value)) return false;
|
|
3611
4259
|
return value.some((group) => {
|
|
3612
|
-
if (!
|
|
4260
|
+
if (!isRecord4(group) || !Array.isArray(group.hooks)) return false;
|
|
3613
4261
|
return group.hooks.some((hook) => {
|
|
3614
|
-
if (!
|
|
4262
|
+
if (!isRecord4(hook) || typeof hook.command !== "string") return false;
|
|
3615
4263
|
return hook.command.includes(needle);
|
|
3616
4264
|
});
|
|
3617
4265
|
});
|
|
3618
4266
|
}
|
|
3619
|
-
async function
|
|
4267
|
+
async function pathExists4(p) {
|
|
3620
4268
|
try {
|
|
3621
4269
|
await fs3.access(p);
|
|
3622
4270
|
return true;
|
|
@@ -3624,7 +4272,7 @@ async function pathExists5(p) {
|
|
|
3624
4272
|
return false;
|
|
3625
4273
|
}
|
|
3626
4274
|
}
|
|
3627
|
-
function
|
|
4275
|
+
function isRecord4(value) {
|
|
3628
4276
|
return typeof value === "object" && value !== null;
|
|
3629
4277
|
}
|
|
3630
4278
|
|
|
@@ -3635,7 +4283,7 @@ var EXIT_UNSUPPORTED_PLATFORM = 2;
|
|
|
3635
4283
|
var EXIT_INSTALL_FAILED = 3;
|
|
3636
4284
|
async function install(rootDir = process4.cwd(), platform = "claude-code", options = {}) {
|
|
3637
4285
|
const stateFile = path15.join(rootDir, ".mancode", "state.json");
|
|
3638
|
-
if (!await
|
|
4286
|
+
if (!await pathExists5(stateFile)) {
|
|
3639
4287
|
console.error("\u2717 mancode not initialized.");
|
|
3640
4288
|
console.error(" Run `mancode init` first.");
|
|
3641
4289
|
return EXIT_NOT_INITIALIZED;
|
|
@@ -3662,6 +4310,11 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
|
|
|
3662
4310
|
await readStatePlatform(rootDir)
|
|
3663
4311
|
);
|
|
3664
4312
|
const alreadyInstalled = configResult.valid && config.platforms && Array.isArray(config.platforms) ? config.platforms.includes(platform) : false;
|
|
4313
|
+
const configuredMinimal = readConfiguredMinimal(
|
|
4314
|
+
config.platformOptions,
|
|
4315
|
+
platform
|
|
4316
|
+
);
|
|
4317
|
+
const effectiveMinimal = options.minimal === true || !options.force && alreadyInstalled && configuredMinimal;
|
|
3665
4318
|
if (alreadyInstalled && !options.force) {
|
|
3666
4319
|
const status2 = await checkPlatformStatus(rootDir, platform, true);
|
|
3667
4320
|
if (status2.ready) {
|
|
@@ -3683,12 +4336,13 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
|
|
|
3683
4336
|
}
|
|
3684
4337
|
}
|
|
3685
4338
|
console.log(`\u2713 Installing ${formatPlatformName(platform)} adapter...`);
|
|
3686
|
-
const
|
|
4339
|
+
const profile = await detectProjectProfile(rootDir);
|
|
3687
4340
|
try {
|
|
3688
4341
|
await installer.install(rootDir, {
|
|
3689
|
-
techStack:
|
|
3690
|
-
uiLibrary:
|
|
3691
|
-
|
|
4342
|
+
techStack: [...profile.languages, ...profile.frameworks],
|
|
4343
|
+
uiLibrary: primaryUiLibrary(profile),
|
|
4344
|
+
projectProfile: profile,
|
|
4345
|
+
minimal: effectiveMinimal,
|
|
3692
4346
|
force: options.force
|
|
3693
4347
|
});
|
|
3694
4348
|
} catch (err) {
|
|
@@ -3705,11 +4359,10 @@ async function install(rootDir = process4.cwd(), platform = "claude-code", optio
|
|
|
3705
4359
|
await updateConfig(rootDir, {
|
|
3706
4360
|
...config,
|
|
3707
4361
|
platforms,
|
|
3708
|
-
platformOptions: updatePlatformOptions(
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
)
|
|
4362
|
+
platformOptions: updatePlatformOptions(config.platformOptions, platform, {
|
|
4363
|
+
...options,
|
|
4364
|
+
minimal: effectiveMinimal
|
|
4365
|
+
})
|
|
3713
4366
|
});
|
|
3714
4367
|
console.log(`\u2713 ${formatPlatformName(platform)} adapter installed.`);
|
|
3715
4368
|
return EXIT_OK2;
|
|
@@ -3720,13 +4373,13 @@ async function readConfig(rootDir) {
|
|
|
3720
4373
|
const raw = await fs4.readFile(configPath, "utf-8");
|
|
3721
4374
|
return { config: JSON.parse(raw), valid: true };
|
|
3722
4375
|
} catch (err) {
|
|
3723
|
-
if (
|
|
4376
|
+
if (isNodeError4(err) && err.code === "ENOENT") {
|
|
3724
4377
|
return { config: {}, valid: false };
|
|
3725
4378
|
}
|
|
3726
4379
|
throw err;
|
|
3727
4380
|
}
|
|
3728
4381
|
}
|
|
3729
|
-
function
|
|
4382
|
+
function isNodeError4(err) {
|
|
3730
4383
|
return err instanceof Error && "code" in err;
|
|
3731
4384
|
}
|
|
3732
4385
|
async function updateConfig(rootDir, config) {
|
|
@@ -3745,19 +4398,24 @@ function withDefaultConfig(config, fallbackPlatform) {
|
|
|
3745
4398
|
return {
|
|
3746
4399
|
...merged,
|
|
3747
4400
|
platforms,
|
|
3748
|
-
hooks:
|
|
3749
|
-
logging:
|
|
4401
|
+
hooks: isRecord5(config.hooks) ? config.hooks : DEFAULT_CONFIG.hooks,
|
|
4402
|
+
logging: isRecord5(config.logging) ? config.logging : DEFAULT_CONFIG.logging
|
|
3750
4403
|
};
|
|
3751
4404
|
}
|
|
3752
4405
|
function updatePlatformOptions(value, platform, options) {
|
|
3753
|
-
const platformOptions =
|
|
4406
|
+
const platformOptions = isRecord5(value) ? { ...value } : {};
|
|
3754
4407
|
const existing = platformOptions[platform];
|
|
3755
4408
|
platformOptions[platform] = {
|
|
3756
|
-
...
|
|
4409
|
+
...isRecord5(existing) ? existing : {},
|
|
3757
4410
|
minimal: options.minimal === true
|
|
3758
4411
|
};
|
|
3759
4412
|
return platformOptions;
|
|
3760
4413
|
}
|
|
4414
|
+
function readConfiguredMinimal(value, platform) {
|
|
4415
|
+
if (!isRecord5(value)) return false;
|
|
4416
|
+
const platformOptions = value[platform];
|
|
4417
|
+
return isRecord5(platformOptions) && platformOptions.minimal === true;
|
|
4418
|
+
}
|
|
3761
4419
|
async function readStatePlatform(rootDir) {
|
|
3762
4420
|
try {
|
|
3763
4421
|
const raw = await fs4.readFile(
|
|
@@ -3770,7 +4428,7 @@ async function readStatePlatform(rootDir) {
|
|
|
3770
4428
|
return null;
|
|
3771
4429
|
}
|
|
3772
4430
|
}
|
|
3773
|
-
async function
|
|
4431
|
+
async function pathExists5(p) {
|
|
3774
4432
|
try {
|
|
3775
4433
|
await fs4.access(p);
|
|
3776
4434
|
return true;
|
|
@@ -3778,7 +4436,7 @@ async function pathExists6(p) {
|
|
|
3778
4436
|
return false;
|
|
3779
4437
|
}
|
|
3780
4438
|
}
|
|
3781
|
-
function
|
|
4439
|
+
function isRecord5(value) {
|
|
3782
4440
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3783
4441
|
}
|
|
3784
4442
|
|
|
@@ -3825,7 +4483,7 @@ function describePlatform(platform) {
|
|
|
3825
4483
|
}
|
|
3826
4484
|
|
|
3827
4485
|
// src/commands/manps.ts
|
|
3828
|
-
import { access as
|
|
4486
|
+
import { access as access3 } from "fs/promises";
|
|
3829
4487
|
import path18 from "path";
|
|
3830
4488
|
import { createInterface } from "readline/promises";
|
|
3831
4489
|
|
|
@@ -3834,11 +4492,11 @@ import { execFile } from "child_process";
|
|
|
3834
4492
|
import { createHash } from "crypto";
|
|
3835
4493
|
import { existsSync, readFileSync } from "fs";
|
|
3836
4494
|
import {
|
|
3837
|
-
access as
|
|
4495
|
+
access as access2,
|
|
3838
4496
|
lstat,
|
|
3839
4497
|
mkdir as mkdir7,
|
|
3840
|
-
readFile as
|
|
3841
|
-
readdir as
|
|
4498
|
+
readFile as readFile8,
|
|
4499
|
+
readdir as readdir4,
|
|
3842
4500
|
rename,
|
|
3843
4501
|
writeFile as writeFile9
|
|
3844
4502
|
} from "fs/promises";
|
|
@@ -4109,7 +4767,7 @@ async function walk(root, current, results, depth) {
|
|
|
4109
4767
|
if (depth > MAX_SCAN_DEPTH || results.length >= MAX_PROJECT_FILES) return;
|
|
4110
4768
|
let entries;
|
|
4111
4769
|
try {
|
|
4112
|
-
entries = await
|
|
4770
|
+
entries = await readdir4(current);
|
|
4113
4771
|
} catch {
|
|
4114
4772
|
return;
|
|
4115
4773
|
}
|
|
@@ -4134,7 +4792,7 @@ async function walk(root, current, results, depth) {
|
|
|
4134
4792
|
}
|
|
4135
4793
|
async function readPackageJson(projectRoot) {
|
|
4136
4794
|
try {
|
|
4137
|
-
const raw = await
|
|
4795
|
+
const raw = await readFile8(path17.join(projectRoot, "package.json"), "utf-8");
|
|
4138
4796
|
return JSON.parse(raw);
|
|
4139
4797
|
} catch {
|
|
4140
4798
|
return null;
|
|
@@ -4377,7 +5035,7 @@ async function hasDependencyCruiserConfig(projectRoot) {
|
|
|
4377
5035
|
"dependency-cruiser.config.mjs"
|
|
4378
5036
|
];
|
|
4379
5037
|
const results = await Promise.all(
|
|
4380
|
-
files.map((file) =>
|
|
5038
|
+
files.map((file) => pathExists6(path17.join(projectRoot, file)))
|
|
4381
5039
|
);
|
|
4382
5040
|
return results.some(Boolean);
|
|
4383
5041
|
}
|
|
@@ -4445,15 +5103,15 @@ function parseDepcruiseJson(output) {
|
|
|
4445
5103
|
}
|
|
4446
5104
|
}
|
|
4447
5105
|
function extractDepcruiseViolations(value) {
|
|
4448
|
-
if (!
|
|
4449
|
-
const rawViolations = Array.isArray(value.violations) ? value.violations :
|
|
5106
|
+
if (!isRecord6(value)) return [];
|
|
5107
|
+
const rawViolations = Array.isArray(value.violations) ? value.violations : isRecord6(value.summary) && Array.isArray(value.summary.violations) ? value.summary.violations : [];
|
|
4450
5108
|
return rawViolations.map((violation) => normalizeDepcruiseViolation(violation)).filter(
|
|
4451
5109
|
(violation) => violation !== null
|
|
4452
5110
|
);
|
|
4453
5111
|
}
|
|
4454
5112
|
function normalizeDepcruiseViolation(value) {
|
|
4455
|
-
if (!
|
|
4456
|
-
const rule =
|
|
5113
|
+
if (!isRecord6(value)) return null;
|
|
5114
|
+
const rule = isRecord6(value.rule) ? value.rule : {};
|
|
4457
5115
|
const from = typeof value.from === "string" ? value.from : "";
|
|
4458
5116
|
const to = typeof value.to === "string" ? value.to : void 0;
|
|
4459
5117
|
const ruleName = typeof rule.name === "string" ? rule.name : typeof value.name === "string" ? value.name : "unnamed-rule";
|
|
@@ -4527,9 +5185,9 @@ async function buildIssueDatabase(projectRoot, report) {
|
|
|
4527
5185
|
async function readIssueDatabase(issueDbPath) {
|
|
4528
5186
|
let raw;
|
|
4529
5187
|
try {
|
|
4530
|
-
raw = await
|
|
5188
|
+
raw = await readFile8(issueDbPath, "utf-8");
|
|
4531
5189
|
} catch (err) {
|
|
4532
|
-
if (
|
|
5190
|
+
if (isNodeError5(err) && err.code === "ENOENT") {
|
|
4533
5191
|
return emptyIssueDatabase();
|
|
4534
5192
|
}
|
|
4535
5193
|
throw err;
|
|
@@ -4643,7 +5301,7 @@ async function addPackageScript(projectRoot, scriptName, script) {
|
|
|
4643
5301
|
const packagePath = path17.join(projectRoot, "package.json");
|
|
4644
5302
|
let pkg;
|
|
4645
5303
|
try {
|
|
4646
|
-
pkg = JSON.parse(await
|
|
5304
|
+
pkg = JSON.parse(await readFile8(packagePath, "utf-8"));
|
|
4647
5305
|
} catch {
|
|
4648
5306
|
return false;
|
|
4649
5307
|
}
|
|
@@ -4732,18 +5390,18 @@ function readFileSyncSafe(file) {
|
|
|
4732
5390
|
function pathExistsSync(file) {
|
|
4733
5391
|
return existsSync(file);
|
|
4734
5392
|
}
|
|
4735
|
-
async function
|
|
5393
|
+
async function pathExists6(file) {
|
|
4736
5394
|
try {
|
|
4737
|
-
await
|
|
5395
|
+
await access2(file);
|
|
4738
5396
|
return true;
|
|
4739
5397
|
} catch {
|
|
4740
5398
|
return false;
|
|
4741
5399
|
}
|
|
4742
5400
|
}
|
|
4743
|
-
function
|
|
5401
|
+
function isNodeError5(err) {
|
|
4744
5402
|
return err instanceof Error && "code" in err;
|
|
4745
5403
|
}
|
|
4746
|
-
function
|
|
5404
|
+
function isRecord6(value) {
|
|
4747
5405
|
return typeof value === "object" && value !== null;
|
|
4748
5406
|
}
|
|
4749
5407
|
|
|
@@ -4753,7 +5411,7 @@ var EXIT_NOT_INITIALIZED2 = 1;
|
|
|
4753
5411
|
var EXIT_SCAN_FAILED = 2;
|
|
4754
5412
|
var EXIT_INVALID_ARG = 3;
|
|
4755
5413
|
async function manps(rootDir, area = "all", options = {}) {
|
|
4756
|
-
if (!await
|
|
5414
|
+
if (!await pathExists7(path18.join(rootDir, ".mancode", "state.json"))) {
|
|
4757
5415
|
if (options.json) {
|
|
4758
5416
|
console.log(JSON.stringify({ error: "not initialized" }, null, 2));
|
|
4759
5417
|
} else {
|
|
@@ -4895,9 +5553,9 @@ async function readStdinLines() {
|
|
|
4895
5553
|
}
|
|
4896
5554
|
return Buffer.concat(chunks).toString("utf-8").split(/\r?\n/).filter((line) => line.length > 0);
|
|
4897
5555
|
}
|
|
4898
|
-
async function
|
|
5556
|
+
async function pathExists7(p) {
|
|
4899
5557
|
try {
|
|
4900
|
-
await
|
|
5558
|
+
await access3(p);
|
|
4901
5559
|
return true;
|
|
4902
5560
|
} catch {
|
|
4903
5561
|
return false;
|
|
@@ -4912,18 +5570,35 @@ var EXIT_OK5 = 0;
|
|
|
4912
5570
|
var EXIT_NOT_INITIALIZED3 = 1;
|
|
4913
5571
|
async function refreshStyle(rootDir = process6.cwd()) {
|
|
4914
5572
|
const stateFile = path19.join(rootDir, ".mancode", "state.json");
|
|
4915
|
-
if (!await
|
|
5573
|
+
if (!await pathExists8(stateFile)) {
|
|
4916
5574
|
console.error("\u2717 mancode not initialized.");
|
|
4917
5575
|
console.error(" Run `mancode init` first.");
|
|
4918
5576
|
return EXIT_NOT_INITIALIZED3;
|
|
4919
5577
|
}
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
5578
|
+
console.log("\u2713 \u5237\u65B0\u9879\u76EE profile...");
|
|
5579
|
+
const profile = await detectProjectProfile(rootDir);
|
|
5580
|
+
const profilePath = path19.join(rootDir, ".mancode", "project-profile.json");
|
|
5581
|
+
await fs6.writeFile(
|
|
5582
|
+
profilePath,
|
|
5583
|
+
`${JSON.stringify(profile, null, 2)}
|
|
5584
|
+
`,
|
|
5585
|
+
"utf-8"
|
|
5586
|
+
);
|
|
5587
|
+
const uiLibraryHint = primaryUiLibrary(profile);
|
|
5588
|
+
await refreshLegacyStateContext(rootDir, profile, uiLibraryHint);
|
|
5589
|
+
console.log(
|
|
5590
|
+
` \u7C7B\u578B: ${profile.projectKind} | UI: ${profile.uiAssets} | \u6D4F\u89C8\u5668: ${profile.browserAutomation}`
|
|
5591
|
+
);
|
|
5592
|
+
if (profile.uiAssets !== "detected") {
|
|
5593
|
+
console.log(
|
|
5594
|
+
"\u2139\uFE0F No UI assets detected in project profile. Skipping style scan."
|
|
5595
|
+
);
|
|
5596
|
+
console.log(" Updated .mancode/project-profile.json.");
|
|
5597
|
+
await printStaticPlatformRefreshHint(rootDir);
|
|
4923
5598
|
return EXIT_OK5;
|
|
4924
5599
|
}
|
|
4925
5600
|
console.log("\u2713 \u626B\u63CF\u9879\u76EE\u8BBE\u8BA1 token...");
|
|
4926
|
-
const tokens = await scanAesthetics(rootDir,
|
|
5601
|
+
const tokens = await scanAesthetics(rootDir, uiLibraryHint);
|
|
4927
5602
|
const tokensPath = path19.join(
|
|
4928
5603
|
rootDir,
|
|
4929
5604
|
".mancode",
|
|
@@ -4940,7 +5615,9 @@ async function refreshStyle(rootDir = process6.cwd()) {
|
|
|
4940
5615
|
console.log("");
|
|
4941
5616
|
if (tokens.matchLevel === "none") {
|
|
4942
5617
|
console.log("\u672A\u68C0\u6D4B\u5230\u8BBE\u8BA1 token\u3002");
|
|
4943
|
-
console.log(
|
|
5618
|
+
console.log(
|
|
5619
|
+
" \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"
|
|
5620
|
+
);
|
|
4944
5621
|
} else {
|
|
4945
5622
|
if (tokens.sourceFiles.length > 0) {
|
|
4946
5623
|
console.log("\u626B\u63CF\u6765\u6E90\uFF1A");
|
|
@@ -4973,7 +5650,9 @@ async function refreshStyle(rootDir = process6.cwd()) {
|
|
|
4973
5650
|
console.log(` \u5339\u914D\u5EA6: ${tokens.matchLevel}`);
|
|
4974
5651
|
}
|
|
4975
5652
|
console.log("");
|
|
4976
|
-
console.log(
|
|
5653
|
+
console.log(
|
|
5654
|
+
"\u5DF2\u66F4\u65B0 .mancode/project-profile.json \u548C .mancode/aesthetics/style-tokens.json"
|
|
5655
|
+
);
|
|
4977
5656
|
await printStaticPlatformRefreshHint(rootDir);
|
|
4978
5657
|
return EXIT_OK5;
|
|
4979
5658
|
}
|
|
@@ -4991,6 +5670,28 @@ async function printStaticPlatformRefreshHint(rootDir) {
|
|
|
4991
5670
|
" Run `mancode install <platform> --force` to refresh their embedded style summaries."
|
|
4992
5671
|
);
|
|
4993
5672
|
}
|
|
5673
|
+
async function refreshLegacyStateContext(rootDir, profile, uiLibrary) {
|
|
5674
|
+
const statePath = path19.join(rootDir, ".mancode", "state.json");
|
|
5675
|
+
try {
|
|
5676
|
+
const state = JSON.parse(await fs6.readFile(statePath, "utf-8"));
|
|
5677
|
+
const stack = [...profile.languages, ...profile.frameworks];
|
|
5678
|
+
await fs6.writeFile(
|
|
5679
|
+
statePath,
|
|
5680
|
+
`${JSON.stringify(
|
|
5681
|
+
{
|
|
5682
|
+
...state,
|
|
5683
|
+
techStack: stack.join(" + ") || profile.projectKind,
|
|
5684
|
+
uiLibrary: uiLibrary ?? "None"
|
|
5685
|
+
},
|
|
5686
|
+
null,
|
|
5687
|
+
2
|
|
5688
|
+
)}
|
|
5689
|
+
`,
|
|
5690
|
+
"utf-8"
|
|
5691
|
+
);
|
|
5692
|
+
} catch {
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
4994
5695
|
async function readInstalledPlatforms2(rootDir) {
|
|
4995
5696
|
try {
|
|
4996
5697
|
const raw = await fs6.readFile(
|
|
@@ -5005,7 +5706,7 @@ async function readInstalledPlatforms2(rootDir) {
|
|
|
5005
5706
|
return [];
|
|
5006
5707
|
}
|
|
5007
5708
|
}
|
|
5008
|
-
async function
|
|
5709
|
+
async function pathExists8(p) {
|
|
5009
5710
|
try {
|
|
5010
5711
|
await fs6.access(p);
|
|
5011
5712
|
return true;
|
|
@@ -5023,10 +5724,10 @@ import process7 from "process";
|
|
|
5023
5724
|
// src/system/workflow.ts
|
|
5024
5725
|
import {
|
|
5025
5726
|
mkdir as mkdir8,
|
|
5026
|
-
readFile as
|
|
5027
|
-
readdir as
|
|
5727
|
+
readFile as readFile9,
|
|
5728
|
+
readdir as readdir5,
|
|
5028
5729
|
rm as rm4,
|
|
5029
|
-
stat as
|
|
5730
|
+
stat as stat3,
|
|
5030
5731
|
writeFile as writeFile10
|
|
5031
5732
|
} from "fs/promises";
|
|
5032
5733
|
import path20 from "path";
|
|
@@ -5045,7 +5746,8 @@ function generateTaskId(task, now = /* @__PURE__ */ new Date()) {
|
|
|
5045
5746
|
function slugify(text) {
|
|
5046
5747
|
return text.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5047
5748
|
}
|
|
5048
|
-
async function createWorkflow(projectRoot, task, mode) {
|
|
5749
|
+
async function createWorkflow(projectRoot, task, mode, options = {}) {
|
|
5750
|
+
await validateParentTask(projectRoot, mode, options.parentTaskId, true);
|
|
5049
5751
|
await mkdir8(workflowsRoot(projectRoot), { recursive: true });
|
|
5050
5752
|
const taskId = await allocateTaskId(projectRoot, generateTaskId(task));
|
|
5051
5753
|
const dir = workflowDir(projectRoot, taskId);
|
|
@@ -5058,7 +5760,9 @@ async function createWorkflow(projectRoot, task, mode) {
|
|
|
5058
5760
|
skippedSteps: [],
|
|
5059
5761
|
startedAt: now,
|
|
5060
5762
|
updatedAt: now,
|
|
5061
|
-
status: "in_progress"
|
|
5763
|
+
status: "in_progress",
|
|
5764
|
+
...options.parentTaskId ? { parentTaskId: options.parentTaskId } : {},
|
|
5765
|
+
...mode === "man" || mode === "manteam" ? { planVersion: 1 } : {}
|
|
5062
5766
|
};
|
|
5063
5767
|
await writeMetadata(dir, meta);
|
|
5064
5768
|
return meta;
|
|
@@ -5070,7 +5774,7 @@ async function allocateTaskId(projectRoot, baseTaskId) {
|
|
|
5070
5774
|
await mkdir8(workflowDir(projectRoot, taskId));
|
|
5071
5775
|
return taskId;
|
|
5072
5776
|
} catch (err) {
|
|
5073
|
-
if (
|
|
5777
|
+
if (isNodeError6(err) && err.code === "EEXIST") {
|
|
5074
5778
|
continue;
|
|
5075
5779
|
}
|
|
5076
5780
|
throw err;
|
|
@@ -5082,7 +5786,7 @@ async function readWorkflow(projectRoot, taskId) {
|
|
|
5082
5786
|
if (!isValidWorkflowTaskId(taskId)) return null;
|
|
5083
5787
|
const file = metadataPath(projectRoot, taskId);
|
|
5084
5788
|
try {
|
|
5085
|
-
const raw = await
|
|
5789
|
+
const raw = await readFile9(file, "utf-8");
|
|
5086
5790
|
return parseWorkflowMeta(raw, taskId);
|
|
5087
5791
|
} catch {
|
|
5088
5792
|
return null;
|
|
@@ -5101,13 +5805,24 @@ async function updateWorkflow(projectRoot, taskId, patch) {
|
|
|
5101
5805
|
startedAt: existing.startedAt,
|
|
5102
5806
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5103
5807
|
};
|
|
5808
|
+
const explicitlyPatchedBlockingReason = Object.hasOwn(
|
|
5809
|
+
patch,
|
|
5810
|
+
"blockingReason"
|
|
5811
|
+
);
|
|
5812
|
+
if (updated.status !== "blocked" && !explicitlyPatchedBlockingReason) {
|
|
5813
|
+
updated.blockingReason = void 0;
|
|
5814
|
+
}
|
|
5815
|
+
await validateWorkflowMeta(projectRoot, updated, existing);
|
|
5104
5816
|
await writeMetadata(workflowDir(projectRoot, taskId), updated);
|
|
5817
|
+
if (updated.parentTaskId) {
|
|
5818
|
+
await propagateChildStatus(projectRoot, updated);
|
|
5819
|
+
}
|
|
5105
5820
|
}
|
|
5106
5821
|
async function listWorkflows(projectRoot) {
|
|
5107
5822
|
const root = workflowsRoot(projectRoot);
|
|
5108
5823
|
let entries;
|
|
5109
5824
|
try {
|
|
5110
|
-
entries = await
|
|
5825
|
+
entries = await readdir5(root);
|
|
5111
5826
|
} catch {
|
|
5112
5827
|
return [];
|
|
5113
5828
|
}
|
|
@@ -5123,10 +5838,14 @@ async function deleteWorkflow(projectRoot, taskId) {
|
|
|
5123
5838
|
if (!isValidWorkflowTaskId(taskId)) return false;
|
|
5124
5839
|
const dir = workflowDir(projectRoot, taskId);
|
|
5125
5840
|
try {
|
|
5126
|
-
await
|
|
5841
|
+
await stat3(dir);
|
|
5127
5842
|
} catch {
|
|
5128
5843
|
return false;
|
|
5129
5844
|
}
|
|
5845
|
+
const existing = await readWorkflow(projectRoot, taskId);
|
|
5846
|
+
if (existing && (existing.mode === "man" || existing.mode === "manteam") && (await listMambaChildren(projectRoot, taskId)).length > 0) {
|
|
5847
|
+
return false;
|
|
5848
|
+
}
|
|
5130
5849
|
await rm4(dir, { recursive: true, force: true });
|
|
5131
5850
|
return true;
|
|
5132
5851
|
}
|
|
@@ -5148,7 +5867,7 @@ async function writeMetadata(dir, meta) {
|
|
|
5148
5867
|
function parseWorkflowMeta(raw, taskId) {
|
|
5149
5868
|
try {
|
|
5150
5869
|
const obj = JSON.parse(raw);
|
|
5151
|
-
if (typeof obj.task !== "string" ||
|
|
5870
|
+
if (typeof obj.task !== "string" || !isWorkflowMode(obj.mode) || typeof obj.currentStep !== "number" || !Array.isArray(obj.skippedSteps) || typeof obj.startedAt !== "string" || typeof obj.updatedAt !== "string" || !isWorkflowStatus(obj.status) || !isWorkflowShape(obj)) {
|
|
5152
5871
|
return null;
|
|
5153
5872
|
}
|
|
5154
5873
|
return { ...obj, taskId };
|
|
@@ -5156,7 +5875,182 @@ function parseWorkflowMeta(raw, taskId) {
|
|
|
5156
5875
|
return null;
|
|
5157
5876
|
}
|
|
5158
5877
|
}
|
|
5159
|
-
function
|
|
5878
|
+
function maxWorkflowStep(mode) {
|
|
5879
|
+
return mode === "mamba" ? 5 : 9;
|
|
5880
|
+
}
|
|
5881
|
+
function isWorkflowMode(value) {
|
|
5882
|
+
return value === "man" || value === "mamba" || value === "manteam";
|
|
5883
|
+
}
|
|
5884
|
+
function isWorkflowStatus(value) {
|
|
5885
|
+
return value === "in_progress" || value === "planned" || value === "completed" || value === "blocked" || value === "abandoned";
|
|
5886
|
+
}
|
|
5887
|
+
function isWorkflowOutcome(value) {
|
|
5888
|
+
return value === "fixed" || value === "verified" || value === "no_repro" || value === "manual_test_required";
|
|
5889
|
+
}
|
|
5890
|
+
async function validateParentTask(projectRoot, mode, parentTaskId, requireStepSix = false) {
|
|
5891
|
+
if (!parentTaskId) return;
|
|
5892
|
+
if (mode !== "mamba") {
|
|
5893
|
+
throw new Error("only mamba workflows can have a parent task");
|
|
5894
|
+
}
|
|
5895
|
+
const parent = await readWorkflow(projectRoot, parentTaskId);
|
|
5896
|
+
if (!parent || parent.mode !== "man" && parent.mode !== "manteam") {
|
|
5897
|
+
throw new Error(`invalid parent workflow: ${parentTaskId}`);
|
|
5898
|
+
}
|
|
5899
|
+
if (parent.status !== "in_progress" && parent.status !== "blocked" || requireStepSix && (parent.status !== "in_progress" || parent.currentStep !== 6)) {
|
|
5900
|
+
throw new Error(
|
|
5901
|
+
`parent workflow must be in_progress at step 6: ${parentTaskId}`
|
|
5902
|
+
);
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
async function validateWorkflowMeta(projectRoot, updated, existing) {
|
|
5906
|
+
if (updated.mode !== existing.mode) {
|
|
5907
|
+
throw new Error("workflow mode cannot be changed");
|
|
5908
|
+
}
|
|
5909
|
+
if (updated.task !== existing.task) {
|
|
5910
|
+
throw new Error("workflow task cannot be changed");
|
|
5911
|
+
}
|
|
5912
|
+
if (updated.parentTaskId !== existing.parentTaskId) {
|
|
5913
|
+
throw new Error("workflow parent task cannot be changed");
|
|
5914
|
+
}
|
|
5915
|
+
if (!Number.isInteger(updated.currentStep) || updated.currentStep < 1 || updated.currentStep > maxWorkflowStep(updated.mode)) {
|
|
5916
|
+
throw new Error(`invalid workflow step: ${updated.currentStep}`);
|
|
5917
|
+
}
|
|
5918
|
+
if (!isWorkflowStatus(updated.status)) {
|
|
5919
|
+
throw new Error(`invalid workflow status: ${String(updated.status)}`);
|
|
5920
|
+
}
|
|
5921
|
+
if (!Array.isArray(updated.skippedSteps) || updated.skippedSteps.some((step) => typeof step !== "string")) {
|
|
5922
|
+
throw new Error("workflow skipped steps must be strings");
|
|
5923
|
+
}
|
|
5924
|
+
if (!canTransition(existing.status, updated.status)) {
|
|
5925
|
+
throw new Error(
|
|
5926
|
+
`invalid workflow status transition: ${existing.status} -> ${updated.status}`
|
|
5927
|
+
);
|
|
5928
|
+
}
|
|
5929
|
+
if (updated.status === "planned" && updated.mode === "mamba") {
|
|
5930
|
+
throw new Error("mamba workflows cannot be planned");
|
|
5931
|
+
}
|
|
5932
|
+
if (updated.status === "planned" && updated.currentStep !== 4) {
|
|
5933
|
+
throw new Error("planned workflows must be at step 4");
|
|
5934
|
+
}
|
|
5935
|
+
if (updated.status === "completed" && updated.currentStep !== maxWorkflowStep(updated.mode)) {
|
|
5936
|
+
throw new Error(
|
|
5937
|
+
`completed ${updated.mode} workflows must be at step ${maxWorkflowStep(updated.mode)}`
|
|
5938
|
+
);
|
|
5939
|
+
}
|
|
5940
|
+
if (updated.blockingReason !== void 0 && typeof updated.blockingReason !== "string") {
|
|
5941
|
+
throw new Error("workflow blocking reason must be a string");
|
|
5942
|
+
}
|
|
5943
|
+
if (updated.status === "blocked") {
|
|
5944
|
+
if (!updated.blockingReason?.trim()) {
|
|
5945
|
+
throw new Error("blocked workflows require a blocking reason");
|
|
5946
|
+
}
|
|
5947
|
+
} else if (updated.blockingReason !== void 0) {
|
|
5948
|
+
throw new Error("only blocked workflows can have a blocking reason");
|
|
5949
|
+
}
|
|
5950
|
+
if (updated.mode !== "mamba" && updated.outcome !== void 0) {
|
|
5951
|
+
throw new Error("only mamba workflows can have an outcome");
|
|
5952
|
+
}
|
|
5953
|
+
if (updated.outcome !== void 0 && !isWorkflowOutcome(updated.outcome)) {
|
|
5954
|
+
throw new Error(`invalid workflow outcome: ${updated.outcome}`);
|
|
5955
|
+
}
|
|
5956
|
+
if (updated.outcome !== void 0 && updated.status !== "completed") {
|
|
5957
|
+
throw new Error("mamba outcomes can only be set on completed workflows");
|
|
5958
|
+
}
|
|
5959
|
+
if (updated.mode === "mamba" && updated.status === "completed" && !updated.outcome) {
|
|
5960
|
+
throw new Error("completed mamba workflows require an outcome");
|
|
5961
|
+
}
|
|
5962
|
+
if (updated.mode === "mamba" && updated.planVersion !== void 0) {
|
|
5963
|
+
throw new Error("mamba workflows cannot have a plan version");
|
|
5964
|
+
}
|
|
5965
|
+
if (updated.planVersion !== void 0 && (!Number.isInteger(updated.planVersion) || updated.planVersion < 1)) {
|
|
5966
|
+
throw new Error("workflow plan version must be a positive integer");
|
|
5967
|
+
}
|
|
5968
|
+
if (updated.planVersion !== existing.planVersion && updated.planVersion !== void 0 && updated.planVersion !== (existing.planVersion ?? 1) + 1) {
|
|
5969
|
+
throw new Error("workflow plan version must increase exactly once");
|
|
5970
|
+
}
|
|
5971
|
+
if (updated.planVersion !== existing.planVersion && updated.currentStep !== 4) {
|
|
5972
|
+
throw new Error("workflow plan version can only change at step 4");
|
|
5973
|
+
}
|
|
5974
|
+
await validateParentTask(projectRoot, updated.mode, updated.parentTaskId);
|
|
5975
|
+
if ((isTerminalWorkflowStatus(updated.status) || updated.status === "planned") && (await listActiveMambaChildren(projectRoot, updated.taskId)).length > 0) {
|
|
5976
|
+
throw new Error("cannot finish workflow with an active mamba child");
|
|
5977
|
+
}
|
|
5978
|
+
}
|
|
5979
|
+
function canTransition(from, to) {
|
|
5980
|
+
if (from === to) return true;
|
|
5981
|
+
if (from === "in_progress") return true;
|
|
5982
|
+
if (from === "planned" || from === "blocked") {
|
|
5983
|
+
return to === "in_progress" || to === "abandoned";
|
|
5984
|
+
}
|
|
5985
|
+
return false;
|
|
5986
|
+
}
|
|
5987
|
+
function isTerminalWorkflowStatus(status2) {
|
|
5988
|
+
return status2 === "completed" || status2 === "abandoned";
|
|
5989
|
+
}
|
|
5990
|
+
function isWorkflowShape(obj) {
|
|
5991
|
+
const { currentStep, mode, status: status2 } = obj;
|
|
5992
|
+
if (!Number.isInteger(currentStep) || !mode || currentStep === void 0 || currentStep < 1 || currentStep > maxWorkflowStep(mode)) {
|
|
5993
|
+
return false;
|
|
5994
|
+
}
|
|
5995
|
+
if (!status2 || !Array.isArray(obj.skippedSteps)) return false;
|
|
5996
|
+
if (obj.skippedSteps.some((step) => typeof step !== "string")) return false;
|
|
5997
|
+
if (obj.blockingReason !== void 0 && typeof obj.blockingReason !== "string")
|
|
5998
|
+
return false;
|
|
5999
|
+
if (obj.parentTaskId !== void 0 && typeof obj.parentTaskId !== "string")
|
|
6000
|
+
return false;
|
|
6001
|
+
if (obj.outcome !== void 0 && !isWorkflowOutcome(obj.outcome))
|
|
6002
|
+
return false;
|
|
6003
|
+
if (obj.planVersion !== void 0 && (!Number.isInteger(obj.planVersion) || obj.planVersion < 1))
|
|
6004
|
+
return false;
|
|
6005
|
+
if (status2 === "blocked") {
|
|
6006
|
+
if (!obj.blockingReason?.trim()) return false;
|
|
6007
|
+
} else if (obj.blockingReason !== void 0) {
|
|
6008
|
+
return false;
|
|
6009
|
+
}
|
|
6010
|
+
if (mode === "mamba") {
|
|
6011
|
+
if (status2 === "planned" || obj.planVersion !== void 0) return false;
|
|
6012
|
+
if (obj.parentTaskId !== void 0 && !isValidWorkflowTaskId(obj.parentTaskId))
|
|
6013
|
+
return false;
|
|
6014
|
+
if (obj.outcome !== void 0 && status2 !== "completed") return false;
|
|
6015
|
+
if (status2 === "completed" && obj.outcome === void 0) return false;
|
|
6016
|
+
} else if (obj.parentTaskId !== void 0 || obj.outcome !== void 0) {
|
|
6017
|
+
return false;
|
|
6018
|
+
}
|
|
6019
|
+
return true;
|
|
6020
|
+
}
|
|
6021
|
+
async function listActiveMambaChildren(projectRoot, parentTaskId) {
|
|
6022
|
+
return (await listMambaChildren(projectRoot, parentTaskId)).filter(
|
|
6023
|
+
(workflow2) => workflow2.status === "in_progress" || workflow2.status === "blocked"
|
|
6024
|
+
);
|
|
6025
|
+
}
|
|
6026
|
+
async function listMambaChildren(projectRoot, parentTaskId) {
|
|
6027
|
+
return (await listWorkflows(projectRoot)).filter(
|
|
6028
|
+
(workflow2) => workflow2.mode === "mamba" && workflow2.parentTaskId === parentTaskId
|
|
6029
|
+
);
|
|
6030
|
+
}
|
|
6031
|
+
async function propagateChildStatus(projectRoot, child) {
|
|
6032
|
+
const shouldBlockParent = child.status === "blocked" || child.status === "completed" && child.outcome === "manual_test_required";
|
|
6033
|
+
if (!shouldBlockParent) return;
|
|
6034
|
+
const parentTaskId = child.parentTaskId;
|
|
6035
|
+
if (!parentTaskId) return;
|
|
6036
|
+
const parent = await readWorkflow(projectRoot, parentTaskId);
|
|
6037
|
+
if (!parent || parent.status !== "in_progress") return;
|
|
6038
|
+
const reason = child.status === "blocked" ? `mamba child ${child.taskId} is blocked: ${child.blockingReason ?? "unknown reason"}` : `mamba child ${child.taskId} requires manual testing`;
|
|
6039
|
+
await writeMetadata(workflowDir(projectRoot, parent.taskId), {
|
|
6040
|
+
...parent,
|
|
6041
|
+
status: "blocked",
|
|
6042
|
+
blockingReason: reason,
|
|
6043
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6044
|
+
});
|
|
6045
|
+
if (parent.mode === "man" || parent.mode === "manteam") {
|
|
6046
|
+
await upsertActivePlan(projectRoot, {
|
|
6047
|
+
taskId: parent.taskId,
|
|
6048
|
+
status: "blocked",
|
|
6049
|
+
planVersion: parent.planVersion ?? 1
|
|
6050
|
+
});
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
function isNodeError6(err) {
|
|
5160
6054
|
return err instanceof Error && "code" in err;
|
|
5161
6055
|
}
|
|
5162
6056
|
function assertValidTaskId(taskId) {
|
|
@@ -5172,7 +6066,7 @@ var EXIT_NOT_INITIALIZED4 = 1;
|
|
|
5172
6066
|
var EXIT_CORRUPT_STATE = 2;
|
|
5173
6067
|
async function status(rootDir = process7.cwd(), options = {}) {
|
|
5174
6068
|
const stateFile = path21.join(rootDir, ".mancode", "state.json");
|
|
5175
|
-
if (!await
|
|
6069
|
+
if (!await pathExists9(stateFile)) {
|
|
5176
6070
|
console.error("\u2717 mancode not initialized.");
|
|
5177
6071
|
console.error(" Run `mancode init` to get started.");
|
|
5178
6072
|
return EXIT_NOT_INITIALIZED4;
|
|
@@ -5248,12 +6142,24 @@ async function getCurrentWorkflow(rootDir, taskId) {
|
|
|
5248
6142
|
if (!taskId) return null;
|
|
5249
6143
|
const meta = await readWorkflow(rootDir, taskId);
|
|
5250
6144
|
if (!meta) return null;
|
|
6145
|
+
const activeChildren = meta.mode === "man" || meta.mode === "manteam" ? (await listWorkflows(rootDir)).filter(
|
|
6146
|
+
(candidate) => candidate.mode === "mamba" && candidate.parentTaskId === meta.taskId && (candidate.status === "in_progress" || candidate.status === "blocked")
|
|
6147
|
+
).map((candidate) => ({
|
|
6148
|
+
taskId: candidate.taskId,
|
|
6149
|
+
currentStep: candidate.currentStep,
|
|
6150
|
+
status: candidate.status
|
|
6151
|
+
})) : void 0;
|
|
5251
6152
|
return {
|
|
5252
6153
|
taskId: meta.taskId,
|
|
5253
6154
|
task: meta.task,
|
|
5254
6155
|
mode: meta.mode,
|
|
5255
6156
|
currentStep: meta.currentStep,
|
|
5256
|
-
status: meta.status
|
|
6157
|
+
status: meta.status,
|
|
6158
|
+
...meta.blockingReason ? { blockingReason: meta.blockingReason } : {},
|
|
6159
|
+
...meta.parentTaskId ? { parentTaskId: meta.parentTaskId } : {},
|
|
6160
|
+
...meta.outcome ? { outcome: meta.outcome } : {},
|
|
6161
|
+
...meta.planVersion ? { planVersion: meta.planVersion } : {},
|
|
6162
|
+
...activeChildren ? { activeChildren } : {}
|
|
5257
6163
|
};
|
|
5258
6164
|
}
|
|
5259
6165
|
async function getProjectName(rootDir) {
|
|
@@ -5297,8 +6203,8 @@ function getEffectiveTeamStatus(state, config, detected) {
|
|
|
5297
6203
|
}
|
|
5298
6204
|
async function checkHooks(rootDir) {
|
|
5299
6205
|
const [sessionStart, userPromptSubmit, registered] = await Promise.all([
|
|
5300
|
-
|
|
5301
|
-
|
|
6206
|
+
pathExists9(path21.join(rootDir, ".mancode", "hooks", "session-start.sh")),
|
|
6207
|
+
pathExists9(
|
|
5302
6208
|
path21.join(rootDir, ".mancode", "hooks", "user-prompt-submit.sh")
|
|
5303
6209
|
),
|
|
5304
6210
|
isRegistered(rootDir)
|
|
@@ -5318,9 +6224,9 @@ async function isRegistered(rootDir) {
|
|
|
5318
6224
|
}
|
|
5319
6225
|
}
|
|
5320
6226
|
function hooksRegistered2(settings) {
|
|
5321
|
-
if (!
|
|
6227
|
+
if (!isRecord7(settings)) return false;
|
|
5322
6228
|
const hooks = settings.hooks;
|
|
5323
|
-
if (!
|
|
6229
|
+
if (!isRecord7(hooks)) return false;
|
|
5324
6230
|
return hasHookCommand2(hooks.SessionStart, ".mancode/hooks/session-start.sh") && hasHookCommand2(
|
|
5325
6231
|
hooks.UserPromptSubmit,
|
|
5326
6232
|
".mancode/hooks/user-prompt-submit.sh"
|
|
@@ -5329,9 +6235,9 @@ function hooksRegistered2(settings) {
|
|
|
5329
6235
|
function hasHookCommand2(value, needle) {
|
|
5330
6236
|
if (!Array.isArray(value)) return false;
|
|
5331
6237
|
return value.some((group) => {
|
|
5332
|
-
if (!
|
|
6238
|
+
if (!isRecord7(group) || !Array.isArray(group.hooks)) return false;
|
|
5333
6239
|
return group.hooks.some((hook) => {
|
|
5334
|
-
if (!
|
|
6240
|
+
if (!isRecord7(hook) || typeof hook.command !== "string") return false;
|
|
5335
6241
|
return hook.command.includes(needle);
|
|
5336
6242
|
});
|
|
5337
6243
|
});
|
|
@@ -5343,7 +6249,7 @@ async function estimateHookInjection(rootDir) {
|
|
|
5343
6249
|
"hooks",
|
|
5344
6250
|
"user-prompt-submit.sh"
|
|
5345
6251
|
);
|
|
5346
|
-
if (!await
|
|
6252
|
+
if (!await pathExists9(hookPath)) {
|
|
5347
6253
|
return { tokens: 0, cap: 800 };
|
|
5348
6254
|
}
|
|
5349
6255
|
try {
|
|
@@ -5405,6 +6311,23 @@ function printText(r) {
|
|
|
5405
6311
|
console.log(
|
|
5406
6312
|
`Workflow: ${r.currentWorkflow.taskId} (Step ${r.currentWorkflow.currentStep}/${stepMax}, ${r.currentWorkflow.status})`
|
|
5407
6313
|
);
|
|
6314
|
+
if (r.currentWorkflow.parentTaskId) {
|
|
6315
|
+
console.log(`Parent: ${r.currentWorkflow.parentTaskId}`);
|
|
6316
|
+
}
|
|
6317
|
+
if (r.currentWorkflow.planVersion !== void 0) {
|
|
6318
|
+
console.log(`Plan version: ${r.currentWorkflow.planVersion}`);
|
|
6319
|
+
}
|
|
6320
|
+
if (r.currentWorkflow.outcome) {
|
|
6321
|
+
console.log(`Outcome: ${r.currentWorkflow.outcome}`);
|
|
6322
|
+
}
|
|
6323
|
+
if (r.currentWorkflow.blockingReason) {
|
|
6324
|
+
console.log(`Blocked: ${r.currentWorkflow.blockingReason}`);
|
|
6325
|
+
}
|
|
6326
|
+
if (r.currentWorkflow.activeChildren?.length) {
|
|
6327
|
+
console.log(
|
|
6328
|
+
`Children: ${r.currentWorkflow.activeChildren.map((child) => `${child.taskId} (${child.status})`).join(", ")}`
|
|
6329
|
+
);
|
|
6330
|
+
}
|
|
5408
6331
|
}
|
|
5409
6332
|
console.log("");
|
|
5410
6333
|
console.log("Installed platforms:");
|
|
@@ -5444,12 +6367,12 @@ function formatTeamStatus(team) {
|
|
|
5444
6367
|
return "solo";
|
|
5445
6368
|
}
|
|
5446
6369
|
function workflowStepMax(mode) {
|
|
5447
|
-
return mode
|
|
6370
|
+
return maxWorkflowStep(mode);
|
|
5448
6371
|
}
|
|
5449
|
-
function
|
|
6372
|
+
function isRecord7(value) {
|
|
5450
6373
|
return typeof value === "object" && value !== null;
|
|
5451
6374
|
}
|
|
5452
|
-
async function
|
|
6375
|
+
async function pathExists9(p) {
|
|
5453
6376
|
try {
|
|
5454
6377
|
await fs7.access(p);
|
|
5455
6378
|
return true;
|
|
@@ -5459,30 +6382,15 @@ async function pathExists10(p) {
|
|
|
5459
6382
|
}
|
|
5460
6383
|
|
|
5461
6384
|
// src/commands/uninstall.ts
|
|
5462
|
-
import { access as
|
|
6385
|
+
import { access as access4, readFile as readFile10, rm as rm5, writeFile as writeFile11 } from "fs/promises";
|
|
5463
6386
|
import path22 from "path";
|
|
5464
6387
|
import process8 from "process";
|
|
5465
6388
|
var EXIT_OK7 = 0;
|
|
5466
6389
|
var EXIT_NOT_INITIALIZED5 = 1;
|
|
5467
6390
|
var EXIT_UNSUPPORTED_PLATFORM2 = 2;
|
|
5468
|
-
var CLAUDE_SKILL_DIRS = [
|
|
5469
|
-
"solo",
|
|
5470
|
-
"man8",
|
|
5471
|
-
"man",
|
|
5472
|
-
"manteam",
|
|
5473
|
-
"manps",
|
|
5474
|
-
"mansolo"
|
|
5475
|
-
];
|
|
5476
|
-
var CLAUDE_AGENT_FILES = [
|
|
5477
|
-
"scout.md",
|
|
5478
|
-
"head-coach.md",
|
|
5479
|
-
"film-analyst-offense.md",
|
|
5480
|
-
"film-analyst-defense.md",
|
|
5481
|
-
"plan-coach.md"
|
|
5482
|
-
];
|
|
5483
6391
|
async function uninstall(rootDir = process8.cwd(), platform, options = {}) {
|
|
5484
6392
|
const stateFile = path22.join(rootDir, ".mancode", "state.json");
|
|
5485
|
-
if (!await
|
|
6393
|
+
if (!await pathExists10(stateFile)) {
|
|
5486
6394
|
console.error("\u2717 mancode not initialized.");
|
|
5487
6395
|
console.error(" Run `mancode init` first.");
|
|
5488
6396
|
return EXIT_NOT_INITIALIZED5;
|
|
@@ -5539,22 +6447,14 @@ async function uninstallAll(rootDir) {
|
|
|
5539
6447
|
});
|
|
5540
6448
|
}
|
|
5541
6449
|
async function uninstallClaudeCode(rootDir) {
|
|
5542
|
-
|
|
5543
|
-
const skillsDir = path22.join(claudeDir, "skills");
|
|
5544
|
-
const agentsDir = path22.join(claudeDir, "agents");
|
|
5545
|
-
for (const skillDir of CLAUDE_SKILL_DIRS) {
|
|
5546
|
-
await rm5(path22.join(skillsDir, skillDir), { recursive: true, force: true });
|
|
5547
|
-
}
|
|
5548
|
-
for (const agentFile of CLAUDE_AGENT_FILES) {
|
|
5549
|
-
await rm5(path22.join(agentsDir, agentFile), { force: true });
|
|
5550
|
-
}
|
|
6450
|
+
await removeClaudeGeneratedContent(rootDir);
|
|
5551
6451
|
await cleanClaudeSettings(rootDir);
|
|
5552
6452
|
}
|
|
5553
6453
|
async function cleanClaudeSettings(rootDir) {
|
|
5554
6454
|
const settingsPath = path22.join(rootDir, ".claude", "settings.json");
|
|
5555
6455
|
let content;
|
|
5556
6456
|
try {
|
|
5557
|
-
content = await
|
|
6457
|
+
content = await readFile10(settingsPath, "utf-8");
|
|
5558
6458
|
} catch {
|
|
5559
6459
|
return;
|
|
5560
6460
|
}
|
|
@@ -5564,28 +6464,21 @@ async function cleanClaudeSettings(rootDir) {
|
|
|
5564
6464
|
} catch {
|
|
5565
6465
|
return;
|
|
5566
6466
|
}
|
|
5567
|
-
if (!isRecord6(settings.hooks)) return;
|
|
5568
6467
|
const cleanedHooks = {};
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
}
|
|
5574
|
-
const filtered = groups.flatMap((group) => {
|
|
5575
|
-
if (!isRecord6(group) || !Array.isArray(group.hooks)) return [group];
|
|
5576
|
-
const hooks = group.hooks.filter(
|
|
5577
|
-
(hook) => !(isRecord6(hook) && typeof hook.command === "string" && hook.command.includes(".mancode/hooks/"))
|
|
5578
|
-
);
|
|
5579
|
-
return hooks.length > 0 ? [{ ...group, hooks }] : [];
|
|
5580
|
-
});
|
|
5581
|
-
if (filtered.length > 0) {
|
|
5582
|
-
cleanedHooks[event] = filtered;
|
|
6468
|
+
if (isRecord8(settings.hooks)) {
|
|
6469
|
+
for (const [event, value] of Object.entries(settings.hooks)) {
|
|
6470
|
+
const cleaned = cleanClaudeHookValue(value);
|
|
6471
|
+
if (cleaned !== void 0) cleanedHooks[event] = cleaned;
|
|
5583
6472
|
}
|
|
6473
|
+
settings.hooks = Object.keys(cleanedHooks).length > 0 ? cleanedHooks : void 0;
|
|
5584
6474
|
}
|
|
5585
|
-
if (
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
6475
|
+
if (isRecord8(settings.skills)) {
|
|
6476
|
+
const retainedSkills = Object.fromEntries(
|
|
6477
|
+
Object.entries(settings.skills).filter(
|
|
6478
|
+
([name, value]) => LEGACY_CLAUDE_SKILL_SETTINGS[name] !== value
|
|
6479
|
+
)
|
|
6480
|
+
);
|
|
6481
|
+
settings.skills = Object.keys(retainedSkills).length > 0 ? retainedSkills : void 0;
|
|
5589
6482
|
}
|
|
5590
6483
|
await writeFile11(
|
|
5591
6484
|
settingsPath,
|
|
@@ -5594,17 +6487,51 @@ async function cleanClaudeSettings(rootDir) {
|
|
|
5594
6487
|
"utf-8"
|
|
5595
6488
|
);
|
|
5596
6489
|
}
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
6490
|
+
function cleanClaudeHookValue(value) {
|
|
6491
|
+
if (Array.isArray(value)) {
|
|
6492
|
+
const entries2 = value.flatMap(cleanClaudeHookEntry);
|
|
6493
|
+
return entries2.length > 0 ? entries2 : void 0;
|
|
6494
|
+
}
|
|
6495
|
+
if (!isRecord8(value)) return value;
|
|
6496
|
+
if (Array.isArray(value.hooks) || typeof value.command === "string") {
|
|
6497
|
+
const entries2 = cleanClaudeHookEntry(value);
|
|
6498
|
+
return entries2.length > 0 ? entries2 : void 0;
|
|
6499
|
+
}
|
|
6500
|
+
const entries = Object.entries(value).flatMap(([key, item]) => {
|
|
6501
|
+
if (key === "mancode" && containsLegacyMancodeHookPath2(item)) return [];
|
|
6502
|
+
const cleaned = cleanClaudeHookValue(item);
|
|
6503
|
+
return cleaned === void 0 ? [] : [[key, cleaned]];
|
|
6504
|
+
});
|
|
6505
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
6506
|
+
}
|
|
6507
|
+
function cleanClaudeHookEntry(entry) {
|
|
6508
|
+
if (Array.isArray(entry)) return entry.flatMap(cleanClaudeHookEntry);
|
|
6509
|
+
if (!isRecord8(entry)) return [entry];
|
|
6510
|
+
if (Array.isArray(entry.hooks)) {
|
|
6511
|
+
const hooks = entry.hooks.filter((hook) => !isMancodeHookEntry(hook));
|
|
6512
|
+
return hooks.length > 0 ? [{ ...entry, hooks }] : [];
|
|
6513
|
+
}
|
|
6514
|
+
return isMancodeHookEntry(entry) ? [] : [entry];
|
|
6515
|
+
}
|
|
6516
|
+
function isMancodeHookEntry(value) {
|
|
6517
|
+
return isRecord8(value) && typeof value.command === "string" && isGeneratedMancodeHookCommand(value.command);
|
|
6518
|
+
}
|
|
6519
|
+
function containsLegacyMancodeHookPath2(value) {
|
|
6520
|
+
if (Array.isArray(value)) return value.some(containsLegacyMancodeHookPath2);
|
|
6521
|
+
if (!isRecord8(value)) return false;
|
|
6522
|
+
if (typeof value.command === "string" && value.command.includes(".mancode/hooks/")) {
|
|
6523
|
+
return true;
|
|
5601
6524
|
}
|
|
6525
|
+
return Object.values(value).some(containsLegacyMancodeHookPath2);
|
|
6526
|
+
}
|
|
6527
|
+
async function uninstallCursor(rootDir) {
|
|
6528
|
+
await removeCursorGeneratedRules(rootDir);
|
|
5602
6529
|
await removeCursorCommands(rootDir);
|
|
5603
6530
|
}
|
|
5604
6531
|
async function uninstallCodex(rootDir) {
|
|
5605
6532
|
const agentsPath = path22.join(rootDir, "AGENTS.md");
|
|
5606
6533
|
try {
|
|
5607
|
-
const content = await
|
|
6534
|
+
const content = await readFile10(agentsPath, "utf-8");
|
|
5608
6535
|
const cleaned = removeManagedBlock(content);
|
|
5609
6536
|
if (cleaned.trim()) {
|
|
5610
6537
|
await writeFile11(agentsPath, `${cleaned}
|
|
@@ -5614,9 +6541,7 @@ async function uninstallCodex(rootDir) {
|
|
|
5614
6541
|
}
|
|
5615
6542
|
} catch {
|
|
5616
6543
|
}
|
|
5617
|
-
|
|
5618
|
-
await removeCodexSkills(rootDir);
|
|
5619
|
-
}
|
|
6544
|
+
await removeCodexSkills(rootDir);
|
|
5620
6545
|
}
|
|
5621
6546
|
async function uninstallCopilot(rootDir) {
|
|
5622
6547
|
const instructionsPath = path22.join(
|
|
@@ -5625,7 +6550,7 @@ async function uninstallCopilot(rootDir) {
|
|
|
5625
6550
|
"copilot-instructions.md"
|
|
5626
6551
|
);
|
|
5627
6552
|
try {
|
|
5628
|
-
const content = await
|
|
6553
|
+
const content = await readFile10(instructionsPath, "utf-8");
|
|
5629
6554
|
const cleaned = removeManagedBlock(content);
|
|
5630
6555
|
if (cleaned.trim()) {
|
|
5631
6556
|
await writeFile11(instructionsPath, `${cleaned}
|
|
@@ -5640,7 +6565,7 @@ async function uninstallCopilot(rootDir) {
|
|
|
5640
6565
|
async function uninstallZcode(rootDir) {
|
|
5641
6566
|
const agentsPath = path22.join(rootDir, "AGENTS.md");
|
|
5642
6567
|
try {
|
|
5643
|
-
const content = await
|
|
6568
|
+
const content = await readFile10(agentsPath, "utf-8");
|
|
5644
6569
|
const cleaned = removeManagedBlock(
|
|
5645
6570
|
content,
|
|
5646
6571
|
ZCODE_MANCODE_START_MARKER,
|
|
@@ -5654,18 +6579,16 @@ async function uninstallZcode(rootDir) {
|
|
|
5654
6579
|
}
|
|
5655
6580
|
} catch {
|
|
5656
6581
|
}
|
|
5657
|
-
|
|
5658
|
-
await removeZcodeSkills(rootDir);
|
|
5659
|
-
}
|
|
6582
|
+
await removeZcodeSkills(rootDir);
|
|
5660
6583
|
}
|
|
5661
6584
|
async function removeFromConfig(rootDir, platform) {
|
|
5662
6585
|
const configPath = path22.join(rootDir, ".mancode", "config.json");
|
|
5663
6586
|
try {
|
|
5664
|
-
const raw = await
|
|
6587
|
+
const raw = await readFile10(configPath, "utf-8");
|
|
5665
6588
|
const config = JSON.parse(raw);
|
|
5666
6589
|
if (Array.isArray(config.platforms)) {
|
|
5667
6590
|
config.platforms = config.platforms.filter((p) => p !== platform);
|
|
5668
|
-
if (
|
|
6591
|
+
if (isRecord8(config.platformOptions)) {
|
|
5669
6592
|
const platformOptions = Object.fromEntries(
|
|
5670
6593
|
Object.entries(config.platformOptions).filter(
|
|
5671
6594
|
([name]) => name !== platform
|
|
@@ -5683,31 +6606,17 @@ async function removeFromConfig(rootDir, platform) {
|
|
|
5683
6606
|
} catch {
|
|
5684
6607
|
}
|
|
5685
6608
|
}
|
|
5686
|
-
function
|
|
6609
|
+
function isRecord8(value) {
|
|
5687
6610
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5688
6611
|
}
|
|
5689
|
-
async function
|
|
6612
|
+
async function pathExists10(p) {
|
|
5690
6613
|
try {
|
|
5691
|
-
await
|
|
6614
|
+
await access4(p);
|
|
5692
6615
|
return true;
|
|
5693
6616
|
} catch {
|
|
5694
6617
|
return false;
|
|
5695
6618
|
}
|
|
5696
6619
|
}
|
|
5697
|
-
async function otherAgentSkillsPlatformActive(rootDir, excluding) {
|
|
5698
|
-
try {
|
|
5699
|
-
const raw = await readFile8(
|
|
5700
|
-
path22.join(rootDir, ".mancode", "config.json"),
|
|
5701
|
-
"utf-8"
|
|
5702
|
-
);
|
|
5703
|
-
const config = JSON.parse(raw);
|
|
5704
|
-
const platforms = Array.isArray(config.platforms) ? config.platforms : [];
|
|
5705
|
-
const peers = ["codex", "zcode"].filter((p) => p !== excluding);
|
|
5706
|
-
return platforms.some((p) => peers.includes(p));
|
|
5707
|
-
} catch {
|
|
5708
|
-
return false;
|
|
5709
|
-
}
|
|
5710
|
-
}
|
|
5711
6620
|
|
|
5712
6621
|
// src/commands/version.ts
|
|
5713
6622
|
import process9 from "process";
|
|
@@ -5720,13 +6629,13 @@ function version() {
|
|
|
5720
6629
|
}
|
|
5721
6630
|
|
|
5722
6631
|
// src/commands/workflow.ts
|
|
5723
|
-
import { access as
|
|
6632
|
+
import { access as access5 } from "fs/promises";
|
|
5724
6633
|
import path23 from "path";
|
|
5725
6634
|
var EXIT_OK8 = 0;
|
|
5726
6635
|
var EXIT_NOT_INITIALIZED6 = 1;
|
|
5727
6636
|
var EXIT_INVALID_ARG2 = 2;
|
|
5728
6637
|
async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
5729
|
-
if (!await
|
|
6638
|
+
if (!await pathExists11(path23.join(rootDir, ".mancode", "state.json"))) {
|
|
5730
6639
|
if (options.json) {
|
|
5731
6640
|
console.log(JSON.stringify({ error: "not initialized" }, null, 2));
|
|
5732
6641
|
} else {
|
|
@@ -5758,7 +6667,7 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
|
5758
6667
|
} else {
|
|
5759
6668
|
console.error(`\u2717 Invalid workflow subcommand: ${subcommand}`);
|
|
5760
6669
|
console.error(
|
|
5761
|
-
" Use: create <
|
|
6670
|
+
" Use: create <man|mamba|manteam> <task> | update <taskId> | list | show <taskId> | clean"
|
|
5762
6671
|
);
|
|
5763
6672
|
}
|
|
5764
6673
|
return EXIT_INVALID_ARG2;
|
|
@@ -5767,25 +6676,34 @@ async function workflow(rootDir, subcommand, args = [], options = {}) {
|
|
|
5767
6676
|
async function workflowCreate(rootDir, args, options) {
|
|
5768
6677
|
const mode = args[0];
|
|
5769
6678
|
const task = args.slice(1).join(" ").trim();
|
|
5770
|
-
if (mode !== "
|
|
6679
|
+
if (mode !== "man" && mode !== "mamba" && mode !== "manteam") {
|
|
5771
6680
|
return invalidArg(
|
|
5772
6681
|
options,
|
|
5773
6682
|
`invalid workflow mode: ${mode ?? ""}`,
|
|
5774
|
-
"Use: mancode workflow create <
|
|
6683
|
+
"Use: mancode workflow create <man|mamba|manteam> <task> [--parent-task <taskId>]"
|
|
5775
6684
|
);
|
|
5776
6685
|
}
|
|
5777
6686
|
if (!task) {
|
|
5778
6687
|
return invalidArg(
|
|
5779
6688
|
options,
|
|
5780
6689
|
"missing workflow task",
|
|
5781
|
-
"Use: mancode workflow create <
|
|
6690
|
+
"Use: mancode workflow create <man|mamba|manteam> <task> [--parent-task <taskId>]"
|
|
5782
6691
|
);
|
|
5783
6692
|
}
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
6693
|
+
try {
|
|
6694
|
+
const meta = await createWorkflow(rootDir, task, mode, {
|
|
6695
|
+
parentTaskId: options.parentTask
|
|
6696
|
+
});
|
|
6697
|
+
if (options.json) {
|
|
6698
|
+
console.log(JSON.stringify(meta, null, 2));
|
|
6699
|
+
} else {
|
|
6700
|
+
console.log(`Created workflow: ${meta.taskId}`);
|
|
6701
|
+
}
|
|
6702
|
+
} catch (error) {
|
|
6703
|
+
return invalidArg(
|
|
6704
|
+
options,
|
|
6705
|
+
error instanceof Error ? error.message : "unable to create workflow"
|
|
6706
|
+
);
|
|
5789
6707
|
}
|
|
5790
6708
|
return EXIT_OK8;
|
|
5791
6709
|
}
|
|
@@ -5794,7 +6712,7 @@ async function workflowUpdate(rootDir, taskId, options) {
|
|
|
5794
6712
|
return invalidArg(
|
|
5795
6713
|
options,
|
|
5796
6714
|
"missing taskId",
|
|
5797
|
-
"Use: mancode workflow update <taskId> [--step N] [--status in_progress|completed|abandoned] [--skipped a,b]"
|
|
6715
|
+
"Use: mancode workflow update <taskId> [--step N] [--status in_progress|planned|completed|blocked|abandoned] [--blocking-reason <reason>] [--outcome <outcome>] [--plan-version N] [--skipped a,b]"
|
|
5798
6716
|
);
|
|
5799
6717
|
}
|
|
5800
6718
|
if (!isValidWorkflowTaskId(taskId)) {
|
|
@@ -5806,9 +6724,9 @@ async function workflowUpdate(rootDir, taskId, options) {
|
|
|
5806
6724
|
}
|
|
5807
6725
|
const patch = {};
|
|
5808
6726
|
if (options.step !== void 0) {
|
|
5809
|
-
const currentStep =
|
|
5810
|
-
const maxStep = existing.mode
|
|
5811
|
-
if (
|
|
6727
|
+
const currentStep = parseExactPositiveInteger(options.step);
|
|
6728
|
+
const maxStep = maxWorkflowStep(existing.mode);
|
|
6729
|
+
if (currentStep === null || currentStep < 1 || currentStep > maxStep) {
|
|
5812
6730
|
return invalidArg(options, `invalid --step: ${options.step}`);
|
|
5813
6731
|
}
|
|
5814
6732
|
patch.currentStep = currentStep;
|
|
@@ -5819,14 +6737,67 @@ async function workflowUpdate(rootDir, taskId, options) {
|
|
|
5819
6737
|
}
|
|
5820
6738
|
patch.status = options.status;
|
|
5821
6739
|
}
|
|
6740
|
+
const nextStatus = patch.status ?? existing.status;
|
|
6741
|
+
if (options.blockingReason !== void 0) {
|
|
6742
|
+
if (nextStatus !== "blocked") {
|
|
6743
|
+
return invalidArg(
|
|
6744
|
+
options,
|
|
6745
|
+
"--blocking-reason requires --status blocked or an already blocked workflow"
|
|
6746
|
+
);
|
|
6747
|
+
}
|
|
6748
|
+
patch.blockingReason = options.blockingReason;
|
|
6749
|
+
}
|
|
6750
|
+
if (options.status === "in_progress" && existing.blockingReason) {
|
|
6751
|
+
patch.blockingReason = void 0;
|
|
6752
|
+
}
|
|
6753
|
+
if (options.outcome !== void 0) {
|
|
6754
|
+
if (!isWorkflowOutcome(options.outcome)) {
|
|
6755
|
+
return invalidArg(options, `invalid --outcome: ${options.outcome}`);
|
|
6756
|
+
}
|
|
6757
|
+
if (existing.mode !== "mamba" || nextStatus !== "completed") {
|
|
6758
|
+
return invalidArg(
|
|
6759
|
+
options,
|
|
6760
|
+
"--outcome is only valid when completing a mamba workflow"
|
|
6761
|
+
);
|
|
6762
|
+
}
|
|
6763
|
+
patch.outcome = options.outcome;
|
|
6764
|
+
}
|
|
6765
|
+
if (options.planVersion !== void 0) {
|
|
6766
|
+
const planVersion = parseExactPositiveInteger(options.planVersion);
|
|
6767
|
+
const currentVersion = existing.planVersion ?? 1;
|
|
6768
|
+
if (planVersion === null || existing.mode !== "man" && existing.mode !== "manteam" || planVersion !== currentVersion + 1 || (patch.currentStep ?? existing.currentStep) !== 4) {
|
|
6769
|
+
return invalidArg(
|
|
6770
|
+
options,
|
|
6771
|
+
`invalid --plan-version: ${options.planVersion}; expected ${currentVersion + 1} at step 4 for ${existing.mode}`
|
|
6772
|
+
);
|
|
6773
|
+
}
|
|
6774
|
+
patch.planVersion = planVersion;
|
|
6775
|
+
}
|
|
5822
6776
|
if (options.skipped !== void 0) {
|
|
5823
6777
|
patch.skippedSteps = options.skipped.split(",").map((item) => item.trim()).filter(Boolean);
|
|
5824
6778
|
}
|
|
5825
6779
|
if (Object.keys(patch).length === 0) {
|
|
5826
6780
|
return invalidArg(options, "missing update fields");
|
|
5827
6781
|
}
|
|
5828
|
-
|
|
6782
|
+
try {
|
|
6783
|
+
await updateWorkflow(rootDir, taskId, patch);
|
|
6784
|
+
} catch (error) {
|
|
6785
|
+
return invalidArg(
|
|
6786
|
+
options,
|
|
6787
|
+
error instanceof Error ? error.message : "unable to update workflow"
|
|
6788
|
+
);
|
|
6789
|
+
}
|
|
5829
6790
|
const updated = await readWorkflow(rootDir, taskId);
|
|
6791
|
+
if (updated && (updated.mode === "man" || updated.mode === "manteam")) {
|
|
6792
|
+
const shouldSyncPlan = updated.currentStep >= 4 || updated.status !== "in_progress";
|
|
6793
|
+
if (shouldSyncPlan) {
|
|
6794
|
+
await upsertActivePlan(rootDir, {
|
|
6795
|
+
taskId: updated.taskId,
|
|
6796
|
+
status: updated.status,
|
|
6797
|
+
planVersion: updated.planVersion ?? 1
|
|
6798
|
+
});
|
|
6799
|
+
}
|
|
6800
|
+
}
|
|
5830
6801
|
if (options.json) {
|
|
5831
6802
|
console.log(JSON.stringify(updated, null, 2));
|
|
5832
6803
|
} else {
|
|
@@ -5836,8 +6807,9 @@ async function workflowUpdate(rootDir, taskId, options) {
|
|
|
5836
6807
|
}
|
|
5837
6808
|
async function workflowList(rootDir, options) {
|
|
5838
6809
|
const workflows = await listWorkflows(rootDir);
|
|
6810
|
+
const views = attachActiveChildren(workflows);
|
|
5839
6811
|
if (options.json) {
|
|
5840
|
-
console.log(JSON.stringify(
|
|
6812
|
+
console.log(JSON.stringify(views, null, 2));
|
|
5841
6813
|
return EXIT_OK8;
|
|
5842
6814
|
}
|
|
5843
6815
|
if (workflows.length === 0) {
|
|
@@ -5849,7 +6821,7 @@ async function workflowList(rootDir, options) {
|
|
|
5849
6821
|
`mancode workflows (${workflows.length} total, ${inProgress} in_progress)`
|
|
5850
6822
|
);
|
|
5851
6823
|
console.log("");
|
|
5852
|
-
for (const meta of
|
|
6824
|
+
for (const meta of views) {
|
|
5853
6825
|
console.log(formatWorkflowRow(meta));
|
|
5854
6826
|
}
|
|
5855
6827
|
return EXIT_OK8;
|
|
@@ -5878,22 +6850,44 @@ async function workflowShow(rootDir, taskId, options) {
|
|
|
5878
6850
|
}
|
|
5879
6851
|
return EXIT_INVALID_ARG2;
|
|
5880
6852
|
}
|
|
6853
|
+
const children = meta.mode === "man" || meta.mode === "manteam" ? attachActiveChildren(await listWorkflows(rootDir)).find(
|
|
6854
|
+
(workflow2) => workflow2.taskId === meta.taskId
|
|
6855
|
+
)?.activeChildren ?? [] : void 0;
|
|
5881
6856
|
if (options.json) {
|
|
5882
|
-
console.log(
|
|
6857
|
+
console.log(
|
|
6858
|
+
JSON.stringify(
|
|
6859
|
+
children === void 0 ? meta : { ...meta, activeChildren: children },
|
|
6860
|
+
null,
|
|
6861
|
+
2
|
|
6862
|
+
)
|
|
6863
|
+
);
|
|
5883
6864
|
return EXIT_OK8;
|
|
5884
6865
|
}
|
|
5885
6866
|
console.log(`Workflow: ${meta.taskId}`);
|
|
5886
6867
|
console.log(`Task: ${meta.task}`);
|
|
5887
6868
|
console.log(`Mode: ${meta.mode}`);
|
|
5888
6869
|
console.log(`Status: ${meta.status}`);
|
|
5889
|
-
console.log(
|
|
5890
|
-
`Current step:${meta.currentStep}/${meta.mode === "man" ? 8 : 3}`
|
|
5891
|
-
);
|
|
6870
|
+
console.log(`Current step:${meta.currentStep}/${maxWorkflowStep(meta.mode)}`);
|
|
5892
6871
|
console.log(
|
|
5893
6872
|
`Skipped: ${meta.skippedSteps.length > 0 ? meta.skippedSteps.join(", ") : "none"}`
|
|
5894
6873
|
);
|
|
5895
6874
|
console.log(`Started: ${meta.startedAt}`);
|
|
5896
6875
|
console.log(`Updated: ${meta.updatedAt}`);
|
|
6876
|
+
if (meta.parentTaskId) console.log(`Parent: ${meta.parentTaskId}`);
|
|
6877
|
+
if (meta.planVersion !== void 0)
|
|
6878
|
+
console.log(`Plan version: ${meta.planVersion}`);
|
|
6879
|
+
if (meta.outcome) console.log(`Outcome: ${meta.outcome}`);
|
|
6880
|
+
if (meta.blockingReason) console.log(`Blocked: ${meta.blockingReason}`);
|
|
6881
|
+
if (children !== void 0) {
|
|
6882
|
+
if (children.length > 0) {
|
|
6883
|
+
console.log(`Children: ${children.length} active mamba workflow(s)`);
|
|
6884
|
+
for (const child of children) {
|
|
6885
|
+
console.log(
|
|
6886
|
+
` - ${child.taskId} (Step ${child.currentStep}/5, ${child.status})`
|
|
6887
|
+
);
|
|
6888
|
+
}
|
|
6889
|
+
}
|
|
6890
|
+
}
|
|
5897
6891
|
return EXIT_OK8;
|
|
5898
6892
|
}
|
|
5899
6893
|
async function workflowClean(rootDir, options) {
|
|
@@ -5914,42 +6908,83 @@ async function workflowClean(rootDir, options) {
|
|
|
5914
6908
|
}
|
|
5915
6909
|
return EXIT_INVALID_ARG2;
|
|
5916
6910
|
}
|
|
5917
|
-
const
|
|
5918
|
-
if (!
|
|
6911
|
+
const eligible = workflows.filter((meta) => {
|
|
6912
|
+
if (!isTerminalWorkflowStatus(meta.status)) return false;
|
|
6913
|
+
if (!cutoff) return true;
|
|
5919
6914
|
const started = Date.parse(meta.startedAt);
|
|
5920
6915
|
return Number.isFinite(started) && started < cutoff.getTime();
|
|
5921
6916
|
});
|
|
6917
|
+
const eligibleIds = new Set(eligible.map((meta) => meta.taskId));
|
|
6918
|
+
const candidates = eligible.filter((meta) => {
|
|
6919
|
+
if (meta.mode !== "man" && meta.mode !== "manteam") return true;
|
|
6920
|
+
const children = workflows.filter(
|
|
6921
|
+
(candidate) => candidate.mode === "mamba" && candidate.parentTaskId === meta.taskId
|
|
6922
|
+
);
|
|
6923
|
+
return children.every((child) => eligibleIds.has(child.taskId));
|
|
6924
|
+
}).sort((a, b) => Number(a.mode !== "mamba") - Number(b.mode !== "mamba"));
|
|
6925
|
+
const removed = [];
|
|
6926
|
+
if (!options.dryRun) {
|
|
6927
|
+
for (const meta of candidates) {
|
|
6928
|
+
const didRemove = await deleteWorkflow(rootDir, meta.taskId);
|
|
6929
|
+
if (didRemove) removed.push(meta);
|
|
6930
|
+
if (didRemove && (meta.mode === "man" || meta.mode === "manteam")) {
|
|
6931
|
+
await upsertActivePlan(rootDir, {
|
|
6932
|
+
taskId: meta.taskId,
|
|
6933
|
+
status: meta.status,
|
|
6934
|
+
planVersion: meta.planVersion ?? 1
|
|
6935
|
+
});
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6938
|
+
}
|
|
5922
6939
|
if (options.json) {
|
|
5923
6940
|
console.log(
|
|
5924
6941
|
JSON.stringify(
|
|
5925
6942
|
{
|
|
5926
6943
|
dryRun: Boolean(options.dryRun),
|
|
5927
|
-
count: candidates.length,
|
|
5928
|
-
workflows: candidates
|
|
6944
|
+
count: options.dryRun ? candidates.length : removed.length,
|
|
6945
|
+
workflows: options.dryRun ? candidates : removed
|
|
5929
6946
|
},
|
|
5930
6947
|
null,
|
|
5931
6948
|
2
|
|
5932
6949
|
)
|
|
5933
6950
|
);
|
|
5934
|
-
} else if (candidates.length === 0) {
|
|
6951
|
+
} else if ((options.dryRun ? candidates : removed).length === 0) {
|
|
5935
6952
|
console.log("No workflows to clean.");
|
|
5936
6953
|
} else if (options.dryRun) {
|
|
5937
6954
|
console.log(`Would remove ${candidates.length} workflow(s):`);
|
|
5938
6955
|
for (const meta of candidates) console.log(` ${meta.taskId}`);
|
|
5939
6956
|
} else {
|
|
5940
|
-
console.log(`Removed ${
|
|
5941
|
-
}
|
|
5942
|
-
if (!options.dryRun) {
|
|
5943
|
-
for (const meta of candidates) {
|
|
5944
|
-
await deleteWorkflow(rootDir, meta.taskId);
|
|
5945
|
-
}
|
|
6957
|
+
console.log(`Removed ${removed.length} workflow(s).`);
|
|
5946
6958
|
}
|
|
5947
6959
|
return EXIT_OK8;
|
|
5948
6960
|
}
|
|
5949
6961
|
function formatWorkflowRow(meta) {
|
|
5950
|
-
const stepMax = meta.mode
|
|
6962
|
+
const stepMax = maxWorkflowStep(meta.mode);
|
|
5951
6963
|
const step = meta.status === "in_progress" ? `Step ${meta.currentStep}/${stepMax}` : ago(meta.updatedAt);
|
|
5952
|
-
|
|
6964
|
+
const details = [
|
|
6965
|
+
meta.planVersion !== void 0 ? `plan=v${meta.planVersion}` : "",
|
|
6966
|
+
meta.parentTaskId ? `parent=${meta.parentTaskId}` : "",
|
|
6967
|
+
meta.outcome ? `outcome=${meta.outcome}` : "",
|
|
6968
|
+
meta.blockingReason ? `blocked=${meta.blockingReason}` : "",
|
|
6969
|
+
meta.activeChildren && meta.activeChildren.length > 0 ? `children=${meta.activeChildren.map((child) => child.taskId).join(",")}` : ""
|
|
6970
|
+
].filter(Boolean);
|
|
6971
|
+
return `${meta.taskId.padEnd(42)} ${meta.mode.padEnd(7)} ${meta.status.padEnd(11)} ${step}${details.length > 0 ? ` | ${details.join(" | ")}` : ""}`;
|
|
6972
|
+
}
|
|
6973
|
+
function attachActiveChildren(workflows) {
|
|
6974
|
+
return workflows.map((meta) => {
|
|
6975
|
+
if (meta.mode !== "man" && meta.mode !== "manteam") return meta;
|
|
6976
|
+
return {
|
|
6977
|
+
...meta,
|
|
6978
|
+
activeChildren: workflows.filter(
|
|
6979
|
+
(candidate) => candidate.mode === "mamba" && candidate.parentTaskId === meta.taskId && (candidate.status === "in_progress" || candidate.status === "blocked")
|
|
6980
|
+
)
|
|
6981
|
+
};
|
|
6982
|
+
});
|
|
6983
|
+
}
|
|
6984
|
+
function parseExactPositiveInteger(value) {
|
|
6985
|
+
if (!/^\d+$/.test(value)) return null;
|
|
6986
|
+
const parsed = Number(value);
|
|
6987
|
+
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
|
5953
6988
|
}
|
|
5954
6989
|
function parseOlderThan(value) {
|
|
5955
6990
|
if (!value) return void 0;
|
|
@@ -5969,9 +7004,6 @@ function invalidArg(options, message, usage) {
|
|
|
5969
7004
|
}
|
|
5970
7005
|
return EXIT_INVALID_ARG2;
|
|
5971
7006
|
}
|
|
5972
|
-
function isWorkflowStatus(value) {
|
|
5973
|
-
return value === "in_progress" || value === "completed" || value === "abandoned";
|
|
5974
|
-
}
|
|
5975
7007
|
function ago(iso) {
|
|
5976
7008
|
const time = Date.parse(iso);
|
|
5977
7009
|
if (!Number.isFinite(time)) return "unknown";
|
|
@@ -5983,9 +7015,9 @@ function ago(iso) {
|
|
|
5983
7015
|
if (hours < 24) return `${hours}h ago`;
|
|
5984
7016
|
return `${Math.floor(hours / 24)}d ago`;
|
|
5985
7017
|
}
|
|
5986
|
-
async function
|
|
7018
|
+
async function pathExists11(p) {
|
|
5987
7019
|
try {
|
|
5988
|
-
await
|
|
7020
|
+
await access5(p);
|
|
5989
7021
|
return true;
|
|
5990
7022
|
} catch {
|
|
5991
7023
|
return false;
|
|
@@ -5993,7 +7025,9 @@ async function pathExists12(p) {
|
|
|
5993
7025
|
}
|
|
5994
7026
|
|
|
5995
7027
|
// src/cli.ts
|
|
5996
|
-
program.name("mancode").description(
|
|
7028
|
+
program.name("mancode").description(
|
|
7029
|
+
"AI coding agent harness. Modes: solo, man, mamba, manteam, manps."
|
|
7030
|
+
).version(VERSION);
|
|
5997
7031
|
program.command("init").description("Initialize mancode in the current project").option("--force", "Reinstall even if already initialized").option("--yes", "Skip all confirmations (CI mode)").option("--team", "Force enable team mode (MVP-2)").option("--no-team", "Force disable team mode (MVP-2)").option("--style <name>", "Specify aesthetic style (MVP-2)").option("--platform <platform>", "Initial adapter platform (MVP-3)").action(async (options) => {
|
|
5998
7032
|
const code = await init(process.cwd(), options);
|
|
5999
7033
|
process.exitCode = code;
|
|
@@ -6020,7 +7054,10 @@ program.command("uninstall [platform]").description("Remove platform adapter or
|
|
|
6020
7054
|
const code = await uninstall(process.cwd(), platform, options);
|
|
6021
7055
|
process.exitCode = code;
|
|
6022
7056
|
});
|
|
6023
|
-
program.command("workflow <subcommand> [args...]").description("Manage mancode workflows").option("--dry-run", "Preview clean without deleting").option("--older-than <duration>", "Clean workflows older than (e.g. 30d)").option("--step <n>", "Update workflow current step").option("--status <status>", "Update workflow status").option(
|
|
7057
|
+
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(
|
|
7058
|
+
"--parent-task <taskId>",
|
|
7059
|
+
"Parent /man or /manteam workflow for mamba"
|
|
7060
|
+
).option("--blocking-reason <reason>", "Explain why a workflow is blocked").option("--outcome <outcome>", "Set mamba outcome").option("--plan-version <n>", "Set the next man/manteam plan revision").option("--skipped <steps>", "Update skipped steps as comma-separated values").option("--json", "Output as JSON (for scripts)").action(async (subcommand, args, options) => {
|
|
6024
7061
|
const code = await workflow(process.cwd(), subcommand, args ?? [], options);
|
|
6025
7062
|
process.exitCode = code;
|
|
6026
7063
|
});
|
|
@@ -6028,7 +7065,7 @@ program.command("manps [area]").description("Run deterministic preseason health
|
|
|
6028
7065
|
const code = await manps(process.cwd(), area ?? "all", options);
|
|
6029
7066
|
process.exitCode = code;
|
|
6030
7067
|
});
|
|
6031
|
-
program.command("refresh-style").description("
|
|
7068
|
+
program.command("refresh-style").description("Refresh project profile and rescan applicable design tokens").action(async () => {
|
|
6032
7069
|
const code = await refreshStyle(process.cwd());
|
|
6033
7070
|
process.exitCode = code;
|
|
6034
7071
|
});
|