@jmtrin/opencode-kevin 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/plugin/index.d.ts +4 -3
  2. package/dist/plugin/index.js +122 -113
  3. package/dist/plugin/native.d.ts +2 -2
  4. package/dist/plugin/native.js +2 -2
  5. package/package.json +56 -68
  6. package/LICENSE +0 -21
  7. package/README.md +0 -605
  8. package/dist/migrations/001_initial.sql +0 -92
  9. package/dist/migrations/002_indexes.sql +0 -14
  10. package/dist/migrations/003_v02_signal.sql +0 -58
  11. package/dist/migrations/004_v03_knowledge.sql +0 -138
  12. package/dist/migrations/005_v04_signal.sql +0 -57
  13. package/dist/migrations/006_v05_glassbox.sql +0 -118
  14. package/dist/migrations/007_v06_pull.sql +0 -145
  15. package/dist/migrations/008_v07_truth.sql +0 -124
  16. package/dist/migrations/009_v08_team.sql +0 -100
  17. package/dist/migrations/010_v09_native.sql +0 -78
  18. package/dist/migrations/011_v10_proven.sql +0 -69
  19. package/dist/migrations/012_v11_drift.sql +0 -24
  20. package/dist/plugin/Archiver.d.ts +0 -42
  21. package/dist/plugin/Archiver.js +0 -83
  22. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  23. package/dist/plugin/ArtifactWriter.js +0 -291
  24. package/dist/plugin/CausalChain.d.ts +0 -22
  25. package/dist/plugin/CausalChain.js +0 -197
  26. package/dist/plugin/ChatBridge.d.ts +0 -41
  27. package/dist/plugin/ChatBridge.js +0 -103
  28. package/dist/plugin/ConflictDetector.d.ts +0 -35
  29. package/dist/plugin/ConflictDetector.js +0 -260
  30. package/dist/plugin/ContextInjector.d.ts +0 -222
  31. package/dist/plugin/ContextInjector.js +0 -533
  32. package/dist/plugin/ConventionMiner.d.ts +0 -35
  33. package/dist/plugin/ConventionMiner.js +0 -242
  34. package/dist/plugin/Curator.d.ts +0 -116
  35. package/dist/plugin/Curator.js +0 -324
  36. package/dist/plugin/DashboardHtml.d.ts +0 -5
  37. package/dist/plugin/DashboardHtml.js +0 -180
  38. package/dist/plugin/Feedback.d.ts +0 -67
  39. package/dist/plugin/Feedback.js +0 -120
  40. package/dist/plugin/HookLiveness.d.ts +0 -88
  41. package/dist/plugin/HookLiveness.js +0 -322
  42. package/dist/plugin/InjectionLedger.d.ts +0 -98
  43. package/dist/plugin/InjectionLedger.js +0 -295
  44. package/dist/plugin/LessonFixer.d.ts +0 -44
  45. package/dist/plugin/LessonFixer.js +0 -45
  46. package/dist/plugin/Materializer.d.ts +0 -83
  47. package/dist/plugin/Materializer.js +0 -181
  48. package/dist/plugin/MemoryService.d.ts +0 -360
  49. package/dist/plugin/MemoryService.js +0 -1242
  50. package/dist/plugin/Migrate.d.ts +0 -17
  51. package/dist/plugin/Migrate.js +0 -271
  52. package/dist/plugin/PatternMiner.d.ts +0 -49
  53. package/dist/plugin/PatternMiner.js +0 -132
  54. package/dist/plugin/QualityGate.d.ts +0 -124
  55. package/dist/plugin/QualityGate.js +0 -119
  56. package/dist/plugin/Reflector.d.ts +0 -91
  57. package/dist/plugin/Reflector.js +0 -307
  58. package/dist/plugin/RepoIdentity.d.ts +0 -124
  59. package/dist/plugin/RepoIdentity.js +0 -300
  60. package/dist/plugin/RepoTruth.d.ts +0 -80
  61. package/dist/plugin/RepoTruth.js +0 -599
  62. package/dist/plugin/Retrospective.d.ts +0 -17
  63. package/dist/plugin/Retrospective.js +0 -216
  64. package/dist/plugin/SharedLayer.d.ts +0 -159
  65. package/dist/plugin/SharedLayer.js +0 -462
  66. package/dist/plugin/Store.d.ts +0 -29
  67. package/dist/plugin/Store.js +0 -50
  68. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  69. package/dist/plugin/ToolCallObserver.js +0 -201
  70. package/dist/plugin/TuiActions.d.ts +0 -43
  71. package/dist/plugin/TuiActions.js +0 -181
  72. package/dist/plugin/TuiSnapshots.d.ts +0 -24
  73. package/dist/plugin/TuiSnapshots.js +0 -158
  74. package/dist/plugin/columns.d.ts +0 -11
  75. package/dist/plugin/columns.js +0 -54
  76. package/dist/plugin/confidence.d.ts +0 -8
  77. package/dist/plugin/confidence.js +0 -34
  78. package/dist/plugin/contract.d.ts +0 -54
  79. package/dist/plugin/contract.js +0 -386
  80. package/dist/plugin/diff.d.ts +0 -8
  81. package/dist/plugin/diff.js +0 -182
  82. package/dist/plugin/escape.d.ts +0 -39
  83. package/dist/plugin/escape.js +0 -60
  84. package/dist/plugin/fingerprint.d.ts +0 -27
  85. package/dist/plugin/fingerprint.js +0 -73
  86. package/dist/plugin/inferability.d.ts +0 -32
  87. package/dist/plugin/inferability.js +0 -88
  88. package/dist/plugin/kevin_approve.d.ts +0 -34
  89. package/dist/plugin/kevin_approve.js +0 -52
  90. package/dist/plugin/kevin_audit.d.ts +0 -229
  91. package/dist/plugin/kevin_audit.js +0 -631
  92. package/dist/plugin/kevin_bench.d.ts +0 -10
  93. package/dist/plugin/kevin_bench.js +0 -82
  94. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  95. package/dist/plugin/kevin_conflicts.js +0 -50
  96. package/dist/plugin/kevin_contract.d.ts +0 -8
  97. package/dist/plugin/kevin_contract.js +0 -59
  98. package/dist/plugin/kevin_doctor.d.ts +0 -62
  99. package/dist/plugin/kevin_doctor.js +0 -196
  100. package/dist/plugin/kevin_facts.d.ts +0 -42
  101. package/dist/plugin/kevin_facts.js +0 -36
  102. package/dist/plugin/kevin_forget.d.ts +0 -33
  103. package/dist/plugin/kevin_forget.js +0 -260
  104. package/dist/plugin/kevin_native.d.ts +0 -29
  105. package/dist/plugin/kevin_native.js +0 -79
  106. package/dist/plugin/kevin_propose.d.ts +0 -23
  107. package/dist/plugin/kevin_propose.js +0 -14
  108. package/dist/plugin/kevin_publish.d.ts +0 -38
  109. package/dist/plugin/kevin_publish.js +0 -18
  110. package/dist/plugin/kevin_why.d.ts +0 -23
  111. package/dist/plugin/kevin_why.js +0 -111
  112. package/dist/plugin/memory-format.d.ts +0 -20
  113. package/dist/plugin/memory-format.js +0 -59
  114. package/dist/plugin/metrics.d.ts +0 -97
  115. package/dist/plugin/metrics.js +0 -288
  116. package/dist/plugin/okf-export.d.ts +0 -3
  117. package/dist/plugin/okf-export.js +0 -134
  118. package/dist/plugin/okf-import.d.ts +0 -76
  119. package/dist/plugin/okf-import.js +0 -271
  120. package/dist/plugin/okf.d.ts +0 -107
  121. package/dist/plugin/okf.js +0 -303
  122. package/dist/plugin/perf.d.ts +0 -35
  123. package/dist/plugin/perf.js +0 -174
  124. package/dist/plugin/query-tokenizer.d.ts +0 -13
  125. package/dist/plugin/query-tokenizer.js +0 -133
  126. package/dist/plugin/redact.d.ts +0 -2
  127. package/dist/plugin/redact.js +0 -18
  128. package/dist/plugin/replay-types.d.ts +0 -69
  129. package/dist/plugin/replay-types.js +0 -156
  130. package/dist/plugin/replay.d.ts +0 -36
  131. package/dist/plugin/replay.js +0 -202
  132. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  133. package/dist/plugin/sqlite-adapter.js +0 -81
  134. package/dist/plugin/time-ms.d.ts +0 -1
  135. package/dist/plugin/time-ms.js +0 -16
  136. package/dist/plugin/tui-types.d.ts +0 -59
  137. package/dist/plugin/tui-types.js +0 -4
  138. package/dist/plugin/tui.d.ts +0 -18
  139. package/dist/plugin/tui.js +0 -198
  140. package/dist/plugin/uuid.d.ts +0 -1
  141. package/dist/plugin/uuid.js +0 -50
@@ -1,300 +0,0 @@
1
- import { mkdirSync, readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { fingerprint, fnv1a64 } from "./fingerprint.js";
4
- // ============================================================
5
- // Kevin 0.8.0 — RepoIdentity (K8-005/K8-006 / plan §5.1, D8-04)
6
- // v0.9.0 — host identity source (K9-006 / plan §5.2, D9-13)
7
- // ============================================================
8
- // Repository-derived identity that survives a clone. Two clones
9
- // of the same remote must agree on one `repo_id` even though
10
- // `process.cwd()` differs.
11
- //
12
- // This file deliberately contains no process-launching calls (D8-01)
13
- // and no `new URL()` — the scp-style `git@host:path` form that git
14
- // accepts and roughly half of all clones use rejects URL parsing
15
- // (K8-005).
16
- // ============================================================
17
- // Comment lines git accepts inside a config section: `#` and `;`.
18
- const COMMENT_RE = /^[#;]/;
19
- // A declared id must be exactly 16 lowercase hex characters (K8-006).
20
- const DECLARED_ID_RE = /^[0-9a-f]{16}$/;
21
- const PROJECT_JSON_PATH = join(".kevin", "project.json");
22
- const GIT_CONFIG_PATH = join(".git", "config");
23
- /**
24
- * Read the first `url = …` value inside `[remote "<name>"]` of a
25
- * `.git/config`-style INI text.
26
- *
27
- * Line-oriented reader, not an INI library: tracks the current
28
- * `[section "sub"]` header, tolerates tabs, spaces around `=`,
29
- * CRLF, and comment lines. Returns `null` on anything unrecognised
30
- * and never throws — on any input, including binary.
31
- */
32
- export function parseGitConfigRemote(text, name = "origin") {
33
- // Never throw on any input, including binary and huge strings.
34
- if (typeof text !== "string" || text.length === 0)
35
- return null;
36
- const lines = text.split(/\r\n|\n|\r/);
37
- let currentSection = null;
38
- let currentSub = null;
39
- for (const raw of lines) {
40
- const line = raw.trim();
41
- if (line.length === 0)
42
- continue;
43
- if (COMMENT_RE.test(line))
44
- continue;
45
- // Section header: `[section "sub"]` or `[section]`.
46
- if (line.startsWith("[")) {
47
- const header = line.slice(1, line.indexOf("]"));
48
- if (header === null)
49
- return null;
50
- const q = header.indexOf('"');
51
- if (q === -1) {
52
- currentSection = header.trim();
53
- currentSub = null;
54
- }
55
- else {
56
- currentSection = header.slice(0, q).trim();
57
- const sub = header.slice(q + 1, header.lastIndexOf('"'));
58
- currentSub = sub;
59
- }
60
- continue;
61
- }
62
- if (currentSection !== "remote")
63
- continue;
64
- const eq = line.indexOf("=");
65
- if (eq === -1)
66
- return null;
67
- const key = line.slice(0, eq).trim();
68
- const value = line.slice(eq + 1).trim();
69
- if (key !== "url")
70
- continue;
71
- if (currentSub === name)
72
- return value;
73
- }
74
- return null;
75
- }
76
- /**
77
- * Fold the URL shapes git accepts into one canonical string.
78
- *
79
- * Steps, in order (K8-005):
80
- * 1. strip a trailing `.git`;
81
- * 2. strip a `scheme://` prefix;
82
- * 3. strip everything up to and including the last `@` in the
83
- * authority (userinfo and embedded credentials — a token must
84
- * never reach a hash that lands in a committed file, D8-04);
85
- * 4. strip a numeric port (`:8443`, `:443`, …) so the same repo
86
- * served over different transports folds to one id;
87
- * 5. rewrite the scp-style `host:path` separator to `host/path`;
88
- * 6. strip a trailing `/`;
89
- * 7. lowercase the whole result.
90
- *
91
- * Returns `null` when the result contains no `/` (e.g. a local
92
- * path remote) — the caller falls through to the path source.
93
- */
94
- export function normalizeRemote(url) {
95
- if (typeof url !== "string" || url.length === 0)
96
- return null;
97
- // 1. Strip a trailing `.git` (case-insensitive for file names
98
- // like `App.GIT`; hosts are lowercased later anyway).
99
- let s = url.replace(/\.git$/i, "");
100
- // 2. Strip a `scheme://` prefix — any scheme, including
101
- // `ssh://`, `https://`, `git://`, `file://`. A `file://`
102
- // remote collapses to a local path and will fall through.
103
- s = s.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "");
104
- // 3. Strip userinfo: everything up to and including the last
105
- // `@` in the authority. `https://user:token@host/…` and
106
- // `ssh://git@host/…` both lose the credential half here,
107
- // before anything else can retain a copy.
108
- const at = s.lastIndexOf("@");
109
- if (at !== -1)
110
- s = s.slice(at + 1);
111
- // 4. Strip a numeric port: `https://host:8443/org/repo`, `host:8443/
112
- // org/repo` and `host/org/repo` are the same repository served
113
- // over different transports — they must fold to one id or the
114
- // team silently splits by transport form. The scp-style
115
- // `host:path` form (path not starting with digits) is rewritten
116
- // by the step below.
117
- s = s.replace(/^([^/]+):(\d+)(\/|$)/, "$1$3");
118
- // 5. scp-style `host:path` → `host/path`. The first `:` that
119
- // remains is the separator; everything after it is the path.
120
- // After step 3 there is no userinfo `:` left to confuse it.
121
- const colon = s.indexOf(":");
122
- if (colon !== -1) {
123
- s = `${s.slice(0, colon)}/${s.slice(colon + 1)}`;
124
- }
125
- // 6. Strip a trailing `/`.
126
- s = s.replace(/\/+$/, "");
127
- // 7. Lowercase.
128
- s = s.toLowerCase();
129
- if (s.length === 0)
130
- return null;
131
- // Anything without a `/` is a bare host or a bare local name,
132
- // not a remote repository path — the caller falls through.
133
- if (!s.includes("/"))
134
- return null;
135
- // A leading `/` means the remote is a local filesystem path
136
- // (e.g. `/srv/git/bare.git` or a `file://` remote) — plan §5.1's
137
- // fifth fixture — and the caller falls through to source 3.
138
- if (s.startsWith("/"))
139
- return null;
140
- return s;
141
- }
142
- /**
143
- * Derive a repository id from a normalized remote (K8-006).
144
- *
145
- * The domain prefix means a repo id can never collide with a memory
146
- * fingerprint computed over the same string, and it leaves room to
147
- * version the derivation later.
148
- */
149
- export function computeRepoId(normalized) {
150
- return fnv1a64(`okf:repo:v1\u0000${normalized}`);
151
- }
152
- /**
153
- * Resolve the repository identity for `cwd`, trying the four
154
- * sources in order (plan §5.2, D9-13):
155
- *
156
- * 1. `declared` — `.kevin/project.json` → `id` (validated as
157
- * exactly 16 lowercase hex characters; anything else is ignored
158
- * and falls through, because a hand-edited garbage id in a
159
- * committed file would otherwise scope a whole team's corpus
160
- * onto a typo);
161
- * 2. `remote` — `.git/config` → `[remote "origin"] url`,
162
- * normalized and hashed;
163
- * 3. `host` — the value the host resolved for this directory,
164
- * `host.project.worktree` first, `host.project.directory` as
165
- * fallback; both empty or absent falls through to `path`.
166
- * This is strictly better than `process.cwd()` — the host's own
167
- * ToolContext documents "prefer this over `process.cwd()`" —
168
- * but it sits below the explicit sources, because monorepos and
169
- * D8-03's confirmed re-keying depend on `.kevin/project.json`
170
- * winning;
171
- * 4. `path` — `fingerprint(cwd)`, the v0.7.0 behaviour, preserved
172
- * exactly when `host` is absent.
173
- *
174
- * Never throws: a directory that is not a git repository, an
175
- * unreadable `.git/config`, a malformed `project.json`, and a host
176
- * with no usable project fields all fall through to source 4.
177
- * `projectId` is always returned alongside `repoId`, regardless of
178
- * which source won.
179
- */
180
- export function resolve(cwd, host) {
181
- const projectId = fingerprint(cwd);
182
- // 1. Declared.
183
- try {
184
- const raw = readFileSync(join(cwd, PROJECT_JSON_PATH), "utf8");
185
- const parsed = JSON.parse(raw);
186
- if (typeof parsed.id === "string" && DECLARED_ID_RE.test(parsed.id)) {
187
- return {
188
- repoId: parsed.id,
189
- source: "declared",
190
- evidence: ".kevin/project.json#id",
191
- projectId,
192
- };
193
- }
194
- }
195
- catch {
196
- // Missing, unreadable, or malformed — fall through.
197
- }
198
- // 2. Remote.
199
- try {
200
- const config = readFileSync(join(cwd, GIT_CONFIG_PATH), "utf8");
201
- const url = parseGitConfigRemote(config);
202
- const normalized = url === null ? null : normalizeRemote(url);
203
- if (normalized !== null) {
204
- return {
205
- repoId: computeRepoId(normalized),
206
- source: "remote",
207
- evidence: `remote:${normalized}`,
208
- projectId,
209
- };
210
- }
211
- }
212
- catch {
213
- // Unreadable or missing — fall through.
214
- }
215
- // 3. Host — the value the host already resolved for this
216
- // directory (D9-13), inserted above `path` and below the two
217
- // explicit sources. `worktree` first, `directory` as fallback;
218
- // both empty or absent falls through. The chosen value feeds
219
- // the unchanged `computeRepoId()` — this chain changes which
220
- // string is hashed, never how.
221
- const worktree = host?.project?.worktree;
222
- if (typeof worktree === "string" && worktree.length > 0) {
223
- return {
224
- repoId: computeRepoId(worktree),
225
- source: "host",
226
- evidence: "host:worktree",
227
- projectId,
228
- };
229
- }
230
- const directory = host?.project?.directory;
231
- if (typeof directory === "string" && directory.length > 0) {
232
- return {
233
- repoId: computeRepoId(directory),
234
- source: "host",
235
- evidence: "host:directory",
236
- projectId,
237
- };
238
- }
239
- // 4. Path — the v0.7.0 behaviour, preserved exactly.
240
- return { repoId: projectId, source: "path", evidence: "cwd", projectId };
241
- }
242
- /**
243
- * Write `.kevin/project.json` pinning the repository identity (K8-008
244
- * / plan §5.8). The id comes from the current `resolve(cwd)` — so `init`
245
- * in a repository with a remote *pins* the remote-derived id, which is
246
- * the point: it survives the organisation renaming the repo (a remote
247
- * URL change would otherwise silently re-scope the corpus).
248
- *
249
- * The file is `{"id": "<16 hex>", "created_at": "<ISO-8601 Z>",
250
- * "generator": "opencode-kevin/0.8.0"}` with sorted keys and a
251
- * terminating newline (deterministic bytes, the codec discipline of
252
- * plan §7.3). `init` refuses when the file already exists: overwriting
253
- * it re-scopes a team's corpus, and that is `rekey`'s job (K8-009),
254
- * with a confirmation.
255
- *
256
- * NOTE (K8-019): the write goes through `ArtifactWriter` in whole-file
257
- * mode — `.kevin/project.json` is a Kevin-owned file, and D8-08 leaves
258
- * no scenario in which a second raw write path is acceptable (asserted
259
- * by tests/unit/single_write_path.test.ts).
260
- */
261
- export function initProjectFile(cwd, writer) {
262
- const path = join(cwd, PROJECT_JSON_PATH);
263
- try {
264
- readFileSync(path, "utf8");
265
- return {
266
- ok: false,
267
- path,
268
- reason: `.kevin/project.json already exists — overwriting it would re-scope the corpus; use "kevin_project rekey" instead`,
269
- };
270
- }
271
- catch {
272
- // Absent or unreadable — proceed.
273
- }
274
- const id = resolve(cwd).repoId;
275
- const createdAt = new Date().toISOString();
276
- const payload = `${JSON.stringify({
277
- created_at: createdAt,
278
- generator: "opencode-kevin/0.9.0",
279
- id,
280
- })}\n`;
281
- try {
282
- mkdirSync(join(cwd, ".kevin"), { recursive: true });
283
- const outcome = writer.write({ path, mode: "whole", content: payload });
284
- if (outcome !== "written") {
285
- return {
286
- ok: false,
287
- path,
288
- reason: `could not write .kevin/project.json: ${outcome}`,
289
- };
290
- }
291
- return { ok: true, path, id, createdAt };
292
- }
293
- catch (err) {
294
- return {
295
- ok: false,
296
- path,
297
- reason: `could not write .kevin/project.json: ${err?.message ?? "unknown error"}`,
298
- };
299
- }
300
- }
@@ -1,80 +0,0 @@
1
- import type { Memory } from "./MemoryService.js";
2
- import type { Store } from "./Store.js";
3
- import type { Metrics } from "./metrics.js";
4
- declare const MAX_FACTS_PER_PROJECT = 500;
5
- export interface RepoFact {
6
- /** "package.json" | "tsconfig.json" */
7
- readonly file: string;
8
- /** "scripts.test", "dependencies.zod", "compilerOptions.strict" */
9
- readonly keyPath: string;
10
- /** always stringified */
11
- readonly value: string;
12
- }
13
- export interface RepoFactRow extends RepoFact {
14
- id: string;
15
- projectId: string;
16
- fingerprint: string;
17
- sourceMtime: string | null;
18
- scannedAt: string;
19
- }
20
- export declare class RepoTruth {
21
- private readonly store;
22
- private readonly projectId;
23
- private readonly projectRoot;
24
- private readonly metrics;
25
- private hasTable;
26
- private readonly lastMtimes;
27
- private readonly lastFacts;
28
- private lastScanAt;
29
- private readonly prevDeps;
30
- constructor(store: Store, projectId: string, projectRoot: string, metrics?: Metrics | null);
31
- private ensureTables;
32
- /**
33
- * v0.7.0 (K7-005/006 / plan §5.1-5.2, D7-13) — refresh the repository
34
- * truth. The steady state of an idle session is two `stat` calls: a file
35
- * whose `source_mtime` is unchanged is NOT re-parsed (no JSON.parse, no
36
- * readFileSync). A changed file replaces that file's facts for THIS
37
- * project only; a file no longer present has its facts removed for this
38
- * project only. The project-wide 500-fact cap is applied to the combined,
39
- * deterministically-ordered fact set and the truncation is recorded, never
40
- * silent. Returns the project's stored fact view after the scan (including
41
- * a `_truncated` row when the cap was hit), so repeated scans of unchanged
42
- * files return identical results.
43
- */
44
- scan(_now?: Date): RepoFact[];
45
- private hasStoredFileFacts;
46
- private currentScanFacts;
47
- private storedFileFacts;
48
- private getStoredMtime;
49
- private persistAll;
50
- private storedFactCount;
51
- private storedTruncation;
52
- /**
53
- * v0.7.0 (K7-006 / plan §5.2) — extract a file's facts WITHOUT writing
54
- * anything. Used by scan to keep a single source of truth for extraction.
55
- */
56
- private extractFile;
57
- private fileMtimes;
58
- /**
59
- * v0.7.0 (K7-006 / plan §5.2, D7-02) — the project-scoped stored facts.
60
- * Every read filters on `project_id`, so a second project's facts never
61
- * leak into this project's view.
62
- */
63
- facts(): RepoFact[];
64
- /** v0.7.0 (K7-009) — human-readable fact rows for `kevin_facts`. */
65
- storeFacts(): RepoFactRow[];
66
- /** Timestamp of the latest scan in this process or persisted facts. */
67
- scannedAt(): string | null;
68
- /**
69
- * v0.7.0 (K7-007 / plan §5.1, D7-05) — exact-match contradiction detection:
70
- * the three and only three checks. Each returns a human-readable reason;
71
- * empty when consistent. Pure read — writes nothing (K7-008 and K7-014 own
72
- * the penalty and conflict-row writes).
73
- */
74
- contradictions(memory: Memory): string[];
75
- private checkMissingScripts;
76
- private checkMissingDependencies;
77
- private checkChangedCompilerOptions;
78
- private currentDepPackages;
79
- }
80
- export { MAX_FACTS_PER_PROJECT };