cli-five 0.1.3 → 0.1.5
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/package.json +3 -2
- package/src/steps/skills.mjs +183 -13
- package/templates/.github/agents/coder.agent.md +4 -4
- package/templates/.github/agents/designer.agent.md +4 -4
- package/templates/.github/agents/orchestrator.agent.md +12 -4
- package/templates/.github/agents/planner.agent.md +4 -4
- package/templates/.github/agents/reviewer.agent.md +4 -4
- package/templates/.github/copilot-instructions.md.tmpl +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cli-five",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Code Like I'm Five — scaffold a 5-agent VS Code Copilot team into any repo.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"kleur": "^4.1.5",
|
|
33
|
-
"prompts": "^2.4.2"
|
|
33
|
+
"prompts": "^2.4.2",
|
|
34
|
+
"skills": "^1.5.0"
|
|
34
35
|
}
|
|
35
36
|
}
|
package/src/steps/skills.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
2
|
import { spawn, execFileSync } from 'node:child_process';
|
|
3
|
+
import { existsSync, mkdirSync, readdirSync, renameSync, rmSync } from 'node:fs';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
3
6
|
import { log } from '../util/log.mjs';
|
|
4
7
|
|
|
5
8
|
// Well-known skill repos matched by stack term → repo + suggested skill names
|
|
@@ -13,6 +16,83 @@ const SKILL_CATALOG = [
|
|
|
13
16
|
{ terms: ['rust'], repo: 'anthropics/skills', skills: ['code-review'] },
|
|
14
17
|
];
|
|
15
18
|
|
|
19
|
+
// ── Environment detection ─────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
function resolveSkillsBin() {
|
|
22
|
+
try {
|
|
23
|
+
const require = createRequire(import.meta.url);
|
|
24
|
+
const pkgPath = require.resolve('skills/package.json');
|
|
25
|
+
const bin = join(dirname(pkgPath), 'bin', 'cli.mjs');
|
|
26
|
+
return existsSync(bin) ? bin : null;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function whichVersion(cmd) {
|
|
33
|
+
try {
|
|
34
|
+
return execFileSync(cmd, ['--version'], { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
35
|
+
.toString()
|
|
36
|
+
.trim()
|
|
37
|
+
.split('\n')[0];
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Detect whether the target workspace itself uses pnpm. */
|
|
44
|
+
function projectUsesPnpm(cwd) {
|
|
45
|
+
return existsSync(join(cwd, 'pnpm-lock.yaml')) || existsSync(join(cwd, 'pnpm-workspace.yaml'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Detect the runtime environment.
|
|
50
|
+
* Returns { runner, label } where runner is a function (skillsArgs, cwd) → {cmd, args}.
|
|
51
|
+
*/
|
|
52
|
+
function detectEnv(cwd) {
|
|
53
|
+
const bundledBin = resolveSkillsBin();
|
|
54
|
+
const pnpmVer = whichVersion('pnpm');
|
|
55
|
+
const npxVer = whichVersion('npx');
|
|
56
|
+
const projectPnpm = projectUsesPnpm(cwd);
|
|
57
|
+
|
|
58
|
+
// ── Terse status lines ────────────────────────────────────────────
|
|
59
|
+
log.info(`skills CLI (bundled) ${bundledBin ? '✓' : '✗ not resolved'}`);
|
|
60
|
+
log.info(`pnpm ${pnpmVer ? `✓ ${pnpmVer}` : '✗ not found'}${projectPnpm ? ' (project uses pnpm)' : ''}`);
|
|
61
|
+
log.info(`npx ${npxVer ? `✓ ${npxVer}` : '✗ not found'}`);
|
|
62
|
+
|
|
63
|
+
// ── Priority: bundled > pnpm (if project or system) > npx ─────────
|
|
64
|
+
if (bundledBin) {
|
|
65
|
+
return {
|
|
66
|
+
label: 'bundled skills CLI',
|
|
67
|
+
runner: (skillsArgs) => ({ cmd: process.execPath, args: [bundledBin, ...skillsArgs] }),
|
|
68
|
+
pnpmVer,
|
|
69
|
+
npxVer,
|
|
70
|
+
projectPnpm,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (pnpmVer) {
|
|
74
|
+
return {
|
|
75
|
+
label: 'pnpm dlx',
|
|
76
|
+
runner: (skillsArgs) => ({ cmd: 'pnpm', args: ['dlx', 'skills', ...skillsArgs] }),
|
|
77
|
+
pnpmVer,
|
|
78
|
+
npxVer,
|
|
79
|
+
projectPnpm,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (npxVer) {
|
|
83
|
+
return {
|
|
84
|
+
label: 'npx',
|
|
85
|
+
runner: (skillsArgs) => ({ cmd: 'npx', args: ['-y', 'skills', ...skillsArgs] }),
|
|
86
|
+
pnpmVer,
|
|
87
|
+
npxVer,
|
|
88
|
+
projectPnpm,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Main entry ────────────────────────────────────────────────────────
|
|
95
|
+
|
|
16
96
|
export async function skillDiscovery({ cwd, answers, args }) {
|
|
17
97
|
if (!args.skills) {
|
|
18
98
|
log.dim('Skipping skills discovery (--no-skills).');
|
|
@@ -23,15 +103,43 @@ export async function skillDiscovery({ cwd, answers, args }) {
|
|
|
23
103
|
return;
|
|
24
104
|
}
|
|
25
105
|
|
|
106
|
+
// ── Environment checks ──────────────────────────────────────────────
|
|
107
|
+
const env = detectEnv(cwd);
|
|
108
|
+
|
|
109
|
+
if (!env) {
|
|
110
|
+
log.warn('Cannot run skills CLI: no bundled binary, pnpm, or npx found.');
|
|
111
|
+
log.warn('Install Node.js (includes npx) or pnpm, then re-run.');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Offer pnpm if the user doesn't have it
|
|
116
|
+
if (!env.pnpmVer) {
|
|
117
|
+
const { wantPnpm } = await prompts({
|
|
118
|
+
type: 'confirm',
|
|
119
|
+
name: 'wantPnpm',
|
|
120
|
+
message: 'pnpm not found. Install it? (faster, stricter deps, saves disk)',
|
|
121
|
+
initial: false,
|
|
122
|
+
});
|
|
123
|
+
if (wantPnpm) {
|
|
124
|
+
try {
|
|
125
|
+
execFileSync('npm', ['install', '-g', 'pnpm'], { stdio: 'inherit' });
|
|
126
|
+
log.ok('pnpm installed.');
|
|
127
|
+
} catch (err) {
|
|
128
|
+
log.warn(`pnpm install failed: ${err.message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
log.ok(`Running skills via ${env.label}`);
|
|
26
134
|
log.raw('');
|
|
27
|
-
|
|
135
|
+
|
|
136
|
+
// ── Skill discovery ─────────────────────────────────────────────────
|
|
28
137
|
|
|
29
138
|
// 1. Build recommendations from catalog based on stack
|
|
30
139
|
const recs = buildRecommendations(answers);
|
|
31
140
|
if (recs.length === 0) {
|
|
32
141
|
log.dim('No stack-specific recommendations found.');
|
|
33
142
|
} else {
|
|
34
|
-
log.raw('');
|
|
35
143
|
log.raw(` ${pad('Skill', 36)} ${pad('Repo', 36)}`);
|
|
36
144
|
log.raw(` ${'─'.repeat(36)} ${'─'.repeat(36)}`);
|
|
37
145
|
for (const r of recs) {
|
|
@@ -47,7 +155,6 @@ export async function skillDiscovery({ cwd, answers, args }) {
|
|
|
47
155
|
log.raw('');
|
|
48
156
|
}
|
|
49
157
|
|
|
50
|
-
// 3. Offer to launch interactive find for each term
|
|
51
158
|
for (const term of terms) {
|
|
52
159
|
const { go } = await prompts({
|
|
53
160
|
type: 'confirm',
|
|
@@ -56,11 +163,11 @@ export async function skillDiscovery({ cwd, answers, args }) {
|
|
|
56
163
|
initial: true,
|
|
57
164
|
});
|
|
58
165
|
if (go) {
|
|
59
|
-
await
|
|
166
|
+
await runSkills(env, ['find', term], cwd);
|
|
60
167
|
}
|
|
61
168
|
}
|
|
62
169
|
|
|
63
|
-
//
|
|
170
|
+
// 3. Offer to install from well-known repos
|
|
64
171
|
if (recs.length > 0) {
|
|
65
172
|
const { install } = await prompts({
|
|
66
173
|
type: 'multiselect',
|
|
@@ -75,7 +182,6 @@ export async function skillDiscovery({ cwd, answers, args }) {
|
|
|
75
182
|
});
|
|
76
183
|
|
|
77
184
|
if (install && install.length > 0) {
|
|
78
|
-
// Group by repo
|
|
79
185
|
const byRepo = new Map();
|
|
80
186
|
for (const r of install) {
|
|
81
187
|
if (!byRepo.has(r.repo)) byRepo.set(r.repo, []);
|
|
@@ -84,16 +190,14 @@ export async function skillDiscovery({ cwd, answers, args }) {
|
|
|
84
190
|
for (const [repo, skills] of byRepo) {
|
|
85
191
|
const skillArgs = skills.flatMap((s) => ['--skill', s]);
|
|
86
192
|
log.info(`Installing from ${repo}: ${skills.join(', ')}`);
|
|
87
|
-
await
|
|
88
|
-
'npx',
|
|
89
|
-
['-y', 'skills', 'add', repo, ...skillArgs, '-a', 'github-copilot'],
|
|
90
|
-
cwd,
|
|
91
|
-
);
|
|
193
|
+
await runSkills(env, ['add', repo, ...skillArgs, '-a', 'github-copilot'], cwd);
|
|
92
194
|
}
|
|
195
|
+
|
|
196
|
+
await relocateSkills(cwd);
|
|
93
197
|
}
|
|
94
198
|
}
|
|
95
199
|
|
|
96
|
-
//
|
|
200
|
+
// 4. Offer freeform catch-all
|
|
97
201
|
const { freeform } = await prompts({
|
|
98
202
|
type: 'confirm',
|
|
99
203
|
name: 'freeform',
|
|
@@ -101,12 +205,78 @@ export async function skillDiscovery({ cwd, answers, args }) {
|
|
|
101
205
|
initial: false,
|
|
102
206
|
});
|
|
103
207
|
if (freeform) {
|
|
104
|
-
await
|
|
208
|
+
await runSkills(env, ['find'], cwd);
|
|
209
|
+
await relocateSkills(cwd);
|
|
105
210
|
}
|
|
106
211
|
|
|
107
212
|
log.dim('Done. Run `npx skills find` anytime to discover more.');
|
|
108
213
|
}
|
|
109
214
|
|
|
215
|
+
// ── Relocation ─────────────────────────────────────────────────────────
|
|
216
|
+
|
|
217
|
+
async function relocateSkills(cwd) {
|
|
218
|
+
const src = join(cwd, '.agents', 'skills');
|
|
219
|
+
const dest = join(cwd, '.github', 'skills');
|
|
220
|
+
|
|
221
|
+
if (!existsSync(src)) return;
|
|
222
|
+
|
|
223
|
+
const candidates = [];
|
|
224
|
+
for (const entry of readdirSync(src, { withFileTypes: true })) {
|
|
225
|
+
if (!entry.isDirectory()) continue;
|
|
226
|
+
if (!existsSync(join(src, entry.name, 'SKILL.md'))) continue;
|
|
227
|
+
if (existsSync(join(dest, entry.name))) continue;
|
|
228
|
+
candidates.push(entry.name);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (candidates.length === 0) return;
|
|
232
|
+
|
|
233
|
+
log.raw('');
|
|
234
|
+
log.info(`${candidates.length} skill${candidates.length > 1 ? 's' : ''} in .agents/skills/: ${candidates.join(', ')}`);
|
|
235
|
+
log.info('cli-five prefers .github/skills/ (co-located with agents & instructions).');
|
|
236
|
+
log.info('Both locations work — Copilot discovers either.');
|
|
237
|
+
|
|
238
|
+
const { move } = await prompts({
|
|
239
|
+
type: 'confirm',
|
|
240
|
+
name: 'move',
|
|
241
|
+
message: 'Move to .github/skills/?',
|
|
242
|
+
initial: true,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
if (!move) {
|
|
246
|
+
log.dim('Keeping skills in .agents/skills/.');
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let moved = 0;
|
|
251
|
+
for (const name of candidates) {
|
|
252
|
+
mkdirSync(dest, { recursive: true });
|
|
253
|
+
renameSync(join(src, name), join(dest, name));
|
|
254
|
+
moved++;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
log.ok(`Moved ${moved} skill${moved > 1 ? 's' : ''} → .github/skills/`);
|
|
258
|
+
|
|
259
|
+
// Clean up empty dirs
|
|
260
|
+
try {
|
|
261
|
+
if (readdirSync(src).length === 0) {
|
|
262
|
+
rmSync(src, { recursive: true });
|
|
263
|
+
const agentsDir = join(cwd, '.agents');
|
|
264
|
+
if (existsSync(agentsDir) && readdirSync(agentsDir).length === 0) {
|
|
265
|
+
rmSync(agentsDir, { recursive: true });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} catch {
|
|
269
|
+
/* best-effort */
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ── Runner ─────────────────────────────────────────────────────────────
|
|
274
|
+
|
|
275
|
+
function runSkills(env, skillsArgs, cwd) {
|
|
276
|
+
const { cmd, args } = env.runner(skillsArgs);
|
|
277
|
+
return runInteractive(cmd, args, cwd);
|
|
278
|
+
}
|
|
279
|
+
|
|
110
280
|
function buildRecommendations(a) {
|
|
111
281
|
const stackLower = (a.stack || []).map((s) => s.toLowerCase());
|
|
112
282
|
const fwLower = (a.frameworks || []).map((f) => f.toLowerCase());
|
|
@@ -50,10 +50,10 @@ Before writing code, read (if they exist):
|
|
|
50
50
|
8. **Modifications** — Follow existing patterns. Prefer full-file rewrites over micro-edits unless told otherwise.
|
|
51
51
|
9. **Quality** — Deterministic, testable behavior. Simple, focused tests.
|
|
52
52
|
|
|
53
|
-
## Decisions
|
|
53
|
+
## Decisions (MANDATORY)
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
Before finishing, if any implementation choice was made (library selection, pattern choice, API approach), append an entry to `decisions.md` using the format in that file. Skip silently if no decisions were made.
|
|
56
56
|
|
|
57
|
-
## History
|
|
57
|
+
## History (MANDATORY)
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Before finishing, append at least one bullet to `histories/coder.md` below the `<!-- Append entries below this line -->` marker. Record: build quirks, API gotchas, pattern preferences, file structure observations, test insights. Format: `- YYYY-MM-DD: <learning>`. Skip only if the session had zero meaningful work.
|
|
@@ -47,10 +47,10 @@ Do not let anyone tell you how to do your job. Your goal is to create the best p
|
|
|
47
47
|
- Responsive/adaptive layouts
|
|
48
48
|
- Use the project's designated design system and component library
|
|
49
49
|
|
|
50
|
-
## Decisions
|
|
50
|
+
## Decisions (MANDATORY)
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Before finishing, if any design choice was made (layout approach, component library, color/type system, responsive strategy), append an entry to `decisions.md` using the format in that file. Skip silently if no decisions were made.
|
|
53
53
|
|
|
54
|
-
## History
|
|
54
|
+
## History (MANDATORY)
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
Before finishing, append at least one bullet to `histories/designer.md` below the `<!-- Append entries below this line -->` marker. Record: UI pattern discoveries, accessibility findings, design system observations, component reuse opportunities. Format: `- YYYY-MM-DD: <learning>`. Skip only if the session had zero meaningful work.
|
|
@@ -63,13 +63,21 @@ Maintain visible counters in responses:
|
|
|
63
63
|
|
|
64
64
|
When exhausted, state it and proceed with current information.
|
|
65
65
|
|
|
66
|
-
## Decisions
|
|
66
|
+
## Decisions (MANDATORY)
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
Before finishing, if any routing, architectural, or technology choice was made during this session, append an entry to `decisions.md` at the workspace root using this format:
|
|
69
69
|
|
|
70
|
-
##
|
|
70
|
+
## YYYY-MM-DD — <decision title>
|
|
71
|
+
**Context:** Why we needed to decide.
|
|
72
|
+
**Choice:** What we chose.
|
|
73
|
+
**Trade-offs:** What we gave up.
|
|
74
|
+
**Revisit:** Trigger that would re-open this decision (or "never").
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
If no decisions were made, skip silently.
|
|
77
|
+
|
|
78
|
+
## History (MANDATORY)
|
|
79
|
+
|
|
80
|
+
Before finishing, append at least one bullet to `histories/orchestrator.md` below the `<!-- Append entries below this line -->` marker. Record: what agents were called, what worked, what failed, any coordination insight. Format: `- YYYY-MM-DD: <learning>`. Skip only if the session had zero meaningful work.
|
|
73
81
|
|
|
74
82
|
## Rules
|
|
75
83
|
|
|
@@ -52,13 +52,13 @@ When invoked by the Orchestrator, only your **final message** is returned. Inter
|
|
|
52
52
|
|
|
53
53
|
**Your response MUST contain the complete plan inline.** Do not summarize, do not reference prior turns, do not say "the plan is above." Re-emit every section in your final message. If truncated, flag it explicitly.
|
|
54
54
|
|
|
55
|
-
## Decisions
|
|
55
|
+
## Decisions (MANDATORY)
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Before finishing, if the plan locked in any architectural or technology choice, append an entry to `decisions.md` using the format in that file. Skip silently if the plan only follows existing decisions.
|
|
58
58
|
|
|
59
|
-
## History
|
|
59
|
+
## History (MANDATORY)
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Before finishing, append at least one bullet to `histories/planner.md` below the `<!-- Append entries below this line -->` marker. Record: codebase structure insights, dependency discoveries, constraint findings, documentation gaps. Format: `- YYYY-MM-DD: <learning>`. Skip only if the session had zero meaningful work.
|
|
62
62
|
|
|
63
63
|
## Rules
|
|
64
64
|
|
|
@@ -71,13 +71,13 @@ Every review MUST check against:
|
|
|
71
71
|
- Positive observations
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
## Decisions
|
|
74
|
+
## Decisions (MANDATORY)
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
Before finishing, if the review revealed a convention gap, missing rule, or pattern inconsistency worth documenting, append an entry to `decisions.md` using the format in that file. Skip silently if nothing warrants a decision entry.
|
|
77
77
|
|
|
78
|
-
## History
|
|
78
|
+
## History (MANDATORY)
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
Before finishing, append at least one bullet to `histories/reviewer.md` below the `<!-- Append entries below this line -->` marker. Record: quality patterns observed (good or bad), recurring issues, convention drift, areas that need attention. Format: `- YYYY-MM-DD: <learning>`. Skip only if the review was trivial (< 10 lines changed, no findings).
|
|
81
81
|
|
|
82
82
|
## Rules
|
|
83
83
|
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
# Project Mandates
|
|
21
21
|
- Fix simple tech debt immediately (< 5 min, no downstream breakage).
|
|
22
22
|
- Document short-sighted choices with warning markers.
|
|
23
|
-
- Read `PROJECT.md`, `STATE.md`, and `decisions.md` on entry.
|
|
23
|
+
- Read `PROJECT.md`, `STATE.md`, and `decisions.md` on entry.
|
|
24
|
+
- Before finishing: update `decisions.md` if any choice was made, update your `histories/<agent>.md` with at least one learning.
|
|
24
25
|
|
|
25
26
|
At the end of every agent session, before stopping, append a terse session summary to `agent-diary.md`
|
|
26
27
|
at the workspace root as a markdown section with format `## YYYY-MM-DD HH:MM` (24-hour time).
|