devlyn-cli 3.1.2 → 3.2.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 (33) hide show
  1. package/AGENTS.md +6 -6
  2. package/CLAUDE.md +7 -7
  3. package/README.md +34 -13
  4. package/bin/devlyn.js +44 -65
  5. package/bin/instruction-templates.json +82 -0
  6. package/bin/instructions.js +187 -0
  7. package/config/skills/_shared/adapters/claude.md +1 -5
  8. package/config/skills/_shared/adapters/codex.md +1 -5
  9. package/config/skills/_shared/adapters/omp.md +0 -4
  10. package/config/skills/_shared/archive_run.py +5 -1
  11. package/config/skills/_shared/codex-config.md +3 -14
  12. package/config/skills/_shared/engine-preflight.md +3 -3
  13. package/config/skills/_shared/expected.schema.json +2 -2
  14. package/config/skills/_shared/platform-support.py +4 -1
  15. package/config/skills/_shared/resolve-bootstrap.py +22 -2
  16. package/config/skills/_shared/role-config.py +14 -2
  17. package/config/skills/_shared/spec-verify-check.py +427 -101
  18. package/config/skills/_shared/state-phase-write.py +497 -49
  19. package/config/skills/_shared/task-complete.py +46 -5
  20. package/config/skills/_shared/terminal-claim-check.py +2 -2
  21. package/config/skills/devlyn:ideate/SKILL.md +1 -1
  22. package/config/skills/devlyn:ideate/references/from-spec-mode.md +1 -1
  23. package/config/skills/devlyn:ideate/references/spec-template.md +2 -2
  24. package/config/skills/devlyn:resolve/SKILL.md +29 -51
  25. package/config/skills/devlyn:resolve/references/free-form-mode.md +2 -2
  26. package/config/skills/devlyn:resolve/references/phases/build-gate.md +8 -7
  27. package/config/skills/devlyn:resolve/references/phases/cleanup.md +5 -11
  28. package/config/skills/devlyn:resolve/references/phases/implement.md +1 -0
  29. package/config/skills/devlyn:resolve/references/phases/plan.md +3 -3
  30. package/config/skills/devlyn:resolve/references/state-schema.md +10 -9
  31. package/package.json +2 -1
  32. package/scripts/lint-skills.sh +14 -4
  33. package/scripts/test-owner-phases.py +193 -0
package/AGENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Codex Project Instructions
2
2
 
3
- devlyn-cli installs `/devlyn:ideate` (optional) and `/devlyn:resolve` (required) into Claude Code, plus the contract below. Codex CLI reads this file when invoked inside a project that has it. The principles are non-negotiable on every change.
3
+ devlyn-cli installs planning and full-pipeline skills alongside the execution contract below. Codex CLI reads this file when invoked inside a project that has it. The principles are non-negotiable on every change.
4
4
 
5
5
  ## North Star
6
6
 
@@ -27,22 +27,22 @@ Three discipline rules govern HOW the principles are applied:
27
27
  ## Quick Start
28
28
 
29
29
  ```text
30
- ideate (optional) -> resolve -> ship
30
+ inspect intent -> direct work or full resolve -> ship
31
31
  ```
32
32
 
33
33
  - `/devlyn:ideate` (optional) — unstructured idea → `docs/specs/<id>/spec.md` + `spec.expected.json`. Modes: default Q&A, `--quick` (autonomous-pipeline-safe), `--from-spec <path>`, `--project` (multi-feature).
34
- - `/devlyn:resolve` — hands-free pipeline for any coding task. Free-form goal, `--spec <path>`, or `--verify-only <ref> --spec <path>`. Phases run inline: PLAN → IMPLEMENT → BUILD_GATE → CLEANUP → VERIFY (fresh-subagent, findings-only).
34
+ - `/devlyn:resolve` — full pipeline for work selected by the conversational-entry rules below. Free-form goal, `--spec <path>`, or `--verify-only <ref> --spec <path>`. Phases: PLAN → IMPLEMENT → BUILD_GATE → CLEANUP → VERIFY (fresh-subagent, findings-only).
35
35
  - `/devlyn:design-ui` — required creative UI exploration surface. Spawns a 5-specialist design team (Creative Director, Product Designer, Visual Designer, Interaction Designer, Accessibility Designer) to generate N (default 5) portfolio-worthy HTML/CSS samples. The optional `/devlyn:reap` companion lives in `optional-skills/` and installs only when the user opts in.
36
36
 
37
37
  Each skill's `SKILL.md` is the source of truth for flags and workflow. Do not duplicate.
38
38
 
39
39
  Engine roles: orchestrator = whichever CLI the user opened (this contract is symmetric with CLAUDE.md. If you are Codex orchestrating `/devlyn:resolve`, the phase machinery is mandatory regardless of task size; if you will not run it, say so explicitly and stop — do not silently degrade to ad-hoc execution); legacy executor (with optional separate worker/primary/pair profiles) uses the canonical skill's orchestrator-supported default, overridable per run with `--engine <name>` or durably via machine-local `.devlyn/engines.json` `{"executor": "<name>", "pair_judge_priority": ["<name>", ...]}`; PLAN is orchestrator-fixed and never inherits `--engine` or the executor pin; pair judge = first available OTHER engine, default for VERIFY whenever available; risk probes remain conditional and --no-pair opts out. Pins fail closed (`BLOCKED:<engine>-unavailable` / `BLOCKED:invalid-engine-config`); new engines plug in by shipping `_shared/adapters/<name>.md`. `/devlyn:engines` (no args) shows the role table + detected engines; `executor <name>` / `pair <name>,...` / `clear` manage the pins. The executor pin binds the orchestrator in plain conversation too, not only inside a skill run: when you would implement directly and executor is pinned to a non-default engine, route that work through the pin — via `/devlyn:resolve` (reads it at PHASE 0) or by delegating to that engine; no pin / no file → use the canonical skill's orchestrator-supported default. The pair-judge pin stays pipeline-scoped. `/devlyn:engines role` configures explicit engine/model/effort for supported worker and judge routes; `/devlyn:resolve --role-config <path>` overrides roles for one run. Canonical skills define precedence, evidence and unsupported-route errors; absent profiles preserve existing defaults.
40
40
 
41
- Conversational entry: Before writes, inspect requested files and relevant callers/tests. Default to direct execution only for clear, local, reversible, low-risk work with a decisive existing or small task-specific acceptance check. Preserve requested behavior, scope, explicit constraints and the executor pin; make scoped edits, run required checks, review the final diff, and report changes with evidence. One concise route/check explanation suffices; ordinary direct work needs no spec, pipeline state, phase workers or extra approval. For direct/full/queue work, the outer owner reads `references/task-completion.md` relative to the installed `devlyn:resolve` skill's directory: prospective task-branch ownership (linked worktree optional), scoped commit acceptance, then PR/merge and recoverable owned-resource cleanup after required archive/queue commits. Local-only/no-push wins; delivery pending/failure stays separate from product verification.
41
+ Conversational entry: Before loading a workflow or writing files, inspect the requested change and relevant callers/tests. Quoted phase names, past logs and skill-file paths are context, not an instruction to invoke that workflow; follow the current user request. Default to direct execution when inspection makes the requested behavior, affected boundaries and verification tractable in the current context, including bounded multi-file work. Preserve requested behavior, scope, explicit constraints and the executor pin; make scoped edits, run required checks, review the final diff, and report changes with evidence. One concise route/check explanation suffices; ordinary direct work needs no spec, pipeline state, phase workers or extra approval. For direct/full/queue work, the outer owner reads `references/task-completion.md` relative to the installed `devlyn:resolve` skill's directory: prospective task-branch ownership (linked worktree optional), scoped commit acceptance, then PR/merge and recoverable owned-resource cleanup after required archive/queue commits. Local-only/no-push wins; delivery pending/failure stays separate from product verification.
42
42
 
43
- Use full `/devlyn:resolve` for material ambiguity, subsystem/design work, security/auth/payment/persistence/concurrency/public-API-contract changes, or inconclusive inspection/checks. Counts of lines, files or words alone do not establish low risk. Explicit resolve (even small), formal spec workflows and queue drains retain all canonical phases, independent verification, pins and failure handling. If new risk appears mid-edit, preserve the delta in the full run's scope/evidence; do not hide it in a new baseline or reconfirm existing authorization.
43
+ Use full `/devlyn:resolve` automatically only when inspection identifies interacting requirements or verification work too complex to manage reliably in the current context, such as coupled durable state, concurrent ownership and failure recovery across boundaries. Name the concrete interaction or verification gap. Domain labels (security/auth/payment/persistence/concurrency/API), file count and a spec document alone do not trigger it. Investigate or clarify missing intent/access first; a pipeline cannot supply them. Direct work still requires risk-proportionate checks and independent review for consequential changes. Explicit resolve (even small), explicit spec-mode workflows and queue drains retain all canonical phases, independent verification, pins and failure handling. If the full-route condition emerges mid-edit, carry the existing delta into its scope/evidence; do not reset the baseline or reconfirm existing authorization.
44
44
 
45
- Full-route handoff + loop engineering: the orchestrating model — not the user — invokes the skills. `/devlyn:queue` fronts the intent queue (`docs/specs/queue.md`): no args = status, `add <intent>`, `drain` = serial unattended drain per the contract below. Large agreed work is written to `docs/specs/<id>/spec.md` (always a spec file — the user's reviewed contract; free-form large assumes-and-logs instead of halting, zero-scope-signal goals still halt to ideate), summarized once for user review, then run hands-free via `--spec`. Outer loop per task: before the first full run, commit the scoped queue-item delta (when applicable) and linked spec bundle; findings-backed verdicts (NEEDS_WORK, verify/build-gate exhaustion) get a committed spec amendment before re-run, max 3 iterations; infrastructure / invalid-input / engine-availability / implement-empty BLOCKED verdicts surface immediately. Unattended queue drain (`docs/specs/queue.md`, strictly serial): assumptions may only take scope-narrowing, reversible, non-user-visible defaults — material ambiguity marks the item `[F] needs-review`; commit every terminal `[x]` / `[F]` transition before advancing. The canonical commit order is `config/skills/devlyn:resolve/references/outer-loop.md`.
45
+ Full-route handoff + loop engineering: the orchestrating model — not the user — invokes the skills. `/devlyn:queue` fronts the intent queue (`docs/specs/queue.md`): no args = status, `add <intent>`, `drain` = serial unattended drain per the contract below. Work selected for full resolve is written to `docs/specs/<id>/spec.md` (always a spec file — the user's reviewed contract; free-form large assumes-and-logs instead of halting, zero-scope-signal goals still halt to ideate), summarized once for user review, then run hands-free via `--spec`. Outer loop per task: before the first full run, commit the scoped queue-item delta (when applicable) and linked spec bundle; findings-backed verdicts (NEEDS_WORK, verify/build-gate exhaustion) get a committed spec amendment before re-run, max 3 iterations; infrastructure / invalid-input / engine-availability / implement-empty BLOCKED verdicts surface immediately. Unattended queue drain (`docs/specs/queue.md`, strictly serial): assumptions may only take scope-narrowing, reversible, non-user-visible defaults — material ambiguity marks the item `[F] needs-review`; commit every terminal `[x]` / `[F]` transition before advancing. The canonical commit order is `config/skills/devlyn:resolve/references/outer-loop.md`.
46
46
 
47
47
  ## Subtractive-First Editing
48
48
 
package/CLAUDE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Project Instructions
2
2
 
3
- devlyn-cli installs `/devlyn:ideate` (optional) and `/devlyn:resolve` (required) into Claude Code, plus the contract below. These principles are non-negotiable on every change — yours and any sub-agent's.
3
+ devlyn-cli installs planning and full-pipeline skills alongside the execution contract below. These principles are non-negotiable on every change — yours and any sub-agent's.
4
4
 
5
5
  ## North Star
6
6
 
@@ -28,10 +28,10 @@ The runtime sub-agent contract below (Subtractive-first / Goal-locked / No-worka
28
28
 
29
29
  ## Quick Start
30
30
 
31
- Engine selection follows the role map below.
31
+ Choose direct work or full resolve using the conversational-entry rules below; engine selection follows the role map.
32
32
 
33
33
  1. `/devlyn:ideate` (optional) — unstructured idea → `docs/specs/<id>/spec.md` + `spec.expected.json`. Modes: default Q&A, `--quick` (autonomous-pipeline-safe), `--from-spec <path>`, `--project`.
34
- 2. `/devlyn:resolve` — hands-free pipeline for any coding task. Free-form goal, `--spec <path>`, or `--verify-only <diff> --spec <path>`. Phases: PLAN → IMPLEMENT → BUILD_GATE → CLEANUP → VERIFY (fresh subagent, findings-only).
34
+ 2. `/devlyn:resolve` — full pipeline for work selected by the conversational-entry rules below. Free-form goal, `--spec <path>`, or `--verify-only <diff> --spec <path>`. Phases: PLAN → IMPLEMENT → BUILD_GATE → CLEANUP → VERIFY (fresh subagent, findings-only).
35
35
 
36
36
  Each skill's `SKILL.md` is the source of truth for its flags and workflow — don't duplicate them here.
37
37
 
@@ -51,13 +51,13 @@ Each skill's `SKILL.md` is the source of truth for its flags and workflow — do
51
51
 
52
52
  ### Conversational entry + full-route handoff
53
53
 
54
- Before writes, inspect requested files and relevant callers/tests. Default to direct execution only for clear, local, reversible, low-risk work with a decisive existing or small task-specific acceptance check. Preserve requested behavior, scope, explicit constraints and the executor pin; make scoped edits, run required checks, review the final diff, and report changes with evidence. One concise route/check explanation suffices; ordinary direct work needs no spec, pipeline state, phase workers or extra approval. For direct/full/queue work, the outer owner reads `references/task-completion.md` relative to the installed `devlyn:resolve` skill's directory: prospective task-branch ownership (linked worktree optional), scoped commit acceptance, then PR/merge and recoverable owned-resource cleanup after required archive/queue commits. Local-only/no-push wins; delivery pending/failure stays separate from product verification.
54
+ Before loading a workflow or writing files, inspect the requested change and relevant callers/tests. Quoted phase names, past logs and skill-file paths are context, not an instruction to invoke that workflow; follow the current user request. Default to direct execution when inspection makes the requested behavior, affected boundaries and verification tractable in the current context, including bounded multi-file work. Preserve requested behavior, scope, explicit constraints and the executor pin; make scoped edits, run required checks, review the final diff, and report changes with evidence. One concise route/check explanation suffices; ordinary direct work needs no spec, pipeline state, phase workers or extra approval. For direct/full/queue work, the outer owner reads `references/task-completion.md` relative to the installed `devlyn:resolve` skill's directory: prospective task-branch ownership (linked worktree optional), scoped commit acceptance, then PR/merge and recoverable owned-resource cleanup after required archive/queue commits. Local-only/no-push wins; delivery pending/failure stays separate from product verification.
55
55
 
56
- Use full `/devlyn:resolve` for material ambiguity, subsystem/design work, security/auth/payment/persistence/concurrency/public-API-contract changes, or inconclusive inspection/checks. Counts of lines, files or words alone do not establish low risk. Explicit resolve (even small), formal spec workflows and queue drains retain all canonical phases, independent verification, pins and failure handling. If new risk appears mid-edit, preserve the delta in the full run's scope/evidence; do not hide it in a new baseline or reconfirm existing authorization.
56
+ Use full `/devlyn:resolve` automatically only when inspection identifies interacting requirements or verification work too complex to manage reliably in the current context, such as coupled durable state, concurrent ownership and failure recovery across boundaries. Name the concrete interaction or verification gap. Domain labels (security/auth/payment/persistence/concurrency/API), file count and a spec document alone do not trigger it. Investigate or clarify missing intent/access first; a pipeline cannot supply them. Direct work still requires risk-proportionate checks and independent review for consequential changes. Explicit resolve (even small), explicit spec-mode workflows and queue drains retain all canonical phases, independent verification, pins and failure handling. If the full-route condition emerges mid-edit, carry the existing delta into its scope/evidence; do not reset the baseline or reconfirm existing authorization.
57
57
 
58
- The user does not invoke skills manually; the orchestrating model does. Large tasks agreed in conversation:
58
+ The user does not invoke skills manually; the orchestrating model does. For work selected for full resolve:
59
59
 
60
- 1. Write the agreed contract to `docs/specs/<id>/spec.md` (+ `spec.expected.json` when mechanical verifications exist). Always a spec file for large work — a written spec is the user's reviewed contract; free-form large now assumes-and-logs instead of halting (zero-scope-signal goals still halt to ideate).
60
+ 1. Write the agreed contract to `docs/specs/<id>/spec.md` (+ `spec.expected.json` when mechanical verifications exist). Use a spec file for the selected work — a written spec is the user's reviewed contract; free-form large now assumes-and-logs instead of halting (zero-scope-signal goals still halt to ideate).
61
61
  2. Present a one-screen plan-contract summary — the user's single review checkpoint, BEFORE the pipeline starts.
62
62
  3. On go-ahead, commit the scoped queue-item delta (when applicable) and linked spec bundle, then invoke `/devlyn:resolve --spec <path>` hands-free to completion.
63
63
  4. **Per-task outer loop**: read the terminal verdict. Successful normal-run archive → outer-owner completion per the canonical task-completion reference, after any terminal queue commit. Verdicts backed by spec/verification findings (NEEDS_WORK, verify/build-gate exhaustion) → adjudicate the findings, amend the spec (recorded in the spec file; spec stays read-only inside a run), commit that scoped amendment, then re-invoke — at most 3 outer iterations, then surface with the findings trail. Infrastructure, invalid-input, engine-availability, and implement-empty BLOCKED verdicts are not spec-amendable: surface immediately. Every iteration re-enters through committed durable artifacts (spec, findings, run archive), never conversation memory.
package/README.md CHANGED
@@ -27,16 +27,16 @@ 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
 
34
- ## How It Works — Two Skills, Full Cycle
34
+ ## How It Works — Direct Work or Full Pipeline
35
35
 
36
- devlyn-cli turns your AI coding agent into a hands-free development pipeline. The pipeline surface is two skills, with `/devlyn:design-ui` installed as the required creative UI surface:
36
+ devlyn-cli supports direct execution for clear, low-risk work with decisive checks and a full pipeline for work that needs it. The pipeline surface is two skills, with `/devlyn:design-ui` installed as the required creative UI surface:
37
37
 
38
38
  ```
39
- ideate (optional) → resolve → ship
39
+ inspect intent → direct work or full resolve → ship
40
40
  ```
41
41
 
42
42
  Non-Claude agents (Codex / omp / Pi / Grok): when one of these is selected, the workflows install as that agent's skills. In Codex / omp / Pi, use `$devlyn:ideate`, `$devlyn:resolve`, or `$devlyn:design-ui`; in Grok, use `/devlyn:ideate`, `/devlyn:resolve`, or `/devlyn:design-ui`, the same slash-command form as Claude Code.
@@ -60,9 +60,9 @@ Default mode produces a `docs/specs/<id>-<slug>/spec.md` plus `spec.expected.jso
60
60
 
61
61
  Skip ideate entirely if you have a spec or just want to describe the work — `/devlyn:resolve` accepts free-form goals too.
62
62
 
63
- ### Step 2 — Resolve with `/devlyn:resolve`
63
+ ### Choose direct execution or `/devlyn:resolve`
64
64
 
65
- Before writes, inspect requested files and relevant callers/tests. Clear, local, reversible, low-risk conversational edits with a decisive acceptance check run directly, honoring scope, constraints and executor pins; finish required checks and diff review, then accept the scoped commit for owner completion below. Material ambiguity, subsystem/design work, security/auth/payment/persistence/concurrency/public-API-contract changes or no decisive check use full `/devlyn:resolve`. Explicit resolve (including small tasks), formal specs and queue drains keep the full workflow below. Pass a spec, a free-form goal, or a diff to verify.
65
+ Inspect the requested behavior, affected callers and tests first. Default to direct work when its scope and verification are tractable, including bounded multi-file changes; honor constraints and executor pins, run risk-proportionate checks and independent review for consequential changes, then review the diff and deliver. Automatically use full `/devlyn:resolve` only for concrete interacting requirements or verification too complex to manage reliably in the current context, such as coupled durable state, concurrent ownership and failure recovery. Domain labels, file count and a spec document alone do not trigger it. Investigate or clarify missing intent/access first. Explicit resolve (including small tasks), explicit spec-mode workflows and queue drains keep the full workflow below.
66
66
 
67
67
  ```
68
68
  /devlyn:resolve "fix the login bug" # free-form
@@ -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
@@ -227,6 +227,27 @@ ids.
227
227
 
228
228
  ### Migration from earlier versions
229
229
 
230
+ Reinstall with the new version to refresh instructions and skills for the selected
231
+ CLIs. Claude uses `CLAUDE.md`; Codex, Grok, omp and Pi share `AGENTS.md`.
232
+ Devlyn updates its checksum-marked instruction block and preserves project-specific
233
+ rules outside it. Keep custom rules outside the block; they take precedence over
234
+ its defaults. Exact pre-update backups are saved in `.devlyn/instructions/`.
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
+ Templates from versions 3.0.0–3.1.2 migrate automatically, including custom
239
+ text before or after the template. Replaced or extended introductions under the
240
+ original project heading also migrate when the entire body from `## North Star`
241
+ onward matches a known template hash. Replacement project rules are preserved
242
+ outside the managed block; only an exact original introduction is removed.
243
+ Recognizable leftover Devlyn text, changed bodies and ambiguous templates stop
244
+ with a concise recovery message and leave the original unchanged. The message
245
+ links an exact `.backup`, new defaults in `.incoming`, and a `.merge.md` guide.
246
+ Compare the two files, preserve all project rules outside one new managed block,
247
+ and rerun the same install command. Earlier selected agents may already be updated.
248
+ Plain custom instruction files retain their contents and gain a managed block.
249
+ Use 3.1.3 or newer consistently: older installers still overwrite `CLAUDE.md`.
250
+
230
251
  <!-- legacy-surface-map:begin — retired command names below are documented as OLD, not current; lint Check 10c skips this block -->
231
252
  Earlier versions of devlyn-cli shipped 16+ slash commands. The iter-0034 Phase 4 cutover (2026-05-04) and the 2026-05-14 follow-up consolidated them down to the three current commands. Upgrades automatically purge the legacy skill directories from `~/.claude/skills/`.
232
253
 
package/bin/devlyn.js CHANGED
@@ -9,6 +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, InstructionError } = require('./instructions');
12
13
 
13
14
  // The devlyn skill bundle installed into every skill-capable agent's loader
14
15
  // directory. Single source of truth so codex/omp/pi stay in lockstep — adding a
@@ -700,20 +701,6 @@ function installSkillsForCLI(cliKey) {
700
701
  return copied;
701
702
  }
702
703
 
703
- // Strip the legacy "# Devlyn Agent Instructions" block (the retired evaluator
704
- // agent) that earlier installs appended to instruction files, so upgrades scrub
705
- // it instead of carrying it forever. Anchors on the LAST marker so user content
706
- // above a stray copy of the block is preserved.
707
- const DEVLYN_AGENTS_MARKER = '# Devlyn Agent Instructions';
708
- function stripManagedBlock(content) {
709
- const markerIdx = content.lastIndexOf(DEVLYN_AGENTS_MARKER);
710
- if (markerIdx > 0) {
711
- const sepIdx = content.lastIndexOf('\n---', markerIdx);
712
- return content.slice(0, sepIdx > 0 ? sepIdx : markerIdx).trimEnd();
713
- }
714
- return content.trimEnd();
715
- }
716
-
717
704
  // One-line description of exactly what selecting a CLI target installs, so the
718
705
  // unified selector stays honest.
719
706
  function targetDesc(cli) {
@@ -723,35 +710,10 @@ function targetDesc(cli) {
723
710
  return `${cli.instructionsFile} instructions only`;
724
711
  }
725
712
 
726
- // Install the packaged base instruction file for a CLI (project AGENTS.md for
727
- // codex/omp/pi). Creates it from the packaged base when missing; on existing
728
- // files, scrubs the legacy appended block and otherwise leaves user content
729
- // untouched. Returns true when the destination was written.
713
+ // Shared instruction ownership and preservation rules apply to every CLI.
730
714
  function installInstructionsForCLI(cliKey) {
731
715
  const cli = CLI_TARGETS[cliKey];
732
- if (!cli || !cli.baseInstructionsFile) return false;
733
-
734
- const destFile = path.join(process.cwd(), cli.instructionsFile);
735
- const baseInstructionsSrc = path.join(__dirname, '..', cli.baseInstructionsFile);
736
- let content = null;
737
- if (fs.existsSync(destFile)) {
738
- const current = fs.readFileSync(destFile, 'utf8');
739
- if (current.includes(DEVLYN_AGENTS_MARKER)) {
740
- content = stripManagedBlock(current) + '\n';
741
- } else {
742
- log(` → Preserved existing ${cli.instructionsFile}. Compare with the bundled template at ${baseInstructionsSrc} and merge relevant instructions.`, 'dim');
743
- }
744
- } else {
745
- if (fs.existsSync(baseInstructionsSrc)) {
746
- content = fs.readFileSync(baseInstructionsSrc, 'utf8');
747
- }
748
- }
749
- if (content === null) return false;
750
-
751
- log(`\n🤖 Installing instructions for ${cli.name}...`, 'cyan');
752
- fs.writeFileSync(destFile, content);
753
- log(` → ${cli.instructionsFile}`, 'dim');
754
- return true;
716
+ return cli?.baseInstructionsFile ? updateInstructions(cli.baseInstructionsFile) : false;
755
717
  }
756
718
 
757
719
  // Install both instructions and skills for a single CLI. Used by the
@@ -803,8 +765,31 @@ function installAgentsForAllDetected() {
803
765
  // Install the Claude Code core config: project .claude/ (skills, templates,
804
766
  // settings, CLAUDE.md, .gitignore) plus global ~/.claude/settings.json tweaks.
805
767
  // Extracted so the unified target selector installs it only when "Claude Code"
806
- // is chosen. Behavior is unchanged from the previous unconditional core install.
768
+ // is chosen. Check global settings and instruction conflicts before writes.
807
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();
792
+ updateInstructions('CLAUDE.md');
808
793
  const targetDir = getTargetDir();
809
794
  const skillsDir = path.join(targetDir, 'skills');
810
795
  log('\n📁 Installing Claude Code config to .claude/', 'green');
@@ -831,15 +816,6 @@ function installClaudeCore() {
831
816
  log(`\n🧹 Cleaned up ${removed} deprecated file${removed > 1 ? 's' : ''}`, 'yellow');
832
817
  }
833
818
 
834
- // Copy Claude project instructions to project root. Other CLI instruction
835
- // files are installed only when explicitly selected below or via `agents`.
836
- const claudeMdSrc = path.join(__dirname, '..', 'CLAUDE.md');
837
- const claudeMdDest = path.join(process.cwd(), 'CLAUDE.md');
838
- if (fs.existsSync(claudeMdSrc)) {
839
- fs.copyFileSync(claudeMdSrc, claudeMdDest);
840
- log(' → CLAUDE.md', 'dim');
841
- }
842
-
843
819
  // Keep installer-managed pipeline state and install metadata out of git.
844
820
  const gitignorePath = path.join(process.cwd(), '.gitignore');
845
821
  const gitignoreEntries = ['.devlyn/', '.claude/skills/.devlyn-install.json'];
@@ -950,17 +926,8 @@ function installClaudeCore() {
950
926
  }
951
927
 
952
928
  // Configure global Claude Code settings (~/.claude/settings.json)
953
- const globalClaudeDir = path.join(os.homedir(), '.claude');
954
- const globalSettingsPath = path.join(globalClaudeDir, 'settings.json');
955
- let globalSettings = {};
956
- if (fs.existsSync(globalSettingsPath)) {
957
- try {
958
- globalSettings = JSON.parse(fs.readFileSync(globalSettingsPath, 'utf8'));
959
- } catch {
960
- globalSettings = {};
961
- }
962
- }
963
- if (!globalSettings.env) globalSettings.env = {};
929
+ // Project settings may refer to this same file; merge the latest bytes.
930
+ const globalSettings = readGlobalSettings();
964
931
  let globalSettingsChanged = false;
965
932
  if (!globalSettings.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING) {
966
933
  globalSettings.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING = '1';
@@ -1250,6 +1217,8 @@ function showBenchmarkModeHelp(mode) {
1250
1217
 
1251
1218
  // Main
1252
1219
  const args = process.argv.slice(2);
1220
+
1221
+ async function main() {
1253
1222
  const command = args[0];
1254
1223
 
1255
1224
  switch (command) {
@@ -1259,7 +1228,7 @@ switch (command) {
1259
1228
  break;
1260
1229
  case '-y':
1261
1230
  case '--yes':
1262
- init(true);
1231
+ await init(true);
1263
1232
  break;
1264
1233
  case 'list':
1265
1234
  case 'ls':
@@ -1314,10 +1283,14 @@ switch (command) {
1314
1283
  log('\n🤖 Installing agents for all supported CLIs...', 'blue');
1315
1284
  const count = installSelectedCLITargets(Object.keys(CLI_TARGETS));
1316
1285
  log(`\n✅ Agents installed for ${count} CLI${count !== 1 ? 's' : ''}`, 'green');
1317
- } else if (subArg && CLI_TARGETS[subArg]) {
1286
+ } else if (Object.keys(CLI_TARGETS).includes(subArg)) {
1318
1287
  // Install for a specific CLI
1319
1288
  installAgentsForCLI(subArg);
1320
1289
  log('\n✅ Done!', 'green');
1290
+ } else if (subArg !== undefined) {
1291
+ log(`Unknown CLI target: ${JSON.stringify(subArg)}`, 'yellow');
1292
+ log(`Supported: ${Object.keys(CLI_TARGETS).join(', ')}, all`, 'dim');
1293
+ process.exit(1);
1321
1294
  } else {
1322
1295
  // Auto-detect and install
1323
1296
  const detected = detectOtherCLIs();
@@ -1336,10 +1309,16 @@ switch (command) {
1336
1309
  }
1337
1310
  case 'init':
1338
1311
  case undefined:
1339
- init(false);
1312
+ await init(false);
1340
1313
  break;
1341
1314
  default:
1342
1315
  log(`Unknown command: ${command}`, 'yellow');
1343
1316
  showHelp();
1344
1317
  process.exit(1);
1345
1318
  }
1319
+ }
1320
+
1321
+ main().catch((error) => {
1322
+ console.error(error instanceof InstructionError ? `\n${error.message}` : error);
1323
+ process.exitCode = 1;
1324
+ });
@@ -0,0 +1,82 @@
1
+ {
2
+ "AGENTS.md": [
3
+ {
4
+ "versions": [
5
+ "3.0.0"
6
+ ],
7
+ "length": 11091,
8
+ "sha256": "099e68f3e203fa735bb1ebcd780a0e18bde462ea7de5f74fb3d17cee5e64b63a",
9
+ "preamble": "# Codex Project Instructions\n\ndevlyn-cli installs `/devlyn:ideate` (optional) and `/devlyn:resolve` (required) into Claude Code, plus the contract below. Codex CLI reads this file when invoked inside a project that has it. The principles are non-negotiable on every change.\n\n",
10
+ "body": {
11
+ "length": 10816,
12
+ "sha256": "8b972c2c379e5ff8411bf1214f926a3b9904bdf857ac6b30a8c79ce0e32182e3"
13
+ }
14
+ },
15
+ {
16
+ "versions": [
17
+ "3.0.1"
18
+ ],
19
+ "length": 12166,
20
+ "sha256": "33fbd5b01c8e65f4c663f95423a45b16dcf5bb28f1fd0c6791079abd20c267ae",
21
+ "preamble": "# Codex Project Instructions\n\ndevlyn-cli installs `/devlyn:ideate` (optional) and `/devlyn:resolve` (required) into Claude Code, plus the contract below. Codex CLI reads this file when invoked inside a project that has it. The principles are non-negotiable on every change.\n\n",
22
+ "body": {
23
+ "length": 11891,
24
+ "sha256": "52326a832df6d5ab13bddca5a1a01ad338a3db71d172c70d3eaed806668db4d8"
25
+ }
26
+ },
27
+ {
28
+ "versions": [
29
+ "3.1.0",
30
+ "3.1.1",
31
+ "3.1.2"
32
+ ],
33
+ "length": 12581,
34
+ "sha256": "acd3672efaa33e75455f8530a7425e950c01c822c6f5d85d7dbc9d7ff1958028",
35
+ "preamble": "# Codex Project Instructions\n\ndevlyn-cli installs `/devlyn:ideate` (optional) and `/devlyn:resolve` (required) into Claude Code, plus the contract below. Codex CLI reads this file when invoked inside a project that has it. The principles are non-negotiable on every change.\n\n",
36
+ "body": {
37
+ "length": 12306,
38
+ "sha256": "0de6061f9dff8e88964e542bb8a5e113996e480bef597774fb15eaa766472129"
39
+ }
40
+ }
41
+ ],
42
+ "CLAUDE.md": [
43
+ {
44
+ "versions": [
45
+ "3.0.0"
46
+ ],
47
+ "length": 24434,
48
+ "sha256": "29ddcc5ef4d5b44b2ba6aae22bd3c0c033ceaa47b240bd647aaefbe00f2a6e4e",
49
+ "preamble": "# Project Instructions\n\ndevlyn-cli installs `/devlyn:ideate` (optional) and `/devlyn:resolve` (required) into Claude Code, plus the contract below. These principles are non-negotiable on every change \u2014 yours and any sub-agent's.\n\n",
50
+ "body": {
51
+ "length": 24204,
52
+ "sha256": "555d8b9a4c154ec5225105919836a4d9831c76c1b1c7ee4c19fecac8fbd1c948"
53
+ }
54
+ },
55
+ {
56
+ "versions": [
57
+ "3.0.1"
58
+ ],
59
+ "length": 25417,
60
+ "sha256": "50301bf98507c83da1576be7f443885236e7d421ff2e40fd077ed9652ea3a2e0",
61
+ "preamble": "# Project Instructions\n\ndevlyn-cli installs `/devlyn:ideate` (optional) and `/devlyn:resolve` (required) into Claude Code, plus the contract below. These principles are non-negotiable on every change \u2014 yours and any sub-agent's.\n\n",
62
+ "body": {
63
+ "length": 25187,
64
+ "sha256": "53a933e22369438a5d9dd7aa5f778909aeba5a38d5e2e68b8a36dab5c874d84d"
65
+ }
66
+ },
67
+ {
68
+ "versions": [
69
+ "3.1.0",
70
+ "3.1.1",
71
+ "3.1.2"
72
+ ],
73
+ "length": 26152,
74
+ "sha256": "d11a863907dabdf827063df7133a8a46a14e018f608884f08c601bf36e0f3f0c",
75
+ "preamble": "# Project Instructions\n\ndevlyn-cli installs `/devlyn:ideate` (optional) and `/devlyn:resolve` (required) into Claude Code, plus the contract below. These principles are non-negotiable on every change \u2014 yours and any sub-agent's.\n\n",
76
+ "body": {
77
+ "length": 25922,
78
+ "sha256": "dd9f0f63f559a3b9a6e2bbf102045b01f6b73bce684dd27124ee6d6af3641b91"
79
+ }
80
+ }
81
+ ]
82
+ }