knodin 0.8.7 → 0.9.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 (44) hide show
  1. package/dist/bin/cli.js +307 -85
  2. package/dist/fixtures/update-verification/config/application.xml +4 -0
  3. package/dist/fixtures/update-verification/dbt/manifest.json +17 -0
  4. package/dist/fixtures/update-verification/dbt/models/accounts.sql +1 -0
  5. package/dist/fixtures/update-verification/dbt/models/contacts.sql +1 -0
  6. package/dist/fixtures/update-verification/fixture.json +147 -0
  7. package/dist/fixtures/update-verification/force-app/main/default/classes/UpdateFixture.cls +14 -0
  8. package/dist/fixtures/update-verification/force-app/main/default/flows/Update_Verification.flow-meta.xml +9 -0
  9. package/dist/fixtures/update-verification/package.json +5 -0
  10. package/dist/fixtures/update-verification/prisma/schema.prisma +10 -0
  11. package/dist/fixtures/update-verification/sql/functions.sql +16 -0
  12. package/dist/fixtures/update-verification/src/CsharpFixture.cs +6 -0
  13. package/dist/fixtures/update-verification/src/JavaFixture.java +9 -0
  14. package/dist/fixtures/update-verification/src/javascript.js +7 -0
  15. package/dist/fixtures/update-verification/src/python_fixture.py +6 -0
  16. package/dist/fixtures/update-verification/src/typescript.ts +7 -0
  17. package/dist/fixtures/update-verification/terraform/main.tf +7 -0
  18. package/dist/fixtures/update-verification/terraform/modules/verified-child/main.tf +3 -0
  19. package/dist/src/agent-integration.js +26 -3
  20. package/dist/src/backup-retention.js +811 -0
  21. package/dist/src/cli-model.js +35 -8
  22. package/dist/src/docs-sections.js +1 -0
  23. package/dist/src/doctor.js +142 -12
  24. package/dist/src/engine/embeddings.js +8 -1
  25. package/dist/src/engine/index.js +355 -292
  26. package/dist/src/init.js +222 -179
  27. package/dist/src/manager-update.js +455 -0
  28. package/dist/src/release-preflight.js +106 -95
  29. package/dist/src/repair-lease.js +77 -8
  30. package/dist/src/response-budget.js +5 -1
  31. package/dist/src/server.js +18 -5
  32. package/dist/src/shared-index/compatibility.js +4 -2
  33. package/dist/src/tools/knodin-tools.js +69 -10
  34. package/dist/src/update-ceremony.js +20 -24
  35. package/dist/src/update-coordination.js +158 -0
  36. package/dist/src/update-executor.js +336 -0
  37. package/dist/src/update-verifier.js +358 -0
  38. package/docs/BACKUP-RETENTION.md +54 -0
  39. package/docs/CLI.md +6 -0
  40. package/docs/DOCTOR-AND-UPDATES.md +44 -12
  41. package/docs/SIGNED-UPDATES.md +29 -22
  42. package/docs/releases/0.9.0.md +43 -0
  43. package/package.json +7 -1
  44. package/roadmap/competitive-roadmap.md +8 -0
package/dist/bin/cli.js CHANGED
@@ -14,12 +14,12 @@
14
14
  import { spawn, spawnSync } from "node:child_process";
15
15
  import fs from "node:fs";
16
16
  import path from "node:path";
17
- import readline from "node:readline/promises";
18
17
  import { fileURLToPath } from "node:url";
19
18
  import { canUseSessionContextCache, parseClaudeHookPayload, readSessionContextCache, recordClaudeLifecycleEvent, renderSessionContext, SESSION_CONTEXT_BUDGET_LINE, sessionStartHookOutput, writeSessionContextCache, } from "../src/agent-events.js";
20
19
  import { inspectClaudeAgentHooks, installClaudeAgentHooks, uninstallClaudeAgentHooks, } from "../src/agent-hooks.js";
21
20
  import { detectSupportedAgents, parseInitScope, } from "../src/agent-integration.js";
22
21
  import { refreshExternalGraphArtifacts, writeArtifactRefreshRecord, } from "../src/artifact-refresh.js";
22
+ import { formatBackupHuman, installBackupRetention, listBackups, maybeRunOpportunisticRetention, pruneBackups, removeBackupRetention, retentionDoctor, retentionStatus, runInstalledRetention, } from "../src/backup-retention.js";
23
23
  import { checkIndexed, extractPositionals, extractRepoFlag, parseReviewArgs, planIndex, resolveCliRuntimeCommand, resolveRepo, } from "../src/cli-args.js";
24
24
  import { helpCommandPath, parseCliInvocation, renderCliHelp } from "../src/cli-model.js";
25
25
  import { buildKnodinContext } from "../src/context.js";
@@ -33,14 +33,16 @@ import { diagnoseFailure, } from "../src/failure-diagnosis.js";
33
33
  import { gitExecutable } from "../src/git-executable.js";
34
34
  import { decorateGraphQueryResult, inspectGraphQueryHealth } from "../src/graph-query-health.js";
35
35
  import { createIndexActivityReporter } from "../src/index-activity.js";
36
- import { detectTrackedTeamIntegration, InitializationHealthError, initializeRepository, inspectRepositoryIntegrationStatus, readRepositoryIntegrationConfig, refreshFromGitEvent, } from "../src/init.js";
36
+ import { InitializationHealthError, initializeRepository, inspectRepositoryIntegrationStatus, readRepositoryIntegrationConfig, refreshFromGitEvent, } from "../src/init.js";
37
37
  import { createInitProgressRenderer } from "../src/init-progress.js";
38
38
  import { attachLifecycleHealth, attachRepairLifecycle } from "../src/lifecycle-health.js";
39
+ import { acknowledgeUpdateFailure, queryOwnerAvailability, readManagerUpdateState, readUpdateJournal, resolveManagerOwnership, setManualPin, unpinUpdate, updateAttention, writeManagerUpdateState, } from "../src/manager-update.js";
39
40
  import { addMirror, listMirrors, refreshMirror, removeMirror } from "../src/mirror.js";
40
41
  import { compressOutput, compressOutputFile, deleteOutputArtifact, readOutputArtifact, } from "../src/output-compression.js";
41
42
  import { clearTelemetry, exportTelemetry, readTelemetryRecords, telemetryStatus, writeTelemetryReport, } from "../src/output-telemetry.js";
42
43
  import { auditPullRequests } from "../src/pr-triage.js";
43
44
  import { deliverProgressiveEvidence, } from "../src/progressive-evidence.js";
45
+ import { acquireRepairLease } from "../src/repair-lease.js";
44
46
  import { createRepairPlan, createRepairProgressRenderer, parseRepairCliArgs, resolveRepairProgressMode, serializeRepairJsonlRecord, } from "../src/repair-progress.js";
45
47
  import { runRepositoryInitializationProcess } from "../src/repository-init-process.js";
46
48
  import { detectRepositorySignals, discoverRepositories, formatRepositoryHuman, initializeRepositories, inventoryRepository, parseFleetInitArgs, parseRepositoryCommandArgs, repositorySignalInspectionLimit, searchRepositories, withRepositorySignals, } from "../src/repository-management.js";
@@ -49,6 +51,8 @@ import { appendSessionEvent, clearSessionTelemetry, disableSessionTelemetry, ena
49
51
  import { inspectKnodinSkills, installKnodinSkills, KNODIN_SKILLS, removeKnodinSkills, } from "../src/skill-management.js";
50
52
  import { configuredRepositoryInitMemoryLimitBytes, enrichSystemRelationships, incorporateSystemQueryEvidence, indexModeForPath, loadSystemConfiguration, queryConfiguredSystem, systemMembershipsForPath, validateSystemHealth, } from "../src/system-config.js";
51
53
  import { applySystemPlan, planSystemImport, planSystemLink, planSystemRelate, planSystemUnlink, planSystemUnrelate, } from "../src/system-management.js";
54
+ import { coordinationStatus } from "../src/update-coordination.js";
55
+ import { certifyAutomaticUpdates, executeManagerUpdate, recoverInterruptedUpdate, } from "../src/update-executor.js";
52
56
  import { applyTrustedUpdate, checkTrustedUpdate, claimScheduledUpdateCheck, detectUpdateInstallMethod, explainTrustedUpdate, releaseScheduledUpdateCheck, rollbackTrustedUpdate, trustedUpdateStatus, } from "../src/update-policy.js";
53
57
  import { KNODIN_VERSION } from "../src/version.js";
54
58
  import { writeVisualization, } from "../src/visualization.js";
@@ -65,44 +69,6 @@ function explicitScope(args) {
65
69
  const equals = args.find((argument) => argument.startsWith("--scope="));
66
70
  return equals ? parseInitScope(equals.slice("--scope=".length)) : null;
67
71
  }
68
- async function chooseInitScope(args, repo) {
69
- const selected = explicitScope(args);
70
- if (selected)
71
- return selected;
72
- if (detectTrackedTeamIntegration(repo)) {
73
- process.stderr.write("[init:scope] Detected tracked knodin team integration; preserving team scope\n");
74
- return "team";
75
- }
76
- if (!process.stdin.isTTY || !process.stderr.isTTY)
77
- return "personal";
78
- const prompt = readline.createInterface({
79
- input: process.stdin,
80
- output: process.stderr,
81
- });
82
- try {
83
- const answer = await prompt.question([
84
- "How should knodin integrate with coding agents?",
85
- " 1. Personal (recommended) — all detected agents; Git stays clean",
86
- " 2. Team — create commit-ready shared configuration",
87
- " 3. CLI-only — agents will not discover or invoke knodin automatically",
88
- "Select [1]: ",
89
- ].join("\n"));
90
- if (!answer.trim() || answer.trim() === "1")
91
- return "personal";
92
- if (answer.trim() === "2")
93
- return "team";
94
- if (answer.trim() === "3") {
95
- const confirmation = await prompt.question("CLI-only requires manual knodin commands. Continue? [y/N] ");
96
- if (!/^y(?:es)?$/i.test(confirmation.trim()))
97
- throw new Error("knodin init: CLI-only selection cancelled");
98
- return "cli-only";
99
- }
100
- return parseInitScope(answer.trim());
101
- }
102
- finally {
103
- prompt.close();
104
- }
105
- }
106
72
  function integrationAgents(repo) {
107
73
  const previous = readRepositoryIntegrationConfig(repo)?.agents ?? [];
108
74
  const repositoryDetected = inspectRepositoryIntegrationStatus(repo)?.agents ?? [];
@@ -151,7 +117,7 @@ function formatConfigureHuman(result) {
151
117
  const externalOutcomeLines = result.paths.externalConfigurationOutcomes
152
118
  .map(({ system, state }) => `\nExternal configuration outcome: ${system} ${state} (external mutation not locally observable)`)
153
119
  .join("");
154
- return `${result.message}\nAgent integration: ${result.paths.scope} — ${configured}${failures}${filesystemMutationLines}${externalOutcomeLines}\nGraph initialization: unchanged\nLifecycle refresh: ${refresh}\nNext: ${result.nextAction}\n`;
120
+ return `${result.message}\nAgent integration: ${result.paths.scope} — ${configured}${failures}${filesystemMutationLines}${externalOutcomeLines}\nGraph initialization: refreshed and verified\nLifecycle refresh: ${refresh}\nNext: ${result.nextAction}\n`;
155
121
  }
156
122
  function formatRepairHuman(result) {
157
123
  const coverage = result.after.coverage;
@@ -705,6 +671,26 @@ async function main() {
705
671
  // `--json` is a shared output flag. Repair owns its richer --json/--jsonl
706
672
  // parser; all other commands receive their original arguments minus it.
707
673
  const rest = cmd === "repair" ? rawRest : rawRest.filter((argument) => argument !== "--json");
674
+ let recoveryJournal = readUpdateJournal();
675
+ const recoverySafeStates = new Set(["planned", "candidate-verified", "rollback-verified"]);
676
+ if (recoveryJournal && !recoverySafeStates.has(recoveryJournal.state) && cmd !== "doctor") {
677
+ const recovery = await recoverInterruptedUpdate({ runtimeCommand, env: process.env });
678
+ recoveryJournal = readUpdateJournal();
679
+ if (recovery?.status === "terminal-unhealthy" && cmd !== "update") {
680
+ throw new Error(`knodin update recovery is terminal-unhealthy: ${recovery.reason ?? "candidate and rollback verification failed"}`);
681
+ }
682
+ }
683
+ if (recoveryJournal &&
684
+ !recoverySafeStates.has(recoveryJournal.state) &&
685
+ cmd !== "update" &&
686
+ cmd !== "doctor") {
687
+ throw new Error(`knodin is in unverified update state ${recoveryJournal.state}; run \`knodin update status\` for recovery guidance`);
688
+ }
689
+ if (!jsonOutput && cmd !== "update") {
690
+ const attention = updateAttention(readManagerUpdateState());
691
+ if (attention)
692
+ process.stderr.write(`${attention}\n`);
693
+ }
708
694
  const selector = {
709
695
  identity: selectorValue("--identity"),
710
696
  file: selectorValue("--file"),
@@ -826,7 +812,11 @@ async function main() {
826
812
  repositories,
827
813
  };
828
814
  const boundedOutput = plan.signals
829
- ? applyResponseBudget(output, "repositories:discover", { bytes: plan.byteBudget, tokens: plan.tokenBudget, items: plan.itemBudget }, { bytes: 65_536, tokens: 16_384, items: 100 })
815
+ ? applyResponseBudget(output, "repositories:discover", {
816
+ bytes: plan.byteBudget,
817
+ tokens: plan.tokenBudget,
818
+ items: plan.itemBudget,
819
+ }, { bytes: 65_536, tokens: 16_384, items: 100 })
830
820
  : output;
831
821
  process.stdout.write(`${JSON.stringify(boundedOutput, null, plan.json ? 0 : 2)}\n`);
832
822
  process.exitCode = discovery.issues.length > 0 ? 1 : 0;
@@ -903,7 +893,12 @@ async function main() {
903
893
  }));
904
894
  }
905
895
  await engine.close();
906
- const output = { schemaVersion: 1, command: "init", ...summary, repositories };
896
+ const output = {
897
+ schemaVersion: 1,
898
+ command: "init",
899
+ ...summary,
900
+ repositories,
901
+ };
907
902
  process.stdout.write(plan.json ? `${JSON.stringify(output)}\n` : formatRepositoryHuman(summary));
908
903
  process.exitCode = summary.exitCode;
909
904
  return;
@@ -1010,13 +1005,15 @@ async function main() {
1010
1005
  if (cmd === "update") {
1011
1006
  if (repoFlag !== undefined)
1012
1007
  throw new Error("knodin update does not accept --repo");
1013
- const [action, ...unsupported] = rest;
1014
- if (!action || !["status", "check", "explain", "apply", "rollback"].includes(action)) {
1015
- throw new Error("knodin update requires status, check, explain, apply, or rollback");
1016
- }
1017
- if (unsupported.length > 0)
1018
- throw new Error(`knodin update ${action}: unknown argument ${unsupported[0]}`);
1008
+ const [action, ...actionArguments] = rest;
1009
+ if (!action)
1010
+ throw new Error("knodin update requires an action");
1019
1011
  const method = detectUpdateInstallMethod(runtimeCommand);
1012
+ const owner = resolveManagerOwnership({
1013
+ runtimeCommand,
1014
+ executablePath: runtimeCommand.at(-1),
1015
+ env: process.env,
1016
+ });
1020
1017
  const runManager = async (argv) => {
1021
1018
  const [executable, ...arguments_] = argv;
1022
1019
  if (!executable)
@@ -1052,8 +1049,16 @@ async function main() {
1052
1049
  healthCheck,
1053
1050
  };
1054
1051
  let output;
1055
- if (action === "status")
1056
- output = trustedUpdateStatus(options);
1052
+ if (action === "status") {
1053
+ output = {
1054
+ ...trustedUpdateStatus(options),
1055
+ owner,
1056
+ managerState: readManagerUpdateState(),
1057
+ journal: readUpdateJournal(),
1058
+ coordination: coordinationStatus(),
1059
+ attention: updateAttention(readManagerUpdateState()),
1060
+ };
1061
+ }
1057
1062
  else if (action === "check") {
1058
1063
  try {
1059
1064
  output = await checkTrustedUpdate(options);
@@ -1067,11 +1072,178 @@ async function main() {
1067
1072
  output = explainTrustedUpdate(options);
1068
1073
  else if (action === "apply")
1069
1074
  output = await applyTrustedUpdate(options);
1070
- else
1075
+ else if (action === "rollback")
1071
1076
  output = await rollbackTrustedUpdate(options);
1077
+ else if (action === "available") {
1078
+ output = await queryOwnerAvailability({ installedVersion: KNODIN_VERSION, owner });
1079
+ const availability = output;
1080
+ const state = readManagerUpdateState();
1081
+ if (availability.latestVersion &&
1082
+ state.pin?.kind === "automatic-rollback" &&
1083
+ state.pin.acknowledgedFailedVersion === state.pin.failedVersion &&
1084
+ availability.latestVersion !== state.pin.failedVersion) {
1085
+ writeManagerUpdateState({
1086
+ ...state,
1087
+ lastAvailableVersion: availability.latestVersion,
1088
+ pin: { ...state.pin, newerAvailableVersion: availability.latestVersion },
1089
+ });
1090
+ }
1091
+ }
1092
+ else if (action === "upgrade") {
1093
+ const apply = actionArguments.includes("--apply");
1094
+ const explicitVersion = actionArguments.find((argument) => argument !== "--apply");
1095
+ let targetVersion = explicitVersion;
1096
+ if (!targetVersion) {
1097
+ const availability = await queryOwnerAvailability({
1098
+ installedVersion: KNODIN_VERSION,
1099
+ owner,
1100
+ });
1101
+ if (!availability.latestVersion)
1102
+ output = availability;
1103
+ else
1104
+ targetVersion = availability.latestVersion;
1105
+ }
1106
+ if (targetVersion)
1107
+ output = await executeManagerUpdate({
1108
+ currentVersion: KNODIN_VERSION,
1109
+ targetVersion,
1110
+ runtimeCommand,
1111
+ env: process.env,
1112
+ apply,
1113
+ });
1114
+ }
1115
+ else if (action === "pin") {
1116
+ const apply = actionArguments.includes("--apply");
1117
+ const version = actionArguments.find((argument) => argument !== "--apply");
1118
+ if (!version)
1119
+ throw new Error("knodin update pin requires a version");
1120
+ output = apply
1121
+ ? await executeManagerUpdate({
1122
+ currentVersion: KNODIN_VERSION,
1123
+ targetVersion: version,
1124
+ runtimeCommand,
1125
+ env: process.env,
1126
+ apply: true,
1127
+ pinKind: "manual",
1128
+ })
1129
+ : setManualPin(version);
1130
+ }
1131
+ else if (action === "unpin")
1132
+ output = unpinUpdate();
1133
+ else if (action === "acknowledge")
1134
+ output = acknowledgeUpdateFailure();
1135
+ else if (action === "auto") {
1136
+ const subaction = actionArguments[0];
1137
+ if (!subaction || !["status", "enable", "disable", "run"].includes(subaction))
1138
+ throw new Error("knodin update auto requires status, enable, disable, or run");
1139
+ const state = readManagerUpdateState();
1140
+ if (subaction === "status")
1141
+ output = state;
1142
+ else if (subaction === "disable") {
1143
+ output = { ...state, autoEnabled: false };
1144
+ writeManagerUpdateState(output);
1145
+ }
1146
+ else if (subaction === "enable") {
1147
+ if (state.platformAutoEligible) {
1148
+ output = { ...state, autoEnabled: true };
1149
+ writeManagerUpdateState(output);
1150
+ }
1151
+ else
1152
+ output = await certifyAutomaticUpdates({
1153
+ currentVersion: KNODIN_VERSION,
1154
+ runtimeCommand,
1155
+ env: process.env,
1156
+ });
1157
+ }
1158
+ else if (!state.autoEnabled || !state.platformAutoEligible) {
1159
+ output = { ...state, status: "disabled" };
1160
+ }
1161
+ else {
1162
+ const availability = await queryOwnerAvailability({
1163
+ installedVersion: KNODIN_VERSION,
1164
+ owner,
1165
+ });
1166
+ output =
1167
+ availability.status === "available" &&
1168
+ availability.autoEligible &&
1169
+ availability.latestVersion
1170
+ ? await executeManagerUpdate({
1171
+ currentVersion: KNODIN_VERSION,
1172
+ targetVersion: availability.latestVersion,
1173
+ runtimeCommand,
1174
+ env: process.env,
1175
+ apply: true,
1176
+ })
1177
+ : availability;
1178
+ }
1179
+ }
1180
+ else
1181
+ throw new Error(`knodin update: unknown action ${action}`);
1072
1182
  process.stdout.write(jsonOutput ? `${JSON.stringify(output)}\n` : formatGenericHuman("update", output));
1073
1183
  return;
1074
1184
  }
1185
+ if (cmd === "backups") {
1186
+ const action = invocation.commandPath[1];
1187
+ const retentionAction = invocation.commandPath[2];
1188
+ const roots = invocation.positionals;
1189
+ const defaultRoots = () => {
1190
+ if (roots.length > 0)
1191
+ return roots;
1192
+ const target = resolveRepo(repoFlag, process.cwd());
1193
+ if (!target.ok)
1194
+ throw new Error(target.error);
1195
+ return [target.repo];
1196
+ };
1197
+ const depth = invocation.options.depth;
1198
+ const retentionDays = invocation.options.retentionDays;
1199
+ const keepNewest = invocation.options.keepNewest;
1200
+ let output;
1201
+ if (action === "list")
1202
+ output = listBackups(defaultRoots(), { depth });
1203
+ else if (action === "prune") {
1204
+ output = pruneBackups(defaultRoots(), {
1205
+ depth,
1206
+ retentionDays,
1207
+ keepNewest,
1208
+ apply: invocation.options.apply === true,
1209
+ });
1210
+ }
1211
+ else if (action === "retention" && retentionAction === "install") {
1212
+ output = installBackupRetention(roots, {
1213
+ depth,
1214
+ retentionDays,
1215
+ keepNewest,
1216
+ schedule: invocation.options.schedule,
1217
+ dryRun: invocation.options.dryRun === true,
1218
+ launcher: runtimeCommand,
1219
+ });
1220
+ }
1221
+ else if (action === "retention" && retentionAction === "status") {
1222
+ output = retentionStatus();
1223
+ }
1224
+ else if (action === "retention" && retentionAction === "doctor") {
1225
+ output = retentionDoctor();
1226
+ }
1227
+ else if (action === "retention" && retentionAction === "remove") {
1228
+ output = removeBackupRetention();
1229
+ }
1230
+ else if (action === "retention" && retentionAction === "run") {
1231
+ output = runInstalledRetention();
1232
+ }
1233
+ else {
1234
+ throw new Error("knodin backups requires list, prune, or retention install|status|doctor|remove");
1235
+ }
1236
+ if (invocation.options.jsonl === true) {
1237
+ const records = output.backups ??
1238
+ output.candidates ?? [output];
1239
+ for (const record of records)
1240
+ process.stdout.write(`${JSON.stringify(record)}\n`);
1241
+ }
1242
+ else {
1243
+ process.stdout.write(jsonOutput ? `${JSON.stringify(output)}\n` : formatBackupHuman("backups", output));
1244
+ }
1245
+ return;
1246
+ }
1075
1247
  if (process.stdout.isTTY &&
1076
1248
  claimScheduledUpdateCheck({
1077
1249
  currentVersion: KNODIN_VERSION,
@@ -1123,6 +1295,14 @@ async function main() {
1123
1295
  process.exit(1);
1124
1296
  }
1125
1297
  const repo = resolved.repo;
1298
+ if (cmd !== "doctor") {
1299
+ try {
1300
+ maybeRunOpportunisticRetention(repo);
1301
+ }
1302
+ catch {
1303
+ // Best-effort cleanup must never make an ordinary command fail.
1304
+ }
1305
+ }
1126
1306
  if (cmd === "agent-event" && !fs.existsSync(resolveDbPath(repo)))
1127
1307
  return;
1128
1308
  if (cmd === "doctor") {
@@ -1246,7 +1426,10 @@ async function main() {
1246
1426
  const input = positionals[0] === "create" ? (positionals[1] ?? "-") : (positionals[0] ?? "-");
1247
1427
  compressionResult =
1248
1428
  input === "-"
1249
- ? compressOutput(repo, { ...request, text: await readBoundedStdin(maxInputBytes) })
1429
+ ? compressOutput(repo, {
1430
+ ...request,
1431
+ text: await readBoundedStdin(maxInputBytes),
1432
+ })
1250
1433
  : compressOutputFile(repo, input, request);
1251
1434
  }
1252
1435
  if (action === "diagnose" &&
@@ -1325,7 +1508,10 @@ async function main() {
1325
1508
  if (existing.state !== "configured")
1326
1509
  throw new Error("knodin shared configure --disable requires a valid existing configuration");
1327
1510
  writeSharedIndexConfig(repo, { ...existing.config, enabled: false });
1328
- result = { state: "configured", config: { ...existing.config, enabled: false } };
1511
+ result = {
1512
+ state: "configured",
1513
+ config: { ...existing.config, enabled: false },
1514
+ };
1329
1515
  break;
1330
1516
  }
1331
1517
  const required = (name) => {
@@ -1360,7 +1546,11 @@ async function main() {
1360
1546
  },
1361
1547
  };
1362
1548
  const target = writeSharedIndexConfig(repo, config);
1363
- result = { state: "configured", path: target, config: loadSharedIndexConfig(repo) };
1549
+ result = {
1550
+ state: "configured",
1551
+ path: target,
1552
+ config: loadSharedIndexConfig(repo),
1553
+ };
1364
1554
  break;
1365
1555
  }
1366
1556
  if (action === "publisher-metadata") {
@@ -1394,7 +1584,10 @@ async function main() {
1394
1584
  if (!bundlePath)
1395
1585
  throw new Error("knodin shared publisher-finalize requires <bundle>");
1396
1586
  const bundle = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), bundlePath), "utf8"));
1397
- const pointer = finalizeSharedPublisherBundle({ config: configuration.config, bundle });
1587
+ const pointer = finalizeSharedPublisherBundle({
1588
+ config: configuration.config,
1589
+ bundle,
1590
+ });
1398
1591
  result = { bundle, pointer: JSON.parse(pointer.toString("utf8")) };
1399
1592
  break;
1400
1593
  }
@@ -1421,12 +1614,19 @@ async function main() {
1421
1614
  let credentialProbe = "not-requested";
1422
1615
  if (invocation.options.probe === true) {
1423
1616
  if (configuration.state !== "configured" || !configuration.config.enabled) {
1424
- credentialProbe = { state: "unavailable", category: "configuration" };
1617
+ credentialProbe = {
1618
+ state: "unavailable",
1619
+ category: "configuration",
1620
+ };
1425
1621
  }
1426
1622
  else {
1427
1623
  const store = createSharedObjectStore(configuration.config);
1428
1624
  try {
1429
- const current = spawnSync(gitExecutable(), ["symbolic-ref", "--quiet", "--short", "HEAD"], { cwd: repo, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).stdout.trim();
1625
+ const current = spawnSync(gitExecutable(), ["symbolic-ref", "--quiet", "--short", "HEAD"], {
1626
+ cwd: repo,
1627
+ encoding: "utf8",
1628
+ stdio: ["ignore", "pipe", "ignore"],
1629
+ }).stdout.trim();
1430
1630
  const branch = configuredBranchCandidates(configuration.config, current || undefined)[0];
1431
1631
  if (!store || !branch)
1432
1632
  throw new SharedIndexError("configuration", "shared-index probe has no configured local branch candidate");
@@ -1703,7 +1903,11 @@ async function main() {
1703
1903
  const [systemId, ...repositories] = invocation.positionals;
1704
1904
  if (!systemId)
1705
1905
  throw new Error("knodin system unlink requires <system-id>");
1706
- const plan = planSystemUnlink({ systemId, repositories, repoPath: repo });
1906
+ const plan = planSystemUnlink({
1907
+ systemId,
1908
+ repositories,
1909
+ repoPath: repo,
1910
+ });
1707
1911
  result = invocation.options.dryRun === true ? plan : applySystemPlan(plan);
1708
1912
  break;
1709
1913
  }
@@ -1822,32 +2026,39 @@ async function main() {
1822
2026
  break;
1823
2027
  }
1824
2028
  case "hook-refresh": {
1825
- const [kind, first, second] = rest;
1826
- let event;
1827
- if (kind === "commit")
1828
- event = { kind };
1829
- else if (kind === "checkout" && first && second) {
1830
- event = { kind, before: first, after: second };
1831
- }
1832
- else if (kind === "merge" && first && second) {
1833
- event = { kind, before: first, after: second };
1834
- }
1835
- else if (kind === "rewrite" && first) {
1836
- event = { kind, inputPath: first };
2029
+ const lifecycleLease = acquireRepairLease(repo, "lifecycle-hook");
2030
+ try {
2031
+ const [kind, first, second] = rest;
2032
+ let event;
2033
+ if (kind === "commit")
2034
+ event = { kind };
2035
+ else if (kind === "checkout" && first && second) {
2036
+ event = { kind, before: first, after: second };
2037
+ }
2038
+ else if (kind === "merge" && first && second) {
2039
+ event = { kind, before: first, after: second };
2040
+ }
2041
+ else if (kind === "rewrite" && first) {
2042
+ event = { kind, inputPath: first };
2043
+ }
2044
+ else {
2045
+ throw new Error("knodin hook-refresh: invalid lifecycle event");
2046
+ }
2047
+ const shared = await opportunisticSharedRestore();
2048
+ const indexed = shared?.restored
2049
+ ? []
2050
+ : await refreshFromGitEvent(repo, event, (target, files) => engine.index(target, files));
2051
+ result = { indexed, ...(shared ? { sharedRestore: shared } : {}) };
1837
2052
  }
1838
- else {
1839
- throw new Error("knodin hook-refresh: invalid lifecycle event");
2053
+ finally {
2054
+ lifecycleLease.release();
1840
2055
  }
1841
- const shared = await opportunisticSharedRestore();
1842
- const indexed = shared?.restored
1843
- ? []
1844
- : await refreshFromGitEvent(repo, event, (target, files) => engine.index(target, files));
1845
- result = { indexed, ...(shared ? { sharedRestore: shared } : {}) };
1846
2056
  break;
1847
2057
  }
1848
2058
  case "init": {
1849
- const scope = await chooseInitScope(rawRest, repo);
1850
- const agents = scope === "team" ? [] : integrationAgents(repo);
2059
+ const requestedScope = explicitScope(rawRest);
2060
+ const scope = requestedScope ?? readRepositoryIntegrationConfig(repo)?.scope ?? "cli-only";
2061
+ const agents = requestedScope && scope !== "team" ? integrationAgents(repo) : [];
1851
2062
  const renderer = createInitRenderer();
1852
2063
  const activity = createIndexActivityReporter(repo);
1853
2064
  activity.start();
@@ -1865,6 +2076,7 @@ async function main() {
1865
2076
  index: (target, options) => engine.index(target, undefined, false, options),
1866
2077
  scope,
1867
2078
  agents,
2079
+ configureIntegration: requestedScope !== null,
1868
2080
  onProgress: (event) => {
1869
2081
  activity.update(event);
1870
2082
  renderer.onProgress(event);
@@ -1909,16 +2121,17 @@ async function main() {
1909
2121
  const agents = scope === "team" ? [] : integrationAgents(repo);
1910
2122
  const paths = await initializeRepository(repo, {
1911
2123
  command: runtimeCommand,
1912
- index: async () => undefined,
2124
+ index: (target, options) => engine.index(target, undefined, false, options),
1913
2125
  scope,
1914
2126
  agents,
1915
2127
  allowTrackedTransition: true,
1916
2128
  auditConfigurationChanges: true,
2129
+ configureIntegration: true,
1917
2130
  });
1918
2131
  result = {
1919
2132
  status: "success",
1920
2133
  message: `knodin agent integration changed to ${scope}.`,
1921
- graphInitialization: "unchanged",
2134
+ graphInitialization: "refreshed",
1922
2135
  nextAction: "run `knodin status` and reload the configured client",
1923
2136
  paths,
1924
2137
  };
@@ -2068,7 +2281,9 @@ async function main() {
2068
2281
  }
2069
2282
  const progressMode = resolveRepairProgressMode(options, process.env, process.stderr.isTTY);
2070
2283
  const renderer = progressMode === "tty"
2071
- ? createProgressWorkerRenderer("repair-progress-worker", { type: "start" })
2284
+ ? createProgressWorkerRenderer("repair-progress-worker", {
2285
+ type: "start",
2286
+ })
2072
2287
  : (() => {
2073
2288
  const direct = createRepairProgressRenderer({
2074
2289
  mode: progressMode,
@@ -2509,7 +2724,10 @@ async function main() {
2509
2724
  else if (action === "export")
2510
2725
  result = exportTelemetry(repo, readTelemetryRecords(repo, input, retentionDays), selectorValue("--output"), readSessionEvents(repo, retentionDays));
2511
2726
  else if (action === "clear")
2512
- result = { ...clearTelemetry(repo, input), session: clearSessionTelemetry(repo) };
2727
+ result = {
2728
+ ...clearTelemetry(repo, input),
2729
+ session: clearSessionTelemetry(repo),
2730
+ };
2513
2731
  else
2514
2732
  throw new Error("knodin telemetry requires enable, disable, status, report, export, or clear");
2515
2733
  break;
@@ -2581,7 +2799,11 @@ async function main() {
2581
2799
  result =
2582
2800
  action === "preview"
2583
2801
  ? persistDiagnosticsPreview(repo, options)
2584
- : collectDiagnostics(repo, { ...options, outputPath: outputOption, previewId });
2802
+ : collectDiagnostics(repo, {
2803
+ ...options,
2804
+ outputPath: outputOption,
2805
+ previewId,
2806
+ });
2585
2807
  }
2586
2808
  else {
2587
2809
  throw new Error("knodin diagnostics requires enable, status, preview, archive, inspect, clear, or disable");
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <application>
3
+ <component name="verified-component" implementation="src/typescript.ts" />
4
+ </application>
@@ -0,0 +1,17 @@
1
+ {
2
+ "metadata": { "dbt_schema_version": "https://schemas.getdbt.com/dbt/manifest/v12.json" },
3
+ "nodes": {
4
+ "model.fixture.accounts": {
5
+ "resource_type": "model",
6
+ "name": "accounts",
7
+ "raw_code": "select 1 as account_id",
8
+ "depends_on": { "nodes": [] }
9
+ },
10
+ "model.fixture.contacts": {
11
+ "resource_type": "model",
12
+ "name": "contacts",
13
+ "raw_code": "select account_id from accounts",
14
+ "depends_on": { "nodes": ["model.fixture.accounts"] }
15
+ }
16
+ }
17
+ }
@@ -0,0 +1 @@
1
+ select 1 as account_id
@@ -0,0 +1 @@
1
+ select account_id from {{ ref('accounts') }}