@house365/comate 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/init-run.mjs +77 -8
- package/bin/init.mjs +14 -4
- package/package.json +1 -1
package/bin/init-run.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// The `init` flow: install whatever the current project is missing.
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { join, resolve } from 'node:path';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
MIN_OPENSPEC,
|
|
7
7
|
NPM_TIMEOUT_MS,
|
|
8
8
|
SKILLS_TIMEOUT_MS,
|
|
9
|
+
capture,
|
|
9
10
|
npmInvocation,
|
|
10
11
|
openSpecState,
|
|
11
12
|
run,
|
|
@@ -82,8 +83,12 @@ export async function init(pkgRoot) {
|
|
|
82
83
|
if (superpowersInstalled('global') || superpowersInstalled('project')) {
|
|
83
84
|
results.push(['Superpowers', 'skipped', '已安装']);
|
|
84
85
|
} else {
|
|
85
|
-
console.log('\n→ 安装 Superpowers
|
|
86
|
-
const r = run(
|
|
86
|
+
console.log('\n→ 安装 Superpowers');
|
|
87
|
+
const r = run(
|
|
88
|
+
skillsInvocation(pkgRoot, ['add', 'obra/superpowers', '-g', '-y']),
|
|
89
|
+
SKILLS_TIMEOUT_MS,
|
|
90
|
+
{ quiet: true },
|
|
91
|
+
);
|
|
87
92
|
// It clones from GitHub, so a bare exit code hides the usual cause: no
|
|
88
93
|
// network. Say so rather than leaving the user with "exit code 1".
|
|
89
94
|
const note = r.ok
|
|
@@ -92,11 +97,18 @@ export async function init(pkgRoot) {
|
|
|
92
97
|
results.push(['Superpowers', r.ok ? 'installed' : 'failed', note]);
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
// 3. comate itself.
|
|
100
|
+
// 3. comate itself. The skills CLI reports per-agent failures without ever
|
|
101
|
+
// setting a non-zero exit code, and we swallow its output — so a zero status
|
|
102
|
+
// is not evidence the skill landed. Check the file.
|
|
96
103
|
console.log('\n→ 安装 comate skills');
|
|
97
104
|
const comateArgs = ['add', pkgRoot, '--skill', '*', '-g', '-y'];
|
|
98
|
-
const r = run(skillsInvocation(pkgRoot, comateArgs), SKILLS_TIMEOUT_MS);
|
|
99
|
-
|
|
105
|
+
const r = run(skillsInvocation(pkgRoot, comateArgs), SKILLS_TIMEOUT_MS, { quiet: true });
|
|
106
|
+
const landed = existsSync(join(home, '.agents', 'skills', 'comate', 'SKILL.md'));
|
|
107
|
+
if (r.ok && !landed) {
|
|
108
|
+
results.push(['comate', 'failed', `未装到 ${join(home, '.agents', 'skills')},重跑看详情`]);
|
|
109
|
+
} else {
|
|
110
|
+
results.push(['comate', r.ok ? 'installed' : 'failed', r.ok ? '' : r.reason]);
|
|
111
|
+
}
|
|
100
112
|
|
|
101
113
|
// 4. openspec/ project structure. Not optional: comate's open and archive
|
|
102
114
|
// phases read it, so an install without it cannot actually run. An existing
|
|
@@ -105,8 +117,17 @@ export async function init(pkgRoot) {
|
|
|
105
117
|
results.push(['openspec 结构', 'skipped', '已存在']);
|
|
106
118
|
} else {
|
|
107
119
|
console.log('\n→ 初始化 openspec/ 项目结构');
|
|
120
|
+
// --tools is not optional in practice: openspec derives the tool list by
|
|
121
|
+
// sniffing existing agent config dirs, so a fresh project (no .claude etc.)
|
|
122
|
+
// aborts with "No tools detected and no --tools flag provided". comate's
|
|
123
|
+
// skills are agent-agnostic and only openspec/ itself matters here, so ask
|
|
124
|
+
// for none rather than guessing which agents the user wants wired up.
|
|
108
125
|
const r2 = run(
|
|
109
|
-
{
|
|
126
|
+
{
|
|
127
|
+
command: 'openspec',
|
|
128
|
+
args: ['init', '.', '--tools', 'none'],
|
|
129
|
+
shell: process.platform === 'win32',
|
|
130
|
+
},
|
|
110
131
|
NPM_TIMEOUT_MS,
|
|
111
132
|
);
|
|
112
133
|
results.push(['openspec 结构', r2.ok ? 'installed' : 'failed', r2.ok ? '' : r2.reason]);
|
|
@@ -127,6 +148,54 @@ function report(results) {
|
|
|
127
148
|
console.error(`\n${failed.length} 项失败,修好后重跑 comate init。`);
|
|
128
149
|
return 1;
|
|
129
150
|
}
|
|
130
|
-
console.log(
|
|
151
|
+
console.log(banner(pkgVersion()));
|
|
152
|
+
console.log('在 agent 中输入 /comate 开始使用,或跑 comate doctor 自检。\n');
|
|
131
153
|
return 0;
|
|
132
154
|
}
|
|
155
|
+
|
|
156
|
+
// ponytail: read package.json rather than hardcoding — npm version bumps the
|
|
157
|
+
// file, and a stale literal in the banner is worse than no version at all.
|
|
158
|
+
export function pkgVersion(root = resolve(import.meta.dirname, '..')) {
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version;
|
|
161
|
+
} catch {
|
|
162
|
+
return '';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Box-drawing glyphs need a UTF-8 capable terminal. Windows consoles still
|
|
167
|
+
// default to a legacy codepage where they render as mojibake, so the wordmark
|
|
168
|
+
// only goes out when the codepage says otherwise; everything else gets the
|
|
169
|
+
// plain line. Piped output is skipped too — logos belong on screens, not in
|
|
170
|
+
// logs or CI transcripts.
|
|
171
|
+
export function banner(version, { tty = process.stdout.isTTY, utf8 = unicodeSafe() } = {}) {
|
|
172
|
+
const tag = `comate${version ? ` v${version}` : ''}`;
|
|
173
|
+
const plain = `\n ${tag} — OpenSpec + Superpowers 规约驱动开发编排器\n`;
|
|
174
|
+
if (!tty || !utf8) return plain;
|
|
175
|
+
|
|
176
|
+
const art = [
|
|
177
|
+
'██████╗ ██████╗ ███╗ ███╗ █████╗ ███████╗',
|
|
178
|
+
'██╔═══╝██╔═══██╗████╗ ████║██╔══██╗╚══██╔══╝',
|
|
179
|
+
'██║ ██║ ██║██╔████╔██║███████║ ██║',
|
|
180
|
+
'██║ ██║ ██║██║╚██╔╝██║██╔══██║ ██║',
|
|
181
|
+
'╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ██║ ██║',
|
|
182
|
+
' ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝',
|
|
183
|
+
];
|
|
184
|
+
const cyan = (s) => (process.env.NO_COLOR ? s : `\x1B[36m${s}\x1B[0m`);
|
|
185
|
+
const dim = (s) => (process.env.NO_COLOR ? s : `\x1B[2m${s}\x1B[0m`);
|
|
186
|
+
return [
|
|
187
|
+
'',
|
|
188
|
+
...art.map((line) => ` ${cyan(line)}`),
|
|
189
|
+
'',
|
|
190
|
+
` ${dim(`${tag} — OpenSpec + Superpowers 规约驱动开发编排器`)}`,
|
|
191
|
+
'',
|
|
192
|
+
].join('\n');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// POSIX terminals are UTF-8 in practice. Windows consoles are not: the legacy
|
|
196
|
+
// codepage (936, 437, …) renders box-drawing glyphs as mojibake, so ask chcp
|
|
197
|
+
// and only claim UTF-8 on 65001.
|
|
198
|
+
function unicodeSafe() {
|
|
199
|
+
if (process.platform !== 'win32') return true;
|
|
200
|
+
return /65001/.test(capture('chcp', []) ?? '');
|
|
201
|
+
}
|
package/bin/init.mjs
CHANGED
|
@@ -75,14 +75,24 @@ function parsePackOutput(stdout) {
|
|
|
75
75
|
throw new Error(`no usable file list in npm pack output:\n${stdout}`);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
// quiet buffers the child's output and prints it only on failure. The skills
|
|
79
|
+
// CLI reports a per-agent failure for every agent that has no user-level skills
|
|
80
|
+
// dir (PromptScript et al.) — cosmetic, since it neither affects the exit code
|
|
81
|
+
// nor the agents that did get installed, but it reads like a broken install.
|
|
82
|
+
// init prints its own summary, so on success the child's output adds nothing.
|
|
83
|
+
function run({ command, args, shell }, timeout, { quiet = false } = {}) {
|
|
84
|
+
const { status, error, stdout, stderr } = spawnSync(command, args, {
|
|
85
|
+
stdio: quiet ? ['ignore', 'pipe', 'pipe'] : 'inherit',
|
|
86
|
+
encoding: quiet ? 'utf8' : undefined,
|
|
81
87
|
timeout,
|
|
82
88
|
...(shell ? { shell: true } : {}),
|
|
83
89
|
});
|
|
84
90
|
if (error) return { ok: false, reason: error.message };
|
|
85
|
-
if (status !== 0)
|
|
91
|
+
if (status !== 0) {
|
|
92
|
+
// Without this the user gets a bare "exit code 1" and no way to diagnose.
|
|
93
|
+
if (quiet) process.stderr.write((stdout ?? '') + (stderr ?? ''));
|
|
94
|
+
return { ok: false, reason: `exit code ${String(status)}` };
|
|
95
|
+
}
|
|
86
96
|
return { ok: true };
|
|
87
97
|
}
|
|
88
98
|
|