@house365/comate 0.10.2 → 1.0.2-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +104 -64
  3. package/bin/cli.mjs +197 -190
  4. package/bin/doctor.mjs +29 -41
  5. package/bin/init-run.mjs +310 -368
  6. package/bin/init.mjs +53 -71
  7. package/bin/postinstall.mjs +56 -56
  8. package/docs/CHANGELOG-fix-simplification.md +145 -0
  9. package/docs/COMATE_BETA.md +90 -0
  10. package/docs/COMATE_MANUAL.md +239 -665
  11. package/docs/superpowers/plans/2026-10-14-review-fixes.md +48 -0
  12. package/docs/superpowers/plans/2026-10-14-workflow-quality.md +48 -0
  13. package/package.json +54 -55
  14. package/skills/comate/SKILL.md +42 -325
  15. package/skills/comate/reference/archive.md +50 -0
  16. package/skills/comate/reference/build.md +67 -0
  17. package/skills/comate/reference/fix.md +150 -0
  18. package/skills/comate/reference/open.md +162 -0
  19. package/skills/comate/reference/verify.md +123 -0
  20. package/skills/comate/scaffold/comate/README.md +18 -0
  21. package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/architecture/README.md +5 -6
  22. package/skills/comate/scaffold/comate/comate/config.json +10 -0
  23. package/skills/comate/scaffold/comate/knowledge-base/README.md +42 -0
  24. package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/knowledge-base/integrations/_TEMPLATE/README.md +2 -2
  25. package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/knowledge-base/integrations/_TEMPLATE/api-snapshot.md +47 -47
  26. package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/knowledge-base/requirements/_templates/PRD.md +4 -4
  27. package/skills/comate/scripts/comate.mjs +147 -0
  28. package/skills/comate/scripts/lib/acceptance.mjs +112 -0
  29. package/skills/comate/scripts/lib/brief.mjs +50 -0
  30. package/skills/comate/scripts/lib/checks.mjs +178 -0
  31. package/skills/comate/scripts/lib/cmd-archive.mjs +152 -0
  32. package/skills/comate/scripts/lib/cmd-decide.mjs +116 -0
  33. package/skills/comate/scripts/lib/cmd-init.mjs +75 -0
  34. package/skills/comate/scripts/lib/cmd-loop.mjs +273 -0
  35. package/skills/comate/scripts/lib/cmd-open.mjs +161 -0
  36. package/skills/comate/scripts/lib/cmd-status.mjs +121 -0
  37. package/skills/comate/scripts/lib/common.mjs +85 -0
  38. package/skills/comate/scripts/lib/docs.mjs +45 -0
  39. package/skills/comate/scripts/lib/finish.mjs +108 -0
  40. package/skills/comate/scripts/lib/git.mjs +112 -0
  41. package/skills/comate/scripts/lib/md.mjs +85 -0
  42. package/skills/comate/scripts/lib/next.mjs +217 -0
  43. package/skills/comate/scripts/lib/paths.mjs +96 -0
  44. package/skills/comate/scripts/lib/render.mjs +46 -0
  45. package/skills/comate/scripts/lib/spec.mjs +106 -0
  46. package/skills/comate/scripts/lib/state.mjs +128 -0
  47. package/skills/comate/scripts/lib/tasks.mjs +53 -0
  48. package/skills/comate/scripts/lib/templates.mjs +58 -0
  49. package/skills/comate/scripts/lib/util.mjs +108 -0
  50. package/skills/comate-kb-integration/SKILL.md +128 -128
  51. package/skills/comate-kb-integration/scripts/fetch_rendered_doc.py +84 -84
  52. package/bin/cleanup-duplicate-superpowers.mjs +0 -119
  53. package/docs/CLEANUP_SUPERPOWERS.md +0 -128
  54. package/docs/COMATE_BEGINNER_GUIDE.md +0 -188
  55. package/docs/COMATE_INIT_DIRECTORY_GUIDE.md +0 -201
  56. package/docs/SUPERPOWERS_FIX_SUMMARY.md +0 -114
  57. package/skills/comate/reference/decision-points.md +0 -103
  58. package/skills/comate/reference/dirty-worktree.md +0 -58
  59. package/skills/comate/reference/prerequisites.md +0 -89
  60. package/skills/comate/scripts/comate-lib.sh +0 -559
  61. package/skills/comate/scripts/comate.sh +0 -2147
  62. package/skills/comate/tests/comate.bats +0 -1019
  63. package/skills/comate/tests/run.sh +0 -101
  64. package/skills/comate-archive/SKILL.md +0 -63
  65. package/skills/comate-build/SKILL.md +0 -278
  66. package/skills/comate-design/SKILL.md +0 -178
  67. package/skills/comate-fix/SKILL.md +0 -195
  68. package/skills/comate-gate/SKILL.md +0 -316
  69. package/skills/comate-gate/scaffold/docs/README.md +0 -36
  70. package/skills/comate-gate/scaffold/docs/knowledge-base/README.md +0 -47
  71. package/skills/comate-grill/SKILL.md +0 -185
  72. package/skills/comate-open/SKILL.md +0 -230
  73. package/skills/comate-verify/SKILL.md +0 -212
  74. /package/skills/{comate-gate/scaffold/docs → comate/scaffold/comate}/knowledge-base/integrations/README.md +0 -0
package/bin/cli.mjs CHANGED
@@ -1,191 +1,198 @@
1
1
  #!/usr/bin/env node
2
- // comate CLI. Two jobs:
3
- // init — install comate + its prerequisites (OpenSpec CLI, Superpowers)
4
- // * — everything else is forwarded to the `skills` CLI, which already
5
- // handles agent detection, symlinking, update/remove for 70+ agents.
6
- import { fileURLToPath } from 'node:url';
7
- import { dirname } from 'node:path';
8
- import { readFileSync } from 'node:fs';
9
- import { get } from 'node:https';
10
-
11
- import { run, skillsInvocation, SKILLS_TIMEOUT_MS, atLeast } from './init.mjs';
12
-
13
- // Check npm registry for the latest version and notify if an update is available.
14
- async function checkForUpdates(currentVersion) {
15
- // Escape hatch: skip the network round-trip entirely. Keeps `--version`
16
- // deterministic and offline-safe in tests and CI.
17
- if (process.env.COMATE_NO_UPDATE_CHECK) return;
18
- return new Promise((resolve) => {
19
- const req = get('https://registry.npmjs.org/@house365/comate/latest', { timeout: 3000 }, (res) => {
20
- if (res.statusCode !== 200) {
21
- resolve();
22
- return;
23
- }
24
- let data = '';
25
- res.on('data', (chunk) => { data += chunk; });
26
- res.on('end', () => {
27
- try {
28
- const pkg = JSON.parse(data);
29
- const latestVersion = pkg.version;
30
- // Notify only when the registry is strictly newer. A plain `!==` also
31
- // fired when local was ahead of the registry (e.g. mid-release, before
32
- // publish lands), printing a bogus "update available" for an older
33
- // version and polluting `--version` stdout.
34
- if (latestVersion && atLeast(latestVersion, currentVersion) && latestVersion !== currentVersion) {
35
- console.log('');
36
- console.log(`\x1b[33m🔔 有新版本可用:${latestVersion} (当前版本:${currentVersion})\x1b[0m`);
37
- console.log(`\x1b[36m更新命令:npm install -g @house365/comate@latest\x1b[0m`);
38
- console.log('');
39
- }
40
- } catch {
41
- // Ignore parse errors
42
- }
43
- resolve();
44
- });
45
- });
46
- req.on('error', () => resolve());
47
- req.on('timeout', () => {
48
- req.destroy();
49
- resolve();
50
- });
51
- });
52
- }
53
-
54
- // comate 自身的子命令(在下方被先取处理,不会透传给 skills CLI)。
55
- const COMATE_COMMANDS = ['init', 'doctor', 'update', 'usage', 'cleanup'];
56
-
57
- // 允许透传给 skills CLI 的子命令(含别名)。留在这里的都是 skills CLI 真实存在
58
- // 的命令,未列入的一律视为未知命令并命中 --help。init/update 已被 comate 先取,
59
- // 故不在此列。
60
- const SKILLS_PASSTHROUGH = new Set([
61
- 'add', 'a',
62
- 'use',
63
- 'remove', 'rm',
64
- 'list', 'ls',
65
- 'find',
66
- 'upgrade',
67
- 'experimental_install',
68
- 'experimental_sync',
69
- ]);
70
-
71
- // Flags accepted on a bare `comate [flags]` invocation — these forward to the
72
- // implicit `skills add`. Anything else starting with `-` is a typo (e.g.
73
- // `--vasdasd`) and must hit help instead of silently installing everything.
74
- // --version/-v and --help/-h are intercepted earlier, so they need not be here.
75
- const ADD_FLAGS = new Set([
76
- '-g', '--global',
77
- '-a', '--agent',
78
- '-s', '--skill',
79
- '-l', '--list',
80
- '-y', '--yes',
81
- '--copy',
82
- '--metadata',
83
- '--subagent',
84
- '--all',
85
- '--full-depth',
86
- ]);
87
-
88
- const HELP_TEXT = `comate — SDD (规约驱动开发) 的 AI 开发编排器
89
-
90
- 用法:
91
- comate 安装/更新本包全部 comate skill 到当前 agent
92
- comate init 安装 comate 和依赖到用户级,并在当前项目创建 openspec/
93
- comate doctor 健康检查
94
- comate update 更新 comate 到最新版本
95
- comate usage 查看本项目 token 用量(--global 看全部;参数透传给 ccusage)
96
- comate cleanup 清理重复的 superpowers 安装(当 plugin 版本已存在时)
97
- comate --help, -h 显示本帮助
98
- comate --version, -v 显示版本号
99
-
100
- 管理 skill(透传给 skills CLI):
101
- comate add <包> 添加 skill 包
102
- comate remove [名] 移除已装 skill(别名 rm)
103
- comate list 列出已装 skill(别名 ls)
104
- comate find [关键词] 搜索 skill
105
-
106
- 装好后在 agent 中输入 /comate 开始使用。`;
107
-
108
- function printHelp(stream = console.log) {
109
- stream(HELP_TEXT);
110
- }
111
-
112
- const pkgRoot = dirname(dirname(fileURLToPath(import.meta.url)));
113
- const args = process.argv.slice(2);
114
- const packageVersion = JSON.parse(readFileSync(`${pkgRoot}/package.json`, 'utf8')).version;
115
-
116
- if (args[0] === '--version' || args[0] === '-v') {
117
- console.log(packageVersion);
118
- // Await the check: without it, process.exit(0) fires synchronously and the
119
- // process dies before the registry request resolves, so the update notice
120
- // never prints. The check has its own 3s timeout, so a dead network stalls
121
- // this by at most 3s.
122
- await checkForUpdates(packageVersion).catch(() => {
123
- // Silently ignore network errors
124
- });
125
- process.exit(0);
126
- }
127
-
128
- if (args[0] === 'init') {
129
- const { init } = await import('./init-run.mjs');
130
- process.exit(await init(pkgRoot));
131
- }
132
-
133
- if (args[0] === 'doctor') {
134
- const { doctor } = await import('./doctor.mjs');
135
- process.exit(doctor(args.slice(1)));
136
- }
137
-
138
- if (args[0] === 'update') {
139
- const { update } = await import('./update.mjs');
140
- process.exit(await update(packageVersion));
141
- }
142
-
143
- if (args[0] === 'usage') {
144
- const { usage } = await import('./usage.mjs');
145
- process.exit(await usage(args.slice(1), { pkgRoot }));
146
- }
147
-
148
- if (args[0] === 'cleanup') {
149
- const { cleanup } = await import('./cleanup-duplicate-superpowers.mjs');
150
- process.exit(cleanup());
151
- }
152
-
153
- if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
154
- printHelp();
155
- process.exit(0);
156
- }
157
-
158
- // A leading token that is neither a flag, a comate command, nor a known skills
159
- // passthrough command is an unknown command. Print help to stderr and exit 1 so
160
- // typos surface as usage guidance instead of an opaque skills CLI error.
161
- if (args.length && !args[0].startsWith('-')) {
162
- const cmd = args[0];
163
- if (!COMATE_COMMANDS.includes(cmd) && !SKILLS_PASSTHROUGH.has(cmd)) {
164
- console.error(`comate: 未知命令 '${cmd}'\n`);
165
- printHelp(console.error);
166
- process.exit(1);
167
- }
168
- } else if (args[0]?.startsWith('-')) {
169
- // Bare-flags mode: every arg is forwarded to the implicit `skills add`. Any
170
- // dash-prefixed arg that isn't a known install flag is a typo (e.g.
171
- // --vasdasd). Without this it fell through and silently installed everything.
172
- // Flag values (e.g. `-a claude-code`) don't start with `-`, so they're safe.
173
- const bad = args.find((a) => a.startsWith('-') && !ADD_FLAGS.has(a));
174
- if (bad) {
175
- console.error(`comate: 未知选项 '${bad}'\n`);
176
- printHelp(console.error);
177
- process.exit(1);
178
- }
179
- }
180
-
181
- // No args, or only flags (-g, --list, …) = install every comate skill from this
182
- // package, forwarding the flags. A leading passthrough subcommand goes straight
183
- // through, so add/remove/list all still work.
184
- const argv =
185
- args.length && !args[0].startsWith('-') ? args : ['add', pkgRoot, '--skill', '*', ...args];
186
-
187
- const result = run(skillsInvocation(pkgRoot, argv), SKILLS_TIMEOUT_MS);
188
- if (!result.ok) {
189
- console.error(`comate: skills CLI 执行失败 — ${result.reason}`);
190
- process.exit(1);
191
- }
2
+ // comate CLI. Three jobs:
3
+ // init — install the comate skill and lay down the comate/ scaffold
4
+ // status / doctor / import-specs
5
+ // — forwarded to the installed skill's scripts/comate.mjs, so
6
+ // the CLI never carries a second copy of the state machine
7
+ // * — everything else is forwarded to the `skills` CLI, which
8
+ // handles agent detection, symlinking, update/remove.
9
+ import { fileURLToPath } from 'node:url';
10
+ import { dirname } from 'node:path';
11
+ import { readFileSync } from 'node:fs';
12
+ import { get } from 'node:https';
13
+
14
+ import { run, skillsInvocation, SKILLS_TIMEOUT_MS, atLeast } from './init.mjs';
15
+
16
+ // Check npm registry for the latest version and notify if an update is available.
17
+ async function checkForUpdates(currentVersion) {
18
+ // Escape hatch: skip the network round-trip entirely. Keeps `--version`
19
+ // deterministic and offline-safe in tests and CI.
20
+ if (process.env.COMATE_NO_UPDATE_CHECK) return;
21
+ return new Promise((resolve) => {
22
+ const req = get('https://registry.npmjs.org/@house365/comate/latest', { timeout: 3000 }, (res) => {
23
+ if (res.statusCode !== 200) {
24
+ resolve();
25
+ return;
26
+ }
27
+ let data = '';
28
+ res.on('data', (chunk) => { data += chunk; });
29
+ res.on('end', () => {
30
+ try {
31
+ const pkg = JSON.parse(data);
32
+ const latestVersion = pkg.version;
33
+ // Notify only when the registry is strictly newer. A plain `!==` also
34
+ // fired when local was ahead of the registry (e.g. mid-release, before
35
+ // publish lands), printing a bogus "update available" for an older
36
+ // version and polluting `--version` stdout.
37
+ if (latestVersion && atLeast(latestVersion, currentVersion) && latestVersion !== currentVersion) {
38
+ console.log('');
39
+ console.log(`\x1b[33m🔔 有新版本可用:${latestVersion} (当前版本:${currentVersion})\x1b[0m`);
40
+ console.log(`\x1b[36m更新命令:npm install -g @house365/comate@latest\x1b[0m`);
41
+ console.log('');
42
+ }
43
+ } catch {
44
+ // Ignore parse errors
45
+ }
46
+ resolve();
47
+ });
48
+ });
49
+ req.on('error', () => resolve());
50
+ req.on('timeout', () => {
51
+ req.destroy();
52
+ resolve();
53
+ });
54
+ });
55
+ }
56
+
57
+ // comate 自身的子命令(在下方被先取处理,不会透传给 skills CLI)。
58
+ const COMATE_COMMANDS = ['init', 'doctor', 'status', 'import-specs', 'update', 'usage'];
59
+
60
+ // 允许透传给 skills CLI 的子命令(含别名)。留在这里的都是 skills CLI 真实存在
61
+ // 的命令,未列入的一律视为未知命令并命中 --help。init/update 已被 comate 先取,
62
+ // 故不在此列。
63
+ const SKILLS_PASSTHROUGH = new Set([
64
+ 'add', 'a',
65
+ 'use',
66
+ 'remove', 'rm',
67
+ 'list', 'ls',
68
+ 'find',
69
+ 'upgrade',
70
+ 'experimental_install',
71
+ 'experimental_sync',
72
+ ]);
73
+
74
+ // Flags accepted on a bare `comate [flags]` invocation — these forward to the
75
+ // implicit `skills add`. Anything else starting with `-` is a typo (e.g.
76
+ // `--vasdasd`) and must hit help instead of silently installing everything.
77
+ // --version/-v and --help/-h are intercepted earlier, so they need not be here.
78
+ const ADD_FLAGS = new Set([
79
+ '-g', '--global',
80
+ '-a', '--agent',
81
+ '-s', '--skill',
82
+ '-l', '--list',
83
+ '-y', '--yes',
84
+ '--copy',
85
+ '--metadata',
86
+ '--subagent',
87
+ '--all',
88
+ '--full-depth',
89
+ ]);
90
+
91
+ const HELP_TEXT = `comate — SDD (规约驱动开发) 的 AI 开发编排器
92
+
93
+ 用法:
94
+ comate 安装/更新本包全部 comate skill 到当前 agent
95
+ comate init 安装 comate skill 到用户级,并在当前项目创建 comate/
96
+ comate init --migrate [--dry-run]
97
+ 把 0.x 的 docs/knowledge-base、docs/architecture 移到 comate/
98
+ comate import-specs [--dry-run]
99
+ 把 openspec/specs 主规格导入 comate/comate/specs
100
+ comate status [名] 查看当前项目的 change 与下一步
101
+ comate doctor 健康检查
102
+ comate update 更新 comate 到最新版本
103
+ comate usage 查看本项目 token 用量(--global 看全部;参数透传给 ccusage)
104
+ comate --help, -h 显示本帮助
105
+ comate --version, -v 显示版本号
106
+
107
+ 管理 skill(透传给 skills CLI):
108
+ comate add <包> 添加 skill 包
109
+ comate remove [名] 移除已装 skill(别名 rm)
110
+ comate list 列出已装 skill(别名 ls)
111
+ comate find [关键词] 搜索 skill
112
+
113
+ 装好后在 agent 中输入 /comate 开始使用。`;
114
+
115
+ function printHelp(stream = console.log) {
116
+ stream(HELP_TEXT);
117
+ }
118
+
119
+ const pkgRoot = dirname(dirname(fileURLToPath(import.meta.url)));
120
+ const args = process.argv.slice(2);
121
+ const packageVersion = JSON.parse(readFileSync(`${pkgRoot}/package.json`, 'utf8')).version;
122
+
123
+ if (args[0] === '--version' || args[0] === '-v') {
124
+ console.log(packageVersion);
125
+ // Await the check: without it, process.exit(0) fires synchronously and the
126
+ // process dies before the registry request resolves, so the update notice
127
+ // never prints. The check has its own 3s timeout, so a dead network stalls
128
+ // this by at most 3s.
129
+ await checkForUpdates(packageVersion).catch(() => {
130
+ // Silently ignore network errors
131
+ });
132
+ process.exit(0);
133
+ }
134
+
135
+ if (args[0] === 'init') {
136
+ const { init } = await import('./init-run.mjs');
137
+ process.exit(await init(pkgRoot, args.slice(1)));
138
+ }
139
+
140
+ if (args[0] === 'doctor') {
141
+ const { doctor } = await import('./doctor.mjs');
142
+ process.exit(doctor(args.slice(1)));
143
+ }
144
+
145
+ if (args[0] === 'status' || args[0] === 'import-specs') {
146
+ const { forward } = await import('./doctor.mjs');
147
+ process.exit(forward(args[0], args.slice(1)));
148
+ }
149
+
150
+ if (args[0] === 'update') {
151
+ const { update } = await import('./update.mjs');
152
+ process.exit(await update(packageVersion));
153
+ }
154
+
155
+ if (args[0] === 'usage') {
156
+ const { usage } = await import('./usage.mjs');
157
+ process.exit(await usage(args.slice(1), { pkgRoot }));
158
+ }
159
+
160
+ if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
161
+ printHelp();
162
+ process.exit(0);
163
+ }
164
+
165
+ // A leading token that is neither a flag, a comate command, nor a known skills
166
+ // passthrough command is an unknown command. Print help to stderr and exit 1 so
167
+ // typos surface as usage guidance instead of an opaque skills CLI error.
168
+ if (args.length && !args[0].startsWith('-')) {
169
+ const cmd = args[0];
170
+ if (!COMATE_COMMANDS.includes(cmd) && !SKILLS_PASSTHROUGH.has(cmd)) {
171
+ console.error(`comate: 未知命令 '${cmd}'\n`);
172
+ printHelp(console.error);
173
+ process.exit(1);
174
+ }
175
+ } else if (args[0]?.startsWith('-')) {
176
+ // Bare-flags mode: every arg is forwarded to the implicit `skills add`. Any
177
+ // dash-prefixed arg that isn't a known install flag is a typo (e.g.
178
+ // --vasdasd). Without this it fell through and silently installed everything.
179
+ // Flag values (e.g. `-a claude-code`) don't start with `-`, so they're safe.
180
+ const bad = args.find((a) => a.startsWith('-') && !ADD_FLAGS.has(a));
181
+ if (bad) {
182
+ console.error(`comate: 未知选项 '${bad}'\n`);
183
+ printHelp(console.error);
184
+ process.exit(1);
185
+ }
186
+ }
187
+
188
+ // No args, or only flags (-g, --list, …) = install every comate skill from this
189
+ // package, forwarding the flags. A leading passthrough subcommand goes straight
190
+ // through, so add/remove/list all still work.
191
+ const argv =
192
+ args.length && !args[0].startsWith('-') ? args : ['add', pkgRoot, '--skill', '*', ...args];
193
+
194
+ const result = run(skillsInvocation(pkgRoot, argv), SKILLS_TIMEOUT_MS);
195
+ if (!result.ok) {
196
+ console.error(`comate: skills CLI 执行失败 — ${result.reason}`);
197
+ process.exit(1);
198
+ }
package/bin/doctor.mjs CHANGED
@@ -1,11 +1,11 @@
1
- // `comate doctor` — run the installed comate.sh health check.
1
+ // `comate doctor` — run the installed comate.mjs health check.
2
2
  //
3
- // ponytail: comate.sh already implements every check. This only locates the
4
- // installed copy so the user doesn't have to remember the path.
3
+ // comate.mjs already implements every check. This only locates the installed
4
+ // copy (the one the agent would load) so the user doesn't need the path.
5
5
  import { spawnSync } from 'node:child_process';
6
6
  import { existsSync, readFileSync } from 'node:fs';
7
7
  import { createRequire } from 'node:module';
8
- import { delimiter, dirname, join, resolve } from 'node:path';
8
+ import { dirname, join, resolve } from 'node:path';
9
9
  import { fileURLToPath } from 'node:url';
10
10
 
11
11
  import { atLeast, capture, npmInvocation } from './init.mjs';
@@ -21,60 +21,48 @@ function skillRoots() {
21
21
 
22
22
  export function findComateScript(roots = skillRoots()) {
23
23
  for (const root of roots) {
24
- const script = join(root, 'comate', 'scripts', 'comate.sh');
24
+ const script = join(root, 'comate', 'scripts', 'comate.mjs');
25
25
  if (existsSync(script)) return script;
26
26
  }
27
27
  return null;
28
28
  }
29
29
 
30
- // On Windows, bare `bash` usually resolves to System32\bash.exe — the WSL
31
- // launcher, which cannot open `D:\...` paths (it eats the backslashes and
32
- // reports "No such file or directory"). Find Git's bash instead, next to the
33
- // git.exe already on PATH, then the standard install dirs.
34
- export function findBash(env = process.env, exists = existsSync) {
35
- if (process.platform !== 'win32') return 'bash';
36
-
37
- const gitDirs = (env.PATH ?? env.Path ?? '').split(delimiter).filter(Boolean);
38
- const candidates = [
39
- // <git>/cmd/git.exe or <git>/mingw64/bin/git.exe -> <git>/bin/bash.exe
40
- ...gitDirs.flatMap((d) =>
41
- exists(join(d, 'git.exe')) ? [resolve(d, '..', 'bin', 'bash.exe'), resolve(d, '..', '..', 'bin', 'bash.exe')] : [],
42
- ),
43
- join(env.ProgramFiles ?? 'C:\\Program Files', 'Git', 'bin', 'bash.exe'),
44
- join(env['ProgramFiles(x86)'] ?? 'C:\\Program Files (x86)', 'Git', 'bin', 'bash.exe'),
45
- join(env.LOCALAPPDATA ?? '', 'Programs', 'Git', 'bin', 'bash.exe'),
46
- ];
47
- return candidates.find(exists) ?? null;
48
- }
30
+ // Installed copy first (the one the agent loads); the package's own copy only
31
+ // when nothing is installed yet, so `comate status` still works right after npm i.
32
+ const scriptOrBundled = () => findComateScript() ?? [join(defaultPkgRoot(), 'skills', 'comate', 'scripts', 'comate.mjs')].find(existsSync) ?? null;
49
33
 
50
- export function doctor(argv) {
51
- const script = findComateScript();
34
+ // `comate status` / `comate import-specs`: run the same state machine the agent uses.
35
+ export function forward(command, argv) {
36
+ const script = scriptOrBundled();
52
37
  if (!script) {
53
- console.error('找不到已安装的 comate。先运行: comate init');
38
+ console.error('找不到 comate 脚本。先运行: comate init');
39
+ return 1;
40
+ }
41
+ const { status, error } = spawnSync(process.execPath, [script, command, ...argv], { stdio: 'inherit' });
42
+ if (error) {
43
+ console.error(`无法执行 ${script}:${error.message}`);
54
44
  return 1;
55
45
  }
46
+ return status ?? 1;
47
+ }
56
48
 
57
- const bash = findBash();
58
- if (!bash) {
59
- console.error('找不到 bash — comate 的脚本需要 bash。请安装 Git for Windows。');
49
+ export function doctor(argv) {
50
+ const script = scriptOrBundled();
51
+ if (!script) {
52
+ console.error('找不到已安装的 comate。先运行: comate init');
60
53
  return 1;
61
54
  }
62
55
 
63
- // comate.sh resolves sibling skills relative to the skills root, so run it
64
- // from there rather than from wherever the user happens to be.
65
- const skillsRoot = join(script, '..', '..', '..');
66
- const { status, error } = spawnSync(bash, [script, 'doctor', ...argv], {
67
- cwd: skillsRoot,
68
- stdio: 'inherit',
69
- });
56
+ // Same node that runs this CLI — no shell, no bash, works the same on Windows.
57
+ const { status, error } = spawnSync(process.execPath, [script, 'doctor', ...argv], { stdio: 'inherit' });
70
58
  if (error) {
71
- console.error(`无法执行 bash (${bash}) — comate 的脚本需要 bash。${error.message}`);
59
+ console.error(`无法执行 ${script}:${error.message}`);
72
60
  return 1;
73
61
  }
74
62
 
75
- // ccusage is a node dependency (for `comate usage`), not a bash-side tool, so
76
- // it lives here rather than in comate.sh's doctor. A miss is a warning, never
77
- // a failure: usage falls back to `npx ccusage@latest`, so it still works.
63
+ // ccusage is a dependency of the npm CLI (for `comate usage`), not of the
64
+ // skill script, so it is checked here. A miss is a warning, never a
65
+ // failure: usage falls back to `npx ccusage@latest`, so it still works.
78
66
  reportCcusage();
79
67
 
80
68
  // Version check is the only place a user learns a newer comate exists: the