@delegance/claude-autopilot 5.2.2 → 6.2.2
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 +1027 -1
- package/README.md +104 -17
- package/dist/src/adapters/council/claude.js +2 -1
- package/dist/src/adapters/council/openai.js +14 -7
- package/dist/src/adapters/deploy/_http.d.ts +43 -0
- package/dist/src/adapters/deploy/_http.js +99 -0
- package/dist/src/adapters/deploy/fly.d.ts +206 -0
- package/dist/src/adapters/deploy/fly.js +696 -0
- package/dist/src/adapters/deploy/generic.d.ts +39 -0
- package/dist/src/adapters/deploy/generic.js +98 -0
- package/dist/src/adapters/deploy/index.d.ts +15 -0
- package/dist/src/adapters/deploy/index.js +78 -0
- package/dist/src/adapters/deploy/render.d.ts +181 -0
- package/dist/src/adapters/deploy/render.js +550 -0
- package/dist/src/adapters/deploy/types.d.ts +221 -0
- package/dist/src/adapters/deploy/types.js +15 -0
- package/dist/src/adapters/deploy/vercel.d.ts +143 -0
- package/dist/src/adapters/deploy/vercel.js +426 -0
- package/dist/src/adapters/pricing.d.ts +36 -0
- package/dist/src/adapters/pricing.js +40 -0
- package/dist/src/adapters/review-engine/claude.js +2 -1
- package/dist/src/adapters/review-engine/codex.js +12 -8
- package/dist/src/adapters/review-engine/gemini.js +2 -1
- package/dist/src/adapters/review-engine/openai-compatible.js +2 -1
- package/dist/src/adapters/sdk-loader.d.ts +15 -0
- package/dist/src/adapters/sdk-loader.js +77 -0
- package/dist/src/cli/autopilot.d.ts +71 -0
- package/dist/src/cli/autopilot.js +735 -0
- package/dist/src/cli/brainstorm.d.ts +23 -0
- package/dist/src/cli/brainstorm.js +131 -0
- package/dist/src/cli/costs.d.ts +15 -1
- package/dist/src/cli/costs.js +99 -10
- package/dist/src/cli/deploy.d.ts +71 -0
- package/dist/src/cli/deploy.js +539 -0
- package/dist/src/cli/fix.d.ts +18 -0
- package/dist/src/cli/fix.js +105 -11
- package/dist/src/cli/help-text.d.ts +52 -0
- package/dist/src/cli/help-text.js +400 -0
- package/dist/src/cli/implement.d.ts +91 -0
- package/dist/src/cli/implement.js +196 -0
- package/dist/src/cli/index.js +784 -222
- package/dist/src/cli/json-envelope.d.ts +187 -0
- package/dist/src/cli/json-envelope.js +270 -0
- package/dist/src/cli/json-mode.d.ts +33 -0
- package/dist/src/cli/json-mode.js +201 -0
- package/dist/src/cli/migrate.d.ts +111 -0
- package/dist/src/cli/migrate.js +305 -0
- package/dist/src/cli/plan.d.ts +81 -0
- package/dist/src/cli/plan.js +149 -0
- package/dist/src/cli/pr.d.ts +106 -0
- package/dist/src/cli/pr.js +191 -19
- package/dist/src/cli/preflight.js +102 -1
- package/dist/src/cli/review.d.ts +27 -0
- package/dist/src/cli/review.js +126 -0
- package/dist/src/cli/runs-watch-renderer.d.ts +45 -0
- package/dist/src/cli/runs-watch-renderer.js +275 -0
- package/dist/src/cli/runs-watch.d.ts +41 -0
- package/dist/src/cli/runs-watch.js +395 -0
- package/dist/src/cli/runs.d.ts +122 -0
- package/dist/src/cli/runs.js +902 -0
- package/dist/src/cli/scan.d.ts +93 -0
- package/dist/src/cli/scan.js +166 -40
- package/dist/src/cli/spec.d.ts +66 -0
- package/dist/src/cli/spec.js +132 -0
- package/dist/src/cli/validate.d.ts +29 -0
- package/dist/src/cli/validate.js +131 -0
- package/dist/src/core/config/schema.d.ts +43 -0
- package/dist/src/core/config/schema.js +25 -0
- package/dist/src/core/config/types.d.ts +17 -0
- package/dist/src/core/council/runner.d.ts +10 -1
- package/dist/src/core/council/runner.js +25 -3
- package/dist/src/core/council/types.d.ts +7 -0
- package/dist/src/core/errors.d.ts +1 -1
- package/dist/src/core/errors.js +12 -0
- package/dist/src/core/logging/redaction.d.ts +13 -0
- package/dist/src/core/logging/redaction.js +20 -0
- package/dist/src/core/migrate/detector-rules.js +6 -0
- package/dist/src/core/migrate/schema-validator.js +22 -1
- package/dist/src/core/phases/static-rules.d.ts +5 -1
- package/dist/src/core/phases/static-rules.js +2 -5
- package/dist/src/core/run-state/budget.d.ts +88 -0
- package/dist/src/core/run-state/budget.js +141 -0
- package/dist/src/core/run-state/cli-internal.d.ts +21 -0
- package/dist/src/core/run-state/cli-internal.js +174 -0
- package/dist/src/core/run-state/events.d.ts +59 -0
- package/dist/src/core/run-state/events.js +504 -0
- package/dist/src/core/run-state/lock.d.ts +61 -0
- package/dist/src/core/run-state/lock.js +206 -0
- package/dist/src/core/run-state/phase-context.d.ts +60 -0
- package/dist/src/core/run-state/phase-context.js +108 -0
- package/dist/src/core/run-state/phase-registry.d.ts +137 -0
- package/dist/src/core/run-state/phase-registry.js +162 -0
- package/dist/src/core/run-state/phase-runner.d.ts +80 -0
- package/dist/src/core/run-state/phase-runner.js +447 -0
- package/dist/src/core/run-state/provider-readback.d.ts +130 -0
- package/dist/src/core/run-state/provider-readback.js +426 -0
- package/dist/src/core/run-state/replay-decision.d.ts +69 -0
- package/dist/src/core/run-state/replay-decision.js +144 -0
- package/dist/src/core/run-state/resolve-engine.d.ts +100 -0
- package/dist/src/core/run-state/resolve-engine.js +190 -0
- package/dist/src/core/run-state/resume-preflight.d.ts +66 -0
- package/dist/src/core/run-state/resume-preflight.js +116 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.d.ts +73 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.js +186 -0
- package/dist/src/core/run-state/runs.d.ts +57 -0
- package/dist/src/core/run-state/runs.js +288 -0
- package/dist/src/core/run-state/snapshot.d.ts +14 -0
- package/dist/src/core/run-state/snapshot.js +114 -0
- package/dist/src/core/run-state/state.d.ts +40 -0
- package/dist/src/core/run-state/state.js +164 -0
- package/dist/src/core/run-state/types.d.ts +278 -0
- package/dist/src/core/run-state/types.js +13 -0
- package/dist/src/core/run-state/ulid.d.ts +11 -0
- package/dist/src/core/run-state/ulid.js +95 -0
- package/dist/src/core/schema-alignment/extractor/index.d.ts +1 -1
- package/dist/src/core/schema-alignment/extractor/index.js +2 -2
- package/dist/src/core/schema-alignment/extractor/prisma.d.ts +13 -1
- package/dist/src/core/schema-alignment/extractor/prisma.js +65 -10
- package/dist/src/core/schema-alignment/git-history.d.ts +19 -0
- package/dist/src/core/schema-alignment/git-history.js +53 -0
- package/dist/src/core/static-rules/rules/brand-tokens.js +2 -2
- package/dist/src/core/static-rules/rules/schema-alignment.js +14 -4
- package/package.json +9 -5
- package/scripts/autoregress.ts +3 -2
- package/skills/claude-autopilot.md +1 -1
- package/skills/make-interfaces-feel-better/SKILL.md +104 -0
- package/skills/migrate/SKILL.md +193 -47
- package/skills/simplify-ui/SKILL.md +103 -0
- package/skills/ui/SKILL.md +117 -0
- package/skills/ui-ux-pro-max/SKILL.md +90 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type RunState, type WriterId } from './types.ts';
|
|
2
|
+
/** Lowest `schema_version` value this binary can replay. Bump only on a
|
|
3
|
+
* major release that drops support for a prior wire shape. */
|
|
4
|
+
export declare const RUN_STATE_MIN_SUPPORTED_SCHEMA_VERSION: 1;
|
|
5
|
+
/** Highest `schema_version` value this binary can replay. Always equal to
|
|
6
|
+
* the writer's `RUN_STATE_SCHEMA_VERSION` — the writer never produces a
|
|
7
|
+
* newer shape than the reader on the same binary. */
|
|
8
|
+
export declare const RUN_STATE_MAX_SUPPORTED_SCHEMA_VERSION: 1;
|
|
9
|
+
export declare function statePath(runDir: string): string;
|
|
10
|
+
/** Write the snapshot atomically. Sequence:
|
|
11
|
+
* open(tmp, 'w') → write → fsync(fd) → close → rename → fsync(dirfd).
|
|
12
|
+
*
|
|
13
|
+
* If any step fails, the tmp file is best-effort-cleaned. The pre-existing
|
|
14
|
+
* state.json is untouched until the rename, so a crash anywhere before
|
|
15
|
+
* rename leaves the previous snapshot intact. */
|
|
16
|
+
export declare function writeStateSnapshot(runDir: string, state: RunState): void;
|
|
17
|
+
/** Read the snapshot. Returns null if missing. Throws GuardrailError(
|
|
18
|
+
* corrupted_state) if it's present but unparseable — recoverState() handles
|
|
19
|
+
* the fallback to events-replay. */
|
|
20
|
+
export declare function readStateSnapshot(runDir: string): RunState | null;
|
|
21
|
+
export interface RecoverStateOptions {
|
|
22
|
+
/** Writer that will own the recovery's `index.rebuilt` event. The
|
|
23
|
+
* caller must already hold the run's advisory lock. */
|
|
24
|
+
writerId: WriterId;
|
|
25
|
+
/** runId override; defaults to basename(runDir). */
|
|
26
|
+
runId?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RecoverStateResult {
|
|
29
|
+
state: RunState;
|
|
30
|
+
/** True if recovery actually re-derived the snapshot (vs. just reading
|
|
31
|
+
* a healthy one). */
|
|
32
|
+
recovered: boolean;
|
|
33
|
+
/** When `recovered === true`, the cause that triggered the rebuild. */
|
|
34
|
+
cause?: 'missing' | 'corrupt';
|
|
35
|
+
}
|
|
36
|
+
/** Open-or-recover the snapshot. If state.json is missing or corrupt, fall
|
|
37
|
+
* back to events.ndjson replay, persist the result, and emit
|
|
38
|
+
* `index.rebuilt`. The caller MUST already hold the advisory lock. */
|
|
39
|
+
export declare function recoverState(runDir: string, opts: RecoverStateOptions): RecoverStateResult;
|
|
40
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// src/core/run-state/state.ts
|
|
2
|
+
//
|
|
3
|
+
// Atomic snapshot writer for state.json. Persistence protocol per spec:
|
|
4
|
+
// 1. write to state.json.tmp
|
|
5
|
+
// 2. fsync(file) on the tmp
|
|
6
|
+
// 3. rename tmp → state.json
|
|
7
|
+
// 4. fsync(dir) so the rename is durable
|
|
8
|
+
//
|
|
9
|
+
// readStateSnapshot() returns null if the snapshot is missing (the canonical
|
|
10
|
+
// "fresh run" state) and throws if it's present-but-corrupt. recoverState()
|
|
11
|
+
// is the resilience entry point — it falls back to events.ndjson replay if
|
|
12
|
+
// the snapshot is unreadable, then rewrites a clean snapshot and emits an
|
|
13
|
+
// `index.rebuilt` event.
|
|
14
|
+
//
|
|
15
|
+
// Spec: docs/specs/v6-run-state-engine.md "Persistence protocol — Durable
|
|
16
|
+
// append", "Failure modes the user should never have to debug".
|
|
17
|
+
import * as fs from 'node:fs';
|
|
18
|
+
import * as path from 'node:path';
|
|
19
|
+
import { GuardrailError } from "../errors.js";
|
|
20
|
+
import { appendEvent, replayState } from "./events.js";
|
|
21
|
+
import { RUN_STATE_SCHEMA_VERSION } from "./types.js";
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// v6.2.2 — cache contract version policy (per spec / codex WARNING #1).
|
|
24
|
+
//
|
|
25
|
+
// `replayState()` uses these bounds to reject run dirs whose `schema_version`
|
|
26
|
+
// is outside the supported window. Strict equality would block resume across
|
|
27
|
+
// rolling deploys / mixed binary fleets — the window allows additive minor
|
|
28
|
+
// schema bumps to ship without breaking forward-read on older readers, and a
|
|
29
|
+
// future major (v7) resets `MIN_SUPPORTED` to break with the past explicitly.
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
/** Lowest `schema_version` value this binary can replay. Bump only on a
|
|
32
|
+
* major release that drops support for a prior wire shape. */
|
|
33
|
+
export const RUN_STATE_MIN_SUPPORTED_SCHEMA_VERSION = 1;
|
|
34
|
+
/** Highest `schema_version` value this binary can replay. Always equal to
|
|
35
|
+
* the writer's `RUN_STATE_SCHEMA_VERSION` — the writer never produces a
|
|
36
|
+
* newer shape than the reader on the same binary. */
|
|
37
|
+
export const RUN_STATE_MAX_SUPPORTED_SCHEMA_VERSION = RUN_STATE_SCHEMA_VERSION;
|
|
38
|
+
const STATE_FILE = 'state.json';
|
|
39
|
+
const STATE_TMP = 'state.json.tmp';
|
|
40
|
+
export function statePath(runDir) {
|
|
41
|
+
return path.join(runDir, STATE_FILE);
|
|
42
|
+
}
|
|
43
|
+
function tmpPath(runDir) {
|
|
44
|
+
return path.join(runDir, STATE_TMP);
|
|
45
|
+
}
|
|
46
|
+
/** Write the snapshot atomically. Sequence:
|
|
47
|
+
* open(tmp, 'w') → write → fsync(fd) → close → rename → fsync(dirfd).
|
|
48
|
+
*
|
|
49
|
+
* If any step fails, the tmp file is best-effort-cleaned. The pre-existing
|
|
50
|
+
* state.json is untouched until the rename, so a crash anywhere before
|
|
51
|
+
* rename leaves the previous snapshot intact. */
|
|
52
|
+
export function writeStateSnapshot(runDir, state) {
|
|
53
|
+
fs.mkdirSync(runDir, { recursive: true });
|
|
54
|
+
const data = JSON.stringify(state, null, 2);
|
|
55
|
+
const tmp = tmpPath(runDir);
|
|
56
|
+
const target = statePath(runDir);
|
|
57
|
+
const fd = fs.openSync(tmp, 'w');
|
|
58
|
+
let wroteOk = false;
|
|
59
|
+
try {
|
|
60
|
+
fs.writeSync(fd, data);
|
|
61
|
+
fs.fsyncSync(fd);
|
|
62
|
+
wroteOk = true;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
fs.closeSync(fd);
|
|
66
|
+
if (!wroteOk) {
|
|
67
|
+
try {
|
|
68
|
+
fs.unlinkSync(tmp);
|
|
69
|
+
}
|
|
70
|
+
catch { /* ignore */ }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
fs.renameSync(tmp, target);
|
|
74
|
+
// fsync the parent directory so the rename is durable on power-loss.
|
|
75
|
+
// On Linux/macOS this is best-effort (ENOTSUP on some filesystems for
|
|
76
|
+
// dir fds); we swallow expected platform-specific failures so a
|
|
77
|
+
// working-directory on tmpfs / SMB / etc. doesn't break the writer.
|
|
78
|
+
try {
|
|
79
|
+
const dirFd = fs.openSync(runDir, 'r');
|
|
80
|
+
try {
|
|
81
|
+
fs.fsyncSync(dirFd);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
fs.closeSync(dirFd);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const code = err.code;
|
|
89
|
+
// EISDIR happens on some Windows configs where opening a dir for read
|
|
90
|
+
// isn't permitted; EPERM/ENOTSUP on certain FS. We don't escalate —
|
|
91
|
+
// the rename itself is atomic; dir-fsync is a defense-in-depth.
|
|
92
|
+
if (code !== 'EISDIR' && code !== 'EPERM' && code !== 'ENOTSUP') {
|
|
93
|
+
// Anything else, surface as warning via a thrown GuardrailError so
|
|
94
|
+
// callers can decide. We choose `corrupted_state` as the closest
|
|
95
|
+
// category since the snapshot may not have been durably committed.
|
|
96
|
+
throw new GuardrailError(`state.json: dir fsync failed: ${err.message}`, {
|
|
97
|
+
code: 'corrupted_state',
|
|
98
|
+
provider: 'run-state',
|
|
99
|
+
details: { runDir, errno: code },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Read the snapshot. Returns null if missing. Throws GuardrailError(
|
|
105
|
+
* corrupted_state) if it's present but unparseable — recoverState() handles
|
|
106
|
+
* the fallback to events-replay. */
|
|
107
|
+
export function readStateSnapshot(runDir) {
|
|
108
|
+
const p = statePath(runDir);
|
|
109
|
+
if (!fs.existsSync(p))
|
|
110
|
+
return null;
|
|
111
|
+
const raw = fs.readFileSync(p, 'utf8');
|
|
112
|
+
if (!raw) {
|
|
113
|
+
throw new GuardrailError(`state.json: empty file`, {
|
|
114
|
+
code: 'corrupted_state',
|
|
115
|
+
provider: 'run-state',
|
|
116
|
+
details: { runDir },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
return JSON.parse(raw);
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
throw new GuardrailError(`state.json: corrupt JSON: ${err.message}`, {
|
|
124
|
+
code: 'corrupted_state',
|
|
125
|
+
provider: 'run-state',
|
|
126
|
+
details: { runDir, error: err.message },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Open-or-recover the snapshot. If state.json is missing or corrupt, fall
|
|
131
|
+
* back to events.ndjson replay, persist the result, and emit
|
|
132
|
+
* `index.rebuilt`. The caller MUST already hold the advisory lock. */
|
|
133
|
+
export function recoverState(runDir, opts) {
|
|
134
|
+
let cause = null;
|
|
135
|
+
let snapshot = null;
|
|
136
|
+
try {
|
|
137
|
+
snapshot = readStateSnapshot(runDir);
|
|
138
|
+
if (!snapshot)
|
|
139
|
+
cause = 'missing';
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
if (err instanceof GuardrailError && err.code === 'corrupted_state') {
|
|
143
|
+
cause = 'corrupt';
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!cause && snapshot) {
|
|
150
|
+
return { state: snapshot, recovered: false };
|
|
151
|
+
}
|
|
152
|
+
// Recovery path. Replay first, then persist, then emit event in that
|
|
153
|
+
// order — emitting the event before persisting would leave a record of
|
|
154
|
+
// a recovery that didn't actually land if we crash between the two.
|
|
155
|
+
const replayed = replayState(runDir);
|
|
156
|
+
writeStateSnapshot(runDir, replayed);
|
|
157
|
+
appendEvent(runDir, { event: 'index.rebuilt', cause: cause }, { writerId: opts.writerId, ...(opts.runId !== undefined ? { runId: opts.runId } : {}) });
|
|
158
|
+
return {
|
|
159
|
+
state: replayed,
|
|
160
|
+
recovered: true,
|
|
161
|
+
cause: cause,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/** Schema version for everything written by this engine. Bump on breaking
|
|
2
|
+
* changes to RunState / RunEvent / PhaseSnapshot shape. */
|
|
3
|
+
export declare const RUN_STATE_SCHEMA_VERSION: 1;
|
|
4
|
+
export type SchemaVersion = typeof RUN_STATE_SCHEMA_VERSION;
|
|
5
|
+
/** Identifies a single OS-level writer. PID + a hash of the hostname (we
|
|
6
|
+
* don't persist the raw hostname to the lock metadata so co-tenant signal
|
|
7
|
+
* doesn't leak between users sharing a directory). */
|
|
8
|
+
export interface WriterId {
|
|
9
|
+
pid: number;
|
|
10
|
+
hostHash: string;
|
|
11
|
+
}
|
|
12
|
+
/** Top-level run status, mirroring the lifecycle diagram in the spec. */
|
|
13
|
+
export type RunStatus = 'pending' | 'running' | 'paused' | 'success' | 'failed' | 'aborted';
|
|
14
|
+
/** Per-phase status within a run. */
|
|
15
|
+
export type PhaseStatus = 'pending' | 'running' | 'succeeded' | 'failed' | 'skipped' | 'aborted';
|
|
16
|
+
/** External operation reference — the persisted breadcrumb that makes replay
|
|
17
|
+
* decisions deterministic. Used heavily in Phase 6 (idempotency contracts);
|
|
18
|
+
* typed now so events can carry it without later schema churn.
|
|
19
|
+
*
|
|
20
|
+
* v6.2.1 — `migration-batch` joins the union as the side-effect contract's
|
|
21
|
+
* PRE-effect breadcrumb for the `migrate` phase. Semantics: a deterministic
|
|
22
|
+
* id covers a planned migration batch and is emitted BEFORE the dispatcher
|
|
23
|
+
* is invoked, so a partial crash leaves a resume target visible to the
|
|
24
|
+
* orchestrator's preflight readback. The post-effect `migration-version`
|
|
25
|
+
* refs (one per actually-applied migration) remain authoritative for
|
|
26
|
+
* reconciliation; `migration-batch` exists purely so resume can tell
|
|
27
|
+
* "we started this batch but didn't finish" apart from "we never started." */
|
|
28
|
+
export type ExternalRefKind = 'github-pr' | 'github-comment' | 'git-remote-push' | 'deploy' | 'migration-batch' | 'migration-version' | 'rollback-target' | 'spec-file' | 'plan-file' | 'sarif-artifact' | 'review-comments';
|
|
29
|
+
export interface ExternalRef {
|
|
30
|
+
kind: ExternalRefKind;
|
|
31
|
+
/** Provider-specific identifier (PR number, commit SHA, deploy ID, …). */
|
|
32
|
+
id: string;
|
|
33
|
+
provider?: string;
|
|
34
|
+
/** Human-readable artifact link if the provider exposes one. */
|
|
35
|
+
url?: string;
|
|
36
|
+
/** ISO timestamp of the platform's confirmation. */
|
|
37
|
+
observedAt: string;
|
|
38
|
+
}
|
|
39
|
+
/** Per-phase artifact pointer recorded inside the snapshot. */
|
|
40
|
+
export interface PhaseArtifactRef {
|
|
41
|
+
/** Logical name (e.g. "spec", "plan", "impl-diff"). */
|
|
42
|
+
name: string;
|
|
43
|
+
/** Path inside `artifacts/` (relative to run dir). */
|
|
44
|
+
path: string;
|
|
45
|
+
sha256?: string;
|
|
46
|
+
size?: number;
|
|
47
|
+
copiedAt?: string;
|
|
48
|
+
}
|
|
49
|
+
/** Snapshot of a single phase, persisted under `phases/<name>.json` and
|
|
50
|
+
* reflected inside state.json's `phases[]`. */
|
|
51
|
+
export interface PhaseSnapshot {
|
|
52
|
+
schema_version: SchemaVersion;
|
|
53
|
+
name: string;
|
|
54
|
+
/** Order within the run (0-indexed). */
|
|
55
|
+
index: number;
|
|
56
|
+
status: PhaseStatus;
|
|
57
|
+
/** True iff `RunPhase.idempotent` was declared true at registration. */
|
|
58
|
+
idempotent: boolean;
|
|
59
|
+
/** True iff `RunPhase.hasSideEffects` was declared true at registration. */
|
|
60
|
+
hasSideEffects: boolean;
|
|
61
|
+
startedAt?: string;
|
|
62
|
+
endedAt?: string;
|
|
63
|
+
durationMs?: number;
|
|
64
|
+
/** Sum of `phase.cost` events for this phase. */
|
|
65
|
+
costUSD: number;
|
|
66
|
+
attempts: number;
|
|
67
|
+
/** Last failure message (string) if status === 'failed'. */
|
|
68
|
+
lastError?: string;
|
|
69
|
+
artifacts: PhaseArtifactRef[];
|
|
70
|
+
externalRefs: ExternalRef[];
|
|
71
|
+
/** Phase-specific metadata. Free-form; engine doesn't introspect. */
|
|
72
|
+
meta?: Record<string, unknown>;
|
|
73
|
+
/** Phase 6 — last successful output, persisted so a future
|
|
74
|
+
* `skip-already-applied` decision can return it without re-execution.
|
|
75
|
+
* The engine writes this on every `phase.success`; absent on failed
|
|
76
|
+
* / pre-Phase-6 snapshots. JSON-serializable values only. */
|
|
77
|
+
result?: unknown;
|
|
78
|
+
}
|
|
79
|
+
/** The state.json checkpoint. Authoritative answer is always
|
|
80
|
+
* events.ndjson; this is a derived snapshot for O(1) status queries. */
|
|
81
|
+
export interface RunState {
|
|
82
|
+
schema_version: SchemaVersion;
|
|
83
|
+
runId: string;
|
|
84
|
+
/** ULID generation time, ISO. */
|
|
85
|
+
startedAt: string;
|
|
86
|
+
endedAt?: string;
|
|
87
|
+
status: RunStatus;
|
|
88
|
+
/** Phase order is fixed at run creation. */
|
|
89
|
+
phases: PhaseSnapshot[];
|
|
90
|
+
/** Index into `phases[]` of the currently-running or last-attempted phase. */
|
|
91
|
+
currentPhaseIdx: number;
|
|
92
|
+
/** Sum of phase.cost events across the whole run. */
|
|
93
|
+
totalCostUSD: number;
|
|
94
|
+
/** Last seq written to events.ndjson at the time of the snapshot. */
|
|
95
|
+
lastEventSeq: number;
|
|
96
|
+
/** The writer that wrote this snapshot. */
|
|
97
|
+
writerId: WriterId;
|
|
98
|
+
/** Working directory the run was started in (absolute path). */
|
|
99
|
+
cwd: string;
|
|
100
|
+
/** Snapshot of the run config at creation (subset of guardrail.config.yaml).
|
|
101
|
+
* Free-form; engine doesn't introspect here, later phases do. */
|
|
102
|
+
config?: Record<string, unknown>;
|
|
103
|
+
}
|
|
104
|
+
/** Universal envelope on every event line. */
|
|
105
|
+
export interface RunEventBase {
|
|
106
|
+
schema_version: SchemaVersion;
|
|
107
|
+
/** ISO timestamp. */
|
|
108
|
+
ts: string;
|
|
109
|
+
runId: string;
|
|
110
|
+
/** Monotonic per-run sequence. Receivers MUST detect gaps. */
|
|
111
|
+
seq: number;
|
|
112
|
+
/** The writer that appended this event. */
|
|
113
|
+
writerId: WriterId;
|
|
114
|
+
}
|
|
115
|
+
export interface RunStartEvent extends RunEventBase {
|
|
116
|
+
event: 'run.start';
|
|
117
|
+
phases: string[];
|
|
118
|
+
config?: Record<string, unknown>;
|
|
119
|
+
}
|
|
120
|
+
export interface RunCompleteEvent extends RunEventBase {
|
|
121
|
+
event: 'run.complete';
|
|
122
|
+
status: 'success' | 'failed' | 'aborted';
|
|
123
|
+
totalCostUSD: number;
|
|
124
|
+
durationMs: number;
|
|
125
|
+
}
|
|
126
|
+
export interface RunWarningEvent extends RunEventBase {
|
|
127
|
+
event: 'run.warning';
|
|
128
|
+
message: string;
|
|
129
|
+
details?: Record<string, unknown>;
|
|
130
|
+
}
|
|
131
|
+
export interface RunRecoveryEvent extends RunEventBase {
|
|
132
|
+
event: 'run.recovery';
|
|
133
|
+
reason: 'recovered-from-partial-write' | 'recovered-from-corrupt-snapshot' | 'recovered-from-missing-snapshot';
|
|
134
|
+
details?: Record<string, unknown>;
|
|
135
|
+
}
|
|
136
|
+
export interface PhaseStartEvent extends RunEventBase {
|
|
137
|
+
event: 'phase.start';
|
|
138
|
+
phase: string;
|
|
139
|
+
phaseIdx: number;
|
|
140
|
+
idempotent: boolean;
|
|
141
|
+
hasSideEffects: boolean;
|
|
142
|
+
/** Attempt counter, 1-based. >1 implies a resume / retry. */
|
|
143
|
+
attempt: number;
|
|
144
|
+
}
|
|
145
|
+
export interface PhaseSuccessEvent extends RunEventBase {
|
|
146
|
+
event: 'phase.success';
|
|
147
|
+
phase: string;
|
|
148
|
+
phaseIdx: number;
|
|
149
|
+
durationMs: number;
|
|
150
|
+
artifacts: PhaseArtifactRef[];
|
|
151
|
+
}
|
|
152
|
+
export interface PhaseFailedEvent extends RunEventBase {
|
|
153
|
+
event: 'phase.failed';
|
|
154
|
+
phase: string;
|
|
155
|
+
phaseIdx: number;
|
|
156
|
+
durationMs: number;
|
|
157
|
+
/** Stringified error message. Stack traces stay out of the durable log. */
|
|
158
|
+
error: string;
|
|
159
|
+
/** Optional structured error code (matches GuardrailError.code if thrown). */
|
|
160
|
+
errorCode?: string;
|
|
161
|
+
}
|
|
162
|
+
export interface PhaseAbortedEvent extends RunEventBase {
|
|
163
|
+
event: 'phase.aborted';
|
|
164
|
+
phase: string;
|
|
165
|
+
phaseIdx: number;
|
|
166
|
+
reason: 'user-interrupt' | 'budget-exceeded' | 'lock-takeover' | 'crash';
|
|
167
|
+
}
|
|
168
|
+
export interface PhaseCostEvent extends RunEventBase {
|
|
169
|
+
event: 'phase.cost';
|
|
170
|
+
phase: string;
|
|
171
|
+
phaseIdx: number;
|
|
172
|
+
provider: string;
|
|
173
|
+
inputTokens: number;
|
|
174
|
+
outputTokens: number;
|
|
175
|
+
costUSD: number;
|
|
176
|
+
}
|
|
177
|
+
export interface PhaseExternalRefEvent extends RunEventBase {
|
|
178
|
+
event: 'phase.externalRef';
|
|
179
|
+
phase: string;
|
|
180
|
+
phaseIdx: number;
|
|
181
|
+
ref: ExternalRef;
|
|
182
|
+
}
|
|
183
|
+
export interface PhaseNeedsHumanEvent extends RunEventBase {
|
|
184
|
+
event: 'phase.needs-human';
|
|
185
|
+
phase: string;
|
|
186
|
+
phaseIdx: number;
|
|
187
|
+
reason: string;
|
|
188
|
+
/** Hint surfaced to the user / CI consumer. */
|
|
189
|
+
nextActions?: string[];
|
|
190
|
+
}
|
|
191
|
+
export interface LockTakeoverEvent extends RunEventBase {
|
|
192
|
+
event: 'lock.takeover';
|
|
193
|
+
/** Identity of the writer who previously held the lock (best-effort —
|
|
194
|
+
* may be null if metadata was missing). */
|
|
195
|
+
previousWriter: WriterId | null;
|
|
196
|
+
reason: string;
|
|
197
|
+
}
|
|
198
|
+
export interface IndexRebuiltEvent extends RunEventBase {
|
|
199
|
+
event: 'index.rebuilt';
|
|
200
|
+
/** Why the rebuild was needed: "missing", "corrupt", or "force". */
|
|
201
|
+
cause: 'missing' | 'corrupt' | 'force';
|
|
202
|
+
}
|
|
203
|
+
/** Phase 4 — budget enforcement preflight. Emitted by `runPhase` BEFORE
|
|
204
|
+
* `phase.start` for every phase whose parent run carries a `BudgetConfig`.
|
|
205
|
+
* Carries the full `BudgetCheck` payload from `checkPhaseBudget` so
|
|
206
|
+
* consumers (cost dashboards, CI) can attribute spend and decisions
|
|
207
|
+
* without re-running the policy. Per the v6 spec "Budget enforcement"
|
|
208
|
+
* section + Codex CRITICAL #3 (two-layer guard, layer 2 always runs). */
|
|
209
|
+
export interface BudgetCheckEvent extends RunEventBase {
|
|
210
|
+
event: 'budget.check';
|
|
211
|
+
phase: string;
|
|
212
|
+
phaseIdx: number;
|
|
213
|
+
decision: 'proceed' | 'pause' | 'hard-fail';
|
|
214
|
+
/** `estimate.high` from `RunPhase.estimateCost` if it returned a value;
|
|
215
|
+
* null when the phase doesn't implement estimateCost. Layer 2 (the
|
|
216
|
+
* mandatory floor) ALWAYS runs regardless. */
|
|
217
|
+
estimatedHigh: number | null;
|
|
218
|
+
/** Sum of every prior `phase.cost` event in this run, in USD. */
|
|
219
|
+
actualSoFar: number;
|
|
220
|
+
/** The reserve the runner deducted against `perRunUSD` for this phase
|
|
221
|
+
* (Layer 2 floor, expressed in USD). */
|
|
222
|
+
reserveApplied: number;
|
|
223
|
+
/** USD remaining under `perRunUSD` after `actualSoFar` + the larger of
|
|
224
|
+
* `estimatedHigh` and `reserveApplied`. May be negative on hard-fail. */
|
|
225
|
+
capRemaining: number;
|
|
226
|
+
reason: string;
|
|
227
|
+
/** v6.2.0 — which scope produced the decision. `'phase'` (legacy
|
|
228
|
+
* default) is the single-phase wrapper path; `'run'` is the
|
|
229
|
+
* orchestrator's cross-phase mode. Optional only for back-compat
|
|
230
|
+
* with older events.ndjson files; events emitted on v6.2.0+ always
|
|
231
|
+
* carry a value. */
|
|
232
|
+
scope?: 'phase' | 'run';
|
|
233
|
+
}
|
|
234
|
+
/** Phase 6 — emitted when a `forceReplay` override flips a needs-human (or
|
|
235
|
+
* any other refusal) into a retry. Carries the phase and the refs that
|
|
236
|
+
* WERE consulted so the durable log shows exactly what was overridden.
|
|
237
|
+
* Spec: "a `--force-replay` override writes an explicit `replay.override`
|
|
238
|
+
* event with user-supplied reason." */
|
|
239
|
+
export interface ReplayOverrideEvent extends RunEventBase {
|
|
240
|
+
event: 'replay.override';
|
|
241
|
+
phase: string;
|
|
242
|
+
phaseIdx: number;
|
|
243
|
+
/** Free-form user / CI reason for the override. */
|
|
244
|
+
reason: string;
|
|
245
|
+
/** Refs the underlying refusal cited (echoed for triage). */
|
|
246
|
+
refsConsulted: ExternalRef[];
|
|
247
|
+
}
|
|
248
|
+
/** Discriminated union of every event variant. Add new variants here and
|
|
249
|
+
* the code that switches over `event` will type-error at compile time. */
|
|
250
|
+
export type RunEvent = RunStartEvent | RunCompleteEvent | RunWarningEvent | RunRecoveryEvent | PhaseStartEvent | PhaseSuccessEvent | PhaseFailedEvent | PhaseAbortedEvent | PhaseCostEvent | PhaseExternalRefEvent | PhaseNeedsHumanEvent | LockTakeoverEvent | IndexRebuiltEvent | BudgetCheckEvent | ReplayOverrideEvent;
|
|
251
|
+
/** Distributive Omit so the discriminated-union shape is preserved when we
|
|
252
|
+
* strip the fields the appender fills in. Plain `Omit<RunEvent, ...>`
|
|
253
|
+
* collapses the union into a single intersection and loses variant-specific
|
|
254
|
+
* fields — so a literal `{ event: 'phase.cost', costUSD: 1, ... }` would
|
|
255
|
+
* fail typecheck. */
|
|
256
|
+
type DistributiveOmit<T, K extends keyof T> = T extends unknown ? Omit<T, K> : never;
|
|
257
|
+
/** When appending we don't know `seq`, `ts`, `runId`, `schema_version`,
|
|
258
|
+
* or `writerId` yet — the appender supplies them. */
|
|
259
|
+
export type RunEventInput = DistributiveOmit<RunEvent, 'seq' | 'ts' | 'runId' | 'schema_version' | 'writerId'>;
|
|
260
|
+
export interface RunIndexEntry {
|
|
261
|
+
runId: string;
|
|
262
|
+
status: RunStatus;
|
|
263
|
+
startedAt: string;
|
|
264
|
+
endedAt?: string;
|
|
265
|
+
totalCostUSD: number;
|
|
266
|
+
/** Last completed phase name (for `runs list` summary). */
|
|
267
|
+
lastPhase?: string;
|
|
268
|
+
/** True if state.json was synthesized via replay because it was missing
|
|
269
|
+
* or corrupt at last open. Surfaces as a warning in the UI. */
|
|
270
|
+
recovered?: boolean;
|
|
271
|
+
}
|
|
272
|
+
export interface RunIndex {
|
|
273
|
+
schema_version: SchemaVersion;
|
|
274
|
+
/** Newest first. */
|
|
275
|
+
runs: RunIndexEntry[];
|
|
276
|
+
}
|
|
277
|
+
export {};
|
|
278
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/core/run-state/types.ts
|
|
2
|
+
//
|
|
3
|
+
// v6 Run State Engine — pure data layer types. Phase 1 (persistence) only.
|
|
4
|
+
// Behavior — phase wrapping, CLI verbs, budget enforcement, etc. — lands in
|
|
5
|
+
// later phases. The shapes here are versioned via `schema_version: 1` so a
|
|
6
|
+
// future migration can detect and migrate older runs.
|
|
7
|
+
//
|
|
8
|
+
// Spec: docs/specs/v6-run-state-engine.md ("State on disk", "Run lifecycle",
|
|
9
|
+
// "Idempotency rules + external operation ledger", "Persistence protocol").
|
|
10
|
+
/** Schema version for everything written by this engine. Bump on breaking
|
|
11
|
+
* changes to RunState / RunEvent / PhaseSnapshot shape. */
|
|
12
|
+
export const RUN_STATE_SCHEMA_VERSION = 1;
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Validate that a string matches the ULID shape (length + alphabet). */
|
|
2
|
+
export declare function isValidULID(s: string): boolean;
|
|
3
|
+
/** Generate a new ULID. Optionally pass a fixed `now` (ms epoch) for tests. */
|
|
4
|
+
export declare function ulid(now?: number): string;
|
|
5
|
+
/** Decode the timestamp-portion of a ULID back to ms epoch. Throws on
|
|
6
|
+
* malformed input. Useful for `runs list` ordering and for tests. */
|
|
7
|
+
export declare function decodeTime(id: string): number;
|
|
8
|
+
/** Exposed for tests that want to verify alphabet membership. */
|
|
9
|
+
export declare const ULID_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
10
|
+
export declare const ULID_LENGTH: number;
|
|
11
|
+
//# sourceMappingURL=ulid.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// src/core/run-state/ulid.ts
|
|
2
|
+
//
|
|
3
|
+
// Tiny pure-TS ULID generator. We deliberately avoid pulling in the `ulid`
|
|
4
|
+
// npm package — the algorithm is short and the runtime dep budget for the
|
|
5
|
+
// engine is tight. Conforms to https://github.com/ulid/spec :
|
|
6
|
+
//
|
|
7
|
+
// - 26 characters, Crockford's Base32 (no I, L, O, U).
|
|
8
|
+
// - First 10 chars = 48-bit Unix-millisecond timestamp (ms since epoch).
|
|
9
|
+
// - Last 16 chars = 80 bits of randomness (crypto.randomBytes).
|
|
10
|
+
// - Lexicographic sort == chronological sort (within ms; tie-break is
|
|
11
|
+
// random within the same ms — Phase 1 does not implement the optional
|
|
12
|
+
// monotonic-overflow within-ms behavior, since runIDs are issued by a
|
|
13
|
+
// single writer and one-per-millisecond is unreachable in practice).
|
|
14
|
+
// - URL-safe (Crockford Base32 only emits [0-9A-Z]).
|
|
15
|
+
import { randomBytes } from 'node:crypto';
|
|
16
|
+
const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford Base32 alphabet (32 chars).
|
|
17
|
+
const ENCODING_LEN = ENCODING.length; // 32.
|
|
18
|
+
const TIME_LEN = 10;
|
|
19
|
+
const RANDOM_LEN = 16;
|
|
20
|
+
const ULID_LEN = TIME_LEN + RANDOM_LEN; // 26.
|
|
21
|
+
/** Max representable timestamp = 2^48 - 1 ms. Sanity-check, not a bug
|
|
22
|
+
* most callers will hit (it's the year 10889). */
|
|
23
|
+
const TIME_MAX = 281474976710655;
|
|
24
|
+
/** Validate that a string matches the ULID shape (length + alphabet). */
|
|
25
|
+
export function isValidULID(s) {
|
|
26
|
+
if (typeof s !== 'string')
|
|
27
|
+
return false;
|
|
28
|
+
if (s.length !== ULID_LEN)
|
|
29
|
+
return false;
|
|
30
|
+
for (let i = 0; i < ULID_LEN; i++) {
|
|
31
|
+
if (ENCODING.indexOf(s[i]) < 0)
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
function encodeTime(now) {
|
|
37
|
+
if (!Number.isFinite(now) || now < 0 || now > TIME_MAX) {
|
|
38
|
+
throw new RangeError(`ulid: timestamp out of range (got ${now})`);
|
|
39
|
+
}
|
|
40
|
+
let out = '';
|
|
41
|
+
let t = now;
|
|
42
|
+
for (let i = TIME_LEN - 1; i >= 0; i--) {
|
|
43
|
+
const mod = t % ENCODING_LEN;
|
|
44
|
+
out = ENCODING[mod] + out;
|
|
45
|
+
t = (t - mod) / ENCODING_LEN;
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
function encodeRandom() {
|
|
50
|
+
// 16 chars * 5 bits each = 80 bits. We draw 10 bytes (80 bits) of
|
|
51
|
+
// crypto-grade randomness and encode 5 bits at a time. Any extra fractional
|
|
52
|
+
// bits are discarded — this is the standard ULID approach.
|
|
53
|
+
const bytes = randomBytes(10);
|
|
54
|
+
// Pack the 10 bytes into a 80-bit unsigned integer view, 5-bit chunks.
|
|
55
|
+
// We do it manually rather than using BigInt for portability — the array
|
|
56
|
+
// is short enough that the explicit math is just as fast and avoids any
|
|
57
|
+
// dependency on BigInt-typed regression in older runtimes.
|
|
58
|
+
const bits = new Array(80);
|
|
59
|
+
for (let i = 0; i < 10; i++) {
|
|
60
|
+
const b = bytes[i];
|
|
61
|
+
for (let j = 0; j < 8; j++) {
|
|
62
|
+
bits[i * 8 + j] = (b >> (7 - j)) & 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
let out = '';
|
|
66
|
+
for (let i = 0; i < RANDOM_LEN; i++) {
|
|
67
|
+
let v = 0;
|
|
68
|
+
for (let j = 0; j < 5; j++) {
|
|
69
|
+
v = (v << 1) | bits[i * 5 + j];
|
|
70
|
+
}
|
|
71
|
+
out += ENCODING[v];
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/** Generate a new ULID. Optionally pass a fixed `now` (ms epoch) for tests. */
|
|
76
|
+
export function ulid(now = Date.now()) {
|
|
77
|
+
return encodeTime(now) + encodeRandom();
|
|
78
|
+
}
|
|
79
|
+
/** Decode the timestamp-portion of a ULID back to ms epoch. Throws on
|
|
80
|
+
* malformed input. Useful for `runs list` ordering and for tests. */
|
|
81
|
+
export function decodeTime(id) {
|
|
82
|
+
if (!isValidULID(id)) {
|
|
83
|
+
throw new Error(`ulid: not a valid ULID: ${String(id)}`);
|
|
84
|
+
}
|
|
85
|
+
let t = 0;
|
|
86
|
+
for (let i = 0; i < TIME_LEN; i++) {
|
|
87
|
+
const idx = ENCODING.indexOf(id[i]);
|
|
88
|
+
t = t * ENCODING_LEN + idx;
|
|
89
|
+
}
|
|
90
|
+
return t;
|
|
91
|
+
}
|
|
92
|
+
/** Exposed for tests that want to verify alphabet membership. */
|
|
93
|
+
export const ULID_ALPHABET = ENCODING;
|
|
94
|
+
export const ULID_LENGTH = ULID_LEN;
|
|
95
|
+
//# sourceMappingURL=ulid.js.map
|
|
@@ -3,7 +3,7 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import { extractFromSql } from "./sql.js";
|
|
5
5
|
import { extractFromPrisma } from "./prisma.js";
|
|
6
|
-
export function extract(filePath) {
|
|
6
|
+
export function extract(filePath, previousContent) {
|
|
7
7
|
let content;
|
|
8
8
|
try {
|
|
9
9
|
content = fs.readFileSync(filePath, 'utf8');
|
|
@@ -16,7 +16,7 @@ export function extract(filePath) {
|
|
|
16
16
|
if (ext === '.sql')
|
|
17
17
|
return extractFromSql(content);
|
|
18
18
|
if (base === 'schema.prisma' || ext === '.prisma')
|
|
19
|
-
return extractFromPrisma(content);
|
|
19
|
+
return extractFromPrisma(content, previousContent);
|
|
20
20
|
process.stderr.write(`[schema-alignment] no extractor for ${ext} files — skipping ${path.basename(filePath)}\n`);
|
|
21
21
|
return [];
|
|
22
22
|
}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
1
|
import type { SchemaEntity } from '../types.ts';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Extract schema entities from a Prisma schema file.
|
|
4
|
+
*
|
|
5
|
+
* When `previousContent` is provided, only the diff (added/removed fields,
|
|
6
|
+
* new/dropped tables) is emitted — this avoids over-reporting when a user
|
|
7
|
+
* touches schema.prisma for any reason (adding one field, editing a comment)
|
|
8
|
+
* and every long-existing field gets re-checked against type/API/UI layers.
|
|
9
|
+
*
|
|
10
|
+
* When `previousContent` is null/undefined, every model and field is emitted
|
|
11
|
+
* — the original "all entities are new" behavior used as a fallback when git
|
|
12
|
+
* history isn't available.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractFromPrisma(content: string, previousContent?: string | null): SchemaEntity[];
|
|
3
15
|
//# sourceMappingURL=prisma.d.ts.map
|