@nexrall/code-core 1.4.76 → 1.4.78
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/dist/agent/effortCap.d.ts +24 -0
- package/dist/agent/effortCap.d.ts.map +1 -0
- package/dist/agent/effortCap.js +66 -0
- package/dist/agent/goal.d.ts +55 -0
- package/dist/agent/goal.d.ts.map +1 -0
- package/dist/agent/goal.js +120 -0
- package/dist/agent/handoffBrief.d.ts +82 -0
- package/dist/agent/handoffBrief.d.ts.map +1 -0
- package/dist/agent/handoffBrief.js +121 -0
- package/dist/agent/hooks.d.ts +10 -2
- package/dist/agent/hooks.d.ts.map +1 -1
- package/dist/agent/hooks.js +9 -3
- package/dist/agent/modelCatalogue.d.ts +23 -0
- package/dist/agent/modelCatalogue.d.ts.map +1 -1
- package/dist/agent/modelCatalogue.js +28 -0
- package/dist/agent/outputStyles.d.ts +27 -0
- package/dist/agent/outputStyles.d.ts.map +1 -0
- package/dist/agent/outputStyles.js +174 -0
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/agent/skills.js +21 -1
- package/dist/api/billingLines.d.ts +11 -0
- package/dist/api/billingLines.d.ts.map +1 -0
- package/dist/api/billingLines.js +56 -0
- package/dist/api/client.d.ts +2 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/scheduleLines.d.ts +53 -0
- package/dist/api/scheduleLines.d.ts.map +1 -0
- package/dist/api/scheduleLines.js +150 -0
- package/dist/context/contextFiles.d.ts +81 -0
- package/dist/context/contextFiles.d.ts.map +1 -0
- package/dist/context/contextFiles.js +310 -0
- package/dist/diff/collect.d.ts +25 -0
- package/dist/diff/collect.d.ts.map +1 -0
- package/dist/diff/collect.js +169 -0
- package/dist/diff/diffView.d.ts +57 -0
- package/dist/diff/diffView.d.ts.map +1 -0
- package/dist/diff/diffView.js +231 -0
- package/dist/diff/reviewQueue.d.ts +53 -0
- package/dist/diff/reviewQueue.d.ts.map +1 -0
- package/dist/diff/reviewQueue.js +349 -0
- package/dist/events/recap.d.ts +54 -0
- package/dist/events/recap.d.ts.map +1 -0
- package/dist/events/recap.js +161 -0
- package/dist/events/sessionEvents.d.ts +54 -0
- package/dist/events/sessionEvents.d.ts.map +1 -0
- package/dist/events/sessionEvents.js +324 -0
- package/dist/events/sessionStats.d.ts +89 -0
- package/dist/events/sessionStats.d.ts.map +1 -0
- package/dist/events/sessionStats.js +264 -0
- package/dist/import/claudeImport.d.ts +44 -0
- package/dist/import/claudeImport.d.ts.map +1 -0
- package/dist/import/claudeImport.js +337 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -0
- package/dist/mcp/manager.d.ts +8 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +64 -57
- package/dist/permissions/destructiveTokens.d.ts +1 -0
- package/dist/permissions/destructiveTokens.d.ts.map +1 -1
- package/dist/permissions/destructiveTokens.js +93 -0
- package/dist/permissions/rules.d.ts +12 -0
- package/dist/permissions/rules.d.ts.map +1 -1
- package/dist/permissions/rules.js +89 -7
- package/dist/plugins/configStore.d.ts +43 -0
- package/dist/plugins/configStore.d.ts.map +1 -0
- package/dist/plugins/configStore.js +253 -0
- package/dist/plugins/files.d.ts +10 -0
- package/dist/plugins/files.d.ts.map +1 -0
- package/dist/plugins/files.js +85 -0
- package/dist/plugins/index.d.ts +3 -22
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +30 -40
- package/dist/plugins/resolve.d.ts +18 -0
- package/dist/plugins/resolve.d.ts.map +1 -0
- package/dist/plugins/resolve.js +152 -0
- package/dist/plugins/userConfig.d.ts +90 -0
- package/dist/plugins/userConfig.d.ts.map +1 -0
- package/dist/plugins/userConfig.js +332 -0
- package/dist/plugins/validate.d.ts +14 -0
- package/dist/plugins/validate.d.ts.map +1 -0
- package/dist/plugins/validate.js +222 -0
- package/dist/project/projectCommands.d.ts +33 -0
- package/dist/project/projectCommands.d.ts.map +1 -0
- package/dist/project/projectCommands.js +246 -0
- package/dist/search/conversationTurns.d.ts +53 -0
- package/dist/search/conversationTurns.d.ts.map +1 -0
- package/dist/search/conversationTurns.js +170 -0
- package/dist/search/globalSearch.d.ts +144 -0
- package/dist/search/globalSearch.d.ts.map +1 -0
- package/dist/search/globalSearch.js +350 -0
- package/dist/share/sessionChannel.d.ts +136 -0
- package/dist/share/sessionChannel.d.ts.map +1 -0
- package/dist/share/sessionChannel.js +373 -0
- package/dist/share/sessionShare.d.ts +104 -0
- package/dist/share/sessionShare.d.ts.map +1 -0
- package/dist/share/sessionShare.js +336 -0
- package/dist/ui/configCmd.d.ts +48 -0
- package/dist/ui/configCmd.d.ts.map +1 -0
- package/dist/ui/configCmd.js +146 -0
- package/dist/ui/deployPanel.d.ts +91 -0
- package/dist/ui/deployPanel.d.ts.map +1 -0
- package/dist/ui/deployPanel.js +442 -0
- package/dist/ui/mergeAssistant.d.ts +120 -0
- package/dist/ui/mergeAssistant.d.ts.map +1 -0
- package/dist/ui/mergeAssistant.js +391 -0
- package/dist/ui/notify.d.ts +114 -0
- package/dist/ui/notify.d.ts.map +1 -0
- package/dist/ui/notify.js +235 -0
- package/dist/ui/promptSuggest.d.ts +56 -0
- package/dist/ui/promptSuggest.d.ts.map +1 -0
- package/dist/ui/promptSuggest.js +160 -0
- package/dist/ui/taskGraph.d.ts +44 -0
- package/dist/ui/taskGraph.d.ts.map +1 -0
- package/dist/ui/taskGraph.js +289 -0
- package/dist/util/addDir.d.ts +31 -0
- package/dist/util/addDir.d.ts.map +1 -0
- package/dist/util/addDir.js +124 -0
- package/dist/util/batch.d.ts +16 -0
- package/dist/util/batch.d.ts.map +1 -0
- package/dist/util/batch.js +92 -0
- package/dist/util/cellWidth.d.ts +12 -0
- package/dist/util/cellWidth.d.ts.map +1 -0
- package/dist/util/cellWidth.js +161 -0
- package/dist/util/duration.d.ts +8 -0
- package/dist/util/duration.d.ts.map +1 -0
- package/dist/util/duration.js +50 -0
- package/dist/util/loopJobs.d.ts +67 -0
- package/dist/util/loopJobs.d.ts.map +1 -0
- package/dist/util/loopJobs.js +169 -0
- package/dist/util/settingsFile.d.ts +14 -0
- package/dist/util/settingsFile.d.ts.map +1 -0
- package/dist/util/settingsFile.js +99 -0
- package/dist/util/toolLabel.d.ts +2 -0
- package/dist/util/toolLabel.d.ts.map +1 -0
- package/dist/util/toolLabel.js +17 -0
- package/dist/verify/failureTimeline.d.ts +100 -0
- package/dist/verify/failureTimeline.d.ts.map +1 -0
- package/dist/verify/failureTimeline.js +253 -0
- package/dist/verify/verification.d.ts +75 -0
- package/dist/verify/verification.d.ts.map +1 -0
- package/dist/verify/verification.js +315 -0
- package/package.json +5 -4
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export interface Retest {
|
|
2
|
+
at: number;
|
|
3
|
+
/** The command as typed, clipped for display. */
|
|
4
|
+
command: string;
|
|
5
|
+
ok: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface Incident {
|
|
8
|
+
id: number;
|
|
9
|
+
/** The command that first failed, clipped. */
|
|
10
|
+
command: string;
|
|
11
|
+
/** Process exit code when the tool reported one. */
|
|
12
|
+
exitCode: number | null;
|
|
13
|
+
at: number;
|
|
14
|
+
/** Tool-output store entry holding this command's full output, for Ctrl+O. */
|
|
15
|
+
outputId?: number;
|
|
16
|
+
/** Files a mutating tool touched while this incident was open, first-touch order. */
|
|
17
|
+
files: string[];
|
|
18
|
+
/** Same-family commands run afterwards, newest last. */
|
|
19
|
+
retests: Retest[];
|
|
20
|
+
/** Set when a same-family command exited 0. */
|
|
21
|
+
resolvedAt?: number;
|
|
22
|
+
}
|
|
23
|
+
/** Incidents kept per session. Older ones are dropped — a failure from an hour ago is noise. */
|
|
24
|
+
export declare const MAX_INCIDENTS = 10;
|
|
25
|
+
/** Files listed per incident. */
|
|
26
|
+
export declare const MAX_FILES = 6;
|
|
27
|
+
/** Retests kept per incident. */
|
|
28
|
+
export declare const MAX_RETESTS = 6;
|
|
29
|
+
/** Command text kept for display. */
|
|
30
|
+
export declare const MAX_CMD_CHARS = 72;
|
|
31
|
+
/**
|
|
32
|
+
* Do two commands belong to the same "run" of the same check?
|
|
33
|
+
*
|
|
34
|
+
* Needed because a fix is followed by a re-run that is rarely byte-identical:
|
|
35
|
+
* `npm test` → `npm test -- --watch=false`, `npm test` → `npm run test`, `pytest` →
|
|
36
|
+
* `pytest -x tests/test_x.py`. Comparing the first token alone would call `npm install`
|
|
37
|
+
* a re-run of `npm test`; comparing whole strings would miss every one of those.
|
|
38
|
+
*
|
|
39
|
+
* Rule: the first token must match (the tool — npm/npm, pytest/pytest) AND the commands
|
|
40
|
+
* must share at least one non-flag word OR an identical second token. `npm run test` vs
|
|
41
|
+
* `npm test` share the literal word `test`; `npm install` shares nothing with `npm test`
|
|
42
|
+
* except the tool, so it stays a different command.
|
|
43
|
+
*/
|
|
44
|
+
export declare function sameCommandFamily(a: string, b: string): boolean;
|
|
45
|
+
export interface NoteBashOptions {
|
|
46
|
+
/** Process exit code when the tool reported one; undefined for non-bash paths. */
|
|
47
|
+
exitCode?: number | null;
|
|
48
|
+
/** Tool-output store entry id, so the rescue can point at the full text. */
|
|
49
|
+
outputId?: number;
|
|
50
|
+
/** Current time — injected so the chain is testable. */
|
|
51
|
+
at: number;
|
|
52
|
+
}
|
|
53
|
+
export declare function isCheckCommand(command: string): boolean;
|
|
54
|
+
export declare class FailureLedger {
|
|
55
|
+
incidents: Incident[];
|
|
56
|
+
private nextId;
|
|
57
|
+
/**
|
|
58
|
+
* Record one main-agent bash result. A non-zero exit (or an explicit `error` that is
|
|
59
|
+
* not an interrupt — timeout, spawn failure) either extends the open incident of the
|
|
60
|
+
* same command family or opens a new one; a zero exit resolves the open incident of
|
|
61
|
+
* that family.
|
|
62
|
+
*
|
|
63
|
+
* Interrupts are deliberately NOT failures: "Command stopped by user" is something the
|
|
64
|
+
* user chose, and offering to "fix" it would be nonsense.
|
|
65
|
+
*/
|
|
66
|
+
noteBash(command: string, ok: boolean, opts: NoteBashOptions): void;
|
|
67
|
+
/** A mutating tool touched `path` — recorded only while something is actually broken. */
|
|
68
|
+
noteEdit(path: string): void;
|
|
69
|
+
/** The most recent incident with no successful same-family re-run. */
|
|
70
|
+
lastUnresolved(): Incident | undefined;
|
|
71
|
+
reset(): void;
|
|
72
|
+
private openFor;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The §65 chain, one line per event, newest last:
|
|
76
|
+
*
|
|
77
|
+
* ✗ npm test — exit 1 · 2m 4s in
|
|
78
|
+
* ↳ edited src/util.ts, src/expr.ts
|
|
79
|
+
* ↳ re-ran npm test — still failing
|
|
80
|
+
* ✓ npm test — passed after 4m 10s
|
|
81
|
+
*
|
|
82
|
+
* Relative offsets, not wall-clock times: the transcript has no timestamps anywhere
|
|
83
|
+
* else, so "2m 4s in" is the only reading that means anything in context.
|
|
84
|
+
*/
|
|
85
|
+
export declare function formatTimeline(inc: Incident, cols: number): string[];
|
|
86
|
+
/**
|
|
87
|
+
* The one line that names the rescue, printed under the timeline. `/failures` re-prints
|
|
88
|
+
* the whole chain later; Ctrl+O shows the failing command's full output (it is the last
|
|
89
|
+
* tool output when the turn ended on it); Ctrl+G opens a file.
|
|
90
|
+
*/
|
|
91
|
+
export declare function rescueHint(inc: Incident, cols: number): string;
|
|
92
|
+
/**
|
|
93
|
+
* The message Ctrl+F sends. It is NOT a pretend user message: it names itself, states the
|
|
94
|
+
* command and its exit code, and includes the last lines of the real output — so the
|
|
95
|
+
* model's next step is grounded in the failure even after a compaction dropped the body.
|
|
96
|
+
*/
|
|
97
|
+
export declare function rescuePrompt(inc: Incident, outputTail: string, maxChars?: number): string;
|
|
98
|
+
/** `/failures` listing: every incident, newest first, with the chain inline. */
|
|
99
|
+
export declare function formatIncidents(ledger: FailureLedger, cols: number): string[];
|
|
100
|
+
//# sourceMappingURL=failureTimeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failureTimeline.d.ts","sourceRoot":"","sources":["../../src/verify/failureTimeline.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wDAAwD;IACxD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gGAAgG;AAChG,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,iCAAiC;AACjC,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,iCAAiC;AACjC,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,qCAAqC;AACrC,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAmB/D;AAED,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;CACZ;AAmBD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAUvD;AAED,qBAAa,aAAa;IACxB,SAAS,EAAE,QAAQ,EAAE,CAAM;IAC3B,OAAO,CAAC,MAAM,CAAK;IAEnB;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI;IA2BnE,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO5B,sEAAsE;IACtE,cAAc,IAAI,QAAQ,GAAG,SAAS;IAOtC,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,OAAO;CAOhB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBpE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,SAAO,GAAG,MAAM,CAkBvF;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAO7E"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// The failure timeline (spec §65) and the one-key rescue (spec §64).
|
|
3
|
+
//
|
|
4
|
+
// WHY: a failing command in the middle of an agentic turn scrolls past like everything
|
|
5
|
+
// else. Ten minutes later the user sees "✗ 2 files changed" and has no idea whether the
|
|
6
|
+
// tests they care about ever passed, or whether the fix the agent attempted worked. The
|
|
7
|
+
// turn summary already reports test outcomes (turnSummary.ts), but it is ONE line: it
|
|
8
|
+
// cannot show that a test failed, the agent edited two files, re-ran it, and it passed.
|
|
9
|
+
//
|
|
10
|
+
// This ledger watches the tool events the CLI already receives and reconstructs that
|
|
11
|
+
// chain: which command failed, what was edited while it was broken, and when (if ever) a
|
|
12
|
+
// same-family command came back green. Everything in it is OBSERVED — a command's exit
|
|
13
|
+
// code, a file tool's path — nothing is inferred from the model's prose, so a chain is
|
|
14
|
+
// never claimed for a fix that did not happen.
|
|
15
|
+
//
|
|
16
|
+
// The same ledger answers "what do I do now?" (§64): the last unresolved incident can be
|
|
17
|
+
// handed back to the model with one keystroke (Ctrl+F), because the failing output is
|
|
18
|
+
// already in its context.
|
|
19
|
+
//
|
|
20
|
+
// Pure (no console, no chalk, no fs) so the chain rules and the line-width behaviour are
|
|
21
|
+
// unit-testable — see test/failureTimeline.test.ts.
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.FailureLedger = exports.MAX_CMD_CHARS = exports.MAX_RETESTS = exports.MAX_FILES = exports.MAX_INCIDENTS = void 0;
|
|
24
|
+
exports.sameCommandFamily = sameCommandFamily;
|
|
25
|
+
exports.isCheckCommand = isCheckCommand;
|
|
26
|
+
exports.formatTimeline = formatTimeline;
|
|
27
|
+
exports.rescueHint = rescueHint;
|
|
28
|
+
exports.rescuePrompt = rescuePrompt;
|
|
29
|
+
exports.formatIncidents = formatIncidents;
|
|
30
|
+
const duration_1 = require("../util/duration");
|
|
31
|
+
/** Incidents kept per session. Older ones are dropped — a failure from an hour ago is noise. */
|
|
32
|
+
exports.MAX_INCIDENTS = 10;
|
|
33
|
+
/** Files listed per incident. */
|
|
34
|
+
exports.MAX_FILES = 6;
|
|
35
|
+
/** Retests kept per incident. */
|
|
36
|
+
exports.MAX_RETESTS = 6;
|
|
37
|
+
/** Command text kept for display. */
|
|
38
|
+
exports.MAX_CMD_CHARS = 72;
|
|
39
|
+
/**
|
|
40
|
+
* Do two commands belong to the same "run" of the same check?
|
|
41
|
+
*
|
|
42
|
+
* Needed because a fix is followed by a re-run that is rarely byte-identical:
|
|
43
|
+
* `npm test` → `npm test -- --watch=false`, `npm test` → `npm run test`, `pytest` →
|
|
44
|
+
* `pytest -x tests/test_x.py`. Comparing the first token alone would call `npm install`
|
|
45
|
+
* a re-run of `npm test`; comparing whole strings would miss every one of those.
|
|
46
|
+
*
|
|
47
|
+
* Rule: the first token must match (the tool — npm/npm, pytest/pytest) AND the commands
|
|
48
|
+
* must share at least one non-flag word OR an identical second token. `npm run test` vs
|
|
49
|
+
* `npm test` share the literal word `test`; `npm install` shares nothing with `npm test`
|
|
50
|
+
* except the tool, so it stays a different command.
|
|
51
|
+
*/
|
|
52
|
+
function sameCommandFamily(a, b) {
|
|
53
|
+
const words = (s) => s
|
|
54
|
+
.trim()
|
|
55
|
+
.split(/\s+/)
|
|
56
|
+
.filter((t) => t && !t.startsWith('-'))
|
|
57
|
+
.map((t) => t.replace(/^['"]|['"]$/g, ''));
|
|
58
|
+
const rawCount = (s) => s.trim().split(/\s+/).filter(Boolean).length;
|
|
59
|
+
const A = words(a);
|
|
60
|
+
const B = words(b);
|
|
61
|
+
if (!A.length || !B.length)
|
|
62
|
+
return false;
|
|
63
|
+
if (A[0] !== B[0])
|
|
64
|
+
return false;
|
|
65
|
+
// A BARE tool (`pytest`, then `pytest -x tests/test_x.py`) has no word to share, so the
|
|
66
|
+
// tool name is the only signal there is. `npm --silent` does not qualify as bare: it
|
|
67
|
+
// carries a flag, and flags never establish a family.
|
|
68
|
+
if (rawCount(a) === 1 || rawCount(b) === 1)
|
|
69
|
+
return true;
|
|
70
|
+
if (A[1] !== undefined && A[1] === B[1])
|
|
71
|
+
return true;
|
|
72
|
+
const rest = new Set(A.slice(1));
|
|
73
|
+
return B.slice(1).some((t) => rest.has(t));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Commands a user reads as "a check": test / build / lint / typecheck. Only these may
|
|
77
|
+
* open an incident — `grep foo` exiting 1 means "not found", `diff` exits 1 when files
|
|
78
|
+
* differ, and a rescue banner over either of those would be noise on every turn.
|
|
79
|
+
*
|
|
80
|
+
* Wider than turnSummary's TEST_RE on purpose: that one answers "should the summary
|
|
81
|
+
* claim tests ran", this answers "is this a verification the user is waiting on".
|
|
82
|
+
* Script names are matched by WORD so custom ones work (`npm run verify:all`,
|
|
83
|
+
* `npm run typecheck:ci`) while `npm run dev` never counts.
|
|
84
|
+
*/
|
|
85
|
+
const BINARY_RE = /(^|[\s;&|])(jest|vitest|mocha|pytest|tsc|vue-tsc|mypy|pyright|eslint|oxlint|stylelint|biome|golangci-lint|staticcheck|shellcheck|hadolint|rubocop|swiftlint|ktlint|detekt|phpcs|phpstan|psalm|cppcheck|clang-tidy|luacheck|svelte-check|phpunit|rspec|xcodebuild|gradle)(\s|$)|(^|[\s;&|])(go (test|build|vet)|cargo (test|check|build|clippy)|python3? -m (pytest|unittest)|node --test|make|mvn (test|verify|compile)|dotnet (test|build)|swift (test|build)|ruff check|(playwright|cypress|newman) (test|run)|webpack|rollup|esbuild|parcel|(vite|next|ng|nx) (build|bundle|compile))(\s|$)/i;
|
|
86
|
+
const RUNNER_RE = /\b(?:npm|yarn|pnpm|bun)(?:\s+run)?\s+([^\s;&|]+)/gi;
|
|
87
|
+
// `t` is npm's own shorthand for `test`, and the boundary rule keeps `npm ts`/`npm tw` out.
|
|
88
|
+
const SCRIPT_WORD_RE = /(^|[:.-])(t|test|tests|lint|build|check|typecheck|types|verify|e2e|ci|coverage|validate)([:.-]|$)/i;
|
|
89
|
+
function isCheckCommand(command) {
|
|
90
|
+
const cmd = command.trim();
|
|
91
|
+
if (!cmd)
|
|
92
|
+
return false;
|
|
93
|
+
if (BINARY_RE.test(cmd))
|
|
94
|
+
return true;
|
|
95
|
+
RUNNER_RE.lastIndex = 0; // global regex: state persists between calls
|
|
96
|
+
let m;
|
|
97
|
+
while ((m = RUNNER_RE.exec(cmd)) !== null) {
|
|
98
|
+
if (SCRIPT_WORD_RE.test(m[1]))
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
class FailureLedger {
|
|
104
|
+
constructor() {
|
|
105
|
+
this.incidents = [];
|
|
106
|
+
this.nextId = 1;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Record one main-agent bash result. A non-zero exit (or an explicit `error` that is
|
|
110
|
+
* not an interrupt — timeout, spawn failure) either extends the open incident of the
|
|
111
|
+
* same command family or opens a new one; a zero exit resolves the open incident of
|
|
112
|
+
* that family.
|
|
113
|
+
*
|
|
114
|
+
* Interrupts are deliberately NOT failures: "Command stopped by user" is something the
|
|
115
|
+
* user chose, and offering to "fix" it would be nonsense.
|
|
116
|
+
*/
|
|
117
|
+
noteBash(command, ok, opts) {
|
|
118
|
+
const cmd = command.replace(/\s+/g, ' ').trim();
|
|
119
|
+
if (!cmd)
|
|
120
|
+
return;
|
|
121
|
+
if (ok) {
|
|
122
|
+
const open = this.openFor(cmd);
|
|
123
|
+
if (open)
|
|
124
|
+
open.resolvedAt = opts.at;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const clipped = cmd.length > exports.MAX_CMD_CHARS ? cmd.slice(0, exports.MAX_CMD_CHARS - 1) + '…' : cmd;
|
|
128
|
+
const open = this.openFor(cmd);
|
|
129
|
+
if (open) {
|
|
130
|
+
open.retests.push({ at: opts.at, command: clipped, ok: false });
|
|
131
|
+
if (open.retests.length > exports.MAX_RETESTS)
|
|
132
|
+
open.retests.splice(0, open.retests.length - exports.MAX_RETESTS);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this.incidents.push({
|
|
136
|
+
id: this.nextId++,
|
|
137
|
+
command: clipped,
|
|
138
|
+
exitCode: opts.exitCode ?? null,
|
|
139
|
+
at: opts.at,
|
|
140
|
+
outputId: opts.outputId,
|
|
141
|
+
files: [],
|
|
142
|
+
retests: [],
|
|
143
|
+
});
|
|
144
|
+
if (this.incidents.length > exports.MAX_INCIDENTS)
|
|
145
|
+
this.incidents.splice(0, this.incidents.length - exports.MAX_INCIDENTS);
|
|
146
|
+
}
|
|
147
|
+
/** A mutating tool touched `path` — recorded only while something is actually broken. */
|
|
148
|
+
noteEdit(path) {
|
|
149
|
+
const open = this.lastUnresolved();
|
|
150
|
+
if (!open || !path)
|
|
151
|
+
return;
|
|
152
|
+
if (!open.files.includes(path))
|
|
153
|
+
open.files.push(path);
|
|
154
|
+
if (open.files.length > exports.MAX_FILES)
|
|
155
|
+
open.files.splice(0, open.files.length - exports.MAX_FILES);
|
|
156
|
+
}
|
|
157
|
+
/** The most recent incident with no successful same-family re-run. */
|
|
158
|
+
lastUnresolved() {
|
|
159
|
+
for (let i = this.incidents.length - 1; i >= 0; i--) {
|
|
160
|
+
if (!this.incidents[i].resolvedAt)
|
|
161
|
+
return this.incidents[i];
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
reset() {
|
|
166
|
+
this.incidents = [];
|
|
167
|
+
this.nextId = 1;
|
|
168
|
+
}
|
|
169
|
+
openFor(cmd) {
|
|
170
|
+
for (let i = this.incidents.length - 1; i >= 0; i--) {
|
|
171
|
+
const inc = this.incidents[i];
|
|
172
|
+
if (!inc.resolvedAt && sameCommandFamily(inc.command, cmd))
|
|
173
|
+
return inc;
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.FailureLedger = FailureLedger;
|
|
179
|
+
/**
|
|
180
|
+
* The §65 chain, one line per event, newest last:
|
|
181
|
+
*
|
|
182
|
+
* ✗ npm test — exit 1 · 2m 4s in
|
|
183
|
+
* ↳ edited src/util.ts, src/expr.ts
|
|
184
|
+
* ↳ re-ran npm test — still failing
|
|
185
|
+
* ✓ npm test — passed after 4m 10s
|
|
186
|
+
*
|
|
187
|
+
* Relative offsets, not wall-clock times: the transcript has no timestamps anywhere
|
|
188
|
+
* else, so "2m 4s in" is the only reading that means anything in context.
|
|
189
|
+
*/
|
|
190
|
+
function formatTimeline(inc, cols) {
|
|
191
|
+
const clip = (s) => (cols > 8 && s.length > cols ? s.slice(0, cols - 1) + '…' : s);
|
|
192
|
+
const lines = [];
|
|
193
|
+
const exit = inc.exitCode !== null ? `exit ${inc.exitCode}` : 'failed';
|
|
194
|
+
const resolved = inc.resolvedAt !== undefined;
|
|
195
|
+
lines.push(clip(`✗ ${inc.command} — ${exit}`));
|
|
196
|
+
if (inc.files.length)
|
|
197
|
+
lines.push(clip(` ↳ edited ${inc.files.join(', ')}`));
|
|
198
|
+
for (const r of inc.retests) {
|
|
199
|
+
lines.push(clip(` ↳ re-ran ${r.command} — still failing`));
|
|
200
|
+
}
|
|
201
|
+
if (resolved) {
|
|
202
|
+
const took = (0, duration_1.formatDuration)(inc.resolvedAt - inc.at);
|
|
203
|
+
lines.push(clip(` ✓ ${inc.command} — passed after ${took}`));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
lines.push(clip(` · still failing — nothing has re-run it successfully since`));
|
|
207
|
+
}
|
|
208
|
+
return lines;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The one line that names the rescue, printed under the timeline. `/failures` re-prints
|
|
212
|
+
* the whole chain later; Ctrl+O shows the failing command's full output (it is the last
|
|
213
|
+
* tool output when the turn ended on it); Ctrl+G opens a file.
|
|
214
|
+
*/
|
|
215
|
+
function rescueHint(inc, cols) {
|
|
216
|
+
const s = `Ctrl+F fix it automatically · Ctrl+O full output${inc.files.length ? ' · Ctrl+G open a file' : ''} · /failures again later`;
|
|
217
|
+
return cols > 8 && s.length > cols ? s.slice(0, cols - 1) + '…' : s;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The message Ctrl+F sends. It is NOT a pretend user message: it names itself, states the
|
|
221
|
+
* command and its exit code, and includes the last lines of the real output — so the
|
|
222
|
+
* model's next step is grounded in the failure even after a compaction dropped the body.
|
|
223
|
+
*/
|
|
224
|
+
function rescuePrompt(inc, outputTail, maxChars = 3000) {
|
|
225
|
+
const exit = inc.exitCode !== null ? ` (exit ${inc.exitCode})` : '';
|
|
226
|
+
const edited = inc.files.length ? `\nFiles touched since it broke: ${inc.files.join(', ')}.` : '';
|
|
227
|
+
const tail = outputTail.trim();
|
|
228
|
+
let body = `[failure rescue] The command \`${inc.command}\` failed${exit}${edited}\n\nFix it, re-run the same command to confirm, and continue.`;
|
|
229
|
+
if (tail) {
|
|
230
|
+
// The budget covers the fences too — they are part of what the model receives, and
|
|
231
|
+
// forgetting them is how a "600 char" limit shipped 637. The TAIL is what survives:
|
|
232
|
+
// the last line of a failing test run is the one that states the failure.
|
|
233
|
+
const open = '\n\nLast lines of the failure:\n```\n';
|
|
234
|
+
const close = '\n```';
|
|
235
|
+
const budget = maxChars - body.length - open.length - close.length;
|
|
236
|
+
if (budget > 0) {
|
|
237
|
+
const clipped = tail.length > budget ? tail.slice(tail.length - budget) : tail;
|
|
238
|
+
body += open + clipped + close;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return body;
|
|
242
|
+
}
|
|
243
|
+
/** `/failures` listing: every incident, newest first, with the chain inline. */
|
|
244
|
+
function formatIncidents(ledger, cols) {
|
|
245
|
+
if (!ledger.incidents.length)
|
|
246
|
+
return ['No failing commands this session.'];
|
|
247
|
+
const out = [];
|
|
248
|
+
for (const inc of [...ledger.incidents].reverse()) {
|
|
249
|
+
out.push(...formatTimeline(inc, cols));
|
|
250
|
+
}
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=failureTimeline.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export type CheckKind = 'typecheck' | 'unit' | 'integration' | 'lint' | 'build';
|
|
2
|
+
export type CriterionId = CheckKind | 'diff';
|
|
3
|
+
/** Canonical order — the panel, the criteria list and the summary all use it. */
|
|
4
|
+
export declare const CHECK_KINDS: CheckKind[];
|
|
5
|
+
export declare const CRITERION_LABELS: Record<CriterionId, string>;
|
|
6
|
+
/**
|
|
7
|
+
* The kind of verification a check command performs, or undefined when it is a check
|
|
8
|
+
* (`npm run verify`, a bare `make ci`) whose kind cannot honestly be told apart.
|
|
9
|
+
*
|
|
10
|
+
* Scanned segment by segment so `cd app && npm test` classifies as `unit`, and the first
|
|
11
|
+
* segment that classifies wins (`npm run build && npm test` is a build run that also
|
|
12
|
+
* tested — calling it `unit` would hide the build, which is what the user is waiting on).
|
|
13
|
+
*/
|
|
14
|
+
export declare function classifyCheck(command: string): CheckKind | undefined;
|
|
15
|
+
export interface CheckRun {
|
|
16
|
+
kind: CheckKind;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
/** The command as typed, clipped for display. */
|
|
19
|
+
command: string;
|
|
20
|
+
at: number;
|
|
21
|
+
/** Test count parsed from the output, when the runner stated one. */
|
|
22
|
+
testsPassed?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class VerificationLedger {
|
|
25
|
+
private runs;
|
|
26
|
+
private diff;
|
|
27
|
+
/** Record one check result. The LATEST run of a kind wins — the panel is "as of now". */
|
|
28
|
+
note(kind: CheckKind, ok: boolean, command: string, at: number, testsPassed?: number): void;
|
|
29
|
+
/** `/diff` printed the changeset: the "reviewed the diff" box is now honestly tickable. */
|
|
30
|
+
noteDiffReviewed(): void;
|
|
31
|
+
get(kind: CheckKind): CheckRun | undefined;
|
|
32
|
+
/** Runs recorded at or after `at` — "what this TURN verified", for the §84 block. */
|
|
33
|
+
runsSince(at: number): CheckRun[];
|
|
34
|
+
reviewedDiff(): boolean;
|
|
35
|
+
reset(): void;
|
|
36
|
+
}
|
|
37
|
+
/** The most recent `N passed` a test runner printed, e.g. "12 passed", "2,341 tests passed". */
|
|
38
|
+
export declare function parseTestsPassed(output: string): number | undefined;
|
|
39
|
+
export interface ParsedCriteria {
|
|
40
|
+
/** Present when the argument SET new criteria (may be empty if every word was valid but repeated). */
|
|
41
|
+
criteria?: CriterionId[];
|
|
42
|
+
clear?: boolean;
|
|
43
|
+
unknown: string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `/accept unittests typecheck` — the words a user would naturally type. Anything not
|
|
47
|
+
* understood is reported instead of silently dropped: a criterion the user thinks is
|
|
48
|
+
* armed but is not would make the panel lie by omission.
|
|
49
|
+
*/
|
|
50
|
+
export declare function parseCriteria(arg: string): ParsedCriteria;
|
|
51
|
+
export declare function canonicalOrder(ids: CriterionId[]): CriterionId[];
|
|
52
|
+
export type CriterionStatus = 'pass' | 'fail' | 'pending';
|
|
53
|
+
export declare function criterionStatus(ledger: VerificationLedger, id: CriterionId): CriterionStatus;
|
|
54
|
+
export declare function unmetCriteria(ledger: VerificationLedger, criteria: CriterionId[]): CriterionId[];
|
|
55
|
+
/**
|
|
56
|
+
* §83. With criteria declared, every criterion gets a line — the point of declaring them is
|
|
57
|
+
* to see the ones that have NOT passed. Without criteria, only what actually ran is shown:
|
|
58
|
+
* a fixed list of five partly-empty rows would train the user to ignore it.
|
|
59
|
+
*/
|
|
60
|
+
export declare function formatVerificationPanel(ledger: VerificationLedger, criteria: CriterionId[], opts: {
|
|
61
|
+
cols: number;
|
|
62
|
+
filesChanged?: number;
|
|
63
|
+
}): string[];
|
|
64
|
+
/**
|
|
65
|
+
* §82, one line for the END OF A TURN: "the agent should not claim completion until the
|
|
66
|
+
* configured checks pass". The CLI cannot police the model's prose, but it can make the
|
|
67
|
+
* unmet criteria impossible to miss — and say nothing at all when everything passed.
|
|
68
|
+
*
|
|
69
|
+
* Returns undefined when there is nothing worth saying (no criteria, all met, or — for
|
|
70
|
+
* the met case — `announce` is false because the panel already showed it).
|
|
71
|
+
*/
|
|
72
|
+
export declare function criteriaLine(ledger: VerificationLedger, criteria: CriterionId[], cols: number, announceMet?: boolean): string | undefined;
|
|
73
|
+
/** `/accept` with no argument: what is declared, and where each one stands. */
|
|
74
|
+
export declare function formatCriteriaList(ledger: VerificationLedger, criteria: CriterionId[], cols: number): string[];
|
|
75
|
+
//# sourceMappingURL=verification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../src/verify/verification.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,aAAa,GAAG,MAAM,GAAG,OAAO,CAAC;AAChF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7C,iFAAiF;AACjF,eAAO,MAAM,WAAW,EAAE,SAAS,EAA0D,CAAC;AAE9F,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAOxD,CAAC;AAuBF;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAOpE;AAoDD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;IACZ,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,IAAI,CAAkC;IAC9C,OAAO,CAAC,IAAI,CAAS;IAErB,yFAAyF;IACzF,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAO3F,2FAA2F;IAC3F,gBAAgB,IAAI,IAAI;IAIxB,GAAG,CAAC,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS;IAI1C,qFAAqF;IACrF,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,EAAE;IAMjC,YAAY,IAAI,OAAO;IAIvB,KAAK,IAAI,IAAI;CAId;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKnE;AAgCD,MAAM,WAAW,cAAc;IAC7B,sGAAsG;IACtG,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAazD;AAID,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAEhE;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1D,wBAAgB,eAAe,CAAC,MAAM,EAAE,kBAAkB,EAAE,EAAE,EAAE,WAAW,GAAG,eAAe,CAK5F;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAEhG;AAMD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,WAAW,EAAE,EACvB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,MAAM,EAAE,CA8CV;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,WAAW,EAAE,EACvB,IAAI,EAAE,MAAM,EACZ,WAAW,UAAQ,GAClB,MAAM,GAAG,SAAS,CAUpB;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAoB9G"}
|