devlyn-cli 3.1.3 → 3.2.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 CHANGED
@@ -27,7 +27,7 @@ If devlyn-cli saved you time, [give it a star](https://github.com/fysoul17/devly
27
27
  npx devlyn-cli
28
28
  ```
29
29
 
30
- That's it. The installer opens with a single **agent selector** — pick any combination of **Claude Code**, **Codex CLI**, **oh-my-pi (omp)**, **Pi**, or **Grok Build CLI**, and devlyn installs into every one you choose in a single pass. Claude Code and any agent already present on your machine are pre-checked, so the common case is just Enter. Skill-capable agents receive the `devlyn:resolve`, `devlyn:ideate`, and `devlyn:design-ui` skills — plus the `devlyn:engines` and `devlyn:queue` utilities — in the directory each one loads from: Codex → `~/.codex/skills/`, Grok → `~/.grok/skills/`, while **omp and Pi share `~/.agents/skills/`** — the cross-agent standard both read — so the bundle is written there once, not duplicated per agent. In Codex / omp / Pi, invoke them as skills (`$devlyn:resolve`, `$devlyn:ideate`, `$devlyn:design-ui`); in Claude Code and Grok Build CLI they're slash commands (`/devlyn:resolve`). Rerunning refreshes skills for selected agents, preserves existing AGENTS.md without legacy managed markers (legacy appended blocks are still cleaned up), and replaces CLAUDE.md when Claude Code is selected. Use the bundled AGENTS.md template path printed by the installer to compare and merge relevant shared runtime instructions into your preserved AGENTS.md, retaining your content and keeping engine-specific instructions distinct. (`npx devlyn-cli -y` installs the Claude core non-interactively; `npx devlyn-cli agents <cli>` adds one agent later.)
30
+ That's it. The installer opens with a single **agent selector** — pick any combination of **Claude Code**, **Codex CLI**, **oh-my-pi (omp)**, **Pi**, or **Grok Build CLI**, and devlyn installs into every one you choose in a single pass. Claude Code and any agent already present on your machine are pre-checked, so the common case is just Enter. Skill-capable agents receive the `devlyn:resolve`, `devlyn:ideate`, and `devlyn:design-ui` skills — plus the `devlyn:engines` and `devlyn:queue` utilities — in the directory each one loads from: Codex → `~/.codex/skills/`, Grok → `~/.grok/skills/`, while **omp and Pi share `~/.agents/skills/`** — the cross-agent standard both read — so the bundle is written there once, not duplicated per agent. In Codex / omp / Pi, invoke them as skills (`$devlyn:resolve`, `$devlyn:ideate`, `$devlyn:design-ui`); in Claude Code and Grok Build CLI they're slash commands (`/devlyn:resolve`). Rerunning refreshes skills and the managed instruction block for selected agents while preserving project rules outside it. See [Migration from earlier versions](#migration-from-earlier-versions) for legacy migration and merge recovery. (`npx devlyn-cli -y` installs the Claude core non-interactively; `npx devlyn-cli agents <cli>` adds one agent later.)
31
31
 
32
32
  ---
33
33
 
@@ -76,10 +76,10 @@ Internal phases run sequentially with file-based handoff via `.devlyn/pipeline.s
76
76
  PLAN → IMPLEMENT → BUILD_GATE → CLEANUP → VERIFY (fresh subagent, findings-only)
77
77
  ```
78
78
 
79
- - **PLAN** is the heaviest phase by design — formalizes invariants from the spec/goal and the file list to touch.
80
- - **BUILD_GATE** runs your project's real compilers, typecheckers, linters, and `python3 .claude/skills/_shared/spec-verify-check.py` (verification commands literal-match). Auto-detects Next.js, Rust, Go, Solidity, Expo, Swift, and Dockerfiles. Browser flows route through Chrome MCP → Playwright → curl tier.
79
+ - **PLAN** runs in the owner context and freezes requirements, verification and the authorized file surface before implementation.
80
+ - **BUILD_GATE** runs commands in the owner context without another model invocation, using your project's real compilers, typecheckers, linters, and `python3 .claude/skills/_shared/spec-verify-check.py` (verification commands literal-match). Auto-detects Next.js, Rust, Go, Solidity, Expo, Swift, and Dockerfiles. Browser flows route through Chrome MCP → Playwright → curl tier.
81
81
  - **VERIFY** runs in a fresh subagent context with no code-mutation tools — findings only, structurally independent.
82
- - Git checkpoints at every phase for safe rollback. Fix-loop budget shared across BUILD_GATE and VERIFY (`--max-rounds N`, default 4).
82
+ - Git checkpoints at every phase for safe rollback. Fix-loop budget shared across BUILD_GATE, CLEANUP and VERIFY (`--max-rounds N`, default 4).
83
83
 
84
84
  Common flags: `--engine claude|codex|omp` (default: the orchestrator-supported default), `--role-config <path>` (one-run worker/judge profiles, see below), `--bypass build-gate,cleanup`, `--pair-verify` (force pair-mode JUDGE in VERIFY), `--no-pair` (intentional solo VERIFY), `--risk-probes` / `--no-risk-probes`, `--perf` (per-phase timing).
85
85
  `--pair-verify` and `--no-pair` are mutually exclusive; using both stops with `BLOCKED:invalid-flags`.
@@ -118,10 +118,10 @@ Stack tasks to run back-to-back without supervision. `/devlyn:queue add "<intent
118
118
  devlyn separates three engine roles:
119
119
 
120
120
  - **Orchestrator** — the CLI you opened (Claude Code, Codex, or omp) that drives the conversation and loop. The contract is symmetric (`CLAUDE.md` ↔ `AGENTS.md`), so the same phase-gated pipeline runs whichever you launch; the file artifacts (spec, queue, state) carry over if you switch.
121
- - **Executor** (legacy) — the worker for IMPLEMENT / CLEANUP and their repair rounds, and the primary VERIFY judge, unless either is separately profiled (below). Defaults to the orchestrator-supported default. PLAN is orchestrator-fixed and never inherits `--engine` or an executor pin. An absent primary profile follows the legacy executor, not an opt-in worker profile.
121
+ - **Executor** (legacy) — the worker for IMPLEMENT, its included code/doc cleanup, and their repair rounds, and the primary VERIFY judge, unless either is separately profiled (below). Defaults to the orchestrator-supported default. PLAN is orchestrator-fixed and never inherits `--engine` or an executor pin. An absent primary profile follows the legacy executor, not an opt-in worker profile.
122
122
  - **Pair judge** — the first available *other* engine, default for VERIFY and conditional for risk probes.
123
123
 
124
- `--engine <name>` sets the legacy executor for one run as an engine-only entry (it clears any lower-precedence profile's model/effort). BUILD_GATE and risk-probe derivation keep their existing routes; PLAN always runs on the orchestrator's native worker. VERIFY/JUDGE runs pair mode by default when the OTHER engine is available.
124
+ `--engine <name>` sets the legacy executor for one run as an engine-only entry (it clears any lower-precedence profile's model/effort). PLAN stays in the owner context; BUILD_GATE/CLEANUP run owner commands; risk-probe derivation keeps its existing route. VERIFY/JUDGE runs pair mode by default when the OTHER engine is available.
125
125
 
126
126
  Pin roles durably with `/devlyn:engines` (no args shows the role table + detected engines; `executor <name>` / `pair <name>,...` / `role <worker|primary_judge|pair_judge> <JSON object>` / `role <name> clear` manage the pins, stored machine-local in `.devlyn/engines.json`; `clear` removes all legacy and role pins, preserves unrelated keys, and deletes an empty file). Pins fail closed: an unavailable pinned engine stops with `BLOCKED:<engine>-unavailable`, and a name with no `_shared/adapters/<name>.md` adapter stops with `BLOCKED:invalid-engine-config`. New engines plug in by shipping an adapter file — no skill changes.
127
127
 
@@ -144,12 +144,12 @@ Pin roles durably with `/devlyn:engines` (no args shows the role table + detecte
144
144
  `--role-config <path>` supplies the same `{"roles": {...}}` object for one run only, with no other top-level keys; the flag may appear once.
145
145
 
146
146
  - **Precedence** — an entry replaces the lower one as a whole; model/effort never merge across engines. Worker and primary judge: run `--role-config` entry > `--engine` (engine-only) > project role entry > legacy executor/default. Pair judge: run entry > project entry > `pair_judge_priority` / the OTHER-engine complement. A missing `model`/`effort` keeps that route's current default and is shown as inherited/unresolved.
147
- - **Boundaries** — `worker` covers IMPLEMENT / CLEANUP and their repair rounds; `primary_judge` and `pair_judge` cover VERIFY only. PLAN stays orchestrator-fixed, BUILD_GATE keeps its existing route, and risk-probe derivation stays outside these controls. OTHER must differ from the primary by engine: an explicit same-engine pair is `BLOCKED:invalid-engine-config`. `--no-pair` is unchanged — explicit solo VERIFY; an unused pair entry is neither dispatched nor availability-checked.
147
+ - **Boundaries** — `worker` covers IMPLEMENT, its included code/doc cleanup, and their repair rounds; `primary_judge` and `pair_judge` cover VERIFY only. PLAN stays orchestrator-fixed, BUILD_GATE/CLEANUP run owner commands, and risk-probe derivation stays outside these controls. OTHER must differ from the primary by engine: an explicit same-engine pair is `BLOCKED:invalid-engine-config`. `--no-pair` is unchanged — explicit solo VERIFY; an unused pair entry is neither dispatched nor availability-checked.
148
148
  - **Initial explicit routes** — Codex worker/primary/pair accept exact `model` + `effort` (dispatched as `-m` / `-c model_reasoning_effort=`, validated against the installed Codex CLI's native model metadata for its version). Claude primary/pair judges pass an exact `model` as `--model` and validate native result identity; model-only selection needs no source update for a new ID. Explicit `effort` separately requires the adapter's version/model support declaration. The Claude worker stays the native `Agent` route and is engine-only: explicit `model`/`effort` on a Claude worker is `BLOCKED:unsupported-role-option` — a supported-route boundary, not a claim that Claude cannot implement. Other adapters (omp, grok) are engine-only for their eligible roles.
149
149
  - **Errors** — `BLOCKED:unsupported-role-option` names the role, engine and field with guidance; there is no clamp, fallback, substituted model, or weaker retry. An explicitly selected engine (flag, pin, or profile) that is unavailable stops with `BLOCKED:<engine>-unavailable`. A native warning that a requested option was ignored is a failed promise even at exit 0.
150
150
  - **Requested vs observed** — `/devlyn:engines` status prints each role's requested engine/model/effort, source and dispatch channel before dispatch; it describes a request and does not prove a native invocation happened. Effective model is reported only with native evidence; a Codex worker receipt binds requested dispatch and leaves effective model unknown. A reported worker model reroute blocks completion even at exit 0. SURFACE_CLOSE inherits native Claude model settings and records the model from its required JSON result. Effective effort stays unknown unless native evidence establishes it — invocation arguments prove dispatch, not provider-internal reasoning.
151
151
 
152
- `--engine codex` routes IMPLEMENT / CLEANUP to Codex as a supported engine-only route (exact model/effort via the profiles above). Historical record: iter-0020 closed Codex BUILD/IMPLEMENT below the quality floor on the 9-fixture suite (L2 vs L1 = −3.6, 3/8 gated fixtures cleared the +5 margin floor — release-readiness FAIL). Separately, PLAN-pair remains research-only: iter-0033g + iter-0034 closed it with explicit unblock conditions (container/sandbox infra OR production telemetry capturing positive evidence of subagent introspection). Install the Codex CLI (https://platform.openai.com/docs/codex) and pass the flag explicitly to opt in:
152
+ `--engine codex` routes IMPLEMENT and its included code/doc cleanup to Codex as a supported engine-only route (exact model/effort via the profiles above). Historical record: iter-0020 closed Codex BUILD/IMPLEMENT below the quality floor on the 9-fixture suite (L2 vs L1 = −3.6, 3/8 gated fixtures cleared the +5 margin floor — release-readiness FAIL). Separately, PLAN-pair remains research-only: iter-0033g + iter-0034 closed it with explicit unblock conditions (container/sandbox infra OR production telemetry capturing positive evidence of subagent introspection). Install the Codex CLI (https://platform.openai.com/docs/codex) and pass the flag explicitly to opt in:
153
153
 
154
154
  ```
155
155
  /devlyn:resolve "fix the auth bug" --engine codex
@@ -232,14 +232,19 @@ CLIs. Claude uses `CLAUDE.md`; Codex, Grok, omp and Pi share `AGENTS.md`.
232
232
  Devlyn updates its checksum-marked instruction block and preserves project-specific
233
233
  rules outside it. Keep custom rules outside the block; they take precedence over
234
234
  its defaults. Exact pre-update backups are saved in `.devlyn/instructions/`.
235
-
236
- Unmodified templates from versions 3.0.0–3.1.2 migrate automatically, including
237
- custom text before or after the template. Edited or unrecognized older Devlyn
238
- templates with recognizable Devlyn contract text, changed managed blocks and
239
- ambiguous markers stop installation without replacing the original.
240
- The error names an `.incoming` file with current defaults:
241
- merge it, move custom rules outside its managed block, and rerun installation.
242
- Plain custom instruction files retain their contents and gain a managed block.
235
+ When recovery is needed, pre-existing symlinks or non-directory entries at `.devlyn/` or
236
+ `.devlyn/instructions/` stop installation without replacing the instruction file.
237
+
238
+ Older and mixed-version templates migrate automatically. Within recognizable
239
+ Devlyn sections, exact historical paragraphs and list items are replaced;
240
+ modified or unknown text and its headings are preserved outside the new block.
241
+ Edits inside an existing managed block are preserved outside it too. Plain custom
242
+ files and rules outside managed blocks retain their contents. Unknown old text
243
+ is kept conservatively, even when it may contain obsolete defaults.
244
+ Malformed or duplicate managed markers still stop with an exact `.backup`, new
245
+ defaults in `.incoming`, and a `.merge.md` guide; the original remains unchanged.
246
+ Release packaging refreshes the offline fingerprints from Git history using
247
+ `node scripts/update-instruction-templates.js`; installation needs no Git or network.
243
248
  Use 3.1.3 or newer consistently: older installers still overwrite `CLAUDE.md`.
244
249
 
245
250
  <!-- legacy-surface-map:begin — retired command names below are documented as OLD, not current; lint Check 10c skips this block -->
package/bin/devlyn.js CHANGED
@@ -9,7 +9,7 @@ const { execSync } = require('child_process');
9
9
  const CONFIG_SOURCE = path.join(__dirname, '..', 'config');
10
10
  const OPTIONAL_SKILLS_SOURCE = path.join(__dirname, '..', 'optional-skills');
11
11
  const PKG = require('../package.json');
12
- const { updateInstructions } = require('./instructions');
12
+ const { updateInstructions, InstructionError } = require('./instructions');
13
13
 
14
14
  // The devlyn skill bundle installed into every skill-capable agent's loader
15
15
  // directory. Single source of truth so codex/omp/pi stay in lockstep — adding a
@@ -765,8 +765,30 @@ function installAgentsForAllDetected() {
765
765
  // Install the Claude Code core config: project .claude/ (skills, templates,
766
766
  // settings, CLAUDE.md, .gitignore) plus global ~/.claude/settings.json tweaks.
767
767
  // Extracted so the unified target selector installs it only when "Claude Code"
768
- // is chosen. Check instruction conflicts before changing settings or skills.
768
+ // is chosen. Check global settings and instruction conflicts before writes.
769
769
  function installClaudeCore() {
770
+ const globalClaudeDir = path.join(os.homedir(), '.claude');
771
+ const globalSettingsPath = path.join(globalClaudeDir, 'settings.json');
772
+ function readGlobalSettings() {
773
+ let settings = {};
774
+ try {
775
+ if (fs.lstatSync(globalSettingsPath, { throwIfNoEntry: false })) {
776
+ settings = JSON.parse(fs.readFileSync(globalSettingsPath, 'utf8'));
777
+ }
778
+ } catch (error) {
779
+ const reason = error instanceof SyntaxError ? 'invalid JSON' : error.message;
780
+ throw new Error(`Cannot merge ${globalSettingsPath}: ${reason}. Original preserved; correct the file or its access and rerun installation.`);
781
+ }
782
+ if (!settings || typeof settings !== 'object' || Array.isArray(settings)) {
783
+ throw new Error(`Cannot merge ${globalSettingsPath}: root must be a JSON object. Original preserved; correct it and rerun installation.`);
784
+ }
785
+ if (!Object.prototype.hasOwnProperty.call(settings, 'env')) settings.env = {};
786
+ if (!settings.env || typeof settings.env !== 'object' || Array.isArray(settings.env)) {
787
+ throw new Error(`Cannot merge ${globalSettingsPath}: env must be a JSON object. Original preserved; correct it and rerun installation.`);
788
+ }
789
+ return settings;
790
+ }
791
+ readGlobalSettings();
770
792
  updateInstructions('CLAUDE.md');
771
793
  const targetDir = getTargetDir();
772
794
  const skillsDir = path.join(targetDir, 'skills');
@@ -904,17 +926,8 @@ function installClaudeCore() {
904
926
  }
905
927
 
906
928
  // Configure global Claude Code settings (~/.claude/settings.json)
907
- const globalClaudeDir = path.join(os.homedir(), '.claude');
908
- const globalSettingsPath = path.join(globalClaudeDir, 'settings.json');
909
- let globalSettings = {};
910
- if (fs.existsSync(globalSettingsPath)) {
911
- try {
912
- globalSettings = JSON.parse(fs.readFileSync(globalSettingsPath, 'utf8'));
913
- } catch {
914
- globalSettings = {};
915
- }
916
- }
917
- if (!globalSettings.env) globalSettings.env = {};
929
+ // Project settings may refer to this same file; merge the latest bytes.
930
+ const globalSettings = readGlobalSettings();
918
931
  let globalSettingsChanged = false;
919
932
  if (!globalSettings.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING) {
920
933
  globalSettings.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING = '1';
@@ -1204,6 +1217,8 @@ function showBenchmarkModeHelp(mode) {
1204
1217
 
1205
1218
  // Main
1206
1219
  const args = process.argv.slice(2);
1220
+
1221
+ async function main() {
1207
1222
  const command = args[0];
1208
1223
 
1209
1224
  switch (command) {
@@ -1213,7 +1228,7 @@ switch (command) {
1213
1228
  break;
1214
1229
  case '-y':
1215
1230
  case '--yes':
1216
- init(true);
1231
+ await init(true);
1217
1232
  break;
1218
1233
  case 'list':
1219
1234
  case 'ls':
@@ -1294,10 +1309,16 @@ switch (command) {
1294
1309
  }
1295
1310
  case 'init':
1296
1311
  case undefined:
1297
- init(false);
1312
+ await init(false);
1298
1313
  break;
1299
1314
  default:
1300
1315
  log(`Unknown command: ${command}`, 'yellow');
1301
1316
  showHelp();
1302
1317
  process.exit(1);
1303
1318
  }
1319
+ }
1320
+
1321
+ main().catch((error) => {
1322
+ console.error(error instanceof InstructionError ? `\n${error.message}` : error);
1323
+ process.exitCode = 1;
1324
+ });