@delfini/cli 0.1.0-rc.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.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @delfini/cli
2
+
3
+ The plumbing layer of the Delfini Skill — local drift detection inside your coding agent (Claude Code), using the agent's existing LLM tokens. The CLI is deterministic and **never calls an LLM**; the host coding agent does, via the skill protocol scaffolded by `delfini install`.
4
+
5
+ ## Installation
6
+
7
+ **Recommended: `npx @delfini/cli`** — zero install, always resolves the latest published version, no global PATH pollution. Fits the run-on-each-PR cadence of `/delfini`.
8
+
9
+ ```bash
10
+ # Recommended — zero-install per invocation
11
+ npx @delfini/cli <subcommand>
12
+
13
+ # Alternative — global install (for offline / latency-sensitive teams)
14
+ npm install -g @delfini/cli
15
+
16
+ # In-repo dev iteration (when working on @delfini/cli itself)
17
+ pnpm install
18
+ pnpm --filter @delfini/cli build # required once — bin/delfini.mjs imports from dist/
19
+ pnpm exec tsx packages/cli/bin/delfini.mjs <subcommand>
20
+ ```
21
+
22
+ ## Quick start
23
+
24
+ ```bash
25
+ delfini install .
26
+ ```
27
+
28
+ That scaffolds `.claude/skills/delfini/SKILL.md`, appends a `/delfini` auto-invocation block to `CLAUDE.md` (creating it if absent), and adds `.delfini-trace/` to `.gitignore`. From then on, invoking `/delfini` inside Claude Code drives the skill protocol — the host agent runs `delfini local-prepare`, dispatches a Claude subagent against the prepared prompt, runs `delfini local-finalize` on the subagent's findings, renders the report, and offers an apply UX.
29
+
30
+ ## Subcommand reference
31
+
32
+ ### `delfini install <path> [--tool <agent>]`
33
+
34
+ Idempotently scaffold the Skill into a target repo.
35
+
36
+ - `<path>` — repo root or any subdirectory; the install resolves up to the git root.
37
+ - `--tool <agent>` — coding-agent target. **Only `CLAUDE` is valid in V1** (design-spec NG2 — Claude-only by design); `CLAUDE` is the default.
38
+ - Writes: `.claude/skills/delfini/SKILL.md` (overwrites — the documented upgrade path); `CLAUDE.md` marker block (creates the file if absent; never duplicates on re-run); `.gitignore` appends `.delfini-trace/` if not already present.
39
+ - Exit codes: `0` success; non-zero on `--tool` other than `CLAUDE`, on a target outside a git repo, or on a filesystem write failure.
40
+
41
+ ### `delfini local-prepare [--scope <paths>] [--base <ref>]`
42
+
43
+ Assemble the analysis input for the host agent to dispatch to a Claude subagent.
44
+
45
+ - `--scope <paths>` — comma-separated list overriding the persisted `.claude/skills/delfini/doc-scope.json`. Per-run only — does not modify the persisted file.
46
+ - `--base <ref>` — diff base ref. Defaults to `git merge-base HEAD origin/main`.
47
+ - `--relevance-threshold <N>` — render only the doc **sections** scoring at/above N against the diff, most-relevant-first up to the prompt budget. See [`@delfini/drift-engine`](../drift-engine/README.md#relevance-gating-opt-in)'s relevance-gating documentation for the scoring contract. **Default: `5` (token-efficient retrieval on)** — a measured ~40%+ prompt-token reduction on doc-heavy runs. Pass `--relevance-threshold 0` to disable retrieval and embed every in-scope doc whole. Example: `delfini local-prepare --relevance-threshold 8`.
48
+ - Writes three files to `.delfini-trace/`: `analysis-input.json`, `analysis-prompt.md`, `schema.json`.
49
+ - Exit codes:
50
+ - `0` — success.
51
+ - `2` — no doc-scope configured AND no `--scope` flag (NFR47 mode 5).
52
+ - `4` — non-doc prompt payload alone exceeds the budget — i.e. the filtered diff + schema + instructions do not fit, or no doc section fits after ranked-fill (NFR47 mode 4); emits a `prompt_too_large` JSON payload on stdout suggesting a narrower scope, a smaller PR, or a smaller diff. Doc-only overflow no longer hard-fails: when `--relevance-threshold` is set, retained sections that exceed the budget are ranked-filled (most-relevant-first) and the run exits `0` with a `dropped N section(s) — over prompt budget` line on stderr.
53
+
54
+ ### `delfini local-finalize <findings.json>`
55
+
56
+ Validate the subagent's findings, reconcile line numbers, render the report.
57
+
58
+ - `<findings.json>` — path to the subagent's output (the host agent writes it to `.delfini-trace/findings.json` per the skill protocol). Relative paths resolve against the repo root.
59
+ - Reads `.delfini-trace/analysis-input.json` to recover the docs array.
60
+ - Writes `.delfini-trace/report.md`; prints the same content to stdout.
61
+ - Exit codes:
62
+ - `0` — no apply-eligible findings (drift + additive both empty; clarifications may still be reported informationally).
63
+ - `1` — at least one drift or additive finding.
64
+ - `3` — schema validation failure (NFR47 mode 1); emits a `{"error":"schema_validation","issues":[...]}` payload on stderr.
65
+
66
+ ### `delfini --reset-scope`
67
+
68
+ Delete `.claude/skills/delfini/doc-scope.json`. Silent no-op if the file is absent or the current directory is outside a git repo. Exit code: `0`.
69
+
70
+ ### `delfini --version`
71
+
72
+ Print the `@delfini/cli` version and exit. Exit code: `0`.
73
+
74
+ ## Dev workflow
75
+
76
+ ```bash
77
+ # Build (required before running bin/delfini.mjs under node)
78
+ pnpm --filter @delfini/cli build
79
+
80
+ # Type-check
81
+ pnpm --filter @delfini/cli typecheck
82
+
83
+ # Lint
84
+ pnpm --filter @delfini/cli lint
85
+
86
+ # Run the unit + integration test suite (vitest)
87
+ pnpm --filter @delfini/cli test
88
+
89
+ # Smoke-test the published artefact locally
90
+ pnpm --filter @delfini/cli build
91
+ cd packages/cli
92
+ npm pack
93
+ npm install -g ./delfini-cli-*.tgz
94
+ delfini --version
95
+ npm uninstall -g @delfini/cli
96
+ ```
97
+
98
+ The vitest suite targets the TypeScript source directly (no build required). The `node bin/delfini.mjs ...` path imports the compiled `dist/cli.js`, so a build is required for manual bin invocation between source edits.
99
+
100
+ ## What the CLI does NOT do
101
+
102
+ - **Never calls an LLM.** All LLM dispatch happens inside the skill protocol via the host coding agent's `Agent` tool (FR145). The CLI ships no Anthropic / OpenAI / LangChain client and reads no LLM API key.
103
+ - Never pushes to a remote, never opens or comments on PRs, never modifies anything outside the working tree.
104
+ - Never integrates with CI; the Delfini GitHub Action (`@delfini/action`) is a separate product that shares the underlying analysis algorithm via `@delfini/drift-engine`.
105
+
106
+ ## Architecture
107
+
108
+ The CLI is one of three Delfini surfaces — the Skill (this package + `packages/drift-engine`), the Action (`apps/action`), and the hosted Web platform (`apps/web`). The Skill and the Action share `@delfini/drift-engine` as their analysis core, so a finding the Skill surfaces locally matches the finding the Action would surface on the eventual PR.
109
+
110
+ See `_bmad-output/planning-artifacts/architecture.md` §`Skill — packages/drift-engine + packages/cli` for the full design.
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ // Executable entry point for the `delfini` CLI.
3
+ //
4
+ // All routing logic lives in src/cli.ts (compiled to dist/cli.js — the
5
+ // published artefact). The .mjs extension here keeps this file ESM
6
+ // regardless of how the consumer's package.json declares "type".
7
+ //
8
+ // Imports from `../dist/cli.js`: that is the canonical published target.
9
+ // Published consumers (`npm install -g @delfini/cli`) only get `dist/` via
10
+ // the `"files"` allow-list in package.json. In-repo iteration must run
11
+ // `pnpm --filter @delfini/cli build` once so dist/ exists; thereafter
12
+ // `node packages/cli/bin/delfini.mjs ...` works from the repo root, and
13
+ // the vitest suite (which targets `src/` directly) does not need a build.
14
+ //
15
+ // `process.exitCode = 1` is used in preference to `process.exit(1)` so
16
+ // that pending stdout writes flush naturally as the event loop drains.
17
+
18
+ import { main } from '../dist/cli.js'
19
+
20
+ main(process.argv).catch((err) => {
21
+ console.error(err instanceof Error ? err.message : String(err))
22
+ process.exitCode = 1
23
+ })