knodin 0.5.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +190 -535
- package/dist/bin/cli.js +173 -17
- package/dist/bin/launcher.js +11 -0
- package/dist/src/agent-integration.js +4 -16
- package/dist/src/artifact-refresh.js +1 -1
- package/dist/src/cli-args.js +8 -1
- package/dist/src/cli-model.js +35 -1
- package/dist/src/codeflow-replay.js +80 -0
- package/dist/src/competitive-cold-mcp.js +40 -0
- package/dist/src/competitive-manifest.js +106 -25
- package/dist/src/competitive-runner.js +37 -3
- package/dist/src/competitive-sandbox.js +4 -4
- package/dist/src/context-export.js +1 -1
- package/dist/src/diagnostics.js +449 -0
- package/dist/src/docs-sections.js +5 -5
- package/dist/src/engine/ann-hnsw.js +5 -5
- package/dist/src/engine/embeddings.js +4 -4
- package/dist/src/engine/git-history.js +289 -0
- package/dist/src/engine/index.js +450 -103
- package/dist/src/engine/prune.js +2 -2
- package/dist/src/engine/scip-import.js +408 -0
- package/dist/src/engine/symbol-delete.js +1 -1
- package/dist/src/execution-profile.js +203 -0
- package/dist/src/failure-diagnosis.js +69 -10
- package/dist/src/hook-manager-integration.js +156 -0
- package/dist/src/index-activity.js +1 -1
- package/dist/src/init-progress-worker.js +1 -1
- package/dist/src/init.js +361 -92
- package/dist/src/lifecycle-health.js +51 -14
- package/dist/src/lsp-readonly.js +1 -1
- package/dist/src/output-compression.js +1 -1
- package/dist/src/output-telemetry.js +8 -4
- package/dist/src/pr-triage.js +2 -2
- package/dist/src/progressive-evidence.js +473 -0
- package/dist/src/pure-compression-cli.js +101 -0
- package/dist/src/release-preflight.js +510 -0
- package/dist/src/repair-progress-worker.js +1 -1
- package/dist/src/repository-init-process.js +1 -1
- package/dist/src/repository-management.js +145 -3
- package/dist/src/response-budget.js +11 -1
- package/dist/src/server.js +23 -3
- package/dist/src/structural-fast-path.js +303 -0
- package/dist/src/structural-snapshot.js +33 -0
- package/dist/src/system-config.js +7 -7
- package/dist/src/tools/knodin-tools.js +106 -15
- package/dist/src/update-ceremony.js +158 -0
- package/dist/src/update-policy.js +4 -4
- package/dist/src/wait-for-fresh.js +2 -2
- package/dist/src/worktree-lifecycle.js +2 -2
- package/docs/CLI.md +22 -0
- package/docs/COMMAND-OUTPUT-COMPRESSION.md +32 -16
- package/docs/CONTAINED-EXECUTION.md +77 -0
- package/docs/DIAGNOSTICS.md +45 -0
- package/docs/DOCTOR-AND-UPDATES.md +7 -4
- package/docs/GIT-HISTORY-REVIEW.md +39 -0
- package/docs/INSTALLATION.md +1 -1
- package/docs/MCP.md +15 -0
- package/docs/PROGRESSIVE-EVIDENCE.md +37 -0
- package/docs/REPOSITORIES-AND-WORKTREES.md +31 -1
- package/docs/SCIP-IMPORT.md +57 -0
- package/docs/SIGNED-UPDATES.md +5 -0
- package/docs/SYSTEMS-AND-RELATIONSHIPS.md +2 -2
- package/docs/TELEMETRY.md +6 -2
- package/docs/releases/0.6.0.md +18 -0
- package/docs/releases/0.7.0.md +24 -0
- package/docs/releases/0.7.1.md +21 -0
- package/docs/releases/0.7.2.md +21 -0
- package/docs/releases/0.7.3.md +23 -0
- package/package.json +34 -3
- package/dist/src/tools/reckon-tools.js +0 -5
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import childProcess from "node:child_process";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
|
|
4
|
+
import { inspectLefthookIntegration, isActiveLefthookHook } from "./hook-manager-integration.js";
|
|
5
|
+
const MANAGED_MARKER = "KNODIN MANAGED HOOK";
|
|
5
6
|
const HOOK_NAMES = ["post-commit", "post-checkout", "post-merge", "post-rewrite"];
|
|
6
7
|
const HOOK_FAILURE_FILE = "last-refresh-failure";
|
|
7
8
|
function readEvent(target) {
|
|
@@ -35,11 +36,11 @@ function readEvent(target) {
|
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
function readLastEvent(repo) {
|
|
38
|
-
return readEvent(path.join(repo, ".
|
|
39
|
+
return readEvent(path.join(repo, ".knodin", "hooks", "last-refresh-event"));
|
|
39
40
|
}
|
|
40
41
|
function readPendingEvent(repo) {
|
|
41
42
|
try {
|
|
42
|
-
const directory = path.join(repo, ".
|
|
43
|
+
const directory = path.join(repo, ".knodin", "hooks", "events");
|
|
43
44
|
const next = fs
|
|
44
45
|
.readdirSync(directory)
|
|
45
46
|
.filter((entry) => entry.endsWith(".event"))
|
|
@@ -51,7 +52,7 @@ function readPendingEvent(repo) {
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
function processorState(repo) {
|
|
54
|
-
const pidPath = path.join(repo, ".
|
|
55
|
+
const pidPath = path.join(repo, ".knodin", "hooks", "refresh.lock", "pid");
|
|
55
56
|
if (!fs.existsSync(path.dirname(pidPath)))
|
|
56
57
|
return { state: "idle", pid: null };
|
|
57
58
|
let pid = null;
|
|
@@ -78,7 +79,7 @@ function processorState(repo) {
|
|
|
78
79
|
function queuedEventCount(repo) {
|
|
79
80
|
try {
|
|
80
81
|
return fs
|
|
81
|
-
.readdirSync(path.join(repo, ".
|
|
82
|
+
.readdirSync(path.join(repo, ".knodin", "hooks", "events"))
|
|
82
83
|
.filter((entry) => entry.endsWith(".event")).length;
|
|
83
84
|
}
|
|
84
85
|
catch {
|
|
@@ -89,26 +90,45 @@ function activeHooksDirectory(repo) {
|
|
|
89
90
|
const output = childProcess.execFileSync("git", ["rev-parse", "--path-format=absolute", "--git-path", "hooks"], { cwd: repo, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
90
91
|
return output.trim();
|
|
91
92
|
}
|
|
92
|
-
function inspectManagedHooks(hooksDirectory) {
|
|
93
|
+
function inspectManagedHooks(repo, hooksDirectory) {
|
|
93
94
|
const activeHooks = [];
|
|
94
95
|
const missingHooks = [];
|
|
96
|
+
let wrappers = 0;
|
|
97
|
+
let managerNative = 0;
|
|
98
|
+
const lefthook = inspectLefthookIntegration(repo);
|
|
95
99
|
for (const hookName of HOOK_NAMES) {
|
|
96
100
|
try {
|
|
97
101
|
const hookPath = path.join(hooksDirectory, hookName);
|
|
98
102
|
const content = fs.readFileSync(hookPath, "utf-8");
|
|
99
103
|
const mode = fs.statSync(hookPath).mode;
|
|
100
|
-
const
|
|
101
|
-
|
|
104
|
+
const wrapper = content.startsWith(`#!/bin/sh\n# ${MANAGED_MARKER}: ${hookName}\n`);
|
|
105
|
+
const native = lefthook.configured && isActiveLefthookHook(content);
|
|
106
|
+
const active = (mode & 0o111) !== 0 && (wrapper || native);
|
|
107
|
+
if (active && wrapper)
|
|
108
|
+
wrappers++;
|
|
109
|
+
else if (active && native)
|
|
110
|
+
managerNative++;
|
|
102
111
|
(active ? activeHooks : missingHooks).push(hookName);
|
|
103
112
|
}
|
|
104
113
|
catch {
|
|
105
114
|
missingHooks.push(hookName);
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
activeHooks,
|
|
119
|
+
missingHooks,
|
|
120
|
+
hookManager: lefthook.configured ? "lefthook" : null,
|
|
121
|
+
routing: wrappers > 0 && managerNative > 0
|
|
122
|
+
? "mixed"
|
|
123
|
+
: wrappers > 0
|
|
124
|
+
? "managed-wrapper"
|
|
125
|
+
: managerNative > 0
|
|
126
|
+
? "manager-native"
|
|
127
|
+
: "unavailable",
|
|
128
|
+
};
|
|
109
129
|
}
|
|
110
130
|
function readRefreshFailure(repo) {
|
|
111
|
-
const failurePath = path.join(repo, ".
|
|
131
|
+
const failurePath = path.join(repo, ".knodin", "hooks", HOOK_FAILURE_FILE);
|
|
112
132
|
if (!fs.existsSync(failurePath))
|
|
113
133
|
return null;
|
|
114
134
|
try {
|
|
@@ -122,7 +142,7 @@ function readRefreshFailure(repo) {
|
|
|
122
142
|
/** Read-only proof that committed Git activity can still reach knodin's indexer. */
|
|
123
143
|
export function inspectLifecycleHealth(repoPath) {
|
|
124
144
|
const repo = path.resolve(repoPath);
|
|
125
|
-
if (!fs.existsSync(path.join(repo, ".
|
|
145
|
+
if (!fs.existsSync(path.join(repo, ".knodin", "db.sqlite"))) {
|
|
126
146
|
return {
|
|
127
147
|
status: "unconfigured",
|
|
128
148
|
refreshCapable: false,
|
|
@@ -135,6 +155,8 @@ export function inspectLifecycleHealth(repoPath) {
|
|
|
135
155
|
pendingEvent: null,
|
|
136
156
|
lastEvent: null,
|
|
137
157
|
lastError: null,
|
|
158
|
+
hookManager: null,
|
|
159
|
+
routing: "unavailable",
|
|
138
160
|
};
|
|
139
161
|
}
|
|
140
162
|
let hooksDirectory;
|
|
@@ -154,17 +176,19 @@ export function inspectLifecycleHealth(repoPath) {
|
|
|
154
176
|
pendingEvent: readPendingEvent(repo),
|
|
155
177
|
lastEvent: readLastEvent(repo),
|
|
156
178
|
lastError: null,
|
|
179
|
+
hookManager: null,
|
|
180
|
+
routing: "unavailable",
|
|
157
181
|
};
|
|
158
182
|
}
|
|
159
|
-
const { activeHooks, missingHooks } = inspectManagedHooks(hooksDirectory);
|
|
160
|
-
const background = path.join(repo, ".
|
|
183
|
+
const { activeHooks, missingHooks, hookManager, routing } = inspectManagedHooks(repo, hooksDirectory);
|
|
184
|
+
const background = path.join(repo, ".knodin", "hooks", "background-index.sh");
|
|
161
185
|
const backgroundReady = fs.existsSync(background) && (fs.statSync(background).mode & 0o111) !== 0;
|
|
162
186
|
const issues = missingHooks.map((hook) => `${hook} no longer routes through knodin's active Git hook path`);
|
|
163
187
|
if (!backgroundReady)
|
|
164
188
|
issues.push("background indexer is missing or not executable");
|
|
165
189
|
const lastError = readRefreshFailure(repo);
|
|
166
190
|
if (lastError)
|
|
167
|
-
issues.push(`${lastError} See .
|
|
191
|
+
issues.push(`${lastError} See .knodin/indexer.log for details`);
|
|
168
192
|
const refreshCapable = missingHooks.length === 0 && backgroundReady && issues.length === 0;
|
|
169
193
|
return {
|
|
170
194
|
status: refreshCapable ? "healthy" : "degraded",
|
|
@@ -178,6 +202,8 @@ export function inspectLifecycleHealth(repoPath) {
|
|
|
178
202
|
pendingEvent: readPendingEvent(repo),
|
|
179
203
|
lastEvent: readLastEvent(repo),
|
|
180
204
|
lastError,
|
|
205
|
+
hookManager,
|
|
206
|
+
routing,
|
|
181
207
|
};
|
|
182
208
|
}
|
|
183
209
|
/** Merge graph and refresh-path health without reporting absent evidence as healthy. */
|
|
@@ -211,3 +237,14 @@ export function attachLifecycleHealth(repo, graph) {
|
|
|
211
237
|
lifecycle,
|
|
212
238
|
};
|
|
213
239
|
}
|
|
240
|
+
/** Keep graph-repair success distinct from lifecycle integration health. */
|
|
241
|
+
export function attachRepairLifecycle(repo, result) {
|
|
242
|
+
const lifecycle = inspectLifecycleHealth(repo);
|
|
243
|
+
return {
|
|
244
|
+
...result,
|
|
245
|
+
lifecycle,
|
|
246
|
+
nextSteps: lifecycle.status === "degraded"
|
|
247
|
+
? ["Run `knodin init` to restore lifecycle routing, then `knodin status`."]
|
|
248
|
+
: [],
|
|
249
|
+
};
|
|
250
|
+
}
|
package/dist/src/lsp-readonly.js
CHANGED
|
@@ -59,7 +59,7 @@ function filesForService(repoPath, ts) {
|
|
|
59
59
|
const files = [];
|
|
60
60
|
const visit = (directory) => {
|
|
61
61
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
62
|
-
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".
|
|
62
|
+
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".knodin")
|
|
63
63
|
continue;
|
|
64
64
|
const candidate = path.join(directory, entry.name);
|
|
65
65
|
if (entry.isDirectory())
|
|
@@ -335,7 +335,7 @@ function percentReduction(input, output) {
|
|
|
335
335
|
}
|
|
336
336
|
function artifactDirectory(repo) {
|
|
337
337
|
const resolvedRepo = fs.realpathSync(repo);
|
|
338
|
-
const knodinDirectory = path.join(resolvedRepo, ".
|
|
338
|
+
const knodinDirectory = path.join(resolvedRepo, ".knodin");
|
|
339
339
|
const outputDirectory = path.join(knodinDirectory, "output");
|
|
340
340
|
for (const candidate of [knodinDirectory, outputDirectory]) {
|
|
341
341
|
if (fs.existsSync(candidate) && fs.lstatSync(candidate).isSymbolicLink())
|
|
@@ -5,7 +5,7 @@ import { encode } from "gpt-tokenizer/encoding/o200k_base";
|
|
|
5
5
|
export const TELEMETRY_TOKENIZER = "gpt-tokenizer@3.4.0:o200k_base";
|
|
6
6
|
export const countOutputTokens = (value) => encode(value).length;
|
|
7
7
|
export const DEFAULT_TELEMETRY_RETENTION_DAYS = 30;
|
|
8
|
-
const DEFAULT_INPUT = ".
|
|
8
|
+
const DEFAULT_INPUT = ".knodin-telemetry.jsonl";
|
|
9
9
|
function lineCount(value) {
|
|
10
10
|
return value.length === 0 ? 0 : value.split(/\r?\n/).length;
|
|
11
11
|
}
|
|
@@ -104,7 +104,7 @@ export function measureOutput(options) {
|
|
|
104
104
|
const availability = options.output.availability;
|
|
105
105
|
const freshness = options.output.freshness;
|
|
106
106
|
const fidelity = options.output.fidelity;
|
|
107
|
-
const database = path.join(fs.realpathSync(options.repo), ".
|
|
107
|
+
const database = path.join(fs.realpathSync(options.repo), ".knodin", "db.sqlite");
|
|
108
108
|
const latencyMs = Math.round((performance.now() - options.startedAt) * 100) / 100;
|
|
109
109
|
const outcome = (status !== undefined &&
|
|
110
110
|
["error", "failed", "unavailable", "blocked", "timeout"].includes(status)) ||
|
|
@@ -121,6 +121,7 @@ export function measureOutput(options) {
|
|
|
121
121
|
at: new Date().toISOString(),
|
|
122
122
|
repositoryId: repositoryIdentifier(options.repo),
|
|
123
123
|
operation: options.operation,
|
|
124
|
+
...(options.executionProfile ? { executionProfile: options.executionProfile } : {}),
|
|
124
125
|
latencyMs,
|
|
125
126
|
rssBytes: process.memoryUsage().rss,
|
|
126
127
|
temperature: "cold",
|
|
@@ -180,7 +181,7 @@ function telemetrySummary(records) {
|
|
|
180
181
|
indexingMs: records.reduce((sum, record) => sum + (record.indexingMs ?? 0), 0),
|
|
181
182
|
};
|
|
182
183
|
}
|
|
183
|
-
export function writeTelemetryReport(repoPath, records, outputPath = ".
|
|
184
|
+
export function writeTelemetryReport(repoPath, records, outputPath = ".knodin/telemetry-report.html") {
|
|
184
185
|
const { repo, target: output } = privatePath(repoPath, outputPath, "report");
|
|
185
186
|
const groups = new Map();
|
|
186
187
|
for (const record of records) {
|
|
@@ -231,6 +232,9 @@ function sanitizeTelemetryRecord(record) {
|
|
|
231
232
|
at: record.at,
|
|
232
233
|
repositoryId: record.repositoryId,
|
|
233
234
|
operation: record.operation,
|
|
235
|
+
...(record.executionProfile && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(record.executionProfile)
|
|
236
|
+
? { executionProfile: record.executionProfile }
|
|
237
|
+
: {}),
|
|
234
238
|
latencyMs: record.latencyMs,
|
|
235
239
|
rssBytes: record.rssBytes,
|
|
236
240
|
temperature: record.temperature,
|
|
@@ -326,7 +330,7 @@ export function telemetryStatus(repoPath, inputPath = DEFAULT_INPUT, retentionDa
|
|
|
326
330
|
tokenizer: TELEMETRY_TOKENIZER,
|
|
327
331
|
};
|
|
328
332
|
}
|
|
329
|
-
export function exportTelemetry(repoPath, records, outputPath = ".
|
|
333
|
+
export function exportTelemetry(repoPath, records, outputPath = ".knodin/telemetry-export.json") {
|
|
330
334
|
const { repo, target } = privatePath(repoPath, outputPath, "export");
|
|
331
335
|
const bundle = {
|
|
332
336
|
schemaVersion: 1,
|
package/dist/src/pr-triage.js
CHANGED
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
* than `ensureGhAvailable()` (throwing, still used by the CLI's hard-fail).
|
|
13
13
|
*/
|
|
14
14
|
import child_process from "node:child_process";
|
|
15
|
-
/** Resolve the `gh` binary to invoke. Tests override via `
|
|
15
|
+
/** Resolve the `gh` binary to invoke. Tests override via `KNODIN_GH_BIN` to
|
|
16
16
|
* point at a stub script fixture — no network in tests. */
|
|
17
17
|
function ghBin() {
|
|
18
|
-
return process.env.
|
|
18
|
+
return process.env.KNODIN_GH_BIN || "gh";
|
|
19
19
|
}
|
|
20
20
|
const STALE_DAYS = 30;
|
|
21
21
|
const PR_LIST_FIELDS = "number,title,updatedAt,reviewDecision,statusCheckRollup,files,headRefName";
|