cli-five 0.2.15 → 0.2.17

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 CHANGED
@@ -10,7 +10,14 @@
10
10
  npx cli-five init
11
11
  ```
12
12
 
13
- Interviews you, lets you pick a target platform and model provider, scaffolds agents + memory files + stack-specific instructions, discovers skills, and tells you what to do next.
13
+ By default `init` is **minimal**: it scaffolds the 5-agent team and the tooling they need, asking only for the target platform and (when it can't find them) your project name and one-liner. Name and description are auto-pulled from `package.json` and `README.md`. Optional integrations — including CodeGraph, skills, and instruction files — are **not** installed unless you ask.
14
+
15
+ ```bash
16
+ npx cli-five init --full-interview # legacy: docs, goals, persona, model picker, CodeGraph, skills, instructions
17
+ npx cli-five init --codegraph # minimal, but add the CodeGraph MCP + AGENTS.md block
18
+ ```
19
+
20
+ `--full-interview` (or passing `--doc`) restores the complete guided setup: stack presets, goals, constraints, persona toggle, per-agent model overrides, Copilot cost modes, CodeGraph, skill discovery, and stack-specific instruction generation. Individual pieces can also be toggled with `--codegraph`, `--skills`, `--instructions`, and `--persona`.
14
21
 
15
22
  ### Quick plugin install (personal use, Copilot only)
16
23
 
@@ -44,9 +51,9 @@ your-repo/
44
51
  │ │ ├── coder.agent.md
45
52
  │ │ ├── designer.agent.md
46
53
  │ │ └── reviewer.agent.md
47
- │ ├── copilot-instructions.md # Persona + project mandates from interview
48
- │ ├── instructions/ # Stack-specific coding guidelines
49
- │ └── skills/ # Installed skills from awesome-copilot + skills.sh
54
+ │ ├── copilot-instructions.md # Project mandates (persona is opt-in)
55
+ │ ├── instructions/ # Container; stack guidelines via --instructions
56
+ │ └── skills/ # Container; skills via --skills
50
57
  ├── .vscode/mcp.json # CodeGraph MCP server (optional)
51
58
  ├── AGENTS.md # Tool-agnostic project context (agents.md standard)
52
59
  ├── PROJECT.md # Long-form vision (rarely changes)
@@ -68,8 +75,8 @@ your-repo/
68
75
  │ ├── designer.md
69
76
  │ └── reviewer.md
70
77
  ├── opencode.json # Project config, model defaults, CodeGraph MCP (optional)
71
- ├── .github/instructions/ # Stack-specific coding guidelines
72
- ├── .github/skills/ # Installed skills
78
+ ├── .github/instructions/ # Container; stack guidelines via --instructions
79
+ ├── .github/skills/ # Container; skills via --skills
73
80
  ├── AGENTS.md # Project rules + optional CodeGraph block
74
81
  ├── PROJECT.md
75
82
  ├── STATE.md
@@ -94,7 +101,10 @@ The plugin agents and scaffolded `.github/agents/*.agent.md` templates are inten
94
101
  ## Commands
95
102
 
96
103
  ```bash
97
- npx cli-five init # interview + scaffold + skill discovery
104
+ npx cli-five init # minimal scaffold (5 agents + required tooling)
105
+ npx cli-five init --full-interview # full guided setup (interview, models, skills, instructions)
106
+ npx cli-five add <name> # install an optional add-on (dispatcher; targets land later)
107
+ npx cli-five list-addons # show installed vs. available add-ons
98
108
  npx cli-five doctor # validate an existing cli-five setup
99
109
  npx cli-five list-stacks # show detectable tech stacks
100
110
  npx cli-five help
@@ -104,33 +114,97 @@ npx cli-five help
104
114
 
105
115
  | Flag | Effect |
106
116
  |---|---|
107
- | `--yes`, `-y` | Accept interview defaults (overwrite gate still active) |
117
+ | `--yes`, `-y` | Accept defaults (overwrite gate still active) |
108
118
  | `--force` | Overwrite without confirmation (use with `--yes`) |
109
119
  | `--dry-run` | Print actions, write nothing |
110
- | `--no-skills` | Skip skills.sh discovery step |
111
- | `--no-codegraph` | Skip CodeGraph MCP + instructions |
120
+ | `--full-interview`, `--full` | Run the full legacy interview (docs, goals/constraints/persona, model picker). Also enables CodeGraph, skills + instructions |
121
+ | `--skills` / `--no-skills` | Force skill discovery on/off |
122
+ | `--instructions` / `--no-instructions` | Force stack-specific instruction generation on/off |
123
+ | `--persona` / `--no-persona` | Include / omit the snarky persona block |
124
+ | `--codegraph` / `--no-codegraph` | Force CodeGraph MCP + instructions on/off (default: on with `--full-interview`, off otherwise) |
112
125
  | `--target <t>` | Target platform: `copilot` or `opencode` (default: `copilot`) |
113
126
  | `--provider <p>` | Model provider: `copilot`, `opencode`, `opencode-go` (default: platform default) |
114
127
  | `--cost-mode <m>` | Set cost mode for Copilot: `premium`, `cheap`, or `mixed` |
115
- | `--doc <file>` | Read project docs to pre-fill interview (repeatable) |
128
+ | `--doc <file>` | Read project docs to pre-fill interview (repeatable; implies `--full-interview`) |
116
129
  | `--cwd <path>` | Run against a different directory |
117
130
 
118
131
  ## What `init` does
119
132
 
133
+ Default (`npx cli-five init`):
134
+
120
135
  1. **Detect** — fingerprints stack (Node/TS, Python, .NET, Kotlin, Rust, Go, etc.). Brownfield-aware.
121
- 2. **Choose platform** — GitHub Copilot or OpenCode, with optional CodeGraph pairing.
136
+ 2. **Choose platform** — explicit `--target`, auto-detected from an existing scaffold, or prompted.
122
137
  3. **git init** — if needed. Asks first.
123
138
  4. **Overwrite gate** — double-confirms ("Proceed?" then "R U Sure?"). Only `--force --yes` bypasses.
124
- 5. **Project info** — name, one-liner, stack, frameworks, goals, constraints, persona toggle.
125
- 6. **Model configuration** — pick a provider and optionally override each agent's model.
126
- 7. **Scaffold** — writes platform-specific files. Swaps `model:` per provider/selection.
127
- 8. **Skill discovery** — multi-source discovery from **awesome-copilot** and **skills.sh**.
128
- 9. **Custom instructions** — generates stack-specific `.instructions.md` files for detected languages.
129
- 10. **Next steps** — platform-specific instructions. No button-clicking required.
139
+ 5. **Project info** — name and one-liner auto-pulled from `package.json` / `README.md`; asks only when missing or ambiguous.
140
+ 6. **Model configuration** — platform/provider defaults, no prompt.
141
+ 7. **Scaffold** — writes the 5 agents + required project memory/tooling files. Swaps `model:` per provider defaults.
142
+ 8. **Done** — prints platform-specific next steps.
143
+
144
+ `--full-interview` additionally runs:
145
+
146
+ - **Docs / manual interview** — project docs, stack preset, goals, constraints.
147
+ - **Model customization** — provider picker + per-agent model overrides.
148
+ - **Persona toggle** — snarky persona block.
149
+ - **CodeGraph** — MCP registration + `AGENTS.md` block (also available standalone via `--codegraph`).
150
+ - **Skill discovery** — multi-source discovery from **awesome-copilot** and **skills.sh**.
151
+ - **Custom instructions** — stack-specific `.instructions.md` files for detected languages.
152
+
153
+ ## Add-ons
154
+
155
+ Optional integrations live outside the default scaffold and are installed with `cli-five add <name>`.
156
+
157
+ ```bash
158
+ npx cli-five add # list known targets
159
+ npx cli-five add codegraph # CodeGraph MCP registration + AGENTS.md instructions
160
+ npx cli-five add jev # tier-routing tool for the Planner (OpenCode only)
161
+ npx cli-five list-addons # installed vs. available status
162
+ ```
163
+
164
+ Add-ons use `mergeBlock(file, markerFence, content)` to layer a fenced block into an existing JSON or Markdown file without touching the rest of it (distinct from init's blunt overwrite gate). Markdown gets `<!-- NAME_START -->` / `<!-- NAME_END -->` fences; JSON is deep-merged with existing keys preserved. Re-running an add-on is idempotent — no duplicate MCP entries or AGENTS.md sections.
165
+
166
+ ### CodeGraph (`add codegraph`)
167
+
168
+ `add codegraph` registers the CodeGraph MCP server and adds the CodeGraph section to `AGENTS.md`. It works on **both platforms**:
169
+
170
+ | Target | MCP registration | Instructions |
171
+ |---|---|---|
172
+ | Copilot | `.vscode/mcp.json` → `servers.codegraph` | `AGENTS.md` block |
173
+ | OpenCode | `opencode.json` → `mcp.codegraph` | `AGENTS.md` block |
174
+
175
+ **The `init` flags are now thin wrappers.** `init --codegraph` and `init --no-codegraph` still behave exactly as before, but they call the *same* underlying `add codegraph` logic — one implementation, two entry points. CodeGraph remains off by default in minimal `init` and on with `--full-interview`; pass `--codegraph` to force it on, `--no-codegraph` to force it off.
176
+
177
+ cli-five still does **not** run `codegraph init` itself — it only registers the server and reminds you to index the project:
178
+
179
+ ```bash
180
+ npm i -g @colbymchenry/codegraph
181
+ codegraph init
182
+ ```
183
+
184
+ ### Jev (`add jev`) — tier-routing only, OpenCode only
185
+
186
+ `add jev` scaffolds an OpenCode plugin that adds a `local_tier_heuristic` tool. The Planner calls it once per task to classify the work as `trivial` / `minor` / `major`, then scales planning depth accordingly.
187
+
188
+ **Two things it deliberately does *not* do, stated plainly:**
189
+
190
+ 1. **It does not call Jev.** `jev-harness` 0.2.0's `route` subcommand exposes no custom-criteria interface — it emits its own fixed tier vocabulary (`deterministic` / `lightweight_system2` / `heavy_system2`) and returns a constant confidence (`0.88`) under its offline/mock engine, so it cannot be thresholded on. The shipped tool is therefore a **local heuristic**, truthfully named `local_tier_heuristic`. The swap point for real Jev wiring is marked in `templates/opencode/plugin/jev-tier-router/index.js` (`JEVR_SWAP_POINT`).
191
+ 2. **The test-gate is parked.** Gating Reviewer spawns via plugin interception (`tool.execute.before` / `permission.ask`) does not work: OpenCode plugin hooks do not fire under OpenChamber's embedded-server routing. Do not expect `add jev` to gate anything.
192
+
193
+ `list-addons` reports each add-on's honest capability rather than a bare "installed":
194
+
195
+ ```
196
+ codegraph installed (MCP registration + AGENTS.md instructions) available MCP registration + AGENTS.md instructions
197
+ jev installed (tier-routing only) available local heuristic — …; test-gate parked — <issue link>
198
+ ```
199
+
200
+ **Fail-open is non-negotiable.** If the classifier is unavailable, errors, or returns malformed output, the tool returns `available: false` with tier `major` (the expensive tier) and never throws. The Planner falls back to its own judgment. cli-five and the scaffolded agents behave identically whether the plugin works, is missing, or is broken.
201
+
202
+ **Copilot has no equivalent.** `add jev` refuses cleanly on a Copilot target (exit 1, no files written) — there is no `tools.add`-style surface there.
203
+
130
204
 
131
205
  ## Model providers
132
206
 
133
- During `init` you can choose the model provider and optionally customize each agent's model.
207
+ During `init --full-interview` you can choose the model provider and optionally customize each agent's model. The minimal `init` path uses provider defaults (honouring `--provider` and `--cost-mode`).
134
208
 
135
209
  | Provider | Platform | Example model |
136
210
  |---|---|---|
@@ -155,7 +229,7 @@ Change anytime by editing the `model:` line in the agent files.
155
229
 
156
230
  ## CodeGraph
157
231
 
158
- CodeGraph adds a local, graph-backed codebase context server. When enabled, cli-five:
232
+ CodeGraph adds a local, graph-backed codebase context server. It is **off by default** in the minimal scaffold; enable it with `--codegraph` or `--full-interview`. When enabled, cli-five:
159
233
 
160
234
  - Registers the CodeGraph MCP server in `opencode.json` (OpenCode) or `.vscode/mcp.json` (Copilot).
161
235
  - Adds a marker-fenced CodeGraph section to `AGENTS.md`.
@@ -184,7 +258,7 @@ No extra settings required. Project agents live in `.opencode/agents/` and the p
184
258
 
185
259
  ## Skill discovery
186
260
 
187
- cli-five searches **two sources** for skills matching your detected stack:
261
+ Skill discovery runs during `init --full-interview` (or when you pass `--skills`). cli-five searches **two sources** for skills matching your detected stack:
188
262
 
189
263
  | Source | What it has | Stars |
190
264
  |---|---|---|
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-five",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "Code Like I'm Five — scaffold a 5-agent AI team (GitHub Copilot or OpenCode) into any repo.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,47 @@
1
+ import kleur from 'kleur';
2
+ import { existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { log } from '../util/log.mjs';
5
+ import { PLATFORM_COPILOT, PLATFORM_OPENCODE, platformLabel } from '../util/platforms.mjs';
6
+ import { addCodegraphTo, CODEGRAPH_INIT_REMINDER, mcpTargetFor } from './codegraph.mjs';
7
+
8
+ /**
9
+ * `add codegraph` — register the CodeGraph MCP server + AGENTS.md section.
10
+ *
11
+ * Works on both platforms. Same content and behavior as the previous inline
12
+ * init implementation; only the invocation path and write mechanism changed
13
+ * (mergeBlock instead of blunt overwrite).
14
+ */
15
+ export async function runCodegraph({ cwd, args = {} }) {
16
+ const platform = detectPlatform(cwd);
17
+
18
+ if (platform === 'unknown') {
19
+ log.err('No cli-five scaffold detected (neither .opencode/agents nor .github/agents).');
20
+ log.dim('Run `npx cli-five init` first, then `npx cli-five add codegraph`.');
21
+ process.exitCode = 1;
22
+ return [];
23
+ }
24
+
25
+ const dryRun = Boolean(args.dryRun);
26
+ const touched = addCodegraphTo({ cwd, platform, dryRun, track: true });
27
+
28
+ for (const t of touched) {
29
+ const rel = t.path.replace(cwd + '/', '');
30
+ const symbol = t.action === 'created' || t.action === 'updated' ? '+' : '~';
31
+ log.raw(` ${symbol} ${rel} ${kleur.dim(`(${t.action})`)}`);
32
+ }
33
+
34
+ log.ok(`CodeGraph registered for ${platformLabel(platform)}`);
35
+ log.dim(`MCP: ${mcpTargetFor(platform)} · instructions: AGENTS.md`);
36
+ log.dim(CODEGRAPH_INIT_REMINDER.replace('CodeGraph is configured. ', ''));
37
+
38
+ return touched;
39
+ }
40
+
41
+ function detectPlatform(cwd) {
42
+ if (existsSync(join(cwd, '.opencode', 'agents'))) return PLATFORM_OPENCODE;
43
+ if (existsSync(join(cwd, '.github', 'agents', 'orchestrator.agent.md'))) return PLATFORM_COPILOT;
44
+ return 'unknown';
45
+ }
46
+
47
+ export const __testables = { detectPlatform };
@@ -0,0 +1,124 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { mergeBlock } from '../util/merge.mjs';
4
+ import { PLATFORM_COPILOT, PLATFORM_OPENCODE } from '../util/platforms.mjs';
5
+
6
+ export const CODEGRAPH_STATUS = 'MCP registration + AGENTS.md instructions';
7
+ export const CODEGRAPH_BLOCK_NAME = 'codegraph';
8
+ export const CODEGRAPH_INIT_REMINDER =
9
+ 'CodeGraph is configured. Remember to run `codegraph init` before asking agents to explore the codebase.';
10
+
11
+ // The AGENTS.md section is byte-identical to the pre-migration inline constant
12
+ // in scaffold.mjs. Do not change its content — only the write path changed.
13
+ export const CODEGRAPH_BLOCK = `## CodeGraph
14
+
15
+ This project is configured to use [CodeGraph](https://codegraph.ru) for graph-backed codebase context.
16
+ When you need to understand relationships, call paths, or impacts, use:
17
+
18
+ \`\`\`
19
+ codegraph explore "<your question>"
20
+ \`\`\`
21
+
22
+ The CodeGraph MCP server is registered in the project config. Run \`codegraph init\` in this directory
23
+ if the project has not been indexed yet.`;
24
+
25
+ /**
26
+ * The CodeGraph MCP server entries, per platform. Unchanged from the previous
27
+ * inline implementation — same server, same command, same shape.
28
+ */
29
+ export function codegraphOpencodeConfig() {
30
+ return {
31
+ codegraph: {
32
+ type: 'local',
33
+ command: ['codegraph', 'serve', '--mcp'],
34
+ enabled: true,
35
+ },
36
+ };
37
+ }
38
+
39
+ export function codegraphCopilotMcpJson() {
40
+ return {
41
+ inputs: [],
42
+ servers: {
43
+ codegraph: {
44
+ command: 'codegraph',
45
+ args: ['serve', '--mcp'],
46
+ },
47
+ },
48
+ };
49
+ }
50
+
51
+ /** Which MCP file a given platform owns. Preserves the old per-platform split. */
52
+ export function mcpTargetFor(platform) {
53
+ return platform === PLATFORM_OPENCODE ? 'opencode.json' : join('.vscode', 'mcp.json');
54
+ }
55
+
56
+ /**
57
+ * Register CodeGraph in a workspace, using mergeBlock() for every write.
58
+ *
59
+ * Shared by `add codegraph` and init's legacy --codegraph flag — one
60
+ * implementation, two entry points.
61
+ *
62
+ * @returns {Array<{path:string, action:string}>} the files touched.
63
+ */
64
+ export function addCodegraphTo({ cwd, platform, dryRun = false, track = false }) {
65
+ const touched = [];
66
+ const isOpenCode = platform === PLATFORM_OPENCODE;
67
+
68
+ // 1. MCP registration.
69
+ if (isOpenCode) {
70
+ const opencodePath = join(cwd, 'opencode.json');
71
+ // Preserve a pre-existing `plugins[]` (e.g. jev): deepMerge replaces arrays,
72
+ // so carry the existing value through untouched at the root.
73
+ const existing = readJson(opencodePath) || {};
74
+ const patch = { mcp: codegraphOpencodeConfig() };
75
+ if (Array.isArray(existing.plugins)) patch.plugins = existing.plugins;
76
+ touched.push(mergeBlock(opencodePath, 'codegraph', patch, { dryRun, track }));
77
+ } else {
78
+ const mcpPath = join(cwd, '.vscode', 'mcp.json');
79
+ const existing = readJson(mcpPath) || {};
80
+ // Preserve the old key order (`inputs` first) so output matches the
81
+ // pre-migration inline implementation byte-for-byte. `inputs` is an array;
82
+ // carry it through so deepMerge's array-replace is a no-op.
83
+ const patch = { inputs: Array.isArray(existing.inputs) ? existing.inputs : [] };
84
+ patch.servers = { codegraph: codegraphCopilotMcpJson().servers.codegraph };
85
+ touched.push(mergeBlock(mcpPath, 'codegraph', patch, { dryRun, track }));
86
+ }
87
+
88
+ // 2. AGENTS.md section (fenced block; same marker name as the old template).
89
+ const agentsPath = join(cwd, 'AGENTS.md');
90
+ touched.push(mergeBlock(agentsPath, CODEGRAPH_BLOCK_NAME, CODEGRAPH_BLOCK, { dryRun }));
91
+
92
+ return touched;
93
+ }
94
+
95
+ /**
96
+ * Idempotent remove-of-duplicates is intentionally NOT implemented: mergeBlock
97
+ * already replaces an existing fenced block in place and deep-merges JSON, so
98
+ * re-running add codegraph does not duplicate anything.
99
+ */
100
+ export function isCodegraphPresent(cwd, platform) {
101
+ const mcpPath = join(cwd, mcpTargetFor(platform));
102
+ const cfg = readJson(mcpPath);
103
+ if (platform === PLATFORM_OPENCODE) return Boolean(cfg?.mcp?.codegraph);
104
+ return Boolean(cfg?.servers?.codegraph);
105
+ }
106
+
107
+ function readJson(filePath) {
108
+ if (!existsSync(filePath)) return null;
109
+ try {
110
+ return JSON.parse(readFileSync(filePath, 'utf8'));
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+
116
+ /** Ensure a file exists with given content (used for a created-not-merged file). */
117
+ export function ensureFile(filePath, contents) {
118
+ if (existsSync(filePath)) return false;
119
+ mkdirSync(dirname(filePath), { recursive: true });
120
+ writeFileSync(filePath, contents);
121
+ return true;
122
+ }
123
+
124
+ export const __testables = { readJson, mcpTargetFor };
@@ -0,0 +1,98 @@
1
+ import kleur from 'kleur';
2
+ import { existsSync, readFileSync, cpSync, mkdirSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { log } from '../util/log.mjs';
5
+ import { mergeBlock } from '../util/merge.mjs';
6
+ import { templatePath } from '../util/fs.mjs';
7
+
8
+ export const JEVR_STATUS =
9
+ 'local heuristic — jev-harness lacks a custom-criteria interface as of 2026-09-26';
10
+ export const JEVR_TOOL = 'local_tier_heuristic';
11
+ export const TEST_GATE_ISSUE = 'https://github.com/idusortus/cli-five/issues';
12
+
13
+ const PLUGIN_REL = join('.opencode', 'plugin', 'jev-tier-router');
14
+
15
+ /**
16
+ * `add jev` — tier-routing only.
17
+ *
18
+ * Ships a local tier-classification tool for the Planner. Does NOT wire the
19
+ * test-gate (parked: OpenCode plugin hooks don't fire under OpenChamber's
20
+ * embedded-server routing) and does NOT call Jev (no custom-criteria
21
+ * interface in jev-harness as of this release).
22
+ */
23
+ export async function runJev({ cwd, args }) {
24
+ const platform = detectPlatform(cwd);
25
+
26
+ if (platform !== 'opencode') {
27
+ log.err('jev is OpenCode-only. No tools.add-equivalent surface exists for Copilot.');
28
+ log.dim('Re-run init with --target opencode, then `npx cli-five add jev`.');
29
+ process.exitCode = 1;
30
+ return;
31
+ }
32
+
33
+ const written = [];
34
+
35
+ // 1. Plugin directory (package.json + index.js), confirmed convention.
36
+ const pluginDir = join(cwd, PLUGIN_REL);
37
+ mkdirSync(pluginDir, { recursive: true });
38
+ for (const file of ['package.json', 'index.js']) {
39
+ const src = templatePath('opencode', 'plugin', 'jev-tier-router', file);
40
+ const dest = join(pluginDir, file);
41
+ cpSync(src, dest);
42
+ written.push({ path: dest, written: true });
43
+ }
44
+
45
+ // 2. Register the plugin path in opencode.json's `plugins` array.
46
+ // mergeBlock deep-merges JSON but replaces arrays, so read the existing
47
+ // array, union it, and hand the unioned value to mergeBlock (single
48
+ // write path — no second merge approach).
49
+ const opencodePath = join(cwd, 'opencode.json');
50
+ const plugins = readPlugins(opencodePath);
51
+ const pluginRef = toPosix(PLUGIN_REL);
52
+ if (!plugins.includes(pluginRef)) plugins.push(pluginRef);
53
+ mergeBlock(opencodePath, 'jev', { plugins }, { track: true, metaKey: '$cliFive' });
54
+
55
+ // 3. Planner instruction in AGENTS.md (fenced block, idempotent).
56
+ const agentsPath = join(cwd, 'AGENTS.md');
57
+ mergeBlock(agentsPath, 'jev-tier-routing', plannerInstruction());
58
+
59
+ log.ok(`Plugin written to ${kleur.bold(toPosix(PLUGIN_REL))}`);
60
+ log.ok(`Registered in opencode.json (plugins[])`);
61
+ log.ok('Planner instruction added to AGENTS.md');
62
+ log.raw('');
63
+ log.info(`Tool: ${kleur.bold(JEVR_TOOL)} (tier-routing only)`);
64
+ log.warn('Status: ' + JEVR_STATUS);
65
+ log.dim(`Test-gate is parked — see ${TEST_GATE_ISSUE}`);
66
+ }
67
+
68
+ function plannerInstruction() {
69
+ return `## Tier routing (local heuristic)
70
+
71
+ Before planning, call the \`${JEVR_TOOL}\` tool once with the task description.
72
+
73
+ - If it returns \`confidence\` >= 0.6, use its \`tier\` (trivial | minor | major) as your planning depth.
74
+ - If \`confidence\` < 0.6, or the tool is unavailable, use your own judgment and default to \`major\`.
75
+ - This is a local heuristic, not a Jev call. Never block or fail a turn because the tool is unavailable.`;
76
+ }
77
+
78
+ function readPlugins(opencodePath) {
79
+ if (!existsSync(opencodePath)) return [];
80
+ try {
81
+ const cfg = JSON.parse(readFileSync(opencodePath, 'utf8'));
82
+ return Array.isArray(cfg.plugins) ? [...cfg.plugins] : [];
83
+ } catch {
84
+ return [];
85
+ }
86
+ }
87
+
88
+ function detectPlatform(cwd) {
89
+ if (existsSync(join(cwd, '.opencode', 'agents'))) return 'opencode';
90
+ if (existsSync(join(cwd, '.github', 'agents', 'orchestrator.agent.md'))) return 'copilot';
91
+ return 'unknown';
92
+ }
93
+
94
+ function toPosix(p) {
95
+ return p.split('\\').join('/');
96
+ }
97
+
98
+ export const __testables = { plannerInstruction, readPlugins, detectPlatform };
@@ -0,0 +1,126 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { runJev, JEVR_STATUS, TEST_GATE_ISSUE } from './jev.mjs';
4
+ import { runCodegraph } from './codegraph-command.mjs';
5
+ import { CODEGRAPH_STATUS } from './codegraph.mjs';
6
+
7
+ /**
8
+ * Add-on registry for `cli-five add <name>`.
9
+ *
10
+ * An add-on describes an optional integration that can be layered onto an
11
+ * already-scaffolded repo. Entries without a `run` function are reserved
12
+ * (dispatcher/stub) targets — the mechanism is live, the integration is not.
13
+ *
14
+ * To wire a real target later: give it a `run({ cwd, args })` function (the
15
+ * `add` command will invoke it) and update `available`/`note` accordingly.
16
+ */
17
+ export const ADDONS = {
18
+ codegraph: {
19
+ name: 'codegraph',
20
+ label: 'CodeGraph',
21
+ description: 'Graph-backed codebase context MCP server and agent instructions.',
22
+ // Matches jev's status-object pattern: explicit capability + status string,
23
+ // not a bare boolean.
24
+ available: true,
25
+ capability: 'MCP registration + AGENTS.md instructions',
26
+ status: CODEGRAPH_STATUS,
27
+ platforms: ['copilot', 'opencode'],
28
+ detect: detectCodeGraph,
29
+ run: runCodegraph,
30
+ },
31
+ jev: {
32
+ name: 'jev',
33
+ label: 'Jev',
34
+ description: 'Tier-routing tool for the Planner (local heuristic; test-gate parked).',
35
+ // Not a bare boolean: jev ships tier-routing ONLY. The test-gate half is
36
+ // parked because OpenCode plugin hooks don't fire under OpenChamber routing.
37
+ available: true,
38
+ capability: 'tier-routing only',
39
+ status: `${JEVR_STATUS}; test-gate parked — ${TEST_GATE_ISSUE}`,
40
+ platforms: ['opencode'],
41
+ detect: detectJev,
42
+ run: runJev,
43
+ },
44
+ };
45
+
46
+ export const ADDON_NAMES = Object.keys(ADDONS);
47
+
48
+ export function listAddons() {
49
+ return Object.values(ADDONS);
50
+ }
51
+
52
+ export function getAddon(name) {
53
+ if (!name) return undefined;
54
+ return ADDONS[String(name).toLowerCase()];
55
+ }
56
+
57
+ /**
58
+ * Inspect a workspace for evidence that an add-on is already present.
59
+ * Returns a list of human-readable signals (empty when not detected).
60
+ */
61
+ export function detectAddon(addon, cwd) {
62
+ if (!addon || typeof addon.detect !== 'function') return [];
63
+ try {
64
+ return addon.detect(cwd) || [];
65
+ } catch {
66
+ return [];
67
+ }
68
+ }
69
+
70
+ // ── Detectors ─────────────────────────────────────────────────────────
71
+
72
+ /**
73
+ * Detect CodeGraph artifacts written by init today. Read-only: this inspects
74
+ * existing registration without modifying it.
75
+ */
76
+ export function detectCodeGraph(cwd) {
77
+ const signals = [];
78
+
79
+ const opencodePath = join(cwd, 'opencode.json');
80
+ const opencodeCfg = readJson(opencodePath);
81
+ if (opencodeCfg?.mcp?.codegraph) signals.push('opencode.json mcp.codegraph');
82
+
83
+ const mcpPath = join(cwd, '.vscode', 'mcp.json');
84
+ const mcpCfg = readJson(mcpPath);
85
+ if (mcpCfg?.servers?.codegraph) signals.push('.vscode/mcp.json servers.codegraph');
86
+
87
+ const agentsPath = join(cwd, 'AGENTS.md');
88
+ if (existsSync(agentsPath)) {
89
+ try {
90
+ const body = readFileSync(agentsPath, 'utf8');
91
+ if (body.includes('<!-- CODEGRAPH_START -->')) signals.push('AGENTS.md CodeGraph section');
92
+ } catch {
93
+ /* ignore */
94
+ }
95
+ }
96
+
97
+ return signals;
98
+ }
99
+
100
+ function readJson(filePath) {
101
+ if (!existsSync(filePath)) return null;
102
+ try {
103
+ return JSON.parse(readFileSync(filePath, 'utf8'));
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Detect the jev tier-router plugin: its directory on disk and/or its entry in
111
+ * opencode.json's `plugins` array. Read-only.
112
+ */
113
+ export function detectJev(cwd) {
114
+ const signals = [];
115
+
116
+ if (existsSync(join(cwd, '.opencode', 'plugin', 'jev-tier-router', 'index.js'))) {
117
+ signals.push('.opencode/plugin/jev-tier-router');
118
+ }
119
+
120
+ const cfg = readJson(join(cwd, 'opencode.json'));
121
+ if (Array.isArray(cfg?.plugins) && cfg.plugins.some((p) => String(p).includes('jev-tier-router'))) {
122
+ signals.push('opencode.json plugins[]');
123
+ }
124
+
125
+ return signals;
126
+ }
package/src/cli.mjs CHANGED
@@ -5,6 +5,8 @@ import { join } from 'node:path';
5
5
  import { init } from './commands/init.mjs';
6
6
  import { doctor } from './commands/doctor.mjs';
7
7
  import { listStacks } from './commands/list-stacks.mjs';
8
+ import { add } from './commands/add.mjs';
9
+ import { listAddonsCommand } from './commands/list-addons.mjs';
8
10
  import { STACK_SIGNATURES } from './steps/detect.mjs';
9
11
 
10
12
  const HELP = `${kleur.bold('cli-five')} ${kleur.gray('— Code Like I\'m Five')}
@@ -14,7 +16,9 @@ ${kleur.bold('Usage')}
14
16
  npx cli-five <command> [options]
15
17
 
16
18
  ${kleur.bold('Commands')}
17
- init Interview + scaffold agents + project memory files
19
+ init Scaffold the 5 agents + required tooling (minimal by default)
20
+ add <name> Install an optional add-on (dispatcher; targets land later)
21
+ list-addons Show installed vs. available add-ons
18
22
  doctor Validate an existing cli-five setup
19
23
  list-stacks Show detectable tech stacks
20
24
  help Show this message
@@ -23,12 +27,19 @@ ${kleur.bold('Flags')}
23
27
  --yes, -y Accept defaults and skip confirmations (still gated on overwrite)
24
28
  --force Overwrite without confirmation. Dangerous. Use with --yes.
25
29
  --dry-run Print actions without writing files
30
+ --full-interview Run the full legacy interview (docs, goals, persona, models). Enables CodeGraph by default
31
+ --skills Force skill discovery (default: full interview only)
26
32
  --no-skills Skip the skills.sh discovery step
27
- --doc <file> Read project docs to pre-fill interview (repeatable)
33
+ --instructions Force stack-specific instruction generation
34
+ --no-instructions Skip instruction generation
35
+ --persona Include the snarky persona block
36
+ --no-persona Omit the snarky persona block
37
+ --codegraph Add CodeGraph MCP + instructions (default: only with --full-interview)
38
+ --no-codegraph Skip CodeGraph MCP + instructions
39
+ --doc <file> Read project docs to pre-fill interview (repeatable; implies --full-interview)
28
40
  --cost-mode <m> Override cost mode (premium, cheap, mixed) — skips interview question
29
41
  --target <t> Target platform: copilot or opencode (default: copilot)
30
42
  --provider <p> Model provider: copilot, opencode, opencode-go (default: platform default)
31
- --no-codegraph Skip CodeGraph MCP + instructions
32
43
  --cwd <path> Run against a directory other than the current one
33
44
  --version, -v Print version and exit
34
45
  `;
@@ -40,6 +51,10 @@ export async function run(argv) {
40
51
  switch (cmd) {
41
52
  case 'init':
42
53
  return init(args);
54
+ case 'add':
55
+ return add(args);
56
+ case 'list-addons':
57
+ return listAddonsCommand(args);
43
58
  case 'doctor':
44
59
  return doctor(args);
45
60
  case 'list-stacks':
@@ -68,8 +83,11 @@ function parse(argv) {
68
83
  yes: false,
69
84
  force: false,
70
85
  dryRun: false,
71
- skills: true,
72
- codegraph: true,
86
+ fullInterview: false,
87
+ skills: null,
88
+ instructions: null,
89
+ persona: null,
90
+ codegraph: null,
73
91
  docs: [],
74
92
  costMode: null,
75
93
  target: null,
@@ -81,7 +99,14 @@ function parse(argv) {
81
99
  if (a === '--yes' || a === '-y') out.yes = true;
82
100
  else if (a === '--force') out.force = true;
83
101
  else if (a === '--dry-run') out.dryRun = true;
102
+ else if (a === '--full-interview' || a === '--full') out.fullInterview = true;
103
+ else if (a === '--skills') out.skills = true;
84
104
  else if (a === '--no-skills') out.skills = false;
105
+ else if (a === '--instructions') out.instructions = true;
106
+ else if (a === '--no-instructions') out.instructions = false;
107
+ else if (a === '--persona') out.persona = true;
108
+ else if (a === '--no-persona') out.persona = false;
109
+ else if (a === '--codegraph') out.codegraph = true;
85
110
  else if (a === '--no-codegraph') out.codegraph = false;
86
111
  else if (a === '--doc') out.docs.push(argv[++i]);
87
112
  else if (a === '--cost-mode') out.costMode = argv[++i];