kibi-cli 2.2.0 → 2.4.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.
- package/dist/cli-json-command.d.ts.map +1 -1
- package/dist/cli-json-command.js +2 -2
- package/dist/cli-operation-loader.d.ts.map +1 -1
- package/dist/cli-operation-loader.js +3 -0
- package/dist/cli-operation-metadata.d.ts +5 -1
- package/dist/cli-operation-metadata.d.ts.map +1 -1
- package/dist/cli-operation-metadata.js +6 -1
- package/dist/cli-register-maintenance.d.ts.map +1 -1
- package/dist/cli-register-maintenance.js +7 -0
- package/dist/cli-register-reporting.d.ts.map +1 -1
- package/dist/cli-register-reporting.js +4 -0
- package/dist/commands/check-diff.d.ts +5 -0
- package/dist/commands/check-diff.d.ts.map +1 -0
- package/dist/commands/check-diff.js +62 -0
- package/dist/commands/check-generated.d.ts.map +1 -1
- package/dist/commands/check-generated.js +41 -18
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +210 -73
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +63 -29
- package/dist/commands/github-init.d.ts.map +1 -1
- package/dist/commands/github-init.js +9 -2
- package/dist/commands/merge-driver.d.ts +31 -0
- package/dist/commands/merge-driver.d.ts.map +1 -0
- package/dist/commands/merge-driver.js +268 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +3 -1
- package/dist/engine.d.ts +15 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +252 -52
- package/dist/extractors/symbols-coordinator.d.ts +7 -0
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +75 -39
- package/dist/operations/bootstrap/candidates.js +1 -1
- package/dist/operations/bootstrap/discovery.js +1 -1
- package/dist/operations/bootstrap/generate.d.ts.map +1 -1
- package/dist/operations/bootstrap/generate.js +21 -5
- package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance-actions.js +3 -0
- package/dist/operations/bootstrap/guidance.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance.js +7 -1
- package/dist/operations/bootstrap/intent-claims.d.ts +17 -0
- package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -0
- package/dist/operations/bootstrap/intent-claims.js +95 -0
- package/dist/operations/bootstrap/presentation.d.ts +2 -0
- package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
- package/dist/operations/bootstrap/presentation.js +60 -1
- package/dist/operations/bootstrap/types.d.ts +28 -0
- package/dist/operations/bootstrap/types.d.ts.map +1 -1
- package/dist/operations/bootstrap/types.js +16 -0
- package/dist/operations/modeling/predicate-inference-1.js +1 -1
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +175 -53
- package/dist/operations/mutation/symbol-refresh.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-refresh.js +4 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +5 -7
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +5 -9
- package/dist/operations/proof/ingest-proof.d.ts.map +1 -1
- package/dist/operations/proof/ingest-proof.js +1 -0
- package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analysis-receipt.js +7 -1
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +26 -4
- package/dist/operations/semantic-advisor/clauses.d.ts +1 -0
- package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/clauses.js +7 -1
- package/dist/performance-trace.d.ts +17 -0
- package/dist/performance-trace.d.ts.map +1 -0
- package/dist/performance-trace.js +134 -0
- package/dist/plugins/approved-source-analyzers.d.ts +13 -0
- package/dist/plugins/approved-source-analyzers.d.ts.map +1 -0
- package/dist/plugins/approved-source-analyzers.generated.d.ts +71 -0
- package/dist/plugins/approved-source-analyzers.generated.d.ts.map +1 -0
- package/dist/plugins/approved-source-analyzers.generated.js +77 -0
- package/dist/plugins/approved-source-analyzers.js +3 -0
- package/dist/plugins/maintenance-source-analysis.d.ts +14 -0
- package/dist/plugins/maintenance-source-analysis.d.ts.map +1 -0
- package/dist/plugins/maintenance-source-analysis.js +191 -0
- package/dist/plugins/registry.d.ts +2 -1
- package/dist/plugins/registry.d.ts.map +1 -1
- package/dist/plugins/registry.js +6 -1
- package/dist/plugins/source-analysis-service.d.ts +36 -1
- package/dist/plugins/source-analysis-service.d.ts.map +1 -1
- package/dist/plugins/source-analysis-service.js +184 -27
- package/dist/plugins/source-change-analysis.d.ts +15 -0
- package/dist/plugins/source-change-analysis.d.ts.map +1 -0
- package/dist/plugins/source-change-analysis.js +54 -0
- package/dist/plugins/source-classification.d.ts +41 -0
- package/dist/plugins/source-classification.d.ts.map +1 -0
- package/dist/plugins/source-classification.js +490 -0
- package/dist/prolog/swipl-resolver.d.ts +88 -0
- package/dist/prolog/swipl-resolver.d.ts.map +1 -0
- package/dist/prolog/swipl-resolver.js +446 -0
- package/dist/prolog.d.ts +15 -1
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +67 -6
- package/dist/public/diagnostic-usage.d.ts +14 -0
- package/dist/public/diagnostic-usage.d.ts.map +1 -1
- package/dist/public/diagnostic-usage.js +55 -1
- package/dist/public/impact/analyzer.d.ts.map +1 -1
- package/dist/public/impact/analyzer.js +56 -5
- package/dist/public/impact/diagnostics.d.ts.map +1 -1
- package/dist/public/impact/diagnostics.js +3 -1
- package/dist/public/impact/manifest.d.ts.map +1 -1
- package/dist/public/impact/manifest.js +15 -1
- package/dist/public/impact/source-changes.d.ts.map +1 -1
- package/dist/public/impact/source-changes.js +35 -27
- package/dist/public/impact/types.d.ts +2 -0
- package/dist/public/impact/types.d.ts.map +1 -1
- package/dist/public/operations/catalog.d.ts +117 -1
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/catalog.js +2 -0
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +309 -8
- package/dist/public/operations/impact-review-preparation.d.ts +14 -0
- package/dist/public/operations/impact-review-preparation.d.ts.map +1 -0
- package/dist/public/operations/impact-review-preparation.js +379 -0
- package/dist/public/operations/index.d.ts +2 -0
- package/dist/public/operations/index.d.ts.map +1 -1
- package/dist/public/operations/index.js +2 -0
- package/dist/public/operations/node-ports.d.ts.map +1 -1
- package/dist/public/operations/node-ports.js +1 -35
- package/dist/public/operations/proof-receipt-projection.d.ts +5 -0
- package/dist/public/operations/proof-receipt-projection.d.ts.map +1 -0
- package/dist/public/operations/proof-receipt-projection.js +36 -0
- package/dist/public/operations/specs/bootstrap.d.ts +73 -1
- package/dist/public/operations/specs/bootstrap.d.ts.map +1 -1
- package/dist/public/operations/specs/bootstrap.js +66 -1
- package/dist/public/operations/specs/impact-review-preparation.d.ts +47 -0
- package/dist/public/operations/specs/impact-review-preparation.d.ts.map +1 -0
- package/dist/public/operations/specs/impact-review-preparation.js +43 -0
- package/dist/public/operations/symbol-repair-plan.d.ts.map +1 -1
- package/dist/public/operations/symbol-repair-plan.js +3 -1
- package/dist/public/operations/types.d.ts +1 -1
- package/dist/public/operations/types.d.ts.map +1 -1
- package/dist/public/skills/kibi-bootstrap/SKILL.md +47 -11
- package/dist/public/skills/kibi-bootstrap/resources/bootstrap.md +6 -1
- package/dist/public/skills/kibi-usage/SKILL.md +14 -0
- package/dist/public/skills/kibi-usage/resources/operation-access.md +5 -0
- package/dist/public/telemetry-acceptance.d.ts +8 -0
- package/dist/public/telemetry-acceptance.d.ts.map +1 -1
- package/dist/public/telemetry-acceptance.js +10 -0
- package/dist/traceability/analysis-gate.d.ts +39 -0
- package/dist/traceability/analysis-gate.d.ts.map +1 -0
- package/dist/traceability/analysis-gate.js +51 -0
- package/dist/traceability/git-change-snapshot.d.ts +27 -0
- package/dist/traceability/git-change-snapshot.d.ts.map +1 -0
- package/dist/traceability/git-change-snapshot.js +411 -0
- package/dist/traceability/git-staged.d.ts +5 -1
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +16 -11
- package/dist/traceability/impact-evaluator.d.ts +54 -0
- package/dist/traceability/impact-evaluator.d.ts.map +1 -0
- package/dist/traceability/impact-evaluator.js +270 -0
- package/dist/traceability/impact-git-tree.d.ts +12 -0
- package/dist/traceability/impact-git-tree.d.ts.map +1 -0
- package/dist/traceability/impact-git-tree.js +46 -0
- package/dist/traceability/impact-knowledge.d.ts +16 -0
- package/dist/traceability/impact-knowledge.d.ts.map +1 -0
- package/dist/traceability/impact-knowledge.js +165 -0
- package/dist/traceability/impact-policy.d.ts +15 -0
- package/dist/traceability/impact-policy.d.ts.map +1 -0
- package/dist/traceability/impact-policy.js +48 -0
- package/dist/traceability/impact-policy.v1.schema.json +48 -0
- package/dist/traceability/impact-pr-diff.d.ts +5 -0
- package/dist/traceability/impact-pr-diff.d.ts.map +1 -0
- package/dist/traceability/impact-pr-diff.js +83 -0
- package/dist/traceability/impact-record-validation.d.ts +9 -0
- package/dist/traceability/impact-record-validation.d.ts.map +1 -0
- package/dist/traceability/impact-record-validation.js +113 -0
- package/dist/traceability/impact-review.d.ts +148 -0
- package/dist/traceability/impact-review.d.ts.map +1 -0
- package/dist/traceability/impact-review.js +494 -0
- package/dist/traceability/impact-review.schema.json +365 -0
- package/dist/traceability/impact-side-evidence.d.ts +13 -0
- package/dist/traceability/impact-side-evidence.d.ts.map +1 -0
- package/dist/traceability/impact-side-evidence.js +240 -0
- package/dist/traceability/reviewed-coordinate-refresh.d.ts +10 -0
- package/dist/traceability/reviewed-coordinate-refresh.d.ts.map +1 -0
- package/dist/traceability/reviewed-coordinate-refresh.js +49 -0
- package/dist/traceability/snapshot-knowledge.d.ts +4 -0
- package/dist/traceability/snapshot-knowledge.d.ts.map +1 -0
- package/dist/traceability/snapshot-knowledge.js +98 -0
- package/dist/traceability/staged-file-coverage.d.ts +23 -0
- package/dist/traceability/staged-file-coverage.d.ts.map +1 -1
- package/dist/traceability/staged-impact-contract.d.ts.map +1 -1
- package/dist/traceability/staged-impact-contract.js +2 -16
- package/dist/traceability/staged-symbols-manifest.d.ts +4 -0
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +17 -6
- package/dist/traceability/symbol-extract.d.ts +3 -5
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +49 -34
- package/dist/traceability/temp-kb.d.ts +1 -1
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +49 -13
- package/dist/traceability/trusted-pr-event.d.ts +13 -0
- package/dist/traceability/trusted-pr-event.d.ts.map +1 -0
- package/dist/traceability/trusted-pr-event.js +105 -0
- package/dist/utils/canonical-path.d.ts +3 -0
- package/dist/utils/canonical-path.d.ts.map +1 -0
- package/dist/utils/canonical-path.js +33 -0
- package/package.json +14 -5
- package/src/public/diagnostic-usage.ts +59 -1
- package/src/public/impact/analyzer.ts +88 -5
- package/src/public/impact/diagnostics.ts +7 -5
- package/src/public/impact/manifest.ts +14 -1
- package/src/public/impact/source-changes.ts +39 -34
- package/src/public/impact/types.ts +5 -0
- package/src/public/operations/catalog.ts +2 -0
- package/src/public/operations/contracts.ts +385 -9
- package/src/public/operations/impact-review-preparation.ts +548 -0
- package/src/public/operations/index.ts +2 -0
- package/src/public/operations/node-ports.ts +1 -39
- package/src/public/operations/proof-receipt-projection.ts +39 -0
- package/src/public/operations/specs/bootstrap.ts +78 -4
- package/src/public/operations/specs/impact-review-preparation.ts +46 -0
- package/src/public/operations/symbol-repair-plan.ts +3 -1
- package/src/public/operations/types.ts +1 -0
- package/src/public/skills/kibi-bootstrap/SKILL.md +47 -11
- package/src/public/skills/kibi-bootstrap/resources/bootstrap.md +6 -1
- package/src/public/skills/kibi-usage/SKILL.md +14 -0
- package/src/public/skills/kibi-usage/resources/operation-access.md +5 -0
- package/src/public/telemetry-acceptance.ts +9 -0
- package/src/traceability/impact-policy.v1.schema.json +48 -0
- package/src/traceability/impact-review.schema.json +365 -0
- package/templates/github/kibi-badge.yml +0 -4
- package/templates/github/kibi-report.yml +0 -4
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
const OBJECT_ID = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
|
|
4
|
+
function runGit(root, args) {
|
|
5
|
+
return execFileSync("git", [...args], {
|
|
6
|
+
cwd: root,
|
|
7
|
+
encoding: "utf8",
|
|
8
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
9
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
10
|
+
}).trim();
|
|
11
|
+
}
|
|
12
|
+
function resolveCommit(root, input) {
|
|
13
|
+
if (!OBJECT_ID.test(input))
|
|
14
|
+
throw new Error("PR event contains an invalid Git object ID");
|
|
15
|
+
const resolved = runGit(root, [
|
|
16
|
+
"rev-parse",
|
|
17
|
+
"--verify",
|
|
18
|
+
"--end-of-options",
|
|
19
|
+
`${input}^{commit}`,
|
|
20
|
+
]);
|
|
21
|
+
if (!OBJECT_ID.test(resolved))
|
|
22
|
+
throw new Error("Git returned an invalid commit ID");
|
|
23
|
+
return resolved;
|
|
24
|
+
}
|
|
25
|
+
function resolveTree(root, commit) {
|
|
26
|
+
const tree = runGit(root, [
|
|
27
|
+
"rev-parse",
|
|
28
|
+
"--verify",
|
|
29
|
+
"--end-of-options",
|
|
30
|
+
`${commit}^{tree}`,
|
|
31
|
+
]);
|
|
32
|
+
if (!OBJECT_ID.test(tree))
|
|
33
|
+
throw new Error("Git returned an invalid tree ID");
|
|
34
|
+
return tree;
|
|
35
|
+
}
|
|
36
|
+
/** Verify a GitHub pull_request_target envelope against the protected checkout. */
|
|
37
|
+
export function resolveTrustedPullRequestSnapshot(workspaceRoot, environment = process.env) {
|
|
38
|
+
if (environment.GITHUB_EVENT_NAME !== "pull_request_target")
|
|
39
|
+
throw new Error("Trusted impact gate requires pull_request_target context");
|
|
40
|
+
const eventPath = environment.GITHUB_EVENT_PATH;
|
|
41
|
+
const repository = environment.GITHUB_REPOSITORY;
|
|
42
|
+
const trustedTargetRef = environment.KIBI_IMPACT_TRUSTED_TARGET_REF;
|
|
43
|
+
const githubSha = environment.GITHUB_SHA;
|
|
44
|
+
if (!eventPath || !repository || !trustedTargetRef || !githubSha)
|
|
45
|
+
throw new Error("Trusted PR event, repository, target ref and GitHub event SHA are required");
|
|
46
|
+
if (!/^[^/]+\/[^/]+$/.test(repository))
|
|
47
|
+
throw new Error("GitHub repository identity is invalid");
|
|
48
|
+
if (!/^refs\/heads\/[A-Za-z0-9._/-]+$/.test(trustedTargetRef))
|
|
49
|
+
throw new Error("Trusted target must be a full refs/heads/... name");
|
|
50
|
+
const payload = JSON.parse(readFileSync(eventPath, "utf8"));
|
|
51
|
+
const request = payload.pull_request;
|
|
52
|
+
const base = request?.base;
|
|
53
|
+
const head = request?.head;
|
|
54
|
+
const baseRef = base?.ref;
|
|
55
|
+
const targetInput = base?.sha;
|
|
56
|
+
const headInput = head?.sha;
|
|
57
|
+
if (typeof baseRef !== "string" ||
|
|
58
|
+
typeof targetInput !== "string" ||
|
|
59
|
+
typeof headInput !== "string" ||
|
|
60
|
+
base?.repo?.full_name !== repository)
|
|
61
|
+
throw new Error("PR event does not identify this repository's base and head");
|
|
62
|
+
if (trustedTargetRef !== `refs/heads/${baseRef}`)
|
|
63
|
+
throw new Error("PR target does not match the configured protected branch");
|
|
64
|
+
if (payload.repository?.full_name !== repository)
|
|
65
|
+
throw new Error("PR event repository identity does not match the trusted checkout");
|
|
66
|
+
const targetCommit = resolveCommit(workspaceRoot, targetInput);
|
|
67
|
+
const headCommit = resolveCommit(workspaceRoot, headInput);
|
|
68
|
+
if (resolveCommit(workspaceRoot, githubSha) !== targetCommit)
|
|
69
|
+
throw new Error("GitHub event target SHA does not match its trusted payload");
|
|
70
|
+
const checkoutCommit = resolveCommit(workspaceRoot, runGit(workspaceRoot, ["rev-parse", "HEAD"]));
|
|
71
|
+
if (checkoutCommit !== targetCommit)
|
|
72
|
+
throw new Error("Trusted evaluator checkout is not the PR target commit");
|
|
73
|
+
if (runGit(workspaceRoot, ["rev-parse", "--is-shallow-repository"]) !== "false")
|
|
74
|
+
throw new Error("Complete Git history is required for impact evaluation");
|
|
75
|
+
const missing = runGit(workspaceRoot, [
|
|
76
|
+
"rev-list",
|
|
77
|
+
"--objects",
|
|
78
|
+
"--missing=print",
|
|
79
|
+
targetCommit,
|
|
80
|
+
headCommit,
|
|
81
|
+
]);
|
|
82
|
+
if (missing.split("\n").some((line) => line.startsWith("?")))
|
|
83
|
+
throw new Error("PR target or head has missing reachable Git objects");
|
|
84
|
+
const mergeBases = runGit(workspaceRoot, [
|
|
85
|
+
"merge-base",
|
|
86
|
+
"--all",
|
|
87
|
+
targetCommit,
|
|
88
|
+
headCommit,
|
|
89
|
+
])
|
|
90
|
+
.split("\n")
|
|
91
|
+
.filter(Boolean);
|
|
92
|
+
if (mergeBases.length !== 1)
|
|
93
|
+
throw new Error("PR target and head must have exactly one merge base");
|
|
94
|
+
const mergeBaseCommit = resolveCommit(workspaceRoot, mergeBases[0] ?? "");
|
|
95
|
+
return {
|
|
96
|
+
repository,
|
|
97
|
+
targetRef: trustedTargetRef,
|
|
98
|
+
targetCommit,
|
|
99
|
+
targetTree: resolveTree(workspaceRoot, targetCommit),
|
|
100
|
+
headCommit,
|
|
101
|
+
headTree: resolveTree(workspaceRoot, headCommit),
|
|
102
|
+
mergeBaseCommit,
|
|
103
|
+
mergeBaseTree: resolveTree(workspaceRoot, mergeBaseCommit),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-path.d.ts","sourceRoot":"","sources":["../../src/utils/canonical-path.ts"],"names":[],"mappings":"AAGA,oFAAoF;AACpF,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAwBjE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { lstatSync, realpathSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/** Resolve filesystem aliases while preserving a suffix that does not exist yet. */
|
|
4
|
+
export function canonicalFilesystemPath(candidate) {
|
|
5
|
+
let existing = path.resolve(candidate);
|
|
6
|
+
const missing = [];
|
|
7
|
+
for (;;) {
|
|
8
|
+
try {
|
|
9
|
+
return path.resolve(realpathSync.native(existing), ...missing);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error.code !== "ENOENT")
|
|
13
|
+
throw error;
|
|
14
|
+
// A dangling symlink is an existing entry, not a missing path segment.
|
|
15
|
+
// Refuse it rather than allowing a later write to follow an unknown target.
|
|
16
|
+
let entry;
|
|
17
|
+
try {
|
|
18
|
+
entry = lstatSync(existing);
|
|
19
|
+
}
|
|
20
|
+
catch (statError) {
|
|
21
|
+
if (statError.code !== "ENOENT")
|
|
22
|
+
throw statError;
|
|
23
|
+
}
|
|
24
|
+
if (entry !== undefined)
|
|
25
|
+
throw error;
|
|
26
|
+
const parent = path.dirname(existing);
|
|
27
|
+
if (parent === existing)
|
|
28
|
+
throw error;
|
|
29
|
+
missing.unshift(path.basename(existing));
|
|
30
|
+
existing = parent;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -10,7 +10,15 @@
|
|
|
10
10
|
"bin": {
|
|
11
11
|
"kibi": "bin/kibi"
|
|
12
12
|
},
|
|
13
|
-
"files": [
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"bin",
|
|
16
|
+
"schema",
|
|
17
|
+
"src/schemas",
|
|
18
|
+
"src/public",
|
|
19
|
+
"src/traceability/*.schema.json",
|
|
20
|
+
"templates"
|
|
21
|
+
],
|
|
14
22
|
"scripts": {
|
|
15
23
|
"build": "tsc -p tsconfig.json && rm -rf dist/public/skills && mkdir -p dist/public/skills dist/report && cp -r src/public/skills/. dist/public/skills/ && cp src/report/html-report.css dist/report/html-report.css && node ../../scripts/generate-entity-schema.mjs --check",
|
|
16
24
|
"test": "node ../../scripts/run-owned-engine-tests.mjs -- bun test --timeout 15000 .",
|
|
@@ -227,9 +235,10 @@
|
|
|
227
235
|
"fast-glob": "^3.2.12",
|
|
228
236
|
"gray-matter": "^4.0.3",
|
|
229
237
|
"js-yaml": "^4.1.0",
|
|
230
|
-
"kibi-core": "^0.14.
|
|
231
|
-
"kibi-plugin-builtin": "^0.
|
|
232
|
-
"kibi-plugin-sdk": "^0.
|
|
238
|
+
"kibi-core": "^0.14.1",
|
|
239
|
+
"kibi-plugin-builtin": "^0.4.0",
|
|
240
|
+
"kibi-plugin-sdk": "^0.4.0",
|
|
241
|
+
"kibi-swipl": "^1.0.0",
|
|
233
242
|
"saxes": "^6.0.0",
|
|
234
243
|
"ignore": "^5.3.0",
|
|
235
244
|
"yaml": "^2.9.0"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
appendPayloadCountField,
|
|
@@ -241,6 +242,60 @@ export function deriveDiagnosticUsageFields(
|
|
|
241
242
|
return fields;
|
|
242
243
|
}
|
|
243
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Whether the operator opted into usage telemetry through the environment.
|
|
247
|
+
*
|
|
248
|
+
* `KIBI_DIAGNOSTIC_MODE` is the one opt-in shared with the MCP server, so a
|
|
249
|
+
* single setting covers both peer surfaces. `KIBI_CLI_DIAGNOSTIC_MODE` is the
|
|
250
|
+
* older CLI-only spelling and keeps working.
|
|
251
|
+
*/
|
|
252
|
+
export function diagnosticEnvOptIn(
|
|
253
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
254
|
+
): boolean {
|
|
255
|
+
return ["KIBI_DIAGNOSTIC_MODE", "KIBI_CLI_DIAGNOSTIC_MODE"].some((key) => {
|
|
256
|
+
const value = env[key]?.trim().toLowerCase();
|
|
257
|
+
return value === "1" || value === "true";
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* The host that ran this CLI call. Hosts that launch Kibi set `KIBI_HOST`;
|
|
263
|
+
* Claude Code marks every shell it runs with `CLAUDECODE=1`, which is the
|
|
264
|
+
* only ambient signal trusted here. Anything else stays `unknown`.
|
|
265
|
+
*/
|
|
266
|
+
export function diagnosticCliHost(
|
|
267
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
268
|
+
): string {
|
|
269
|
+
const explicit = env.KIBI_HOST?.trim() || env.KIBI_MCP_HOST?.trim();
|
|
270
|
+
if (explicit) return explicit;
|
|
271
|
+
return env.CLAUDECODE === "1" ? "claude-code" : "unknown";
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let cachedCliPackageVersion: string | null | undefined;
|
|
275
|
+
|
|
276
|
+
function cliPackageVersion(): string | null {
|
|
277
|
+
if (cachedCliPackageVersion !== undefined) return cachedCliPackageVersion;
|
|
278
|
+
cachedCliPackageVersion = null;
|
|
279
|
+
let current = path.dirname(fileURLToPath(import.meta.url));
|
|
280
|
+
while (true) {
|
|
281
|
+
const candidate = path.join(current, "package.json");
|
|
282
|
+
if (existsSync(candidate)) {
|
|
283
|
+
try {
|
|
284
|
+
const parsed: unknown = JSON.parse(readFileSync(candidate, "utf8"));
|
|
285
|
+
if (isRecord(parsed) && typeof parsed.version === "string") {
|
|
286
|
+
cachedCliPackageVersion = parsed.version;
|
|
287
|
+
}
|
|
288
|
+
} catch {
|
|
289
|
+
// An unreadable manifest leaves the version unknown.
|
|
290
|
+
}
|
|
291
|
+
return cachedCliPackageVersion;
|
|
292
|
+
}
|
|
293
|
+
const parent = path.dirname(current);
|
|
294
|
+
if (parent === current) return null;
|
|
295
|
+
current = parent;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
244
299
|
export function appendCliDiagnosticUsage(input: DiagnosticUsageInput): void {
|
|
245
300
|
const finishedAt = input.finishedAt ?? new Date();
|
|
246
301
|
const requestId = input.requestId ?? `cli-${randomUUID()}`;
|
|
@@ -256,6 +311,9 @@ export function appendCliDiagnosticUsage(input: DiagnosticUsageInput): void {
|
|
|
256
311
|
request_id: requestId,
|
|
257
312
|
tool: input.tool,
|
|
258
313
|
interface: "cli_json",
|
|
314
|
+
host: diagnosticCliHost(),
|
|
315
|
+
package_version: cliPackageVersion(),
|
|
316
|
+
workspace_root: input.workspaceRoot,
|
|
259
317
|
telemetry: input.telemetry,
|
|
260
318
|
business_args: input.businessArgs,
|
|
261
319
|
status: input.status,
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
createMaintenanceSourceAnalysisService,
|
|
4
|
+
readSnapshotSourceConfig,
|
|
5
|
+
} from "../../plugins/maintenance-source-analysis.js";
|
|
6
|
+
import {
|
|
7
|
+
analyzeSourceChanges,
|
|
8
|
+
assertSourceAnalysisGate,
|
|
9
|
+
} from "../../plugins/source-change-analysis.js";
|
|
10
|
+
import { captureStagedSnapshot } from "../../traceability/git-change-snapshot.js";
|
|
11
|
+
import { readSnapshotKnowledge } from "../../traceability/snapshot-knowledge.js";
|
|
2
12
|
import type { ExtractedSymbol } from "../../traceability/symbol-extract.js";
|
|
3
13
|
import { extractSymbolsFromStagedFileAsync } from "../../traceability/symbol-extract.js";
|
|
4
14
|
import {
|
|
@@ -9,6 +19,7 @@ import {
|
|
|
9
19
|
createImpactManifestLookup,
|
|
10
20
|
readImpactManifestResults,
|
|
11
21
|
} from "./manifest.js";
|
|
22
|
+
import { normalizeSourceFile } from "./source-changes.js";
|
|
12
23
|
import { collectSourceChanges, uniqueSorted } from "./source-changes.js";
|
|
13
24
|
import {
|
|
14
25
|
buildNextActions,
|
|
@@ -25,16 +36,73 @@ export async function analyzeChangedFileImpact(
|
|
|
25
36
|
options: ChangedFileImpactOptions,
|
|
26
37
|
): Promise<ChangedFileImpactResult> {
|
|
27
38
|
const workspaceRoot = path.resolve(options.workspaceRoot);
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
const snapshot = options.staged
|
|
40
|
+
? captureStagedSnapshot(workspaceRoot)
|
|
41
|
+
: undefined;
|
|
42
|
+
const filter = new Set(
|
|
43
|
+
(options.sourceFiles ?? []).map((file) =>
|
|
44
|
+
normalizeSourceFile(workspaceRoot, file),
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
const snapshotAnalysisInventory = snapshot
|
|
48
|
+
? snapshot.inventory.filter(
|
|
49
|
+
(file) => filter.size === 0 || filter.has(file.path),
|
|
50
|
+
)
|
|
51
|
+
: undefined;
|
|
52
|
+
const snapshotSourceInventory = snapshot
|
|
53
|
+
? snapshot.inventory.filter(
|
|
54
|
+
(file) =>
|
|
55
|
+
file.analysisDepth !== "metadata" &&
|
|
56
|
+
!file.skipReason &&
|
|
57
|
+
file.content !== undefined &&
|
|
58
|
+
(filter.size === 0 || filter.has(file.path)),
|
|
59
|
+
)
|
|
60
|
+
: undefined;
|
|
61
|
+
const sourceChanges = snapshot
|
|
62
|
+
? (snapshotSourceInventory ?? []).map((file) => ({
|
|
63
|
+
file: file.path,
|
|
64
|
+
status: file.status,
|
|
65
|
+
hunkRanges: file.hunkRanges,
|
|
66
|
+
content: file.content ?? "",
|
|
67
|
+
}))
|
|
68
|
+
: collectSourceChanges({ ...options, workspaceRoot });
|
|
69
|
+
const service = createMaintenanceSourceAnalysisService(
|
|
70
|
+
workspaceRoot,
|
|
71
|
+
snapshot ? readSnapshotSourceConfig(snapshot) : undefined,
|
|
72
|
+
);
|
|
73
|
+
const analyses = snapshot
|
|
74
|
+
? await analyzeSourceChanges(snapshotAnalysisInventory ?? [], service)
|
|
75
|
+
: undefined;
|
|
76
|
+
if (analyses)
|
|
77
|
+
assertSourceAnalysisGate(snapshotAnalysisInventory ?? [], analyses);
|
|
78
|
+
const sourceFiles = uniqueSorted(
|
|
79
|
+
snapshot
|
|
80
|
+
? snapshot.inventory
|
|
81
|
+
.filter(
|
|
82
|
+
(file) =>
|
|
83
|
+
file.analysisDepth !== "metadata" &&
|
|
84
|
+
(filter.size === 0 || filter.has(file.path)),
|
|
85
|
+
)
|
|
86
|
+
.map((file) => file.path)
|
|
87
|
+
: sourceChanges.map((change) => change.file),
|
|
88
|
+
);
|
|
89
|
+
const manifestResults = snapshot
|
|
90
|
+
? readSnapshotKnowledge(
|
|
91
|
+
snapshot.readGit,
|
|
92
|
+
snapshot.headTree,
|
|
93
|
+
snapshot.readBlobs,
|
|
94
|
+
).filter((result) => result.entity.type === "symbol")
|
|
95
|
+
: readImpactManifestResults(workspaceRoot);
|
|
31
96
|
const manifestLookup = createImpactManifestLookup(manifestResults);
|
|
32
97
|
const symbolsByFile = new Map<string, ExtractedSymbol[]>();
|
|
33
98
|
const sourceContentByFile = new Map<string, string>();
|
|
99
|
+
const sourceSymbolsByFile = new Map<string, ExtractedSymbol[]>();
|
|
34
100
|
|
|
35
101
|
for (const change of sourceChanges) {
|
|
36
102
|
sourceContentByFile.set(change.file, change.content);
|
|
37
|
-
|
|
103
|
+
const analysis =
|
|
104
|
+
analyses?.get(change.file)?.after ??
|
|
105
|
+
(await service.analyzeTextV2(change.file, change.content));
|
|
38
106
|
const extracted = await extractSymbolsFromStagedFileAsync(
|
|
39
107
|
{
|
|
40
108
|
path: change.file,
|
|
@@ -43,8 +111,21 @@ export async function analyzeChangedFileImpact(
|
|
|
43
111
|
content: change.content,
|
|
44
112
|
},
|
|
45
113
|
manifestLookup,
|
|
46
|
-
{},
|
|
114
|
+
{ analyzeText: async () => analysis },
|
|
115
|
+
);
|
|
116
|
+
const allSymbols = await extractSymbolsFromStagedFileAsync(
|
|
117
|
+
{
|
|
118
|
+
path: change.file,
|
|
119
|
+
status: change.status,
|
|
120
|
+
content: change.content,
|
|
121
|
+
hunkRanges: [
|
|
122
|
+
{ start: 1, end: Math.max(1, change.content.split("\n").length) },
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
manifestLookup,
|
|
126
|
+
{ analyzeText: async () => analysis },
|
|
47
127
|
);
|
|
128
|
+
sourceSymbolsByFile.set(change.file, allSymbols);
|
|
48
129
|
symbolsByFile.set(
|
|
49
130
|
change.file,
|
|
50
131
|
extracted.filter((symbol) => symbol.hunkRanges.length > 0),
|
|
@@ -65,6 +146,7 @@ export async function analyzeChangedFileImpact(
|
|
|
65
146
|
manifestResults: activeManifestResults,
|
|
66
147
|
symbolsByFile,
|
|
67
148
|
sourceContentByFile,
|
|
149
|
+
sourceSymbolsByFile,
|
|
68
150
|
workspaceRoot,
|
|
69
151
|
}),
|
|
70
152
|
...createSymbolQualityDiagnostics({
|
|
@@ -79,6 +161,7 @@ export async function analyzeChangedFileImpact(
|
|
|
79
161
|
? impactDiagnostics.slice(0, maxDiagnostics)
|
|
80
162
|
: impactDiagnostics;
|
|
81
163
|
|
|
164
|
+
snapshot?.assertUnchanged();
|
|
82
165
|
return {
|
|
83
166
|
impactDiagnostics: cappedDiagnostics,
|
|
84
167
|
sourceFiles,
|
|
@@ -74,11 +74,13 @@ function getGranularSymbolsForSourceFile(
|
|
|
74
74
|
readonly candidateSymbols: readonly ExtractedSymbol[];
|
|
75
75
|
readonly sourceSymbols: readonly ExtractedSymbol[];
|
|
76
76
|
} {
|
|
77
|
-
const sourceSymbols =
|
|
78
|
-
sourceFile
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
const sourceSymbols = options.sourceSymbolsByFile
|
|
78
|
+
? (options.sourceSymbolsByFile.get(sourceFile) ?? [])
|
|
79
|
+
: readSourceSymbols(
|
|
80
|
+
sourceFile,
|
|
81
|
+
options.sourceContentByFile,
|
|
82
|
+
options.workspaceRoot ?? process.cwd(),
|
|
83
|
+
);
|
|
82
84
|
const changedSymbols = options.symbolsByFile.get(sourceFile);
|
|
83
85
|
|
|
84
86
|
return {
|
|
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { extractFromManifest } from "../../extractors/manifest.js";
|
|
4
4
|
import type { ExtractionResult } from "../../extractors/markdown.js";
|
|
5
|
+
import { createManifestLookupSentinelKey } from "../../traceability/symbol-extract.js";
|
|
5
6
|
import type { ManifestLookup } from "../../traceability/symbol-extract.js";
|
|
6
7
|
import { CANONICAL_ENTITY_PATHS } from "../../utils/kb-paths.js";
|
|
7
8
|
import { isTraceabilityRelationshipType } from "../symbol-granularity.js";
|
|
@@ -42,10 +43,22 @@ export function createImpactManifestLookup(
|
|
|
42
43
|
manifestResults: readonly ExtractionResult[],
|
|
43
44
|
): ManifestLookup {
|
|
44
45
|
const manifestLookup: ManifestLookup = new Map();
|
|
46
|
+
manifestLookup.set(createManifestLookupSentinelKey(".kb/symbols.yaml"), {
|
|
47
|
+
id: "__captured_manifest__",
|
|
48
|
+
relationships: [],
|
|
49
|
+
});
|
|
50
|
+
const ambiguous = new Set<string>();
|
|
45
51
|
|
|
46
52
|
for (const result of manifestResults) {
|
|
47
53
|
const sourceFile = result.sourceFile ?? result.entity.source;
|
|
48
|
-
|
|
54
|
+
const key = `${sourceFile}:${result.entity.title}`;
|
|
55
|
+
if (ambiguous.has(key)) continue;
|
|
56
|
+
if (manifestLookup.has(key)) {
|
|
57
|
+
manifestLookup.delete(key);
|
|
58
|
+
ambiguous.add(key);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
manifestLookup.set(key, {
|
|
49
62
|
id: result.entity.id,
|
|
50
63
|
relationships: toTraceabilityRelationships(result.relationships),
|
|
51
64
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
2
|
+
import { existsSync, lstatSync, readFileSync, realpathSync } from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { isQualifiedSourcePath } from "../../plugins/source-classification.js";
|
|
4
5
|
import type { HunkRange } from "../../traceability/git-staged.js";
|
|
5
6
|
import {
|
|
6
7
|
getStagedFiles,
|
|
@@ -9,19 +10,8 @@ import {
|
|
|
9
10
|
import { hasMeaningfulSourceDiff } from "./diff-meaning.js";
|
|
10
11
|
import type { ChangedFileImpactOptions, SourceChange } from "./types.js";
|
|
11
12
|
|
|
12
|
-
const SOURCE_EXTENSIONS = new Set([
|
|
13
|
-
".ts",
|
|
14
|
-
".tsx",
|
|
15
|
-
".js",
|
|
16
|
-
".jsx",
|
|
17
|
-
".mts",
|
|
18
|
-
".cts",
|
|
19
|
-
".mjs",
|
|
20
|
-
".cjs",
|
|
21
|
-
]);
|
|
22
|
-
|
|
23
13
|
function isSupportedSourcePath(filePath: string): boolean {
|
|
24
|
-
return
|
|
14
|
+
return isQualifiedSourcePath(filePath);
|
|
25
15
|
}
|
|
26
16
|
|
|
27
17
|
export function normalizeSourceFile(
|
|
@@ -38,12 +28,12 @@ export function uniqueSorted(values: Iterable<string>): string[] {
|
|
|
38
28
|
return [...new Set(values)].sort();
|
|
39
29
|
}
|
|
40
30
|
|
|
41
|
-
function
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
31
|
+
function runGit(workspaceRoot: string, args: readonly string[]): string {
|
|
32
|
+
return execFileSync("git", [...args], {
|
|
33
|
+
cwd: workspaceRoot,
|
|
34
|
+
encoding: "utf8",
|
|
35
|
+
env: { ...process.env, GIT_LITERAL_PATHSPECS: "1" },
|
|
36
|
+
});
|
|
47
37
|
}
|
|
48
38
|
|
|
49
39
|
function readWorkingTreeSource(
|
|
@@ -51,8 +41,24 @@ function readWorkingTreeSource(
|
|
|
51
41
|
sourceFile: string,
|
|
52
42
|
): string | null {
|
|
53
43
|
const absolutePath = path.resolve(workspaceRoot, sourceFile);
|
|
54
|
-
|
|
55
|
-
|
|
44
|
+
const relative = path.relative(workspaceRoot, absolutePath);
|
|
45
|
+
if (!relative || relative.startsWith("..") || path.isAbsolute(relative))
|
|
46
|
+
throw new Error("Source path must remain inside the workspace");
|
|
47
|
+
if (!existsSync(absolutePath) || !lstatSync(absolutePath).isFile())
|
|
48
|
+
return null;
|
|
49
|
+
const actualRelative = path.relative(
|
|
50
|
+
realpathSync(workspaceRoot),
|
|
51
|
+
realpathSync(absolutePath),
|
|
52
|
+
);
|
|
53
|
+
if (actualRelative.startsWith("..") || path.isAbsolute(actualRelative))
|
|
54
|
+
throw new Error("Source path escapes the workspace");
|
|
55
|
+
const bytes = readFileSync(absolutePath);
|
|
56
|
+
if (bytes.includes(0)) return null;
|
|
57
|
+
try {
|
|
58
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
function fullFileHunk(content: string): HunkRange[] {
|
|
@@ -62,9 +68,9 @@ function fullFileHunk(content: string): HunkRange[] {
|
|
|
62
68
|
function getWorkingTreeDiffSourceFiles(workspaceRoot: string): string[] {
|
|
63
69
|
try {
|
|
64
70
|
return uniqueSorted(
|
|
65
|
-
runGit(workspaceRoot, "
|
|
66
|
-
.split(
|
|
67
|
-
.map((filePath) => normalizeSourceFile(workspaceRoot, filePath
|
|
71
|
+
runGit(workspaceRoot, ["diff", "--name-only", "-z", "--diff-filter=ACMR"])
|
|
72
|
+
.split("\0")
|
|
73
|
+
.map((filePath) => normalizeSourceFile(workspaceRoot, filePath))
|
|
68
74
|
.filter((filePath) => filePath.length > 0)
|
|
69
75
|
.filter(isSupportedSourcePath),
|
|
70
76
|
);
|
|
@@ -78,10 +84,7 @@ function getWorkingTreeHunks(
|
|
|
78
84
|
sourceFile: string,
|
|
79
85
|
): HunkRange[] {
|
|
80
86
|
try {
|
|
81
|
-
const diffText = runGit(
|
|
82
|
-
workspaceRoot,
|
|
83
|
-
`git diff -U0 -- "${escapeGitPath(sourceFile)}"`,
|
|
84
|
-
);
|
|
87
|
+
const diffText = runGit(workspaceRoot, ["diff", "-U0", "--", sourceFile]);
|
|
85
88
|
return parseHunksFromDiff(diffText);
|
|
86
89
|
} catch {
|
|
87
90
|
return [];
|
|
@@ -104,6 +107,7 @@ function getStagedSourceChanges(
|
|
|
104
107
|
.flatMap((file): SourceChange[] => {
|
|
105
108
|
if (file.content === undefined || file.hunkRanges.length === 0) return [];
|
|
106
109
|
if (
|
|
110
|
+
!/\.(pyi?|go|rs)$/.test(file.path) &&
|
|
107
111
|
file.diffText !== undefined &&
|
|
108
112
|
!hasMeaningfulSourceDiff(file.diffText)
|
|
109
113
|
) {
|
|
@@ -132,11 +136,12 @@ function getWorkingTreeSourceChanges(
|
|
|
132
136
|
return sourceFiles.flatMap((sourceFile): SourceChange[] => {
|
|
133
137
|
const content = readWorkingTreeSource(workspaceRoot, sourceFile);
|
|
134
138
|
if (content === null) return [];
|
|
135
|
-
const diffText = runGit(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
const diffText = runGit(workspaceRoot, ["diff", "-U0", "--", sourceFile]);
|
|
140
|
+
if (
|
|
141
|
+
!/\.(pyi?|go|rs)$/.test(sourceFile) &&
|
|
142
|
+
!hasMeaningfulSourceDiff(diffText)
|
|
143
|
+
)
|
|
144
|
+
return [];
|
|
140
145
|
const hunkRanges = getWorkingTreeHunks(workspaceRoot, sourceFile);
|
|
141
146
|
if (hunkRanges.length === 0) return [];
|
|
142
147
|
return [{ file: sourceFile, status: "M", hunkRanges, content }];
|
|
@@ -39,6 +39,11 @@ export type SymbolGranularityDiagnosticsOptions = {
|
|
|
39
39
|
readonly activeEntityIds?: ReadonlySet<string>;
|
|
40
40
|
readonly symbolsByFile: SymbolsByFile;
|
|
41
41
|
readonly sourceContentByFile?: ReadonlyMap<string, string>;
|
|
42
|
+
/** Complete snapshot analysis; when supplied, filesystem fallback is forbidden. */
|
|
43
|
+
readonly sourceSymbolsByFile?: ReadonlyMap<
|
|
44
|
+
string,
|
|
45
|
+
readonly ExtractedSymbol[]
|
|
46
|
+
>;
|
|
42
47
|
readonly workspaceRoot?: string;
|
|
43
48
|
};
|
|
44
49
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { planBootstrapSpec } from "./specs/bootstrap.js";
|
|
7
7
|
import { checkSpec } from "./specs/check.js";
|
|
8
8
|
import { querySpec, searchSpec, statusSpec } from "./specs/discovery.js";
|
|
9
|
+
import { prepareImpactReviewSpec } from "./specs/impact-review-preparation.js";
|
|
9
10
|
import {
|
|
10
11
|
modelRequirementSpec,
|
|
11
12
|
suggestPredicatesSpec,
|
|
@@ -49,6 +50,7 @@ export const OPERATION_CATALOG = [
|
|
|
49
50
|
upsertSpec,
|
|
50
51
|
deleteSpec,
|
|
51
52
|
checkSpec,
|
|
53
|
+
prepareImpactReviewSpec,
|
|
52
54
|
sparqlRemoteSpec,
|
|
53
55
|
compileIntentSpec,
|
|
54
56
|
applyPlanSpec,
|