@getripple/cli 1.0.4
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/CHANGELOG.md +21 -0
- package/README.md +110 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3169 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
const fs = __importStar(require("fs"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
const core_1 = require("@getripple/core");
|
|
30
|
+
const CONTROL_MODES = ["brainstorm", "function", "file", "task", "pr"];
|
|
31
|
+
function usage() {
|
|
32
|
+
return [
|
|
33
|
+
"Ripple CLI",
|
|
34
|
+
"",
|
|
35
|
+
"Usage:",
|
|
36
|
+
" ripple init [--force] [--json]",
|
|
37
|
+
" ripple doctor [--strict] [--agent]",
|
|
38
|
+
" ripple scan [path]",
|
|
39
|
+
" ripple focus <file>",
|
|
40
|
+
" ripple blast <file>",
|
|
41
|
+
" ripple imports <file>",
|
|
42
|
+
" ripple importers <file>",
|
|
43
|
+
" ripple symbols <file>",
|
|
44
|
+
" ripple callers <file>::<symbol>",
|
|
45
|
+
" ripple history [--last N]",
|
|
46
|
+
" ripple plan --file <file> --task <task> [--mode file|function|brainstorm|task|pr] [--symbol name] [--budget N] [--save]",
|
|
47
|
+
" ripple check --staged [--intent latest|path] [--strict]",
|
|
48
|
+
" ripple check --changed --base <ref> [--intent latest|path] [--strict]",
|
|
49
|
+
" ripple audit [--intent latest|path] [--changed --base <ref>] [--strict]",
|
|
50
|
+
" ripple gate [--intent latest|path] [--changed --base <ref>] [--strict]",
|
|
51
|
+
" ripple approval [--intent latest|path] [--gate before-risky-edit|before-merge]",
|
|
52
|
+
" ripple approve [--intent latest|path] [--gate before-risky-edit|before-merge] [--reason text]",
|
|
53
|
+
" ripple repair [--intent latest|path] [--strict]",
|
|
54
|
+
" ripple ci [--base <ref>] [--intent latest|path] [--github-annotations]",
|
|
55
|
+
" ripple init-ci [--print] [--force]",
|
|
56
|
+
" ripple policy init [--print] [--force]",
|
|
57
|
+
" ripple policy explain --file <file>",
|
|
58
|
+
" ripple agent",
|
|
59
|
+
"",
|
|
60
|
+
"Options:",
|
|
61
|
+
" --json, -j Print machine-readable JSON",
|
|
62
|
+
" --agent Print compact agent handoff for ripple doctor/plan/check/audit/gate/repair",
|
|
63
|
+
" --last N Limit history groups (default: 10)",
|
|
64
|
+
" --file PATH Target file for plan",
|
|
65
|
+
" --task TEXT Task description for plan",
|
|
66
|
+
" --mode MODE Agent control boundary for saved plans (default: file)",
|
|
67
|
+
" --symbol NAME Allowed symbol for --mode function",
|
|
68
|
+
" --gate GATE Human approval gate for approve (before-risky-edit or before-merge)",
|
|
69
|
+
" --reason TEXT Human approval reason",
|
|
70
|
+
" --approved-by NAME Human approver name for approval records",
|
|
71
|
+
" --budget N Token budget for plan (default: 4000)",
|
|
72
|
+
" --staged Check currently staged JS/TS files",
|
|
73
|
+
" --changed Check JS/TS files changed against --base",
|
|
74
|
+
" --base REF Base git ref for --changed checks (default: HEAD)",
|
|
75
|
+
" --save Save a change intent from ripple plan",
|
|
76
|
+
" --intent REF Validate changes against saved intent (latest, id, or path; ci default: latest)",
|
|
77
|
+
" --strict Exit non-zero when check/repair detects missing intent, drift, or contract danger",
|
|
78
|
+
" --github-annotations Emit GitHub Actions annotations for CI findings",
|
|
79
|
+
" --print Print generated setup content instead of writing files",
|
|
80
|
+
" --force Overwrite existing generated setup files",
|
|
81
|
+
"",
|
|
82
|
+
"Examples:",
|
|
83
|
+
" ripple init",
|
|
84
|
+
" ripple doctor",
|
|
85
|
+
" ripple doctor --agent",
|
|
86
|
+
" ripple agent",
|
|
87
|
+
" ripple agent --json",
|
|
88
|
+
" ripple plan --file src/auth.ts --task \"change token refresh behavior\" --mode file --agent --save",
|
|
89
|
+
" ripple plan --file src/auth.ts --symbol refreshToken --task \"fix retry behavior\" --mode function --agent --save",
|
|
90
|
+
" ripple check --staged --agent --intent latest",
|
|
91
|
+
" ripple audit --agent --intent latest",
|
|
92
|
+
" ripple gate --agent --intent latest",
|
|
93
|
+
" ripple approval --intent latest --agent",
|
|
94
|
+
" ripple approve --intent latest --gate before-risky-edit --reason \"plan reviewed\"",
|
|
95
|
+
" ripple repair --agent --intent latest",
|
|
96
|
+
" ripple check --staged --intent latest --strict",
|
|
97
|
+
" ripple check --changed --base origin/main --strict",
|
|
98
|
+
" ripple ci --base origin/main --intent latest --github-annotations",
|
|
99
|
+
" ripple init",
|
|
100
|
+
" ripple init-ci",
|
|
101
|
+
" ripple policy init",
|
|
102
|
+
" ripple policy explain --file src/auth.ts",
|
|
103
|
+
"",
|
|
104
|
+
" ripple --help",
|
|
105
|
+
" ripple --version",
|
|
106
|
+
].join("\n");
|
|
107
|
+
}
|
|
108
|
+
function agentWorkflowGuide() {
|
|
109
|
+
const workflow = (0, core_1.getAgentWorkflowSummary)();
|
|
110
|
+
return [
|
|
111
|
+
"Ripple Agent Workflow",
|
|
112
|
+
"",
|
|
113
|
+
"Setup readiness:",
|
|
114
|
+
` ${workflow.commands.initializeRepo}`,
|
|
115
|
+
` ${workflow.commands.checkReadiness}`,
|
|
116
|
+
` ${workflow.commands.installCi} (CI-only repair path)`,
|
|
117
|
+
"",
|
|
118
|
+
"Before editing:",
|
|
119
|
+
` ${workflow.commands.planBeforeEditing}`,
|
|
120
|
+
` ${workflow.policyWorkflow.defaultAgentPath}`,
|
|
121
|
+
"",
|
|
122
|
+
"If human gate is required:",
|
|
123
|
+
` ${workflow.commands.checkApproval}`,
|
|
124
|
+
` ${workflow.commands.approveHumanGate}`,
|
|
125
|
+
"",
|
|
126
|
+
"Policy-only check:",
|
|
127
|
+
` ${workflow.commands.explainPolicy}`,
|
|
128
|
+
` ${workflow.policyWorkflow.policyOnlyPath}`,
|
|
129
|
+
"",
|
|
130
|
+
"Policy drift:",
|
|
131
|
+
` ${workflow.policyWorkflow.policyDriftPath}`,
|
|
132
|
+
"",
|
|
133
|
+
"After staging changes:",
|
|
134
|
+
` ${workflow.commands.checkAfterStaging}`,
|
|
135
|
+
"",
|
|
136
|
+
"Audit current change:",
|
|
137
|
+
` ${workflow.commands.auditCurrentChange}`,
|
|
138
|
+
` ${workflow.commands.gateCurrentChange}`,
|
|
139
|
+
"",
|
|
140
|
+
"If staged changes drift:",
|
|
141
|
+
` ${workflow.commands.repairIntentDrift}`,
|
|
142
|
+
"",
|
|
143
|
+
"CI gate:",
|
|
144
|
+
` ${workflow.commands.ciGate}`,
|
|
145
|
+
"",
|
|
146
|
+
"Loop:",
|
|
147
|
+
` ${workflow.loop.join(" -> ")}`,
|
|
148
|
+
"",
|
|
149
|
+
"Runtime contract:",
|
|
150
|
+
...workflow.runtimeContract.phases.map((phase) => ` ${phase.order}. ${phase.id}: ${phase.agentAction}`),
|
|
151
|
+
"",
|
|
152
|
+
"Stop if:",
|
|
153
|
+
...workflow.runtimeContract.stopConditions.map((condition) => ` - ${condition}`),
|
|
154
|
+
"",
|
|
155
|
+
"Example:",
|
|
156
|
+
...workflow.example.map((command) => ` ${command}`),
|
|
157
|
+
].join("\n");
|
|
158
|
+
}
|
|
159
|
+
function parseCliArgs(argv) {
|
|
160
|
+
let command;
|
|
161
|
+
const args = [];
|
|
162
|
+
const options = {
|
|
163
|
+
json: false,
|
|
164
|
+
agent: false,
|
|
165
|
+
last: 10,
|
|
166
|
+
budget: 4000,
|
|
167
|
+
staged: false,
|
|
168
|
+
changed: false,
|
|
169
|
+
save: false,
|
|
170
|
+
strict: false,
|
|
171
|
+
githubAnnotations: false,
|
|
172
|
+
force: false,
|
|
173
|
+
print: false,
|
|
174
|
+
};
|
|
175
|
+
for (let i = 0; i < argv.length; i++) {
|
|
176
|
+
const token = argv[i];
|
|
177
|
+
if (token === "--json" || token === "-j") {
|
|
178
|
+
options.json = true;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (token === "--agent") {
|
|
182
|
+
options.agent = true;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (token === "--staged") {
|
|
186
|
+
options.staged = true;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (token === "--changed") {
|
|
190
|
+
options.changed = true;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (token === "--save") {
|
|
194
|
+
options.save = true;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (token === "--strict") {
|
|
198
|
+
options.strict = true;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (token === "--github-annotations") {
|
|
202
|
+
options.githubAnnotations = true;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (token === "--force") {
|
|
206
|
+
options.force = true;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (token === "--print") {
|
|
210
|
+
options.print = true;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (token === "--last") {
|
|
214
|
+
const value = argv[i + 1];
|
|
215
|
+
if (!value || value.startsWith("-")) {
|
|
216
|
+
throw new Error("Missing value for --last");
|
|
217
|
+
}
|
|
218
|
+
options.last = parsePositiveInteger(value, "--last");
|
|
219
|
+
i++;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (token.startsWith("--last=")) {
|
|
223
|
+
options.last = parsePositiveInteger(token.slice("--last=".length), "--last");
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (token === "--file") {
|
|
227
|
+
const value = argv[i + 1];
|
|
228
|
+
if (!value || value.startsWith("-")) {
|
|
229
|
+
throw new Error("Missing value for --file");
|
|
230
|
+
}
|
|
231
|
+
options.file = value;
|
|
232
|
+
i++;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (token.startsWith("--file=")) {
|
|
236
|
+
options.file = token.slice("--file=".length);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (token === "--task") {
|
|
240
|
+
const value = argv[i + 1];
|
|
241
|
+
if (!value || value.startsWith("-")) {
|
|
242
|
+
throw new Error("Missing value for --task");
|
|
243
|
+
}
|
|
244
|
+
options.task = value;
|
|
245
|
+
i++;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (token.startsWith("--task=")) {
|
|
249
|
+
options.task = token.slice("--task=".length);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (token === "--mode") {
|
|
253
|
+
const value = argv[i + 1];
|
|
254
|
+
if (!value || value.startsWith("-")) {
|
|
255
|
+
throw new Error("Missing value for --mode");
|
|
256
|
+
}
|
|
257
|
+
options.mode = parseControlMode(value);
|
|
258
|
+
i++;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (token.startsWith("--mode=")) {
|
|
262
|
+
options.mode = parseControlMode(token.slice("--mode=".length));
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (token === "--symbol") {
|
|
266
|
+
const value = argv[i + 1];
|
|
267
|
+
if (!value || value.startsWith("-")) {
|
|
268
|
+
throw new Error("Missing value for --symbol");
|
|
269
|
+
}
|
|
270
|
+
options.symbol = value;
|
|
271
|
+
i++;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (token.startsWith("--symbol=")) {
|
|
275
|
+
options.symbol = token.slice("--symbol=".length);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (token === "--gate") {
|
|
279
|
+
const value = argv[i + 1];
|
|
280
|
+
if (!value || value.startsWith("-")) {
|
|
281
|
+
throw new Error("Missing value for --gate");
|
|
282
|
+
}
|
|
283
|
+
options.gate = parseApprovalGate(value);
|
|
284
|
+
i++;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (token.startsWith("--gate=")) {
|
|
288
|
+
options.gate = parseApprovalGate(token.slice("--gate=".length));
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (token === "--reason") {
|
|
292
|
+
const value = argv[i + 1];
|
|
293
|
+
if (!value || value.startsWith("-")) {
|
|
294
|
+
throw new Error("Missing value for --reason");
|
|
295
|
+
}
|
|
296
|
+
options.reason = value;
|
|
297
|
+
i++;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (token.startsWith("--reason=")) {
|
|
301
|
+
options.reason = token.slice("--reason=".length);
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (token === "--approved-by") {
|
|
305
|
+
const value = argv[i + 1];
|
|
306
|
+
if (!value || value.startsWith("-")) {
|
|
307
|
+
throw new Error("Missing value for --approved-by");
|
|
308
|
+
}
|
|
309
|
+
options.approvedBy = value;
|
|
310
|
+
i++;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (token.startsWith("--approved-by=")) {
|
|
314
|
+
options.approvedBy = token.slice("--approved-by=".length);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (token === "--intent") {
|
|
318
|
+
const value = argv[i + 1];
|
|
319
|
+
if (!value || value.startsWith("-")) {
|
|
320
|
+
throw new Error("Missing value for --intent");
|
|
321
|
+
}
|
|
322
|
+
options.intent = value;
|
|
323
|
+
i++;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (token.startsWith("--intent=")) {
|
|
327
|
+
options.intent = token.slice("--intent=".length);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (token === "--base") {
|
|
331
|
+
const value = argv[i + 1];
|
|
332
|
+
if (!value || value.startsWith("-")) {
|
|
333
|
+
throw new Error("Missing value for --base");
|
|
334
|
+
}
|
|
335
|
+
options.base = value;
|
|
336
|
+
i++;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (token.startsWith("--base=")) {
|
|
340
|
+
options.base = token.slice("--base=".length);
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (token === "--budget") {
|
|
344
|
+
const value = argv[i + 1];
|
|
345
|
+
if (!value || value.startsWith("-")) {
|
|
346
|
+
throw new Error("Missing value for --budget");
|
|
347
|
+
}
|
|
348
|
+
options.budget = parsePositiveInteger(value, "--budget");
|
|
349
|
+
i++;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (token.startsWith("--budget=")) {
|
|
353
|
+
options.budget = parsePositiveInteger(token.slice("--budget=".length), "--budget");
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (!command) {
|
|
357
|
+
command = token;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
args.push(token);
|
|
361
|
+
}
|
|
362
|
+
return { command, args, options };
|
|
363
|
+
}
|
|
364
|
+
function parsePositiveInteger(value, optionName) {
|
|
365
|
+
const parsed = Number(value);
|
|
366
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
367
|
+
throw new Error(`${optionName} must be a positive integer`);
|
|
368
|
+
}
|
|
369
|
+
return parsed;
|
|
370
|
+
}
|
|
371
|
+
function parseControlMode(value) {
|
|
372
|
+
if (CONTROL_MODES.includes(value)) {
|
|
373
|
+
return value;
|
|
374
|
+
}
|
|
375
|
+
throw new Error(`--mode must be one of: ${CONTROL_MODES.join(", ")}`);
|
|
376
|
+
}
|
|
377
|
+
function parseApprovalGate(value) {
|
|
378
|
+
if (value === "before-risky-edit" || value === "before-merge") {
|
|
379
|
+
return value;
|
|
380
|
+
}
|
|
381
|
+
throw new Error("--gate must be one of: before-risky-edit, before-merge");
|
|
382
|
+
}
|
|
383
|
+
function version() {
|
|
384
|
+
const pkgPath = path.resolve(__dirname, "..", "package.json");
|
|
385
|
+
try {
|
|
386
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
387
|
+
return pkg.version ?? "0.0.0";
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
return "0.0.0";
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
const GITHUB_ACTIONS_WORKFLOW_PATH = core_1.RIPPLE_CI_WORKFLOW_PATH;
|
|
394
|
+
function githubActionsWorkflow() {
|
|
395
|
+
return [
|
|
396
|
+
"name: Ripple",
|
|
397
|
+
"",
|
|
398
|
+
"on:",
|
|
399
|
+
" pull_request:",
|
|
400
|
+
"",
|
|
401
|
+
"permissions:",
|
|
402
|
+
" contents: read",
|
|
403
|
+
" pull-requests: read",
|
|
404
|
+
"",
|
|
405
|
+
"jobs:",
|
|
406
|
+
" ripple:",
|
|
407
|
+
" name: Ripple architecture gate",
|
|
408
|
+
" runs-on: ubuntu-latest",
|
|
409
|
+
" steps:",
|
|
410
|
+
" - name: Checkout",
|
|
411
|
+
" uses: actions/checkout@v4",
|
|
412
|
+
" with:",
|
|
413
|
+
" fetch-depth: 0",
|
|
414
|
+
" - name: Setup Node",
|
|
415
|
+
" uses: actions/setup-node@v4",
|
|
416
|
+
" with:",
|
|
417
|
+
" node-version: 20",
|
|
418
|
+
" - name: Ripple CI",
|
|
419
|
+
" run: npx -y @getripple/cli@latest ci --base origin/${{ github.base_ref }} --intent latest --github-annotations",
|
|
420
|
+
"",
|
|
421
|
+
].join("\n");
|
|
422
|
+
}
|
|
423
|
+
function defaultInitNextSteps(readiness) {
|
|
424
|
+
return uniqueLines([
|
|
425
|
+
...(readiness?.nextSteps ?? []),
|
|
426
|
+
"Run ripple plan --file <file> --task \"<task>\" --mode file --agent --save.",
|
|
427
|
+
"Run ripple doctor --agent --strict after saving the first intent.",
|
|
428
|
+
"Commit .ripple/policy.json, .github/workflows/ripple.yml, and the saved intent when you want CI to enforce the gate.",
|
|
429
|
+
]);
|
|
430
|
+
}
|
|
431
|
+
function uniqueLines(lines) {
|
|
432
|
+
const seen = new Set();
|
|
433
|
+
return lines.filter((line) => {
|
|
434
|
+
if (seen.has(line)) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
seen.add(line);
|
|
438
|
+
return true;
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function resolveWorkspaceRoot(inputPath) {
|
|
442
|
+
const candidate = path.resolve(process.cwd(), inputPath ?? ".");
|
|
443
|
+
if (!fs.existsSync(candidate)) {
|
|
444
|
+
throw new Error(`Path does not exist: ${candidate}`);
|
|
445
|
+
}
|
|
446
|
+
const stat = fs.statSync(candidate);
|
|
447
|
+
if (!stat.isDirectory()) {
|
|
448
|
+
throw new Error(`Scan path must be a directory: ${candidate}`);
|
|
449
|
+
}
|
|
450
|
+
return candidate;
|
|
451
|
+
}
|
|
452
|
+
function countCallEdges(engine) {
|
|
453
|
+
let count = 0;
|
|
454
|
+
engine.graph.symbols.forEach((symbol) => {
|
|
455
|
+
count += symbol.calls.size;
|
|
456
|
+
});
|
|
457
|
+
return count;
|
|
458
|
+
}
|
|
459
|
+
function printJson(value) {
|
|
460
|
+
console.log(JSON.stringify(value, null, 2));
|
|
461
|
+
}
|
|
462
|
+
function applyStrictExit(shouldFail) {
|
|
463
|
+
if (shouldFail) {
|
|
464
|
+
process.exitCode = 1;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function strictCheckShouldFail(summary) {
|
|
468
|
+
return !summary.intentValidation || summary.intentValidation.driftVerdict.status !== "pass";
|
|
469
|
+
}
|
|
470
|
+
function strictRepairShouldFail(plan) {
|
|
471
|
+
return plan.status !== "no-repair-needed";
|
|
472
|
+
}
|
|
473
|
+
function strictAuditShouldFail(summary) {
|
|
474
|
+
return summary.status !== "pass";
|
|
475
|
+
}
|
|
476
|
+
const MISSING_INTENT_NEXT_REQUIRED_PHASE = "plan_before_edit";
|
|
477
|
+
const MISSING_INTENT_NEXT_REQUIRED_ACTION = "Create a saved Ripple plan with ripple plan --file <file> --task \"<task>\" --agent --save before relying on CI or drift checks.";
|
|
478
|
+
function intentLoadFailureMessage(intentRef, error) {
|
|
479
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
480
|
+
return [
|
|
481
|
+
`Could not load Ripple change intent '${intentRef}'.`,
|
|
482
|
+
"Run ripple plan --save and include .ripple/intents/latest.json in the PR, or pass a valid --intent path.",
|
|
483
|
+
detail,
|
|
484
|
+
].join(" ");
|
|
485
|
+
}
|
|
486
|
+
function defaultCiBaseRef() {
|
|
487
|
+
const githubBaseRef = process.env.GITHUB_BASE_REF?.trim();
|
|
488
|
+
if (githubBaseRef) {
|
|
489
|
+
return `origin/${githubBaseRef}`;
|
|
490
|
+
}
|
|
491
|
+
return "HEAD";
|
|
492
|
+
}
|
|
493
|
+
function shouldEmitGithubAnnotations(options) {
|
|
494
|
+
return options.githubAnnotations || process.env.GITHUB_ACTIONS === "true";
|
|
495
|
+
}
|
|
496
|
+
function printGithubIntentLoadError(message) {
|
|
497
|
+
printGithubErrorAnnotation({
|
|
498
|
+
title: "Ripple intent required",
|
|
499
|
+
message: `next=${MISSING_INTENT_NEXT_REQUIRED_PHASE}. ${MISSING_INTENT_NEXT_REQUIRED_ACTION} ${message}`,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
function writeGithubStepSummary(input) {
|
|
503
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY?.trim();
|
|
504
|
+
if (!summaryPath) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
try {
|
|
508
|
+
fs.appendFileSync(summaryPath, buildGithubStepSummary(input), "utf8");
|
|
509
|
+
}
|
|
510
|
+
catch (err) {
|
|
511
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
512
|
+
console.error(`Ripple CLI warning: Could not write GitHub step summary: ${message}`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
function writeGithubAuditStepSummary(audit) {
|
|
516
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY?.trim();
|
|
517
|
+
if (!summaryPath) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
try {
|
|
521
|
+
fs.appendFileSync(summaryPath, buildGithubAuditStepSummary(audit), "utf8");
|
|
522
|
+
}
|
|
523
|
+
catch (err) {
|
|
524
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
525
|
+
console.error(`Ripple CLI warning: Could not write GitHub step summary: ${message}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function buildGithubStepSummary(input) {
|
|
529
|
+
const { summary, intentLoadError } = input;
|
|
530
|
+
const validation = summary.intentValidation;
|
|
531
|
+
const gateDecision = validation?.handoff.decision ?? "create-intent";
|
|
532
|
+
const canContinue = validation?.handoff.canContinue ?? false;
|
|
533
|
+
const mustStop = validation?.handoff.mustStop ?? true;
|
|
534
|
+
const needsHuman = validation?.handoff.needsHuman ?? true;
|
|
535
|
+
const status = intentLoadError || !validation || validation.driftVerdict.status !== "pass"
|
|
536
|
+
? "failed"
|
|
537
|
+
: "passed";
|
|
538
|
+
const gateStatus = canContinue ? "open" : "closed";
|
|
539
|
+
const nextRequiredPhase = intentLoadError
|
|
540
|
+
? MISSING_INTENT_NEXT_REQUIRED_PHASE
|
|
541
|
+
: validation?.nextRequiredPhase ?? MISSING_INTENT_NEXT_REQUIRED_PHASE;
|
|
542
|
+
const nextRequiredAction = intentLoadError
|
|
543
|
+
? MISSING_INTENT_NEXT_REQUIRED_ACTION
|
|
544
|
+
: validation?.nextRequiredAction ?? MISSING_INTENT_NEXT_REQUIRED_ACTION;
|
|
545
|
+
const pushList = (lines, title, items, limit) => {
|
|
546
|
+
lines.push(`#### ${title}`);
|
|
547
|
+
if (items.length === 0) {
|
|
548
|
+
lines.push("- none");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
items.slice(0, limit).forEach((item) => lines.push(`- ${item}`));
|
|
552
|
+
if (items.length > limit) {
|
|
553
|
+
lines.push(`- ...and ${items.length - limit} more`);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
const lines = [
|
|
557
|
+
"## Ripple architecture gate",
|
|
558
|
+
"",
|
|
559
|
+
`Status: ${status}`,
|
|
560
|
+
`Gate status: ${gateStatus}`,
|
|
561
|
+
`Gate decision: ${gateDecision}`,
|
|
562
|
+
`Can continue: ${canContinue}`,
|
|
563
|
+
`Must stop: ${mustStop}`,
|
|
564
|
+
`Needs human: ${needsHuman}`,
|
|
565
|
+
`Next required phase: ${nextRequiredPhase}`,
|
|
566
|
+
`Next required action: ${nextRequiredAction}`,
|
|
567
|
+
`Mode: ${summary.mode}`,
|
|
568
|
+
];
|
|
569
|
+
if (summary.baseRef) {
|
|
570
|
+
lines.push(`Base ref: ${summary.baseRef}`);
|
|
571
|
+
}
|
|
572
|
+
lines.push(`Checked files: ${summary.checkedFiles}`, `Highest risk: ${summary.highestRisk}`, "");
|
|
573
|
+
if (intentLoadError) {
|
|
574
|
+
lines.push("### Intent", "", `- ${intentLoadError}`, `- Next required phase: ${nextRequiredPhase}`, `- Next required action: ${nextRequiredAction}`, "");
|
|
575
|
+
}
|
|
576
|
+
else if (validation) {
|
|
577
|
+
lines.push("### Intent", "", `- Intent: ${validation.intentId}`, `- Verdict: ${validation.verdict}`, `- Drift verdict: ${validation.driftVerdict.label}`, `- Control mode: ${validation.controlMode}`, `- Boundary verdict: ${validation.boundaryVerdict.label}`, `- Policy drift: ${validation.policyDrift.label}`, `- Readiness drift: ${validation.readinessDrift.label}`, `- Planned scope: ${validation.plannedScope}`, `- Next required phase: ${validation.nextRequiredPhase}`, `- Recommended action: ${validation.recommendedAction}`, "");
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
lines.push("### Intent", "", "- No saved change intent was provided.", `- Next required phase: ${nextRequiredPhase}`, `- Next required action: ${nextRequiredAction}`, "");
|
|
581
|
+
}
|
|
582
|
+
const blockingReasons = validation?.blockingReasons ?? [];
|
|
583
|
+
if (blockingReasons.length > 0) {
|
|
584
|
+
lines.push("### Blocking reasons", "");
|
|
585
|
+
blockingReasons.forEach((reason) => lines.push(`- ${reason}`));
|
|
586
|
+
lines.push("");
|
|
587
|
+
}
|
|
588
|
+
if (validation?.policyDrift) {
|
|
589
|
+
lines.push("### Policy drift", "", `- Status: ${validation.policyDrift.status}`, `- Decision: ${validation.policyDrift.decision}`, `- Summary: ${validation.policyDrift.summary}`, "");
|
|
590
|
+
pushList(lines, "Changed policy fields", validation.policyDrift.changedFields, 12);
|
|
591
|
+
lines.push("");
|
|
592
|
+
pushList(lines, "Policy drift fix", validation.policyDrift.fix, 8);
|
|
593
|
+
lines.push("");
|
|
594
|
+
}
|
|
595
|
+
if (validation?.readinessDrift) {
|
|
596
|
+
lines.push("### Readiness drift", "", `- Status: ${validation.readinessDrift.status}`, `- Decision: ${validation.readinessDrift.decision}`, `- Summary: ${validation.readinessDrift.summary}`, `- Saved enforcement: ${validation.readinessDrift.savedReadiness.enforcementLevel}`, `- Current enforcement: ${validation.readinessDrift.currentReadiness?.enforcementLevel ?? "unknown"}`, "");
|
|
597
|
+
pushList(lines, "Weakened readiness fields", validation.readinessDrift.weakenedFields, 12);
|
|
598
|
+
lines.push("");
|
|
599
|
+
pushList(lines, "Readiness drift fix", validation.readinessDrift.fix, 8);
|
|
600
|
+
lines.push("");
|
|
601
|
+
}
|
|
602
|
+
if (summary.files.length > 0) {
|
|
603
|
+
lines.push("### Changed files", "");
|
|
604
|
+
summary.files.slice(0, 20).forEach((file) => {
|
|
605
|
+
lines.push(`- ${file.file} (${file.modificationRisk}, importers: ${file.importerCount})`);
|
|
606
|
+
});
|
|
607
|
+
if (summary.files.length > 20) {
|
|
608
|
+
lines.push(`- ...and ${summary.files.length - 20} more`);
|
|
609
|
+
}
|
|
610
|
+
lines.push("");
|
|
611
|
+
}
|
|
612
|
+
lines.push("### Agent actions", "");
|
|
613
|
+
pushList(lines, "Trusted findings", summary.agentActions.trustedFindings, 12);
|
|
614
|
+
lines.push("");
|
|
615
|
+
pushList(lines, "Verify before commit", summary.agentActions.verifyBeforeCommit, 12);
|
|
616
|
+
lines.push("");
|
|
617
|
+
pushList(lines, "Manual review required", summary.agentActions.manualReviewRequired, 12);
|
|
618
|
+
lines.push("");
|
|
619
|
+
const verificationTargets = uniqueItems(summary.files.flatMap((file) => file.verificationTargets));
|
|
620
|
+
if (verificationTargets.length > 0) {
|
|
621
|
+
lines.push("### Verify", "");
|
|
622
|
+
verificationTargets.slice(0, 20).forEach((target) => lines.push(`- ${target}`));
|
|
623
|
+
if (verificationTargets.length > 20) {
|
|
624
|
+
lines.push(`- ...and ${verificationTargets.length - 20} more`);
|
|
625
|
+
}
|
|
626
|
+
lines.push("");
|
|
627
|
+
}
|
|
628
|
+
return `${lines.join("\n")}\n`;
|
|
629
|
+
}
|
|
630
|
+
function buildGithubAuditStepSummary(audit) {
|
|
631
|
+
const summary = audit.stagedCheck;
|
|
632
|
+
const validation = summary.intentValidation;
|
|
633
|
+
const gate = (0, core_1.buildRippleGateSummary)(audit);
|
|
634
|
+
const status = gate.canContinue ? "passed" : "failed";
|
|
635
|
+
const pushList = (lines, title, items, limit) => {
|
|
636
|
+
lines.push(`#### ${title}`);
|
|
637
|
+
if (items.length === 0) {
|
|
638
|
+
lines.push("- none");
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
items.slice(0, limit).forEach((item) => lines.push(`- ${item}`));
|
|
642
|
+
if (items.length > limit) {
|
|
643
|
+
lines.push(`- ...and ${items.length - limit} more`);
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
const lines = [
|
|
647
|
+
"## Ripple architecture gate",
|
|
648
|
+
"",
|
|
649
|
+
`Status: ${status}`,
|
|
650
|
+
`Gate status: ${gate.status}`,
|
|
651
|
+
`Gate decision: ${gate.decision}`,
|
|
652
|
+
`Can continue: ${gate.canContinue}`,
|
|
653
|
+
`Must stop: ${gate.mustStop}`,
|
|
654
|
+
`Needs human: ${gate.needsHuman}`,
|
|
655
|
+
`Gate next required phase: ${gate.nextRequiredPhase}`,
|
|
656
|
+
`Gate next required action: ${gate.nextRequiredAction}`,
|
|
657
|
+
`Audit status: ${audit.status}`,
|
|
658
|
+
`Decision: ${audit.decision}`,
|
|
659
|
+
`Can proceed: ${audit.canProceed}`,
|
|
660
|
+
`Next required phase: ${audit.nextRequiredPhase}`,
|
|
661
|
+
`Next required action: ${audit.nextRequiredAction}`,
|
|
662
|
+
`Mode: ${audit.mode}`,
|
|
663
|
+
];
|
|
664
|
+
if (audit.baseRef) {
|
|
665
|
+
lines.push(`Base ref: ${audit.baseRef}`);
|
|
666
|
+
}
|
|
667
|
+
lines.push(`Checked files: ${summary.checkedFiles}`, `Highest risk: ${summary.highestRisk}`, "");
|
|
668
|
+
lines.push("### Intent", "", `- Intent: ${audit.intent.id}`, `- Task: ${audit.intent.task}`, `- Target: ${audit.intent.targetFile}`, `- Verdict: ${validation?.verdict ?? "unknown"}`, `- Drift verdict: ${validation?.driftVerdict.label ?? "UNKNOWN"}`, `- Control mode: ${audit.intent.controlMode}`, `- Boundary verdict: ${validation?.boundaryVerdict.label ?? "UNKNOWN"}`, `- Human gate: ${audit.intent.humanGate}`, `- Policy drift: ${validation?.policyDrift.label ?? "UNKNOWN"}`, `- Readiness drift: ${validation?.readinessDrift.label ?? "UNKNOWN"}`, `- Repair status: ${audit.repairPlan.status}`, `- Next required phase: ${audit.nextRequiredPhase}`, `- Recommended action: ${audit.recommendedAction}`, "");
|
|
669
|
+
lines.push("### Approval", "", `- Status: ${audit.approvalStatus.status}`, `- Decision: ${audit.approvalStatus.decision}`, `- Required: ${audit.approvalStatus.required}`, `- Approved: ${audit.approvalStatus.approved}`, `- Gate: ${audit.approvalStatus.gate ?? "none"}`, `- Summary: ${audit.approvalStatus.summary}`, "");
|
|
670
|
+
if (audit.approvalStatus.approval) {
|
|
671
|
+
lines.push(`- Approved by: ${audit.approvalStatus.approval.approvedBy}`, `- Approved at: ${audit.approvalStatus.approval.approvedAt}`, "");
|
|
672
|
+
}
|
|
673
|
+
pushList(lines, "Approval why", audit.approvalStatus.why, 8);
|
|
674
|
+
lines.push("");
|
|
675
|
+
if (audit.blockingReasons.length > 0) {
|
|
676
|
+
lines.push("### Blocking reasons", "");
|
|
677
|
+
audit.blockingReasons.forEach((reason) => lines.push(`- ${reason}`));
|
|
678
|
+
lines.push("");
|
|
679
|
+
}
|
|
680
|
+
lines.push("### Gate handoff", "", `- Summary: ${gate.summary}`, `- Decision: ${gate.decision}`, `- Can continue: ${gate.canContinue}`, `- Must stop: ${gate.mustStop}`, `- Needs human: ${gate.needsHuman}`, "");
|
|
681
|
+
pushList(lines, "Gate why", gate.why, 8);
|
|
682
|
+
lines.push("");
|
|
683
|
+
pushList(lines, "Fix now", gate.fixNow, 12);
|
|
684
|
+
lines.push("");
|
|
685
|
+
pushList(lines, "Ask human", gate.askHuman, 8);
|
|
686
|
+
lines.push("");
|
|
687
|
+
pushList(lines, "Gate commands", uniqueItems([
|
|
688
|
+
...gate.commands.doctor,
|
|
689
|
+
...gate.commands.check,
|
|
690
|
+
...gate.commands.audit,
|
|
691
|
+
...gate.commands.repair,
|
|
692
|
+
...gate.commands.approve,
|
|
693
|
+
...gate.commands.unstage,
|
|
694
|
+
...gate.commands.verify,
|
|
695
|
+
]), 16);
|
|
696
|
+
lines.push("");
|
|
697
|
+
if (validation?.policyDrift) {
|
|
698
|
+
lines.push("### Policy drift", "", `- Status: ${validation.policyDrift.status}`, `- Decision: ${validation.policyDrift.decision}`, `- Summary: ${validation.policyDrift.summary}`, "");
|
|
699
|
+
pushList(lines, "Changed policy fields", validation.policyDrift.changedFields, 12);
|
|
700
|
+
lines.push("");
|
|
701
|
+
pushList(lines, "Policy drift fix", validation.policyDrift.fix, 8);
|
|
702
|
+
lines.push("");
|
|
703
|
+
}
|
|
704
|
+
if (validation?.readinessDrift) {
|
|
705
|
+
lines.push("### Readiness drift", "", `- Status: ${validation.readinessDrift.status}`, `- Decision: ${validation.readinessDrift.decision}`, `- Summary: ${validation.readinessDrift.summary}`, `- Saved enforcement: ${validation.readinessDrift.savedReadiness.enforcementLevel}`, `- Current enforcement: ${validation.readinessDrift.currentReadiness?.enforcementLevel ?? "unknown"}`, "");
|
|
706
|
+
pushList(lines, "Weakened readiness fields", validation.readinessDrift.weakenedFields, 12);
|
|
707
|
+
lines.push("");
|
|
708
|
+
pushList(lines, "Readiness drift fix", validation.readinessDrift.fix, 8);
|
|
709
|
+
lines.push("");
|
|
710
|
+
}
|
|
711
|
+
if (summary.files.length > 0) {
|
|
712
|
+
lines.push("### Changed files", "");
|
|
713
|
+
summary.files.slice(0, 20).forEach((file) => {
|
|
714
|
+
lines.push(`- ${file.file} (${file.modificationRisk}, importers: ${file.importerCount})`);
|
|
715
|
+
});
|
|
716
|
+
if (summary.files.length > 20) {
|
|
717
|
+
lines.push(`- ...and ${summary.files.length - 20} more`);
|
|
718
|
+
}
|
|
719
|
+
lines.push("");
|
|
720
|
+
}
|
|
721
|
+
lines.push("### Agent actions", "");
|
|
722
|
+
pushList(lines, "Trusted findings", summary.agentActions.trustedFindings, 12);
|
|
723
|
+
lines.push("");
|
|
724
|
+
pushList(lines, "Verify before commit", summary.agentActions.verifyBeforeCommit, 12);
|
|
725
|
+
lines.push("");
|
|
726
|
+
pushList(lines, "Manual review required", summary.agentActions.manualReviewRequired, 12);
|
|
727
|
+
lines.push("");
|
|
728
|
+
pushList(lines, "Fix actions", audit.repairPlan.fixActions.map(formatRepairActionForAgent), 12);
|
|
729
|
+
lines.push("");
|
|
730
|
+
if (audit.verificationTargets.length > 0) {
|
|
731
|
+
lines.push("### Verify", "");
|
|
732
|
+
audit.verificationTargets.slice(0, 20).forEach((target) => lines.push(`- ${target}`));
|
|
733
|
+
if (audit.verificationTargets.length > 20) {
|
|
734
|
+
lines.push(`- ...and ${audit.verificationTargets.length - 20} more`);
|
|
735
|
+
}
|
|
736
|
+
lines.push("");
|
|
737
|
+
}
|
|
738
|
+
return `${lines.join("\n")}\n`;
|
|
739
|
+
}
|
|
740
|
+
function printGithubCheckAnnotations(summary) {
|
|
741
|
+
const validation = summary.intentValidation;
|
|
742
|
+
if (!validation) {
|
|
743
|
+
printGithubErrorAnnotation({
|
|
744
|
+
title: "Ripple intent required",
|
|
745
|
+
message: "Strict Ripple checks need --intent latest or another saved change intent.",
|
|
746
|
+
});
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
if (validation.policyDrift.status === "changed") {
|
|
750
|
+
printGithubErrorAnnotation({
|
|
751
|
+
file: validation.targetFile,
|
|
752
|
+
title: "Ripple policy drift",
|
|
753
|
+
message: validation.policyDrift.summary,
|
|
754
|
+
});
|
|
755
|
+
validation.policyDrift.changedFields.slice(0, 8).forEach((field) => {
|
|
756
|
+
printGithubErrorAnnotation({
|
|
757
|
+
file: validation.targetFile,
|
|
758
|
+
title: "Ripple policy drift",
|
|
759
|
+
message: field,
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
if (validation.readinessDrift.status === "weakened") {
|
|
764
|
+
printGithubErrorAnnotation({
|
|
765
|
+
file: validation.targetFile,
|
|
766
|
+
title: "Ripple readiness drift",
|
|
767
|
+
message: validation.readinessDrift.summary,
|
|
768
|
+
});
|
|
769
|
+
validation.readinessDrift.weakenedFields.slice(0, 8).forEach((field) => {
|
|
770
|
+
printGithubErrorAnnotation({
|
|
771
|
+
file: validation.targetFile,
|
|
772
|
+
title: "Ripple readiness drift",
|
|
773
|
+
message: `Weakened readiness field: ${field}`,
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
validation.unplannedFiles.forEach((file) => {
|
|
778
|
+
printGithubErrorAnnotation({
|
|
779
|
+
file,
|
|
780
|
+
title: "Ripple intent drift",
|
|
781
|
+
message: `Unplanned file changed: ${file}`,
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
validation.unplannedSymbols.forEach((symbol) => {
|
|
785
|
+
const file = symbolFile(symbol);
|
|
786
|
+
printGithubErrorAnnotation({
|
|
787
|
+
file,
|
|
788
|
+
title: "Ripple symbol drift",
|
|
789
|
+
message: `Unplanned symbol changed: ${symbol}`,
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
validation.boundaryVerdict.changedOutsideBoundaryFiles.forEach((file) => {
|
|
793
|
+
printGithubErrorAnnotation({
|
|
794
|
+
file,
|
|
795
|
+
title: "Ripple boundary drift",
|
|
796
|
+
message: `File changed outside ${validation.controlMode} boundary: ${file}`,
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
validation.boundaryVerdict.changedOutsideBoundarySymbols.forEach((symbol) => {
|
|
800
|
+
printGithubErrorAnnotation({
|
|
801
|
+
file: symbolFile(symbol),
|
|
802
|
+
title: "Ripple boundary drift",
|
|
803
|
+
message: `Symbol changed outside ${validation.controlMode} boundary: ${symbol}`,
|
|
804
|
+
});
|
|
805
|
+
});
|
|
806
|
+
uniqueItems([
|
|
807
|
+
...validation.protectedContractChanges,
|
|
808
|
+
...validation.unplannedContractChanges,
|
|
809
|
+
]).forEach((symbol) => {
|
|
810
|
+
const file = symbolFile(symbol);
|
|
811
|
+
printGithubErrorAnnotation({
|
|
812
|
+
file,
|
|
813
|
+
title: "Ripple contract drift",
|
|
814
|
+
message: `Contract review required: ${symbol}`,
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
validation.blockingReasons
|
|
818
|
+
.filter((reason) => !reason.startsWith("Unplanned file changed: "))
|
|
819
|
+
.forEach((reason) => {
|
|
820
|
+
printGithubErrorAnnotation({
|
|
821
|
+
title: "Ripple check blocked",
|
|
822
|
+
message: reason,
|
|
823
|
+
});
|
|
824
|
+
});
|
|
825
|
+
summary.agentActions.trustedFindings.slice(0, 12).forEach((action) => {
|
|
826
|
+
printGithubNoticeAnnotation({
|
|
827
|
+
file: actionFile(action),
|
|
828
|
+
title: "Ripple trusted finding",
|
|
829
|
+
message: action,
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
summary.agentActions.verifyBeforeCommit.slice(0, 12).forEach((action) => {
|
|
833
|
+
printGithubWarningAnnotation({
|
|
834
|
+
file: actionFile(action),
|
|
835
|
+
title: "Ripple verify before commit",
|
|
836
|
+
message: action,
|
|
837
|
+
});
|
|
838
|
+
});
|
|
839
|
+
summary.agentActions.manualReviewRequired.slice(0, 12).forEach((action) => {
|
|
840
|
+
printGithubErrorAnnotation({
|
|
841
|
+
file: actionFile(action),
|
|
842
|
+
title: "Ripple manual review required",
|
|
843
|
+
message: action,
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
function printGithubAuditAnnotations(audit) {
|
|
848
|
+
const gate = (0, core_1.buildRippleGateSummary)(audit);
|
|
849
|
+
if (audit.status !== "pass") {
|
|
850
|
+
printGithubErrorAnnotation({
|
|
851
|
+
file: audit.intent.targetFile,
|
|
852
|
+
title: "Ripple gate closed",
|
|
853
|
+
message: `${gate.status}/${gate.decision}: next=${gate.nextRequiredPhase}. ${gate.nextRequiredAction}`,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
if (audit.approvalStatus.required && !audit.approvalStatus.approved) {
|
|
857
|
+
printGithubErrorAnnotation({
|
|
858
|
+
file: audit.intent.targetFile,
|
|
859
|
+
title: "Ripple approval required",
|
|
860
|
+
message: audit.approvalStatus.summary,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
printGithubCheckAnnotations(audit.stagedCheck);
|
|
864
|
+
}
|
|
865
|
+
function printGithubNoticeAnnotation(input) {
|
|
866
|
+
printGithubAnnotation("notice", input);
|
|
867
|
+
}
|
|
868
|
+
function printGithubWarningAnnotation(input) {
|
|
869
|
+
printGithubAnnotation("warning", input);
|
|
870
|
+
}
|
|
871
|
+
function printGithubErrorAnnotation(input) {
|
|
872
|
+
printGithubAnnotation("error", input);
|
|
873
|
+
}
|
|
874
|
+
function printGithubAnnotation(kind, input) {
|
|
875
|
+
const properties = [
|
|
876
|
+
input.file ? `file=${escapeGithubCommandProperty(input.file)}` : null,
|
|
877
|
+
`title=${escapeGithubCommandProperty(input.title)}`,
|
|
878
|
+
].filter(Boolean).join(",");
|
|
879
|
+
console.log(`::${kind} ${properties}::${escapeGithubCommandData(input.message)}`);
|
|
880
|
+
}
|
|
881
|
+
function escapeGithubCommandData(value) {
|
|
882
|
+
return value
|
|
883
|
+
.replace(/%/g, "%25")
|
|
884
|
+
.replace(/\r/g, "%0D")
|
|
885
|
+
.replace(/\n/g, "%0A");
|
|
886
|
+
}
|
|
887
|
+
function escapeGithubCommandProperty(value) {
|
|
888
|
+
return escapeGithubCommandData(value)
|
|
889
|
+
.replace(/:/g, "%3A")
|
|
890
|
+
.replace(/,/g, "%2C");
|
|
891
|
+
}
|
|
892
|
+
function symbolFile(symbol) {
|
|
893
|
+
const index = symbol.indexOf("::");
|
|
894
|
+
if (index <= 0) {
|
|
895
|
+
return undefined;
|
|
896
|
+
}
|
|
897
|
+
return symbol.slice(0, index);
|
|
898
|
+
}
|
|
899
|
+
function actionFile(action) {
|
|
900
|
+
const subjectEnd = action.indexOf(":");
|
|
901
|
+
if (subjectEnd <= 0) {
|
|
902
|
+
return undefined;
|
|
903
|
+
}
|
|
904
|
+
const subject = action.slice(0, subjectEnd);
|
|
905
|
+
const fileFromSymbol = symbolFile(subject);
|
|
906
|
+
if (fileFromSymbol) {
|
|
907
|
+
return fileFromSymbol;
|
|
908
|
+
}
|
|
909
|
+
return /\.(ts|tsx|js|jsx)$/i.test(subject) ? subject : undefined;
|
|
910
|
+
}
|
|
911
|
+
function relativeToWorkspace(workspaceRoot, filePath) {
|
|
912
|
+
return path.relative(workspaceRoot, filePath).split(path.sep).join("/");
|
|
913
|
+
}
|
|
914
|
+
function normalizeProjectPath(filePath) {
|
|
915
|
+
return filePath.replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
916
|
+
}
|
|
917
|
+
function formatEventLine(event) {
|
|
918
|
+
const target = event.target ? ` -> ${event.target}` : "";
|
|
919
|
+
const details = [
|
|
920
|
+
event.kind ? `kind:${event.kind}` : null,
|
|
921
|
+
event.layer ? `layer:${event.layer}` : null,
|
|
922
|
+
event.metadata ?? null,
|
|
923
|
+
].filter(Boolean).join(", ");
|
|
924
|
+
return `${event.type} ${event.source}${target}${details ? ` (${details})` : ""}`;
|
|
925
|
+
}
|
|
926
|
+
async function runWithQuietEngine(task) {
|
|
927
|
+
const originalLog = console.log;
|
|
928
|
+
console.log = () => { };
|
|
929
|
+
try {
|
|
930
|
+
return await task();
|
|
931
|
+
}
|
|
932
|
+
finally {
|
|
933
|
+
console.log = originalLog;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
function printScanSummary(summary) {
|
|
937
|
+
console.log("Ripple scan complete");
|
|
938
|
+
console.log(`Workspace: ${summary.workspace}`);
|
|
939
|
+
console.log(`Files: ${summary.files}`);
|
|
940
|
+
console.log(`Symbols: ${summary.symbols}`);
|
|
941
|
+
console.log(`Call edges: ${summary.callEdges}`);
|
|
942
|
+
console.log(`Context: .ripple/ ${summary.contextGenerated ? "generated" : "not generated"}`);
|
|
943
|
+
}
|
|
944
|
+
function printDoctorSummary(summary) {
|
|
945
|
+
console.log("Ripple doctor");
|
|
946
|
+
console.log(`Workspace: ${summary.workspace}`);
|
|
947
|
+
console.log(`Status: ${summary.status}`);
|
|
948
|
+
console.log("");
|
|
949
|
+
console.log(`Adapter: ${summary.adapterSupport.primaryAdapter.capabilities.displayName} (${summary.adapterSupport.supportLevel})`);
|
|
950
|
+
console.log(`Adapter confidence: ${Math.round(summary.adapterSupport.primaryAdapter.confidence * 100)}%`);
|
|
951
|
+
console.log(`Agent trust: ${summary.adapterSupport.primaryAdapter.agentPolicy.canTrust.join(", ")}`);
|
|
952
|
+
console.log(`Graph: ${summary.checks.graph.ok ? "ok" : "missing"} - ${summary.checks.graph.detail}`);
|
|
953
|
+
console.log(`Git: ${summary.checks.git.ok ? "ok" : "missing"} - ${summary.checks.git.detail}`);
|
|
954
|
+
console.log(`CI workflow: ${summary.checks.ciWorkflow.ok ? "ok" : "missing"} - ${summary.checks.ciWorkflow.detail}`);
|
|
955
|
+
console.log(`Latest intent: ${summary.checks.latestIntent.ok ? "ok" : "missing"} - ${summary.checks.latestIntent.detail}`);
|
|
956
|
+
console.log("");
|
|
957
|
+
console.log("Enforcement:");
|
|
958
|
+
console.log(` level: ${summary.enforcement.level}`);
|
|
959
|
+
console.log(` can guide agents: ${summary.enforcement.canGuideAgents}`);
|
|
960
|
+
console.log(` can detect drift: ${summary.enforcement.canDetectDrift}`);
|
|
961
|
+
console.log(` can block in CI: ${summary.enforcement.canBlockInCi}`);
|
|
962
|
+
console.log(` policy: ${summary.enforcement.explicitPolicy.detail}`);
|
|
963
|
+
console.log(` summary: ${summary.enforcement.summary}`);
|
|
964
|
+
if (summary.enforcement.gaps.length > 0) {
|
|
965
|
+
console.log(" gaps:");
|
|
966
|
+
summary.enforcement.gaps.forEach((gap) => console.log(` - ${gap}`));
|
|
967
|
+
}
|
|
968
|
+
console.log("");
|
|
969
|
+
console.log("Next steps:");
|
|
970
|
+
summary.nextSteps.forEach((step) => console.log(` - ${step}`));
|
|
971
|
+
}
|
|
972
|
+
function printInitSummary(summary) {
|
|
973
|
+
console.log("Ripple init");
|
|
974
|
+
console.log(`Workspace: ${summary.workspace}`);
|
|
975
|
+
console.log("");
|
|
976
|
+
console.log("Setup files:");
|
|
977
|
+
summary.files.forEach((file) => {
|
|
978
|
+
console.log(` - ${file.path}: ${file.status}`);
|
|
979
|
+
});
|
|
980
|
+
if (summary.readiness) {
|
|
981
|
+
console.log("");
|
|
982
|
+
console.log("Readiness after init:");
|
|
983
|
+
console.log(` status: ${summary.readiness.status}`);
|
|
984
|
+
console.log(` enforcement: ${summary.readiness.enforcement.level}`);
|
|
985
|
+
console.log(` can guide agents: ${summary.readiness.enforcement.canGuideAgents}`);
|
|
986
|
+
console.log(` can detect drift: ${summary.readiness.enforcement.canDetectDrift}`);
|
|
987
|
+
console.log(` can block in CI: ${summary.readiness.enforcement.canBlockInCi}`);
|
|
988
|
+
}
|
|
989
|
+
console.log("");
|
|
990
|
+
console.log("Next steps:");
|
|
991
|
+
summary.nextSteps.forEach((step) => console.log(` - ${step}`));
|
|
992
|
+
}
|
|
993
|
+
function printAgentDoctorSummary(summary) {
|
|
994
|
+
console.log("RIPPLE_DOCTOR");
|
|
995
|
+
console.log(`status: ${summary.status}`);
|
|
996
|
+
console.log(`readiness_decision: ${summary.status === "ready" ? "continue" : "setup-required"}`);
|
|
997
|
+
console.log(`workspace: ${summary.workspace}`);
|
|
998
|
+
console.log(`adapter: ${summary.adapterSupport.primaryAdapter.id}`);
|
|
999
|
+
console.log(`adapter_support: ${summary.adapterSupport.supportLevel}`);
|
|
1000
|
+
console.log(`adapter_confidence: ${Math.round(summary.adapterSupport.primaryAdapter.confidence * 100)}%`);
|
|
1001
|
+
console.log(`enforcement_level: ${summary.enforcement.level}`);
|
|
1002
|
+
console.log(`can_guide_agents: ${summary.enforcement.canGuideAgents}`);
|
|
1003
|
+
console.log(`can_detect_drift: ${summary.enforcement.canDetectDrift}`);
|
|
1004
|
+
console.log(`can_block_in_ci: ${summary.enforcement.canBlockInCi}`);
|
|
1005
|
+
console.log(`policy_explicit: ${summary.enforcement.explicitPolicy.ok}`);
|
|
1006
|
+
console.log(`policy_detail: ${summary.enforcement.explicitPolicy.detail}`);
|
|
1007
|
+
console.log(`graph: ${summary.checks.graph.ok ? "ok" : "missing"} - ${summary.checks.graph.detail}`);
|
|
1008
|
+
console.log(`git: ${summary.checks.git.ok ? "ok" : "missing"} - ${summary.checks.git.detail}`);
|
|
1009
|
+
console.log(`ci_workflow: ${summary.checks.ciWorkflow.ok ? "ok" : "missing"} - ${summary.checks.ciWorkflow.detail}`);
|
|
1010
|
+
console.log(`latest_intent: ${summary.checks.latestIntent.ok ? "ok" : "missing"} - ${summary.checks.latestIntent.detail}`);
|
|
1011
|
+
console.log("");
|
|
1012
|
+
printAgentList("gaps", summary.enforcement.gaps);
|
|
1013
|
+
console.log("");
|
|
1014
|
+
printAgentList("next_steps", summary.nextSteps);
|
|
1015
|
+
}
|
|
1016
|
+
function printHistorySummary(summary) {
|
|
1017
|
+
console.log("Ripple history");
|
|
1018
|
+
console.log(`Events: ${summary.totalEvents}`);
|
|
1019
|
+
console.log(`Groups: ${summary.returnedGroups}`);
|
|
1020
|
+
if (summary.groups.length === 0) {
|
|
1021
|
+
console.log("");
|
|
1022
|
+
console.log("No history events found.");
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
summary.groups.forEach((group, index) => {
|
|
1026
|
+
console.log("");
|
|
1027
|
+
console.log(`${index + 1}. ${group.changedAt} ${group.id}`);
|
|
1028
|
+
console.log(` Events: ${group.eventCount}`);
|
|
1029
|
+
if (group.filesChanged.length > 0) {
|
|
1030
|
+
console.log(` Files: ${group.filesChanged.join(", ")}`);
|
|
1031
|
+
}
|
|
1032
|
+
if (group.symbolsChanged.length > 0) {
|
|
1033
|
+
console.log(` Symbols: ${group.symbolsChanged.slice(0, 5).join(", ")}`);
|
|
1034
|
+
}
|
|
1035
|
+
if (group.relatedFiles.length > 0) {
|
|
1036
|
+
console.log(` Related: ${group.relatedFiles.join(", ")}`);
|
|
1037
|
+
}
|
|
1038
|
+
group.events.slice(0, 5).forEach((event) => {
|
|
1039
|
+
console.log(` - ${formatEventLine(event)}`);
|
|
1040
|
+
});
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
function printPlanFiles(title, files) {
|
|
1044
|
+
console.log(title);
|
|
1045
|
+
if (files.length === 0) {
|
|
1046
|
+
console.log(" none");
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
files.forEach((file) => {
|
|
1050
|
+
const role = file.role ?? "related";
|
|
1051
|
+
const score = file.score === undefined ? "" : `, score: ${file.score}`;
|
|
1052
|
+
const signals = file.signals && file.signals.length > 0
|
|
1053
|
+
? file.signals.join(", ")
|
|
1054
|
+
: "none";
|
|
1055
|
+
console.log(` - ${file.file} [${role}, ${file.modificationRisk}${score}, ~${file.estimatedTokens} tokens]`);
|
|
1056
|
+
console.log(` signals: ${signals}`);
|
|
1057
|
+
if (file.adapterSignals && file.adapterSignals.length > 0) {
|
|
1058
|
+
console.log(` adapter signals: ${formatAdapterSignalInline(file.adapterSignals)}`);
|
|
1059
|
+
}
|
|
1060
|
+
console.log(` reason: ${file.reason}`);
|
|
1061
|
+
console.log(` focus: ${file.focus}`);
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
function formatAdapterSignalInline(signals) {
|
|
1065
|
+
if (!signals || signals.length === 0) {
|
|
1066
|
+
return "none";
|
|
1067
|
+
}
|
|
1068
|
+
return signals
|
|
1069
|
+
.map((signal) => `${signal.capability}:${signal.agentUse}/${Math.round(signal.confidence * 100)}%`)
|
|
1070
|
+
.join(", ");
|
|
1071
|
+
}
|
|
1072
|
+
function printSymbolFocus(symbols) {
|
|
1073
|
+
console.log("Symbol focus:");
|
|
1074
|
+
if (symbols.length === 0) {
|
|
1075
|
+
console.log(" none");
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
symbols.slice(0, 12).forEach((symbol) => {
|
|
1079
|
+
const signals = symbol.signals.length > 0 ? symbol.signals.join(", ") : "none";
|
|
1080
|
+
console.log(` - ${symbol.symbol} [${symbol.kind}, ${symbol.layer}, score: ${symbol.score}, callers: ${symbol.callers}, calls: ${symbol.calls}]`);
|
|
1081
|
+
console.log(` signals: ${signals}`);
|
|
1082
|
+
console.log(` reason: ${symbol.reason}`);
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
function adapterConfidencePercent(summary) {
|
|
1086
|
+
return Math.round(summary.adapterSupport.primaryAdapter.confidence * 100);
|
|
1087
|
+
}
|
|
1088
|
+
function printPlanAdapterSupport(summary) {
|
|
1089
|
+
const adapter = summary.adapterSupport.primaryAdapter;
|
|
1090
|
+
console.log(`Adapter: ${adapter.capabilities.displayName} (${adapter.supportLevel}, ${adapterConfidencePercent(summary)}%)`);
|
|
1091
|
+
console.log(`Adapter language: ${adapter.capabilities.language}`);
|
|
1092
|
+
console.log("Adapter trust:");
|
|
1093
|
+
adapter.agentPolicy.canTrust.forEach((item) => console.log(` - ${item}`));
|
|
1094
|
+
console.log("Adapter verify:");
|
|
1095
|
+
adapter.agentPolicy.beCarefulWith.forEach((item) => console.log(` - ${item}`));
|
|
1096
|
+
if (adapter.agentPolicy.mustFallbackToManual.length > 0) {
|
|
1097
|
+
console.log("Adapter manual fallback:");
|
|
1098
|
+
adapter.agentPolicy.mustFallbackToManual.forEach((item) => console.log(` - ${item}`));
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function printContextPlan(summary, savedIntent) {
|
|
1102
|
+
console.log("Ripple context plan");
|
|
1103
|
+
console.log(`Task: ${summary.task}`);
|
|
1104
|
+
console.log(`Target: ${summary.targetFile}`);
|
|
1105
|
+
console.log(`Risk: ${summary.risk}`);
|
|
1106
|
+
console.log(`Budget: ${summary.tokenBudget}`);
|
|
1107
|
+
console.log(`Estimated readFirst tokens: ${summary.estimatedTokens}`);
|
|
1108
|
+
console.log(`Why: ${summary.why}`);
|
|
1109
|
+
console.log("");
|
|
1110
|
+
printPlanAdapterSupport(summary);
|
|
1111
|
+
if (summary.planningSignals && summary.planningSignals.length > 0) {
|
|
1112
|
+
console.log("");
|
|
1113
|
+
console.log("Planning signals:");
|
|
1114
|
+
summary.planningSignals.forEach((signal) => console.log(` - ${signal}`));
|
|
1115
|
+
}
|
|
1116
|
+
console.log("");
|
|
1117
|
+
printPlanFiles("Read first:", summary.readFirst);
|
|
1118
|
+
console.log("");
|
|
1119
|
+
printPlanFiles("Read if needed:", summary.readIfNeeded);
|
|
1120
|
+
console.log("");
|
|
1121
|
+
printSymbolFocus(summary.symbolFocus);
|
|
1122
|
+
console.log("");
|
|
1123
|
+
console.log("Avoid initially:");
|
|
1124
|
+
summary.avoidInitially.forEach((item) => console.log(` - ${item}`));
|
|
1125
|
+
if (summary.doNotReadFirst && summary.doNotReadFirst.length > 0) {
|
|
1126
|
+
console.log("");
|
|
1127
|
+
console.log("Do not read first:");
|
|
1128
|
+
summary.doNotReadFirst.forEach((item) => console.log(` - ${item}`));
|
|
1129
|
+
}
|
|
1130
|
+
if (summary.verificationTargets.length > 0) {
|
|
1131
|
+
console.log("");
|
|
1132
|
+
console.log("Verification targets:");
|
|
1133
|
+
summary.verificationTargets.slice(0, 12).forEach((item) => console.log(` - ${item}`));
|
|
1134
|
+
}
|
|
1135
|
+
if (savedIntent) {
|
|
1136
|
+
console.log("");
|
|
1137
|
+
console.log(`Saved change intent: ${relativeToWorkspace(process.cwd(), savedIntent.path)}`);
|
|
1138
|
+
console.log(`Intent id: ${savedIntent.intent.id}`);
|
|
1139
|
+
console.log(`Control mode: ${savedIntent.intent.controlMode}`);
|
|
1140
|
+
console.log(`Human gate: ${savedIntent.intent.humanGate}`);
|
|
1141
|
+
console.log(`Boundary risk: ${savedIntent.intent.boundaryRisk}`);
|
|
1142
|
+
console.log(`Policy source: ${savedIntent.intent.policySource}`);
|
|
1143
|
+
console.log(`Enforcement at plan time: ${savedIntent.intent.readinessSnapshot.enforcementLevel}`);
|
|
1144
|
+
console.log(`Can detect drift: ${savedIntent.intent.readinessSnapshot.canDetectDrift ? "yes" : "no"}`);
|
|
1145
|
+
console.log(`Can block in CI: ${savedIntent.intent.readinessSnapshot.canBlockInCi ? "yes" : "no"}`);
|
|
1146
|
+
console.log("Editable files:");
|
|
1147
|
+
savedIntent.intent.editableFiles.forEach((file) => console.log(` - ${file}`));
|
|
1148
|
+
if (savedIntent.intent.allowedSymbols.length > 0) {
|
|
1149
|
+
console.log("Allowed symbols:");
|
|
1150
|
+
savedIntent.intent.allowedSymbols.forEach((symbol) => console.log(` - ${symbol}`));
|
|
1151
|
+
}
|
|
1152
|
+
if (savedIntent.intent.policyMatches.length > 0) {
|
|
1153
|
+
console.log("Policy matches:");
|
|
1154
|
+
savedIntent.intent.policyMatches.forEach((match) => console.log(` - ${match}`));
|
|
1155
|
+
}
|
|
1156
|
+
console.log("Context-only files:");
|
|
1157
|
+
savedIntent.intent.contextFiles.slice(0, 12).forEach((file) => console.log(` - ${file}`));
|
|
1158
|
+
if (savedIntent.intent.humanGateReason.length > 0) {
|
|
1159
|
+
console.log("Human gate reason:");
|
|
1160
|
+
savedIntent.intent.humanGateReason.forEach((reason) => console.log(` - ${reason}`));
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
function printAgentList(title, items) {
|
|
1165
|
+
console.log(`${title}:`);
|
|
1166
|
+
if (items.length === 0) {
|
|
1167
|
+
console.log("- none");
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
items.forEach((item) => console.log(`- ${item}`));
|
|
1171
|
+
}
|
|
1172
|
+
function printAgentHandoffBlock(title, handoff) {
|
|
1173
|
+
console.log(`${title}:`);
|
|
1174
|
+
console.log(`can_continue: ${handoff.canContinue}`);
|
|
1175
|
+
console.log(`must_stop: ${handoff.mustStop}`);
|
|
1176
|
+
console.log(`needs_human: ${handoff.needsHuman}`);
|
|
1177
|
+
console.log(`decision: ${handoff.decision}`);
|
|
1178
|
+
console.log(`next_required_phase: ${handoff.nextRequiredPhase}`);
|
|
1179
|
+
console.log(`next_required_action: ${handoff.nextRequiredAction}`);
|
|
1180
|
+
console.log(`summary: ${handoff.summary}`);
|
|
1181
|
+
console.log("");
|
|
1182
|
+
printAgentList("handoff_why", handoff.why);
|
|
1183
|
+
console.log("");
|
|
1184
|
+
printAgentList("fix_now", handoff.fixNow);
|
|
1185
|
+
console.log("");
|
|
1186
|
+
printAgentList("ask_human", handoff.askHuman);
|
|
1187
|
+
console.log("");
|
|
1188
|
+
printAgentList("commands_doctor", handoff.commands.doctor);
|
|
1189
|
+
console.log("");
|
|
1190
|
+
printAgentList("commands_plan", handoff.commands.plan);
|
|
1191
|
+
console.log("");
|
|
1192
|
+
printAgentList("commands_check", handoff.commands.check);
|
|
1193
|
+
console.log("");
|
|
1194
|
+
printAgentList("commands_audit", handoff.commands.audit);
|
|
1195
|
+
console.log("");
|
|
1196
|
+
printAgentList("commands_repair", handoff.commands.repair);
|
|
1197
|
+
console.log("");
|
|
1198
|
+
printAgentList("commands_approve", handoff.commands.approve);
|
|
1199
|
+
console.log("");
|
|
1200
|
+
printAgentList("commands_unstage", handoff.commands.unstage);
|
|
1201
|
+
console.log("");
|
|
1202
|
+
printAgentList("commands_verify", handoff.commands.verify);
|
|
1203
|
+
}
|
|
1204
|
+
function printAgentPolicyExplanationBlock(title, explanation) {
|
|
1205
|
+
console.log(`${title}:`);
|
|
1206
|
+
console.log(`effective_mode: ${explanation.effectiveMode}`);
|
|
1207
|
+
console.log(`policy_risk: ${explanation.policyRisk}`);
|
|
1208
|
+
console.log(`human_gate: ${explanation.humanGate}`);
|
|
1209
|
+
console.log(`human_required: ${explanation.humanRequired}`);
|
|
1210
|
+
console.log(`policy_source: ${explanation.policySource}`);
|
|
1211
|
+
printAgentList("policy_matches", explanation.matchedRules);
|
|
1212
|
+
}
|
|
1213
|
+
function printPolicyExplanationSummary(title, explanation) {
|
|
1214
|
+
console.log(title);
|
|
1215
|
+
console.log(` effective mode: ${explanation.effectiveMode}`);
|
|
1216
|
+
console.log(` policy risk: ${explanation.policyRisk}`);
|
|
1217
|
+
console.log(` human gate: ${explanation.humanGate}`);
|
|
1218
|
+
console.log(` human required: ${explanation.humanRequired}`);
|
|
1219
|
+
console.log(` policy source: ${explanation.policySource}`);
|
|
1220
|
+
console.log(` matched rules: ${explanation.matchedRules.length > 0 ? explanation.matchedRules.join("; ") : "none"}`);
|
|
1221
|
+
}
|
|
1222
|
+
function printAgentPolicyDriftBlock(title, drift) {
|
|
1223
|
+
console.log(`${title}:`);
|
|
1224
|
+
console.log(`label: ${drift.label}`);
|
|
1225
|
+
console.log(`status: ${drift.status}`);
|
|
1226
|
+
console.log(`decision: ${drift.decision}`);
|
|
1227
|
+
console.log(`summary: ${drift.summary}`);
|
|
1228
|
+
printAgentList("changed_policy_fields", drift.changedFields);
|
|
1229
|
+
console.log("");
|
|
1230
|
+
printAgentList("policy_drift_why", drift.why);
|
|
1231
|
+
console.log("");
|
|
1232
|
+
printAgentList("policy_drift_fix", drift.fix);
|
|
1233
|
+
}
|
|
1234
|
+
function printAgentReadinessDriftBlock(title, drift) {
|
|
1235
|
+
console.log(`${title}:`);
|
|
1236
|
+
console.log(`label: ${drift.label}`);
|
|
1237
|
+
console.log(`status: ${drift.status}`);
|
|
1238
|
+
console.log(`decision: ${drift.decision}`);
|
|
1239
|
+
console.log(`summary: ${drift.summary}`);
|
|
1240
|
+
console.log(`saved_enforcement_level: ${drift.savedReadiness.enforcementLevel}`);
|
|
1241
|
+
if (drift.currentReadiness) {
|
|
1242
|
+
console.log(`current_enforcement_level: ${drift.currentReadiness.enforcementLevel}`);
|
|
1243
|
+
}
|
|
1244
|
+
printAgentList("changed_readiness_fields", drift.changedFields);
|
|
1245
|
+
console.log("");
|
|
1246
|
+
printAgentList("weakened_readiness_fields", drift.weakenedFields);
|
|
1247
|
+
console.log("");
|
|
1248
|
+
printAgentList("readiness_drift_why", drift.why);
|
|
1249
|
+
console.log("");
|
|
1250
|
+
printAgentList("readiness_drift_fix", drift.fix);
|
|
1251
|
+
}
|
|
1252
|
+
function printPolicyDriftSummary(title, drift) {
|
|
1253
|
+
console.log(title);
|
|
1254
|
+
console.log(` verdict: ${drift.label}`);
|
|
1255
|
+
console.log(` status: ${drift.status}`);
|
|
1256
|
+
console.log(` decision: ${drift.decision}`);
|
|
1257
|
+
console.log(` summary: ${drift.summary}`);
|
|
1258
|
+
if (drift.changedFields.length > 0) {
|
|
1259
|
+
console.log(" changed fields:");
|
|
1260
|
+
drift.changedFields.forEach((field) => console.log(` - ${field}`));
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
function printReadinessDriftSummary(title, drift) {
|
|
1264
|
+
console.log(title);
|
|
1265
|
+
console.log(` verdict: ${drift.label}`);
|
|
1266
|
+
console.log(` status: ${drift.status}`);
|
|
1267
|
+
console.log(` decision: ${drift.decision}`);
|
|
1268
|
+
console.log(` summary: ${drift.summary}`);
|
|
1269
|
+
console.log(` saved enforcement: ${drift.savedReadiness.enforcementLevel}`);
|
|
1270
|
+
if (drift.currentReadiness) {
|
|
1271
|
+
console.log(` current enforcement: ${drift.currentReadiness.enforcementLevel}`);
|
|
1272
|
+
}
|
|
1273
|
+
if (drift.weakenedFields.length > 0) {
|
|
1274
|
+
console.log(" weakened fields:");
|
|
1275
|
+
drift.weakenedFields.forEach((field) => console.log(` - ${field}`));
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
function printAgentContextPlan(summary, savedIntent) {
|
|
1279
|
+
const adapter = summary.adapterSupport.primaryAdapter;
|
|
1280
|
+
console.log("RIPPLE_AGENT_CONTEXT");
|
|
1281
|
+
console.log(`task: ${summary.task}`);
|
|
1282
|
+
console.log(`target: ${summary.targetFile}`);
|
|
1283
|
+
console.log(`risk: ${summary.risk}`);
|
|
1284
|
+
console.log(`adapter: ${adapter.id} (${adapter.capabilities.displayName}, ${adapter.supportLevel}, ${adapterConfidencePercent(summary)}%)`);
|
|
1285
|
+
console.log(`adapter_language: ${adapter.capabilities.language}`);
|
|
1286
|
+
console.log(`token_budget: ${summary.tokenBudget}`);
|
|
1287
|
+
console.log(`estimated_read_first_tokens: ${summary.estimatedTokens}`);
|
|
1288
|
+
if (savedIntent) {
|
|
1289
|
+
console.log(`intent_id: ${savedIntent.intent.id}`);
|
|
1290
|
+
console.log(`intent_path: ${relativeToWorkspace(process.cwd(), savedIntent.path)}`);
|
|
1291
|
+
console.log(`control_mode: ${savedIntent.intent.controlMode}`);
|
|
1292
|
+
console.log(`human_gate: ${savedIntent.intent.humanGate}`);
|
|
1293
|
+
console.log(`human_required: ${savedIntent.intent.humanGate !== "none"}`);
|
|
1294
|
+
console.log(`boundary_risk: ${savedIntent.intent.boundaryRisk}`);
|
|
1295
|
+
console.log(`policy_source: ${savedIntent.intent.policySource}`);
|
|
1296
|
+
console.log(`readiness_status: ${savedIntent.intent.readinessSnapshot.status}`);
|
|
1297
|
+
console.log(`enforcement_level: ${savedIntent.intent.readinessSnapshot.enforcementLevel}`);
|
|
1298
|
+
console.log(`can_guide_agents: ${savedIntent.intent.readinessSnapshot.canGuideAgents}`);
|
|
1299
|
+
console.log(`can_detect_drift: ${savedIntent.intent.readinessSnapshot.canDetectDrift}`);
|
|
1300
|
+
console.log(`can_block_in_ci: ${savedIntent.intent.readinessSnapshot.canBlockInCi}`);
|
|
1301
|
+
console.log(`policy_explicit: ${savedIntent.intent.readinessSnapshot.policyExplicit}`);
|
|
1302
|
+
}
|
|
1303
|
+
console.log("");
|
|
1304
|
+
if (savedIntent) {
|
|
1305
|
+
printAgentPolicyExplanationBlock("policy_explanation", savedIntent.intent.policyExplanation);
|
|
1306
|
+
console.log("");
|
|
1307
|
+
printAgentList("readiness_gaps", savedIntent.intent.readinessSnapshot.gaps);
|
|
1308
|
+
console.log("");
|
|
1309
|
+
printAgentList("readiness_next_steps", savedIntent.intent.readinessSnapshot.nextSteps);
|
|
1310
|
+
console.log("");
|
|
1311
|
+
printAgentList("allowed_files", savedIntent.intent.editableFiles);
|
|
1312
|
+
console.log("");
|
|
1313
|
+
printAgentList("allowed_symbols", savedIntent.intent.allowedSymbols);
|
|
1314
|
+
console.log("");
|
|
1315
|
+
printAgentList("human_gate_reason", savedIntent.intent.humanGateReason);
|
|
1316
|
+
console.log("");
|
|
1317
|
+
printAgentList("editable_files", savedIntent.intent.editableFiles);
|
|
1318
|
+
console.log("");
|
|
1319
|
+
printAgentList("context_files", savedIntent.intent.contextFiles.slice(0, 16));
|
|
1320
|
+
console.log("");
|
|
1321
|
+
}
|
|
1322
|
+
printAgentList("adapter_trust", adapter.agentPolicy.canTrust);
|
|
1323
|
+
console.log("");
|
|
1324
|
+
printAgentList("adapter_verify", adapter.agentPolicy.beCarefulWith);
|
|
1325
|
+
console.log("");
|
|
1326
|
+
printAgentList("adapter_manual_fallback", adapter.agentPolicy.mustFallbackToManual);
|
|
1327
|
+
console.log("");
|
|
1328
|
+
printAgentList("adapter_guidance", adapter.agentPolicy.planningGuidance);
|
|
1329
|
+
console.log("");
|
|
1330
|
+
printAgentList("read_first", summary.readFirst.map((file) => file.file));
|
|
1331
|
+
if (summary.readIfNeeded.length > 0) {
|
|
1332
|
+
console.log("");
|
|
1333
|
+
printAgentList("read_if_needed", summary.readIfNeeded.slice(0, 8).map((file) => file.file));
|
|
1334
|
+
}
|
|
1335
|
+
console.log("");
|
|
1336
|
+
printAgentList("symbols_first", summary.symbolFocus.slice(0, 8).map((symbol) => symbol.symbol));
|
|
1337
|
+
console.log("");
|
|
1338
|
+
printAgentList("verify", summary.verificationTargets.slice(0, 12));
|
|
1339
|
+
console.log("");
|
|
1340
|
+
printAgentList("avoid_first", (summary.doNotReadFirst ?? summary.avoidInitially).slice(0, 6));
|
|
1341
|
+
}
|
|
1342
|
+
function uniqueItems(items) {
|
|
1343
|
+
const seen = new Set();
|
|
1344
|
+
return items.filter((item) => {
|
|
1345
|
+
if (seen.has(item)) {
|
|
1346
|
+
return false;
|
|
1347
|
+
}
|
|
1348
|
+
seen.add(item);
|
|
1349
|
+
return true;
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
function printAgentIntentValidation(validation) {
|
|
1353
|
+
if (!validation) {
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
console.log("");
|
|
1357
|
+
console.log(`intent_id: ${validation.intentId}`);
|
|
1358
|
+
console.log(`intent_verdict: ${validation.verdict}`);
|
|
1359
|
+
console.log(`control_mode: ${validation.controlMode}`);
|
|
1360
|
+
console.log(`human_gate: ${validation.humanGate}`);
|
|
1361
|
+
console.log(`human_required: ${validation.boundaryVerdict.humanRequired}`);
|
|
1362
|
+
console.log(`boundary_risk: ${validation.boundaryRisk}`);
|
|
1363
|
+
console.log(`boundary_verdict: ${validation.boundaryVerdict.label}`);
|
|
1364
|
+
console.log(`boundary_status: ${validation.boundaryVerdict.status}`);
|
|
1365
|
+
console.log(`boundary_decision: ${validation.boundaryVerdict.decision}`);
|
|
1366
|
+
console.log(`boundary_summary: ${validation.boundaryVerdict.summary}`);
|
|
1367
|
+
console.log(`planned_scope: ${validation.plannedScope}`);
|
|
1368
|
+
printAgentDriftVerdict(validation.driftVerdict);
|
|
1369
|
+
console.log(`next_required_phase: ${validation.nextRequiredPhase}`);
|
|
1370
|
+
console.log(`next_required_action: ${validation.nextRequiredAction}`);
|
|
1371
|
+
console.log(`recommended_action: ${validation.recommendedAction}`);
|
|
1372
|
+
console.log("");
|
|
1373
|
+
printAgentHandoffBlock("handoff", validation.handoff);
|
|
1374
|
+
console.log("");
|
|
1375
|
+
printAgentPolicyExplanationBlock("saved_policy_explanation", validation.policyExplanation);
|
|
1376
|
+
console.log("");
|
|
1377
|
+
printAgentPolicyDriftBlock("policy_drift", validation.policyDrift);
|
|
1378
|
+
console.log("");
|
|
1379
|
+
printAgentReadinessDriftBlock("readiness_drift", validation.readinessDrift);
|
|
1380
|
+
console.log("");
|
|
1381
|
+
printAgentList("allowed_files", validation.allowedFiles);
|
|
1382
|
+
console.log("");
|
|
1383
|
+
printAgentList("allowed_symbols", validation.allowedSymbols);
|
|
1384
|
+
console.log("");
|
|
1385
|
+
printAgentList("boundary_why", validation.boundaryVerdict.why);
|
|
1386
|
+
console.log("");
|
|
1387
|
+
printAgentList("boundary_fix", validation.boundaryVerdict.fix);
|
|
1388
|
+
console.log("");
|
|
1389
|
+
printAgentList("changed_outside_boundary_files", validation.boundaryVerdict.changedOutsideBoundaryFiles);
|
|
1390
|
+
console.log("");
|
|
1391
|
+
printAgentList("changed_outside_boundary_symbols", validation.boundaryVerdict.changedOutsideBoundarySymbols);
|
|
1392
|
+
console.log("");
|
|
1393
|
+
printAgentList("blocking_reasons", validation.blockingReasons);
|
|
1394
|
+
console.log("");
|
|
1395
|
+
printAgentList("next_steps", validation.nextSteps);
|
|
1396
|
+
console.log("");
|
|
1397
|
+
printAgentList("editable_files", validation.editableFiles);
|
|
1398
|
+
console.log("");
|
|
1399
|
+
printAgentList("context_files_changed", validation.contextFilesChanged);
|
|
1400
|
+
console.log("");
|
|
1401
|
+
printAgentList("unplanned_files", validation.unplannedFiles);
|
|
1402
|
+
console.log("");
|
|
1403
|
+
printAgentList("unplanned_symbols", validation.unplannedSymbols);
|
|
1404
|
+
console.log("");
|
|
1405
|
+
printAgentList("contract_drift", uniqueItems([
|
|
1406
|
+
...validation.protectedContractChanges,
|
|
1407
|
+
...validation.unplannedContractChanges,
|
|
1408
|
+
]));
|
|
1409
|
+
}
|
|
1410
|
+
function printAgentDriftVerdict(verdict) {
|
|
1411
|
+
console.log(`drift_verdict: ${verdict.label}`);
|
|
1412
|
+
console.log(`drift_status: ${verdict.status}`);
|
|
1413
|
+
console.log(`drift_decision: ${verdict.decision}`);
|
|
1414
|
+
console.log(`drift_summary: ${verdict.summary}`);
|
|
1415
|
+
console.log("");
|
|
1416
|
+
printAgentList("drift_why", verdict.why);
|
|
1417
|
+
console.log("");
|
|
1418
|
+
printAgentList("drift_fix", verdict.fix);
|
|
1419
|
+
}
|
|
1420
|
+
function printAgentStagedCheckSummary(summary) {
|
|
1421
|
+
const adapter = summary.adapterSupport.primaryAdapter;
|
|
1422
|
+
console.log("RIPPLE_STAGED_CHECK");
|
|
1423
|
+
console.log(`mode: ${summary.mode}`);
|
|
1424
|
+
if (summary.baseRef) {
|
|
1425
|
+
console.log(`base_ref: ${summary.baseRef}`);
|
|
1426
|
+
}
|
|
1427
|
+
console.log(`highest_risk: ${summary.highestRisk}`);
|
|
1428
|
+
console.log(`requires_attention: ${summary.requiresAttention}`);
|
|
1429
|
+
console.log(`adapter: ${adapter.id} (${adapter.capabilities.displayName}, ${adapter.supportLevel}, ${Math.round(adapter.confidence * 100)}%)`);
|
|
1430
|
+
console.log(`adapter_language: ${adapter.capabilities.language}`);
|
|
1431
|
+
console.log(`checked_js_ts_files: ${summary.stagedFiles}`);
|
|
1432
|
+
console.log(`checked_files: ${summary.checkedFiles}`);
|
|
1433
|
+
printAgentIntentValidation(summary.intentValidation);
|
|
1434
|
+
console.log("");
|
|
1435
|
+
printAgentList("trusted_findings", summary.agentActions.trustedFindings);
|
|
1436
|
+
console.log("");
|
|
1437
|
+
printAgentList("verify_before_commit", summary.agentActions.verifyBeforeCommit);
|
|
1438
|
+
console.log("");
|
|
1439
|
+
printAgentList("manual_review_required", summary.agentActions.manualReviewRequired);
|
|
1440
|
+
console.log("");
|
|
1441
|
+
console.log("changed_files:");
|
|
1442
|
+
if (summary.files.length === 0) {
|
|
1443
|
+
console.log("- none");
|
|
1444
|
+
}
|
|
1445
|
+
else {
|
|
1446
|
+
summary.files.forEach((file) => {
|
|
1447
|
+
console.log(`- ${file.file} [${file.modificationRisk}, importers: ${file.importerCount}, symbols: ${file.symbolCount}, adapter: ${formatAdapterSignalInline(file.adapterSignals)}]`);
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
console.log("");
|
|
1451
|
+
printAgentList("changed_symbols", summary.changedSymbols.slice(0, 16).map((symbol) => {
|
|
1452
|
+
return `${symbol.symbol} [${symbol.symbolStatus}, ${symbol.changeKind}, signature_changed: ${symbol.signatureChanged}, risk: ${symbol.contractRisk}, callers: ${symbol.callers}, adapter: ${formatAdapterSignalInline(symbol.adapterSignals)}]`;
|
|
1453
|
+
}));
|
|
1454
|
+
console.log("");
|
|
1455
|
+
printAgentList("contract_risk", summary.contractRisks.slice(0, 16).map((risk) => {
|
|
1456
|
+
return `${risk.symbol} [${risk.risk}, adapter: ${formatAdapterSignalInline(risk.adapterSignals)}] ${risk.reason}`;
|
|
1457
|
+
}));
|
|
1458
|
+
console.log("");
|
|
1459
|
+
printAgentList("read_first", uniqueItems(summary.files.flatMap((file) => file.readFirst)).slice(0, 16));
|
|
1460
|
+
console.log("");
|
|
1461
|
+
printAgentList("symbols_first", uniqueItems(summary.files.flatMap((file) => file.symbolFocus)).slice(0, 16));
|
|
1462
|
+
console.log("");
|
|
1463
|
+
printAgentList("verify", uniqueItems(summary.files.flatMap((file) => file.verificationTargets)).slice(0, 16));
|
|
1464
|
+
if (summary.skippedFiles.length > 0) {
|
|
1465
|
+
console.log("");
|
|
1466
|
+
printAgentList("skipped", summary.skippedFiles);
|
|
1467
|
+
}
|
|
1468
|
+
if (summary.missingFiles.length > 0) {
|
|
1469
|
+
console.log("");
|
|
1470
|
+
printAgentList("missing_from_graph", summary.missingFiles);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
function printAgentIntentDriftRepairPlan(plan) {
|
|
1474
|
+
console.log("RIPPLE_INTENT_DRIFT_REPAIR");
|
|
1475
|
+
if (plan.intentId) {
|
|
1476
|
+
console.log(`intent_id: ${plan.intentId}`);
|
|
1477
|
+
}
|
|
1478
|
+
console.log(`verdict: ${plan.verdict}`);
|
|
1479
|
+
console.log(`status: ${plan.status}`);
|
|
1480
|
+
console.log("");
|
|
1481
|
+
printAgentHandoffBlock("handoff", plan.handoff);
|
|
1482
|
+
console.log("");
|
|
1483
|
+
printAgentDriftVerdict(plan.driftVerdict);
|
|
1484
|
+
if (plan.boundaryVerdict) {
|
|
1485
|
+
console.log(`boundary_verdict: ${plan.boundaryVerdict.label}`);
|
|
1486
|
+
console.log(`boundary_decision: ${plan.boundaryVerdict.decision}`);
|
|
1487
|
+
console.log(`control_mode: ${plan.boundaryVerdict.controlMode}`);
|
|
1488
|
+
console.log(`human_required: ${plan.boundaryVerdict.humanRequired}`);
|
|
1489
|
+
}
|
|
1490
|
+
if (plan.policyExplanation) {
|
|
1491
|
+
printAgentPolicyExplanationBlock("saved_policy_explanation", plan.policyExplanation);
|
|
1492
|
+
}
|
|
1493
|
+
if (plan.policyDrift) {
|
|
1494
|
+
console.log("");
|
|
1495
|
+
printAgentPolicyDriftBlock("policy_drift", plan.policyDrift);
|
|
1496
|
+
}
|
|
1497
|
+
if (plan.readinessDrift) {
|
|
1498
|
+
console.log("");
|
|
1499
|
+
printAgentReadinessDriftBlock("readiness_drift", plan.readinessDrift);
|
|
1500
|
+
}
|
|
1501
|
+
console.log(`create_new_intent: ${plan.createNewIntent}`);
|
|
1502
|
+
console.log(`recommended_action: ${plan.recommendedAction}`);
|
|
1503
|
+
console.log(`summary: ${plan.summary}`);
|
|
1504
|
+
console.log("");
|
|
1505
|
+
printAgentList("blocking_reasons", plan.blockingReasons);
|
|
1506
|
+
console.log("");
|
|
1507
|
+
if (plan.boundaryVerdict) {
|
|
1508
|
+
printAgentList("boundary_why", plan.boundaryVerdict.why);
|
|
1509
|
+
console.log("");
|
|
1510
|
+
printAgentList("boundary_fix", plan.boundaryVerdict.fix);
|
|
1511
|
+
console.log("");
|
|
1512
|
+
printAgentList("changed_outside_boundary_files", plan.boundaryVerdict.changedOutsideBoundaryFiles);
|
|
1513
|
+
console.log("");
|
|
1514
|
+
printAgentList("changed_outside_boundary_symbols", plan.boundaryVerdict.changedOutsideBoundarySymbols);
|
|
1515
|
+
console.log("");
|
|
1516
|
+
}
|
|
1517
|
+
printAgentList("unstage_files", plan.unstageFiles);
|
|
1518
|
+
console.log("");
|
|
1519
|
+
printAgentList("review_contracts", plan.reviewContracts);
|
|
1520
|
+
console.log("");
|
|
1521
|
+
printAgentList("fix_actions", plan.fixActions.map(formatRepairActionForAgent));
|
|
1522
|
+
console.log("");
|
|
1523
|
+
printAgentList("trusted_findings", plan.agentActions.trustedFindings);
|
|
1524
|
+
console.log("");
|
|
1525
|
+
printAgentList("verify_before_commit", plan.agentActions.verifyBeforeCommit);
|
|
1526
|
+
console.log("");
|
|
1527
|
+
printAgentList("manual_review_required", plan.agentActions.manualReviewRequired);
|
|
1528
|
+
console.log("");
|
|
1529
|
+
printAgentList("commands_unstage", plan.commands.unstage);
|
|
1530
|
+
console.log("");
|
|
1531
|
+
printAgentList("commands_replan", plan.commands.replan);
|
|
1532
|
+
console.log("");
|
|
1533
|
+
printAgentList("verify", plan.verificationTargets);
|
|
1534
|
+
console.log("");
|
|
1535
|
+
printAgentList("next_steps", plan.nextSteps);
|
|
1536
|
+
}
|
|
1537
|
+
function formatRepairActionForAgent(action) {
|
|
1538
|
+
const target = action.target ? ` target=${action.target}` : "";
|
|
1539
|
+
const command = action.command ? ` command=${action.command}` : "";
|
|
1540
|
+
return `${action.priority} ${action.type}${target}${command} :: ${action.instruction} Reason: ${action.reason}`;
|
|
1541
|
+
}
|
|
1542
|
+
function printIntentDriftRepairPlan(plan) {
|
|
1543
|
+
console.log("Ripple intent drift repair");
|
|
1544
|
+
if (plan.intentId) {
|
|
1545
|
+
console.log(`Intent: ${plan.intentId}`);
|
|
1546
|
+
}
|
|
1547
|
+
console.log(`Verdict: ${plan.verdict}`);
|
|
1548
|
+
console.log(`Status: ${plan.status}`);
|
|
1549
|
+
console.log(`Drift verdict: ${plan.driftVerdict.label}`);
|
|
1550
|
+
console.log(`Drift decision: ${plan.driftVerdict.decision}`);
|
|
1551
|
+
console.log(`Drift summary: ${plan.driftVerdict.summary}`);
|
|
1552
|
+
if (plan.boundaryVerdict) {
|
|
1553
|
+
console.log(`Boundary verdict: ${plan.boundaryVerdict.label}`);
|
|
1554
|
+
console.log(`Boundary decision: ${plan.boundaryVerdict.decision}`);
|
|
1555
|
+
console.log(`Control mode: ${plan.boundaryVerdict.controlMode}`);
|
|
1556
|
+
console.log(`Human required: ${plan.boundaryVerdict.humanRequired}`);
|
|
1557
|
+
}
|
|
1558
|
+
if (plan.policyExplanation) {
|
|
1559
|
+
printPolicyExplanationSummary("Saved policy explanation:", plan.policyExplanation);
|
|
1560
|
+
}
|
|
1561
|
+
if (plan.policyDrift) {
|
|
1562
|
+
printPolicyDriftSummary("Policy drift:", plan.policyDrift);
|
|
1563
|
+
}
|
|
1564
|
+
if (plan.readinessDrift) {
|
|
1565
|
+
printReadinessDriftSummary("Readiness drift:", plan.readinessDrift);
|
|
1566
|
+
}
|
|
1567
|
+
console.log(`Create new intent: ${plan.createNewIntent}`);
|
|
1568
|
+
console.log(`Recommended action: ${plan.recommendedAction}`);
|
|
1569
|
+
console.log(`Summary: ${plan.summary}`);
|
|
1570
|
+
if (plan.blockingReasons.length > 0) {
|
|
1571
|
+
console.log("");
|
|
1572
|
+
console.log("Blocking reasons:");
|
|
1573
|
+
plan.blockingReasons.forEach((reason) => console.log(` - ${reason}`));
|
|
1574
|
+
}
|
|
1575
|
+
console.log("");
|
|
1576
|
+
console.log("Drift why:");
|
|
1577
|
+
if (plan.driftVerdict.why.length === 0) {
|
|
1578
|
+
console.log(" - none");
|
|
1579
|
+
}
|
|
1580
|
+
else {
|
|
1581
|
+
plan.driftVerdict.why.forEach((reason) => console.log(` - ${reason}`));
|
|
1582
|
+
}
|
|
1583
|
+
console.log("");
|
|
1584
|
+
console.log("Drift fix:");
|
|
1585
|
+
if (plan.driftVerdict.fix.length === 0) {
|
|
1586
|
+
console.log(" - none");
|
|
1587
|
+
}
|
|
1588
|
+
else {
|
|
1589
|
+
plan.driftVerdict.fix.forEach((fix) => console.log(` - ${fix}`));
|
|
1590
|
+
}
|
|
1591
|
+
if (plan.boundaryVerdict) {
|
|
1592
|
+
console.log("");
|
|
1593
|
+
console.log("Boundary why:");
|
|
1594
|
+
if (plan.boundaryVerdict.why.length === 0) {
|
|
1595
|
+
console.log(" - none");
|
|
1596
|
+
}
|
|
1597
|
+
else {
|
|
1598
|
+
plan.boundaryVerdict.why.forEach((reason) => console.log(` - ${reason}`));
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
console.log("");
|
|
1602
|
+
console.log("Unstage files:");
|
|
1603
|
+
if (plan.unstageFiles.length === 0) {
|
|
1604
|
+
console.log(" none");
|
|
1605
|
+
}
|
|
1606
|
+
else {
|
|
1607
|
+
plan.unstageFiles.forEach((file) => console.log(` - ${file}`));
|
|
1608
|
+
}
|
|
1609
|
+
if (plan.reviewContracts.length > 0) {
|
|
1610
|
+
console.log("");
|
|
1611
|
+
console.log("Review contracts:");
|
|
1612
|
+
plan.reviewContracts.forEach((contract) => console.log(` - ${contract}`));
|
|
1613
|
+
}
|
|
1614
|
+
if (plan.fixActions.length > 0) {
|
|
1615
|
+
console.log("");
|
|
1616
|
+
console.log("Fix actions:");
|
|
1617
|
+
plan.fixActions.slice(0, 16).forEach((action) => {
|
|
1618
|
+
const target = action.target ? ` ${action.target}` : "";
|
|
1619
|
+
const command = action.command ? ` (${action.command})` : "";
|
|
1620
|
+
console.log(` - [${action.priority}] ${action.type}${target}${command}: ${action.instruction}`);
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
console.log("");
|
|
1624
|
+
console.log("Agent actions:");
|
|
1625
|
+
console.log(" trusted findings:");
|
|
1626
|
+
if (plan.agentActions.trustedFindings.length === 0) {
|
|
1627
|
+
console.log(" - none");
|
|
1628
|
+
}
|
|
1629
|
+
else {
|
|
1630
|
+
plan.agentActions.trustedFindings.slice(0, 12).forEach((item) => {
|
|
1631
|
+
console.log(` - ${item}`);
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
console.log(" verify before commit:");
|
|
1635
|
+
if (plan.agentActions.verifyBeforeCommit.length === 0) {
|
|
1636
|
+
console.log(" - none");
|
|
1637
|
+
}
|
|
1638
|
+
else {
|
|
1639
|
+
plan.agentActions.verifyBeforeCommit.slice(0, 12).forEach((item) => {
|
|
1640
|
+
console.log(` - ${item}`);
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
console.log(" manual review required:");
|
|
1644
|
+
if (plan.agentActions.manualReviewRequired.length === 0) {
|
|
1645
|
+
console.log(" - none");
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
plan.agentActions.manualReviewRequired.slice(0, 12).forEach((item) => {
|
|
1649
|
+
console.log(` - ${item}`);
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
if (plan.commands.unstage.length > 0 || plan.commands.replan.length > 0) {
|
|
1653
|
+
console.log("");
|
|
1654
|
+
console.log("Commands:");
|
|
1655
|
+
[...plan.commands.unstage, ...plan.commands.replan].forEach((command) => {
|
|
1656
|
+
console.log(` - ${command}`);
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
if (plan.verificationTargets.length > 0) {
|
|
1660
|
+
console.log("");
|
|
1661
|
+
console.log("Verify:");
|
|
1662
|
+
plan.verificationTargets.slice(0, 16).forEach((target) => console.log(` - ${target}`));
|
|
1663
|
+
}
|
|
1664
|
+
if (plan.nextSteps.length > 0) {
|
|
1665
|
+
console.log("");
|
|
1666
|
+
console.log("Next steps:");
|
|
1667
|
+
plan.nextSteps.forEach((step) => console.log(` - ${step}`));
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
function printAgentAuditSummary(summary) {
|
|
1671
|
+
const validation = summary.stagedCheck.intentValidation;
|
|
1672
|
+
console.log("RIPPLE_AUDIT");
|
|
1673
|
+
console.log(`status: ${summary.status}`);
|
|
1674
|
+
console.log(`decision: ${summary.decision}`);
|
|
1675
|
+
console.log(`can_proceed: ${summary.canProceed}`);
|
|
1676
|
+
console.log(`next_required_phase: ${summary.nextRequiredPhase}`);
|
|
1677
|
+
console.log(`next_required_action: ${summary.nextRequiredAction}`);
|
|
1678
|
+
console.log(`approval_status: ${summary.approvalStatus.status}`);
|
|
1679
|
+
console.log(`approval_decision: ${summary.approvalStatus.decision}`);
|
|
1680
|
+
console.log(`approval_required: ${summary.approvalStatus.required}`);
|
|
1681
|
+
console.log(`approval_approved: ${summary.approvalStatus.approved}`);
|
|
1682
|
+
if (summary.approvalStatus.gate) {
|
|
1683
|
+
console.log(`approval_gate: ${summary.approvalStatus.gate}`);
|
|
1684
|
+
}
|
|
1685
|
+
console.log(`mode: ${summary.mode}`);
|
|
1686
|
+
if (summary.baseRef) {
|
|
1687
|
+
console.log(`base_ref: ${summary.baseRef}`);
|
|
1688
|
+
}
|
|
1689
|
+
console.log(`intent_id: ${summary.intent.id}`);
|
|
1690
|
+
console.log(`task: ${summary.intent.task}`);
|
|
1691
|
+
console.log(`target: ${summary.intent.targetFile}`);
|
|
1692
|
+
console.log(`control_mode: ${summary.intent.controlMode}`);
|
|
1693
|
+
console.log(`human_gate: ${summary.intent.humanGate}`);
|
|
1694
|
+
console.log(`boundary_risk: ${summary.intent.boundaryRisk}`);
|
|
1695
|
+
console.log(`drift_verdict: ${validation?.driftVerdict.label ?? "UNKNOWN"}`);
|
|
1696
|
+
console.log(`boundary_verdict: ${validation?.boundaryVerdict.label ?? "UNKNOWN"}`);
|
|
1697
|
+
console.log(`policy_drift: ${validation?.policyDrift.label ?? "UNKNOWN"}`);
|
|
1698
|
+
console.log(`readiness_drift: ${validation?.readinessDrift.label ?? "UNKNOWN"}`);
|
|
1699
|
+
console.log(`repair_status: ${summary.repairPlan.status}`);
|
|
1700
|
+
console.log(`recommended_action: ${summary.recommendedAction}`);
|
|
1701
|
+
console.log("");
|
|
1702
|
+
printAgentHandoffBlock("handoff", summary.handoff);
|
|
1703
|
+
if (summary.approvalStatus.approval) {
|
|
1704
|
+
console.log(`approved_by: ${summary.approvalStatus.approval.approvedBy}`);
|
|
1705
|
+
console.log(`approved_at: ${summary.approvalStatus.approval.approvedAt}`);
|
|
1706
|
+
}
|
|
1707
|
+
console.log("");
|
|
1708
|
+
printAgentList("approval_why", summary.approvalStatus.why);
|
|
1709
|
+
console.log("");
|
|
1710
|
+
printAgentPolicyExplanationBlock("saved_policy_explanation", summary.savedPolicyExplanation);
|
|
1711
|
+
console.log("");
|
|
1712
|
+
if (summary.currentPolicyExplanation) {
|
|
1713
|
+
printAgentPolicyExplanationBlock("current_policy_explanation", summary.currentPolicyExplanation);
|
|
1714
|
+
console.log("");
|
|
1715
|
+
}
|
|
1716
|
+
if (validation) {
|
|
1717
|
+
printAgentPolicyDriftBlock("policy_drift_detail", validation.policyDrift);
|
|
1718
|
+
console.log("");
|
|
1719
|
+
printAgentReadinessDriftBlock("readiness_drift_detail", validation.readinessDrift);
|
|
1720
|
+
console.log("");
|
|
1721
|
+
}
|
|
1722
|
+
printAgentList("blocking_reasons", summary.blockingReasons);
|
|
1723
|
+
console.log("");
|
|
1724
|
+
printAgentList("next_steps", summary.nextSteps);
|
|
1725
|
+
console.log("");
|
|
1726
|
+
printAgentList("changed_files", summary.changedFiles);
|
|
1727
|
+
console.log("");
|
|
1728
|
+
printAgentList("verify", summary.verificationTargets);
|
|
1729
|
+
console.log("");
|
|
1730
|
+
printAgentList("fix_actions", summary.repairPlan.fixActions.map(formatRepairActionForAgent));
|
|
1731
|
+
}
|
|
1732
|
+
function printAgentGateSummary(summary) {
|
|
1733
|
+
console.log("RIPPLE_GATE");
|
|
1734
|
+
console.log(`status: ${summary.status}`);
|
|
1735
|
+
console.log(`decision: ${summary.decision}`);
|
|
1736
|
+
console.log(`can_continue: ${summary.canContinue}`);
|
|
1737
|
+
console.log(`must_stop: ${summary.mustStop}`);
|
|
1738
|
+
console.log(`needs_human: ${summary.needsHuman}`);
|
|
1739
|
+
console.log(`next_required_phase: ${summary.nextRequiredPhase}`);
|
|
1740
|
+
console.log(`next_required_action: ${summary.nextRequiredAction}`);
|
|
1741
|
+
console.log(`summary: ${summary.summary}`);
|
|
1742
|
+
console.log(`audit_status: ${summary.auditStatus}`);
|
|
1743
|
+
console.log(`audit_decision: ${summary.auditDecision}`);
|
|
1744
|
+
console.log(`approval_status: ${summary.approvalStatus}`);
|
|
1745
|
+
console.log(`mode: ${summary.mode}`);
|
|
1746
|
+
if (summary.baseRef) {
|
|
1747
|
+
console.log(`base_ref: ${summary.baseRef}`);
|
|
1748
|
+
}
|
|
1749
|
+
console.log(`intent_id: ${summary.intent.id}`);
|
|
1750
|
+
console.log(`task: ${summary.intent.task}`);
|
|
1751
|
+
console.log(`target: ${summary.intent.targetFile}`);
|
|
1752
|
+
console.log(`control_mode: ${summary.intent.controlMode}`);
|
|
1753
|
+
console.log(`human_gate: ${summary.intent.humanGate}`);
|
|
1754
|
+
console.log(`boundary_risk: ${summary.intent.boundaryRisk}`);
|
|
1755
|
+
console.log("");
|
|
1756
|
+
printAgentList("why", summary.why);
|
|
1757
|
+
console.log("");
|
|
1758
|
+
printAgentList("fix_now", summary.fixNow);
|
|
1759
|
+
console.log("");
|
|
1760
|
+
printAgentList("ask_human", summary.askHuman);
|
|
1761
|
+
console.log("");
|
|
1762
|
+
printAgentList("commands_doctor", summary.commands.doctor);
|
|
1763
|
+
console.log("");
|
|
1764
|
+
printAgentList("commands_plan", summary.commands.plan);
|
|
1765
|
+
console.log("");
|
|
1766
|
+
printAgentList("commands_check", summary.commands.check);
|
|
1767
|
+
console.log("");
|
|
1768
|
+
printAgentList("commands_audit", summary.commands.audit);
|
|
1769
|
+
console.log("");
|
|
1770
|
+
printAgentList("commands_repair", summary.commands.repair);
|
|
1771
|
+
console.log("");
|
|
1772
|
+
printAgentList("commands_approve", summary.commands.approve);
|
|
1773
|
+
console.log("");
|
|
1774
|
+
printAgentList("commands_unstage", summary.commands.unstage);
|
|
1775
|
+
console.log("");
|
|
1776
|
+
printAgentList("commands_verify", summary.commands.verify);
|
|
1777
|
+
}
|
|
1778
|
+
function printGateSummary(summary) {
|
|
1779
|
+
console.log("Ripple gate");
|
|
1780
|
+
console.log(`Status: ${summary.status}`);
|
|
1781
|
+
console.log(`Decision: ${summary.decision}`);
|
|
1782
|
+
console.log(`Can continue: ${summary.canContinue}`);
|
|
1783
|
+
console.log(`Must stop: ${summary.mustStop}`);
|
|
1784
|
+
console.log(`Needs human: ${summary.needsHuman}`);
|
|
1785
|
+
console.log(`Next required phase: ${summary.nextRequiredPhase}`);
|
|
1786
|
+
console.log(`Next required action: ${summary.nextRequiredAction}`);
|
|
1787
|
+
console.log(`Summary: ${summary.summary}`);
|
|
1788
|
+
console.log("");
|
|
1789
|
+
console.log("Intent:");
|
|
1790
|
+
console.log(` id: ${summary.intent.id}`);
|
|
1791
|
+
console.log(` task: ${summary.intent.task}`);
|
|
1792
|
+
console.log(` target: ${summary.intent.targetFile}`);
|
|
1793
|
+
console.log(` control mode: ${summary.intent.controlMode}`);
|
|
1794
|
+
console.log(` human gate: ${summary.intent.humanGate}`);
|
|
1795
|
+
console.log(` boundary risk: ${summary.intent.boundaryRisk}`);
|
|
1796
|
+
console.log("");
|
|
1797
|
+
console.log("Audit:");
|
|
1798
|
+
console.log(` status: ${summary.auditStatus}`);
|
|
1799
|
+
console.log(` decision: ${summary.auditDecision}`);
|
|
1800
|
+
console.log(` approval status: ${summary.approvalStatus}`);
|
|
1801
|
+
console.log("");
|
|
1802
|
+
printHumanList("Why:", summary.why);
|
|
1803
|
+
printHumanList("Fix now:", summary.fixNow);
|
|
1804
|
+
printHumanList("Ask human:", summary.askHuman);
|
|
1805
|
+
printHumanList("Changed files:", summary.changedFiles);
|
|
1806
|
+
printHumanList("Verify:", summary.verificationTargets);
|
|
1807
|
+
const commands = [
|
|
1808
|
+
...summary.commands.doctor,
|
|
1809
|
+
...summary.commands.check,
|
|
1810
|
+
...summary.commands.audit,
|
|
1811
|
+
...summary.commands.repair,
|
|
1812
|
+
...summary.commands.approve,
|
|
1813
|
+
...summary.commands.unstage,
|
|
1814
|
+
];
|
|
1815
|
+
printHumanList("Commands:", commands);
|
|
1816
|
+
}
|
|
1817
|
+
function printAuditSummary(summary) {
|
|
1818
|
+
const validation = summary.stagedCheck.intentValidation;
|
|
1819
|
+
const gate = (0, core_1.buildRippleGateSummary)(summary);
|
|
1820
|
+
console.log("Ripple audit");
|
|
1821
|
+
console.log(`Workspace: ${summary.workspace}`);
|
|
1822
|
+
console.log(`Mode: ${summary.mode}`);
|
|
1823
|
+
if (summary.baseRef) {
|
|
1824
|
+
console.log(`Base ref: ${summary.baseRef}`);
|
|
1825
|
+
}
|
|
1826
|
+
console.log(`Status: ${summary.status}`);
|
|
1827
|
+
console.log(`Decision: ${summary.decision}`);
|
|
1828
|
+
console.log(`Can proceed: ${summary.canProceed}`);
|
|
1829
|
+
console.log(`Next required phase: ${summary.nextRequiredPhase}`);
|
|
1830
|
+
console.log(`Next required action: ${summary.nextRequiredAction}`);
|
|
1831
|
+
console.log(`Recommended action: ${summary.recommendedAction}`);
|
|
1832
|
+
console.log("");
|
|
1833
|
+
console.log("Gate:");
|
|
1834
|
+
console.log(` status: ${gate.status}`);
|
|
1835
|
+
console.log(` decision: ${gate.decision}`);
|
|
1836
|
+
console.log(` can continue: ${gate.canContinue}`);
|
|
1837
|
+
console.log(` must stop: ${gate.mustStop}`);
|
|
1838
|
+
console.log(` needs human: ${gate.needsHuman}`);
|
|
1839
|
+
console.log(` next required phase: ${gate.nextRequiredPhase}`);
|
|
1840
|
+
console.log(` next required action: ${gate.nextRequiredAction}`);
|
|
1841
|
+
console.log(` summary: ${gate.summary}`);
|
|
1842
|
+
console.log("");
|
|
1843
|
+
console.log("Approval:");
|
|
1844
|
+
console.log(` status: ${summary.approvalStatus.status}`);
|
|
1845
|
+
console.log(` decision: ${summary.approvalStatus.decision}`);
|
|
1846
|
+
console.log(` required: ${summary.approvalStatus.required}`);
|
|
1847
|
+
console.log(` approved: ${summary.approvalStatus.approved}`);
|
|
1848
|
+
if (summary.approvalStatus.gate) {
|
|
1849
|
+
console.log(` gate: ${summary.approvalStatus.gate}`);
|
|
1850
|
+
}
|
|
1851
|
+
if (summary.approvalStatus.approval) {
|
|
1852
|
+
console.log(` approved by: ${summary.approvalStatus.approval.approvedBy}`);
|
|
1853
|
+
console.log(` approved at: ${summary.approvalStatus.approval.approvedAt}`);
|
|
1854
|
+
}
|
|
1855
|
+
console.log(` summary: ${summary.approvalStatus.summary}`);
|
|
1856
|
+
console.log("");
|
|
1857
|
+
console.log("Intent:");
|
|
1858
|
+
console.log(` id: ${summary.intent.id}`);
|
|
1859
|
+
console.log(` task: ${summary.intent.task}`);
|
|
1860
|
+
console.log(` target: ${summary.intent.targetFile}`);
|
|
1861
|
+
console.log(` control mode: ${summary.intent.controlMode}`);
|
|
1862
|
+
console.log(` human gate: ${summary.intent.humanGate}`);
|
|
1863
|
+
console.log(` boundary risk: ${summary.intent.boundaryRisk}`);
|
|
1864
|
+
console.log("");
|
|
1865
|
+
console.log("Verdicts:");
|
|
1866
|
+
console.log(` drift: ${validation?.driftVerdict.label ?? "UNKNOWN"}`);
|
|
1867
|
+
console.log(` boundary: ${validation?.boundaryVerdict.label ?? "UNKNOWN"}`);
|
|
1868
|
+
console.log(` policy drift: ${validation?.policyDrift.label ?? "UNKNOWN"}`);
|
|
1869
|
+
console.log(` readiness drift: ${validation?.readinessDrift.label ?? "UNKNOWN"}`);
|
|
1870
|
+
console.log(` repair status: ${summary.repairPlan.status}`);
|
|
1871
|
+
console.log("");
|
|
1872
|
+
printPolicyExplanationSummary("Saved policy explanation:", summary.savedPolicyExplanation);
|
|
1873
|
+
if (summary.currentPolicyExplanation) {
|
|
1874
|
+
printPolicyExplanationSummary("Current policy explanation:", summary.currentPolicyExplanation);
|
|
1875
|
+
}
|
|
1876
|
+
if (validation) {
|
|
1877
|
+
printPolicyDriftSummary("Policy drift:", validation.policyDrift);
|
|
1878
|
+
printReadinessDriftSummary("Readiness drift:", validation.readinessDrift);
|
|
1879
|
+
}
|
|
1880
|
+
console.log("");
|
|
1881
|
+
console.log("Blocking reasons:");
|
|
1882
|
+
if (summary.blockingReasons.length === 0) {
|
|
1883
|
+
console.log(" - none");
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
summary.blockingReasons.forEach((reason) => console.log(` - ${reason}`));
|
|
1887
|
+
}
|
|
1888
|
+
console.log("");
|
|
1889
|
+
console.log("Changed files:");
|
|
1890
|
+
if (summary.changedFiles.length === 0) {
|
|
1891
|
+
console.log(" - none");
|
|
1892
|
+
}
|
|
1893
|
+
else {
|
|
1894
|
+
summary.changedFiles.forEach((file) => console.log(` - ${file}`));
|
|
1895
|
+
}
|
|
1896
|
+
if (summary.verificationTargets.length > 0) {
|
|
1897
|
+
console.log("");
|
|
1898
|
+
console.log("Verify:");
|
|
1899
|
+
summary.verificationTargets.slice(0, 16).forEach((target) => console.log(` - ${target}`));
|
|
1900
|
+
}
|
|
1901
|
+
if (summary.nextSteps.length > 0) {
|
|
1902
|
+
console.log("");
|
|
1903
|
+
console.log("Next steps:");
|
|
1904
|
+
summary.nextSteps.forEach((step) => console.log(` - ${step}`));
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
function printAgentApprovalRecord(approval) {
|
|
1908
|
+
console.log("RIPPLE_APPROVAL");
|
|
1909
|
+
console.log(`approval_id: ${approval.id}`);
|
|
1910
|
+
console.log(`intent_id: ${approval.intentId}`);
|
|
1911
|
+
console.log(`gate: ${approval.gate}`);
|
|
1912
|
+
console.log(`target: ${approval.targetFile}`);
|
|
1913
|
+
console.log(`control_mode: ${approval.controlMode}`);
|
|
1914
|
+
console.log(`human_gate: ${approval.humanGate}`);
|
|
1915
|
+
console.log(`boundary_risk: ${approval.boundaryRisk}`);
|
|
1916
|
+
console.log(`approved_by: ${approval.approvedBy}`);
|
|
1917
|
+
console.log(`approved_at: ${approval.approvedAt}`);
|
|
1918
|
+
if (approval.reason) {
|
|
1919
|
+
console.log(`reason: ${approval.reason}`);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
function printApprovalRecord(approval) {
|
|
1923
|
+
console.log("Ripple approval recorded");
|
|
1924
|
+
console.log(`Approval id: ${approval.id}`);
|
|
1925
|
+
console.log(`Intent id: ${approval.intentId}`);
|
|
1926
|
+
console.log(`Gate: ${approval.gate}`);
|
|
1927
|
+
console.log(`Target: ${approval.targetFile}`);
|
|
1928
|
+
console.log(`Control mode: ${approval.controlMode}`);
|
|
1929
|
+
console.log(`Human gate: ${approval.humanGate}`);
|
|
1930
|
+
console.log(`Boundary risk: ${approval.boundaryRisk}`);
|
|
1931
|
+
console.log(`Approved by: ${approval.approvedBy}`);
|
|
1932
|
+
console.log(`Approved at: ${approval.approvedAt}`);
|
|
1933
|
+
if (approval.reason) {
|
|
1934
|
+
console.log(`Reason: ${approval.reason}`);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
function printAgentApprovalStatus(status) {
|
|
1938
|
+
console.log("RIPPLE_APPROVAL_STATUS");
|
|
1939
|
+
console.log(`status: ${status.status}`);
|
|
1940
|
+
console.log(`decision: ${status.decision}`);
|
|
1941
|
+
console.log(`required: ${status.required}`);
|
|
1942
|
+
console.log(`approved: ${status.approved}`);
|
|
1943
|
+
if (status.gate) {
|
|
1944
|
+
console.log(`gate: ${status.gate}`);
|
|
1945
|
+
}
|
|
1946
|
+
console.log(`intent_id: ${status.intent.id}`);
|
|
1947
|
+
console.log(`task: ${status.intent.task}`);
|
|
1948
|
+
console.log(`target: ${status.intent.targetFile}`);
|
|
1949
|
+
console.log(`control_mode: ${status.intent.controlMode}`);
|
|
1950
|
+
console.log(`human_gate: ${status.intent.humanGate}`);
|
|
1951
|
+
console.log(`boundary_risk: ${status.intent.boundaryRisk}`);
|
|
1952
|
+
if (status.approvalPath) {
|
|
1953
|
+
console.log(`approval_path: ${status.approvalPath}`);
|
|
1954
|
+
}
|
|
1955
|
+
if (status.approval) {
|
|
1956
|
+
console.log(`approved_by: ${status.approval.approvedBy}`);
|
|
1957
|
+
console.log(`approved_at: ${status.approval.approvedAt}`);
|
|
1958
|
+
}
|
|
1959
|
+
console.log(`summary: ${status.summary}`);
|
|
1960
|
+
console.log("");
|
|
1961
|
+
printAgentList("why", status.why);
|
|
1962
|
+
console.log("");
|
|
1963
|
+
printAgentList("next_steps", status.nextSteps);
|
|
1964
|
+
}
|
|
1965
|
+
function printApprovalStatus(status) {
|
|
1966
|
+
console.log("Ripple approval status");
|
|
1967
|
+
console.log(`Status: ${status.status}`);
|
|
1968
|
+
console.log(`Decision: ${status.decision}`);
|
|
1969
|
+
console.log(`Required: ${status.required}`);
|
|
1970
|
+
console.log(`Approved: ${status.approved}`);
|
|
1971
|
+
if (status.gate) {
|
|
1972
|
+
console.log(`Gate: ${status.gate}`);
|
|
1973
|
+
}
|
|
1974
|
+
console.log("");
|
|
1975
|
+
console.log("Intent:");
|
|
1976
|
+
console.log(` id: ${status.intent.id}`);
|
|
1977
|
+
console.log(` task: ${status.intent.task}`);
|
|
1978
|
+
console.log(` target: ${status.intent.targetFile}`);
|
|
1979
|
+
console.log(` control mode: ${status.intent.controlMode}`);
|
|
1980
|
+
console.log(` human gate: ${status.intent.humanGate}`);
|
|
1981
|
+
console.log(` boundary risk: ${status.intent.boundaryRisk}`);
|
|
1982
|
+
if (status.approvalPath) {
|
|
1983
|
+
console.log(`Approval path: ${status.approvalPath}`);
|
|
1984
|
+
}
|
|
1985
|
+
if (status.approval) {
|
|
1986
|
+
console.log(`Approved by: ${status.approval.approvedBy}`);
|
|
1987
|
+
console.log(`Approved at: ${status.approval.approvedAt}`);
|
|
1988
|
+
}
|
|
1989
|
+
console.log(`Summary: ${status.summary}`);
|
|
1990
|
+
printHumanList("Why:", status.why);
|
|
1991
|
+
printHumanList("Next steps:", status.nextSteps);
|
|
1992
|
+
}
|
|
1993
|
+
function printHumanList(title, items) {
|
|
1994
|
+
console.log(title);
|
|
1995
|
+
if (items.length === 0) {
|
|
1996
|
+
console.log(" - none");
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
items.forEach((item) => console.log(` - ${item}`));
|
|
2000
|
+
}
|
|
2001
|
+
function printStagedCheckSummary(summary) {
|
|
2002
|
+
const adapter = summary.adapterSupport.primaryAdapter;
|
|
2003
|
+
console.log(summary.mode === "changed" ? "Ripple changed-files check" : "Ripple staged check");
|
|
2004
|
+
console.log(`Workspace: ${summary.workspace}`);
|
|
2005
|
+
console.log(`Mode: ${summary.mode}`);
|
|
2006
|
+
if (summary.baseRef) {
|
|
2007
|
+
console.log(`Base ref: ${summary.baseRef}`);
|
|
2008
|
+
}
|
|
2009
|
+
console.log(`Checked JS/TS files: ${summary.stagedFiles}`);
|
|
2010
|
+
console.log(`Checked files: ${summary.checkedFiles}`);
|
|
2011
|
+
console.log(`Highest risk: ${summary.highestRisk}`);
|
|
2012
|
+
console.log(`Adapter: ${adapter.capabilities.displayName} (${adapter.supportLevel}, ${Math.round(adapter.confidence * 100)}%)`);
|
|
2013
|
+
if (summary.intentValidation) {
|
|
2014
|
+
console.log(`Intent: ${summary.intentValidation.intentId}`);
|
|
2015
|
+
console.log(`Intent verdict: ${summary.intentValidation.verdict}`);
|
|
2016
|
+
console.log(`Drift verdict: ${summary.intentValidation.driftVerdict.label}`);
|
|
2017
|
+
console.log(`Drift decision: ${summary.intentValidation.driftVerdict.decision}`);
|
|
2018
|
+
console.log(`Control mode: ${summary.intentValidation.controlMode}`);
|
|
2019
|
+
console.log(`Boundary verdict: ${summary.intentValidation.boundaryVerdict.label}`);
|
|
2020
|
+
console.log(`Boundary decision: ${summary.intentValidation.boundaryVerdict.decision}`);
|
|
2021
|
+
console.log(`Human required: ${summary.intentValidation.boundaryVerdict.humanRequired}`);
|
|
2022
|
+
printPolicyExplanationSummary("Saved policy explanation:", summary.intentValidation.policyExplanation);
|
|
2023
|
+
printPolicyDriftSummary("Policy drift:", summary.intentValidation.policyDrift);
|
|
2024
|
+
printReadinessDriftSummary("Readiness drift:", summary.intentValidation.readinessDrift);
|
|
2025
|
+
console.log(`Planned scope: ${summary.intentValidation.plannedScope}`);
|
|
2026
|
+
}
|
|
2027
|
+
if (summary.skippedFiles.length > 0) {
|
|
2028
|
+
console.log(`Skipped non-source files: ${summary.skippedFiles.length}`);
|
|
2029
|
+
}
|
|
2030
|
+
if (summary.missingFiles.length > 0) {
|
|
2031
|
+
console.log("");
|
|
2032
|
+
console.log("Missing from graph:");
|
|
2033
|
+
summary.missingFiles.forEach((file) => console.log(` - ${file}`));
|
|
2034
|
+
}
|
|
2035
|
+
if (summary.files.length === 0) {
|
|
2036
|
+
console.log("");
|
|
2037
|
+
console.log(summary.mode === "changed"
|
|
2038
|
+
? "No changed JS/TS files found."
|
|
2039
|
+
: "No staged JS/TS files found.");
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
console.log("");
|
|
2043
|
+
console.log("Agent actions:");
|
|
2044
|
+
console.log(" trusted findings:");
|
|
2045
|
+
if (summary.agentActions.trustedFindings.length === 0) {
|
|
2046
|
+
console.log(" - none");
|
|
2047
|
+
}
|
|
2048
|
+
else {
|
|
2049
|
+
summary.agentActions.trustedFindings.slice(0, 12).forEach((item) => {
|
|
2050
|
+
console.log(` - ${item}`);
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
console.log(" verify before commit:");
|
|
2054
|
+
if (summary.agentActions.verifyBeforeCommit.length === 0) {
|
|
2055
|
+
console.log(" - none");
|
|
2056
|
+
}
|
|
2057
|
+
else {
|
|
2058
|
+
summary.agentActions.verifyBeforeCommit.slice(0, 12).forEach((item) => {
|
|
2059
|
+
console.log(` - ${item}`);
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
console.log(" manual review required:");
|
|
2063
|
+
if (summary.agentActions.manualReviewRequired.length === 0) {
|
|
2064
|
+
console.log(" - none");
|
|
2065
|
+
}
|
|
2066
|
+
else {
|
|
2067
|
+
summary.agentActions.manualReviewRequired.slice(0, 12).forEach((item) => {
|
|
2068
|
+
console.log(` - ${item}`);
|
|
2069
|
+
});
|
|
2070
|
+
}
|
|
2071
|
+
console.log("");
|
|
2072
|
+
console.log("Files:");
|
|
2073
|
+
summary.files.forEach((file) => {
|
|
2074
|
+
console.log(` - ${file.file} [${file.modificationRisk}, importers: ${file.importerCount}, symbols: ${file.symbolCount}]`);
|
|
2075
|
+
console.log(` ${file.focus}`);
|
|
2076
|
+
if (file.adapterSignals.length > 0) {
|
|
2077
|
+
console.log(` adapter signals: ${formatAdapterSignalInline(file.adapterSignals)}`);
|
|
2078
|
+
}
|
|
2079
|
+
if (file.readFirst.length > 0) {
|
|
2080
|
+
console.log(` read first: ${file.readFirst.join(", ")}`);
|
|
2081
|
+
}
|
|
2082
|
+
if (file.changedSymbols.length > 0) {
|
|
2083
|
+
console.log(` changed symbols: ${file.changedSymbols.map((symbol) => `${symbol.symbol} [${symbol.symbolStatus}, ${symbol.changeKind}, signature_changed: ${symbol.signatureChanged}, adapter: ${formatAdapterSignalInline(symbol.adapterSignals)}]`).join(", ")}`);
|
|
2084
|
+
}
|
|
2085
|
+
if (file.contractRisks.length > 0) {
|
|
2086
|
+
console.log(` contract risk: ${file.contractRisks.map((risk) => `${risk.symbol} [${risk.risk}, adapter: ${formatAdapterSignalInline(risk.adapterSignals)}]`).join(", ")}`);
|
|
2087
|
+
}
|
|
2088
|
+
if (file.verificationTargets.length > 0) {
|
|
2089
|
+
console.log(` verify: ${file.verificationTargets.join(", ")}`);
|
|
2090
|
+
}
|
|
2091
|
+
});
|
|
2092
|
+
if (summary.intentValidation) {
|
|
2093
|
+
console.log("");
|
|
2094
|
+
console.log("Intent validation:");
|
|
2095
|
+
summary.intentValidation.reasons.forEach((reason) => console.log(` - ${reason}`));
|
|
2096
|
+
console.log(` recommended action: ${summary.intentValidation.recommendedAction}`);
|
|
2097
|
+
console.log(` drift summary: ${summary.intentValidation.driftVerdict.summary}`);
|
|
2098
|
+
console.log(" drift why:");
|
|
2099
|
+
summary.intentValidation.driftVerdict.why.forEach((reason) => {
|
|
2100
|
+
console.log(` - ${reason}`);
|
|
2101
|
+
});
|
|
2102
|
+
console.log(" drift fix:");
|
|
2103
|
+
summary.intentValidation.driftVerdict.fix.forEach((fix) => {
|
|
2104
|
+
console.log(` - ${fix}`);
|
|
2105
|
+
});
|
|
2106
|
+
if (summary.intentValidation.blockingReasons.length > 0) {
|
|
2107
|
+
console.log(" blocking reasons:");
|
|
2108
|
+
summary.intentValidation.blockingReasons.forEach((reason) => {
|
|
2109
|
+
console.log(` - ${reason}`);
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
if (summary.intentValidation.nextSteps.length > 0) {
|
|
2113
|
+
console.log(" next steps:");
|
|
2114
|
+
summary.intentValidation.nextSteps.forEach((step) => {
|
|
2115
|
+
console.log(` - ${step}`);
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
if (summary.intentValidation.unplannedFiles.length > 0) {
|
|
2119
|
+
console.log(` unplanned files: ${summary.intentValidation.unplannedFiles.join(", ")}`);
|
|
2120
|
+
}
|
|
2121
|
+
if (summary.intentValidation.contextFilesChanged.length > 0) {
|
|
2122
|
+
console.log(` context-only files changed: ${summary.intentValidation.contextFilesChanged.join(", ")}`);
|
|
2123
|
+
}
|
|
2124
|
+
if (summary.intentValidation.unplannedSymbols.length > 0) {
|
|
2125
|
+
console.log(` unplanned symbols: ${summary.intentValidation.unplannedSymbols.join(", ")}`);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
function printFocusSummary(summary) {
|
|
2130
|
+
console.log("Ripple focus");
|
|
2131
|
+
console.log(`File: ${summary.projectPath}`);
|
|
2132
|
+
console.log(`Risk: ${summary.modificationRisk}`);
|
|
2133
|
+
console.log(`Imports: ${summary.imports.length}`);
|
|
2134
|
+
console.log(`Imported by: ${summary.importedBy.length}`);
|
|
2135
|
+
console.log(`Symbols: ${summary.symbols.length}`);
|
|
2136
|
+
console.log(`Focus file: ${summary.focusPath}`);
|
|
2137
|
+
if (summary.importedBy.length > 0) {
|
|
2138
|
+
console.log("");
|
|
2139
|
+
console.log("Imported by:");
|
|
2140
|
+
summary.importedBy.slice(0, 10).forEach((item) => {
|
|
2141
|
+
console.log(` - ${item.file} [${item.modificationRisk}]`);
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
if (summary.symbols.length > 0) {
|
|
2145
|
+
console.log("");
|
|
2146
|
+
console.log("Symbols:");
|
|
2147
|
+
summary.symbols.slice(0, 10).forEach((symbol) => {
|
|
2148
|
+
console.log(` - ${symbol.name} (${symbol.kind}, ${symbol.layer}, callers: ${symbol.callerCount})`);
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
function printDependencyItems(items) {
|
|
2153
|
+
items.slice(0, 25).forEach((item) => {
|
|
2154
|
+
console.log(` - ${item.file} [${item.modificationRisk}, imports: ${item.importCount}, importers: ${item.importerCount}]`);
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
function printDependencySummary(summary, direction) {
|
|
2158
|
+
const items = direction === "imports" ? summary.imports : summary.importers;
|
|
2159
|
+
const title = direction === "imports" ? "Ripple imports" : "Ripple importers";
|
|
2160
|
+
console.log(title);
|
|
2161
|
+
console.log(`File: ${summary.projectPath}`);
|
|
2162
|
+
console.log(`Risk: ${summary.modificationRisk}`);
|
|
2163
|
+
console.log(`${direction === "imports" ? "Imports" : "Importers"}: ${items.length}`);
|
|
2164
|
+
if (items.length === 0) {
|
|
2165
|
+
console.log("");
|
|
2166
|
+
console.log(`No ${direction} found.`);
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
console.log("");
|
|
2170
|
+
console.log(direction === "imports" ? "Imports:" : "Importers:");
|
|
2171
|
+
printDependencyItems(items);
|
|
2172
|
+
}
|
|
2173
|
+
function printSymbolLink(symbol) {
|
|
2174
|
+
console.log(` - ${symbol.projectSymbolId} (${symbol.kind}, ${symbol.layer}, callers: ${symbol.callerCount}, calls: ${symbol.callCount})`);
|
|
2175
|
+
}
|
|
2176
|
+
function printSymbolSummary(symbol) {
|
|
2177
|
+
printSymbolLink(symbol);
|
|
2178
|
+
if (symbol.calledBy.length > 0) {
|
|
2179
|
+
console.log(` called by: ${symbol.calledBy.slice(0, 5).map((caller) => caller.projectSymbolId).join(", ")}`);
|
|
2180
|
+
}
|
|
2181
|
+
if (symbol.calls.length > 0) {
|
|
2182
|
+
console.log(` calls: ${symbol.calls.slice(0, 5).map((call) => call.projectSymbolId).join(", ")}`);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
function printFileSymbolsSummary(summary) {
|
|
2186
|
+
console.log("Ripple symbols");
|
|
2187
|
+
console.log(`File: ${summary.projectPath}`);
|
|
2188
|
+
console.log(`Risk: ${summary.modificationRisk}`);
|
|
2189
|
+
console.log(`Symbols: ${summary.symbols.length}`);
|
|
2190
|
+
if (summary.symbols.length === 0) {
|
|
2191
|
+
console.log("");
|
|
2192
|
+
console.log("No symbols found.");
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
console.log("");
|
|
2196
|
+
console.log("Symbols:");
|
|
2197
|
+
summary.symbols.slice(0, 25).forEach(printSymbolSummary);
|
|
2198
|
+
}
|
|
2199
|
+
function printSymbolCallersSummary(summary) {
|
|
2200
|
+
console.log("Ripple callers");
|
|
2201
|
+
console.log(`Symbol: ${summary.symbol.projectSymbolId}`);
|
|
2202
|
+
console.log(`Kind: ${summary.symbol.kind}`);
|
|
2203
|
+
console.log(`Layer: ${summary.symbol.layer}`);
|
|
2204
|
+
console.log(`Callers: ${summary.callerCount}`);
|
|
2205
|
+
if (summary.callers.length === 0) {
|
|
2206
|
+
console.log("");
|
|
2207
|
+
console.log("No callers found.");
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
2210
|
+
console.log("");
|
|
2211
|
+
console.log("Callers:");
|
|
2212
|
+
summary.callers.slice(0, 25).forEach(printSymbolLink);
|
|
2213
|
+
}
|
|
2214
|
+
function printBlastRadiusSummary(summary) {
|
|
2215
|
+
console.log("Ripple blast radius");
|
|
2216
|
+
console.log(`File: ${summary.projectPath}`);
|
|
2217
|
+
console.log(`Risk: ${summary.modificationRisk}`);
|
|
2218
|
+
console.log(`Direct importers: ${summary.affectedCount}`);
|
|
2219
|
+
if (summary.directImporters.length === 0) {
|
|
2220
|
+
console.log("");
|
|
2221
|
+
console.log("No direct downstream files found.");
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2224
|
+
console.log("");
|
|
2225
|
+
console.log("Affected files:");
|
|
2226
|
+
summary.directImporters.slice(0, 25).forEach((item) => {
|
|
2227
|
+
console.log(` - ${item.file} [${item.modificationRisk}, importers: ${item.importerCount}]`);
|
|
2228
|
+
});
|
|
2229
|
+
}
|
|
2230
|
+
async function planCommand(options) {
|
|
2231
|
+
if (!options.file) {
|
|
2232
|
+
throw new Error("Missing target file. Usage: ripple plan --file <file> --task <task> [--budget N]");
|
|
2233
|
+
}
|
|
2234
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2235
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2236
|
+
try {
|
|
2237
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2238
|
+
const summary = engine.planContext(options.task ?? "", options.file, options.budget);
|
|
2239
|
+
if (!summary) {
|
|
2240
|
+
throw new Error(`File is not in the Ripple graph: ${options.file}`);
|
|
2241
|
+
}
|
|
2242
|
+
const policyExplanation = (0, core_1.explainRipplePolicyForTarget)((0, core_1.loadRipplePolicy)(workspaceRoot), summary.targetFile, { controlMode: options.mode });
|
|
2243
|
+
const savedIntent = options.save
|
|
2244
|
+
? savePlanChangeIntent(workspaceRoot, engine, summary, options, policyExplanation)
|
|
2245
|
+
: undefined;
|
|
2246
|
+
if (options.json) {
|
|
2247
|
+
const output = savedIntent
|
|
2248
|
+
? {
|
|
2249
|
+
...summary,
|
|
2250
|
+
policyExplanation,
|
|
2251
|
+
changeIntent: savedIntent.intent,
|
|
2252
|
+
changeIntentPath: savedIntent.path,
|
|
2253
|
+
}
|
|
2254
|
+
: {
|
|
2255
|
+
...summary,
|
|
2256
|
+
policyExplanation,
|
|
2257
|
+
};
|
|
2258
|
+
printJson(output);
|
|
2259
|
+
}
|
|
2260
|
+
else if (options.agent) {
|
|
2261
|
+
printAgentContextPlan(summary, savedIntent);
|
|
2262
|
+
}
|
|
2263
|
+
else {
|
|
2264
|
+
printContextPlan(summary, savedIntent);
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
finally {
|
|
2268
|
+
engine.dispose();
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
function savePlanChangeIntent(workspaceRoot, engine, summary, options, policyExplanation) {
|
|
2272
|
+
const loadedPolicy = (0, core_1.loadRipplePolicy)(workspaceRoot);
|
|
2273
|
+
const policy = (0, core_1.resolveRipplePolicyForTarget)(loadedPolicy, summary.targetFile);
|
|
2274
|
+
const intent = (0, core_1.buildChangeIntent)(summary, {
|
|
2275
|
+
controlMode: options.mode,
|
|
2276
|
+
allowedSymbols: options.symbol ? [options.symbol] : undefined,
|
|
2277
|
+
policy,
|
|
2278
|
+
policyExplanation,
|
|
2279
|
+
});
|
|
2280
|
+
const intentPath = (0, core_1.saveChangeIntent)(workspaceRoot, intent, (0, core_1.defaultChangeIntentPath)(workspaceRoot));
|
|
2281
|
+
const readiness = (0, core_1.buildRippleReadinessSummary)(workspaceRoot, engine);
|
|
2282
|
+
intent.readinessSnapshot = (0, core_1.buildChangeIntentReadinessSnapshot)(readiness);
|
|
2283
|
+
(0, core_1.saveChangeIntent)(workspaceRoot, intent, intentPath);
|
|
2284
|
+
return {
|
|
2285
|
+
intent,
|
|
2286
|
+
path: intentPath,
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
function currentPolicyExplanationForIntent(workspaceRoot, intent) {
|
|
2290
|
+
return (0, core_1.explainRipplePolicyForIntent)((0, core_1.loadRipplePolicy)(workspaceRoot), intent);
|
|
2291
|
+
}
|
|
2292
|
+
function currentReadinessSnapshotForEngine(workspaceRoot, engine) {
|
|
2293
|
+
return (0, core_1.buildChangeIntentReadinessSnapshot)((0, core_1.buildRippleReadinessSummary)(workspaceRoot, engine));
|
|
2294
|
+
}
|
|
2295
|
+
function approvalStatusOutput(intent, status) {
|
|
2296
|
+
return {
|
|
2297
|
+
...status,
|
|
2298
|
+
intent: {
|
|
2299
|
+
id: intent.id,
|
|
2300
|
+
task: intent.task,
|
|
2301
|
+
targetFile: intent.targetFile,
|
|
2302
|
+
controlMode: intent.controlMode,
|
|
2303
|
+
humanGate: intent.humanGate,
|
|
2304
|
+
boundaryRisk: intent.boundaryRisk,
|
|
2305
|
+
},
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
async function buildAuditForFiles(input) {
|
|
2309
|
+
const engine = new core_1.GraphEngine(input.workspaceRoot);
|
|
2310
|
+
try {
|
|
2311
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2312
|
+
const stagedCheck = (0, core_1.buildStagedCheckSummary)(engine, {
|
|
2313
|
+
workspaceRoot: input.workspaceRoot,
|
|
2314
|
+
stagedFiles: input.files,
|
|
2315
|
+
mode: input.mode,
|
|
2316
|
+
baseRef: input.baseRef,
|
|
2317
|
+
tokenBudget: input.tokenBudget,
|
|
2318
|
+
});
|
|
2319
|
+
const validatedCheck = (0, core_1.validateStagedCheckAgainstIntent)(stagedCheck, input.intent, {
|
|
2320
|
+
currentPolicyExplanation: input.currentPolicyExplanation,
|
|
2321
|
+
currentReadinessSnapshot: currentReadinessSnapshotForEngine(input.workspaceRoot, engine),
|
|
2322
|
+
});
|
|
2323
|
+
const repairPlan = (0, core_1.buildIntentDriftRepairPlan)(validatedCheck);
|
|
2324
|
+
return (0, core_1.buildRippleAuditSummary)({
|
|
2325
|
+
workspaceRoot: input.workspaceRoot,
|
|
2326
|
+
mode: input.mode,
|
|
2327
|
+
baseRef: input.baseRef,
|
|
2328
|
+
stagedCheck: validatedCheck,
|
|
2329
|
+
repairPlan,
|
|
2330
|
+
intent: input.intent,
|
|
2331
|
+
currentPolicyExplanation: input.currentPolicyExplanation,
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2334
|
+
finally {
|
|
2335
|
+
engine.dispose();
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
async function buildCheckSummaryForFiles(input) {
|
|
2339
|
+
const engine = new core_1.GraphEngine(input.workspaceRoot);
|
|
2340
|
+
try {
|
|
2341
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2342
|
+
return (0, core_1.buildStagedCheckSummary)(engine, {
|
|
2343
|
+
workspaceRoot: input.workspaceRoot,
|
|
2344
|
+
stagedFiles: input.files,
|
|
2345
|
+
mode: input.mode,
|
|
2346
|
+
baseRef: input.baseRef,
|
|
2347
|
+
tokenBudget: input.tokenBudget,
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
finally {
|
|
2351
|
+
engine.dispose();
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
async function checkCommand(options) {
|
|
2355
|
+
if (!options.staged && !options.changed) {
|
|
2356
|
+
throw new Error("Missing check mode. Usage: ripple check --staged or ripple check --changed --base <ref>");
|
|
2357
|
+
}
|
|
2358
|
+
if (options.staged && options.changed) {
|
|
2359
|
+
throw new Error("Choose one check mode: --staged or --changed");
|
|
2360
|
+
}
|
|
2361
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2362
|
+
const baseRef = options.base ?? "HEAD";
|
|
2363
|
+
const checkFiles = options.changed
|
|
2364
|
+
? (0, core_1.listGitChangedFiles)(workspaceRoot, baseRef)
|
|
2365
|
+
: (0, core_1.listGitStagedFiles)(workspaceRoot);
|
|
2366
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2367
|
+
try {
|
|
2368
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2369
|
+
const stagedSummary = (0, core_1.buildStagedCheckSummary)(engine, {
|
|
2370
|
+
workspaceRoot,
|
|
2371
|
+
stagedFiles: checkFiles,
|
|
2372
|
+
mode: options.changed ? "changed" : "staged",
|
|
2373
|
+
baseRef: options.changed ? baseRef : undefined,
|
|
2374
|
+
tokenBudget: options.budget,
|
|
2375
|
+
});
|
|
2376
|
+
let summary = stagedSummary;
|
|
2377
|
+
if (options.intent) {
|
|
2378
|
+
try {
|
|
2379
|
+
const intent = (0, core_1.loadChangeIntent)(workspaceRoot, options.intent);
|
|
2380
|
+
summary = (0, core_1.validateStagedCheckAgainstIntent)(stagedSummary, intent, {
|
|
2381
|
+
currentPolicyExplanation: currentPolicyExplanationForIntent(workspaceRoot, intent),
|
|
2382
|
+
currentReadinessSnapshot: currentReadinessSnapshotForEngine(workspaceRoot, engine),
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
catch (err) {
|
|
2386
|
+
if (!options.strict && !options.githubAnnotations) {
|
|
2387
|
+
throw err;
|
|
2388
|
+
}
|
|
2389
|
+
const message = intentLoadFailureMessage(options.intent, err);
|
|
2390
|
+
if (options.json) {
|
|
2391
|
+
printJson({
|
|
2392
|
+
...summary,
|
|
2393
|
+
nextRequiredPhase: MISSING_INTENT_NEXT_REQUIRED_PHASE,
|
|
2394
|
+
nextRequiredAction: MISSING_INTENT_NEXT_REQUIRED_ACTION,
|
|
2395
|
+
intentLoadError: {
|
|
2396
|
+
intent: options.intent,
|
|
2397
|
+
message,
|
|
2398
|
+
nextRequiredPhase: MISSING_INTENT_NEXT_REQUIRED_PHASE,
|
|
2399
|
+
nextRequiredAction: MISSING_INTENT_NEXT_REQUIRED_ACTION,
|
|
2400
|
+
},
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
else if (options.agent) {
|
|
2404
|
+
printAgentStagedCheckSummary(summary);
|
|
2405
|
+
console.log("");
|
|
2406
|
+
console.log(`next_required_phase: ${MISSING_INTENT_NEXT_REQUIRED_PHASE}`);
|
|
2407
|
+
console.log(`next_required_action: ${MISSING_INTENT_NEXT_REQUIRED_ACTION}`);
|
|
2408
|
+
console.log("");
|
|
2409
|
+
console.log("intent_error:");
|
|
2410
|
+
console.log(`- ${message}`);
|
|
2411
|
+
}
|
|
2412
|
+
else {
|
|
2413
|
+
printStagedCheckSummary(summary);
|
|
2414
|
+
console.log("");
|
|
2415
|
+
console.log(`Next required phase: ${MISSING_INTENT_NEXT_REQUIRED_PHASE}`);
|
|
2416
|
+
console.log(`Next required action: ${MISSING_INTENT_NEXT_REQUIRED_ACTION}`);
|
|
2417
|
+
console.log(`Intent error: ${message}`);
|
|
2418
|
+
}
|
|
2419
|
+
if (options.githubAnnotations && !options.json) {
|
|
2420
|
+
printGithubIntentLoadError(message);
|
|
2421
|
+
}
|
|
2422
|
+
writeGithubStepSummary({ summary, intentLoadError: message });
|
|
2423
|
+
applyStrictExit(options.strict);
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
if (options.json) {
|
|
2428
|
+
printJson(summary);
|
|
2429
|
+
}
|
|
2430
|
+
else if (options.agent) {
|
|
2431
|
+
printAgentStagedCheckSummary(summary);
|
|
2432
|
+
}
|
|
2433
|
+
else {
|
|
2434
|
+
printStagedCheckSummary(summary);
|
|
2435
|
+
}
|
|
2436
|
+
if (options.githubAnnotations && !options.json) {
|
|
2437
|
+
printGithubCheckAnnotations(summary);
|
|
2438
|
+
}
|
|
2439
|
+
writeGithubStepSummary({ summary });
|
|
2440
|
+
applyStrictExit(options.strict && strictCheckShouldFail(summary));
|
|
2441
|
+
}
|
|
2442
|
+
finally {
|
|
2443
|
+
engine.dispose();
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
async function buildAuditFromCliOptions(options) {
|
|
2447
|
+
if (options.staged && options.changed) {
|
|
2448
|
+
throw new Error("Choose one gate/audit mode: --staged or --changed");
|
|
2449
|
+
}
|
|
2450
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2451
|
+
const intentRef = options.intent ?? "latest";
|
|
2452
|
+
const mode = options.changed ? "changed" : "staged";
|
|
2453
|
+
const baseRef = options.base ?? "HEAD";
|
|
2454
|
+
const files = mode === "changed"
|
|
2455
|
+
? (0, core_1.listGitChangedFiles)(workspaceRoot, baseRef)
|
|
2456
|
+
: (0, core_1.listGitStagedFiles)(workspaceRoot);
|
|
2457
|
+
const intent = (0, core_1.loadChangeIntent)(workspaceRoot, intentRef);
|
|
2458
|
+
const currentPolicyExplanation = currentPolicyExplanationForIntent(workspaceRoot, intent);
|
|
2459
|
+
return buildAuditForFiles({
|
|
2460
|
+
workspaceRoot,
|
|
2461
|
+
files,
|
|
2462
|
+
mode,
|
|
2463
|
+
baseRef: mode === "changed" ? baseRef : undefined,
|
|
2464
|
+
tokenBudget: options.budget,
|
|
2465
|
+
intent,
|
|
2466
|
+
currentPolicyExplanation,
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
async function auditCommand(options) {
|
|
2470
|
+
const audit = await buildAuditFromCliOptions(options);
|
|
2471
|
+
if (options.json) {
|
|
2472
|
+
printJson({
|
|
2473
|
+
...audit,
|
|
2474
|
+
gate: (0, core_1.buildRippleGateSummary)(audit),
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
else if (options.agent) {
|
|
2478
|
+
printAgentAuditSummary(audit);
|
|
2479
|
+
}
|
|
2480
|
+
else {
|
|
2481
|
+
printAuditSummary(audit);
|
|
2482
|
+
}
|
|
2483
|
+
applyStrictExit(options.strict && strictAuditShouldFail(audit));
|
|
2484
|
+
}
|
|
2485
|
+
async function gateCommand(options) {
|
|
2486
|
+
const audit = await buildAuditFromCliOptions(options);
|
|
2487
|
+
const gate = (0, core_1.buildRippleGateSummary)(audit);
|
|
2488
|
+
if (options.json) {
|
|
2489
|
+
printJson(gate);
|
|
2490
|
+
}
|
|
2491
|
+
else if (options.agent) {
|
|
2492
|
+
printAgentGateSummary(gate);
|
|
2493
|
+
}
|
|
2494
|
+
else {
|
|
2495
|
+
printGateSummary(gate);
|
|
2496
|
+
}
|
|
2497
|
+
applyStrictExit(options.strict && !gate.canContinue);
|
|
2498
|
+
}
|
|
2499
|
+
function approveCommand(options) {
|
|
2500
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2501
|
+
const intentRef = options.intent ?? "latest";
|
|
2502
|
+
const intent = (0, core_1.loadChangeIntent)(workspaceRoot, intentRef);
|
|
2503
|
+
const approval = (0, core_1.recordRippleApproval)(workspaceRoot, intent, {
|
|
2504
|
+
gate: options.gate,
|
|
2505
|
+
approvedBy: options.approvedBy,
|
|
2506
|
+
reason: options.reason,
|
|
2507
|
+
});
|
|
2508
|
+
if (options.json) {
|
|
2509
|
+
printJson(approval);
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2512
|
+
if (options.agent) {
|
|
2513
|
+
printAgentApprovalRecord(approval);
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
printApprovalRecord(approval);
|
|
2517
|
+
}
|
|
2518
|
+
function approvalCommand(options) {
|
|
2519
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2520
|
+
const intentRef = options.intent ?? "latest";
|
|
2521
|
+
const intent = (0, core_1.loadChangeIntent)(workspaceRoot, intentRef);
|
|
2522
|
+
const status = approvalStatusOutput(intent, (0, core_1.resolveRippleApprovalStatus)(workspaceRoot, intent, options.gate));
|
|
2523
|
+
if (options.json) {
|
|
2524
|
+
printJson(status);
|
|
2525
|
+
return;
|
|
2526
|
+
}
|
|
2527
|
+
if (options.agent) {
|
|
2528
|
+
printAgentApprovalStatus(status);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
printApprovalStatus(status);
|
|
2532
|
+
}
|
|
2533
|
+
async function ciCommand(options) {
|
|
2534
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2535
|
+
const baseRef = options.base ?? defaultCiBaseRef();
|
|
2536
|
+
const intentRef = options.intent ?? "latest";
|
|
2537
|
+
const files = (0, core_1.listGitChangedFiles)(workspaceRoot, baseRef);
|
|
2538
|
+
const emitGithubAnnotations = shouldEmitGithubAnnotations(options);
|
|
2539
|
+
let intent;
|
|
2540
|
+
try {
|
|
2541
|
+
intent = (0, core_1.loadChangeIntent)(workspaceRoot, intentRef);
|
|
2542
|
+
}
|
|
2543
|
+
catch (err) {
|
|
2544
|
+
const summary = await buildCheckSummaryForFiles({
|
|
2545
|
+
workspaceRoot,
|
|
2546
|
+
files,
|
|
2547
|
+
mode: "changed",
|
|
2548
|
+
baseRef,
|
|
2549
|
+
tokenBudget: options.budget,
|
|
2550
|
+
});
|
|
2551
|
+
const message = intentLoadFailureMessage(intentRef, err);
|
|
2552
|
+
if (options.json) {
|
|
2553
|
+
printJson({
|
|
2554
|
+
...summary,
|
|
2555
|
+
nextRequiredPhase: MISSING_INTENT_NEXT_REQUIRED_PHASE,
|
|
2556
|
+
nextRequiredAction: MISSING_INTENT_NEXT_REQUIRED_ACTION,
|
|
2557
|
+
intentLoadError: {
|
|
2558
|
+
intent: intentRef,
|
|
2559
|
+
message,
|
|
2560
|
+
nextRequiredPhase: MISSING_INTENT_NEXT_REQUIRED_PHASE,
|
|
2561
|
+
nextRequiredAction: MISSING_INTENT_NEXT_REQUIRED_ACTION,
|
|
2562
|
+
},
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
else if (options.agent) {
|
|
2566
|
+
printAgentStagedCheckSummary(summary);
|
|
2567
|
+
console.log("");
|
|
2568
|
+
console.log(`next_required_phase: ${MISSING_INTENT_NEXT_REQUIRED_PHASE}`);
|
|
2569
|
+
console.log(`next_required_action: ${MISSING_INTENT_NEXT_REQUIRED_ACTION}`);
|
|
2570
|
+
console.log("");
|
|
2571
|
+
console.log("intent_error:");
|
|
2572
|
+
console.log(`- ${message}`);
|
|
2573
|
+
}
|
|
2574
|
+
else {
|
|
2575
|
+
printStagedCheckSummary(summary);
|
|
2576
|
+
console.log("");
|
|
2577
|
+
console.log(`Next required phase: ${MISSING_INTENT_NEXT_REQUIRED_PHASE}`);
|
|
2578
|
+
console.log(`Next required action: ${MISSING_INTENT_NEXT_REQUIRED_ACTION}`);
|
|
2579
|
+
console.log(`Intent error: ${message}`);
|
|
2580
|
+
}
|
|
2581
|
+
if (emitGithubAnnotations && !options.json) {
|
|
2582
|
+
printGithubIntentLoadError(message);
|
|
2583
|
+
}
|
|
2584
|
+
writeGithubStepSummary({ summary, intentLoadError: message });
|
|
2585
|
+
process.exitCode = 1;
|
|
2586
|
+
return;
|
|
2587
|
+
}
|
|
2588
|
+
const audit = await buildAuditForFiles({
|
|
2589
|
+
workspaceRoot,
|
|
2590
|
+
files,
|
|
2591
|
+
mode: "changed",
|
|
2592
|
+
baseRef,
|
|
2593
|
+
tokenBudget: options.budget,
|
|
2594
|
+
intent,
|
|
2595
|
+
currentPolicyExplanation: currentPolicyExplanationForIntent(workspaceRoot, intent),
|
|
2596
|
+
});
|
|
2597
|
+
if (options.json) {
|
|
2598
|
+
printJson({
|
|
2599
|
+
...audit,
|
|
2600
|
+
gate: (0, core_1.buildRippleGateSummary)(audit),
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
else if (options.agent) {
|
|
2604
|
+
printAgentAuditSummary(audit);
|
|
2605
|
+
}
|
|
2606
|
+
else {
|
|
2607
|
+
printAuditSummary(audit);
|
|
2608
|
+
}
|
|
2609
|
+
if (emitGithubAnnotations && !options.json) {
|
|
2610
|
+
printGithubAuditAnnotations(audit);
|
|
2611
|
+
}
|
|
2612
|
+
writeGithubAuditStepSummary(audit);
|
|
2613
|
+
applyStrictExit(strictAuditShouldFail(audit));
|
|
2614
|
+
}
|
|
2615
|
+
async function doctorCommand(options) {
|
|
2616
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2617
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2618
|
+
try {
|
|
2619
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2620
|
+
const summary = (0, core_1.buildRippleReadinessSummary)(workspaceRoot, engine);
|
|
2621
|
+
if (options.json) {
|
|
2622
|
+
printJson(summary);
|
|
2623
|
+
}
|
|
2624
|
+
else if (options.agent) {
|
|
2625
|
+
printAgentDoctorSummary(summary);
|
|
2626
|
+
}
|
|
2627
|
+
else {
|
|
2628
|
+
printDoctorSummary(summary);
|
|
2629
|
+
}
|
|
2630
|
+
applyStrictExit(options.strict && summary.status !== "ready");
|
|
2631
|
+
}
|
|
2632
|
+
finally {
|
|
2633
|
+
engine.dispose();
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
async function initCommand(options) {
|
|
2637
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2638
|
+
const policy = (0, core_1.defaultRipplePolicy)();
|
|
2639
|
+
const policyContents = (0, core_1.formatRipplePolicy)(policy);
|
|
2640
|
+
const workflow = githubActionsWorkflow();
|
|
2641
|
+
const files = [
|
|
2642
|
+
{
|
|
2643
|
+
path: core_1.RIPPLE_POLICY_PATH.split(path.sep).join("/"),
|
|
2644
|
+
absolutePath: (0, core_1.ripplePolicyPath)(workspaceRoot),
|
|
2645
|
+
content: policyContents,
|
|
2646
|
+
},
|
|
2647
|
+
{
|
|
2648
|
+
path: GITHUB_ACTIONS_WORKFLOW_PATH.split(path.sep).join("/"),
|
|
2649
|
+
absolutePath: path.join(workspaceRoot, GITHUB_ACTIONS_WORKFLOW_PATH),
|
|
2650
|
+
content: workflow,
|
|
2651
|
+
},
|
|
2652
|
+
];
|
|
2653
|
+
if (options.print) {
|
|
2654
|
+
const summary = {
|
|
2655
|
+
protocol: "ripple-init",
|
|
2656
|
+
version: 1,
|
|
2657
|
+
workspace: workspaceRoot,
|
|
2658
|
+
files: files.map((file) => ({
|
|
2659
|
+
path: file.path,
|
|
2660
|
+
status: "printed",
|
|
2661
|
+
written: false,
|
|
2662
|
+
overwritten: false,
|
|
2663
|
+
content: file.content,
|
|
2664
|
+
})),
|
|
2665
|
+
nextSteps: defaultInitNextSteps(),
|
|
2666
|
+
};
|
|
2667
|
+
if (options.json) {
|
|
2668
|
+
printJson(summary);
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2671
|
+
process.stdout.write([
|
|
2672
|
+
`# ${files[0].path}`,
|
|
2673
|
+
files[0].content.trimEnd(),
|
|
2674
|
+
"",
|
|
2675
|
+
`# ${files[1].path}`,
|
|
2676
|
+
files[1].content.trimEnd(),
|
|
2677
|
+
"",
|
|
2678
|
+
].join("\n"));
|
|
2679
|
+
return;
|
|
2680
|
+
}
|
|
2681
|
+
const writtenFiles = files.map((file) => writeInitFile(file, options.force));
|
|
2682
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2683
|
+
try {
|
|
2684
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2685
|
+
const readiness = (0, core_1.buildRippleReadinessSummary)(workspaceRoot, engine);
|
|
2686
|
+
const summary = {
|
|
2687
|
+
protocol: "ripple-init",
|
|
2688
|
+
version: 1,
|
|
2689
|
+
workspace: workspaceRoot,
|
|
2690
|
+
files: writtenFiles,
|
|
2691
|
+
readiness,
|
|
2692
|
+
nextSteps: defaultInitNextSteps(readiness),
|
|
2693
|
+
};
|
|
2694
|
+
if (options.json) {
|
|
2695
|
+
printJson(summary);
|
|
2696
|
+
}
|
|
2697
|
+
else {
|
|
2698
|
+
printInitSummary(summary);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
finally {
|
|
2702
|
+
engine.dispose();
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
function writeInitFile(file, force) {
|
|
2706
|
+
const existed = fs.existsSync(file.absolutePath);
|
|
2707
|
+
if (existed && !force) {
|
|
2708
|
+
return {
|
|
2709
|
+
path: file.path,
|
|
2710
|
+
status: "exists",
|
|
2711
|
+
written: false,
|
|
2712
|
+
overwritten: false,
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
fs.mkdirSync(path.dirname(file.absolutePath), { recursive: true });
|
|
2716
|
+
fs.writeFileSync(file.absolutePath, file.content, "utf8");
|
|
2717
|
+
return {
|
|
2718
|
+
path: file.path,
|
|
2719
|
+
status: existed ? "overwritten" : "written",
|
|
2720
|
+
written: true,
|
|
2721
|
+
overwritten: existed,
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
function initCiCommand(options) {
|
|
2725
|
+
const workflow = githubActionsWorkflow();
|
|
2726
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2727
|
+
const targetPath = path.join(workspaceRoot, GITHUB_ACTIONS_WORKFLOW_PATH);
|
|
2728
|
+
const relativeTargetPath = GITHUB_ACTIONS_WORKFLOW_PATH.split(path.sep).join("/");
|
|
2729
|
+
if (options.print) {
|
|
2730
|
+
if (options.json) {
|
|
2731
|
+
printJson({
|
|
2732
|
+
path: relativeTargetPath,
|
|
2733
|
+
workflow,
|
|
2734
|
+
written: false,
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
else {
|
|
2738
|
+
process.stdout.write(workflow);
|
|
2739
|
+
}
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
const existed = fs.existsSync(targetPath);
|
|
2743
|
+
if (existed && !options.force) {
|
|
2744
|
+
throw new Error(`${relativeTargetPath} already exists. Re-run with --force to overwrite it.`);
|
|
2745
|
+
}
|
|
2746
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
2747
|
+
fs.writeFileSync(targetPath, workflow, "utf8");
|
|
2748
|
+
if (options.json) {
|
|
2749
|
+
printJson({
|
|
2750
|
+
path: relativeTargetPath,
|
|
2751
|
+
written: true,
|
|
2752
|
+
overwritten: existed,
|
|
2753
|
+
});
|
|
2754
|
+
return;
|
|
2755
|
+
}
|
|
2756
|
+
console.log(existed ? "Ripple CI workflow overwritten" : "Ripple CI workflow written");
|
|
2757
|
+
console.log(`Path: ${relativeTargetPath}`);
|
|
2758
|
+
console.log("Command: npx -y @getripple/cli@latest ci --base origin/${{ github.base_ref }} --intent latest --github-annotations");
|
|
2759
|
+
}
|
|
2760
|
+
function policyCommand(args, options) {
|
|
2761
|
+
const subcommand = args[0];
|
|
2762
|
+
if (subcommand === "init") {
|
|
2763
|
+
policyInitCommand(options);
|
|
2764
|
+
return;
|
|
2765
|
+
}
|
|
2766
|
+
if (subcommand === "explain") {
|
|
2767
|
+
policyExplainCommand(options);
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
throw new Error("Usage: ripple policy init [--print] [--force] or ripple policy explain --file <file>");
|
|
2771
|
+
}
|
|
2772
|
+
function policyInitCommand(options) {
|
|
2773
|
+
const policy = (0, core_1.defaultRipplePolicy)();
|
|
2774
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2775
|
+
const targetPath = (0, core_1.ripplePolicyPath)(workspaceRoot);
|
|
2776
|
+
const relativeTargetPath = core_1.RIPPLE_POLICY_PATH.split(path.sep).join("/");
|
|
2777
|
+
const contents = (0, core_1.formatRipplePolicy)(policy);
|
|
2778
|
+
if (options.print) {
|
|
2779
|
+
if (options.json) {
|
|
2780
|
+
printJson({
|
|
2781
|
+
path: relativeTargetPath,
|
|
2782
|
+
policy,
|
|
2783
|
+
written: false,
|
|
2784
|
+
});
|
|
2785
|
+
}
|
|
2786
|
+
else {
|
|
2787
|
+
process.stdout.write(contents);
|
|
2788
|
+
}
|
|
2789
|
+
return;
|
|
2790
|
+
}
|
|
2791
|
+
const existed = fs.existsSync(targetPath);
|
|
2792
|
+
if (existed && !options.force) {
|
|
2793
|
+
throw new Error(`${relativeTargetPath} already exists. Re-run with --force to overwrite it.`);
|
|
2794
|
+
}
|
|
2795
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
2796
|
+
fs.writeFileSync(targetPath, contents, "utf8");
|
|
2797
|
+
if (options.json) {
|
|
2798
|
+
printJson({
|
|
2799
|
+
path: relativeTargetPath,
|
|
2800
|
+
written: true,
|
|
2801
|
+
overwritten: existed,
|
|
2802
|
+
policy,
|
|
2803
|
+
});
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
console.log(existed ? "Ripple policy overwritten" : "Ripple policy written");
|
|
2807
|
+
console.log(`Path: ${relativeTargetPath}`);
|
|
2808
|
+
console.log(`Default mode: ${policy.defaultMode ?? "file"}`);
|
|
2809
|
+
console.log(`Risk rules: ${policy.riskRules?.length ?? 0}`);
|
|
2810
|
+
}
|
|
2811
|
+
function policyExplainCommand(options) {
|
|
2812
|
+
if (!options.file) {
|
|
2813
|
+
throw new Error("Missing target file. Usage: ripple policy explain --file <file>");
|
|
2814
|
+
}
|
|
2815
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2816
|
+
const loadedPolicy = (0, core_1.loadRipplePolicy)(workspaceRoot);
|
|
2817
|
+
const explanation = (0, core_1.explainRipplePolicyForTarget)(loadedPolicy, normalizeProjectPath(options.file));
|
|
2818
|
+
if (options.json) {
|
|
2819
|
+
printJson(explanation);
|
|
2820
|
+
return;
|
|
2821
|
+
}
|
|
2822
|
+
if (options.agent) {
|
|
2823
|
+
printAgentPolicyExplanation(explanation);
|
|
2824
|
+
return;
|
|
2825
|
+
}
|
|
2826
|
+
console.log("Ripple policy explanation");
|
|
2827
|
+
console.log(`File: ${explanation.targetFile}`);
|
|
2828
|
+
console.log(`Policy source: ${explanation.policySource}`);
|
|
2829
|
+
console.log(`Policy exists: ${explanation.policyExists}`);
|
|
2830
|
+
console.log(`Effective mode: ${explanation.effectiveMode}`);
|
|
2831
|
+
console.log(`Policy risk: ${explanation.policyRisk}`);
|
|
2832
|
+
console.log(`Human gate: ${explanation.humanGate}`);
|
|
2833
|
+
console.log(`Human required: ${explanation.humanRequired}`);
|
|
2834
|
+
console.log(`Allow PR mode: ${explanation.allowPrMode}`);
|
|
2835
|
+
console.log("");
|
|
2836
|
+
console.log("Matched rules:");
|
|
2837
|
+
if (explanation.matchedRules.length === 0) {
|
|
2838
|
+
console.log(" - none");
|
|
2839
|
+
}
|
|
2840
|
+
else {
|
|
2841
|
+
explanation.matchedRules.forEach((rule) => console.log(` - ${rule}`));
|
|
2842
|
+
}
|
|
2843
|
+
console.log("");
|
|
2844
|
+
console.log("Why:");
|
|
2845
|
+
explanation.why.forEach((reason) => console.log(` - ${reason}`));
|
|
2846
|
+
console.log("");
|
|
2847
|
+
console.log("Next steps:");
|
|
2848
|
+
explanation.nextSteps.forEach((step) => console.log(` - ${step}`));
|
|
2849
|
+
}
|
|
2850
|
+
function printAgentPolicyExplanation(explanation) {
|
|
2851
|
+
console.log("RIPPLE_POLICY_EXPLAIN");
|
|
2852
|
+
console.log(`target: ${explanation.targetFile}`);
|
|
2853
|
+
console.log(`policy_source: ${explanation.policySource}`);
|
|
2854
|
+
console.log(`policy_exists: ${explanation.policyExists}`);
|
|
2855
|
+
console.log(`effective_mode: ${explanation.effectiveMode}`);
|
|
2856
|
+
console.log(`policy_risk: ${explanation.policyRisk}`);
|
|
2857
|
+
console.log(`human_gate: ${explanation.humanGate}`);
|
|
2858
|
+
console.log(`human_required: ${explanation.humanRequired}`);
|
|
2859
|
+
console.log(`allow_pr_mode: ${explanation.allowPrMode}`);
|
|
2860
|
+
console.log("");
|
|
2861
|
+
printAgentList("matched_rules", explanation.matchedRules);
|
|
2862
|
+
console.log("");
|
|
2863
|
+
printAgentList("why", explanation.why);
|
|
2864
|
+
console.log("");
|
|
2865
|
+
printAgentList("next_steps", explanation.nextSteps);
|
|
2866
|
+
}
|
|
2867
|
+
async function repairCommand(options) {
|
|
2868
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2869
|
+
const stagedFiles = (0, core_1.listGitStagedFiles)(workspaceRoot);
|
|
2870
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2871
|
+
try {
|
|
2872
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2873
|
+
const stagedSummary = (0, core_1.buildStagedCheckSummary)(engine, {
|
|
2874
|
+
workspaceRoot,
|
|
2875
|
+
stagedFiles,
|
|
2876
|
+
tokenBudget: options.budget,
|
|
2877
|
+
});
|
|
2878
|
+
const intent = (0, core_1.loadChangeIntent)(workspaceRoot, options.intent ?? "latest");
|
|
2879
|
+
const summary = (0, core_1.validateStagedCheckAgainstIntent)(stagedSummary, intent, {
|
|
2880
|
+
currentPolicyExplanation: currentPolicyExplanationForIntent(workspaceRoot, intent),
|
|
2881
|
+
currentReadinessSnapshot: currentReadinessSnapshotForEngine(workspaceRoot, engine),
|
|
2882
|
+
});
|
|
2883
|
+
const repairPlan = (0, core_1.buildIntentDriftRepairPlan)(summary);
|
|
2884
|
+
if (options.json) {
|
|
2885
|
+
printJson(repairPlan);
|
|
2886
|
+
}
|
|
2887
|
+
else if (options.agent) {
|
|
2888
|
+
printAgentIntentDriftRepairPlan(repairPlan);
|
|
2889
|
+
}
|
|
2890
|
+
else {
|
|
2891
|
+
printIntentDriftRepairPlan(repairPlan);
|
|
2892
|
+
}
|
|
2893
|
+
applyStrictExit(options.strict && strictRepairShouldFail(repairPlan));
|
|
2894
|
+
}
|
|
2895
|
+
finally {
|
|
2896
|
+
engine.dispose();
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
async function historyCommand(options) {
|
|
2900
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2901
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2902
|
+
try {
|
|
2903
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2904
|
+
const summary = engine.getRecentHistorySummary(options.last);
|
|
2905
|
+
if (options.json) {
|
|
2906
|
+
printJson(summary);
|
|
2907
|
+
}
|
|
2908
|
+
else {
|
|
2909
|
+
printHistorySummary(summary);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
finally {
|
|
2913
|
+
engine.dispose();
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
async function callersCommand(symbolId, options) {
|
|
2917
|
+
if (!symbolId) {
|
|
2918
|
+
throw new Error("Missing symbol id. Usage: ripple callers <file>::<symbol>");
|
|
2919
|
+
}
|
|
2920
|
+
if (!symbolId.includes("::")) {
|
|
2921
|
+
throw new Error("Symbol id must use <file>::<symbol>, for example src/auth.ts::validateToken");
|
|
2922
|
+
}
|
|
2923
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2924
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2925
|
+
try {
|
|
2926
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2927
|
+
const summary = engine.getSymbolCallersSummary(symbolId);
|
|
2928
|
+
if (!summary) {
|
|
2929
|
+
throw new Error(`Symbol is not in the Ripple graph: ${symbolId}`);
|
|
2930
|
+
}
|
|
2931
|
+
if (options.json) {
|
|
2932
|
+
printJson(summary);
|
|
2933
|
+
}
|
|
2934
|
+
else {
|
|
2935
|
+
printSymbolCallersSummary(summary);
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
finally {
|
|
2939
|
+
engine.dispose();
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
async function scanCommand(targetPath, options) {
|
|
2943
|
+
const workspaceRoot = resolveWorkspaceRoot(targetPath);
|
|
2944
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2945
|
+
try {
|
|
2946
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2947
|
+
const workflowPath = path.join(workspaceRoot, ".ripple", "WORKFLOW.md");
|
|
2948
|
+
const summary = {
|
|
2949
|
+
workspace: workspaceRoot,
|
|
2950
|
+
files: engine.graph.files.size,
|
|
2951
|
+
symbols: engine.graph.symbols.size,
|
|
2952
|
+
callEdges: countCallEdges(engine),
|
|
2953
|
+
contextGenerated: fs.existsSync(workflowPath),
|
|
2954
|
+
contextPath: workflowPath,
|
|
2955
|
+
};
|
|
2956
|
+
if (options.json) {
|
|
2957
|
+
printJson(summary);
|
|
2958
|
+
}
|
|
2959
|
+
else {
|
|
2960
|
+
printScanSummary(summary);
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
finally {
|
|
2964
|
+
engine.dispose();
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
async function symbolsCommand(filePath, options) {
|
|
2968
|
+
if (!filePath) {
|
|
2969
|
+
throw new Error("Missing file path. Usage: ripple symbols <file>");
|
|
2970
|
+
}
|
|
2971
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2972
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2973
|
+
try {
|
|
2974
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2975
|
+
const summary = engine.getFileSymbolsSummary(filePath);
|
|
2976
|
+
if (!summary) {
|
|
2977
|
+
throw new Error(`File is not in the Ripple graph: ${filePath}`);
|
|
2978
|
+
}
|
|
2979
|
+
if (options.json) {
|
|
2980
|
+
printJson(summary);
|
|
2981
|
+
}
|
|
2982
|
+
else {
|
|
2983
|
+
printFileSymbolsSummary(summary);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
finally {
|
|
2987
|
+
engine.dispose();
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
async function blastCommand(filePath, options) {
|
|
2991
|
+
if (!filePath) {
|
|
2992
|
+
throw new Error("Missing file path. Usage: ripple blast <file>");
|
|
2993
|
+
}
|
|
2994
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
2995
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
2996
|
+
try {
|
|
2997
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
2998
|
+
const summary = engine.getBlastRadiusSummary(filePath);
|
|
2999
|
+
if (!summary) {
|
|
3000
|
+
throw new Error(`File is not in the Ripple graph: ${filePath}`);
|
|
3001
|
+
}
|
|
3002
|
+
if (options.json) {
|
|
3003
|
+
printJson(summary);
|
|
3004
|
+
}
|
|
3005
|
+
else {
|
|
3006
|
+
printBlastRadiusSummary(summary);
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
finally {
|
|
3010
|
+
engine.dispose();
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
async function dependencyCommand(filePath, direction, options) {
|
|
3014
|
+
if (!filePath) {
|
|
3015
|
+
throw new Error(`Missing file path. Usage: ripple ${direction} <file>`);
|
|
3016
|
+
}
|
|
3017
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
3018
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
3019
|
+
try {
|
|
3020
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
3021
|
+
const summary = engine.getFileDependencySummary(filePath);
|
|
3022
|
+
if (!summary) {
|
|
3023
|
+
throw new Error(`File is not in the Ripple graph: ${filePath}`);
|
|
3024
|
+
}
|
|
3025
|
+
const output = direction === "imports"
|
|
3026
|
+
? { ...summary, importers: undefined }
|
|
3027
|
+
: { ...summary, imports: undefined };
|
|
3028
|
+
if (options.json) {
|
|
3029
|
+
printJson(output);
|
|
3030
|
+
}
|
|
3031
|
+
else {
|
|
3032
|
+
printDependencySummary(summary, direction);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
finally {
|
|
3036
|
+
engine.dispose();
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
async function focusCommand(filePath, options) {
|
|
3040
|
+
if (!filePath) {
|
|
3041
|
+
throw new Error("Missing file path. Usage: ripple focus <file>");
|
|
3042
|
+
}
|
|
3043
|
+
const workspaceRoot = resolveWorkspaceRoot(".");
|
|
3044
|
+
const engine = new core_1.GraphEngine(workspaceRoot);
|
|
3045
|
+
try {
|
|
3046
|
+
await runWithQuietEngine(() => engine.initialScan());
|
|
3047
|
+
const summary = engine.getFileFocusSummary(filePath);
|
|
3048
|
+
if (!summary) {
|
|
3049
|
+
throw new Error(`File is not in the Ripple graph: ${filePath}`);
|
|
3050
|
+
}
|
|
3051
|
+
if (options.json) {
|
|
3052
|
+
printJson(summary);
|
|
3053
|
+
}
|
|
3054
|
+
else {
|
|
3055
|
+
printFocusSummary(summary);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
finally {
|
|
3059
|
+
engine.dispose();
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
async function main() {
|
|
3063
|
+
const { command, args, options } = parseCliArgs(process.argv.slice(2));
|
|
3064
|
+
const [arg] = args;
|
|
3065
|
+
if (!command || command === "--help" || command === "-h") {
|
|
3066
|
+
console.log(usage());
|
|
3067
|
+
return;
|
|
3068
|
+
}
|
|
3069
|
+
if (command === "--version" || command === "-v") {
|
|
3070
|
+
console.log(version());
|
|
3071
|
+
return;
|
|
3072
|
+
}
|
|
3073
|
+
if (command === "agent") {
|
|
3074
|
+
if (options.json) {
|
|
3075
|
+
printJson((0, core_1.getAgentWorkflowSummary)());
|
|
3076
|
+
}
|
|
3077
|
+
else {
|
|
3078
|
+
console.log(agentWorkflowGuide());
|
|
3079
|
+
}
|
|
3080
|
+
return;
|
|
3081
|
+
}
|
|
3082
|
+
if (command === "init") {
|
|
3083
|
+
await initCommand(options);
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
3086
|
+
if (command === "scan") {
|
|
3087
|
+
await scanCommand(arg, options);
|
|
3088
|
+
return;
|
|
3089
|
+
}
|
|
3090
|
+
if (command === "doctor") {
|
|
3091
|
+
await doctorCommand(options);
|
|
3092
|
+
return;
|
|
3093
|
+
}
|
|
3094
|
+
if (command === "focus") {
|
|
3095
|
+
await focusCommand(arg, options);
|
|
3096
|
+
return;
|
|
3097
|
+
}
|
|
3098
|
+
if (command === "blast") {
|
|
3099
|
+
await blastCommand(arg, options);
|
|
3100
|
+
return;
|
|
3101
|
+
}
|
|
3102
|
+
if (command === "imports") {
|
|
3103
|
+
await dependencyCommand(arg, "imports", options);
|
|
3104
|
+
return;
|
|
3105
|
+
}
|
|
3106
|
+
if (command === "importers") {
|
|
3107
|
+
await dependencyCommand(arg, "importers", options);
|
|
3108
|
+
return;
|
|
3109
|
+
}
|
|
3110
|
+
if (command === "symbols") {
|
|
3111
|
+
await symbolsCommand(arg, options);
|
|
3112
|
+
return;
|
|
3113
|
+
}
|
|
3114
|
+
if (command === "callers") {
|
|
3115
|
+
await callersCommand(arg, options);
|
|
3116
|
+
return;
|
|
3117
|
+
}
|
|
3118
|
+
if (command === "history") {
|
|
3119
|
+
await historyCommand(options);
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
if (command === "plan") {
|
|
3123
|
+
await planCommand(options);
|
|
3124
|
+
return;
|
|
3125
|
+
}
|
|
3126
|
+
if (command === "check") {
|
|
3127
|
+
await checkCommand(options);
|
|
3128
|
+
return;
|
|
3129
|
+
}
|
|
3130
|
+
if (command === "audit") {
|
|
3131
|
+
await auditCommand(options);
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
if (command === "gate") {
|
|
3135
|
+
await gateCommand(options);
|
|
3136
|
+
return;
|
|
3137
|
+
}
|
|
3138
|
+
if (command === "approval") {
|
|
3139
|
+
approvalCommand(options);
|
|
3140
|
+
return;
|
|
3141
|
+
}
|
|
3142
|
+
if (command === "approve") {
|
|
3143
|
+
approveCommand(options);
|
|
3144
|
+
return;
|
|
3145
|
+
}
|
|
3146
|
+
if (command === "ci") {
|
|
3147
|
+
await ciCommand(options);
|
|
3148
|
+
return;
|
|
3149
|
+
}
|
|
3150
|
+
if (command === "init-ci") {
|
|
3151
|
+
initCiCommand(options);
|
|
3152
|
+
return;
|
|
3153
|
+
}
|
|
3154
|
+
if (command === "policy") {
|
|
3155
|
+
policyCommand(args, options);
|
|
3156
|
+
return;
|
|
3157
|
+
}
|
|
3158
|
+
if (command === "repair") {
|
|
3159
|
+
await repairCommand(options);
|
|
3160
|
+
return;
|
|
3161
|
+
}
|
|
3162
|
+
throw new Error(`Unknown command: ${command}\n\n${usage()}`);
|
|
3163
|
+
}
|
|
3164
|
+
main().catch((err) => {
|
|
3165
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3166
|
+
console.error(`Ripple CLI error: ${message}`);
|
|
3167
|
+
process.exitCode = 1;
|
|
3168
|
+
});
|
|
3169
|
+
//# sourceMappingURL=index.js.map
|