archctx 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/catalog.yaml +2 -2
- package/bin/archctx.mjs +1899 -311
- package/package.json +1 -1
- package/schemas/runtime/archctx-capabilities.schema.json +1 -1
- package/schemas/runtime/capability-dependencies.schema.json +116 -0
- package/schemas/runtime/docs-anchors.schema.json +143 -0
- package/schemas/runtime/flow-selector-check.schema.json +109 -0
- package/schemas/runtime/recommendation-v3.schema.json +1 -0
- package/schemas/runtime/runtime-recommendation-show.schema.json +14 -0
- package/schemas/runtime/runtime-refactor-scan.schema.json +19 -0
- package/schemas/runtime/structural-observation-payload.schema.json +1 -0
package/bin/archctx.mjs
CHANGED
|
@@ -167,6 +167,65 @@ var init_architecture = __esm(() => {
|
|
|
167
167
|
];
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
+
// packages/contracts/src/docs-anchors.ts
|
|
171
|
+
function docsAnchorsReportIssues(report) {
|
|
172
|
+
const issues = [];
|
|
173
|
+
const ids = report.nodes.map((node) => node.nodeId);
|
|
174
|
+
const sorted = [...new Set(ids)].sort();
|
|
175
|
+
if (sorted.length !== ids.length || sorted.some((id, index) => id !== ids[index]))
|
|
176
|
+
issues.push("nodes.nodeId must be sorted and unique");
|
|
177
|
+
const findings = report.nodes.flatMap((node) => node.findings);
|
|
178
|
+
const unchecked = report.nodes.flatMap((node) => node.unchecked);
|
|
179
|
+
if (report.summary.citations !== report.nodes.reduce((total, node) => total + node.citationCount, 0))
|
|
180
|
+
issues.push("summary.citations must equal the sum of nodes[].citationCount");
|
|
181
|
+
if (report.summary.findings !== findings.length)
|
|
182
|
+
issues.push("summary.findings must equal the number of findings");
|
|
183
|
+
if (report.summary.unchecked !== unchecked.length)
|
|
184
|
+
issues.push("summary.unchecked must equal the number of unchecked citations");
|
|
185
|
+
for (const kind of DOCS_ANCHOR_FINDING_KINDS) {
|
|
186
|
+
if (report.summary.byKind[kind] !== findings.filter((finding) => finding.kind === kind).length)
|
|
187
|
+
issues.push(`summary.byKind.${kind} must equal the number of ${kind} findings`);
|
|
188
|
+
}
|
|
189
|
+
for (const [index, node] of report.nodes.entries()) {
|
|
190
|
+
if (node.citationCount === 0)
|
|
191
|
+
issues.push(`nodes[${index}] must have at least one citation`);
|
|
192
|
+
for (const [findingIndex, finding] of node.findings.entries()) {
|
|
193
|
+
issues.push(...docsAnchorFindingIssues(finding, `nodes[${index}].findings[${findingIndex}]`));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (report.codeGraph.availability === "ready" && unchecked.some((entry) => entry.reasonCode === "codegraph-unavailable")) {
|
|
197
|
+
issues.push("codegraph-unavailable is reported only when codeGraph.availability is unavailable");
|
|
198
|
+
}
|
|
199
|
+
return issues;
|
|
200
|
+
}
|
|
201
|
+
function docsAnchorFindingIssues(finding, prefix) {
|
|
202
|
+
const issues = [];
|
|
203
|
+
const { citation } = finding;
|
|
204
|
+
if (finding.kind === "missing-path" && citation.path === undefined)
|
|
205
|
+
issues.push(`${prefix} missing-path must name citation.path`);
|
|
206
|
+
if (finding.kind === "missing-symbol" && citation.symbol === undefined)
|
|
207
|
+
issues.push(`${prefix} missing-symbol must name citation.symbol`);
|
|
208
|
+
if (finding.kind === "line-out-of-range" && (citation.line === undefined || finding.lineCount === undefined)) {
|
|
209
|
+
issues.push(`${prefix} line-out-of-range must name citation.line and lineCount`);
|
|
210
|
+
}
|
|
211
|
+
if (finding.lineCount !== undefined !== (finding.kind === "line-out-of-range"))
|
|
212
|
+
issues.push(`${prefix}.lineCount is present only on line-out-of-range`);
|
|
213
|
+
if (finding.kind === "line-moved") {
|
|
214
|
+
const lines = finding.currentLines ?? [];
|
|
215
|
+
if (citation.line === undefined || citation.symbol === undefined)
|
|
216
|
+
issues.push(`${prefix} line-moved must name citation.line and citation.symbol`);
|
|
217
|
+
if (lines.length === 0 || lines.some((line, index) => index > 0 && line <= lines[index - 1]))
|
|
218
|
+
issues.push(`${prefix}.currentLines must be non-empty, sorted and unique`);
|
|
219
|
+
} else if (finding.currentLines !== undefined) {
|
|
220
|
+
issues.push(`${prefix}.currentLines is present only on line-moved`);
|
|
221
|
+
}
|
|
222
|
+
return issues;
|
|
223
|
+
}
|
|
224
|
+
var DOCS_ANCHORS_REPORT_SCHEMA_VERSION = "archcontext.docs-anchors/v1", DOCS_ANCHOR_FINDING_KINDS;
|
|
225
|
+
var init_docs_anchors = __esm(() => {
|
|
226
|
+
DOCS_ANCHOR_FINDING_KINDS = ["line-moved", "line-out-of-range", "missing-path", "missing-symbol"];
|
|
227
|
+
});
|
|
228
|
+
|
|
170
229
|
// packages/contracts/src/external-docs.ts
|
|
171
230
|
var EXTERNAL_DOCUMENTATION_RESOURCE_SCHEMA_VERSION = "archcontext.external-document/v1", CONTEXT7_LOCKFILE_SCHEMA_VERSION = "archcontext.context7-lock/v1";
|
|
172
231
|
|
|
@@ -674,6 +733,9 @@ var init_ledger = __esm(() => {
|
|
|
674
733
|
];
|
|
675
734
|
});
|
|
676
735
|
|
|
736
|
+
// packages/contracts/src/ownership-dependencies.ts
|
|
737
|
+
var CHANGESET_OWNERSHIP_DELTA_SCHEMA_VERSION = "archcontext.changeset-ownership-delta/v1", CHANGESET_OWNERSHIP_DELTA_PATH_LIMIT = 50, CAPABILITY_DEPENDENCIES_SCHEMA_VERSION = "archcontext.capability-dependencies/v1", CAPABILITY_DEPENDENCIES_EDGE_LIMIT = 200, CAPABILITY_DEPENDENCIES_CALLS_UNAVAILABLE_REASON = "call-edges-not-exposed";
|
|
738
|
+
|
|
677
739
|
// packages/contracts/src/ports.ts
|
|
678
740
|
function baseModelBlockingErrors(result) {
|
|
679
741
|
if (result.valid)
|
|
@@ -1407,7 +1469,7 @@ function sortedUniqueIssues(label, values) {
|
|
|
1407
1469
|
const expected = [...new Set(values)].sort();
|
|
1408
1470
|
return expected.length === values.length && expected.every((value, index) => value === values[index]) ? [] : [`${label} must be sorted and unique`];
|
|
1409
1471
|
}
|
|
1410
|
-
var PROJECTION_REQUEST_SCHEMA_VERSION = "archcontext.projection-request/v1", PROJECTION_RESULT_SCHEMA_VERSION = "archcontext.projection-result/v2", PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION = "archcontext.projection-apply-identity/v1", PROJECTION_APPLY_RECEIPT_SCHEMA_VERSION = "archcontext.projection-apply-receipt/v1", PROJECTION_APPLY_RECOVERY_BINDING_SCHEMA_VERSION = "archcontext.projection-apply-recovery-binding/v1", PROJECTION_APPLY_RECOVERY_INTENT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-intent/v1", PROJECTION_APPLY_RECOVERY_PROOF_SCHEMA_VERSION = "archcontext.projection-apply-recovery-proof/v1", PROJECTION_APPLY_RECOVERY_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-result/v1", PROJECTION_APPLY_READBACK_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-readback-result/v1", ARCHITECTURE_REFRESH_SIGNAL_SCHEMA_VERSION = "archcontext.architecture-refresh-signal/v1", ARCHCTX_CAPABILITIES_SCHEMA_VERSION = "archcontext.capabilities/v1", ARCHITECTURE_DOCS_RENDERER_VERSION = "archcontext.docs-renderer/v5", AGENT_CONTEXT_RENDERER_VERSION = "archcontext.agent-context-renderer/v1", PROJECTION_MODES, PROJECTION_PRIOR_COMMITTED_APPLY_OPERATIONS, PROJECTION_TARGETS, PROJECTION_HUMAN_ACTION_REASON_CODES, ARCHITECTURE_MAJOR_CHANGE_REASON_CODES, ARCHCTX_FEATURES, ARCHITECTURE_SEMANTIC_FACETS, ARCHITECTURE_PROOF_P1_STATUSES, ARCHITECTURE_PROOF_P2_STATUSES, PROJECTION_FILE_PREVIEW_MAX_BYTES = 65536, PROJECTION_PREVIEW_TOTAL_MAX_BYTES = 1048576, PROJECTION_FRESHNESS_REASON_CODES, UTF8, SHA256_DIGEST, ISO_INSTANT;
|
|
1472
|
+
var PROJECTION_REQUEST_SCHEMA_VERSION = "archcontext.projection-request/v1", PROJECTION_RESULT_SCHEMA_VERSION = "archcontext.projection-result/v2", PROJECTION_APPLY_IDENTITY_SCHEMA_VERSION = "archcontext.projection-apply-identity/v1", PROJECTION_APPLY_RECEIPT_SCHEMA_VERSION = "archcontext.projection-apply-receipt/v1", PROJECTION_APPLY_RECOVERY_BINDING_SCHEMA_VERSION = "archcontext.projection-apply-recovery-binding/v1", PROJECTION_APPLY_RECOVERY_INTENT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-intent/v1", PROJECTION_APPLY_RECOVERY_PROOF_SCHEMA_VERSION = "archcontext.projection-apply-recovery-proof/v1", PROJECTION_APPLY_RECOVERY_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-recovery-result/v1", PROJECTION_APPLY_READBACK_RESULT_SCHEMA_VERSION = "archcontext.projection-apply-readback-result/v1", ARCHITECTURE_REFRESH_SIGNAL_SCHEMA_VERSION = "archcontext.architecture-refresh-signal/v1", ARCHCTX_CAPABILITIES_SCHEMA_VERSION = "archcontext.capabilities/v1", ARCHITECTURE_DOCS_RENDERER_VERSION = "archcontext.docs-renderer/v5", AGENT_CONTEXT_RENDERER_VERSION = "archcontext.agent-context-renderer/v1", PROJECTION_MODES, PROJECTION_PRIOR_COMMITTED_APPLY_OPERATIONS, PROJECTION_TARGETS, PROJECTION_HUMAN_ACTION_REASON_CODES, ARCHITECTURE_MAJOR_CHANGE_REASON_CODES, ARCHCTX_FEATURES, ARCHITECTURE_SEMANTIC_FACETS, ARCHITECTURE_PROOF_P1_STATUSES, ARCHITECTURE_PROOF_P2_STATUSES, ARCHITECTURE_FLOW_SELECTOR_CHECK_SCHEMA_VERSION = "archcontext.flow-selector-check/v1", PROJECTION_FILE_PREVIEW_MAX_BYTES = 65536, PROJECTION_PREVIEW_TOTAL_MAX_BYTES = 1048576, PROJECTION_FRESHNESS_REASON_CODES, UTF8, SHA256_DIGEST, ISO_INSTANT;
|
|
1411
1473
|
var init_projection = __esm(() => {
|
|
1412
1474
|
init_schema();
|
|
1413
1475
|
PROJECTION_MODES = ["check", "plan", "apply", "adopt"];
|
|
@@ -1440,8 +1502,12 @@ var init_projection = __esm(() => {
|
|
|
1440
1502
|
"architecture-change-capabilities-v1",
|
|
1441
1503
|
"architecture-docs-renderer-v2",
|
|
1442
1504
|
"architecture-refresh-signal-v1",
|
|
1505
|
+
"capability-dependencies-v1",
|
|
1443
1506
|
"changeset-entity-operations-v1",
|
|
1507
|
+
"changeset-ownership-delta-v1",
|
|
1444
1508
|
"code-facts-unavailable-error-v1",
|
|
1509
|
+
"docs-anchors-v1",
|
|
1510
|
+
"flow-selector-check-v1",
|
|
1445
1511
|
"module-statistics-v1",
|
|
1446
1512
|
"projection-apply-readback-v1",
|
|
1447
1513
|
"projection-apply-receipt-v1",
|
|
@@ -1459,7 +1525,8 @@ var init_projection = __esm(() => {
|
|
|
1459
1525
|
"refactor-assessment-v1",
|
|
1460
1526
|
"refactor-observation-evidence-v1",
|
|
1461
1527
|
"refactor-resolution-v1",
|
|
1462
|
-
"refactor-scan-limits-v1"
|
|
1528
|
+
"refactor-scan-limits-v1",
|
|
1529
|
+
"refactor-scan-untracked-count-v1"
|
|
1463
1530
|
];
|
|
1464
1531
|
ARCHITECTURE_SEMANTIC_FACETS = [
|
|
1465
1532
|
"constraints",
|
|
@@ -1802,7 +1869,7 @@ function productVersionManifest() {
|
|
|
1802
1869
|
}
|
|
1803
1870
|
};
|
|
1804
1871
|
}
|
|
1805
|
-
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.7.
|
|
1872
|
+
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.7.2", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
|
|
1806
1873
|
|
|
1807
1874
|
// packages/contracts/src/refactor.ts
|
|
1808
1875
|
function refactorProposalDigest(proposal) {
|
|
@@ -1919,6 +1986,9 @@ function refactorRequestInvariantIssues(request, prefix = "request") {
|
|
|
1919
1986
|
}
|
|
1920
1987
|
if (request.proposal)
|
|
1921
1988
|
issues.push(...refactorProposalInvariantIssues(request.proposal, `${prefix}.proposal`));
|
|
1989
|
+
if (request.importNodeLimit !== undefined && !(Number.isInteger(request.importNodeLimit) && request.importNodeLimit >= 1)) {
|
|
1990
|
+
issues.push(`${prefix}.importNodeLimit must be a positive integer`);
|
|
1991
|
+
}
|
|
1922
1992
|
return issues;
|
|
1923
1993
|
}
|
|
1924
1994
|
function refactorVerificationRequestInvariantIssues(request, prefix = "request") {
|
|
@@ -1937,6 +2007,9 @@ function refactorVerificationRequestInvariantIssues(request, prefix = "request")
|
|
|
1937
2007
|
if (request.expectedWorktreeDigest !== undefined) {
|
|
1938
2008
|
issues.push(...digestIssues(`${prefix}.expectedWorktreeDigest`, request.expectedWorktreeDigest));
|
|
1939
2009
|
}
|
|
2010
|
+
if (request.importNodeLimit !== undefined && !(Number.isInteger(request.importNodeLimit) && request.importNodeLimit >= 1)) {
|
|
2011
|
+
issues.push(`${prefix}.importNodeLimit must be a positive integer`);
|
|
2012
|
+
}
|
|
1940
2013
|
if (request.executionEvidenceRefs !== undefined) {
|
|
1941
2014
|
if (!Array.isArray(request.executionEvidenceRefs)) {
|
|
1942
2015
|
issues.push(`${prefix}.executionEvidenceRefs must be an array`);
|
|
@@ -2013,6 +2086,16 @@ function moduleStatisticsSnapshotInvariantIssues(snapshot, prefix = "snapshot")
|
|
|
2013
2086
|
}
|
|
2014
2087
|
}
|
|
2015
2088
|
issues.push(...nonNegativeIntegerIssues(`${prefix}.codeFacts.edgeLimit`, snapshot.codeFacts.edgeLimit));
|
|
2089
|
+
const importNodeCount = snapshot.codeFacts.importNodeCount ?? null;
|
|
2090
|
+
issues.push(...nonNegativeIntegerIssues(`${prefix}.codeFacts.importNodeCount`, importNodeCount));
|
|
2091
|
+
if (snapshot.codeFacts.reasonCodes.includes("code-facts-import-limit")) {
|
|
2092
|
+
const { edgeLimit } = snapshot.codeFacts;
|
|
2093
|
+
if (snapshot.codeFacts.coverage !== "partial")
|
|
2094
|
+
issues.push(`${prefix}.codeFacts.coverage must be partial when the import limit was reached`);
|
|
2095
|
+
if (edgeLimit === null || importNodeCount !== null && importNodeCount <= edgeLimit) {
|
|
2096
|
+
issues.push(`${prefix}.codeFacts.importNodeCount must exceed edgeLimit when the import limit was reached`);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2016
2099
|
for (const [field, value] of Object.entries(snapshot.repositorySummary)) {
|
|
2017
2100
|
issues.push(...nonNegativeIntegerIssues(`${prefix}.repositorySummary.${field}`, value));
|
|
2018
2101
|
}
|
|
@@ -2467,6 +2550,7 @@ var init_refactor = __esm(() => {
|
|
|
2467
2550
|
"executionEvidenceRefs",
|
|
2468
2551
|
"expectedHeadSha",
|
|
2469
2552
|
"expectedWorktreeDigest",
|
|
2553
|
+
"importNodeLimit",
|
|
2470
2554
|
"recommendationId",
|
|
2471
2555
|
"schemaVersion"
|
|
2472
2556
|
];
|
|
@@ -2479,6 +2563,7 @@ var init_refactor = __esm(() => {
|
|
|
2479
2563
|
];
|
|
2480
2564
|
REFACTOR_SCALE_REASON_CODES = [
|
|
2481
2565
|
"caller-coverage-unknown",
|
|
2566
|
+
"code-facts-import-limit",
|
|
2482
2567
|
"code-facts-missing",
|
|
2483
2568
|
"code-facts-truncated",
|
|
2484
2569
|
"major-change-detected",
|
|
@@ -2712,6 +2797,7 @@ var init_validator = __esm(() => {
|
|
|
2712
2797
|
var init_src = __esm(() => {
|
|
2713
2798
|
init_control_plane_routes();
|
|
2714
2799
|
init_architecture();
|
|
2800
|
+
init_docs_anchors();
|
|
2715
2801
|
init_github_governance();
|
|
2716
2802
|
init_ledger();
|
|
2717
2803
|
init_ports();
|
|
@@ -13366,14 +13452,18 @@ function sanitizeGithubDeveloperReviewState(state, statePath) {
|
|
|
13366
13452
|
assertNoReviewSecrets(data, []);
|
|
13367
13453
|
return data;
|
|
13368
13454
|
}
|
|
13455
|
+
function isStandardBase64Value(token) {
|
|
13456
|
+
return token.length % 4 === 0 && /^[A-Za-z0-9+/]+={0,2}$/.test(token) && /[A-Za-z0-9+]{20}/.test(token);
|
|
13457
|
+
}
|
|
13369
13458
|
function assertNoCliSecretMaterial(value) {
|
|
13370
13459
|
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
13371
13460
|
if (!serialized)
|
|
13372
13461
|
return;
|
|
13373
13462
|
if (/-----BEGIN [A-Z ]*PRIVATE KEY-----/.test(serialized))
|
|
13374
13463
|
throw new Error("device-private-key-material-forbidden");
|
|
13375
|
-
|
|
13376
|
-
|
|
13464
|
+
for (const [, token] of serialized.matchAll(/(?:^|["'\s])((?:file:\/\/|\.{0,2}\/|~\/)[^"'\s]*)/gi)) {
|
|
13465
|
+
if (/private|device|key/i.test(token) && !isStandardBase64Value(token))
|
|
13466
|
+
throw new Error("device-private-key-file-ref-forbidden");
|
|
13377
13467
|
}
|
|
13378
13468
|
if (/(access|refresh|token)_[A-Za-z0-9_-]+/.test(serialized))
|
|
13379
13469
|
throw new Error("github-token-material-forbidden");
|
|
@@ -15017,7 +15107,7 @@ function parseScalar(value) {
|
|
|
15017
15107
|
|
|
15018
15108
|
// packages/local-runtime/git-adapter/src/index.ts
|
|
15019
15109
|
import { execFileSync as execFileSync2, spawnSync } from "node:child_process";
|
|
15020
|
-
import { existsSync as existsSync6, lstatSync as lstatSync3, mkdirSync as mkdirSync4, mkdtempSync, readFileSync as readFileSync7, rmSync as rmSync4 } from "node:fs";
|
|
15110
|
+
import { existsSync as existsSync6, lstatSync as lstatSync3, mkdirSync as mkdirSync4, mkdtempSync, readdirSync as readdirSync4, readFileSync as readFileSync7, rmSync as rmSync4 } from "node:fs";
|
|
15021
15111
|
import { dirname as dirname4, join as join4, resolve as resolve8 } from "node:path";
|
|
15022
15112
|
import { tmpdir } from "node:os";
|
|
15023
15113
|
|
|
@@ -16363,29 +16453,86 @@ function validateStep(input) {
|
|
|
16363
16453
|
input.diagnostics.push({ code: "selector-declaration-ambiguous", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
|
|
16364
16454
|
return;
|
|
16365
16455
|
}
|
|
16366
|
-
const
|
|
16367
|
-
if (
|
|
16368
|
-
input.diagnostics.push({ code:
|
|
16369
|
-
return;
|
|
16370
|
-
}
|
|
16371
|
-
if (evidence.length > 1) {
|
|
16372
|
-
input.diagnostics.push({ code: "selector-evidence-ambiguous", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
|
|
16373
|
-
return;
|
|
16374
|
-
}
|
|
16375
|
-
if (evidence[0].ambiguous) {
|
|
16376
|
-
input.diagnostics.push({ code: "selector-evidence-ambiguous", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
|
|
16377
|
-
return;
|
|
16378
|
-
}
|
|
16379
|
-
if (evidence[0].truncated) {
|
|
16380
|
-
input.diagnostics.push({ code: "selector-evidence-truncated", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
|
|
16381
|
-
return;
|
|
16382
|
-
}
|
|
16383
|
-
if (!evidence[0].matched || evidence[0].callSites.length === 0) {
|
|
16384
|
-
input.diagnostics.push({ code: "selector-evidence-unmatched", flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
|
|
16456
|
+
const status = architectureSelectorProofStatus(input.evidence.filter((entry) => evidenceSelectorKey(entry) === selectorKey && entry.nodeId === declarations[0].nodeId));
|
|
16457
|
+
if (status !== "proven") {
|
|
16458
|
+
input.diagnostics.push({ code: SELECTOR_STATUS_DIAGNOSTICS[status], flowId: input.flowId, stepId: input.step.id, detail: selectorKey });
|
|
16385
16459
|
return;
|
|
16386
16460
|
}
|
|
16387
16461
|
input.provenSelectorKeys.add(selectorKey);
|
|
16388
16462
|
}
|
|
16463
|
+
var SELECTOR_STATUS_DIAGNOSTICS = {
|
|
16464
|
+
"ambiguous-symbol": "selector-evidence-ambiguous",
|
|
16465
|
+
"evidence-missing": "selector-evidence-missing",
|
|
16466
|
+
"evidence-truncated": "selector-evidence-truncated",
|
|
16467
|
+
"false-builtin-call": "selector-evidence-unmatched",
|
|
16468
|
+
"no-direct-call": "selector-evidence-unmatched",
|
|
16469
|
+
"only-references": "selector-evidence-unmatched"
|
|
16470
|
+
};
|
|
16471
|
+
function architectureSelectorProofStatus(evidence) {
|
|
16472
|
+
if (evidence.length === 0)
|
|
16473
|
+
return "evidence-missing";
|
|
16474
|
+
if (evidence.length > 1 || evidence[0].ambiguous)
|
|
16475
|
+
return "ambiguous-symbol";
|
|
16476
|
+
if (evidence[0].truncated)
|
|
16477
|
+
return "evidence-truncated";
|
|
16478
|
+
if (evidence[0].matched && evidence[0].callSites.length > 0)
|
|
16479
|
+
return "proven";
|
|
16480
|
+
if ((evidence[0].rejectedBuiltinCallSites ?? []).length > 0)
|
|
16481
|
+
return "false-builtin-call";
|
|
16482
|
+
if ((evidence[0].referenceSites ?? []).length > 0)
|
|
16483
|
+
return "only-references";
|
|
16484
|
+
return "no-direct-call";
|
|
16485
|
+
}
|
|
16486
|
+
function checkSemanticFlowSelectors(input) {
|
|
16487
|
+
const stepsByKey = new Map;
|
|
16488
|
+
for (const flow of [...input.flows].sort((left, right) => left.id.localeCompare(right.id))) {
|
|
16489
|
+
if (flow.applicability !== "required")
|
|
16490
|
+
continue;
|
|
16491
|
+
for (const step of [...flow.steps, ...flow.outcomes.flatMap((outcome) => outcome.steps)]) {
|
|
16492
|
+
const key = evidenceSelectorKey(step.evidence);
|
|
16493
|
+
stepsByKey.set(key, [...stepsByKey.get(key) ?? [], { flowId: flow.id, stepId: step.id }]);
|
|
16494
|
+
}
|
|
16495
|
+
}
|
|
16496
|
+
const selectors = input.nodes.flatMap((node) => (node.source?.entrypoints ?? []).flatMap((entrypoint) => entrypoint.symbols.flatMap((source) => source.sinks.map((sink) => {
|
|
16497
|
+
const key = evidenceSelectorKey({ entrypointId: entrypoint.id, sourceSymbol: source.name, sinkId: sink.id });
|
|
16498
|
+
const evidence = input.evidence.filter((entry) => evidenceSelectorKey(entry) === key && entry.nodeId === node.id);
|
|
16499
|
+
const single = evidence.length === 1 ? evidence[0] : undefined;
|
|
16500
|
+
return {
|
|
16501
|
+
nodeId: node.id,
|
|
16502
|
+
entrypointId: entrypoint.id,
|
|
16503
|
+
sourcePath: entrypoint.path,
|
|
16504
|
+
sourceSymbol: source.name,
|
|
16505
|
+
sinkId: sink.id,
|
|
16506
|
+
sinkPath: sink.path,
|
|
16507
|
+
sinkSymbol: sink.symbol,
|
|
16508
|
+
status: architectureSelectorProofStatus(evidence),
|
|
16509
|
+
callSites: single?.callSites ?? [],
|
|
16510
|
+
referenceSites: single?.referenceSites ?? [],
|
|
16511
|
+
rejectedBuiltinCallSites: single?.rejectedBuiltinCallSites ?? [],
|
|
16512
|
+
flowSteps: stepsByKey.get(key) ?? []
|
|
16513
|
+
};
|
|
16514
|
+
})))).sort((left, right) => compareStrings(left.nodeId, right.nodeId) || compareStrings(left.entrypointId, right.entrypointId) || compareStrings(left.sourceSymbol, right.sourceSymbol) || compareStrings(left.sinkId, right.sinkId));
|
|
16515
|
+
const capabilityIds = [...new Set(input.flows.map((flow) => flow.capabilityId))].sort(compareStrings);
|
|
16516
|
+
const capabilities = capabilityIds.map((capabilityId) => {
|
|
16517
|
+
const { p2 } = compileSemanticCapabilityDiagrams({ capabilityId, nodes: input.nodes, relations: input.relations, flows: input.flows, evidence: input.evidence });
|
|
16518
|
+
return { capabilityId, p2Status: p2.status, diagnostics: p2.diagnostics };
|
|
16519
|
+
});
|
|
16520
|
+
const unproven = selectors.filter((selector) => selector.status !== "proven");
|
|
16521
|
+
return {
|
|
16522
|
+
ok: capabilities.every((capability) => capability.p2Status !== "unprovable"),
|
|
16523
|
+
summary: {
|
|
16524
|
+
selectors: selectors.length,
|
|
16525
|
+
proven: selectors.length - unproven.length,
|
|
16526
|
+
unproven: unproven.length,
|
|
16527
|
+
usedUnproven: unproven.filter((selector) => selector.flowSteps.length > 0).length
|
|
16528
|
+
},
|
|
16529
|
+
selectors,
|
|
16530
|
+
capabilities
|
|
16531
|
+
};
|
|
16532
|
+
}
|
|
16533
|
+
function compareStrings(left, right) {
|
|
16534
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
16535
|
+
}
|
|
16389
16536
|
function proofRelevantSelectorEvidence(evidence) {
|
|
16390
16537
|
return evidence.map((entry) => ({
|
|
16391
16538
|
nodeId: entry.nodeId,
|
|
@@ -16486,7 +16633,7 @@ function mermaidStableId(prefix, value) {
|
|
|
16486
16633
|
return `${prefix}_${slug}_${digestJson(value).slice(7, 15)}`;
|
|
16487
16634
|
}
|
|
16488
16635
|
function escapeMermaidLabel(value) {
|
|
16489
|
-
return value.replace(/["#;,]/g, (character) => ({ '"': "
|
|
16636
|
+
return value.replace(/["#;,]/g, (character) => ({ '"': "#quot;", "#": "#35;", ";": "#59;", ",": "#44;" })[character]);
|
|
16490
16637
|
}
|
|
16491
16638
|
|
|
16492
16639
|
// packages/core/projection-engine/src/major-change.ts
|
|
@@ -17142,6 +17289,374 @@ function diffHunks(ops) {
|
|
|
17142
17289
|
}
|
|
17143
17290
|
return hunks;
|
|
17144
17291
|
}
|
|
17292
|
+
// packages/core/projection-engine/src/prose-anchors.ts
|
|
17293
|
+
init_src();
|
|
17294
|
+
var IDENTIFIER = "[A-Za-z_][A-Za-z0-9_$]*";
|
|
17295
|
+
var QUALIFIED_IDENTIFIER = `${IDENTIFIER}(?:\\.${IDENTIFIER})*`;
|
|
17296
|
+
var IDENTIFIER_PATTERN = new RegExp(`^${IDENTIFIER}$`);
|
|
17297
|
+
var QUALIFIED_PATTERN = new RegExp(`^${QUALIFIED_IDENTIFIER}$`);
|
|
17298
|
+
var CALL_PATTERN = new RegExp(`^(${QUALIFIED_IDENTIFIER})\\(\\)$`);
|
|
17299
|
+
var PATH_PATTERN = /^[A-Za-z0-9_.@+\-/]+$/;
|
|
17300
|
+
var LINE_FRAGMENT = /^L(\d+)(?:-L?(\d+))?$/;
|
|
17301
|
+
var LINE_SUFFIX = /^(.*?):(\d+)(?:-(\d+))?$/;
|
|
17302
|
+
var FILE_EXTENSION = /\.[A-Za-z0-9]+$/;
|
|
17303
|
+
var BINDING_GAP = /^\s*[([,:–—-]?\s*$/;
|
|
17304
|
+
var GENERATED_BEGIN = /<!-- BEGIN ARCHCONTEXT:generated target="([^"]+)"[^>]*-->/;
|
|
17305
|
+
function checkProseAnchors(input) {
|
|
17306
|
+
const nodes = [];
|
|
17307
|
+
let documents = 0;
|
|
17308
|
+
for (const node of [...input.nodes].sort((left, right) => left.nodeId < right.nodeId ? -1 : left.nodeId > right.nodeId ? 1 : 0)) {
|
|
17309
|
+
if (node.document)
|
|
17310
|
+
documents += 1;
|
|
17311
|
+
const result = checkNode(node, input);
|
|
17312
|
+
if (result.citationCount > 0)
|
|
17313
|
+
nodes.push(result);
|
|
17314
|
+
}
|
|
17315
|
+
const findings = nodes.flatMap((node) => node.findings);
|
|
17316
|
+
const byKind = Object.fromEntries(DOCS_ANCHOR_FINDING_KINDS.map((kind) => [kind, findings.filter((finding) => finding.kind === kind).length]));
|
|
17317
|
+
return {
|
|
17318
|
+
schemaVersion: DOCS_ANCHORS_REPORT_SCHEMA_VERSION,
|
|
17319
|
+
profile: input.profile,
|
|
17320
|
+
codeGraph: input.codeGraph.availability === "ready" ? { availability: "ready" } : { availability: "unavailable", reasonCode: input.codeGraph.reasonCode },
|
|
17321
|
+
summary: {
|
|
17322
|
+
nodes: input.nodes.length,
|
|
17323
|
+
documents,
|
|
17324
|
+
citations: nodes.reduce((total, node) => total + node.citationCount, 0),
|
|
17325
|
+
findings: findings.length,
|
|
17326
|
+
unchecked: nodes.reduce((total, node) => total + node.unchecked.length, 0),
|
|
17327
|
+
byKind
|
|
17328
|
+
},
|
|
17329
|
+
nodes
|
|
17330
|
+
};
|
|
17331
|
+
}
|
|
17332
|
+
function extractNodeProseCitations(node) {
|
|
17333
|
+
const located = [];
|
|
17334
|
+
if (typeof node.summary === "string") {
|
|
17335
|
+
for (const line of node.summary.split(/\r?\n/)) {
|
|
17336
|
+
located.push(...parseLineCitations(line).map((parsed) => ({ origin: { kind: "node-summary" }, parsed })));
|
|
17337
|
+
}
|
|
17338
|
+
}
|
|
17339
|
+
for (const [index, responsibility] of (node.responsibilities ?? []).entries()) {
|
|
17340
|
+
if (typeof responsibility !== "string")
|
|
17341
|
+
continue;
|
|
17342
|
+
for (const line of responsibility.split(/\r?\n/)) {
|
|
17343
|
+
located.push(...parseLineCitations(line).map((parsed) => ({ origin: { kind: "node-responsibility", index }, parsed })));
|
|
17344
|
+
}
|
|
17345
|
+
}
|
|
17346
|
+
if (node.document) {
|
|
17347
|
+
for (const { line, text } of humanOwnedMarkdownLines(node.document.body)) {
|
|
17348
|
+
located.push(...parseLineCitations(text).map((parsed) => ({ origin: { kind: "document", path: node.document.path, line }, parsed })));
|
|
17349
|
+
}
|
|
17350
|
+
}
|
|
17351
|
+
return located;
|
|
17352
|
+
}
|
|
17353
|
+
function humanOwnedMarkdownLines(body) {
|
|
17354
|
+
const lines = [];
|
|
17355
|
+
let generatedEnd;
|
|
17356
|
+
let fence;
|
|
17357
|
+
let inComment = false;
|
|
17358
|
+
for (const [index, raw] of body.split(/\r?\n/).entries()) {
|
|
17359
|
+
if (generatedEnd !== undefined) {
|
|
17360
|
+
if (raw.includes(generatedEnd))
|
|
17361
|
+
generatedEnd = undefined;
|
|
17362
|
+
continue;
|
|
17363
|
+
}
|
|
17364
|
+
const begin = GENERATED_BEGIN.exec(raw);
|
|
17365
|
+
if (begin) {
|
|
17366
|
+
const end = `<!-- END ARCHCONTEXT:generated target="${begin[1]}" -->`;
|
|
17367
|
+
if (!raw.slice(begin.index + begin[0].length).includes(end))
|
|
17368
|
+
generatedEnd = end;
|
|
17369
|
+
continue;
|
|
17370
|
+
}
|
|
17371
|
+
if (fence) {
|
|
17372
|
+
const close = /^ {0,3}(`{3,}|~{3,})\s*$/.exec(raw);
|
|
17373
|
+
if (close && close[1][0] === fence.char && close[1].length >= fence.length)
|
|
17374
|
+
fence = undefined;
|
|
17375
|
+
continue;
|
|
17376
|
+
}
|
|
17377
|
+
const open = /^ {0,3}(`{3,}|~{3,})/.exec(raw);
|
|
17378
|
+
if (open && !inComment) {
|
|
17379
|
+
fence = { char: open[1][0], length: open[1].length };
|
|
17380
|
+
continue;
|
|
17381
|
+
}
|
|
17382
|
+
const stripped = stripHtmlComments(raw, inComment);
|
|
17383
|
+
inComment = stripped.inComment;
|
|
17384
|
+
if (stripped.text.trim() !== "")
|
|
17385
|
+
lines.push({ line: index + 1, text: stripped.text });
|
|
17386
|
+
}
|
|
17387
|
+
return lines;
|
|
17388
|
+
}
|
|
17389
|
+
function stripHtmlComments(raw, inComment) {
|
|
17390
|
+
let text = "";
|
|
17391
|
+
let rest = raw;
|
|
17392
|
+
let open = inComment;
|
|
17393
|
+
while (rest.length > 0) {
|
|
17394
|
+
if (open) {
|
|
17395
|
+
const close = rest.indexOf("-->");
|
|
17396
|
+
if (close < 0)
|
|
17397
|
+
return { text, inComment: true };
|
|
17398
|
+
rest = rest.slice(close + 3);
|
|
17399
|
+
open = false;
|
|
17400
|
+
continue;
|
|
17401
|
+
}
|
|
17402
|
+
const start = rest.indexOf("<!--");
|
|
17403
|
+
if (start < 0)
|
|
17404
|
+
return { text: text + rest, inComment: false };
|
|
17405
|
+
text += rest.slice(0, start);
|
|
17406
|
+
rest = rest.slice(start + 4);
|
|
17407
|
+
open = true;
|
|
17408
|
+
}
|
|
17409
|
+
return { text, inComment: open };
|
|
17410
|
+
}
|
|
17411
|
+
function inlineCodeSpans(text) {
|
|
17412
|
+
const spans = [];
|
|
17413
|
+
let index = 0;
|
|
17414
|
+
while (index < text.length) {
|
|
17415
|
+
if (text[index] !== "`") {
|
|
17416
|
+
index += 1;
|
|
17417
|
+
continue;
|
|
17418
|
+
}
|
|
17419
|
+
if (precedingBackslashes(text, index) % 2 === 1) {
|
|
17420
|
+
index += 1;
|
|
17421
|
+
continue;
|
|
17422
|
+
}
|
|
17423
|
+
let runEnd = index;
|
|
17424
|
+
while (text[runEnd] === "`")
|
|
17425
|
+
runEnd += 1;
|
|
17426
|
+
const ticks = runEnd - index;
|
|
17427
|
+
let cursor = runEnd;
|
|
17428
|
+
let close = -1;
|
|
17429
|
+
while (cursor < text.length) {
|
|
17430
|
+
if (text[cursor] !== "`") {
|
|
17431
|
+
cursor += 1;
|
|
17432
|
+
continue;
|
|
17433
|
+
}
|
|
17434
|
+
let closeEnd = cursor;
|
|
17435
|
+
while (text[closeEnd] === "`")
|
|
17436
|
+
closeEnd += 1;
|
|
17437
|
+
if (closeEnd - cursor === ticks) {
|
|
17438
|
+
close = cursor;
|
|
17439
|
+
break;
|
|
17440
|
+
}
|
|
17441
|
+
cursor = closeEnd;
|
|
17442
|
+
}
|
|
17443
|
+
if (close < 0) {
|
|
17444
|
+
index = runEnd;
|
|
17445
|
+
continue;
|
|
17446
|
+
}
|
|
17447
|
+
let content = text.slice(runEnd, close);
|
|
17448
|
+
if (content.length > 2 && content.startsWith(" ") && content.endsWith(" "))
|
|
17449
|
+
content = content.slice(1, -1);
|
|
17450
|
+
spans.push({ content, start: index, end: close + ticks });
|
|
17451
|
+
index = close + ticks;
|
|
17452
|
+
}
|
|
17453
|
+
return spans;
|
|
17454
|
+
}
|
|
17455
|
+
function precedingBackslashes(text, index) {
|
|
17456
|
+
let count = 0;
|
|
17457
|
+
while (index - count - 1 >= 0 && text[index - count - 1] === "\\")
|
|
17458
|
+
count += 1;
|
|
17459
|
+
return count;
|
|
17460
|
+
}
|
|
17461
|
+
function parseLineCitations(text) {
|
|
17462
|
+
const spans = inlineCodeSpans(text);
|
|
17463
|
+
const parsed = spans.map((span) => parseCitationText(span.content));
|
|
17464
|
+
const consumed = new Set;
|
|
17465
|
+
for (const [index, citation] of parsed.entries()) {
|
|
17466
|
+
if (citation?.form !== "path" || citation.line === undefined || citation.symbol !== undefined)
|
|
17467
|
+
continue;
|
|
17468
|
+
const bind = (other, gap) => {
|
|
17469
|
+
const candidate = parsed[other];
|
|
17470
|
+
if (consumed.has(other) || !candidate || candidate.form === "path" || !BINDING_GAP.test(gap))
|
|
17471
|
+
return false;
|
|
17472
|
+
citation.symbol = candidate.symbol;
|
|
17473
|
+
consumed.add(other);
|
|
17474
|
+
return true;
|
|
17475
|
+
};
|
|
17476
|
+
if (index + 1 < spans.length && bind(index + 1, text.slice(spans[index].end, spans[index + 1].start)))
|
|
17477
|
+
continue;
|
|
17478
|
+
if (index > 0)
|
|
17479
|
+
bind(index - 1, text.slice(spans[index - 1].end, spans[index].start));
|
|
17480
|
+
}
|
|
17481
|
+
return parsed.flatMap((citation, index) => citation && !consumed.has(index) ? [citation] : []);
|
|
17482
|
+
}
|
|
17483
|
+
function parseCitationText(raw) {
|
|
17484
|
+
const text = raw.trim();
|
|
17485
|
+
if (text === "" || /\s/.test(text))
|
|
17486
|
+
return;
|
|
17487
|
+
const call = CALL_PATTERN.exec(text);
|
|
17488
|
+
if (call)
|
|
17489
|
+
return { form: "call", text, symbol: call[1] };
|
|
17490
|
+
if (IDENTIFIER_PATTERN.test(text))
|
|
17491
|
+
return { form: "identifier", text, symbol: text };
|
|
17492
|
+
let rest = text;
|
|
17493
|
+
let line;
|
|
17494
|
+
let endLine;
|
|
17495
|
+
let symbol;
|
|
17496
|
+
let explicit = false;
|
|
17497
|
+
const hash = rest.indexOf("#");
|
|
17498
|
+
if (hash >= 0) {
|
|
17499
|
+
const fragment = rest.slice(hash + 1);
|
|
17500
|
+
rest = rest.slice(0, hash);
|
|
17501
|
+
const lineFragment = LINE_FRAGMENT.exec(fragment);
|
|
17502
|
+
if (lineFragment) {
|
|
17503
|
+
line = Number(lineFragment[1]);
|
|
17504
|
+
endLine = lineFragment[2] === undefined ? undefined : Number(lineFragment[2]);
|
|
17505
|
+
} else if (QUALIFIED_PATTERN.test(fragment)) {
|
|
17506
|
+
symbol = fragment;
|
|
17507
|
+
} else {
|
|
17508
|
+
return;
|
|
17509
|
+
}
|
|
17510
|
+
explicit = true;
|
|
17511
|
+
}
|
|
17512
|
+
const lineSuffix = LINE_SUFFIX.exec(rest);
|
|
17513
|
+
if (lineSuffix) {
|
|
17514
|
+
if (line !== undefined)
|
|
17515
|
+
return;
|
|
17516
|
+
rest = lineSuffix[1];
|
|
17517
|
+
line = Number(lineSuffix[2]);
|
|
17518
|
+
endLine = lineSuffix[3] === undefined ? undefined : Number(lineSuffix[3]);
|
|
17519
|
+
explicit = true;
|
|
17520
|
+
}
|
|
17521
|
+
if (line !== undefined && (line < 1 || endLine !== undefined && endLine < line))
|
|
17522
|
+
return;
|
|
17523
|
+
const written = rest.replace(/^\.\//, "");
|
|
17524
|
+
if (!PATH_PATTERN.test(written) || written.startsWith("/"))
|
|
17525
|
+
return;
|
|
17526
|
+
const directory = written.endsWith("/");
|
|
17527
|
+
const path = directory ? written.slice(0, -1) : written;
|
|
17528
|
+
const segments = path.split("/");
|
|
17529
|
+
if (path === "" || segments.some((segment) => segment === "" || /^\.+$/.test(segment)))
|
|
17530
|
+
return;
|
|
17531
|
+
if (!explicit && segments.length < 2)
|
|
17532
|
+
return;
|
|
17533
|
+
if (explicit && segments.length < 2 && !FILE_EXTENSION.test(path))
|
|
17534
|
+
return;
|
|
17535
|
+
return {
|
|
17536
|
+
form: "path",
|
|
17537
|
+
text,
|
|
17538
|
+
path,
|
|
17539
|
+
directory,
|
|
17540
|
+
explicit,
|
|
17541
|
+
...line === undefined ? {} : { line },
|
|
17542
|
+
...endLine === undefined ? {} : { endLine },
|
|
17543
|
+
...symbol === undefined ? {} : { symbol }
|
|
17544
|
+
};
|
|
17545
|
+
}
|
|
17546
|
+
function checkNode(node, input) {
|
|
17547
|
+
const findings = [];
|
|
17548
|
+
const unchecked = [];
|
|
17549
|
+
let citationCount = 0;
|
|
17550
|
+
for (const { origin, parsed } of extractNodeProseCitations(node)) {
|
|
17551
|
+
const outcome = parsed.form === "path" ? checkPathCitation(parsed, node, input) : checkSymbolCitation(parsed, input.codeGraph);
|
|
17552
|
+
if (!outcome.counted)
|
|
17553
|
+
continue;
|
|
17554
|
+
citationCount += 1;
|
|
17555
|
+
for (const finding of outcome.findings)
|
|
17556
|
+
findings.push({ ...finding, origin, citation: outcome.citation });
|
|
17557
|
+
for (const reasonCode of outcome.unchecked)
|
|
17558
|
+
unchecked.push({ reasonCode, origin, citation: outcome.citation });
|
|
17559
|
+
}
|
|
17560
|
+
return {
|
|
17561
|
+
nodeId: node.nodeId,
|
|
17562
|
+
...node.document ? { documentPath: node.document.path } : {},
|
|
17563
|
+
citationCount,
|
|
17564
|
+
findings,
|
|
17565
|
+
unchecked
|
|
17566
|
+
};
|
|
17567
|
+
}
|
|
17568
|
+
function checkSymbolCitation(parsed, codeGraph) {
|
|
17569
|
+
const citation = { text: parsed.text, symbol: parsed.symbol };
|
|
17570
|
+
if (codeGraph.availability !== "ready") {
|
|
17571
|
+
return parsed.form === "call" ? { counted: true, citation, findings: [], unchecked: ["codegraph-unavailable"] } : { counted: false, citation, findings: [], unchecked: [] };
|
|
17572
|
+
}
|
|
17573
|
+
const { name, container } = splitSymbol(parsed.symbol);
|
|
17574
|
+
const declared = matchingDeclarations(codeGraph.symbols.declarationsNamed(name), container).length > 0;
|
|
17575
|
+
if (parsed.form === "identifier")
|
|
17576
|
+
return { counted: declared, citation, findings: [], unchecked: [] };
|
|
17577
|
+
const missing = !codeGraph.symbols.appearsInSource(name);
|
|
17578
|
+
return { counted: true, citation, findings: missing ? [{ kind: "missing-symbol" }] : [], unchecked: [] };
|
|
17579
|
+
}
|
|
17580
|
+
function checkPathCitation(parsed, node, input) {
|
|
17581
|
+
const citation = {
|
|
17582
|
+
text: parsed.text,
|
|
17583
|
+
path: parsed.path,
|
|
17584
|
+
...parsed.line === undefined ? {} : { line: parsed.line },
|
|
17585
|
+
...parsed.endLine === undefined ? {} : { endLine: parsed.endLine },
|
|
17586
|
+
...parsed.symbol === undefined ? {} : { symbol: parsed.symbol }
|
|
17587
|
+
};
|
|
17588
|
+
const resolution = resolveCitedPath(parsed, node, input.tree);
|
|
17589
|
+
if (resolution.status === "ignored")
|
|
17590
|
+
return { counted: false, citation, findings: [], unchecked: [] };
|
|
17591
|
+
if (resolution.status === "ambiguous")
|
|
17592
|
+
return { counted: true, citation, findings: [], unchecked: ["path-ambiguous"] };
|
|
17593
|
+
const needsFile = parsed.line !== undefined || parsed.symbol !== undefined;
|
|
17594
|
+
if (resolution.status === "missing" || needsFile && resolution.kind !== "file") {
|
|
17595
|
+
return { counted: true, citation, findings: [{ kind: "missing-path" }], unchecked: [] };
|
|
17596
|
+
}
|
|
17597
|
+
const resolved = resolution;
|
|
17598
|
+
if (resolved.path !== parsed.path)
|
|
17599
|
+
citation.resolvedPath = resolved.path;
|
|
17600
|
+
if (!needsFile)
|
|
17601
|
+
return { counted: true, citation, findings: [], unchecked: [] };
|
|
17602
|
+
const lineFinding = () => {
|
|
17603
|
+
if (parsed.line === undefined)
|
|
17604
|
+
return [];
|
|
17605
|
+
const lineCount = input.tree.lineCount(resolved.path);
|
|
17606
|
+
return (parsed.endLine ?? parsed.line) > lineCount ? [{ kind: "line-out-of-range", lineCount }] : [];
|
|
17607
|
+
};
|
|
17608
|
+
if (parsed.symbol === undefined)
|
|
17609
|
+
return { counted: true, citation, findings: lineFinding(), unchecked: [] };
|
|
17610
|
+
if (input.codeGraph.availability !== "ready") {
|
|
17611
|
+
return { counted: true, citation, findings: lineFinding(), unchecked: ["codegraph-unavailable"] };
|
|
17612
|
+
}
|
|
17613
|
+
const symbols = input.codeGraph.symbols;
|
|
17614
|
+
if (!symbols.supportsPath(resolved.path)) {
|
|
17615
|
+
return { counted: true, citation, findings: lineFinding(), unchecked: ["language-unsupported"] };
|
|
17616
|
+
}
|
|
17617
|
+
const { name, container } = splitSymbol(parsed.symbol);
|
|
17618
|
+
const declarations = matchingDeclarations(symbols.declarationsInFile(resolved.path, name), container);
|
|
17619
|
+
if (declarations.length === 0)
|
|
17620
|
+
return { counted: true, citation, findings: [{ kind: "missing-symbol" }], unchecked: [] };
|
|
17621
|
+
const line = parsed.line;
|
|
17622
|
+
if (line === undefined || declarations.some((declaration) => declaration.startLine <= line && line <= declaration.endLine)) {
|
|
17623
|
+
return { counted: true, citation, findings: [], unchecked: [] };
|
|
17624
|
+
}
|
|
17625
|
+
const currentLines = [...new Set(declarations.map((declaration) => declaration.startLine))].sort((left, right) => left - right);
|
|
17626
|
+
return { counted: true, citation, findings: [{ kind: "line-moved", currentLines }], unchecked: [] };
|
|
17627
|
+
}
|
|
17628
|
+
function resolveCitedPath(parsed, node, tree) {
|
|
17629
|
+
const direct = tree.kind(parsed.path);
|
|
17630
|
+
if (direct && !(parsed.directory && direct === "file"))
|
|
17631
|
+
return { status: "resolved", path: parsed.path, kind: direct };
|
|
17632
|
+
const missing = () => tree.isIgnored(parsed.path) ? { status: "ignored" } : { status: "missing" };
|
|
17633
|
+
const anchored = parsed.path.includes("/") && tree.hasTopLevelEntry(parsed.path.split("/")[0]);
|
|
17634
|
+
if (anchored)
|
|
17635
|
+
return missing();
|
|
17636
|
+
if (!parsed.explicit)
|
|
17637
|
+
return { status: "ignored" };
|
|
17638
|
+
let matches = tree.filesEndingWith(parsed.path);
|
|
17639
|
+
if (matches.length > 1 && node.footprint) {
|
|
17640
|
+
const footprint = new Set(node.footprint);
|
|
17641
|
+
const inFootprint = matches.filter((path) => footprint.has(path));
|
|
17642
|
+
if (inFootprint.length === 1)
|
|
17643
|
+
matches = inFootprint;
|
|
17644
|
+
}
|
|
17645
|
+
if (matches.length === 0)
|
|
17646
|
+
return missing();
|
|
17647
|
+
if (matches.length > 1)
|
|
17648
|
+
return { status: "ambiguous" };
|
|
17649
|
+
return { status: "resolved", path: matches[0], kind: "file" };
|
|
17650
|
+
}
|
|
17651
|
+
function splitSymbol(symbol) {
|
|
17652
|
+
const segments = symbol.split(".");
|
|
17653
|
+
return segments.length > 1 ? { name: segments[segments.length - 1], container: segments[segments.length - 2] } : { name: symbol };
|
|
17654
|
+
}
|
|
17655
|
+
function matchingDeclarations(declarations, container) {
|
|
17656
|
+
if (container === undefined)
|
|
17657
|
+
return declarations;
|
|
17658
|
+
return declarations.filter((declaration) => declaration.qualifiedName.split(/::|\.|#|\//).includes(container));
|
|
17659
|
+
}
|
|
17145
17660
|
|
|
17146
17661
|
// packages/core/projection-engine/src/index.ts
|
|
17147
17662
|
function nativeNodeSource(node) {
|
|
@@ -17164,15 +17679,10 @@ function renderArchitectureDocumentationProjection(input) {
|
|
|
17164
17679
|
const generatedAt = input.generatedAt ?? "1970-01-01T00:00:00.000Z";
|
|
17165
17680
|
const scaleSignalsByNodeId = new Map((input.sourceScaleSignals ?? []).map((signal) => [signal.nodeId, signal]));
|
|
17166
17681
|
const scaleByNodeId = new Map([...scaleSignalsByNodeId].map(([nodeId, signal]) => [nodeId, architectureCapabilityScale(signal)]));
|
|
17682
|
+
const semanticNodes = semanticArchitectureNodes(model);
|
|
17167
17683
|
const semanticCompilationsByNodeId = new Map(model.nodes.map((node) => [node.id, compileSemanticCapabilityDiagrams({
|
|
17168
17684
|
capabilityId: node.id,
|
|
17169
|
-
nodes:
|
|
17170
|
-
id: entry.id,
|
|
17171
|
-
kind: entry.kind,
|
|
17172
|
-
name: entry.name,
|
|
17173
|
-
...entry.parent ? { parent: entry.parent } : {},
|
|
17174
|
-
...nativeNodeSource(entry) ? { source: nativeNodeSource(entry) } : {}
|
|
17175
|
-
})),
|
|
17685
|
+
nodes: semanticNodes,
|
|
17176
17686
|
relations: model.relations,
|
|
17177
17687
|
flows: model.flows ?? [],
|
|
17178
17688
|
evidence: input.selectorEvidence
|
|
@@ -17187,7 +17697,8 @@ function renderArchitectureDocumentationProjection(input) {
|
|
|
17187
17697
|
resulting: semanticState,
|
|
17188
17698
|
acceptedChange: input.refreshContext?.acceptedChange
|
|
17189
17699
|
});
|
|
17190
|
-
const
|
|
17700
|
+
const docsLanguage = input.docsLanguage === undefined ? undefined : assertArchitectureDocsLanguage(input.docsLanguage);
|
|
17701
|
+
const targetDrafts = architectureDocumentationTargetDrafts(model, layout2, docsLanguage);
|
|
17191
17702
|
const stampedNodeIds = new Set(architectureProjectionStampedNodeIds(model, layout2.profile));
|
|
17192
17703
|
const footprintDigestByNodeId = new Map(input.sourceFootprints.map((entry) => [entry.nodeId, entry.digest]));
|
|
17193
17704
|
const rendered = targetDrafts.map((draft) => {
|
|
@@ -17426,7 +17937,8 @@ function loadArchitectureDocumentationInputs(root, profile = "default") {
|
|
|
17426
17937
|
return {
|
|
17427
17938
|
model,
|
|
17428
17939
|
decisions: loadArchitectureDecisionRecords(root),
|
|
17429
|
-
existingFiles: loadArchitectureFilesForLayout(root, layout2)
|
|
17940
|
+
existingFiles: loadArchitectureFilesForLayout(root, layout2),
|
|
17941
|
+
docsLanguage: loadArchitectureDocumentationLanguage(root)
|
|
17430
17942
|
};
|
|
17431
17943
|
}
|
|
17432
17944
|
function loadCapabilitySourceScaleSignals(root, model, sourceFiles) {
|
|
@@ -17514,6 +18026,28 @@ function loadArchitectureDocumentationProfile(root) {
|
|
|
17514
18026
|
}
|
|
17515
18027
|
return manifest.profile;
|
|
17516
18028
|
}
|
|
18029
|
+
var ARCHITECTURE_DOCS_LANGUAGES = ["zh-Hant", "zh-Hans", "en"];
|
|
18030
|
+
var ARCHCONTEXT_MANIFEST_PATH = ".archcontext/manifest.yaml";
|
|
18031
|
+
function loadArchitectureDocumentationLanguage(root) {
|
|
18032
|
+
const path = resolve7(root, ARCHCONTEXT_MANIFEST_PATH);
|
|
18033
|
+
if (!existsSync5(path))
|
|
18034
|
+
return;
|
|
18035
|
+
let manifest;
|
|
18036
|
+
try {
|
|
18037
|
+
manifest = parseJsonOrStableYaml(readFileSync6(path, "utf8"), ARCHCONTEXT_MANIFEST_PATH);
|
|
18038
|
+
} catch {
|
|
18039
|
+
return;
|
|
18040
|
+
}
|
|
18041
|
+
const docs = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? manifest.docs : undefined;
|
|
18042
|
+
const language = docs && typeof docs === "object" && !Array.isArray(docs) ? docs.language : undefined;
|
|
18043
|
+
return language === undefined ? undefined : assertArchitectureDocsLanguage(language);
|
|
18044
|
+
}
|
|
18045
|
+
function assertArchitectureDocsLanguage(value) {
|
|
18046
|
+
if (!ARCHITECTURE_DOCS_LANGUAGES.includes(value)) {
|
|
18047
|
+
throw new Error(`architecture-docs-language-invalid: docs.language must be one of ${ARCHITECTURE_DOCS_LANGUAGES.join(", ")} (got ${JSON.stringify(value)})`);
|
|
18048
|
+
}
|
|
18049
|
+
return value;
|
|
18050
|
+
}
|
|
17517
18051
|
function loadArchitectureDecisionRecords(root) {
|
|
17518
18052
|
const dir = resolve7(root, "docs/adr");
|
|
17519
18053
|
if (!existsSync5(dir))
|
|
@@ -17663,6 +18197,28 @@ function loadArchitectureProjectionManifestStamps(root) {
|
|
|
17663
18197
|
...isV3Provenance ? { provenance } : {}
|
|
17664
18198
|
};
|
|
17665
18199
|
}
|
|
18200
|
+
function semanticArchitectureNodes(model) {
|
|
18201
|
+
return model.nodes.map((entry) => ({
|
|
18202
|
+
id: entry.id,
|
|
18203
|
+
kind: entry.kind,
|
|
18204
|
+
name: entry.name,
|
|
18205
|
+
...entry.parent ? { parent: entry.parent } : {},
|
|
18206
|
+
...nativeNodeSource(entry) ? { source: nativeNodeSource(entry) } : {}
|
|
18207
|
+
}));
|
|
18208
|
+
}
|
|
18209
|
+
function checkArchitectureFlowSelectors(input) {
|
|
18210
|
+
const model = normalizeNativeModel(input.model);
|
|
18211
|
+
return {
|
|
18212
|
+
schemaVersion: ARCHITECTURE_FLOW_SELECTOR_CHECK_SCHEMA_VERSION,
|
|
18213
|
+
codeGraph: input.codeGraph,
|
|
18214
|
+
...checkSemanticFlowSelectors({
|
|
18215
|
+
nodes: semanticArchitectureNodes(model),
|
|
18216
|
+
relations: model.relations,
|
|
18217
|
+
flows: model.flows ?? [],
|
|
18218
|
+
evidence: input.selectorEvidence
|
|
18219
|
+
})
|
|
18220
|
+
};
|
|
18221
|
+
}
|
|
17666
18222
|
function normalizeNativeModel(model) {
|
|
17667
18223
|
return {
|
|
17668
18224
|
nodes: [...model.nodes].sort((a, b) => a.id.localeCompare(b.id)),
|
|
@@ -17903,8 +18459,8 @@ function readYamlObjects(dir) {
|
|
|
17903
18459
|
function escapeMermaid(value) {
|
|
17904
18460
|
return value.replace(/"/g, "'");
|
|
17905
18461
|
}
|
|
17906
|
-
function architectureDocumentationTargetDrafts(model, layout2) {
|
|
17907
|
-
return layout2.targets.map((target) => target.type === "entity-summary" ? { ...target, skeleton: entitySummarySkeleton(model.nodes.find((node) => node.id === target.scope.id)) } : target);
|
|
18462
|
+
function architectureDocumentationTargetDrafts(model, layout2, docsLanguage) {
|
|
18463
|
+
return layout2.targets.map((target) => target.type === "entity-summary" ? { ...target, skeleton: entitySummarySkeleton(model.nodes.find((node) => node.id === target.scope.id), docsLanguage) } : target);
|
|
17908
18464
|
}
|
|
17909
18465
|
function projectionTarget(input) {
|
|
17910
18466
|
return {
|
|
@@ -17932,6 +18488,7 @@ function renderTargetGeneratedBody(target, model, input) {
|
|
|
17932
18488
|
return renderEntitySummary(node, model, {
|
|
17933
18489
|
scaleSignal: input.scaleSignalsByNodeId.get(node.id),
|
|
17934
18490
|
scale: input.scaleByNodeId.get(node.id),
|
|
18491
|
+
scaleByNodeId: input.scaleByNodeId,
|
|
17935
18492
|
semanticCompilation: input.semanticCompilationsByNodeId.get(node.id)
|
|
17936
18493
|
});
|
|
17937
18494
|
}
|
|
@@ -17992,6 +18549,7 @@ function renderEntitySummary(node, model, input) {
|
|
|
17992
18549
|
const incoming = model.relations.filter((relation) => relation.target === node.id);
|
|
17993
18550
|
const outgoing = model.relations.filter((relation) => relation.source === node.id);
|
|
17994
18551
|
const localContracts = contractFilesForNode(node);
|
|
18552
|
+
const components = model.nodes.filter((entry) => entry.parent === node.id && entry.kind === "component");
|
|
17995
18553
|
const lines = [
|
|
17996
18554
|
`> **狀態**:${node.status ? `\`${node.status}\`` : "未宣告(`status` 缺失)"}`,
|
|
17997
18555
|
`> **Capability ID**:\`${node.id}\`(kind \`${node.kind}\`)`,
|
|
@@ -18013,6 +18571,7 @@ function renderEntitySummary(node, model, input) {
|
|
|
18013
18571
|
"| --- | --- | --- |",
|
|
18014
18572
|
...entrypoints.flatMap((entry) => entry.symbols.map((symbol) => `| \`${entry.id}\` | \`${entry.path}#${symbol.name}\` | ${symbol.sinks.map((sink) => `\`${sink.id}\` → \`${sink.path}#${sink.symbol}\``).join("、")} |`))
|
|
18015
18573
|
] : ["- 未宣告 `source.entrypoints`,入口清單無法從架構模型推導。"],
|
|
18574
|
+
...renderComponentEntrypoints(components),
|
|
18016
18575
|
"",
|
|
18017
18576
|
"### 1.3 規模信號",
|
|
18018
18577
|
"",
|
|
@@ -18032,6 +18591,8 @@ function renderEntitySummary(node, model, input) {
|
|
|
18032
18591
|
"入向關係:",
|
|
18033
18592
|
"",
|
|
18034
18593
|
...incoming.length === 0 ? ["- 無。"] : incoming.map((relation) => `- \`${relation.kind}\` ← \`${relation.source}\` — ${relation.intent}`),
|
|
18594
|
+
...renderComponentOutboundRelations(components, model.relations),
|
|
18595
|
+
...renderComponentTable(components, input.scaleByNodeId),
|
|
18035
18596
|
"",
|
|
18036
18597
|
"## 2. P2:端到端數據流",
|
|
18037
18598
|
"",
|
|
@@ -18041,6 +18602,56 @@ function renderEntitySummary(node, model, input) {
|
|
|
18041
18602
|
`)}
|
|
18042
18603
|
`;
|
|
18043
18604
|
}
|
|
18605
|
+
function renderComponentEntrypoints(components) {
|
|
18606
|
+
if (components.length === 0)
|
|
18607
|
+
return [];
|
|
18608
|
+
const rows = components.flatMap((component) => (nativeNodeSource(component)?.entrypoints ?? []).flatMap((entry) => entry.symbols.map((symbol) => `| \`${component.id}\` | \`${entry.id}\` | \`${entry.path}#${symbol.name}\` | ${symbol.sinks.map((sink) => `\`${sink.id}\` → \`${sink.path}#${sink.symbol}\``).join("、")} |`)));
|
|
18609
|
+
return [
|
|
18610
|
+
"",
|
|
18611
|
+
"組件入口:",
|
|
18612
|
+
"",
|
|
18613
|
+
...rows.length > 0 ? ["| 組件 | 宣告入口 | 錨點 | 職責 |", "| --- | --- | --- | --- |", ...rows] : ["- 組件均未宣告 `source.entrypoints`。"]
|
|
18614
|
+
];
|
|
18615
|
+
}
|
|
18616
|
+
function renderComponentOutboundRelations(components, relations) {
|
|
18617
|
+
if (components.length === 0)
|
|
18618
|
+
return [];
|
|
18619
|
+
return [
|
|
18620
|
+
"",
|
|
18621
|
+
"組件出向關係:",
|
|
18622
|
+
"",
|
|
18623
|
+
...components.flatMap((component) => {
|
|
18624
|
+
const outgoing = relations.filter((relation) => relation.source === component.id);
|
|
18625
|
+
return [
|
|
18626
|
+
`- \`${component.id}\`:`,
|
|
18627
|
+
...outgoing.length === 0 ? [" - 無。"] : outgoing.map((relation) => ` - \`${relation.kind}\` → \`${relation.target}\` — ${relation.intent}`)
|
|
18628
|
+
];
|
|
18629
|
+
})
|
|
18630
|
+
];
|
|
18631
|
+
}
|
|
18632
|
+
function renderComponentTable(components, scaleByNodeId) {
|
|
18633
|
+
if (components.length === 0)
|
|
18634
|
+
return [];
|
|
18635
|
+
return [
|
|
18636
|
+
"",
|
|
18637
|
+
"### 1.5 組件結構",
|
|
18638
|
+
"",
|
|
18639
|
+
"| 組件 | 名稱 | 規模量級 | 宣告入口 |",
|
|
18640
|
+
"| --- | --- | --- | --- |",
|
|
18641
|
+
...components.map((component) => {
|
|
18642
|
+
const source = nativeNodeSource(component);
|
|
18643
|
+
let scaleCell = "未宣告 `source.include`";
|
|
18644
|
+
if ((source?.include ?? []).length > 0) {
|
|
18645
|
+
const scale = scaleByNodeId.get(component.id);
|
|
18646
|
+
if (!scale)
|
|
18647
|
+
throw new Error(`architecture-docs-projection-scale-signal-missing: ${component.id}`);
|
|
18648
|
+
scaleCell = `\`${scaleMagnitudeBucketLabel(scale.fileCountBucket)}\` 個文件 / \`${scaleMagnitudeBucketLabel(scale.lineCountBucket)}\` 行`;
|
|
18649
|
+
}
|
|
18650
|
+
const entrypointIds = (source?.entrypoints ?? []).map((entry) => `\`${entry.id}\``);
|
|
18651
|
+
return `| \`${component.id}\` | ${component.name.replaceAll("|", "\\|")} | ${scaleCell} | ${entrypointIds.length > 0 ? entrypointIds.join("、") : "未宣告"} |`;
|
|
18652
|
+
})
|
|
18653
|
+
];
|
|
18654
|
+
}
|
|
18044
18655
|
function renderSemanticP1Section(compilation) {
|
|
18045
18656
|
if (compilation.p1.status === "proven") {
|
|
18046
18657
|
return [
|
|
@@ -18081,16 +18692,23 @@ function entitySummaryTitle(node) {
|
|
|
18081
18692
|
const segments = node.id.replace(/^capability\./, "").split(".").filter((segment) => segment !== "");
|
|
18082
18693
|
return segments.length > 0 ? segments.join("/") : node.id;
|
|
18083
18694
|
}
|
|
18084
|
-
|
|
18695
|
+
var ENTITY_SUMMARY_SKELETON_TEXT = {
|
|
18696
|
+
legacy: { titleSuffix: " 架構文檔", p3: "## 3. P3:設計決策與不變量", history: "## 4. 歷史決策記錄(append-only)" },
|
|
18697
|
+
"zh-Hant": { titleSuffix: " 架構文檔", p3: "## 3. P3:設計決策與不變量", history: "## 4. 歷史決策記錄(append-only)" },
|
|
18698
|
+
"zh-Hans": { titleSuffix: " 架构文档", p3: "## 3. P3:设计决策与不变量", history: "## 4. 历史决策记录(append-only)" },
|
|
18699
|
+
en: { titleSuffix: " Architecture", p3: "## 3. P3: Design Decisions and Invariants", history: "## 4. Decision History (append-only)" }
|
|
18700
|
+
};
|
|
18701
|
+
function entitySummarySkeleton(node, docsLanguage) {
|
|
18702
|
+
const text = ENTITY_SUMMARY_SKELETON_TEXT[docsLanguage ?? "legacy"];
|
|
18085
18703
|
return {
|
|
18086
|
-
prefix: `# ${entitySummaryTitle(node)}
|
|
18704
|
+
prefix: `# ${entitySummaryTitle(node)}${text.titleSuffix}
|
|
18087
18705
|
|
|
18088
18706
|
`,
|
|
18089
18707
|
suffix: [
|
|
18090
18708
|
"",
|
|
18091
|
-
|
|
18709
|
+
text.p3,
|
|
18092
18710
|
"",
|
|
18093
|
-
|
|
18711
|
+
text.history,
|
|
18094
18712
|
"",
|
|
18095
18713
|
"## Optimization Backlog",
|
|
18096
18714
|
""
|
|
@@ -18098,6 +18716,9 @@ function entitySummarySkeleton(node) {
|
|
|
18098
18716
|
`)
|
|
18099
18717
|
};
|
|
18100
18718
|
}
|
|
18719
|
+
function entitySummarySkeletonVariants(node) {
|
|
18720
|
+
return [undefined, ...ARCHITECTURE_DOCS_LANGUAGES].map((language) => entitySummarySkeleton(node, language));
|
|
18721
|
+
}
|
|
18101
18722
|
function renderRelationSummary(relation, model) {
|
|
18102
18723
|
const source = model.nodes.find((node) => node.id === relation.source);
|
|
18103
18724
|
const target = model.nodes.find((node) => node.id === relation.target);
|
|
@@ -18263,7 +18884,7 @@ function architectureDocumentationProjectionDrift(input) {
|
|
|
18263
18884
|
path: existing.path,
|
|
18264
18885
|
targetId: region.targetId,
|
|
18265
18886
|
actualDigest,
|
|
18266
|
-
disposition: orphanHoldsOnlyIntactGeneratedRegion(existing.body, region.targetId,
|
|
18887
|
+
disposition: orphanHoldsOnlyIntactGeneratedRegion(existing.body, region.targetId, orphanSkeletons(committedEntityTargets, existing.path, region.targetId)) ? "delete" : "human-review"
|
|
18267
18888
|
});
|
|
18268
18889
|
}
|
|
18269
18890
|
}
|
|
@@ -18299,17 +18920,17 @@ function committedEntitySummaryTargets(manifestBody) {
|
|
|
18299
18920
|
}
|
|
18300
18921
|
return targets;
|
|
18301
18922
|
}
|
|
18302
|
-
function
|
|
18923
|
+
function orphanSkeletons(committedEntityTargets, path, targetId) {
|
|
18303
18924
|
const committed = committedEntityTargets.get(targetId);
|
|
18304
|
-
return committed && committed.path === path ?
|
|
18925
|
+
return committed && committed.path === path ? entitySummarySkeletonVariants({ id: committed.nodeId }) : [];
|
|
18305
18926
|
}
|
|
18306
|
-
function orphanHoldsOnlyIntactGeneratedRegion(body, targetId,
|
|
18927
|
+
function orphanHoldsOnlyIntactGeneratedRegion(body, targetId, skeletons) {
|
|
18307
18928
|
const region = findGeneratedRegion(body, targetId);
|
|
18308
18929
|
if (!region)
|
|
18309
18930
|
return false;
|
|
18310
18931
|
const before = body.slice(0, region.start);
|
|
18311
18932
|
const after = body.slice(region.end);
|
|
18312
|
-
const generatedOnly = `${before}${after}`.trim() === "" || skeleton
|
|
18933
|
+
const generatedOnly = `${before}${after}`.trim() === "" || skeletons.some((skeleton) => before === skeleton.prefix && after === skeleton.suffix);
|
|
18313
18934
|
if (!generatedOnly)
|
|
18314
18935
|
return false;
|
|
18315
18936
|
const metadata = parseGeneratedRegionMetadata(region.startMarker);
|
|
@@ -18545,26 +19166,78 @@ function findAgentContextRegion(body, nodeId) {
|
|
|
18545
19166
|
};
|
|
18546
19167
|
}
|
|
18547
19168
|
function resolveArchitectureOwnerForPath(nodes, path) {
|
|
18548
|
-
|
|
18549
|
-
|
|
19169
|
+
return resolveArchitectureOwnersForPaths(nodes, [path])[0];
|
|
19170
|
+
}
|
|
19171
|
+
function resolveArchitectureOwnersForPaths(nodes, paths) {
|
|
19172
|
+
const declaring = nodes.flatMap((node) => {
|
|
18550
19173
|
const source = nativeNodeSource(node);
|
|
18551
19174
|
const include = source?.include ?? [];
|
|
18552
19175
|
if (include.length === 0)
|
|
18553
|
-
|
|
18554
|
-
|
|
18555
|
-
|
|
18556
|
-
|
|
18557
|
-
|
|
18558
|
-
|
|
18559
|
-
|
|
19176
|
+
return [];
|
|
19177
|
+
return [{
|
|
19178
|
+
node,
|
|
19179
|
+
include: include.map((pattern) => ({ regex: globToRegExp(pattern), specificity: globLiteralPrefixLength(pattern) })),
|
|
19180
|
+
exclude: (source?.exclude ?? []).map(globToRegExp)
|
|
19181
|
+
}];
|
|
19182
|
+
});
|
|
19183
|
+
const parents = new Map(nodes.map((node) => [node.id, node.parent]));
|
|
19184
|
+
const kinds = new Map(nodes.map((node) => [node.id, node.kind]));
|
|
19185
|
+
const owningCapabilityOf = (id) => {
|
|
19186
|
+
for (const ancestor of ancestorChain(id, parents))
|
|
19187
|
+
if (kinds.get(ancestor) === "capability")
|
|
19188
|
+
return ancestor;
|
|
19189
|
+
return null;
|
|
19190
|
+
};
|
|
19191
|
+
return paths.map((path) => {
|
|
19192
|
+
const candidates = [];
|
|
19193
|
+
for (const entry of declaring) {
|
|
19194
|
+
if (entry.exclude.some((regex) => regex.test(path)))
|
|
19195
|
+
continue;
|
|
19196
|
+
const specificity = entry.include.filter((pattern) => pattern.regex.test(path)).reduce((max, pattern) => Math.max(max, pattern.specificity), -1);
|
|
19197
|
+
if (specificity >= 0)
|
|
19198
|
+
candidates.push({ node: entry.node, specificity });
|
|
19199
|
+
}
|
|
19200
|
+
if (candidates.length === 0)
|
|
19201
|
+
return { status: "no-match", owningCapabilityId: null };
|
|
19202
|
+
const chainOwner = deepestOnOneParentChain(candidates.map((candidate) => candidate.node.id), parents);
|
|
19203
|
+
if (chainOwner !== undefined) {
|
|
19204
|
+
const node = candidates.find((candidate) => candidate.node.id === chainOwner).node;
|
|
19205
|
+
return { status: "matched", node, owningCapabilityId: owningCapabilityOf(node.id) };
|
|
19206
|
+
}
|
|
19207
|
+
const chains = new Map(candidates.map((candidate) => [candidate.node.id, ancestorChain(candidate.node.id, parents)]));
|
|
19208
|
+
const isStrictAncestorOf = (ancestor, descendant) => ancestor !== descendant && chains.get(descendant).has(ancestor) && !chains.get(ancestor).has(descendant);
|
|
19209
|
+
const leaves = candidates.filter((candidate) => !candidates.some((other) => isStrictAncestorOf(candidate.node.id, other.node.id)));
|
|
19210
|
+
const contenders = leaves.length > 0 ? leaves : candidates;
|
|
19211
|
+
const maxSpecificity = contenders.reduce((max, candidate) => Math.max(max, candidate.specificity), -1);
|
|
19212
|
+
const winners = contenders.filter((candidate) => candidate.specificity === maxSpecificity).map((candidate) => candidate.node);
|
|
19213
|
+
if (winners.length === 1)
|
|
19214
|
+
return { status: "matched", node: winners[0], owningCapabilityId: owningCapabilityOf(winners[0].id) };
|
|
19215
|
+
const capabilities = new Set(winners.map((node) => owningCapabilityOf(node.id)));
|
|
19216
|
+
return { status: "ambiguous", candidates: winners, owningCapabilityId: capabilities.size === 1 ? [...capabilities][0] : null };
|
|
19217
|
+
});
|
|
19218
|
+
}
|
|
19219
|
+
function deepestOnOneParentChain(candidateIds, parents) {
|
|
19220
|
+
if (candidateIds.length === 0)
|
|
19221
|
+
return;
|
|
19222
|
+
const depthOf = (id) => ancestorChain(id, parents).size - 1;
|
|
19223
|
+
const deepest = candidateIds.reduce((best, id) => {
|
|
19224
|
+
const bestDepth = depthOf(best);
|
|
19225
|
+
const depth = depthOf(id);
|
|
19226
|
+
if (depth !== bestDepth)
|
|
19227
|
+
return depth > bestDepth ? id : best;
|
|
19228
|
+
return id < best ? id : best;
|
|
19229
|
+
});
|
|
19230
|
+
const chain = ancestorChain(deepest, parents);
|
|
19231
|
+
return candidateIds.every((id) => chain.has(id)) ? deepest : undefined;
|
|
19232
|
+
}
|
|
19233
|
+
function ancestorChain(id, parents) {
|
|
19234
|
+
const chain = new Set;
|
|
19235
|
+
let current = id;
|
|
19236
|
+
while (current !== undefined && parents.has(current) && !chain.has(current)) {
|
|
19237
|
+
chain.add(current);
|
|
19238
|
+
current = parents.get(current);
|
|
18560
19239
|
}
|
|
18561
|
-
|
|
18562
|
-
return { status: "no-match" };
|
|
18563
|
-
const maxSpecificity = candidates.reduce((max, candidate) => Math.max(max, candidate.specificity), -1);
|
|
18564
|
-
const winners = candidates.filter((candidate) => candidate.specificity === maxSpecificity).map((candidate) => candidate.node);
|
|
18565
|
-
if (winners.length > 1)
|
|
18566
|
-
return { status: "ambiguous", candidates: winners };
|
|
18567
|
-
return { status: "matched", node: winners[0] };
|
|
19240
|
+
return chain;
|
|
18568
19241
|
}
|
|
18569
19242
|
function matchesGlob(path, pattern) {
|
|
18570
19243
|
return globToRegExp(pattern).test(path);
|
|
@@ -18752,12 +19425,23 @@ function readTrackedSourceFiles(root, options = {}) {
|
|
|
18752
19425
|
}
|
|
18753
19426
|
function readWorkspacePackages(root) {
|
|
18754
19427
|
const manifest = JSON.parse(readFileSync7(join4(root, "package.json"), "utf8"));
|
|
18755
|
-
return (manifest.workspaces ?? []).map((directory) => {
|
|
19428
|
+
return (manifest.workspaces ?? []).flatMap((entry) => expandWorkspaceEntry(root, entry)).map((directory) => {
|
|
18756
19429
|
const path = join4(root, ...directory.split("/"), "package.json");
|
|
18757
19430
|
const packageManifest = JSON.parse(readFileSync7(path, "utf8"));
|
|
18758
19431
|
return { name: packageManifest.name, root: directory, exports: packageManifest.exports ?? {} };
|
|
18759
19432
|
}).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
18760
19433
|
}
|
|
19434
|
+
function expandWorkspaceEntry(root, entry) {
|
|
19435
|
+
if (!entry.includes("*"))
|
|
19436
|
+
return [entry];
|
|
19437
|
+
const base = entry.slice(0, -2);
|
|
19438
|
+
if (!entry.endsWith("/*") || base.includes("*"))
|
|
19439
|
+
throw new Error(`workspace-glob-unsupported: ${entry}`);
|
|
19440
|
+
const directory = join4(root, ...base.split("/"));
|
|
19441
|
+
if (!existsSync6(directory))
|
|
19442
|
+
return [];
|
|
19443
|
+
return readdirSync4(directory, { withFileTypes: true }).filter((child) => child.isDirectory() && existsSync6(join4(directory, child.name, "package.json"))).map((child) => `${base}/${child.name}`).sort();
|
|
19444
|
+
}
|
|
18761
19445
|
function isDetachedHead(root) {
|
|
18762
19446
|
return runGit(root, ["rev-parse", "--abbrev-ref", "HEAD"]).trim() === "HEAD";
|
|
18763
19447
|
}
|
|
@@ -18991,7 +19675,8 @@ var CODEGRAPH_TELEMETRY_ENV = "DO_NOT_TRACK";
|
|
|
18991
19675
|
var CODEGRAPH_TELEMETRY_DISABLED_VALUE = "1";
|
|
18992
19676
|
var DEFAULT_CODEGRAPH_BINARY = "codegraph";
|
|
18993
19677
|
var requireFromAdapter = createRequire2(import.meta.url);
|
|
18994
|
-
var
|
|
19678
|
+
var codeGraphSdk = requireFromAdapter(REQUIRED_CODEGRAPH_PACKAGE);
|
|
19679
|
+
var { CodeGraph } = codeGraphSdk;
|
|
18995
19680
|
var CODEGRAPH_OUTPUT_MAX_BYTES = 32 * 1024 * 1024;
|
|
18996
19681
|
var CODEGRAPH_QUERY_TIMEOUT_MS = 30000;
|
|
18997
19682
|
function disableCodeGraphTelemetryByDefault(env = process.env) {
|
|
@@ -19502,7 +20187,8 @@ function architectureDeltaTask(git) {
|
|
|
19502
20187
|
...git.paths.flatMap((change) => [change.path, change.previousPath ?? ""])
|
|
19503
20188
|
].filter(Boolean)).join(" ");
|
|
19504
20189
|
}
|
|
19505
|
-
var
|
|
20190
|
+
var CODEGRAPH_IMPORT_NODE_READ_LIMIT = 50000;
|
|
20191
|
+
var CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES = 4 * 1024;
|
|
19506
20192
|
function prepareArchitectureDocumentationProjectionSnapshot(root, model, options = {}) {
|
|
19507
20193
|
const baseHeadSha = readProjectionHeadSha(root);
|
|
19508
20194
|
const sourceFiles = listProjectionSourceFiles(root);
|
|
@@ -19567,7 +20253,7 @@ function assertProjectionCodeFactsAvailable(root, prepared) {
|
|
|
19567
20253
|
function prepareProjectionCodeFacts(root, model, options) {
|
|
19568
20254
|
const invocation = options.binary ? codeGraphCliInvocation(options.binary, root, "") : packagedCodeGraphCliInvocation();
|
|
19569
20255
|
const binaryPath = invocation.argsPrefix[0] ?? invocation.command;
|
|
19570
|
-
const actualVersion = runProjectionCodeGraph(invocation, root, ["--version"], options.timeouts?.versionMs ??
|
|
20256
|
+
const actualVersion = runProjectionCodeGraph(invocation, root, ["--version"], options.timeouts?.versionMs ?? 1e4).trim();
|
|
19571
20257
|
if (actualVersion !== REQUIRED_CODEGRAPH_VERSION) {
|
|
19572
20258
|
throw new Error(`CodeGraph ${REQUIRED_CODEGRAPH_VERSION} required, got actual binary ${actualVersion || "unknown"}`);
|
|
19573
20259
|
}
|
|
@@ -19608,7 +20294,9 @@ function prepareProjectionCodeFacts(root, model, options) {
|
|
|
19608
20294
|
const inputs = loadCapabilityCodeGraphProjectionInputs(root, model, {
|
|
19609
20295
|
...options.sourceFiles ? { sourceFiles: options.sourceFiles } : {},
|
|
19610
20296
|
binary: binaryPath,
|
|
19611
|
-
importNodeLimit: options.importNodeLimit
|
|
20297
|
+
importNodeLimit: options.importNodeLimit,
|
|
20298
|
+
importNodeCount: statusImportNodeCount(postStatus),
|
|
20299
|
+
...options.importGraphs === false ? { importGraphs: false } : {}
|
|
19612
20300
|
});
|
|
19613
20301
|
const finalStatus = readProjectionCodeGraphStatus(invocation, root, statusTimeout);
|
|
19614
20302
|
assertProjectionCodeGraphStatus(finalStatus, root, "post-query");
|
|
@@ -19743,8 +20431,36 @@ function digestFile(path) {
|
|
|
19743
20431
|
function codeGraphIndexAvailable(workspaceRoot) {
|
|
19744
20432
|
return existsSync7(join5(workspaceRoot, ".codegraph"));
|
|
19745
20433
|
}
|
|
20434
|
+
var NON_DECLARATION_KINDS = new Set(["file", "import", "export", "parameter"]);
|
|
20435
|
+
function openCodeGraphSymbolReader(workspaceRoot, openIndex = (root, options) => CodeGraph.openSync(root, options)) {
|
|
20436
|
+
if (!codeGraphIndexAvailable(workspaceRoot))
|
|
20437
|
+
return { availability: "unavailable", reasonCode: "index-missing" };
|
|
20438
|
+
disableCodeGraphTelemetryByDefault();
|
|
20439
|
+
let graph;
|
|
20440
|
+
try {
|
|
20441
|
+
graph = openIndex(workspaceRoot, { readOnly: true });
|
|
20442
|
+
} catch {
|
|
20443
|
+
return { availability: "unavailable", reasonCode: "index-open-failed" };
|
|
20444
|
+
}
|
|
20445
|
+
const declarations = (nodes) => nodes.filter((node) => !NON_DECLARATION_KINDS.has(node.kind)).map((node) => ({ name: node.name, qualifiedName: node.qualifiedName, startLine: node.startLine, endLine: node.endLine })).sort((left, right) => left.startLine - right.startLine || left.qualifiedName.localeCompare(right.qualifiedName));
|
|
20446
|
+
return {
|
|
20447
|
+
availability: "ready",
|
|
20448
|
+
reader: {
|
|
20449
|
+
supportsPath: (path) => codeGraphSdk.isLanguageSupported(codeGraphSdk.detectLanguage(path)),
|
|
20450
|
+
declarationsInSource: (path, content) => {
|
|
20451
|
+
try {
|
|
20452
|
+
return declarations(graph.extractFromSource(path, content).nodes);
|
|
20453
|
+
} catch {
|
|
20454
|
+
return;
|
|
20455
|
+
}
|
|
20456
|
+
},
|
|
20457
|
+
declarationsNamed: (name) => declarations(graph.getNodesByName(name).filter((node) => node.name === name)),
|
|
20458
|
+
close: () => graph.close()
|
|
20459
|
+
}
|
|
20460
|
+
};
|
|
20461
|
+
}
|
|
19746
20462
|
function loadCapabilityCodeGraphProjectionInputs(root, model, options = {}) {
|
|
19747
|
-
const declaresFootprint = model.nodes.some((node) => (nativeNodeSource(node)?.include ?? []).length > 0);
|
|
20463
|
+
const declaresFootprint = options.importGraphs !== false && model.nodes.some((node) => (nativeNodeSource(node)?.include ?? []).length > 0);
|
|
19748
20464
|
const footprints = declaresFootprint ? loadCapabilitySourceFootprints(root, model, options.sourceFiles ?? listProjectionSourceFiles(root)) : [];
|
|
19749
20465
|
const entrypointNodes = model.nodes.map((node) => ({ nodeId: node.id, entrypoints: nativeNodeSource(node)?.entrypoints ?? [] })).filter((entry) => entry.entrypoints.length > 0).sort((left, right) => left.nodeId.localeCompare(right.nodeId));
|
|
19750
20466
|
if (footprints.length === 0 && entrypointNodes.length === 0)
|
|
@@ -19753,11 +20469,11 @@ function loadCapabilityCodeGraphProjectionInputs(root, model, options = {}) {
|
|
|
19753
20469
|
return { importGraphs: [], selectorEvidence: [] };
|
|
19754
20470
|
disableCodeGraphTelemetryByDefault();
|
|
19755
20471
|
const binary = options.binary ?? DEFAULT_CODEGRAPH_BINARY;
|
|
19756
|
-
const importGraphs = footprints.length > 0 ? capabilityImportGraphs(root, binary, footprints, options.importNodeLimit ??
|
|
20472
|
+
const importGraphs = footprints.length > 0 ? capabilityImportGraphs(root, binary, footprints, options.importNodeLimit ?? CODEGRAPH_IMPORT_NODE_READ_LIMIT, options.importNodeCount ?? null) : [];
|
|
19757
20473
|
const selectorIndex = entrypointNodes.length > 0 ? options.selectorIndexFactory?.(root) ?? CodeGraph.openSync(root) : undefined;
|
|
19758
20474
|
let selectorEvidence;
|
|
19759
20475
|
try {
|
|
19760
|
-
selectorEvidence = entrypointNodes.flatMap((entry) => entry.entrypoints.flatMap((entrypoint) => entrypoint.symbols.flatMap((source) => source.sinks.map((sink) => exactSelectorEvidence(selectorIndex, {
|
|
20476
|
+
selectorEvidence = entrypointNodes.flatMap((entry) => entry.entrypoints.flatMap((entrypoint) => entrypoint.symbols.flatMap((source) => source.sinks.map((sink) => exactSelectorEvidence(root, selectorIndex, {
|
|
19761
20477
|
nodeId: entry.nodeId,
|
|
19762
20478
|
entrypointId: entrypoint.id,
|
|
19763
20479
|
sourcePath: entrypoint.path,
|
|
@@ -19772,10 +20488,21 @@ function loadCapabilityCodeGraphProjectionInputs(root, model, options = {}) {
|
|
|
19772
20488
|
return { importGraphs, selectorEvidence };
|
|
19773
20489
|
}
|
|
19774
20490
|
function repositoryImportPairs(root, binary, limit, worktreeDigest) {
|
|
20491
|
+
if (!Number.isInteger(limit) || limit < 1)
|
|
20492
|
+
throw new Error(`import node limit must be a positive integer, got ${limit}`);
|
|
19775
20493
|
if (!codeGraphIndexAvailable(root)) {
|
|
19776
|
-
return {
|
|
20494
|
+
return {
|
|
20495
|
+
pairs: [],
|
|
20496
|
+
truncated: true,
|
|
20497
|
+
availability: "unavailable",
|
|
20498
|
+
indexedWorktreeDigest: null,
|
|
20499
|
+
importNodeCount: null,
|
|
20500
|
+
importNodeLimit: limit,
|
|
20501
|
+
importNodeLimitExceeded: false
|
|
20502
|
+
};
|
|
19777
20503
|
}
|
|
19778
|
-
const
|
|
20504
|
+
const status = readProjectionCodeGraphStatus(codeGraphCliInvocation(binary, root, ""), root, CODEGRAPH_QUERY_TIMEOUT_MS);
|
|
20505
|
+
const nodes = codeGraphImportPopulation(root, binary, limit, statusImportNodeCount(status));
|
|
19779
20506
|
const pairs = new Map;
|
|
19780
20507
|
for (const node of nodes.imports) {
|
|
19781
20508
|
if (node.kind !== "import")
|
|
@@ -19787,18 +20514,43 @@ function repositoryImportPairs(root, binary, limit, worktreeDigest) {
|
|
|
19787
20514
|
const firstLine = line === undefined ? previousLine : previousLine === undefined ? line : Math.min(line, previousLine);
|
|
19788
20515
|
pairs.set(key, { from: node.filePath, specifier: node.name, to, ...firstLine === undefined ? {} : { line: firstLine } });
|
|
19789
20516
|
}
|
|
19790
|
-
const status = readProjectionCodeGraphStatus(codeGraphCliInvocation(binary, root, ""), root, CODEGRAPH_QUERY_TIMEOUT_MS);
|
|
19791
20517
|
const pending = status.pendingChanges;
|
|
19792
20518
|
const clean = status.initialized && pending !== undefined && pending.added === 0 && pending.modified === 0 && pending.removed === 0;
|
|
19793
20519
|
return {
|
|
19794
20520
|
pairs: [...pairs.keys()].sort().map((key) => pairs.get(key)),
|
|
19795
20521
|
truncated: nodes.truncated,
|
|
19796
20522
|
availability: clean ? "ready" : "unavailable",
|
|
19797
|
-
indexedWorktreeDigest: clean ? worktreeDigest : null
|
|
20523
|
+
indexedWorktreeDigest: clean ? worktreeDigest : null,
|
|
20524
|
+
importNodeCount: nodes.count,
|
|
20525
|
+
importNodeLimit: limit,
|
|
20526
|
+
importNodeLimitExceeded: nodes.limitExceeded
|
|
19798
20527
|
};
|
|
19799
20528
|
}
|
|
19800
|
-
function
|
|
19801
|
-
|
|
20529
|
+
function statusImportNodeCount(status) {
|
|
20530
|
+
if (!status.nodesByKind || typeof status.nodesByKind !== "object")
|
|
20531
|
+
return null;
|
|
20532
|
+
const count = status.nodesByKind.import;
|
|
20533
|
+
return Number.isInteger(count) && count >= 0 ? count : null;
|
|
20534
|
+
}
|
|
20535
|
+
function codeGraphImportPopulation(root, binary, limit, reportedCount) {
|
|
20536
|
+
const queryLimit = Math.min(reportedCount ?? limit, limit) + 1;
|
|
20537
|
+
const output = runCodeGraphCli(binary, root, ["query", "-p", root, "-j", "-l", String(queryLimit), "-k", "import", "import"], Math.max(CODEGRAPH_OUTPUT_MAX_BYTES, queryLimit * CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES));
|
|
20538
|
+
const imports = JSON.parse(output).map(({ node }) => node);
|
|
20539
|
+
const limitExceeded = (reportedCount ?? imports.length) > limit;
|
|
20540
|
+
if (limitExceeded) {
|
|
20541
|
+
return { imports: imports.slice(0, limit), count: reportedCount, truncated: imports.length < limit, limitExceeded };
|
|
20542
|
+
}
|
|
20543
|
+
return {
|
|
20544
|
+
imports,
|
|
20545
|
+
count: reportedCount ?? imports.length,
|
|
20546
|
+
truncated: reportedCount !== null && imports.length !== reportedCount,
|
|
20547
|
+
limitExceeded
|
|
20548
|
+
};
|
|
20549
|
+
}
|
|
20550
|
+
function capabilityImportGraphs(root, binary, footprints, limit, reportedCount) {
|
|
20551
|
+
if (!Number.isInteger(limit) || limit < 1)
|
|
20552
|
+
throw new Error(`import node limit must be a positive integer, got ${limit}`);
|
|
20553
|
+
const nodes = codeGraphImportPopulation(root, binary, limit, reportedCount);
|
|
19802
20554
|
const pairs = resolvedImportPairs(root, nodes.imports);
|
|
19803
20555
|
return footprints.map((footprint) => {
|
|
19804
20556
|
const scope = new Set(footprint.files);
|
|
@@ -19812,39 +20564,147 @@ function capabilityImportGraphs(root, binary, footprints, limit) {
|
|
|
19812
20564
|
nodeId: footprint.nodeId,
|
|
19813
20565
|
files: footprint.files,
|
|
19814
20566
|
edges: [...edges.values()].sort((left, right) => left.from.localeCompare(right.from) || left.to.localeCompare(right.to)),
|
|
19815
|
-
truncated: nodes.truncated
|
|
20567
|
+
truncated: nodes.truncated,
|
|
20568
|
+
...nodes.limitExceeded ? { importNodeLimitExceeded: true } : {}
|
|
19816
20569
|
};
|
|
19817
20570
|
});
|
|
19818
20571
|
}
|
|
19819
|
-
|
|
19820
|
-
|
|
19821
|
-
|
|
19822
|
-
|
|
19823
|
-
|
|
19824
|
-
|
|
19825
|
-
|
|
19826
|
-
|
|
20572
|
+
var RUNTIME_BUILTIN_CALL_NAMES = new Set([
|
|
20573
|
+
"basename",
|
|
20574
|
+
"dirname",
|
|
20575
|
+
"extname",
|
|
20576
|
+
"format",
|
|
20577
|
+
"isAbsolute",
|
|
20578
|
+
"join",
|
|
20579
|
+
"normalize",
|
|
20580
|
+
"parse",
|
|
20581
|
+
"relative",
|
|
20582
|
+
"resolve",
|
|
20583
|
+
"toNamespacedPath",
|
|
20584
|
+
"access",
|
|
20585
|
+
"accessSync",
|
|
20586
|
+
"appendFile",
|
|
20587
|
+
"appendFileSync",
|
|
20588
|
+
"chmod",
|
|
20589
|
+
"chmodSync",
|
|
20590
|
+
"close",
|
|
20591
|
+
"closeSync",
|
|
20592
|
+
"copyFile",
|
|
20593
|
+
"copyFileSync",
|
|
20594
|
+
"cp",
|
|
20595
|
+
"cpSync",
|
|
20596
|
+
"createReadStream",
|
|
20597
|
+
"createWriteStream",
|
|
20598
|
+
"exists",
|
|
20599
|
+
"existsSync",
|
|
20600
|
+
"fsync",
|
|
20601
|
+
"fsyncSync",
|
|
20602
|
+
"lstat",
|
|
20603
|
+
"lstatSync",
|
|
20604
|
+
"mkdir",
|
|
20605
|
+
"mkdirSync",
|
|
20606
|
+
"mkdtemp",
|
|
20607
|
+
"mkdtempSync",
|
|
20608
|
+
"open",
|
|
20609
|
+
"openSync",
|
|
20610
|
+
"opendir",
|
|
20611
|
+
"opendirSync",
|
|
20612
|
+
"read",
|
|
20613
|
+
"readFile",
|
|
20614
|
+
"readFileSync",
|
|
20615
|
+
"readSync",
|
|
20616
|
+
"readdir",
|
|
20617
|
+
"readdirSync",
|
|
20618
|
+
"readlink",
|
|
20619
|
+
"readlinkSync",
|
|
20620
|
+
"realpath",
|
|
20621
|
+
"realpathSync",
|
|
20622
|
+
"rename",
|
|
20623
|
+
"renameSync",
|
|
20624
|
+
"rm",
|
|
20625
|
+
"rmSync",
|
|
20626
|
+
"rmdir",
|
|
20627
|
+
"rmdirSync",
|
|
20628
|
+
"stat",
|
|
20629
|
+
"statSync",
|
|
20630
|
+
"symlink",
|
|
20631
|
+
"symlinkSync",
|
|
20632
|
+
"unlink",
|
|
20633
|
+
"unlinkSync",
|
|
20634
|
+
"watch",
|
|
20635
|
+
"write",
|
|
20636
|
+
"writeFile",
|
|
20637
|
+
"writeFileSync",
|
|
20638
|
+
"writeSync",
|
|
20639
|
+
"exec",
|
|
20640
|
+
"execFile",
|
|
20641
|
+
"execFileSync",
|
|
20642
|
+
"execSync",
|
|
20643
|
+
"fork",
|
|
20644
|
+
"spawn",
|
|
20645
|
+
"spawnSync",
|
|
20646
|
+
"homedir",
|
|
20647
|
+
"hostname",
|
|
20648
|
+
"platform",
|
|
20649
|
+
"tmpdir",
|
|
20650
|
+
"fileURLToPath",
|
|
20651
|
+
"pathToFileURL",
|
|
20652
|
+
"createHash",
|
|
20653
|
+
"randomBytes",
|
|
20654
|
+
"randomUUID",
|
|
20655
|
+
"clearInterval",
|
|
20656
|
+
"clearTimeout",
|
|
20657
|
+
"fetch",
|
|
20658
|
+
"queueMicrotask",
|
|
20659
|
+
"setInterval",
|
|
20660
|
+
"setTimeout",
|
|
20661
|
+
"structuredClone"
|
|
20662
|
+
]);
|
|
20663
|
+
function exactSelectorEvidence(root, index, selector) {
|
|
20664
|
+
const sources = indexedSymbolsAt(index, selector.sourceSymbol, selector.sourcePath);
|
|
20665
|
+
const sinkDeclarations = indexedSymbolsAt(index, selector.sinkSymbol, selector.sinkPath);
|
|
20666
|
+
const exactEdges = sources.flatMap((source) => index.getOutgoingEdges(source.id).filter((edge) => edge.kind === "calls" || edge.kind === "references").flatMap((edge) => {
|
|
19827
20667
|
const target = index.getNode(edge.target);
|
|
19828
20668
|
return target && target.name === selector.sinkSymbol && normalizedIndexPath(target.filePath) === normalizedIndexPath(selector.sinkPath) ? [{ edge, target }] : [];
|
|
19829
20669
|
}));
|
|
20670
|
+
const calls = exactEdges.filter(({ edge }) => edge.kind === "calls");
|
|
20671
|
+
const falseBuiltinCalls = calls.length > 0 && isFalseBuiltinCall(root, index, sources[0].filePath, selector);
|
|
20672
|
+
const exactCalls = falseBuiltinCalls ? [] : calls;
|
|
20673
|
+
const references = exactCalls.length === 0 ? exactEdges.filter(({ edge }) => edge.kind === "references") : [];
|
|
19830
20674
|
const sinkIdentities = new Set(exactCalls.map(({ target }) => target.id));
|
|
19831
|
-
const ambiguous = sources.length > 1 || sinkIdentities.size > 1;
|
|
19832
|
-
const callSites = [...new Map(exactCalls.map(({ edge }) => {
|
|
19833
|
-
const callSite = { path: selector.sourcePath, ...edge.line === undefined ? {} : { line: edge.line } };
|
|
19834
|
-
return [`${callSite.path}\x00${callSite.line ?? ""}`, callSite];
|
|
19835
|
-
})).values()].sort((left, right) => left.path.localeCompare(right.path) || (left.line ?? 0) - (right.line ?? 0));
|
|
20675
|
+
const ambiguous = sources.length > 1 || sinkDeclarations.length > 1 || sinkIdentities.size > 1;
|
|
19836
20676
|
return {
|
|
19837
20677
|
...selector,
|
|
19838
20678
|
matched: !ambiguous && sinkIdentities.size === 1,
|
|
19839
20679
|
ambiguous,
|
|
19840
20680
|
truncated: false,
|
|
19841
|
-
callSites
|
|
20681
|
+
callSites: edgeSites(selector.sourcePath, exactCalls),
|
|
20682
|
+
...falseBuiltinCalls ? { rejectedBuiltinCallSites: edgeSites(selector.sourcePath, calls) } : {},
|
|
20683
|
+
...references.length > 0 ? { referenceSites: edgeSites(selector.sourcePath, references) } : {}
|
|
19842
20684
|
};
|
|
19843
20685
|
}
|
|
20686
|
+
function indexedSymbolsAt(index, name, path) {
|
|
20687
|
+
return index.getNodesByName(name).filter((node) => normalizedIndexPath(node.filePath) === normalizedIndexPath(path));
|
|
20688
|
+
}
|
|
20689
|
+
function edgeSites(sourcePath, edges) {
|
|
20690
|
+
return [...new Map(edges.map(({ edge }) => {
|
|
20691
|
+
const site = { path: sourcePath, ...edge.line === undefined ? {} : { line: edge.line } };
|
|
20692
|
+
return [`${site.path}\x00${site.line ?? ""}`, site];
|
|
20693
|
+
})).values()].sort((left, right) => left.path.localeCompare(right.path) || (left.line ?? 0) - (right.line ?? 0));
|
|
20694
|
+
}
|
|
20695
|
+
function isFalseBuiltinCall(root, index, indexedSourcePath, selector) {
|
|
20696
|
+
if (!RUNTIME_BUILTIN_CALL_NAMES.has(selector.sinkSymbol))
|
|
20697
|
+
return false;
|
|
20698
|
+
const sourcePath = normalizedIndexPath(selector.sourcePath);
|
|
20699
|
+
const sinkPath = normalizedIndexPath(selector.sinkPath);
|
|
20700
|
+
if (sourcePath === sinkPath)
|
|
20701
|
+
return false;
|
|
20702
|
+
return !index.getNodesInFile(indexedSourcePath).some((node) => node.kind === "import" && [node.name, node.name.replace(/\.[cm]?js$/, "")].some((specifier) => resolveImportTarget(root, sourcePath, specifier) === sinkPath));
|
|
20703
|
+
}
|
|
19844
20704
|
function normalizedIndexPath(path) {
|
|
19845
20705
|
return path.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
19846
20706
|
}
|
|
19847
|
-
function runCodeGraphCli(binary, workspaceRoot, args) {
|
|
20707
|
+
function runCodeGraphCli(binary, workspaceRoot, args, maxBuffer = CODEGRAPH_OUTPUT_MAX_BYTES) {
|
|
19848
20708
|
disableCodeGraphTelemetryByDefault();
|
|
19849
20709
|
const invocation = codeGraphCliInvocation(binary, workspaceRoot);
|
|
19850
20710
|
try {
|
|
@@ -19852,7 +20712,7 @@ function runCodeGraphCli(binary, workspaceRoot, args) {
|
|
|
19852
20712
|
cwd: workspaceRoot,
|
|
19853
20713
|
encoding: "utf8",
|
|
19854
20714
|
stdio: ["ignore", "pipe", "pipe"],
|
|
19855
|
-
maxBuffer
|
|
20715
|
+
maxBuffer,
|
|
19856
20716
|
timeout: CODEGRAPH_QUERY_TIMEOUT_MS
|
|
19857
20717
|
});
|
|
19858
20718
|
} catch (error) {
|
|
@@ -20464,14 +21324,15 @@ function localEgressStatus(env = process.env, live = {}) {
|
|
|
20464
21324
|
}
|
|
20465
21325
|
|
|
20466
21326
|
// packages/surfaces/cli/src/main.ts
|
|
20467
|
-
import { spawn as spawn2, spawnSync as
|
|
20468
|
-
import { accessSync as accessSync2, closeSync as closeSync10, constants as constants2, existsSync as
|
|
20469
|
-
import { dirname as dirname15, join as join18, resolve as
|
|
21327
|
+
import { spawn as spawn2, spawnSync as spawnSync4 } from "child_process";
|
|
21328
|
+
import { accessSync as accessSync2, closeSync as closeSync10, constants as constants2, existsSync as existsSync28, mkdirSync as mkdirSync13, openSync as openSync10, readFileSync as readFileSync28, rmSync as rmSync15, statSync as statSync10 } from "fs";
|
|
21329
|
+
import { dirname as dirname15, join as join18, resolve as resolve29 } from "path";
|
|
20470
21330
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
20471
21331
|
|
|
20472
21332
|
// packages/contracts/src/index.ts
|
|
20473
21333
|
init_control_plane_routes();
|
|
20474
21334
|
init_architecture();
|
|
21335
|
+
init_docs_anchors();
|
|
20475
21336
|
init_github_governance();
|
|
20476
21337
|
init_ledger();
|
|
20477
21338
|
init_ports();
|
|
@@ -20597,7 +21458,7 @@ function assertAllowedArchContextPath2(root, relativePath, scope) {
|
|
|
20597
21458
|
// packages/core/architecture-domain/src/index.ts
|
|
20598
21459
|
init_src();
|
|
20599
21460
|
import { createHash as createHash6 } from "node:crypto";
|
|
20600
|
-
import { readdirSync as
|
|
21461
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync10, realpathSync as realpathSync6, statSync as statSync4 } from "node:fs";
|
|
20601
21462
|
import { relative as relative7, resolve as resolve12, sep as sep6 } from "node:path";
|
|
20602
21463
|
var DEFAULT_IGNORES2 = new Set([
|
|
20603
21464
|
".git",
|
|
@@ -20658,7 +21519,7 @@ function listRepoFiles2(root, ignore = DEFAULT_IGNORES2) {
|
|
|
20658
21519
|
walk(resolve12(root));
|
|
20659
21520
|
return out.sort();
|
|
20660
21521
|
function walk(dir) {
|
|
20661
|
-
for (const entry of
|
|
21522
|
+
for (const entry of readdirSync5(dir, { withFileTypes: true })) {
|
|
20662
21523
|
const absolute = resolve12(dir, entry.name);
|
|
20663
21524
|
const rel = relative7(root, absolute).split(sep6).join("/");
|
|
20664
21525
|
if (!rel || shouldIgnorePath2(entry.name, rel, ignore))
|
|
@@ -22792,7 +23653,7 @@ var UNTRUSTED_TOOL_ESCAPE_KEYS = new Set([
|
|
|
22792
23653
|
]);
|
|
22793
23654
|
|
|
22794
23655
|
// packages/cloud/hardening/src/index.ts
|
|
22795
|
-
import { readdirSync as
|
|
23656
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync5 } from "node:fs";
|
|
22796
23657
|
import { resolve as resolve13 } from "node:path";
|
|
22797
23658
|
init_src2();
|
|
22798
23659
|
init_src();
|
|
@@ -22888,7 +23749,7 @@ function listFiles(root) {
|
|
|
22888
23749
|
walk(".");
|
|
22889
23750
|
return out;
|
|
22890
23751
|
function walk(relativeDir) {
|
|
22891
|
-
for (const entry of
|
|
23752
|
+
for (const entry of readdirSync6(resolve13(root, relativeDir), { withFileTypes: true })) {
|
|
22892
23753
|
if ([".git", "node_modules", "coverage"].includes(entry.name))
|
|
22893
23754
|
continue;
|
|
22894
23755
|
const child = relativeDir === "." ? entry.name : `${relativeDir}/${entry.name}`;
|
|
@@ -22905,6 +23766,11 @@ function listFiles(root) {
|
|
|
22905
23766
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
22906
23767
|
import { closeSync as closeSync5, constants, existsSync as existsSync10, fstatSync as fstatSync2, openSync as openSync5, readFileSync as readFileSync12, rmSync as rmSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22907
23768
|
import { join as join6 } from "node:path";
|
|
23769
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
23770
|
+
var WINDOWS_CREATE_TIMEOUT_MS = 60000;
|
|
23771
|
+
var WINDOWS_READ_TIMEOUT_MS = 1e4;
|
|
23772
|
+
var REMOVE_HANDLE_RELEASE_MARGIN_MS = 2000;
|
|
23773
|
+
var REMOVE_RETRY_DELAY_MS = 25;
|
|
22908
23774
|
var WINDOWS_CONTROL_FILE_SCRIPT = String.raw`
|
|
22909
23775
|
$ErrorActionPreference = 'Stop'
|
|
22910
23776
|
# A pwsh -> Node/Bun -> powershell.exe launch inherits incompatible PS7 module paths.
|
|
@@ -22982,7 +23848,7 @@ function windowsControlFile(operation, path, body) {
|
|
|
22982
23848
|
encoding: "utf8",
|
|
22983
23849
|
windowsHide: true,
|
|
22984
23850
|
stdio: ["pipe", "pipe", "pipe"],
|
|
22985
|
-
timeout: operation === "create" ?
|
|
23851
|
+
timeout: operation === "create" ? WINDOWS_CREATE_TIMEOUT_MS : WINDOWS_READ_TIMEOUT_MS,
|
|
22986
23852
|
maxBuffer: 1024 * 1024
|
|
22987
23853
|
});
|
|
22988
23854
|
} catch (error) {
|
|
@@ -23041,6 +23907,21 @@ function readPrivateControlFile(path) {
|
|
|
23041
23907
|
return;
|
|
23042
23908
|
}
|
|
23043
23909
|
}
|
|
23910
|
+
async function removePrivateControlFile(path) {
|
|
23911
|
+
const deadline = Date.now() + WINDOWS_READ_TIMEOUT_MS + REMOVE_HANDLE_RELEASE_MARGIN_MS;
|
|
23912
|
+
for (;; ) {
|
|
23913
|
+
try {
|
|
23914
|
+
rmSync6(path, { force: true });
|
|
23915
|
+
return;
|
|
23916
|
+
} catch (error) {
|
|
23917
|
+
const code = error.code;
|
|
23918
|
+
const sharingFailure = process.platform === "win32" && (code === "EBUSY" || code === "EPERM" || code === "EACCES");
|
|
23919
|
+
if (!sharingFailure || Date.now() >= deadline)
|
|
23920
|
+
throw error;
|
|
23921
|
+
}
|
|
23922
|
+
await sleep(REMOVE_RETRY_DELAY_MS);
|
|
23923
|
+
}
|
|
23924
|
+
}
|
|
23044
23925
|
|
|
23045
23926
|
// packages/local-runtime/process-liveness/src/index.ts
|
|
23046
23927
|
function isProcessAlive(pid) {
|
|
@@ -25871,7 +26752,7 @@ function isIgnorableDirectoryFsyncError2(error) {
|
|
|
25871
26752
|
|
|
25872
26753
|
// packages/local-runtime/git-adapter/src/index.ts
|
|
25873
26754
|
import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
|
|
25874
|
-
import { existsSync as existsSync12, lstatSync as lstatSync6, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync2, readFileSync as readFileSync14, rmSync as rmSync8 } from "node:fs";
|
|
26755
|
+
import { existsSync as existsSync12, lstatSync as lstatSync6, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync2, readdirSync as readdirSync7, readFileSync as readFileSync14, rmSync as rmSync8 } from "node:fs";
|
|
25875
26756
|
import { dirname as dirname8, join as join8, resolve as resolve15 } from "node:path";
|
|
25876
26757
|
init_src();
|
|
25877
26758
|
var GIT_CAT_FILE_MAX_BYTES2 = 512 * 1024 * 1024;
|
|
@@ -27427,7 +28308,7 @@ function unresolvedChangeSetJournalSummary(journals) {
|
|
|
27427
28308
|
}
|
|
27428
28309
|
|
|
27429
28310
|
// packages/local-runtime/runtime-daemon/src/external-documentation.ts
|
|
27430
|
-
import { existsSync as existsSync15, readdirSync as
|
|
28311
|
+
import { existsSync as existsSync15, readdirSync as readdirSync10, readFileSync as readFileSync17, statSync as statSync7 } from "node:fs";
|
|
27431
28312
|
import { resolve as resolve18 } from "node:path";
|
|
27432
28313
|
|
|
27433
28314
|
// packages/core/context-compiler/src/index.ts
|
|
@@ -27435,7 +28316,7 @@ init_src();
|
|
|
27435
28316
|
|
|
27436
28317
|
// packages/core/practice-catalog/src/index.ts
|
|
27437
28318
|
init_src();
|
|
27438
|
-
import { existsSync as existsSync13, lstatSync as lstatSync7, readdirSync as
|
|
28319
|
+
import { existsSync as existsSync13, lstatSync as lstatSync7, readdirSync as readdirSync8, readFileSync as readFileSync15, realpathSync as realpathSync8 } from "node:fs";
|
|
27439
28320
|
import { dirname as dirname9, relative as relative9, resolve as resolve16, sep as sep7 } from "node:path";
|
|
27440
28321
|
import { fileURLToPath } from "node:url";
|
|
27441
28322
|
var PRACTICE_CATALOG_VERSION = "2026.06.0";
|
|
@@ -28046,7 +28927,7 @@ function listDataFiles(dir, allowedRoot, errors) {
|
|
|
28046
28927
|
return;
|
|
28047
28928
|
}
|
|
28048
28929
|
if (stat.isDirectory()) {
|
|
28049
|
-
for (const entry of
|
|
28930
|
+
for (const entry of readdirSync8(current, { withFileTypes: true }))
|
|
28050
28931
|
walk(resolve16(current, entry.name));
|
|
28051
28932
|
return;
|
|
28052
28933
|
}
|
|
@@ -28660,7 +29541,7 @@ function checkResult(input, result) {
|
|
|
28660
29541
|
}
|
|
28661
29542
|
// packages/core/practice-engine/src/enforcement.ts
|
|
28662
29543
|
init_src();
|
|
28663
|
-
import { existsSync as existsSync14, lstatSync as lstatSync8, readdirSync as
|
|
29544
|
+
import { existsSync as existsSync14, lstatSync as lstatSync8, readdirSync as readdirSync9, readFileSync as readFileSync16 } from "node:fs";
|
|
28664
29545
|
import { basename as basename4, join as join9, relative as relative10, resolve as resolve17, sep as sep8 } from "node:path";
|
|
28665
29546
|
var ENFORCEMENT_RANK = { advisory: 0, checkpoint: 1, complete: 2 };
|
|
28666
29547
|
var POLICY_MODES = new Set(["advisory", "active", "fail-open", "fail-closed"]);
|
|
@@ -28702,7 +29583,7 @@ function loadPracticeWaivers(root) {
|
|
|
28702
29583
|
throw new Error("practice-waiver-path-not-directory");
|
|
28703
29584
|
const waivers = [];
|
|
28704
29585
|
const ownerRegistry = loadPracticeWaiverOwnerRegistry(root);
|
|
28705
|
-
for (const entry of
|
|
29586
|
+
for (const entry of readdirSync9(dir, { withFileTypes: true })) {
|
|
28706
29587
|
if (!entry.isFile() || !/\.(?:ya?ml|json)$/.test(entry.name))
|
|
28707
29588
|
continue;
|
|
28708
29589
|
const path = join9(dir, entry.name);
|
|
@@ -29005,7 +29886,7 @@ function parseJsonYamlFile(path) {
|
|
|
29005
29886
|
function collectFiles(root, relativeDir) {
|
|
29006
29887
|
const dir = resolve17(root, relativeDir);
|
|
29007
29888
|
const out = [];
|
|
29008
|
-
for (const entry of
|
|
29889
|
+
for (const entry of readdirSync9(dir, { withFileTypes: true })) {
|
|
29009
29890
|
const child = `${relativeDir}/${entry.name}`;
|
|
29010
29891
|
if (entry.isDirectory())
|
|
29011
29892
|
out.push(...collectFiles(root, child));
|
|
@@ -30571,7 +31452,7 @@ function expandWorkspacePackageJson(root, pattern) {
|
|
|
30571
31452
|
const base = resolve18(root, pattern.slice(0, -2));
|
|
30572
31453
|
if (!existsSync15(base) || !statSync7(base).isDirectory())
|
|
30573
31454
|
return [];
|
|
30574
|
-
return
|
|
31455
|
+
return readdirSync10(base, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => resolve18(base, entry.name, "package.json")).filter((path) => existsSync15(path));
|
|
30575
31456
|
}
|
|
30576
31457
|
function exactVersionFromManifest(manifest, packageName) {
|
|
30577
31458
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
@@ -31349,7 +32230,7 @@ function parsePracticeCheckpointBaselineState(state, repositoryId, taskSessionId
|
|
|
31349
32230
|
// packages/local-runtime/runtime-daemon/src/developer-review-run.ts
|
|
31350
32231
|
init_src();
|
|
31351
32232
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
31352
|
-
import { chmodSync as chmodSync6, existsSync as existsSync18, lstatSync as lstatSync9, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync3, readdirSync as
|
|
32233
|
+
import { chmodSync as chmodSync6, existsSync as existsSync18, lstatSync as lstatSync9, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync3, readdirSync as readdirSync11, readFileSync as readFileSync18, rmSync as rmSync10, writeFileSync as writeFileSync7 } from "node:fs";
|
|
31353
32234
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
31354
32235
|
import { basename as basename5, dirname as dirname10, join as join11, resolve as resolve20, sep as sep9 } from "node:path";
|
|
31355
32236
|
// packages/local-runtime/runtime-daemon/src/rpc-protocol.ts
|
|
@@ -31911,6 +32792,17 @@ function acquireDaemonLock(lockPath, root) {
|
|
|
31911
32792
|
throw new Error(`archctxd already running for ${root}; lock=${lockPath}`);
|
|
31912
32793
|
}
|
|
31913
32794
|
}
|
|
32795
|
+
function liveDaemonLockOwnerPid(root = process.cwd()) {
|
|
32796
|
+
try {
|
|
32797
|
+
const body = readPrivateControlFile(defaultDaemonLockPath(root));
|
|
32798
|
+
if (body === undefined)
|
|
32799
|
+
return;
|
|
32800
|
+
const pid = JSON.parse(body).pid;
|
|
32801
|
+
return typeof pid === "number" && pid > 0 && isProcessAlive(pid) ? pid : undefined;
|
|
32802
|
+
} catch {
|
|
32803
|
+
return;
|
|
32804
|
+
}
|
|
32805
|
+
}
|
|
31914
32806
|
function isValidRuntimeRpcConnection(value) {
|
|
31915
32807
|
return value.schemaVersion === RUNTIME_RPC_VERSION && value.protocol === "http-loopback" && value.version === 1 && typeof value.url === "string" && value.url.startsWith("http://127.0.0.1:") && typeof value.token === "string" && value.token.length > 0 && typeof value.pid === "number" && typeof value.connectionPath === "string" && typeof value.lockPath === "string";
|
|
31916
32808
|
}
|
|
@@ -32120,7 +33012,7 @@ class DeveloperReviewRunService {
|
|
|
32120
33012
|
};
|
|
32121
33013
|
if (!existsSync18(stateDir))
|
|
32122
33014
|
return recovery;
|
|
32123
|
-
for (const entry of
|
|
33015
|
+
for (const entry of readdirSync11(stateDir).sort()) {
|
|
32124
33016
|
if (!entry.endsWith(".json"))
|
|
32125
33017
|
continue;
|
|
32126
33018
|
const manifestPath = join11(stateDir, entry);
|
|
@@ -32148,7 +33040,7 @@ class DeveloperReviewRunService {
|
|
|
32148
33040
|
recovery.rejected.push(`${entry}: ${error instanceof Error ? error.message : String(error)}`);
|
|
32149
33041
|
}
|
|
32150
33042
|
}
|
|
32151
|
-
for (const entry of
|
|
33043
|
+
for (const entry of readdirSync11(stateDir).sort()) {
|
|
32152
33044
|
if (!entry.endsWith(".lock"))
|
|
32153
33045
|
continue;
|
|
32154
33046
|
const lockPath = join11(stateDir, entry);
|
|
@@ -33344,7 +34236,7 @@ init_src();
|
|
|
33344
34236
|
|
|
33345
34237
|
// packages/local-runtime/model-store-yaml/src/index.ts
|
|
33346
34238
|
init_src();
|
|
33347
|
-
import { existsSync as existsSync19, lstatSync as lstatSync10, mkdirSync as mkdirSync9, readdirSync as
|
|
34239
|
+
import { existsSync as existsSync19, lstatSync as lstatSync10, mkdirSync as mkdirSync9, readdirSync as readdirSync12, readFileSync as readFileSync19, rmSync as rmSync11 } from "node:fs";
|
|
33348
34240
|
import { dirname as dirname11, resolve as resolve21 } from "node:path";
|
|
33349
34241
|
var GENERATED_ARCHITECTURE_PATH = ".archcontext/generated/ARCHITECTURE.md";
|
|
33350
34242
|
var GENERATED_ARCHITECTURE_BODY = [
|
|
@@ -33569,7 +34461,7 @@ function collectGeneratedProjectionFiles(root) {
|
|
|
33569
34461
|
const generatedRoot = ".archcontext/generated";
|
|
33570
34462
|
if (!existsSync19(resolve21(root, generatedRoot)))
|
|
33571
34463
|
return [];
|
|
33572
|
-
const walk = (relativeDir) =>
|
|
34464
|
+
const walk = (relativeDir) => readdirSync12(resolve21(root, relativeDir), { withFileTypes: true }).flatMap((entry) => {
|
|
33573
34465
|
const child = `${relativeDir}/${entry.name}`;
|
|
33574
34466
|
if (entry.isDirectory())
|
|
33575
34467
|
return walk(child);
|
|
@@ -33664,7 +34556,7 @@ function collectArchContextFiles(root, entries) {
|
|
|
33664
34556
|
function walk(relativeDir) {
|
|
33665
34557
|
const out = [];
|
|
33666
34558
|
const dir = resolve21(root, relativeDir);
|
|
33667
|
-
for (const entry of
|
|
34559
|
+
for (const entry of readdirSync12(dir, { withFileTypes: true })) {
|
|
33668
34560
|
const child = `${relativeDir}/${entry.name}`;
|
|
33669
34561
|
if (entry.isDirectory())
|
|
33670
34562
|
out.push(...walk(child));
|
|
@@ -33681,7 +34573,7 @@ function isCollectableModelFile(path) {
|
|
|
33681
34573
|
}
|
|
33682
34574
|
function readdirOrFile(path) {
|
|
33683
34575
|
try {
|
|
33684
|
-
|
|
34576
|
+
readdirSync12(path);
|
|
33685
34577
|
return "directory";
|
|
33686
34578
|
} catch {
|
|
33687
34579
|
return "file";
|
|
@@ -41814,6 +42706,9 @@ function runNodeInvestigationTransport(input, options) {
|
|
|
41814
42706
|
child.once("exit", (code) => {
|
|
41815
42707
|
settleResolve(unwrapClaudeCodeEnvelope(code ?? 1, stdout, stderr));
|
|
41816
42708
|
});
|
|
42709
|
+
child.stdin?.on("error", () => {
|
|
42710
|
+
return;
|
|
42711
|
+
});
|
|
41817
42712
|
child.stdin?.end(input.stdin);
|
|
41818
42713
|
});
|
|
41819
42714
|
}
|
|
@@ -42854,6 +43749,7 @@ function buildArchitectureDocsProjection(_host, root, generatedAt, profile = "de
|
|
|
42854
43749
|
profile,
|
|
42855
43750
|
decisions: loaded.decisions,
|
|
42856
43751
|
existingFiles: loaded.existingFiles,
|
|
43752
|
+
docsLanguage: loaded.docsLanguage,
|
|
42857
43753
|
sourceFootprints,
|
|
42858
43754
|
sourceScaleSignals: loadCapabilitySourceScaleSignals(root, loaded.model, codeGraphInputs.sourceFiles),
|
|
42859
43755
|
importGraphs: codeGraphInputs.importGraphs,
|
|
@@ -43954,6 +44850,7 @@ function buildRuntimeProjectionRecoveryFixedPoint(root) {
|
|
|
43954
44850
|
profile: REPO_HARNESS_PROJECTION_PROFILE,
|
|
43955
44851
|
decisions: loaded.decisions,
|
|
43956
44852
|
existingFiles: loaded.existingFiles,
|
|
44853
|
+
docsLanguage: loaded.docsLanguage,
|
|
43957
44854
|
sourceFootprints: loadCapabilitySourceFootprintDigests(root, loaded.model, codeGraphInputs.sourceFiles),
|
|
43958
44855
|
sourceScaleSignals: loadCapabilitySourceScaleSignals(root, loaded.model, codeGraphInputs.sourceFiles),
|
|
43959
44856
|
importGraphs: codeGraphInputs.importGraphs,
|
|
@@ -44068,12 +44965,123 @@ function runtimeProjectionOwnedOutputDigest(projection) {
|
|
|
44068
44965
|
files: projection.files.map((file) => ({ path: file.path, body: file.body })).sort((left, right) => left.path.localeCompare(right.path))
|
|
44069
44966
|
});
|
|
44070
44967
|
}
|
|
44071
|
-
// packages/
|
|
44072
|
-
init_src();
|
|
44073
|
-
|
|
44074
|
-
// packages/core/refactor-assessment/src/index.ts
|
|
44968
|
+
// packages/local-runtime/runtime-daemon/src/docs-anchors.ts
|
|
44075
44969
|
init_src();
|
|
44076
|
-
|
|
44970
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
44971
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
44972
|
+
import { resolve as resolve25 } from "node:path";
|
|
44973
|
+
function runDocsAnchorsCommand(cwd, options = {}) {
|
|
44974
|
+
let opened;
|
|
44975
|
+
try {
|
|
44976
|
+
const root = findRepositoryRoot(cwd);
|
|
44977
|
+
const profile = options.profile ?? committedArchitectureDocsProfile(root);
|
|
44978
|
+
const model = loadNativeModelFromArchContext(root);
|
|
44979
|
+
const layout = resolveArchitectureDocumentationLayout({ nodes: model.nodes, relations: model.relations, profile });
|
|
44980
|
+
const documents = new Map(loadArchitectureFilesForLayout(root, layout).map((file) => [file.path, file.body]));
|
|
44981
|
+
const files = listProjectionSourceFiles(root);
|
|
44982
|
+
const footprints = new Map(loadCapabilitySourceFootprints(root, model, files).map((footprint) => [footprint.nodeId, footprint.files]));
|
|
44983
|
+
const contents = fileContentReader(root);
|
|
44984
|
+
const tree = gitVisibleTree(root, files, contents);
|
|
44985
|
+
opened = openCodeGraphSymbolReader(root);
|
|
44986
|
+
const codeGraph = opened.availability === "ready" ? { availability: "ready", symbols: proseAnchorSymbols(opened.reader, files, contents) } : { availability: "unavailable", reasonCode: opened.reasonCode };
|
|
44987
|
+
const report = checkProseAnchors({
|
|
44988
|
+
profile,
|
|
44989
|
+
codeGraph,
|
|
44990
|
+
tree,
|
|
44991
|
+
nodes: model.nodes.map((node) => {
|
|
44992
|
+
const documentPath = layout.entityPathByNodeId.get(node.id);
|
|
44993
|
+
const body = documentPath === undefined ? undefined : documents.get(documentPath);
|
|
44994
|
+
const responsibilities = Array.isArray(node.responsibilities) ? node.responsibilities.filter((entry) => typeof entry === "string") : undefined;
|
|
44995
|
+
return {
|
|
44996
|
+
nodeId: node.id,
|
|
44997
|
+
...typeof node.summary === "string" ? { summary: node.summary } : {},
|
|
44998
|
+
...responsibilities ? { responsibilities } : {},
|
|
44999
|
+
...footprints.has(node.id) ? { footprint: footprints.get(node.id) } : {},
|
|
45000
|
+
...documentPath !== undefined && body !== undefined ? { document: { path: documentPath, body } } : {}
|
|
45001
|
+
};
|
|
45002
|
+
})
|
|
45003
|
+
});
|
|
45004
|
+
const issues = docsAnchorsReportIssues(report);
|
|
45005
|
+
if (issues.length > 0)
|
|
45006
|
+
throw new Error(`docs-anchors-report-invalid: ${issues.join("; ")}`);
|
|
45007
|
+
return okEnvelope("docs.anchors", report);
|
|
45008
|
+
} catch (error) {
|
|
45009
|
+
return projectionFailureEnvelope("docs.anchors", error);
|
|
45010
|
+
} finally {
|
|
45011
|
+
if (opened?.availability === "ready")
|
|
45012
|
+
opened.reader.close();
|
|
45013
|
+
}
|
|
45014
|
+
}
|
|
45015
|
+
function fileContentReader(root) {
|
|
45016
|
+
const cache = new Map;
|
|
45017
|
+
return (path) => {
|
|
45018
|
+
let content = cache.get(path);
|
|
45019
|
+
if (content === undefined) {
|
|
45020
|
+
content = readFileSync24(resolve25(root, path), "utf8").replace(/\r\n/g, `
|
|
45021
|
+
`);
|
|
45022
|
+
cache.set(path, content);
|
|
45023
|
+
}
|
|
45024
|
+
return content;
|
|
45025
|
+
};
|
|
45026
|
+
}
|
|
45027
|
+
function gitVisibleTree(root, files, contents) {
|
|
45028
|
+
const fileSet = new Set(files);
|
|
45029
|
+
const directories = new Set;
|
|
45030
|
+
const topLevel = new Set;
|
|
45031
|
+
const ignored = new Map;
|
|
45032
|
+
for (const file of files) {
|
|
45033
|
+
const segments = file.split("/");
|
|
45034
|
+
topLevel.add(segments[0]);
|
|
45035
|
+
for (let index = 1;index < segments.length; index += 1)
|
|
45036
|
+
directories.add(segments.slice(0, index).join("/"));
|
|
45037
|
+
}
|
|
45038
|
+
return {
|
|
45039
|
+
kind: (path) => fileSet.has(path) ? "file" : directories.has(path) ? "directory" : undefined,
|
|
45040
|
+
hasTopLevelEntry: (segment) => topLevel.has(segment),
|
|
45041
|
+
filesEndingWith: (suffix) => files.filter((file) => file === suffix || file.endsWith(`/${suffix}`)),
|
|
45042
|
+
lineCount: (path) => {
|
|
45043
|
+
const content = contents(path);
|
|
45044
|
+
if (content === "")
|
|
45045
|
+
return 0;
|
|
45046
|
+
return content.split(`
|
|
45047
|
+
`).length - (content.endsWith(`
|
|
45048
|
+
`) ? 1 : 0);
|
|
45049
|
+
},
|
|
45050
|
+
isIgnored: (path) => {
|
|
45051
|
+
let found = ignored.get(path);
|
|
45052
|
+
if (found === undefined) {
|
|
45053
|
+
found = spawnSync3("git", ["check-ignore", "--", path, `${path}/`], { cwd: root, stdio: "ignore" }).status === 0;
|
|
45054
|
+
ignored.set(path, found);
|
|
45055
|
+
}
|
|
45056
|
+
return found;
|
|
45057
|
+
}
|
|
45058
|
+
};
|
|
45059
|
+
}
|
|
45060
|
+
var SOURCE_SEARCH_EXCLUDED_PREFIXES = [".archcontext/"];
|
|
45061
|
+
function proseAnchorSymbols(reader, files, contents) {
|
|
45062
|
+
const sourceFiles = files.filter((file) => !SOURCE_SEARCH_EXCLUDED_PREFIXES.some((prefix) => file.startsWith(prefix)) && reader.supportsPath(file));
|
|
45063
|
+
const appears = new Map;
|
|
45064
|
+
const parsed = new Map;
|
|
45065
|
+
const declarationsIn = (path) => {
|
|
45066
|
+
if (!parsed.has(path))
|
|
45067
|
+
parsed.set(path, reader.supportsPath(path) ? reader.declarationsInSource(path, contents(path)) : undefined);
|
|
45068
|
+
return parsed.get(path);
|
|
45069
|
+
};
|
|
45070
|
+
return {
|
|
45071
|
+
supportsPath: (path) => declarationsIn(path) !== undefined,
|
|
45072
|
+
declarationsInFile: (path, name) => (declarationsIn(path) ?? []).filter((declaration) => declaration.name === name),
|
|
45073
|
+
declarationsNamed: (name) => reader.declarationsNamed(name),
|
|
45074
|
+
appearsInSource: (name) => {
|
|
45075
|
+
let found = appears.get(name);
|
|
45076
|
+
if (found === undefined) {
|
|
45077
|
+
const word = new RegExp(`(?<![A-Za-z0-9_$])${name.replace(/[$]/g, "\\$")}(?![A-Za-z0-9_$])`);
|
|
45078
|
+
found = sourceFiles.some((file) => word.test(contents(file)));
|
|
45079
|
+
appears.set(name, found);
|
|
45080
|
+
}
|
|
45081
|
+
return found;
|
|
45082
|
+
}
|
|
45083
|
+
};
|
|
45084
|
+
}
|
|
44077
45085
|
// packages/core/module-statistics/src/index.ts
|
|
44078
45086
|
init_src();
|
|
44079
45087
|
|
|
@@ -44221,10 +45229,10 @@ function tarjanComponents(nodeIds, successors) {
|
|
|
44221
45229
|
}
|
|
44222
45230
|
|
|
44223
45231
|
// packages/core/module-statistics/src/ownership.ts
|
|
45232
|
+
init_src();
|
|
44224
45233
|
function resolveOwnership(nodes, paths) {
|
|
44225
45234
|
const declaring = nodes.filter((node) => (nativeNodeSource(node)?.include ?? []).length > 0);
|
|
44226
45235
|
const parents = new Map(nodes.map((node) => [node.id, node.parent]));
|
|
44227
|
-
const depths = new Map(nodes.map((node) => [node.id, depthOf(node.id, parents)]));
|
|
44228
45236
|
const roots = declaredSourceRoots(declaring);
|
|
44229
45237
|
const excludes = declaring.flatMap((node) => nativeNodeSource(node)?.exclude ?? []);
|
|
44230
45238
|
const byPath = new Map;
|
|
@@ -44243,7 +45251,7 @@ function resolveOwnership(nodes, paths) {
|
|
|
44243
45251
|
byPath.set(path, { owners: [], ambiguous: false });
|
|
44244
45252
|
continue;
|
|
44245
45253
|
}
|
|
44246
|
-
const resolution = resolveCandidates(candidates, parents
|
|
45254
|
+
const resolution = resolveCandidates(candidates, parents);
|
|
44247
45255
|
ownedFileCount += 1;
|
|
44248
45256
|
if (resolution.ambiguous)
|
|
44249
45257
|
multiplyOwnedFileCount += 1;
|
|
@@ -44290,38 +45298,90 @@ function claims(node, path) {
|
|
|
44290
45298
|
return false;
|
|
44291
45299
|
return include.some((pattern) => matchesGlob(path, pattern));
|
|
44292
45300
|
}
|
|
44293
|
-
function resolveCandidates(candidates, parents
|
|
44294
|
-
|
|
44295
|
-
|
|
44296
|
-
|
|
44297
|
-
|
|
44298
|
-
|
|
44299
|
-
|
|
44300
|
-
|
|
44301
|
-
|
|
44302
|
-
|
|
44303
|
-
const
|
|
44304
|
-
|
|
44305
|
-
|
|
44306
|
-
|
|
44307
|
-
|
|
44308
|
-
|
|
44309
|
-
|
|
44310
|
-
|
|
44311
|
-
|
|
44312
|
-
|
|
44313
|
-
|
|
44314
|
-
|
|
44315
|
-
|
|
45301
|
+
function resolveCandidates(candidates, parents) {
|
|
45302
|
+
const ids = candidates.map((node) => node.id);
|
|
45303
|
+
const owner = deepestOnOneParentChain(ids, parents);
|
|
45304
|
+
if (owner !== undefined)
|
|
45305
|
+
return { owners: [owner], ambiguous: false };
|
|
45306
|
+
return { owners: ids.sort(), ambiguous: true };
|
|
45307
|
+
}
|
|
45308
|
+
function ownershipDelta(before, after, paths, pathLimit = CHANGESET_OWNERSHIP_DELTA_PATH_LIMIT) {
|
|
45309
|
+
const changedNodeIds = ownershipRelevantChanges(before, after);
|
|
45310
|
+
const beforeIndex = resolveOwnership(before, paths);
|
|
45311
|
+
const afterIndex = changedNodeIds.length === 0 ? beforeIndex : resolveOwnership(after, paths);
|
|
45312
|
+
const categories = {
|
|
45313
|
+
becameOwned: [],
|
|
45314
|
+
becameContested: [],
|
|
45315
|
+
ownerChanged: [],
|
|
45316
|
+
becameUnowned: []
|
|
45317
|
+
};
|
|
45318
|
+
for (const path of [...paths].sort()) {
|
|
45319
|
+
const was = beforeIndex.byPath.get(path) ?? { owners: [], ambiguous: false };
|
|
45320
|
+
const now = afterIndex.byPath.get(path) ?? { owners: [], ambiguous: false };
|
|
45321
|
+
if (sameOwners(was.owners, now.owners))
|
|
45322
|
+
continue;
|
|
45323
|
+
const file = { path, before: [...was.owners].sort(), after: [...now.owners].sort() };
|
|
45324
|
+
if (now.ambiguous && !was.ambiguous)
|
|
45325
|
+
categories.becameContested.push(file);
|
|
45326
|
+
else if (was.owners.length === 0)
|
|
45327
|
+
categories.becameOwned.push(file);
|
|
45328
|
+
else if (now.owners.length === 0)
|
|
45329
|
+
categories.becameUnowned.push(file);
|
|
45330
|
+
else
|
|
45331
|
+
categories.ownerChanged.push(file);
|
|
45332
|
+
}
|
|
45333
|
+
const summary = (index) => ({
|
|
45334
|
+
ownedFileCount: index.ownedFileCount,
|
|
45335
|
+
multiplyOwnedFileCount: index.multiplyOwnedFileCount,
|
|
45336
|
+
unownedFileCount: index.unownedFileCount
|
|
45337
|
+
});
|
|
45338
|
+
const bounded = (files) => {
|
|
45339
|
+
const listed = files.slice(0, pathLimit);
|
|
45340
|
+
return { count: files.length, files: listed, omittedCount: files.length - listed.length };
|
|
45341
|
+
};
|
|
45342
|
+
return {
|
|
45343
|
+
schemaVersion: CHANGESET_OWNERSHIP_DELTA_SCHEMA_VERSION,
|
|
45344
|
+
status: "computed",
|
|
45345
|
+
ownershipRule: "parent-chain",
|
|
45346
|
+
fileUniverse: "head-tracked-files",
|
|
45347
|
+
trackedFileCount: paths.length,
|
|
45348
|
+
changedNodeIds,
|
|
45349
|
+
pathLimit,
|
|
45350
|
+
before: summary(beforeIndex),
|
|
45351
|
+
after: summary(afterIndex),
|
|
45352
|
+
becameOwned: bounded(categories.becameOwned),
|
|
45353
|
+
becameContested: bounded(categories.becameContested),
|
|
45354
|
+
ownerChanged: bounded(categories.ownerChanged),
|
|
45355
|
+
becameUnowned: bounded(categories.becameUnowned)
|
|
45356
|
+
};
|
|
45357
|
+
}
|
|
45358
|
+
function ownershipRelevantChanges(before, after) {
|
|
45359
|
+
const key = (node) => {
|
|
45360
|
+
if (node === undefined)
|
|
45361
|
+
return "absent";
|
|
45362
|
+
const source = nativeNodeSource(node);
|
|
45363
|
+
return digestJson({
|
|
45364
|
+
include: source?.include ?? [],
|
|
45365
|
+
exclude: source?.exclude ?? [],
|
|
45366
|
+
parent: node.parent ?? null
|
|
45367
|
+
});
|
|
45368
|
+
};
|
|
45369
|
+
const beforeById = new Map(before.map((node) => [node.id, node]));
|
|
45370
|
+
const afterById = new Map(after.map((node) => [node.id, node]));
|
|
45371
|
+
const ids = new Set([...beforeById.keys(), ...afterById.keys()]);
|
|
45372
|
+
return [...ids].filter((id) => key(beforeById.get(id)) !== key(afterById.get(id))).sort();
|
|
44316
45373
|
}
|
|
44317
|
-
function
|
|
44318
|
-
|
|
45374
|
+
function sameOwners(left, right) {
|
|
45375
|
+
if (left.length !== right.length)
|
|
45376
|
+
return false;
|
|
45377
|
+
const sortedRight = [...right].sort();
|
|
45378
|
+
return [...left].sort().every((owner, index) => owner === sortedRight[index]);
|
|
44319
45379
|
}
|
|
44320
45380
|
|
|
44321
45381
|
// packages/core/module-statistics/src/index.ts
|
|
44322
45382
|
function evaluateDependencyConstraints(input) {
|
|
44323
45383
|
const constraints = [...input.constraints].sort((left, right) => compare(left.id, right.id));
|
|
44324
|
-
const certification = certifyCodeFacts(input.codeFacts, input.worktreeDigest, input.truncated);
|
|
45384
|
+
const certification = certifyCodeFacts(input.codeFacts, input.worktreeDigest, input.truncated, input.importNodeLimitExceeded ?? false);
|
|
44325
45385
|
if (constraints.length === 0) {
|
|
44326
45386
|
return {
|
|
44327
45387
|
schemaVersion: "archcontext.dependency-constraint-evaluation/v1",
|
|
@@ -44389,7 +45449,87 @@ function evaluateDependencyConstraints(input) {
|
|
|
44389
45449
|
violations: sortedViolations
|
|
44390
45450
|
};
|
|
44391
45451
|
}
|
|
44392
|
-
function
|
|
45452
|
+
function measureCapabilityDependencies(input) {
|
|
45453
|
+
const certification = certifyCodeFacts(input.codeFacts, input.worktreeDigest, input.truncated, input.importNodeLimitExceeded);
|
|
45454
|
+
const paths = new Set(input.files);
|
|
45455
|
+
const edges = (certification.measurable ? resolveEdges(input.importEdges, input.workspacePackages, paths) : []).filter((edge) => paths.has(edge.from) && (edge.to === null || paths.has(edge.to)));
|
|
45456
|
+
const ownership = resolveOwnership(input.nodes, [...paths].sort());
|
|
45457
|
+
const parents = new Map(input.nodes.map((node) => [node.id, node.parent]));
|
|
45458
|
+
const kinds = new Map(input.nodes.map((node) => [node.id, node.kind]));
|
|
45459
|
+
const capabilityOf = (owner) => {
|
|
45460
|
+
for (const ancestor of ancestorChain(owner, parents))
|
|
45461
|
+
if (kinds.get(ancestor) === "capability")
|
|
45462
|
+
return ancestor;
|
|
45463
|
+
return null;
|
|
45464
|
+
};
|
|
45465
|
+
const ownersOf = (path) => ownership.byPath.get(path)?.owners ?? [];
|
|
45466
|
+
const within = (path, nodeId) => nodeId === undefined || ownersOf(path).some((owner) => ancestorChain(owner, parents).has(nodeId));
|
|
45467
|
+
const attribute = (path) => {
|
|
45468
|
+
const owners = ownersOf(path);
|
|
45469
|
+
if (owners.length === 0)
|
|
45470
|
+
return { gap: "unownedEndpoint" };
|
|
45471
|
+
const capabilities = new Set(owners.map(capabilityOf));
|
|
45472
|
+
if (capabilities.size > 1)
|
|
45473
|
+
return { gap: "contestedEndpoint" };
|
|
45474
|
+
const [capability] = capabilities;
|
|
45475
|
+
return capability === null ? { gap: "noCapabilityEndpoint" } : { capability };
|
|
45476
|
+
};
|
|
45477
|
+
const unattributed = { unownedEndpoint: 0, contestedEndpoint: 0, noCapabilityEndpoint: 0 };
|
|
45478
|
+
const cells = new Map;
|
|
45479
|
+
const attributedEdges = [];
|
|
45480
|
+
const seen = new Set;
|
|
45481
|
+
let unresolvedCount = 0;
|
|
45482
|
+
for (const edge of edges) {
|
|
45483
|
+
if (!within(edge.from, input.from))
|
|
45484
|
+
continue;
|
|
45485
|
+
if (edge.to === null) {
|
|
45486
|
+
if (input.into === undefined && isRepositoryLocalSpecifier(edge.specifier, input.workspacePackages))
|
|
45487
|
+
unresolvedCount += 1;
|
|
45488
|
+
continue;
|
|
45489
|
+
}
|
|
45490
|
+
if (edge.to === edge.from || !within(edge.to, input.into))
|
|
45491
|
+
continue;
|
|
45492
|
+
const key = JSON.stringify([edge.from, edge.to]);
|
|
45493
|
+
if (seen.has(key))
|
|
45494
|
+
continue;
|
|
45495
|
+
seen.add(key);
|
|
45496
|
+
const from = attribute(edge.from);
|
|
45497
|
+
const to = attribute(edge.to);
|
|
45498
|
+
if ("gap" in from || "gap" in to) {
|
|
45499
|
+
unattributed["gap" in from ? from.gap : to.gap] += 1;
|
|
45500
|
+
continue;
|
|
45501
|
+
}
|
|
45502
|
+
const cellKey = JSON.stringify([from.capability, to.capability]);
|
|
45503
|
+
const cell = cells.get(cellKey) ?? { from: from.capability, to: to.capability, imports: 0, calls: null };
|
|
45504
|
+
cell.imports += 1;
|
|
45505
|
+
cells.set(cellKey, cell);
|
|
45506
|
+
attributedEdges.push({ fromPath: edge.from, toPath: edge.to, fromCapability: from.capability, toCapability: to.capability });
|
|
45507
|
+
}
|
|
45508
|
+
const edgeLimit = input.edgeLimit ?? CAPABILITY_DEPENDENCIES_EDGE_LIMIT;
|
|
45509
|
+
attributedEdges.sort((left, right) => compare(left.fromPath, right.fromPath) || compare(left.toPath, right.toPath));
|
|
45510
|
+
const listed = attributedEdges.slice(0, edgeLimit);
|
|
45511
|
+
return {
|
|
45512
|
+
schemaVersion: CAPABILITY_DEPENDENCIES_SCHEMA_VERSION,
|
|
45513
|
+
ownershipRule: "parent-chain",
|
|
45514
|
+
filter: { from: input.from ?? null, into: input.into ?? null },
|
|
45515
|
+
capabilities: input.nodes.filter((node) => node.kind === "capability").map((node) => node.id).sort(compare),
|
|
45516
|
+
matrix: [...cells.values()].sort((left, right) => compare(left.from, right.from) || compare(left.to, right.to)),
|
|
45517
|
+
imports: {
|
|
45518
|
+
coverage: certification.coverage,
|
|
45519
|
+
reasonCodes: [...certification.reasonCodes].sort(),
|
|
45520
|
+
truncated: input.truncated,
|
|
45521
|
+
importNodeCount: input.importNodeCount,
|
|
45522
|
+
importNodeLimit: input.importNodeLimit,
|
|
45523
|
+
importNodeLimitExceeded: input.importNodeLimitExceeded,
|
|
45524
|
+
edgeCount: seen.size,
|
|
45525
|
+
unresolvedCount,
|
|
45526
|
+
unattributed
|
|
45527
|
+
},
|
|
45528
|
+
calls: { coverage: "unknown", reasonCodes: [CAPABILITY_DEPENDENCIES_CALLS_UNAVAILABLE_REASON] },
|
|
45529
|
+
edges: { limit: edgeLimit, count: attributedEdges.length, omittedCount: attributedEdges.length - listed.length, items: listed }
|
|
45530
|
+
};
|
|
45531
|
+
}
|
|
45532
|
+
function certifyCodeFacts(codeFacts, worktreeDigest, truncated, importNodeLimitExceeded) {
|
|
44393
45533
|
const attested = codeFacts.indexedWorktreeDigest !== null && codeFacts.indexedWorktreeDigest === worktreeDigest;
|
|
44394
45534
|
const measurable = codeFacts.availability === "ready" && attested;
|
|
44395
45535
|
const reasonCodes = [];
|
|
@@ -44397,9 +45537,12 @@ function certifyCodeFacts(codeFacts, worktreeDigest, truncated) {
|
|
|
44397
45537
|
reasonCodes.push("code-facts-unavailable");
|
|
44398
45538
|
else if (!attested)
|
|
44399
45539
|
reasonCodes.push("code-facts-stale");
|
|
45540
|
+
if (measurable && importNodeLimitExceeded)
|
|
45541
|
+
reasonCodes.push("code-facts-import-limit");
|
|
44400
45542
|
if (measurable && truncated)
|
|
44401
45543
|
reasonCodes.push("code-facts-truncated");
|
|
44402
|
-
|
|
45544
|
+
const partial = truncated || importNodeLimitExceeded;
|
|
45545
|
+
return { measurable, coverage: !measurable ? "unknown" : partial ? "partial" : "complete", reasonCodes };
|
|
44403
45546
|
}
|
|
44404
45547
|
function isRepositoryLocalSpecifier(specifier, workspacePackages) {
|
|
44405
45548
|
if (specifier === "." || specifier === ".." || specifier.startsWith("./") || specifier.startsWith("../"))
|
|
@@ -44414,7 +45557,8 @@ function measureModuleStatistics(input) {
|
|
|
44414
45557
|
const trackedFiles = [...input.trackedFiles].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
44415
45558
|
const linesByPath = new Map(trackedFiles.map((file) => [file.path, file.lineCount]));
|
|
44416
45559
|
const ownership = resolveOwnership(nodes, trackedFiles.map((file) => file.path));
|
|
44417
|
-
const
|
|
45560
|
+
const importNodeLimitExceeded = input.importNodeLimitExceeded ?? false;
|
|
45561
|
+
const { measurable, coverage } = certifyCodeFacts(input.codeFacts, input.worktree.worktreeDigest, input.truncated, importNodeLimitExceeded);
|
|
44418
45562
|
const edges = measurable ? resolveEdges(input.importEdges, input.workspacePackages, new Set(linesByPath.keys())) : [];
|
|
44419
45563
|
const constraints = input.constraints ?? [];
|
|
44420
45564
|
const directionEvaluation = constraints.length === 0 ? undefined : evaluateDependencyConstraints({
|
|
@@ -44425,6 +45569,7 @@ function measureModuleStatistics(input) {
|
|
|
44425
45569
|
importEdges: input.importEdges,
|
|
44426
45570
|
workspacePackages: input.workspacePackages,
|
|
44427
45571
|
truncated: input.truncated,
|
|
45572
|
+
importNodeLimitExceeded,
|
|
44428
45573
|
codeFacts: input.codeFacts
|
|
44429
45574
|
});
|
|
44430
45575
|
const directionViolations = directionEvaluation === undefined ? undefined : countViolationsByNode(directionEvaluation);
|
|
@@ -44447,7 +45592,8 @@ function measureModuleStatistics(input) {
|
|
|
44447
45592
|
coverage,
|
|
44448
45593
|
truncated: coverage === "unknown" ? true : input.truncated,
|
|
44449
45594
|
edgeLimit: input.edgeLimit,
|
|
44450
|
-
|
|
45595
|
+
...input.importNodeCount === undefined ? {} : { importNodeCount: input.importNodeCount },
|
|
45596
|
+
reasonCodes: reasonCodes({ coverage, truncated: input.truncated, importNodeLimitExceeded, measurable, modules, ownership })
|
|
44451
45597
|
};
|
|
44452
45598
|
const draft = {
|
|
44453
45599
|
schemaVersion: MODULE_STATISTICS_SCHEMA_VERSION,
|
|
@@ -44557,6 +45703,8 @@ function reasonCodes(context) {
|
|
|
44557
45703
|
codes.add("code-facts-missing");
|
|
44558
45704
|
if (context.truncated || context.coverage === "unknown")
|
|
44559
45705
|
codes.add("code-facts-truncated");
|
|
45706
|
+
if (context.measurable && context.importNodeLimitExceeded)
|
|
45707
|
+
codes.add("code-facts-import-limit");
|
|
44560
45708
|
if (context.modules.some((module) => !module.footprintDeclared))
|
|
44561
45709
|
codes.add("node-footprint-undeclared");
|
|
44562
45710
|
if (context.ownership.multiplyOwnedFileCount > 0)
|
|
@@ -44566,6 +45714,16 @@ function reasonCodes(context) {
|
|
|
44566
45714
|
return [...codes].sort();
|
|
44567
45715
|
}
|
|
44568
45716
|
|
|
45717
|
+
// packages/local-runtime/runtime-daemon/src/refactor-scan.ts
|
|
45718
|
+
init_src();
|
|
45719
|
+
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
45720
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
45721
|
+
import { existsSync as existsSync24, readdirSync as readdirSync13, readFileSync as readFileSync25 } from "node:fs";
|
|
45722
|
+
import { join as join16 } from "node:path";
|
|
45723
|
+
|
|
45724
|
+
// packages/core/refactor-assessment/src/index.ts
|
|
45725
|
+
init_src();
|
|
45726
|
+
|
|
44569
45727
|
// packages/core/refactor-assessment/src/evidence.ts
|
|
44570
45728
|
init_src();
|
|
44571
45729
|
class ObservationEvidenceBuilder {
|
|
@@ -45320,7 +46478,8 @@ function buildObservations(snapshot, structure, model) {
|
|
|
45320
46478
|
subjectSelectorId: repositorySubject,
|
|
45321
46479
|
metrics: {
|
|
45322
46480
|
unresolvedImportCount: snapshot.repositorySummary.unresolvedImportCount,
|
|
45323
|
-
edgeLimit: snapshot.codeFacts.edgeLimit
|
|
46481
|
+
edgeLimit: snapshot.codeFacts.edgeLimit,
|
|
46482
|
+
...snapshot.codeFacts.importNodeCount === undefined ? {} : { importNodeCount: snapshot.codeFacts.importNodeCount }
|
|
45324
46483
|
},
|
|
45325
46484
|
evidence: evidence.evidenceGap()
|
|
45326
46485
|
});
|
|
@@ -45359,8 +46518,12 @@ function classifyProposal(context) {
|
|
|
45359
46518
|
codes.add("node-footprint-undeclared");
|
|
45360
46519
|
if (coverage === "unknown")
|
|
45361
46520
|
codes.add("code-facts-missing");
|
|
45362
|
-
if (coverage === "partial")
|
|
45363
|
-
|
|
46521
|
+
if (coverage === "partial") {
|
|
46522
|
+
const limited = context.snapshot.codeFacts.reasonCodes.includes("code-facts-import-limit");
|
|
46523
|
+
codes.add(limited ? "code-facts-import-limit" : "code-facts-truncated");
|
|
46524
|
+
if (limited && context.snapshot.codeFacts.truncated)
|
|
46525
|
+
codes.add("code-facts-truncated");
|
|
46526
|
+
}
|
|
45364
46527
|
if (scopeOwnership.contested.length > 0)
|
|
45365
46528
|
codes.add("ownership-ambiguous");
|
|
45366
46529
|
if (derivation.unresolvedTargets.length > 0)
|
|
@@ -45502,7 +46665,11 @@ function isPresent2(value) {
|
|
|
45502
46665
|
return value !== undefined;
|
|
45503
46666
|
}
|
|
45504
46667
|
|
|
46668
|
+
// packages/local-runtime/runtime-daemon/src/refactor-recording.ts
|
|
46669
|
+
init_src();
|
|
46670
|
+
|
|
45505
46671
|
// packages/core/recommendation-engine/src/index.ts
|
|
46672
|
+
init_src();
|
|
45506
46673
|
var RECOMMENDATION_SCHEDULER_ENGINE_VERSION = "archcontext.recommendation-scheduler/v1";
|
|
45507
46674
|
var ACTIVE_RECOMMENDATION_STATUSES = new Set(["open", "acknowledged", "deferred"]);
|
|
45508
46675
|
var REFACTOR_ACTIVE_RECOMMENDATION_STATUSES = new Set([
|
|
@@ -46176,11 +47343,16 @@ function observationExplanation(observation, affectedNodeIds) {
|
|
|
46176
47343
|
const examples = evidence.unresolvedImports.slice(0, EXPLANATION_EXAMPLE_LIMIT);
|
|
46177
47344
|
return [
|
|
46178
47345
|
`Code facts coverage is ${evidence.coverage} (${evidence.reasonCodes.join(", ")}), so structural metrics${affectedNodeIds.length > 0 ? ` for ${affectedNodeIds.join(", ")}` : ""} are incomplete.`,
|
|
47346
|
+
...evidence.reasonCodes.includes("code-facts-import-limit") ? [importLimitText(observation.metrics)] : [],
|
|
46179
47347
|
...evidence.totalCount === 0 ? [] : [`${evidence.totalCount} import specifier(s) did not resolve to a tracked file.`, ...examples.map((entry) => `${locationText(entry.fromPath, entry.fromLine)} imports ${entry.specifier}.`), ...more(examples.length)]
|
|
46180
47348
|
];
|
|
46181
47349
|
}
|
|
46182
47350
|
}
|
|
46183
47351
|
}
|
|
47352
|
+
function importLimitText(metrics) {
|
|
47353
|
+
const count = typeof metrics.importNodeCount === "number" ? `${metrics.importNodeCount} import nodes` : "more import nodes";
|
|
47354
|
+
return `The index holds ${count}, more than the scan import node limit of ${metrics.edgeLimit ?? "unknown"}. Run archctx refactor scan --import-node-limit <n> with a larger limit; a new CodeGraph index does not change this.`;
|
|
47355
|
+
}
|
|
46184
47356
|
function evidenceLength(evidence) {
|
|
46185
47357
|
switch (evidence.kind) {
|
|
46186
47358
|
case "cycle":
|
|
@@ -46365,7 +47537,6 @@ function latestRecommendationV3ByFingerprint(previous) {
|
|
|
46365
47537
|
}
|
|
46366
47538
|
|
|
46367
47539
|
// packages/local-runtime/runtime-daemon/src/refactor-recording.ts
|
|
46368
|
-
init_src();
|
|
46369
47540
|
var REFACTOR_ASSESSMENT_REGISTRY_CAPACITY = 8;
|
|
46370
47541
|
var REFACTOR_CLASSIFIER_RULESET_SCHEMA_VERSION = "archcontext.refactor-classifier-ruleset/v1";
|
|
46371
47542
|
var REFACTOR_SCAN_EVENT_TYPE = "architecture.refactor.scan";
|
|
@@ -46606,17 +47777,15 @@ function digestSuffix4(digest) {
|
|
|
46606
47777
|
}
|
|
46607
47778
|
|
|
46608
47779
|
// packages/local-runtime/runtime-daemon/src/refactor-scan.ts
|
|
46609
|
-
init_src();
|
|
46610
|
-
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
46611
|
-
import { createHash as createHash10 } from "node:crypto";
|
|
46612
|
-
import { existsSync as existsSync24, readdirSync as readdirSync11, readFileSync as readFileSync24 } from "node:fs";
|
|
46613
|
-
import { join as join16 } from "node:path";
|
|
46614
47780
|
var CODEGRAPH_BINARY = "codegraph";
|
|
46615
47781
|
var CODEGRAPH_VERSION_TIMEOUT_MS = 15000;
|
|
46616
47782
|
var REPOSITORY_REFACTOR_REQUEST = {
|
|
46617
47783
|
schemaVersion: REFACTOR_REQUEST_SCHEMA_VERSION,
|
|
46618
47784
|
scope: { kind: "repository" }
|
|
46619
47785
|
};
|
|
47786
|
+
function repositoryRefactorRequest(importNodeLimit) {
|
|
47787
|
+
return importNodeLimit === undefined ? REPOSITORY_REFACTOR_REQUEST : { ...REPOSITORY_REFACTOR_REQUEST, importNodeLimit };
|
|
47788
|
+
}
|
|
46620
47789
|
|
|
46621
47790
|
class RefactorScanError extends Error {
|
|
46622
47791
|
code;
|
|
@@ -46640,7 +47809,7 @@ function runRefactorScan(input) {
|
|
|
46640
47809
|
const constraints = readDependencyConstraints(listModelFiles(input.root)).constraints;
|
|
46641
47810
|
const createdAt = readHeadCommitterDate(input.root);
|
|
46642
47811
|
const requestId = refactorRequestId(input.request);
|
|
46643
|
-
const edgeLimit = input.
|
|
47812
|
+
const edgeLimit = input.request.importNodeLimit ?? CODEGRAPH_IMPORT_NODE_READ_LIMIT;
|
|
46644
47813
|
const trackedFiles = readTrackedSourceFiles(input.root);
|
|
46645
47814
|
const codeFacts = readCodeFacts(input.root, input.worktree.worktreeDigest, edgeLimit);
|
|
46646
47815
|
const { snapshot, structure } = measureModuleStatistics({
|
|
@@ -46652,6 +47821,8 @@ function runRefactorScan(input) {
|
|
|
46652
47821
|
workspacePackages: declaredWorkspacePackages(input.root),
|
|
46653
47822
|
truncated: codeFacts.truncated,
|
|
46654
47823
|
edgeLimit,
|
|
47824
|
+
importNodeCount: codeFacts.importNodeCount,
|
|
47825
|
+
importNodeLimitExceeded: codeFacts.importNodeLimitExceeded,
|
|
46655
47826
|
codeFacts: {
|
|
46656
47827
|
version: codeFacts.version,
|
|
46657
47828
|
binaryDigest: codeFacts.binaryDigest,
|
|
@@ -46707,7 +47878,8 @@ function runRefactorScan(input) {
|
|
|
46707
47878
|
maxEvidenceSampleLimit: REFACTOR_OBSERVATION_EVIDENCE_LIMIT,
|
|
46708
47879
|
...plan.candidateBudget
|
|
46709
47880
|
},
|
|
46710
|
-
trackedFileCount: trackedFiles.length
|
|
47881
|
+
trackedFileCount: trackedFiles.length,
|
|
47882
|
+
untrackedExcludedCount: countFilesOutsideHead(input.root)
|
|
46711
47883
|
};
|
|
46712
47884
|
}
|
|
46713
47885
|
function evaluateReviewDependencyConstraints(input) {
|
|
@@ -46723,11 +47895,20 @@ function evaluateReviewDependencyConstraints(input) {
|
|
|
46723
47895
|
codeFacts: { availability: "unavailable", indexedWorktreeDigest: null }
|
|
46724
47896
|
});
|
|
46725
47897
|
}
|
|
47898
|
+
const edgeLimit = input.edgeLimit ?? CODEGRAPH_IMPORT_NODE_READ_LIMIT;
|
|
46726
47899
|
let imports;
|
|
46727
47900
|
try {
|
|
46728
|
-
imports = repositoryImportPairs(input.root, CODEGRAPH_BINARY,
|
|
47901
|
+
imports = repositoryImportPairs(input.root, CODEGRAPH_BINARY, edgeLimit, input.worktreeDigest);
|
|
46729
47902
|
} catch {
|
|
46730
|
-
imports = {
|
|
47903
|
+
imports = {
|
|
47904
|
+
pairs: [],
|
|
47905
|
+
truncated: true,
|
|
47906
|
+
availability: "unavailable",
|
|
47907
|
+
indexedWorktreeDigest: null,
|
|
47908
|
+
importNodeCount: null,
|
|
47909
|
+
importNodeLimit: edgeLimit,
|
|
47910
|
+
importNodeLimitExceeded: false
|
|
47911
|
+
};
|
|
46731
47912
|
}
|
|
46732
47913
|
const workspaces = reviewWorkspacePackages(input.root);
|
|
46733
47914
|
return evaluateDependencyConstraints({
|
|
@@ -46737,6 +47918,7 @@ function evaluateReviewDependencyConstraints(input) {
|
|
|
46737
47918
|
workspacePackages: workspaces.packages,
|
|
46738
47919
|
workspacePackagesResolved: workspaces.resolved,
|
|
46739
47920
|
truncated: imports.truncated,
|
|
47921
|
+
importNodeLimitExceeded: imports.importNodeLimitExceeded,
|
|
46740
47922
|
codeFacts: { availability: imports.availability, indexedWorktreeDigest: imports.indexedWorktreeDigest }
|
|
46741
47923
|
});
|
|
46742
47924
|
}
|
|
@@ -46744,7 +47926,7 @@ function reviewWorkspacePackages(root) {
|
|
|
46744
47926
|
if (!existsSync24(join16(root, "package.json")))
|
|
46745
47927
|
return { packages: [], resolved: true };
|
|
46746
47928
|
try {
|
|
46747
|
-
const manifest = JSON.parse(
|
|
47929
|
+
const manifest = JSON.parse(readFileSync25(join16(root, "package.json"), "utf8"));
|
|
46748
47930
|
const declared = Array.isArray(manifest.workspaces) ? manifest.workspaces : manifest.workspaces?.packages ?? [];
|
|
46749
47931
|
if (!Array.isArray(declared))
|
|
46750
47932
|
throw new Error("workspaces must be a list");
|
|
@@ -46757,10 +47939,10 @@ function reviewWorkspacePackages(root) {
|
|
|
46757
47939
|
const parent = /^([^*?[\]{}!]+)\/\*$/.exec(pattern)?.[1];
|
|
46758
47940
|
if (parent === undefined)
|
|
46759
47941
|
throw new Error(`unsupported workspace pattern: ${entry}`);
|
|
46760
|
-
return
|
|
47942
|
+
return readdirSync13(join16(root, parent), { withFileTypes: true }).filter((child) => child.isDirectory() && existsSync24(join16(root, parent, child.name, "package.json"))).map((child) => `${parent}/${child.name}`);
|
|
46761
47943
|
});
|
|
46762
47944
|
const packages = directories.flatMap((directory) => {
|
|
46763
|
-
const packageManifest = JSON.parse(
|
|
47945
|
+
const packageManifest = JSON.parse(readFileSync25(join16(root, ...directory.split("/"), "package.json"), "utf8"));
|
|
46764
47946
|
if (typeof packageManifest.name !== "string")
|
|
46765
47947
|
return [];
|
|
46766
47948
|
const exports = typeof packageManifest.exports === "string" ? { ".": packageManifest.exports } : Object.fromEntries(Object.entries(packageManifest.exports ?? {}).filter((entry) => typeof entry[1] === "string"));
|
|
@@ -46797,6 +47979,10 @@ function readWorktreeFiles(root) {
|
|
|
46797
47979
|
}
|
|
46798
47980
|
return [...new Set(output.split("\x00").filter(Boolean))].sort();
|
|
46799
47981
|
}
|
|
47982
|
+
function countFilesOutsideHead(root) {
|
|
47983
|
+
const head = new Set(readTrackedTreeEntries(root).map((entry) => entry.path));
|
|
47984
|
+
return readWorktreeFiles(root).filter((path) => !head.has(path)).length;
|
|
47985
|
+
}
|
|
46800
47986
|
function declaredWorkspacePackages(root) {
|
|
46801
47987
|
return existsSync24(join16(root, "package.json")) ? readWorkspacePackages(root) : [];
|
|
46802
47988
|
}
|
|
@@ -46827,6 +48013,8 @@ function readCodeFacts(root, worktreeDigest, edgeLimit) {
|
|
|
46827
48013
|
return {
|
|
46828
48014
|
pairs: imports.pairs,
|
|
46829
48015
|
truncated: imports.truncated,
|
|
48016
|
+
importNodeCount: imports.importNodeCount,
|
|
48017
|
+
importNodeLimitExceeded: imports.importNodeLimitExceeded,
|
|
46830
48018
|
availability: imports.availability,
|
|
46831
48019
|
indexedWorktreeDigest: imports.indexedWorktreeDigest,
|
|
46832
48020
|
...attestation
|
|
@@ -46843,10 +48031,108 @@ function attestCodeGraphBinary(root) {
|
|
|
46843
48031
|
const binaryPath = invocation.argsPrefix[0] ?? invocation.command;
|
|
46844
48032
|
return {
|
|
46845
48033
|
version,
|
|
46846
|
-
binaryDigest: `sha256:${createHash10("sha256").update(
|
|
48034
|
+
binaryDigest: `sha256:${createHash10("sha256").update(readFileSync25(binaryPath)).digest("hex")}`
|
|
46847
48035
|
};
|
|
46848
48036
|
}
|
|
46849
48037
|
|
|
48038
|
+
// packages/local-runtime/runtime-daemon/src/capability-dependencies.ts
|
|
48039
|
+
var CODEGRAPH_BINARY2 = "codegraph";
|
|
48040
|
+
|
|
48041
|
+
class CapabilityDependenciesError extends Error {
|
|
48042
|
+
code;
|
|
48043
|
+
constructor(code, message) {
|
|
48044
|
+
super(message);
|
|
48045
|
+
this.code = code;
|
|
48046
|
+
this.name = "CapabilityDependenciesError";
|
|
48047
|
+
}
|
|
48048
|
+
}
|
|
48049
|
+
function readCapabilityDependencies(root, request = {}) {
|
|
48050
|
+
const importNodeLimit = request.importNodeLimit ?? CODEGRAPH_IMPORT_NODE_READ_LIMIT;
|
|
48051
|
+
for (const [name, value] of [["importNodeLimit", request.importNodeLimit], ["edgeLimit", request.edgeLimit]]) {
|
|
48052
|
+
if (value !== undefined && (!Number.isInteger(value) || value < (name === "edgeLimit" ? 0 : 1))) {
|
|
48053
|
+
throw new CapabilityDependenciesError("AC_SCHEMA_INVALID", `${name} must be a ${name === "edgeLimit" ? "non-negative" : "positive"} integer, got ${value}`);
|
|
48054
|
+
}
|
|
48055
|
+
}
|
|
48056
|
+
const model = loadModel2(root);
|
|
48057
|
+
const nodeIds = new Set(model.nodes.map((node) => node.id));
|
|
48058
|
+
for (const [flag, nodeId] of [["--from", request.from], ["--into", request.into]]) {
|
|
48059
|
+
if (nodeId !== undefined && !nodeIds.has(nodeId)) {
|
|
48060
|
+
throw new CapabilityDependenciesError("AC_SCHEMA_INVALID", `deps ${flag} names no node in the model: ${nodeId}`);
|
|
48061
|
+
}
|
|
48062
|
+
}
|
|
48063
|
+
const headSha = readHeadSha(root);
|
|
48064
|
+
const worktreeDigest = computeWorktreeDigest(root);
|
|
48065
|
+
const files = readTrackedTreeEntries(root).filter((entry) => entry.type === "blob").map((entry) => entry.path);
|
|
48066
|
+
const imports = readImports(root, importNodeLimit, worktreeDigest);
|
|
48067
|
+
const matrix = measureCapabilityDependencies({
|
|
48068
|
+
nodes: model.nodes,
|
|
48069
|
+
worktreeDigest,
|
|
48070
|
+
files,
|
|
48071
|
+
importEdges: imports.pairs,
|
|
48072
|
+
workspacePackages: readWorkspaces(root),
|
|
48073
|
+
truncated: imports.truncated,
|
|
48074
|
+
importNodeCount: imports.importNodeCount,
|
|
48075
|
+
importNodeLimit: imports.importNodeLimit,
|
|
48076
|
+
importNodeLimitExceeded: imports.importNodeLimitExceeded,
|
|
48077
|
+
codeFacts: { availability: imports.availability, indexedWorktreeDigest: imports.indexedWorktreeDigest },
|
|
48078
|
+
...request.from === undefined ? {} : { from: request.from },
|
|
48079
|
+
...request.into === undefined ? {} : { into: request.into },
|
|
48080
|
+
...request.edgeLimit === undefined ? {} : { edgeLimit: request.edgeLimit }
|
|
48081
|
+
});
|
|
48082
|
+
return { ...matrix, worktree: { headSha, worktreeDigest }, privacy: { writes: "none" } };
|
|
48083
|
+
}
|
|
48084
|
+
function readWorkspaces(root) {
|
|
48085
|
+
try {
|
|
48086
|
+
return declaredWorkspacePackages(root);
|
|
48087
|
+
} catch (error) {
|
|
48088
|
+
throw new CapabilityDependenciesError("AC_SCHEMA_INVALID", `workspace packages are unreadable: ${error instanceof Error ? error.message : String(error)}`);
|
|
48089
|
+
}
|
|
48090
|
+
}
|
|
48091
|
+
function loadModel2(root) {
|
|
48092
|
+
try {
|
|
48093
|
+
return loadNativeModelFromArchContext(root);
|
|
48094
|
+
} catch (error) {
|
|
48095
|
+
throw new CapabilityDependenciesError("AC_MODEL_ADOPTION_REQUIRED", `architecture model is unreadable: ${error instanceof Error ? error.message : String(error)}`);
|
|
48096
|
+
}
|
|
48097
|
+
}
|
|
48098
|
+
function readImports(root, limit, worktreeDigest) {
|
|
48099
|
+
try {
|
|
48100
|
+
return repositoryImportPairs(root, CODEGRAPH_BINARY2, limit, worktreeDigest);
|
|
48101
|
+
} catch {
|
|
48102
|
+
return {
|
|
48103
|
+
pairs: [],
|
|
48104
|
+
truncated: true,
|
|
48105
|
+
availability: "unavailable",
|
|
48106
|
+
indexedWorktreeDigest: null,
|
|
48107
|
+
importNodeCount: null,
|
|
48108
|
+
importNodeLimit: limit,
|
|
48109
|
+
importNodeLimitExceeded: false
|
|
48110
|
+
};
|
|
48111
|
+
}
|
|
48112
|
+
}
|
|
48113
|
+
// packages/local-runtime/runtime-daemon/src/flow-selector-check.ts
|
|
48114
|
+
init_src();
|
|
48115
|
+
function runFlowSelectorCheckCommand(cwd, options = {}) {
|
|
48116
|
+
try {
|
|
48117
|
+
const root = findRepositoryRoot(cwd);
|
|
48118
|
+
const model = loadNativeModelFromArchContext(root);
|
|
48119
|
+
const sourceFiles = listProjectionSourceFiles(root);
|
|
48120
|
+
const prepared = prepareProjectionCodeFacts(root, model, {
|
|
48121
|
+
sourceTreeDigest: architectureDocumentationSourceTreeDigest(root, model, sourceFiles),
|
|
48122
|
+
sourceFiles,
|
|
48123
|
+
importGraphs: false,
|
|
48124
|
+
...options.binary ? { binary: options.binary } : {}
|
|
48125
|
+
});
|
|
48126
|
+
assertProjectionCodeFactsAvailable(root, prepared);
|
|
48127
|
+
return okEnvelope("flows.check", checkArchitectureFlowSelectors({
|
|
48128
|
+
model,
|
|
48129
|
+
selectorEvidence: prepared.selectorEvidence,
|
|
48130
|
+
codeGraph: { availability: prepared.handshake.availability, version: prepared.handshake.actualVersion }
|
|
48131
|
+
}));
|
|
48132
|
+
} catch (error) {
|
|
48133
|
+
return projectionFailureEnvelope("flows.check", error);
|
|
48134
|
+
}
|
|
48135
|
+
}
|
|
46850
48136
|
// packages/local-runtime/runtime-daemon/src/refactor-verify.ts
|
|
46851
48137
|
init_src();
|
|
46852
48138
|
var REFACTOR_RESOLUTION_EVENT_TYPE = "architecture.refactor.resolution";
|
|
@@ -46993,6 +48279,12 @@ function baselineSnapshotForRecommendation(evidenceState, recommendationId) {
|
|
|
46993
48279
|
}
|
|
46994
48280
|
return;
|
|
46995
48281
|
}
|
|
48282
|
+
function baselineImportNodeLimit(snapshot) {
|
|
48283
|
+
if (snapshot === undefined || snapshot.codeFacts.importNodeCount === undefined)
|
|
48284
|
+
return;
|
|
48285
|
+
const { edgeLimit } = snapshot.codeFacts;
|
|
48286
|
+
return edgeLimit === null || edgeLimit === CODEGRAPH_IMPORT_NODE_READ_LIMIT ? undefined : edgeLimit;
|
|
48287
|
+
}
|
|
46996
48288
|
function refactorVerifyIngressIssues(recommendation) {
|
|
46997
48289
|
if (recommendation.category === "practice") {
|
|
46998
48290
|
return [`recommendation ${recommendation.recommendationId} is a practice recommendation and carries no refactor outcome`];
|
|
@@ -47087,6 +48379,9 @@ class RecommendationsService {
|
|
|
47087
48379
|
}
|
|
47088
48380
|
});
|
|
47089
48381
|
};
|
|
48382
|
+
if (input.importNodeLimit !== undefined && !(Number.isInteger(input.importNodeLimit) && input.importNodeLimit >= 1)) {
|
|
48383
|
+
return errorEnvelope(`recommendations.${command}`, "AC_SCHEMA_INVALID", `recommendations ${command} importNodeLimit must be a positive integer`);
|
|
48384
|
+
}
|
|
47090
48385
|
if (command === "metrics")
|
|
47091
48386
|
return readMetrics();
|
|
47092
48387
|
if (command === "list")
|
|
@@ -47094,7 +48389,7 @@ class RecommendationsService {
|
|
|
47094
48389
|
if (command === "show") {
|
|
47095
48390
|
if (!input.recommendationId)
|
|
47096
48391
|
return errorEnvelope("recommendations.show", "AC_SCHEMA_INVALID", "recommendations show requires --id");
|
|
47097
|
-
return this.showRecommendation(repositoryRoot, input.recommendationId);
|
|
48392
|
+
return this.showRecommendation(repositoryRoot, input.recommendationId, input.importNodeLimit);
|
|
47098
48393
|
}
|
|
47099
48394
|
if (!isRecommendationLifecycleCliAction(command)) {
|
|
47100
48395
|
return errorEnvelope("recommendations", "AC_SCHEMA_INVALID", "recommendations requires acknowledge|accept|reject|defer|waive|resolve|metrics|show|list");
|
|
@@ -47158,7 +48453,7 @@ class RecommendationsService {
|
|
|
47158
48453
|
async decideScanCandidate(root, repositoryRoot, input, command, now) {
|
|
47159
48454
|
const surface = `recommendations.${command}`;
|
|
47160
48455
|
const recommendationId = input.recommendationId;
|
|
47161
|
-
const measured = await this.measureRepository(repositoryRoot,
|
|
48456
|
+
const measured = await this.measureRepository(repositoryRoot, repositoryRefactorRequest(input.importNodeLimit), `recommendations ${command}`);
|
|
47162
48457
|
if (!measured.ok) {
|
|
47163
48458
|
return errorEnvelope(surface, measured.code, `recommendation ${recommendationId} is not recorded and the current refactor scan could not run: ${measured.message}`, measured.reasonCode);
|
|
47164
48459
|
}
|
|
@@ -47239,7 +48534,7 @@ class RecommendationsService {
|
|
|
47239
48534
|
}
|
|
47240
48535
|
});
|
|
47241
48536
|
}
|
|
47242
|
-
async showRecommendation(repositoryRoot, recommendationId) {
|
|
48537
|
+
async showRecommendation(repositoryRoot, recommendationId, importNodeLimit) {
|
|
47243
48538
|
const scope = await this.context.architectureLedgerScope(repositoryRoot);
|
|
47244
48539
|
const replay = await this.context.localStore.replayArchitectureLedger({ ...scope, mode: "genesis" });
|
|
47245
48540
|
const artifacts = recommendationArtifactsFromEvents(replay.events);
|
|
@@ -47257,7 +48552,7 @@ class RecommendationsService {
|
|
|
47257
48552
|
worktree: null
|
|
47258
48553
|
});
|
|
47259
48554
|
}
|
|
47260
|
-
const measured = await this.measureRepository(repositoryRoot,
|
|
48555
|
+
const measured = await this.measureRepository(repositoryRoot, repositoryRefactorRequest(importNodeLimit), "recommendations show");
|
|
47261
48556
|
if (!measured.ok) {
|
|
47262
48557
|
return errorEnvelope("recommendations.show", measured.code, `recommendation ${recommendationId} is not recorded and the current refactor scan could not run: ${measured.message}`, measured.reasonCode);
|
|
47263
48558
|
}
|
|
@@ -47446,6 +48741,7 @@ class RecommendationsService {
|
|
|
47446
48741
|
requestId: result.requestId,
|
|
47447
48742
|
request,
|
|
47448
48743
|
trackedFileCount: result.trackedFileCount,
|
|
48744
|
+
untrackedExcludedCount: result.untrackedExcludedCount,
|
|
47449
48745
|
snapshot: result.snapshot,
|
|
47450
48746
|
assessment: result.assessment,
|
|
47451
48747
|
...result.proposal ? { proposal: result.proposal } : {},
|
|
@@ -47612,11 +48908,13 @@ class RecommendationsService {
|
|
|
47612
48908
|
graphDigest: replay.graphDigest
|
|
47613
48909
|
});
|
|
47614
48910
|
}
|
|
48911
|
+
const baseline = baselineSnapshotForRecommendation(replay.evidenceState, recommendation.recommendationId);
|
|
48912
|
+
const importNodeLimit = input.importNodeLimit ?? baselineImportNodeLimit(baseline?.snapshot);
|
|
47615
48913
|
let scan;
|
|
47616
48914
|
try {
|
|
47617
48915
|
scan = runRefactorScan({
|
|
47618
48916
|
root: repositoryRoot,
|
|
47619
|
-
request:
|
|
48917
|
+
request: repositoryRefactorRequest(importNodeLimit),
|
|
47620
48918
|
repository: gitScope.repository,
|
|
47621
48919
|
worktree: gitScope.worktree,
|
|
47622
48920
|
previousRecommendations: artifacts.recommendations,
|
|
@@ -47627,7 +48925,6 @@ class RecommendationsService {
|
|
|
47627
48925
|
return errorEnvelope("refactor.verify", error.code, error.message, error.reasonCode);
|
|
47628
48926
|
return errorEnvelope("refactor.verify", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
|
|
47629
48927
|
}
|
|
47630
|
-
const baseline = baselineSnapshotForRecommendation(replay.evidenceState, recommendation.recommendationId);
|
|
47631
48928
|
let plan;
|
|
47632
48929
|
try {
|
|
47633
48930
|
plan = runRefactorVerify({
|
|
@@ -47764,11 +49061,15 @@ function decodeRuntimeRefactorVerifyInput(value) {
|
|
|
47764
49061
|
throw new RuntimeRefactorInputError(`refactor verify ${field} must be a non-empty string`);
|
|
47765
49062
|
}
|
|
47766
49063
|
}
|
|
49064
|
+
if (input.importNodeLimit !== undefined && typeof input.importNodeLimit !== "number") {
|
|
49065
|
+
throw new RuntimeRefactorInputError("refactor verify importNodeLimit must be a positive integer");
|
|
49066
|
+
}
|
|
47767
49067
|
const request = {
|
|
47768
49068
|
schemaVersion: REFACTOR_VERIFICATION_REQUEST_SCHEMA_VERSION,
|
|
47769
49069
|
recommendationId: input.recommendationId,
|
|
47770
49070
|
...typeof input.expectedHeadSha === "string" ? { expectedHeadSha: input.expectedHeadSha } : {},
|
|
47771
49071
|
...typeof input.expectedWorktreeDigest === "string" ? { expectedWorktreeDigest: input.expectedWorktreeDigest } : {},
|
|
49072
|
+
...typeof input.importNodeLimit === "number" ? { importNodeLimit: input.importNodeLimit } : {},
|
|
47772
49073
|
...input.executionEvidenceRefs === undefined ? {} : { executionEvidenceRefs: decodeRuntimeExecutionEvidenceRefs(input.executionEvidenceRefs) }
|
|
47773
49074
|
};
|
|
47774
49075
|
if (input.schemaVersion !== REFACTOR_VERIFICATION_REQUEST_SCHEMA_VERSION) {
|
|
@@ -47966,14 +49267,14 @@ function recommendationActorKind(input) {
|
|
|
47966
49267
|
}
|
|
47967
49268
|
|
|
47968
49269
|
// packages/local-runtime/runtime-daemon/src/changeset-authority.ts
|
|
47969
|
-
import { existsSync as existsSync25, readFileSync as
|
|
47970
|
-
import { resolve as
|
|
49270
|
+
import { existsSync as existsSync25, readFileSync as readFileSync27 } from "node:fs";
|
|
49271
|
+
import { resolve as resolve27 } from "node:path";
|
|
47971
49272
|
init_src();
|
|
47972
49273
|
|
|
47973
49274
|
// packages/local-runtime/runtime-daemon/src/committed-change-acceptance.ts
|
|
47974
49275
|
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
47975
|
-
import { closeSync as closeSync8, constants as fsConstants3, fstatSync as fstatSync3, lstatSync as lstatSync13, openSync as openSync9, readdirSync as
|
|
47976
|
-
import { resolve as
|
|
49276
|
+
import { closeSync as closeSync8, constants as fsConstants3, fstatSync as fstatSync3, lstatSync as lstatSync13, openSync as openSync9, readdirSync as readdirSync14, readFileSync as readFileSync26 } from "node:fs";
|
|
49277
|
+
import { resolve as resolve26 } from "node:path";
|
|
47977
49278
|
init_src();
|
|
47978
49279
|
var SEMANTIC_MODEL_DIRECTORIES = ["nodes", "relations", "flows"];
|
|
47979
49280
|
var SEMANTIC_MODEL_PATH = /^\.archcontext\/model\/(nodes|relations|flows)\/[^/]+\.ya?ml$/;
|
|
@@ -48030,7 +49331,7 @@ function readSemanticModelSnapshot(root, readEntry) {
|
|
|
48030
49331
|
try {
|
|
48031
49332
|
if (!lstatSync13(absolute).isDirectory())
|
|
48032
49333
|
throw new Error(`semantic-model-directory-invalid: ${relativeDirectory}`);
|
|
48033
|
-
entries =
|
|
49334
|
+
entries = readdirSync14(absolute);
|
|
48034
49335
|
} catch (error) {
|
|
48035
49336
|
if (error.code === "ENOENT")
|
|
48036
49337
|
continue;
|
|
@@ -48038,7 +49339,7 @@ function readSemanticModelSnapshot(root, readEntry) {
|
|
|
48038
49339
|
}
|
|
48039
49340
|
for (const entry of entries.filter((name) => /\.ya?ml$/.test(name)).sort()) {
|
|
48040
49341
|
const path = `${relativeDirectory}/${entry}`;
|
|
48041
|
-
bodies.set(path, strictUtf8(readEntry(
|
|
49342
|
+
bodies.set(path, strictUtf8(readEntry(resolve26(absolute, entry), path), path));
|
|
48042
49343
|
}
|
|
48043
49344
|
}
|
|
48044
49345
|
const hashes = new Map([...bodies].map(([path, body]) => [path, digestJson({ body })]));
|
|
@@ -48051,7 +49352,7 @@ function readRegularFileBytes(absolute, path) {
|
|
|
48051
49352
|
try {
|
|
48052
49353
|
if (!fstatSync3(fd).isFile())
|
|
48053
49354
|
throw new Error(`semantic-model-entry-not-regular: ${path}`);
|
|
48054
|
-
return
|
|
49355
|
+
return readFileSync26(fd);
|
|
48055
49356
|
} finally {
|
|
48056
49357
|
closeSync8(fd);
|
|
48057
49358
|
}
|
|
@@ -48401,6 +49702,40 @@ function sameStrings(left, right) {
|
|
|
48401
49702
|
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
48402
49703
|
}
|
|
48403
49704
|
|
|
49705
|
+
// packages/local-runtime/runtime-daemon/src/ownership-delta-preview.ts
|
|
49706
|
+
init_src();
|
|
49707
|
+
var NODE_DIRECTORY = ".archcontext/model/nodes/";
|
|
49708
|
+
var NODE_FILE_OPERATIONS = new Set(["create_entity", "update_entity_fields", "delete_entity"]);
|
|
49709
|
+
function planOwnershipDelta(root, draft) {
|
|
49710
|
+
const nodeOperations = draft.operations.filter((operation) => NODE_FILE_OPERATIONS.has(operation.op) && operation.path !== undefined && isNodeFile(operation.path));
|
|
49711
|
+
if (nodeOperations.length === 0)
|
|
49712
|
+
return;
|
|
49713
|
+
try {
|
|
49714
|
+
const current = new Map(listModelFiles(root).filter((file) => isNodeFile(file.path)).map((file) => [file.path, file.body]));
|
|
49715
|
+
const planned = new Map(current);
|
|
49716
|
+
for (const operation of nodeOperations) {
|
|
49717
|
+
if (operation.op === "delete_entity")
|
|
49718
|
+
planned.delete(operation.path);
|
|
49719
|
+
else
|
|
49720
|
+
planned.set(operation.path, operation.body ?? "");
|
|
49721
|
+
}
|
|
49722
|
+
const paths = readTrackedTreeEntries(root).filter((entry) => entry.type === "blob").map((entry) => entry.path);
|
|
49723
|
+
return ownershipDelta(loadNativeModelFromModelFiles(current).nodes, loadNativeModelFromModelFiles(planned).nodes, paths);
|
|
49724
|
+
} catch (error) {
|
|
49725
|
+
return {
|
|
49726
|
+
schemaVersion: CHANGESET_OWNERSHIP_DELTA_SCHEMA_VERSION,
|
|
49727
|
+
status: "unavailable",
|
|
49728
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
49729
|
+
};
|
|
49730
|
+
}
|
|
49731
|
+
}
|
|
49732
|
+
function isNodeFile(path) {
|
|
49733
|
+
if (!path.startsWith(NODE_DIRECTORY))
|
|
49734
|
+
return false;
|
|
49735
|
+
const name = path.slice(NODE_DIRECTORY.length);
|
|
49736
|
+
return !name.includes("/") && /\.ya?ml$/.test(name);
|
|
49737
|
+
}
|
|
49738
|
+
|
|
48404
49739
|
// packages/local-runtime/runtime-daemon/src/changeset-authority.ts
|
|
48405
49740
|
class RuntimeUpdateInputError extends Error {
|
|
48406
49741
|
}
|
|
@@ -48516,10 +49851,10 @@ class ChangeSetAuthorityService {
|
|
|
48516
49851
|
return errorEnvelope("practices.waive", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
|
|
48517
49852
|
}
|
|
48518
49853
|
const path = `.archcontext/waivers/${waiverId}.json`;
|
|
48519
|
-
const absolute =
|
|
49854
|
+
const absolute = resolve27(session.workspace.root, path);
|
|
48520
49855
|
const body = `${JSON.stringify(waiver, null, 2)}
|
|
48521
49856
|
`;
|
|
48522
|
-
const expectedHash = existsSync25(absolute) ? digestJson({ body:
|
|
49857
|
+
const expectedHash = existsSync25(absolute) ? digestJson({ body: readFileSync27(absolute, "utf8") }) : "missing";
|
|
48523
49858
|
const draft = this.changeSetEngine.plan({
|
|
48524
49859
|
id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
|
|
48525
49860
|
base: {
|
|
@@ -48574,10 +49909,14 @@ class ChangeSetAuthorityService {
|
|
|
48574
49909
|
this.changesets.set(draft.id, draft);
|
|
48575
49910
|
this.changeSetRoots.set(draft.id, canonicalRepositoryRoot(root));
|
|
48576
49911
|
this.changeSetWorktreeDigestProfiles.set(draft.id, worktreeDigestProfile);
|
|
49912
|
+
const ownershipDelta = planOwnershipDelta(root, draft);
|
|
48577
49913
|
return okEnvelope("plan_update", {
|
|
48578
49914
|
draft,
|
|
48579
49915
|
changeSetDigest: digestJson(draft),
|
|
48580
|
-
preview:
|
|
49916
|
+
preview: {
|
|
49917
|
+
...this.changeSetEngine.preview(root, draft),
|
|
49918
|
+
...ownershipDelta === undefined ? {} : { ownershipDelta }
|
|
49919
|
+
}
|
|
48581
49920
|
});
|
|
48582
49921
|
}
|
|
48583
49922
|
async mcpProjection(root, input, approved) {
|
|
@@ -48876,7 +50215,7 @@ function architectureLedgerWriteAppendsEvents(mode) {
|
|
|
48876
50215
|
// packages/local-runtime/runtime-daemon/src/rpc-server.ts
|
|
48877
50216
|
init_src();
|
|
48878
50217
|
import { createServer as createServer2 } from "node:http";
|
|
48879
|
-
import { closeSync as closeSync9,
|
|
50218
|
+
import { closeSync as closeSync9, existsSync as existsSync26, mkdirSync as mkdirSync12 } from "node:fs";
|
|
48880
50219
|
import { dirname as dirname14 } from "node:path";
|
|
48881
50220
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
48882
50221
|
var DEFAULT_DAEMON_IDLE_TIMEOUT_MS = 30 * 60000;
|
|
@@ -48904,6 +50243,8 @@ class ArchctxRuntimeRpcServer {
|
|
|
48904
50243
|
idleTimeoutMs;
|
|
48905
50244
|
idleTimer;
|
|
48906
50245
|
inFlightRpcRequests = 0;
|
|
50246
|
+
authorizedRequests = 0;
|
|
50247
|
+
idleExitPending = false;
|
|
48907
50248
|
constructor(daemon, options = {}) {
|
|
48908
50249
|
this.daemon = daemon;
|
|
48909
50250
|
this.options = options;
|
|
@@ -48930,11 +50271,11 @@ class ArchctxRuntimeRpcServer {
|
|
|
48930
50271
|
if (server)
|
|
48931
50272
|
await new Promise((resolveClose) => server.close(() => resolveClose()));
|
|
48932
50273
|
if (connection)
|
|
48933
|
-
|
|
50274
|
+
await removePrivateControlFile(connection.connectionPath);
|
|
48934
50275
|
if (this.lockFd !== undefined) {
|
|
48935
50276
|
closeSync9(this.lockFd);
|
|
48936
50277
|
this.lockFd = undefined;
|
|
48937
|
-
|
|
50278
|
+
await removePrivateControlFile(this.options.lockPath ?? defaultDaemonLockPath(this.options.root ?? process.cwd()));
|
|
48938
50279
|
}
|
|
48939
50280
|
if (startedDaemon)
|
|
48940
50281
|
await this.daemon.stop().catch(() => {
|
|
@@ -48992,13 +50333,16 @@ class ArchctxRuntimeRpcServer {
|
|
|
48992
50333
|
});
|
|
48993
50334
|
}
|
|
48994
50335
|
await this.daemon.stop();
|
|
50336
|
+
try {
|
|
50337
|
+
if (connection)
|
|
50338
|
+
await removePrivateControlFile(connection.connectionPath);
|
|
50339
|
+
} finally {
|
|
50340
|
+
if (this.lockFd !== undefined)
|
|
50341
|
+
closeSync9(this.lockFd);
|
|
50342
|
+
this.lockFd = undefined;
|
|
50343
|
+
}
|
|
48995
50344
|
if (connection)
|
|
48996
|
-
|
|
48997
|
-
if (this.lockFd !== undefined)
|
|
48998
|
-
closeSync9(this.lockFd);
|
|
48999
|
-
this.lockFd = undefined;
|
|
49000
|
-
if (connection)
|
|
49001
|
-
rmSync15(connection.lockPath, { force: true });
|
|
50345
|
+
await removePrivateControlFile(connection.lockPath);
|
|
49002
50346
|
this.options.onStop?.();
|
|
49003
50347
|
}
|
|
49004
50348
|
armIdleTimer() {
|
|
@@ -49015,21 +50359,44 @@ class ArchctxRuntimeRpcServer {
|
|
|
49015
50359
|
this.idleTimer = undefined;
|
|
49016
50360
|
}
|
|
49017
50361
|
async checkIdleAndMaybeExit() {
|
|
49018
|
-
if (!this.server || !this.connection)
|
|
50362
|
+
if (!this.server || !this.connection || this.idleExitPending)
|
|
49019
50363
|
return;
|
|
49020
50364
|
if (this.inFlightRpcRequests > 0) {
|
|
49021
50365
|
this.armIdleTimer();
|
|
49022
50366
|
return;
|
|
49023
50367
|
}
|
|
50368
|
+
this.idleExitPending = true;
|
|
49024
50369
|
const hasActiveWork = await this.daemon.hasActiveBackgroundWork().catch(() => true);
|
|
49025
50370
|
if (hasActiveWork || this.inFlightRpcRequests > 0 || !this.connection) {
|
|
50371
|
+
this.idleExitPending = false;
|
|
49026
50372
|
this.armIdleTimer();
|
|
49027
50373
|
return;
|
|
49028
50374
|
}
|
|
49029
|
-
|
|
50375
|
+
const connection = this.connection;
|
|
50376
|
+
const requestsBeforeRemoval = this.authorizedRequests;
|
|
50377
|
+
let cleanupError;
|
|
50378
|
+
await removePrivateControlFile(connection.connectionPath).catch((error) => {
|
|
50379
|
+
cleanupError = error;
|
|
50380
|
+
});
|
|
50381
|
+
if (this.server && this.connection && (this.inFlightRpcRequests > 0 || this.authorizedRequests !== requestsBeforeRemoval)) {
|
|
50382
|
+
try {
|
|
50383
|
+
if (!existsSync26(connection.connectionPath))
|
|
50384
|
+
createPrivateControlFile(connection.connectionPath, JSON.stringify(connection, null, 2));
|
|
50385
|
+
this.idleExitPending = false;
|
|
50386
|
+
this.armIdleTimer();
|
|
50387
|
+
return;
|
|
50388
|
+
} catch (error) {
|
|
50389
|
+
cleanupError ??= error;
|
|
50390
|
+
}
|
|
50391
|
+
}
|
|
49030
50392
|
try {
|
|
49031
50393
|
await this.stop();
|
|
50394
|
+
} catch (error) {
|
|
50395
|
+
cleanupError ??= error;
|
|
49032
50396
|
} finally {
|
|
50397
|
+
if (cleanupError)
|
|
50398
|
+
process.stderr.write(`archctxd idle exit cleanup failed: ${cleanupError.code ?? "unknown"}
|
|
50399
|
+
`);
|
|
49033
50400
|
(this.options.exit ?? process.exit)(0);
|
|
49034
50401
|
}
|
|
49035
50402
|
}
|
|
@@ -49059,6 +50426,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
49059
50426
|
writeJson(response, 401, { schemaVersion: RUNTIME_RPC_VERSION, ok: false, error: "runtime RPC token required" });
|
|
49060
50427
|
return;
|
|
49061
50428
|
}
|
|
50429
|
+
this.authorizedRequests += 1;
|
|
49062
50430
|
const changeSetRecovery = this.daemon.status().changeSetRecovery;
|
|
49063
50431
|
writeJson(response, 200, {
|
|
49064
50432
|
schemaVersion: RUNTIME_RPC_VERSION,
|
|
@@ -49082,6 +50450,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
49082
50450
|
return;
|
|
49083
50451
|
}
|
|
49084
50452
|
this.inFlightRpcRequests += 1;
|
|
50453
|
+
this.authorizedRequests += 1;
|
|
49085
50454
|
try {
|
|
49086
50455
|
const read = await readRpcRequestBody(request, {
|
|
49087
50456
|
maxBytes: this.options.maxRequestBodyBytes ?? RUNTIME_RPC_MAX_REQUEST_BODY_BYTES,
|
|
@@ -49111,8 +50480,12 @@ class ArchctxRuntimeRpcServer {
|
|
|
49111
50480
|
throw error;
|
|
49112
50481
|
});
|
|
49113
50482
|
writeJson(response, 200, result);
|
|
49114
|
-
if (body.method === "shutdown")
|
|
49115
|
-
setTimeout(() => void this.stop()
|
|
50483
|
+
if (body.method === "shutdown") {
|
|
50484
|
+
setTimeout(() => void this.stop().catch((error) => {
|
|
50485
|
+
process.stderr.write(`archctxd shutdown cleanup failed: ${error.code ?? "unknown"}
|
|
50486
|
+
`);
|
|
50487
|
+
}), 0);
|
|
50488
|
+
}
|
|
49116
50489
|
} finally {
|
|
49117
50490
|
this.inFlightRpcRequests -= 1;
|
|
49118
50491
|
this.armIdleTimer();
|
|
@@ -49214,8 +50587,8 @@ async function readRpcRequestBody(request, limits) {
|
|
|
49214
50587
|
}
|
|
49215
50588
|
|
|
49216
50589
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
49217
|
-
import { existsSync as
|
|
49218
|
-
import { resolve as
|
|
50590
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
50591
|
+
import { resolve as resolve28 } from "node:path";
|
|
49219
50592
|
init_src();
|
|
49220
50593
|
|
|
49221
50594
|
// packages/local-runtime/runtime-daemon/src/egress.ts
|
|
@@ -50154,7 +51527,7 @@ class ArchctxDaemon {
|
|
|
50154
51527
|
}
|
|
50155
51528
|
async restoreRepositorySessions() {
|
|
50156
51529
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
50157
|
-
if (!record.root || !
|
|
51530
|
+
if (!record.root || !existsSync27(record.root))
|
|
50158
51531
|
continue;
|
|
50159
51532
|
if (repositoryFingerprint(record.root) !== record.repositoryId)
|
|
50160
51533
|
continue;
|
|
@@ -50419,7 +51792,7 @@ function blockedProductionInjections(deps) {
|
|
|
50419
51792
|
].filter((key) => (key in deps));
|
|
50420
51793
|
}
|
|
50421
51794
|
function completeTaskProjectionDrift(root) {
|
|
50422
|
-
if (!
|
|
51795
|
+
if (!existsSync27(resolve28(root, "docs/architecture/.projection-manifest.json")))
|
|
50423
51796
|
return;
|
|
50424
51797
|
const profile = loadArchitectureDocumentationProfile(root);
|
|
50425
51798
|
const loaded = loadArchitectureDocumentationInputs(root, profile);
|
|
@@ -50436,6 +51809,7 @@ function completeTaskProjectionDrift(root) {
|
|
|
50436
51809
|
profile,
|
|
50437
51810
|
decisions: loaded.decisions,
|
|
50438
51811
|
existingFiles: loaded.existingFiles,
|
|
51812
|
+
docsLanguage: loaded.docsLanguage,
|
|
50439
51813
|
sourceFootprints: loadCapabilitySourceFootprintDigests(root, loaded.model, codeGraphInputs.sourceFiles),
|
|
50440
51814
|
sourceScaleSignals: loadCapabilitySourceScaleSignals(root, loaded.model, codeGraphInputs.sourceFiles),
|
|
50441
51815
|
importGraphs: codeGraphInputs.importGraphs,
|
|
@@ -50470,7 +51844,6 @@ function completeTaskProjectionFreshness(root) {
|
|
|
50470
51844
|
currentSourceTreeDigest: architectureDocumentationSourceTreeDigest(root, model, sourceFiles)
|
|
50471
51845
|
});
|
|
50472
51846
|
}
|
|
50473
|
-
|
|
50474
51847
|
// packages/surfaces/adapter-likec4/src/index.ts
|
|
50475
51848
|
init_src();
|
|
50476
51849
|
function exportLikeC4Model(model) {
|
|
@@ -50691,7 +52064,7 @@ var LOCAL_MCP_TOOLS = [
|
|
|
50691
52064
|
{
|
|
50692
52065
|
name: "archcontext_plan_update",
|
|
50693
52066
|
inputSchema: MCP_TOOL_INPUT_SCHEMAS.archcontext_plan_update,
|
|
50694
|
-
description: "Create a ChangeSet draft and preview for architecture model updates. Does not write files.",
|
|
52067
|
+
description: "Create a ChangeSet draft and preview for architecture model updates. When the plan writes a node file, preview.ownershipDelta lists the files that become owned, change owner, become contested or become unowned. Does not write files.",
|
|
50695
52068
|
annotations: { safety: "idempotent", requiresConfirmation: false }
|
|
50696
52069
|
},
|
|
50697
52070
|
{
|
|
@@ -51134,6 +52507,9 @@ class RuntimeVersionUnsupportedError extends Error {
|
|
|
51134
52507
|
|
|
51135
52508
|
class RefactorFlagError extends Error {
|
|
51136
52509
|
}
|
|
52510
|
+
|
|
52511
|
+
class CliUsageError extends Error {
|
|
52512
|
+
}
|
|
51137
52513
|
var AUDIT_RUN_STATUSES = ["pending", "issuing", "issued", "failed"];
|
|
51138
52514
|
var AUDIT_GLOBAL_VALUE_FLAGS = ["--format"];
|
|
51139
52515
|
var AUDIT_GLOBAL_BOOLEAN_FLAGS = ["--json"];
|
|
@@ -51141,6 +52517,17 @@ var AUDIT_RUN_VALUE_FLAGS = ["--task-session-id", "--reason", "--risk", "--uncer
|
|
|
51141
52517
|
var AUDIT_RUN_BOOLEAN_FLAGS = ["--no-wait", ...AUDIT_GLOBAL_BOOLEAN_FLAGS];
|
|
51142
52518
|
var AUDIT_APPROVE_VALUE_FLAGS = ["--run-id", "--confirm-public-repo", ...AUDIT_GLOBAL_VALUE_FLAGS];
|
|
51143
52519
|
var AUDIT_APPROVE_BOOLEAN_FLAGS = ["--resume", ...AUDIT_GLOBAL_BOOLEAN_FLAGS];
|
|
52520
|
+
var CLI_DOCS_ANCHORS_HELP = {
|
|
52521
|
+
usage: "archctx docs anchors [--profile default|repo-harness/v1] [--json]",
|
|
52522
|
+
description: "Read-only check of citations in human-owned module document regions and node summary/responsibilities. It reports missing-path, missing-symbol, line-out-of-range and line-moved per node. It is a signal, not a gate: the result stays ok when it has findings.",
|
|
52523
|
+
options: {
|
|
52524
|
+
"--profile <profile>": "Layout whose module documents are read (default: the committed manifest profile; default without a manifest).",
|
|
52525
|
+
"--json": "Accepted for symmetry; output is always JSON.",
|
|
52526
|
+
"--help, -h": "Show help without reading the repository."
|
|
52527
|
+
},
|
|
52528
|
+
writes: "none; no daemon, no local store. Without a CodeGraph index, path and line checks still run and symbol checks are reported as unchecked (codegraph-unavailable).",
|
|
52529
|
+
capability: "docs-anchors-v1"
|
|
52530
|
+
};
|
|
51144
52531
|
function refactorFlagValue(args, flag) {
|
|
51145
52532
|
const index = args.indexOf(flag);
|
|
51146
52533
|
if (index === -1)
|
|
@@ -51153,39 +52540,6 @@ function refactorFlagValueAt(args, index, flag) {
|
|
|
51153
52540
|
throw new RefactorFlagError(`refactor ${flag} requires a value`);
|
|
51154
52541
|
return value;
|
|
51155
52542
|
}
|
|
51156
|
-
if (__require.main == __require.module) {
|
|
51157
|
-
if (command === "capabilities") {
|
|
51158
|
-
const result = await runCli(command, args, process.cwd());
|
|
51159
|
-
if ("ok" in result) {
|
|
51160
|
-
process.stdout.write(`${renderResult(result, "json")}
|
|
51161
|
-
`);
|
|
51162
|
-
if (result.ok === false)
|
|
51163
|
-
process.exitCode = 1;
|
|
51164
|
-
} else {
|
|
51165
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
51166
|
-
`);
|
|
51167
|
-
}
|
|
51168
|
-
} else if (command === "mcp" && args.length === 0) {
|
|
51169
|
-
await runStdioMcpLoop(stdinLines(), (line) => process.stdout.write(`${line}
|
|
51170
|
-
`), (line) => process.stderr.write(`${line}
|
|
51171
|
-
`), { runtimeResolver: (root) => createOrStartRuntimeRpcClient(root) });
|
|
51172
|
-
} else if (command === "daemon" && args[0] === "start" && args.includes("--foreground")) {
|
|
51173
|
-
await runForegroundDaemon(process.cwd(), args).catch((error) => {
|
|
51174
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
51175
|
-
process.stderr.write(`${message}
|
|
51176
|
-
`);
|
|
51177
|
-
process.exitCode = isDaemonOwnedElsewhereError(message) ? DAEMON_OWNED_ELSEWHERE_EXIT_CODE : 1;
|
|
51178
|
-
});
|
|
51179
|
-
} else {
|
|
51180
|
-
const result = await runCli(command, args, process.cwd()).catch((error) => errorEnvelope("cli", "AC_RUNTIME_UNAVAILABLE", error instanceof Error ? error.message : String(error)));
|
|
51181
|
-
process.stdout.write(`${renderResult(result, readFlag(args, "--format") ?? "json")}
|
|
51182
|
-
`);
|
|
51183
|
-
if (result.ok === false)
|
|
51184
|
-
process.exitCode = 1;
|
|
51185
|
-
if (command === "resolve")
|
|
51186
|
-
process.exitCode = resolveCommandExitCode(result);
|
|
51187
|
-
}
|
|
51188
|
-
}
|
|
51189
52543
|
async function* stdinLines() {
|
|
51190
52544
|
let buffer = "";
|
|
51191
52545
|
for await (const chunk of process.stdin) {
|
|
@@ -51271,11 +52625,13 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
|
|
|
51271
52625
|
return (await runtime()).runtimeStatus(cwd);
|
|
51272
52626
|
case "projection":
|
|
51273
52627
|
return await runProjectionProtocolCommand2(args, cwd, runtime);
|
|
52628
|
+
case "flows":
|
|
52629
|
+
return runFlowsCommand(args, cwd);
|
|
51274
52630
|
case "repo": {
|
|
51275
52631
|
const subcommand = args[0] ?? "list";
|
|
51276
52632
|
if (subcommand === "add") {
|
|
51277
52633
|
const root = readFlag(args, "--root") ?? cwd;
|
|
51278
|
-
if (
|
|
52634
|
+
if (resolve29(root) !== resolve29(cwd)) {
|
|
51279
52635
|
return errorEnvelope("repo.add", "AC_CAPABILITY_UNSUPPORTED", "Multi-repo architecture context is outside the Local Core MVP; run archctx inside one Git repository.");
|
|
51280
52636
|
}
|
|
51281
52637
|
const daemon = await runtime();
|
|
@@ -51359,6 +52715,8 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
|
|
|
51359
52715
|
return result.ok ? { ...result, data: paginate(result.data, args) } : result;
|
|
51360
52716
|
}
|
|
51361
52717
|
case "docs":
|
|
52718
|
+
if (args[0] === "anchors")
|
|
52719
|
+
return runDocsAnchorsCliCommand(args.slice(1), cwd);
|
|
51362
52720
|
return await runDocsCommand(args, cwd, await runtime());
|
|
51363
52721
|
case "agent-context":
|
|
51364
52722
|
return await runAgentContextProjectionCommand2(args, cwd, await runtime());
|
|
@@ -51517,36 +52875,30 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
|
|
|
51517
52875
|
revocation: "archctx tunnel --revoke"
|
|
51518
52876
|
}
|
|
51519
52877
|
};
|
|
52878
|
+
case "deps":
|
|
52879
|
+
return runDepsCommand(args, cwd);
|
|
51520
52880
|
case "resolve": {
|
|
52881
|
+
if (args.includes("--help") || args.includes("-h"))
|
|
52882
|
+
return okEnvelope("resolve", CLI_RESOLVE_HELP);
|
|
52883
|
+
if (args.includes("--path") && args.includes("--paths-from")) {
|
|
52884
|
+
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve accepts exactly one of --path or --paths-from");
|
|
52885
|
+
}
|
|
52886
|
+
if (args.includes("--paths-from")) {
|
|
52887
|
+
const read = await readResolvePathsFrom(args, cwd, deps);
|
|
52888
|
+
if ("problem" in read)
|
|
52889
|
+
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", read.problem);
|
|
52890
|
+
const model = loadNativeModelFromArchContext(cwd);
|
|
52891
|
+
const outcomes = resolveArchitectureOwnersForPaths(model.nodes, read.paths);
|
|
52892
|
+
return okEnvelope("resolve", { results: outcomes.map((outcome, index) => resolveResultData(read.paths[index], outcome)) });
|
|
52893
|
+
}
|
|
51521
52894
|
const path = readFlag(args, "--path");
|
|
51522
52895
|
if (!path)
|
|
51523
|
-
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve requires --path");
|
|
52896
|
+
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve requires --path or --paths-from");
|
|
51524
52897
|
if (!isRepoRelativePosixPath(path)) {
|
|
51525
52898
|
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve --path must be a repository-relative POSIX path");
|
|
51526
52899
|
}
|
|
51527
52900
|
const model = loadNativeModelFromArchContext(cwd);
|
|
51528
|
-
|
|
51529
|
-
if (outcome.status === "matched") {
|
|
51530
|
-
const node = outcome.node;
|
|
51531
|
-
return okEnvelope("resolve", {
|
|
51532
|
-
matched: true,
|
|
51533
|
-
ambiguous: false,
|
|
51534
|
-
stableId: node.id,
|
|
51535
|
-
kind: node.kind,
|
|
51536
|
-
name: node.name,
|
|
51537
|
-
...node.source ? { source: node.source } : {},
|
|
51538
|
-
...node.extensions ? { extensions: node.extensions } : {}
|
|
51539
|
-
});
|
|
51540
|
-
}
|
|
51541
|
-
if (outcome.status === "ambiguous") {
|
|
51542
|
-
return okEnvelope("resolve", {
|
|
51543
|
-
matched: false,
|
|
51544
|
-
ambiguous: true,
|
|
51545
|
-
path,
|
|
51546
|
-
candidates: outcome.candidates.map((node) => node.id)
|
|
51547
|
-
});
|
|
51548
|
-
}
|
|
51549
|
-
return okEnvelope("resolve", { matched: false, ambiguous: false, path });
|
|
52901
|
+
return okEnvelope("resolve", resolveResultData(path, resolveArchitectureOwnerForPath(model.nodes, path)));
|
|
51550
52902
|
}
|
|
51551
52903
|
case "help":
|
|
51552
52904
|
default:
|
|
@@ -51555,8 +52907,8 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
|
|
|
51555
52907
|
ok: true,
|
|
51556
52908
|
requestId: "help",
|
|
51557
52909
|
data: {
|
|
51558
|
-
commands: ["capabilities", "projection", "init", "sync", "validate", "context", "status", "daemon", "state", "repo", "landscape", "ledger", "book", "recommendations", "refactor", "explore", "prepare", "practices", "checkpoint", "hook", "hooks", "investigate", "agents", "jobs", "audit", "plan", "apply", "hash", "review", "complete", "github", "manifest", "config", "mcp", "install", "uninstall", "doctor", "update", "paths", "privacy-audit", "export", "import", "resolve", "tunnel"],
|
|
51559
|
-
examples: ["archctx init --name MyApp", "archctx projection run --request-json '{...}'", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body '<complete YAML>'", "archctx plan --id changeset.<id> --op delete_entity --path .archcontext/model/flows/<flow>.yaml --expected-hash sha256:<64-hex>", "archctx hash --path .archcontext/model/nodes/<node>.yaml", "archctx plan --id changeset.<id> --operations-file <operations.json>", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body-file <entity.yaml>", "archctx apply --id changeset.<id> --approved --expected-worktree-digest <draft.base.worktreeDigest>", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx recommendations list --status rejected", "archctx refactor scan --json", "archctx refactor verify --request-json '{...}' --json", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit consent", "archctx audit consent --revoke", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel"]
|
|
52910
|
+
commands: ["capabilities", "projection", "init", "sync", "validate", "context", "status", "daemon", "state", "repo", "landscape", "ledger", "book", "recommendations", "refactor", "explore", "prepare", "practices", "checkpoint", "hook", "hooks", "investigate", "agents", "jobs", "audit", "plan", "apply", "hash", "review", "complete", "github", "manifest", "config", "mcp", "install", "uninstall", "doctor", "update", "paths", "privacy-audit", "export", "import", "resolve", "tunnel", "flows", "deps"],
|
|
52911
|
+
examples: ["archctx init --name MyApp", "archctx projection run --request-json '{...}'", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body '<complete YAML>'", "archctx plan --id changeset.<id> --op delete_entity --path .archcontext/model/flows/<flow>.yaml --expected-hash sha256:<64-hex>", "archctx hash --path .archcontext/model/nodes/<node>.yaml", "archctx plan --id changeset.<id> --operations-file <operations.json>", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body-file <entity.yaml>", "archctx apply --id changeset.<id> --approved --expected-worktree-digest <draft.base.worktreeDigest>", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx recommendations list --status rejected", "archctx refactor scan --json", "archctx refactor verify --request-json '{...}' --json", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit consent", "archctx audit consent --revoke", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx docs anchors --json", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel", "archctx refactor scan --import-node-limit 100000 --json", "archctx resolve --paths-from changed-paths.txt", "archctx flows check --json", "archctx recommendations defer --id recommendation.<id> --import-node-limit 100000 --reason 'Scanned with the same limit.'", "archctx deps --json", "archctx deps --into capability.<id> --json"]
|
|
51560
52912
|
}
|
|
51561
52913
|
};
|
|
51562
52914
|
}
|
|
@@ -51965,7 +53317,10 @@ async function runRecommendationsCommand(args, cwd, daemon) {
|
|
|
51965
53317
|
if (!recommendationId || recommendationId.startsWith("--")) {
|
|
51966
53318
|
return errorEnvelope("recommendations.show", "AC_SCHEMA_INVALID", "recommendations show requires --id");
|
|
51967
53319
|
}
|
|
51968
|
-
|
|
53320
|
+
const importNodeLimit = recommendationsImportNodeLimit(args);
|
|
53321
|
+
if (importNodeLimit === null)
|
|
53322
|
+
return recommendationsImportNodeLimitError("show");
|
|
53323
|
+
return daemon.recommendations(cwd, { command: "show", recommendationId, ...importNodeLimit === undefined ? {} : { importNodeLimit } });
|
|
51969
53324
|
}
|
|
51970
53325
|
if (subcommand === "list") {
|
|
51971
53326
|
const status = readFlag(args, "--status");
|
|
@@ -51981,6 +53336,9 @@ async function runRecommendationsCommand(args, cwd, daemon) {
|
|
|
51981
53336
|
const reason = readFlag(args, "--reason");
|
|
51982
53337
|
if (!reason)
|
|
51983
53338
|
return errorEnvelope(`recommendations.${subcommand}`, "AC_SCHEMA_INVALID", `recommendations ${subcommand} requires --reason`);
|
|
53339
|
+
const importNodeLimit = recommendationsImportNodeLimit(args);
|
|
53340
|
+
if (importNodeLimit === null)
|
|
53341
|
+
return recommendationsImportNodeLimitError(subcommand);
|
|
51984
53342
|
const input = {
|
|
51985
53343
|
command: subcommand,
|
|
51986
53344
|
recommendationId,
|
|
@@ -51990,11 +53348,25 @@ async function runRecommendationsCommand(args, cwd, daemon) {
|
|
|
51990
53348
|
...readFlag(args, "--source") === undefined ? {} : { source: readFlag(args, "--source") },
|
|
51991
53349
|
...readFlag(args, "--expected-worktree-digest") === undefined ? {} : { expectedWorktreeDigest: readFlag(args, "--expected-worktree-digest") },
|
|
51992
53350
|
...readFlag(args, "--evidence-digest") === undefined ? {} : { evidenceDigest: readFlag(args, "--evidence-digest") },
|
|
53351
|
+
...importNodeLimit === undefined ? {} : { importNodeLimit },
|
|
51993
53352
|
...readFlag(args, "--agent-job-id") === undefined ? {} : { agentJobId: readFlag(args, "--agent-job-id") },
|
|
51994
53353
|
...readFlag(args, "--now") === undefined ? {} : { now: readFlag(args, "--now") }
|
|
51995
53354
|
};
|
|
51996
53355
|
return daemon.recommendations(cwd, input);
|
|
51997
53356
|
}
|
|
53357
|
+
function recommendationsImportNodeLimit(args) {
|
|
53358
|
+
const index = args.indexOf("--import-node-limit");
|
|
53359
|
+
if (index === -1)
|
|
53360
|
+
return;
|
|
53361
|
+
const raw = args[index + 1];
|
|
53362
|
+
return raw === undefined ? null : importNodeLimitValue(raw) ?? null;
|
|
53363
|
+
}
|
|
53364
|
+
function recommendationsImportNodeLimitError(subcommand) {
|
|
53365
|
+
return errorEnvelope(`recommendations.${subcommand}`, "AC_SCHEMA_INVALID", `recommendations ${subcommand} --import-node-limit must be a positive integer`);
|
|
53366
|
+
}
|
|
53367
|
+
function importNodeLimitValue(raw) {
|
|
53368
|
+
return /^[0-9]+$/.test(raw) && Number(raw) >= 1 ? Number(raw) : undefined;
|
|
53369
|
+
}
|
|
51998
53370
|
async function runRefactorCommand(args, cwd, runtime) {
|
|
51999
53371
|
const subcommand = args[0] ?? "scan";
|
|
52000
53372
|
if (subcommand !== "scan" && subcommand !== "record" && subcommand !== "verify") {
|
|
@@ -52004,17 +53376,27 @@ async function runRefactorCommand(args, cwd, runtime) {
|
|
|
52004
53376
|
try {
|
|
52005
53377
|
if (subcommand === "scan") {
|
|
52006
53378
|
const raw = refactorFlagValue(args, "--request-json");
|
|
52007
|
-
|
|
53379
|
+
const limitRaw = refactorFlagValue(args, "--import-node-limit");
|
|
53380
|
+
if (raw === undefined && limitRaw === undefined)
|
|
52008
53381
|
return (await runtime()).refactorScan(cwd, {});
|
|
52009
|
-
let request;
|
|
52010
|
-
|
|
52011
|
-
|
|
52012
|
-
|
|
52013
|
-
|
|
53382
|
+
let request = { schemaVersion: REFACTOR_REQUEST_SCHEMA_VERSION, scope: { kind: "repository" } };
|
|
53383
|
+
if (raw !== undefined) {
|
|
53384
|
+
try {
|
|
53385
|
+
request = JSON.parse(raw);
|
|
53386
|
+
} catch (error) {
|
|
53387
|
+
return errorEnvelope(surface, "AC_SCHEMA_INVALID", `refactor scan --request-json is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
53388
|
+
}
|
|
52014
53389
|
}
|
|
52015
53390
|
if (typeof request !== "object" || request === null || Array.isArray(request)) {
|
|
52016
53391
|
return errorEnvelope(surface, "AC_SCHEMA_INVALID", "refactor scan --request-json must be a RefactorRequestV1 object");
|
|
52017
53392
|
}
|
|
53393
|
+
if (limitRaw !== undefined) {
|
|
53394
|
+
const importNodeLimit = importNodeLimitValue(limitRaw);
|
|
53395
|
+
if (importNodeLimit === undefined) {
|
|
53396
|
+
return errorEnvelope(surface, "AC_SCHEMA_INVALID", "refactor scan --import-node-limit must be a positive integer");
|
|
53397
|
+
}
|
|
53398
|
+
request = { ...request, importNodeLimit };
|
|
53399
|
+
}
|
|
52018
53400
|
const issues = refactorRequestInvariantIssues(request);
|
|
52019
53401
|
if (issues.length > 0)
|
|
52020
53402
|
return errorEnvelope(surface, "AC_SCHEMA_INVALID", issues.join("; "));
|
|
@@ -52034,6 +53416,14 @@ async function runRefactorCommand(args, cwd, runtime) {
|
|
|
52034
53416
|
if (typeof request !== "object" || request === null || Array.isArray(request)) {
|
|
52035
53417
|
return errorEnvelope(surface, "AC_SCHEMA_INVALID", "refactor verify --request-json must be a RefactorVerificationRequestV1 object");
|
|
52036
53418
|
}
|
|
53419
|
+
const limitRaw = refactorFlagValue(args, "--import-node-limit");
|
|
53420
|
+
if (limitRaw !== undefined) {
|
|
53421
|
+
const importNodeLimit = importNodeLimitValue(limitRaw);
|
|
53422
|
+
if (importNodeLimit === undefined) {
|
|
53423
|
+
return errorEnvelope(surface, "AC_SCHEMA_INVALID", "refactor verify --import-node-limit must be a positive integer");
|
|
53424
|
+
}
|
|
53425
|
+
request = { ...request, importNodeLimit };
|
|
53426
|
+
}
|
|
52037
53427
|
const issues = refactorVerificationRequestInvariantIssues(request);
|
|
52038
53428
|
if (issues.length > 0)
|
|
52039
53429
|
return errorEnvelope(surface, "AC_SCHEMA_INVALID", issues.join("; "));
|
|
@@ -52060,7 +53450,7 @@ async function runDocsCommand(args, cwd, daemon) {
|
|
|
52060
53450
|
return runArchitectureDocsProjectionCommand2(args, cwd, daemon);
|
|
52061
53451
|
}
|
|
52062
53452
|
if (!["status", "resolve", "pin", "fetch", "purge"].includes(subcommand)) {
|
|
52063
|
-
return errorEnvelope("docs", "AC_SCHEMA_INVALID", "docs requires status|resolve|pin|fetch|purge|plan|apply|adopt|drift|clean");
|
|
53453
|
+
return errorEnvelope("docs", "AC_SCHEMA_INVALID", "docs requires status|resolve|pin|fetch|purge|plan|apply|adopt|drift|clean|anchors");
|
|
52064
53454
|
}
|
|
52065
53455
|
if (subcommand === "status") {
|
|
52066
53456
|
return daemon.docs(cwd, { command: "status", provider: "context7" });
|
|
@@ -52101,6 +53491,27 @@ async function runDocsCommand(args, cwd, daemon) {
|
|
|
52101
53491
|
all: args.includes("--all")
|
|
52102
53492
|
});
|
|
52103
53493
|
}
|
|
53494
|
+
function runDocsAnchorsCliCommand(args, cwd) {
|
|
53495
|
+
if (args.includes("--help") || args.includes("-h"))
|
|
53496
|
+
return okEnvelope("help", CLI_DOCS_ANCHORS_HELP);
|
|
53497
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
53498
|
+
const arg = args[index];
|
|
53499
|
+
if (arg === "--json")
|
|
53500
|
+
continue;
|
|
53501
|
+
if (arg === "--profile" && args[index + 1] !== undefined && !args[index + 1].startsWith("--")) {
|
|
53502
|
+
index += 1;
|
|
53503
|
+
continue;
|
|
53504
|
+
}
|
|
53505
|
+
return errorEnvelope("docs.anchors", "AC_SCHEMA_INVALID", `docs anchors does not accept ${arg}; use --help`);
|
|
53506
|
+
}
|
|
53507
|
+
let profile;
|
|
53508
|
+
try {
|
|
53509
|
+
profile = architectureProjectionProfile(args);
|
|
53510
|
+
} catch (error) {
|
|
53511
|
+
return errorEnvelope("docs.anchors", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
|
|
53512
|
+
}
|
|
53513
|
+
return runDocsAnchorsCommand(cwd, profile ? { profile } : {});
|
|
53514
|
+
}
|
|
52104
53515
|
async function runArchitectureDocsProjectionCommand2(args, cwd, daemon) {
|
|
52105
53516
|
const action = args[0] ?? "plan";
|
|
52106
53517
|
try {
|
|
@@ -52146,6 +53557,21 @@ async function runProjectionProtocolCommand2(args, cwd, runtime) {
|
|
|
52146
53557
|
}
|
|
52147
53558
|
return (await runtime()).projection(findRepositoryRoot2(cwd), { action, request });
|
|
52148
53559
|
}
|
|
53560
|
+
function runFlowsCommand(args, cwd) {
|
|
53561
|
+
if (args.includes("--help") || args.includes("-h"))
|
|
53562
|
+
return okEnvelope("flows", {
|
|
53563
|
+
command: "archctx flows check [--json]",
|
|
53564
|
+
description: "Check every declared flow selector against the CodeGraph index without rendering a projection.",
|
|
53565
|
+
statuses: "Each selector reports proven, no-direct-call, only-references, false-builtin-call, ambiguous-symbol, evidence-missing or evidence-truncated. Only proven binds a flow step.",
|
|
53566
|
+
writes: "none; runs in-process with no daemon, local store or task session. Like projection check, it runs codegraph sync first."
|
|
53567
|
+
});
|
|
53568
|
+
const formatAt = args.indexOf("--format");
|
|
53569
|
+
const unexpected = args.slice(1).filter((arg, index) => arg !== "--json" && (formatAt < 0 || index + 1 !== formatAt && index !== formatAt));
|
|
53570
|
+
if (args[0] !== "check" || unexpected.length > 0) {
|
|
53571
|
+
return errorEnvelope("flows", "AC_SCHEMA_INVALID", "flows requires check [--json]; use --help");
|
|
53572
|
+
}
|
|
53573
|
+
return runFlowSelectorCheckCommand(cwd);
|
|
53574
|
+
}
|
|
52149
53575
|
function acceptedArchitectureChange(args) {
|
|
52150
53576
|
const changeSetId = readFlag(args, "--accepted-change-set-id");
|
|
52151
53577
|
const eventId = readFlag(args, "--accepted-event-id");
|
|
@@ -52520,12 +53946,12 @@ function auditManifestGateRoot(cwd) {
|
|
|
52520
53946
|
}
|
|
52521
53947
|
}
|
|
52522
53948
|
function auditGithubIssuesEnabled(cwd) {
|
|
52523
|
-
const manifestPath =
|
|
52524
|
-
if (!
|
|
53949
|
+
const manifestPath = resolve29(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
|
|
53950
|
+
if (!existsSync28(manifestPath))
|
|
52525
53951
|
return false;
|
|
52526
53952
|
let raw;
|
|
52527
53953
|
try {
|
|
52528
|
-
raw =
|
|
53954
|
+
raw = readFileSync28(manifestPath, "utf8");
|
|
52529
53955
|
} catch {
|
|
52530
53956
|
return false;
|
|
52531
53957
|
}
|
|
@@ -52768,7 +54194,7 @@ async function runAuditCommand(args, cwd, runtime) {
|
|
|
52768
54194
|
const elapsedSeconds = Math.round((Date.now() - (deadline - pollTimeoutMs)) / 1000);
|
|
52769
54195
|
process.stderr.write(`archctx audit run: ${jobId} is still running (${elapsedSeconds}s elapsed)...
|
|
52770
54196
|
`);
|
|
52771
|
-
await
|
|
54197
|
+
await sleep2(pollIntervalMs);
|
|
52772
54198
|
}
|
|
52773
54199
|
return errorEnvelope("audit.run", "AC_PRECONDITION_FAILED", `archctx audit run: job ${jobId} has not reached a terminal status after ${Math.round(pollTimeoutMs / 1000)}s; this is not a failure, the daemon may still be running it \u2014 check later with: archctx audit list`);
|
|
52774
54200
|
}
|
|
@@ -52907,8 +54333,8 @@ async function runGithubCommand(args, cwd, deps) {
|
|
|
52907
54333
|
}
|
|
52908
54334
|
if (subcommand === "disconnect") {
|
|
52909
54335
|
const connection = await deps.githubConnectionReader?.readVerifiedConnection({ repositoryRoot: cwd });
|
|
52910
|
-
const localRecordRemoved =
|
|
52911
|
-
|
|
54336
|
+
const localRecordRemoved = existsSync28(connectionPath);
|
|
54337
|
+
rmSync15(connectionPath, { force: true });
|
|
52912
54338
|
return okEnvelope("github.disconnect", {
|
|
52913
54339
|
connected: connection !== undefined,
|
|
52914
54340
|
localRecordRemoved,
|
|
@@ -53187,7 +54613,7 @@ function readPullRequestNumber(args) {
|
|
|
53187
54613
|
return;
|
|
53188
54614
|
const value = Number(raw);
|
|
53189
54615
|
if (!Number.isInteger(value) || value < 1)
|
|
53190
|
-
throw new
|
|
54616
|
+
throw new CliUsageError("--pr must be a positive integer");
|
|
53191
54617
|
return value;
|
|
53192
54618
|
}
|
|
53193
54619
|
async function digestReviewChallenge(challenge) {
|
|
@@ -53219,7 +54645,7 @@ async function readReviewChallengeV2Arg(args, cwd, commandName = "github verify-
|
|
|
53219
54645
|
if (!inline && !challengePath)
|
|
53220
54646
|
return { ok: false, message: `${commandName} requires --challenge-json or --challenge-path` };
|
|
53221
54647
|
try {
|
|
53222
|
-
const raw = inline ??
|
|
54648
|
+
const raw = inline ?? readFileSync28(resolve29(cwd, challengePath), "utf8");
|
|
53223
54649
|
const parsed = JSON.parse(raw);
|
|
53224
54650
|
const attestation = await Promise.resolve().then(() => (init_src6(), {}));
|
|
53225
54651
|
const assertReviewChallengeV2 = assertReviewChallengeV22;
|
|
@@ -53600,7 +55026,7 @@ function readLatestPackageVersion(env) {
|
|
|
53600
55026
|
if (env[LATEST_VERSION_ENV]) {
|
|
53601
55027
|
return { source: "env", version: env[LATEST_VERSION_ENV] };
|
|
53602
55028
|
}
|
|
53603
|
-
const result = withLocalEgress("npm-update-check", () =>
|
|
55029
|
+
const result = withLocalEgress("npm-update-check", () => spawnSync4("npm", ["view", RELEASE_PACKAGE_NAME, "version", "--json"], {
|
|
53604
55030
|
encoding: "utf8",
|
|
53605
55031
|
timeout: NPM_VIEW_TIMEOUT_MS,
|
|
53606
55032
|
shell: false
|
|
@@ -53694,10 +55120,10 @@ function doctorSqlite(cwd) {
|
|
|
53694
55120
|
const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
|
|
53695
55121
|
return {
|
|
53696
55122
|
path,
|
|
53697
|
-
exists:
|
|
55123
|
+
exists: existsSync28(path),
|
|
53698
55124
|
migrations: productVersionManifest().runtime.sqliteMigrations,
|
|
53699
55125
|
legacyPath: paths.legacyLocalStorePath,
|
|
53700
|
-
legacyExists:
|
|
55126
|
+
legacyExists: existsSync28(paths.legacyLocalStorePath),
|
|
53701
55127
|
legacyLocalStore
|
|
53702
55128
|
};
|
|
53703
55129
|
}
|
|
@@ -53728,7 +55154,7 @@ function runtimePathsReport(cwd) {
|
|
|
53728
55154
|
};
|
|
53729
55155
|
}
|
|
53730
55156
|
function pathAccess(path) {
|
|
53731
|
-
const exists =
|
|
55157
|
+
const exists = existsSync28(path);
|
|
53732
55158
|
return {
|
|
53733
55159
|
path,
|
|
53734
55160
|
exists,
|
|
@@ -53789,14 +55215,14 @@ async function createOrStartRuntimeRpcClient(cwd) {
|
|
|
53789
55215
|
throw new RuntimeVersionUnsupportedError(fileIssue);
|
|
53790
55216
|
const client = createRuntimeRpcClientFromConnectionFile(cwd);
|
|
53791
55217
|
if (client) {
|
|
53792
|
-
const health = await client
|
|
53793
|
-
return;
|
|
53794
|
-
});
|
|
55218
|
+
const { health, unresponsivePid } = await probeDaemonHealth(client);
|
|
53795
55219
|
const healthIssue = runtimeRpcCompatibilityIssueFromHealth(cwd, client, health);
|
|
53796
55220
|
if (healthIssue)
|
|
53797
55221
|
throw new RuntimeVersionUnsupportedError(healthIssue);
|
|
53798
55222
|
if (health?.ok === true)
|
|
53799
55223
|
return client;
|
|
55224
|
+
if (unresponsivePid !== undefined)
|
|
55225
|
+
throw new Error(unresponsiveDaemonMessage(unresponsivePid));
|
|
53800
55226
|
recoverStaleDaemonControlFiles(cwd, { removeUnhealthyConnection: true });
|
|
53801
55227
|
} else {
|
|
53802
55228
|
recoverStaleDaemonControlFiles(cwd);
|
|
@@ -53878,16 +55304,16 @@ async function runDaemonCommand(args, cwd) {
|
|
|
53878
55304
|
return okEnvelope("daemon.status", incompatibleDaemonStatus(compatibilityIssue));
|
|
53879
55305
|
}
|
|
53880
55306
|
const recovery = recoverStaleDaemonControlFiles(cwd);
|
|
55307
|
+
const lockOwnerPid = liveDaemonLockOwnerPid(cwd);
|
|
53881
55308
|
return okEnvelope("daemon.status", {
|
|
53882
55309
|
running: false,
|
|
53883
55310
|
protocol: "http-loopback",
|
|
53884
55311
|
connectionPath: defaultDaemonConnectionPath(cwd),
|
|
55312
|
+
...lockOwnerPid === undefined ? {} : { lockOwnerPid, action: orphanedDaemonMessage(lockOwnerPid) },
|
|
53885
55313
|
...recoveryData(recovery)
|
|
53886
55314
|
});
|
|
53887
55315
|
}
|
|
53888
|
-
const health = await client
|
|
53889
|
-
return;
|
|
53890
|
-
});
|
|
55316
|
+
const { health, unresponsivePid } = await probeDaemonHealth(client);
|
|
53891
55317
|
const healthIssue = runtimeRpcCompatibilityIssueFromHealth(cwd, client, health);
|
|
53892
55318
|
if (healthIssue) {
|
|
53893
55319
|
return okEnvelope("daemon.status", incompatibleDaemonStatus(healthIssue));
|
|
@@ -53906,6 +55332,16 @@ async function runDaemonCommand(args, cwd) {
|
|
|
53906
55332
|
token: "stored-in-connection-file"
|
|
53907
55333
|
});
|
|
53908
55334
|
}
|
|
55335
|
+
if (unresponsivePid !== undefined) {
|
|
55336
|
+
return okEnvelope("daemon.status", {
|
|
55337
|
+
running: true,
|
|
55338
|
+
responding: false,
|
|
55339
|
+
pid: unresponsivePid,
|
|
55340
|
+
protocol: "http-loopback",
|
|
55341
|
+
connectionPath: client.connectionInfo().connectionPath,
|
|
55342
|
+
action: unresponsiveDaemonMessage(unresponsivePid)
|
|
55343
|
+
});
|
|
55344
|
+
}
|
|
53909
55345
|
const recovery = recoverStaleDaemonControlFiles(cwd, { removeUnhealthyConnection: true });
|
|
53910
55346
|
return okEnvelope("daemon.status", {
|
|
53911
55347
|
running: false,
|
|
@@ -53945,6 +55381,9 @@ async function startBackgroundDaemon(args, cwd) {
|
|
|
53945
55381
|
if (discovered.compatibilityIssue?.pidAlive) {
|
|
53946
55382
|
return errorEnvelope("daemon.start", "AC_RUNTIME_VERSION_UNSUPPORTED", runtimeVersionUnsupportedMessage(discovered.compatibilityIssue));
|
|
53947
55383
|
}
|
|
55384
|
+
if (discovered.unresponsivePid !== undefined) {
|
|
55385
|
+
return errorEnvelope("daemon.start", "AC_RUNTIME_UNAVAILABLE", unresponsiveDaemonMessage(discovered.unresponsivePid));
|
|
55386
|
+
}
|
|
53948
55387
|
if (discovered.info) {
|
|
53949
55388
|
return okEnvelope("daemon.start", {
|
|
53950
55389
|
running: true,
|
|
@@ -53986,7 +55425,7 @@ async function startBackgroundDaemon(args, cwd) {
|
|
|
53986
55425
|
child.unref();
|
|
53987
55426
|
const ready = await waitForDaemonReady(cwd, daemonStartTimeoutMs(args), () => childError !== undefined || childExit !== undefined && childExit.code !== DAEMON_OWNED_ELSEWHERE_EXIT_CODE);
|
|
53988
55427
|
if (!ready) {
|
|
53989
|
-
return errorEnvelope("daemon.start", "AC_RUNTIME_UNAVAILABLE", daemonStartFailureMessage(logPath, childExit, childError));
|
|
55428
|
+
return errorEnvelope("daemon.start", "AC_RUNTIME_UNAVAILABLE", daemonStartFailureMessage(cwd, logPath, child.pid, childExit, childError));
|
|
53990
55429
|
}
|
|
53991
55430
|
return okEnvelope("daemon.start", {
|
|
53992
55431
|
running: true,
|
|
@@ -54056,9 +55495,7 @@ async function discoverRunningDaemonInfo(cwd, options = {}) {
|
|
|
54056
55495
|
recovery: options.recoverStale ? recoverStaleDaemonControlFiles(cwd) : emptyRecovery(cwd)
|
|
54057
55496
|
};
|
|
54058
55497
|
}
|
|
54059
|
-
const health = await client
|
|
54060
|
-
return;
|
|
54061
|
-
});
|
|
55498
|
+
const { health, unresponsivePid } = await probeDaemonHealth(client);
|
|
54062
55499
|
const compatibilityIssue = runtimeRpcCompatibilityIssueFromHealth(cwd, client, health) ?? cliEntryStalenessIssue(cwd);
|
|
54063
55500
|
if (compatibilityIssue) {
|
|
54064
55501
|
return {
|
|
@@ -54070,6 +55507,9 @@ async function discoverRunningDaemonInfo(cwd, options = {}) {
|
|
|
54070
55507
|
if (health?.ok === true) {
|
|
54071
55508
|
return { info: client.connectionInfo(), compatibilityIssue: undefined, recovery: emptyRecovery(cwd) };
|
|
54072
55509
|
}
|
|
55510
|
+
if (unresponsivePid !== undefined) {
|
|
55511
|
+
return { info: undefined, compatibilityIssue: undefined, unresponsivePid, recovery: emptyRecovery(cwd) };
|
|
55512
|
+
}
|
|
54073
55513
|
return {
|
|
54074
55514
|
info: undefined,
|
|
54075
55515
|
compatibilityIssue: undefined,
|
|
@@ -54135,12 +55575,30 @@ function runtimeVersionUnsupportedMessage(issue) {
|
|
|
54135
55575
|
}
|
|
54136
55576
|
return `archctxd RPC version ${issue.received} is incompatible with this CLI (${issue.expected}); run ${issue.upgradeCommand} to replace the local daemon.`;
|
|
54137
55577
|
}
|
|
55578
|
+
async function probeDaemonHealth(client) {
|
|
55579
|
+
try {
|
|
55580
|
+
return { health: await client.health() };
|
|
55581
|
+
} catch (error) {
|
|
55582
|
+
const pid = client.connectionInfo().pid;
|
|
55583
|
+
const busy = error instanceof RuntimeRpcTransportError && error.code === "RPC_TIMEOUT" && typeof pid === "number" && isPidAlive(pid);
|
|
55584
|
+
return { health: undefined, ...busy ? { unresponsivePid: pid } : {} };
|
|
55585
|
+
}
|
|
55586
|
+
}
|
|
55587
|
+
function stopProcessCommand(pid) {
|
|
55588
|
+
return process.platform === "win32" ? `taskkill /PID ${pid}` : `kill -TERM ${pid}`;
|
|
55589
|
+
}
|
|
55590
|
+
function unresponsiveDaemonMessage(pid) {
|
|
55591
|
+
return `archctxd pid ${pid} is alive but did not answer its health check in time; it is likely busy, so its connection file was kept. Retry later, or stop it with \`${stopProcessCommand(pid)}\` and run the command again.`;
|
|
55592
|
+
}
|
|
55593
|
+
function orphanedDaemonMessage(pid) {
|
|
55594
|
+
return `the daemon lock names live pid ${pid}, but no connection file points to it; stop it with \`${stopProcessCommand(pid)}\`, then run the command again.`;
|
|
55595
|
+
}
|
|
54138
55596
|
async function waitForPidExit(pid, timeoutMs) {
|
|
54139
55597
|
const deadline = Date.now() + timeoutMs;
|
|
54140
55598
|
while (Date.now() < deadline) {
|
|
54141
55599
|
if (!isPidAlive(pid))
|
|
54142
55600
|
return true;
|
|
54143
|
-
await
|
|
55601
|
+
await sleep2(50);
|
|
54144
55602
|
}
|
|
54145
55603
|
return !isPidAlive(pid);
|
|
54146
55604
|
}
|
|
@@ -54169,9 +55627,11 @@ function daemonStartTimeoutMs(args) {
|
|
|
54169
55627
|
const parsed = Number(configured);
|
|
54170
55628
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : DAEMON_START_TIMEOUT_MS;
|
|
54171
55629
|
}
|
|
54172
|
-
function daemonStartFailureMessage(logPath, childExit, childError) {
|
|
55630
|
+
function daemonStartFailureMessage(cwd, logPath, childPid, childExit, childError) {
|
|
54173
55631
|
const childState = childError ? `childError=${childError.message}` : childExit ? `childExit=${childExit.code ?? "null"}/${childExit.signal ?? "none"}` : "childState=still-running";
|
|
54174
|
-
|
|
55632
|
+
const lockOwnerPid = existsSync28(defaultDaemonConnectionPath(cwd)) ? undefined : liveDaemonLockOwnerPid(cwd);
|
|
55633
|
+
const owner = lockOwnerPid === undefined || lockOwnerPid === childPid ? "" : `; ${orphanedDaemonMessage(lockOwnerPid)}`;
|
|
55634
|
+
return `archctxd did not become ready; ${childState}${owner}; log=${logPath}; logTail=${readFileTail(logPath)}`;
|
|
54175
55635
|
}
|
|
54176
55636
|
async function waitForDaemonReady(cwd, timeoutMs, shouldStop) {
|
|
54177
55637
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -54181,16 +55641,16 @@ async function waitForDaemonReady(cwd, timeoutMs, shouldStop) {
|
|
|
54181
55641
|
return ready;
|
|
54182
55642
|
if (shouldStop?.())
|
|
54183
55643
|
return;
|
|
54184
|
-
await
|
|
55644
|
+
await sleep2(50);
|
|
54185
55645
|
}
|
|
54186
55646
|
return;
|
|
54187
55647
|
}
|
|
54188
|
-
function
|
|
55648
|
+
function sleep2(ms) {
|
|
54189
55649
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
54190
55650
|
}
|
|
54191
55651
|
function readFileTail(path, maxBytes = 4096) {
|
|
54192
55652
|
try {
|
|
54193
|
-
const content =
|
|
55653
|
+
const content = readFileSync28(path, "utf8");
|
|
54194
55654
|
return content.slice(Math.max(0, content.length - maxBytes)).replace(/\s+/g, " ").trim();
|
|
54195
55655
|
} catch {
|
|
54196
55656
|
return "<unavailable>";
|
|
@@ -54254,19 +55714,109 @@ function resolveCommandExitCode(result) {
|
|
|
54254
55714
|
if (!result || result.ok !== true)
|
|
54255
55715
|
return 1;
|
|
54256
55716
|
const data = result.data;
|
|
55717
|
+
if (Array.isArray(data?.results))
|
|
55718
|
+
return data.results.some((entry) => entry.ambiguous === true) ? 2 : 0;
|
|
54257
55719
|
if (data?.matched === true)
|
|
54258
55720
|
return 0;
|
|
54259
55721
|
if (data?.ambiguous === true)
|
|
54260
55722
|
return 2;
|
|
54261
55723
|
return 1;
|
|
54262
55724
|
}
|
|
55725
|
+
var CLI_RESOLVE_HELP = {
|
|
55726
|
+
command: "archctx resolve (--path <repo-relative path> | --paths-from <file> | --paths-from -)",
|
|
55727
|
+
rule: "ADR-0043. source.exclude applies first. When every matching node lies on one parent chain, the deepest node owns the path; refactor scan uses the same rule. When matching nodes lie on different chains, an ancestor yields to its own descendant, then the longest literal source.include prefix wins, and an equal-length tie is ambiguous.",
|
|
55728
|
+
batch: "--paths-from reads one repository-relative POSIX path per line from a file (relative to the current directory) or from stdin (-). Blank lines are ignored and surrounding whitespace is trimmed. The model loads once. data.results has one entry per input path, in input order. The command reads no daemon state and no CodeGraph index.",
|
|
55729
|
+
result: "Each result carries path, matched, ambiguous and owningCapabilityId. A match adds stableId, kind, name, source and extensions of the most specific node. An ambiguous result adds candidates. owningCapabilityId is the nearest kind: capability node on the owner's parent chain (the owner itself when it is a capability), or null. For an ambiguous result it is set only when all candidates share one owning capability.",
|
|
55730
|
+
exitCodes: "--path: 0 matched, 1 no match or error, 2 ambiguous. --paths-from: 0 when no path is ambiguous (unowned paths included), 2 when any path is ambiguous, 1 on error."
|
|
55731
|
+
};
|
|
55732
|
+
function resolveResultData(path, outcome) {
|
|
55733
|
+
if (outcome.status === "matched") {
|
|
55734
|
+
const node = outcome.node;
|
|
55735
|
+
return {
|
|
55736
|
+
path,
|
|
55737
|
+
matched: true,
|
|
55738
|
+
ambiguous: false,
|
|
55739
|
+
stableId: node.id,
|
|
55740
|
+
kind: node.kind,
|
|
55741
|
+
name: node.name,
|
|
55742
|
+
owningCapabilityId: outcome.owningCapabilityId,
|
|
55743
|
+
...node.source ? { source: node.source } : {},
|
|
55744
|
+
...node.extensions ? { extensions: node.extensions } : {}
|
|
55745
|
+
};
|
|
55746
|
+
}
|
|
55747
|
+
if (outcome.status === "ambiguous") {
|
|
55748
|
+
return { path, matched: false, ambiguous: true, candidates: outcome.candidates.map((node) => node.id), owningCapabilityId: outcome.owningCapabilityId };
|
|
55749
|
+
}
|
|
55750
|
+
return { path, matched: false, ambiguous: false, owningCapabilityId: null };
|
|
55751
|
+
}
|
|
55752
|
+
async function readResolvePathsFrom(args, cwd, deps) {
|
|
55753
|
+
const source = readFlag(args, "--paths-from");
|
|
55754
|
+
if (source === undefined || source.startsWith("--"))
|
|
55755
|
+
return { problem: "resolve --paths-from requires a file path or - for stdin" };
|
|
55756
|
+
let content;
|
|
55757
|
+
try {
|
|
55758
|
+
content = source === "-" ? await (deps.readStdin ? deps.readStdin() : readFileSync28(0, "utf8")) : readFileSync28(resolve29(cwd, source), "utf8");
|
|
55759
|
+
} catch (error) {
|
|
55760
|
+
return { problem: `resolve --paths-from ${source} is not readable: ${error instanceof Error ? error.message : String(error)}` };
|
|
55761
|
+
}
|
|
55762
|
+
const paths = [];
|
|
55763
|
+
for (const [index, line] of content.split(`
|
|
55764
|
+
`).entries()) {
|
|
55765
|
+
const path = line.trim();
|
|
55766
|
+
if (path === "")
|
|
55767
|
+
continue;
|
|
55768
|
+
if (!isRepoRelativePosixPath(path))
|
|
55769
|
+
return { problem: `resolve --paths-from line ${index + 1} must be a repository-relative POSIX path: ${path}` };
|
|
55770
|
+
paths.push(path);
|
|
55771
|
+
}
|
|
55772
|
+
return { paths };
|
|
55773
|
+
}
|
|
54263
55774
|
var CLI_PLAN_ENTITY_FLAGS = ["--op", "--path", "--expected-hash", "--body", "--body-file"];
|
|
55775
|
+
var CLI_DEPS_HELP = {
|
|
55776
|
+
command: "archctx deps [--from <node-id>] [--into <node-id>] [--import-node-limit <n>] [--edge-limit <n>] [--json]",
|
|
55777
|
+
description: "Read-only capability x capability import matrix. The command reads the model files, the HEAD tree and the CodeGraph index. It writes nothing and needs no daemon.",
|
|
55778
|
+
rule: "Each file gets its owners from the parent-chain rule refactor scan uses. Each owner rolls up to the nearest kind: capability node on its parent chain. An edge counts in data.matrix only when both files roll up to exactly one capability. Other edges count in data.imports.unattributed by cause.",
|
|
55779
|
+
filters: "--from <node-id> keeps edges whose importing file the node or a descendant owns. --into <node-id> keeps edges whose imported file the node or a descendant owns. An unknown node id is AC_SCHEMA_INVALID.",
|
|
55780
|
+
imports: "data.imports reports coverage, reasonCodes, truncated, importNodeCount, importNodeLimit and importNodeLimitExceeded from the CodeGraph import read. A missing or stale index gives coverage unknown and an empty matrix. --import-node-limit raises the read cap (default 50000).",
|
|
55781
|
+
calls: "Call counts are not available in v1. data.calls.coverage is unknown with reason call-edges-not-exposed, and every cell has calls: null.",
|
|
55782
|
+
edges: "data.edges lists the attributed file-level edges behind the matrix, sorted by path, up to --edge-limit (default 200). data.edges.count is the full total."
|
|
55783
|
+
};
|
|
55784
|
+
function runDepsCommand(args, cwd) {
|
|
55785
|
+
if (args.includes("--help") || args.includes("-h"))
|
|
55786
|
+
return okEnvelope("deps", CLI_DEPS_HELP);
|
|
55787
|
+
const importNodeLimit = readOptionalPositiveIntegerFlag(args, "--import-node-limit", "deps");
|
|
55788
|
+
if (!importNodeLimit.ok)
|
|
55789
|
+
return importNodeLimit.envelope;
|
|
55790
|
+
const edgeLimit = readOptionalNonNegativeIntegerFlag(args, "--edge-limit", "deps");
|
|
55791
|
+
if (!edgeLimit.ok)
|
|
55792
|
+
return edgeLimit.envelope;
|
|
55793
|
+
const from = readFlag(args, "--from");
|
|
55794
|
+
const into = readFlag(args, "--into");
|
|
55795
|
+
for (const [flag, value] of [["--from", from], ["--into", into]]) {
|
|
55796
|
+
if (args.includes(flag) && (value === undefined || value.startsWith("--"))) {
|
|
55797
|
+
return errorEnvelope("deps", "AC_SCHEMA_INVALID", `deps ${flag} requires a node id`);
|
|
55798
|
+
}
|
|
55799
|
+
}
|
|
55800
|
+
try {
|
|
55801
|
+
return okEnvelope("deps", readCapabilityDependencies(findRepositoryRoot2(cwd), {
|
|
55802
|
+
...from === undefined ? {} : { from },
|
|
55803
|
+
...into === undefined ? {} : { into },
|
|
55804
|
+
...importNodeLimit.value === undefined ? {} : { importNodeLimit: importNodeLimit.value },
|
|
55805
|
+
...edgeLimit.value === undefined ? {} : { edgeLimit: edgeLimit.value }
|
|
55806
|
+
}));
|
|
55807
|
+
} catch (error) {
|
|
55808
|
+
if (error instanceof CapabilityDependenciesError)
|
|
55809
|
+
return errorEnvelope("deps", error.code, error.message);
|
|
55810
|
+
throw error;
|
|
55811
|
+
}
|
|
55812
|
+
}
|
|
54264
55813
|
var CLI_PLAN_HELP = {
|
|
54265
|
-
command: "archctx plan --id <changeset.id> (--op <create_entity|update_entity_fields|delete_entity> --path <file> [--expected-hash sha256:<64-hex>] [--body <yaml> | --body - | --body-file <path>] | --operations-file <operations.json
|
|
54266
|
-
operations: "--operations-file takes a repository-relative path to a JSON array of operations in the archcontext_plan_update MCP shape and plans them as one ChangeSet; it cannot be combined with --op, --path, --expected-hash, --body or --body-file.",
|
|
54267
|
-
body: "--body is the complete new YAML document, not a field patch. Give exactly one of --body <yaml>, --body - (stdin) or --body-file <path>. --body-file and --operations-file read only repository-relative files inside the repository (no absolute or .. paths, no symlinks); content from outside the repository must come through --body -
|
|
55814
|
+
command: "archctx plan --id <changeset.id> (--op <create_entity|update_entity_fields|delete_entity> --path <file> [--expected-hash sha256:<64-hex>] [--body <yaml> | --body - | --body-file <path>] | --operations-file <operations.json | ->)",
|
|
55815
|
+
operations: "--operations-file takes a repository-relative path to a JSON array of operations in the archcontext_plan_update MCP shape, or - to read that array from stdin, and plans them as one ChangeSet; it cannot be combined with --op, --path, --expected-hash, --body or --body-file.",
|
|
55816
|
+
body: "--body is the complete new YAML document, not a field patch. Give exactly one of --body <yaml>, --body - (stdin) or --body-file <path>. --body-file and --operations-file read only repository-relative files inside the repository (no absolute or .. paths, no symlinks); content from outside the repository must come through stdin (--body - or --operations-file -). delete_entity accepts no body.",
|
|
54268
55817
|
expectedHash: "digestJson({ body }) of the file's current UTF-8 contents, not sha256sum of the file. Read it with `archctx hash --path <file>`; use `missing` (the create default) for a new file.",
|
|
54269
55818
|
drafts: "A draft lives in daemon memory. Planning the same --id again replaces it, and a daemon restart between plan and apply drops it: apply then fails with `Unknown ChangeSet`; plan again.",
|
|
55819
|
+
ownershipDelta: "When the plan writes a node file, data.preview.ownershipDelta compares file ownership before and after the planned model over the files the HEAD tree carries, with the parent-chain rule refactor scan uses. becameOwned, becameContested, ownerChanged and becameUnowned each give count, up to 50 files with before and after owners, and omittedCount. status unavailable gives the reason, for example a planned body that does not parse. Read-only.",
|
|
54270
55820
|
next: "archctx apply --id <changeset.id> --approved --expected-worktree-digest <draft.base.worktreeDigest>"
|
|
54271
55821
|
};
|
|
54272
55822
|
var CLI_HASH_HELP = {
|
|
@@ -54295,7 +55845,7 @@ function readRepositoryInputFile(cwd, flag, file) {
|
|
|
54295
55845
|
const absolute = assertPathHasNoSymlinkSegments2(cwd, file);
|
|
54296
55846
|
if (!statSync10(absolute).isFile())
|
|
54297
55847
|
return { problem: `plan ${flag} ${file} is not a regular file` };
|
|
54298
|
-
return { content:
|
|
55848
|
+
return { content: readFileSync28(absolute, "utf8") };
|
|
54299
55849
|
} catch (error) {
|
|
54300
55850
|
return { problem: `plan ${flag} ${file} is not readable: ${error instanceof Error ? error.message : String(error)}` };
|
|
54301
55851
|
}
|
|
@@ -54313,10 +55863,10 @@ function runHashCommand(args, cwd) {
|
|
|
54313
55863
|
} catch (error) {
|
|
54314
55864
|
return errorEnvelope("hash", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
|
|
54315
55865
|
}
|
|
54316
|
-
if (!
|
|
55866
|
+
if (!existsSync28(absolute) || !statSync10(absolute).isFile()) {
|
|
54317
55867
|
return errorEnvelope("hash", "AC_SCHEMA_INVALID", `hash --path is not an existing file: ${path}`);
|
|
54318
55868
|
}
|
|
54319
|
-
return okEnvelope("hash", { path, hash: digestJson({ body:
|
|
55869
|
+
return okEnvelope("hash", { path, hash: digestJson({ body: readFileSync28(absolute, "utf8") }) });
|
|
54320
55870
|
}
|
|
54321
55871
|
async function readCliPlanOperations(args, cwd, deps) {
|
|
54322
55872
|
if (args.includes("--operations-file")) {
|
|
@@ -54326,7 +55876,7 @@ async function readCliPlanOperations(args, cwd, deps) {
|
|
|
54326
55876
|
const file = readFlag(args, "--operations-file");
|
|
54327
55877
|
if (!file)
|
|
54328
55878
|
return "plan --operations-file requires a path";
|
|
54329
|
-
const read = readRepositoryInputFile(cwd, "--operations-file", file);
|
|
55879
|
+
const read = file === "-" ? { content: await (deps.readStdin ? deps.readStdin() : readFileSync28(0, "utf8")) } : readRepositoryInputFile(cwd, "--operations-file", file);
|
|
54330
55880
|
if ("problem" in read)
|
|
54331
55881
|
return read.problem;
|
|
54332
55882
|
let parsed;
|
|
@@ -54388,7 +55938,7 @@ async function readCliPlanBody(args, cwd, deps) {
|
|
|
54388
55938
|
return { problem: "plan --body requires a value (use - to read stdin)" };
|
|
54389
55939
|
if (inline !== "-")
|
|
54390
55940
|
return inline;
|
|
54391
|
-
return deps.readStdin ? deps.readStdin() :
|
|
55941
|
+
return deps.readStdin ? deps.readStdin() : readFileSync28(0, "utf8");
|
|
54392
55942
|
}
|
|
54393
55943
|
function readFlag(args, flag) {
|
|
54394
55944
|
const index = args.indexOf(flag);
|
|
@@ -54399,7 +55949,7 @@ function readFlag(args, flag) {
|
|
|
54399
55949
|
function requireFlag(args, flag) {
|
|
54400
55950
|
const value = readFlag(args, flag);
|
|
54401
55951
|
if (!value)
|
|
54402
|
-
throw new
|
|
55952
|
+
throw new CliUsageError(`${flag} is required`);
|
|
54403
55953
|
return value;
|
|
54404
55954
|
}
|
|
54405
55955
|
function copyOptionalFlag(args, flag) {
|
|
@@ -54424,11 +55974,11 @@ function explorerProjectionQueryV2FromCli(args) {
|
|
|
54424
55974
|
const expectedObservedFactsDigest = readFlag(args, "--expected-observed-facts-digest");
|
|
54425
55975
|
const expectedRequired = [expectedHeadSha, expectedWorktreeDigest, expectedGraphDigest];
|
|
54426
55976
|
if (expectedRequired.some(Boolean) && !expectedRequired.every(Boolean)) {
|
|
54427
|
-
throw new
|
|
55977
|
+
throw new CliUsageError("--expected-head-sha, --expected-worktree-digest, and --expected-graph-digest must be provided together");
|
|
54428
55978
|
}
|
|
54429
55979
|
const viewId = readFlag(args, "--view") ?? "system-map";
|
|
54430
55980
|
if (!EXPLORER_VIEW_IDS.includes(viewId))
|
|
54431
|
-
throw new
|
|
55981
|
+
throw new CliUsageError(`unsupported Explorer view: ${viewId}`);
|
|
54432
55982
|
return {
|
|
54433
55983
|
schemaVersion: "archcontext.explorer-projection-query/v2",
|
|
54434
55984
|
viewId,
|
|
@@ -54451,6 +56001,44 @@ function explorerProjectionQueryV2FromCli(args) {
|
|
|
54451
56001
|
}
|
|
54452
56002
|
};
|
|
54453
56003
|
}
|
|
56004
|
+
function cliFailureEnvelope(error) {
|
|
56005
|
+
if (error instanceof CliUsageError)
|
|
56006
|
+
return errorEnvelope("cli", "AC_SCHEMA_INVALID", error.message);
|
|
56007
|
+
return errorEnvelope("cli", "AC_RUNTIME_UNAVAILABLE", error instanceof Error ? error.message : String(error));
|
|
56008
|
+
}
|
|
56009
|
+
if (__require.main == __require.module) {
|
|
56010
|
+
if (command === "capabilities") {
|
|
56011
|
+
const result = await runCli(command, args, process.cwd());
|
|
56012
|
+
if ("ok" in result) {
|
|
56013
|
+
process.stdout.write(`${renderResult(result, "json")}
|
|
56014
|
+
`);
|
|
56015
|
+
if (result.ok === false)
|
|
56016
|
+
process.exitCode = 1;
|
|
56017
|
+
} else {
|
|
56018
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
56019
|
+
`);
|
|
56020
|
+
}
|
|
56021
|
+
} else if (command === "mcp" && args.length === 0) {
|
|
56022
|
+
await runStdioMcpLoop(stdinLines(), (line) => process.stdout.write(`${line}
|
|
56023
|
+
`), (line) => process.stderr.write(`${line}
|
|
56024
|
+
`), { runtimeResolver: (root) => createOrStartRuntimeRpcClient(root) });
|
|
56025
|
+
} else if (command === "daemon" && args[0] === "start" && args.includes("--foreground")) {
|
|
56026
|
+
await runForegroundDaemon(process.cwd(), args).catch((error) => {
|
|
56027
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56028
|
+
process.stderr.write(`${message}
|
|
56029
|
+
`);
|
|
56030
|
+
process.exitCode = isDaemonOwnedElsewhereError(message) ? DAEMON_OWNED_ELSEWHERE_EXIT_CODE : 1;
|
|
56031
|
+
});
|
|
56032
|
+
} else {
|
|
56033
|
+
const result = await runCli(command, args, process.cwd()).catch(cliFailureEnvelope);
|
|
56034
|
+
process.stdout.write(`${renderResult(result, readFlag(args, "--format") ?? "json")}
|
|
56035
|
+
`);
|
|
56036
|
+
if (result.ok === false)
|
|
56037
|
+
process.exitCode = 1;
|
|
56038
|
+
if (command === "resolve")
|
|
56039
|
+
process.exitCode = resolveCommandExitCode(result);
|
|
56040
|
+
}
|
|
56041
|
+
}
|
|
54454
56042
|
export {
|
|
54455
56043
|
resolveCommandExitCode,
|
|
54456
56044
|
runCapabilitiesCommand,
|