@momentiq/dark-factory-cli 0.1.0-alpha.4 → 0.1.0-alpha.6

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
@@ -45,11 +45,11 @@ critic / aggregator logic lands in Phase F.
45
45
 
46
46
  ## Status
47
47
 
48
- `0.1.0-alpha.3` — extracted from `momentiq-ai/sage3c:tools/agent-review/` +
49
- `scripts/ci/` per cycle 331.1 Phases B, C, D, and E. Library API is stable;
50
- the binary's `review`/`gate`/`doctor` subcommands are still stubs (Phase F);
51
- the Phase E stubs (`status-check`, `critic`) intentionally exit 0 so the
52
- reusable workflow shapes can break the dogfood chicken-and-egg.
48
+ `0.1.0-alpha.6` — extracted from `momentiq-ai/sage3c:tools/agent-review/` +
49
+ `scripts/ci/` per cycle 331.1 Phases B–F-LOCAL. Library API is stable;
50
+ binary subcommands cover the full hook-facing surface (review, gate-push,
51
+ doctor, gates, stats) under the subscription cost model. The Phase F
52
+ `df critic` subcommand is the CI cold-path API-key version.
53
53
 
54
54
  ## Install
55
55
 
@@ -98,6 +98,14 @@ df attribute-pr # env-driven; needs PR_NUMBER, PR_NODE_ID, PR_BODY_FILE, PROJEC
98
98
  df audit stats --path .git/agent-reviews/_runs.ndjson
99
99
  df admit-pr --files-stdin # newline-separated file paths on stdin
100
100
  df admit-pr --files docs/roadmap/cycles/cycle331.md,packages/cli/src/cli.ts
101
+
102
+ # Phase F-LOCAL hook-facing subcommands (subscription cost model).
103
+ df review --commit HEAD --profile local --foreground
104
+ df gate-push # local pre-push, reads stdin
105
+ df gate-push --commit HEAD --ci # CI replay
106
+ df doctor --profile local # env + per-adapter auth check
107
+ df gates # static gates, no LLM
108
+ df stats # alias for `df audit stats`
101
109
  ```
102
110
 
103
111
  > **Note on `--use-bundled-default-spec`**: the bundled `spec-default.yaml`
@@ -109,11 +117,131 @@ df admit-pr --files docs/roadmap/cycles/cycle331.md,packages/cli/src/cli.ts
109
117
  > against an arbitrary repo will surface drift against contexts that
110
118
  > don't exist there.
111
119
 
112
- The Phase F subcommands (`review`, `gate`, `doctor`) are stubbed and exit 2
113
- with a "not implemented" message pointing at the library API. The Phase E
114
- stubs (`status-check`, `critic`) intentionally exit 0 so the five reusable
115
- workflows can satisfy dark-factory's own ruleset while real critic
116
- orchestration lands in Phase F.
120
+ ## For consumer repos hook wiring + subscription cost model
121
+
122
+ The Phase F-LOCAL subcommands (`review`, `gate-push`, `doctor`, `gates`,
123
+ `stats`) are designed to power consumer repos' `.husky/post-commit` and
124
+ `.husky/pre-push` hooks. The **cost model** is critical: per-commit critic
125
+ invocations from API tokens cost $1000s/week on a busy repo, while
126
+ subscription-auth invocations (using the developer's existing Cursor /
127
+ Codex / Claude CLI logins) are flat-rate.
128
+
129
+ ### Subscription auth — what runs on each git push
130
+
131
+ | Subcommand | Hook | Cost model |
132
+ | --- | --- | --- |
133
+ | `df review` | `.husky/post-commit` (background) | **Subscription** — consumes Cursor / Codex / Claude CLI logins via the active profile's `auth` pins. No API spend by default. |
134
+ | `df gate-push` | `.husky/pre-push` | Free — reads pre-existing artifacts, no LLM calls. |
135
+ | `df doctor` | None (operator-run) | Free. Validates that per-adapter auth source is reachable. |
136
+ | `df gates` | None (operator-run) | Free. Runs static quality gates per `validation.requiredQualityGates`. |
137
+ | `df stats` | None (operator-run) | Free. Reads `.git/agent-reviews/_runs.ndjson`. |
138
+
139
+ CI cold-path (the 4 vendor API keys: `CURSOR_API_KEY`, `CODEX_API_KEY`,
140
+ `GEMINI_API_KEY`, `XAI_API_KEY`) is intentionally the fallback only —
141
+ used when:
142
+
143
+ - The first PR on a fresh repo runs critic before any developer has run
144
+ hooks locally.
145
+ - A hook bypass landed and the CI gate needs to re-evaluate.
146
+ - The developer hasn't logged in to a vendor CLI yet
147
+ (`cursor login` / `codex login` / Claude desktop OAuth).
148
+
149
+ ### `.agent-review/config.json` — the profile that pins subscription auth
150
+
151
+ ```json
152
+ {
153
+ "version": 2,
154
+ "critics": [
155
+ { "id": "cursor-local-chief-engineer", "adapter": "cursor-sdk", ... },
156
+ { "id": "codex-local-chief-engineer", "adapter": "codex-sdk", ... }
157
+ ],
158
+ "profiles": {
159
+ "local": {
160
+ "criticIds": ["cursor-local-chief-engineer", "codex-local-chief-engineer"],
161
+ "quorum": 1,
162
+ "auth": {
163
+ "codex-local-chief-engineer": "chatgpt"
164
+ }
165
+ },
166
+ "cloud": {
167
+ "criticIds": ["cursor-local-chief-engineer", "codex-local-chief-engineer"],
168
+ "quorum": 2,
169
+ "auth": {
170
+ "codex-local-chief-engineer": "api"
171
+ }
172
+ }
173
+ }
174
+ }
175
+ ```
176
+
177
+ The `local` profile pins `codex` to `"chatgpt"` — the Codex SDK will use
178
+ `~/.codex/auth.json` (from `codex login`) and **NOT** fall back to
179
+ `CODEX_API_KEY` even if it's set in env. This is the firewall against
180
+ accidental API-token billing.
181
+
182
+ `df doctor --profile local` validates the configured subscription source
183
+ is reachable. Run it after first-time setup.
184
+
185
+ ### Sample `.husky/post-commit`
186
+
187
+ ```bash
188
+ #!/usr/bin/env bash
189
+ set -euo pipefail
190
+ if [[ "${AGENT_REVIEW_SKIP:-}" == "1" ]]; then
191
+ echo "df: skipped by AGENT_REVIEW_SKIP=1"
192
+ exit 0
193
+ fi
194
+ SHA="$(git rev-parse HEAD)"
195
+ COMMON_DIR="$(git rev-parse --git-common-dir 2>/dev/null || echo .git)"
196
+ mkdir -p "${COMMON_DIR}/agent-reviews"
197
+ LOG_FILE="${COMMON_DIR}/agent-reviews/post-commit.log"
198
+ # Detached background invocation — does not block the commit.
199
+ AGENT_REVIEW_PROFILE=local nohup npx df review --commit "${SHA}" \
200
+ >"${LOG_FILE}" 2>&1 </dev/null &
201
+ disown || true
202
+ echo "df: review started for ${SHA:0:12} (log: ${LOG_FILE})"
203
+ ```
204
+
205
+ ### Sample `.husky/pre-push`
206
+
207
+ ```bash
208
+ #!/usr/bin/env bash
209
+ set -euo pipefail
210
+ if [[ -n "${AGENT_REVIEW_BYPASS:-}" ]]; then
211
+ echo "df: pre-push gate BYPASSED — reason: ${AGENT_REVIEW_BYPASS}" >&2
212
+ exit 0
213
+ fi
214
+ npx df gate-push --profile local
215
+ ```
216
+
217
+ ### Doppler bootstrap (optional)
218
+
219
+ For repos that use Doppler to manage `DOPPLER_TOKEN`, place it in
220
+ `<main-checkout>/.env` and the bootstrap loader will hoist it from any
221
+ worktree:
222
+
223
+ ```bash
224
+ echo 'DOPPLER_TOKEN=dp.st.dev.…' > <main-checkout>/.env
225
+ chmod 600 <main-checkout>/.env
226
+ ```
227
+
228
+ The default allowlist is **just `DOPPLER_TOKEN`**. Consumers that use
229
+ project-scoped service-token vars (e.g. `DOPPLER_SERVICE_TOKEN_ACME`) can
230
+ pass a custom allowlist to `loadDopplerBootstrapEnv()` via the library
231
+ API — see `packages/cli/src/doppler-bootstrap.ts` for the
232
+ `serviceTokenAlias` parameter that bridges to `DOPPLER_TOKEN`.
233
+
234
+ ### First-time setup checklist
235
+
236
+ 1. `npm install --ignore-scripts @momentiq/dark-factory-cli`
237
+ 2. Add `.agent-review/config.json` with the `local` profile (above).
238
+ 3. Add `.husky/post-commit` + `.husky/pre-push` (samples above).
239
+ 4. `git config --local core.hooksPath .husky`
240
+ 5. `cursor login` / `codex login` (or Claude desktop OAuth) on the workstation.
241
+ 6. Run `df doctor --profile local` — should report all OK.
242
+ 7. Commit something — observe `.git/agent-reviews/<sha>.json` arrives.
243
+ 8. (Optional) Set up CI with `CURSOR_API_KEY` / `CODEX_API_KEY` /
244
+ `GEMINI_API_KEY` / `XAI_API_KEY` as repo secrets for the cold path.
117
245
 
118
246
  ## System requirements
119
247
 
@@ -51,30 +51,30 @@ export declare const CriticResultZodSchema: z.ZodObject<{
51
51
  routeId: z.ZodOptional<z.ZodString>;
52
52
  justification: z.ZodOptional<z.ZodString>;
53
53
  }, "strip", z.ZodTypeAny, {
54
+ evidence: string;
54
55
  severity: string;
55
56
  category: string;
56
- evidence: string;
57
57
  impact: string;
58
58
  requiredFix: string;
59
59
  symbol?: string | undefined;
60
+ routeId?: string | undefined;
60
61
  file?: string | undefined;
61
62
  line?: number | undefined;
62
63
  manifestoSection?: string | undefined;
63
64
  evidencePath?: string | undefined;
64
- routeId?: string | undefined;
65
65
  justification?: string | undefined;
66
66
  }, {
67
+ evidence: string;
67
68
  severity: string;
68
69
  category: string;
69
- evidence: string;
70
70
  impact: string;
71
71
  requiredFix: string;
72
72
  symbol?: string | undefined;
73
+ routeId?: string | undefined;
73
74
  file?: string | undefined;
74
75
  line?: number | undefined;
75
76
  manifestoSection?: string | undefined;
76
77
  evidencePath?: string | undefined;
77
- routeId?: string | undefined;
78
78
  justification?: string | undefined;
79
79
  }>, "many">;
80
80
  validation: z.ZodObject<{
@@ -89,16 +89,16 @@ export declare const CriticResultZodSchema: z.ZodObject<{
89
89
  }, "strip", z.ZodTypeAny, {
90
90
  exitCode: number;
91
91
  command: string;
92
- durationMs: number;
93
92
  logExcerpt: string;
93
+ durationMs: number;
94
94
  startedAt: string;
95
95
  finishedAt: string;
96
96
  routeId?: string | undefined;
97
97
  }, {
98
98
  exitCode: number;
99
99
  command: string;
100
- durationMs: number;
101
100
  logExcerpt: string;
101
+ durationMs: number;
102
102
  startedAt: string;
103
103
  finishedAt: string;
104
104
  routeId?: string | undefined;
@@ -108,8 +108,8 @@ export declare const CriticResultZodSchema: z.ZodObject<{
108
108
  qualityGateResults: {
109
109
  exitCode: number;
110
110
  command: string;
111
- durationMs: number;
112
111
  logExcerpt: string;
112
+ durationMs: number;
113
113
  startedAt: string;
114
114
  finishedAt: string;
115
115
  routeId?: string | undefined;
@@ -119,8 +119,8 @@ export declare const CriticResultZodSchema: z.ZodObject<{
119
119
  qualityGateResults: {
120
120
  exitCode: number;
121
121
  command: string;
122
- durationMs: number;
123
122
  logExcerpt: string;
123
+ durationMs: number;
124
124
  startedAt: string;
125
125
  finishedAt: string;
126
126
  routeId?: string | undefined;
@@ -133,65 +133,65 @@ export declare const CriticResultZodSchema: z.ZodObject<{
133
133
  qualityGateResults: {
134
134
  exitCode: number;
135
135
  command: string;
136
- durationMs: number;
137
136
  logExcerpt: string;
137
+ durationMs: number;
138
138
  startedAt: string;
139
139
  finishedAt: string;
140
140
  routeId?: string | undefined;
141
141
  }[];
142
142
  qualityGatesMissing: string[];
143
143
  };
144
- verdict: string;
145
144
  status: string;
146
145
  requiresHumanJudgment: boolean;
147
146
  summary: string;
147
+ confidence: string;
148
+ verdict: string;
148
149
  findings: {
150
+ evidence: string;
149
151
  severity: string;
150
152
  category: string;
151
- evidence: string;
152
153
  impact: string;
153
154
  requiredFix: string;
154
155
  symbol?: string | undefined;
156
+ routeId?: string | undefined;
155
157
  file?: string | undefined;
156
158
  line?: number | undefined;
157
159
  manifestoSection?: string | undefined;
158
160
  evidencePath?: string | undefined;
159
- routeId?: string | undefined;
160
161
  justification?: string | undefined;
161
162
  }[];
162
- confidence: string;
163
163
  }, {
164
164
  validation: {
165
165
  qualityGateResults: {
166
166
  exitCode: number;
167
167
  command: string;
168
- durationMs: number;
169
168
  logExcerpt: string;
169
+ durationMs: number;
170
170
  startedAt: string;
171
171
  finishedAt: string;
172
172
  routeId?: string | undefined;
173
173
  }[];
174
174
  qualityGatesMissing: string[];
175
175
  };
176
- verdict: string;
177
176
  status: string;
178
177
  requiresHumanJudgment: boolean;
179
178
  summary: string;
179
+ confidence: string;
180
+ verdict: string;
180
181
  findings: {
182
+ evidence: string;
181
183
  severity: string;
182
184
  category: string;
183
- evidence: string;
184
185
  impact: string;
185
186
  requiredFix: string;
186
187
  symbol?: string | undefined;
188
+ routeId?: string | undefined;
187
189
  file?: string | undefined;
188
190
  line?: number | undefined;
189
191
  manifestoSection?: string | undefined;
190
192
  evidencePath?: string | undefined;
191
- routeId?: string | undefined;
192
193
  justification?: string | undefined;
193
194
  }[];
194
- confidence: string;
195
195
  }>;
196
196
  export type CriticResultModelEnvelope = z.infer<typeof CriticResultZodSchema>;
197
197
  /**