@aiwg/cli 2026.10.5 → 2026.10.6
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/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
- package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
- package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
- package/agentic/code/providers/model-capabilities.v1.json +74 -50
- package/bin/aiwg.mjs +3 -1
- package/dist/src/artifacts/browser-export.js +1 -0
- package/dist/src/artifacts/citation-parser.js +8 -1
- package/dist/src/artifacts/cli.js +57 -6
- package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
- package/dist/src/artifacts/fortemi-core-sync.js +26 -8
- package/dist/src/artifacts/graph-query.js +18 -8
- package/dist/src/artifacts/index-adapters.js +339 -0
- package/dist/src/artifacts/index-builder.js +143 -14
- package/dist/src/artifacts/index-files.js +15 -4
- package/dist/src/artifacts/index-reader.js +44 -0
- package/dist/src/artifacts/index-refresh.js +114 -55
- package/dist/src/artifacts/index-status.js +20 -5
- package/dist/src/artifacts/memory-maintenance.js +503 -0
- package/dist/src/artifacts/prebuilt-relations.js +3 -1
- package/dist/src/artifacts/query-engine.js +263 -108
- package/dist/src/artifacts/relations-builder.js +2 -2
- package/dist/src/artifacts/source-graph.js +2 -2
- package/dist/src/artifacts/types.js +9 -1
- package/dist/src/cli/handlers/decision.js +19 -1
- package/dist/src/cli/handlers/guided-setup.js +470 -0
- package/dist/src/cli/handlers/help.js +2 -2
- package/dist/src/cli/handlers/housekeeper.js +1110 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/init.js +41 -6
- package/dist/src/cli/handlers/local-executor.js +5 -3
- package/dist/src/cli/handlers/mc.js +509 -53
- package/dist/src/cli/handlers/models.js +32 -2
- package/dist/src/cli/handlers/ralph-launcher.js +76 -4
- package/dist/src/cli/handlers/rules.js +1 -1
- package/dist/src/cli/handlers/script-runner.js +73 -2
- package/dist/src/cli/handlers/serve.js +5 -3
- package/dist/src/cli/handlers/sessions.js +1 -0
- package/dist/src/cli/handlers/setup.js +428 -1
- package/dist/src/cli/handlers/skill-lint.js +1 -1
- package/dist/src/cli/handlers/subcommands.js +118 -8
- package/dist/src/cli/handlers/use.js +279 -93
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/prompt-utils.js +8 -0
- package/dist/src/cli/router.js +16 -1
- package/dist/src/config/aiwg-config.js +42 -0
- package/dist/src/config/cli.js +7 -3
- package/dist/src/config/decision-config.js +14 -1
- package/dist/src/config/native-sandbox-warnings.js +25 -0
- package/dist/src/contributors/validation.js +6 -5
- package/dist/src/decision/admission.js +7 -3
- package/dist/src/decision/benchmark.js +469 -0
- package/dist/src/decision/ledger.js +22 -3
- package/dist/src/decision/pricing.js +147 -0
- package/dist/src/decision/qualification/gate-evidence.js +18 -15
- package/dist/src/decision/qualification/load-staged.js +539 -0
- package/dist/src/decision/qualification/load.js +109 -33
- package/dist/src/decision/qualification/runner.js +5 -2
- package/dist/src/decision/review/service.js +6 -4
- package/dist/src/decision/review/validate.js +23 -2
- package/dist/src/decision/savings.js +8 -3
- package/dist/src/discovery/index.js +492 -0
- package/dist/src/discovery/interview.js +233 -0
- package/dist/src/discovery/plan-apply.js +571 -0
- package/dist/src/discovery/plan-store.js +171 -0
- package/dist/src/discovery/plan-types.js +2 -0
- package/dist/src/discovery/plan-validation.js +139 -0
- package/dist/src/discovery/types.js +2 -0
- package/dist/src/extensions/commands/definitions.js +49 -9
- package/dist/src/extensions/manifest.js +71 -44
- package/dist/src/extensions/project-local-discovery.js +1 -1
- package/dist/src/extensions/project-quickref.js +18 -17
- package/dist/src/extensions/validation.js +32 -29
- package/dist/src/housekeeper/daemon-config.js +148 -0
- package/dist/src/housekeeper/ipc.js +84 -0
- package/dist/src/housekeeper/lifecycle.js +148 -0
- package/dist/src/housekeeper/platform.js +35 -0
- package/dist/src/housekeeper/status.js +70 -0
- package/dist/src/housekeeper/units.js +114 -0
- package/dist/src/jobs/flow.js +22 -21
- package/dist/src/lint/rule-activation.js +20 -5
- package/dist/src/lint/runner.js +1 -1
- package/dist/src/mcp/cli.mjs +5 -6
- package/dist/src/mcp/config-file.mjs +6 -2
- package/dist/src/mcp/config-layers.mjs +23 -7
- package/dist/src/mcp/profiles.js +18 -31
- package/dist/src/mcp/profiles.mjs +18 -32
- package/dist/src/mcp/registry.js +7 -27
- package/dist/src/mcp/registry.mjs +7 -28
- package/dist/src/mcp/server.mjs +34 -1
- package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
- package/dist/src/mcp/tools/command-run.mjs +3 -2
- package/dist/src/mcp/tools/decision.mjs +49 -48
- package/dist/src/mcp/tools/discovery.mjs +46 -21
- package/dist/src/mcp/tools/orchestration.mjs +22 -22
- package/dist/src/mcp/tools/subsystems.mjs +9 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +17 -4
- package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
- package/dist/src/memory/audit.js +719 -0
- package/dist/src/memory/canonical-context.js +392 -13
- package/dist/src/memory/canonical-maintenance.js +95 -0
- package/dist/src/memory/cli-result.js +22 -0
- package/dist/src/memory/cli.js +176 -0
- package/dist/src/memory/context-pack.js +57 -6
- package/dist/src/memory/cost.js +512 -0
- package/dist/src/memory/firewall-classification.d.mts +7 -0
- package/dist/src/memory/firewall-classification.mjs +32 -0
- package/dist/src/memory/index.js +8 -0
- package/dist/src/memory/line-maintenance.js +68 -0
- package/dist/src/memory/maintenance-cli.js +286 -0
- package/dist/src/memory/maintenance-contract.js +136 -0
- package/dist/src/memory/maintenance-index.js +18 -0
- package/dist/src/memory/maintenance-owner.js +2 -0
- package/dist/src/memory/maintenance-record.js +138 -0
- package/dist/src/memory/maintenance.js +254 -0
- package/dist/src/memory/provider-memory-adapter.js +154 -0
- package/dist/src/memory/recommend-cli.js +83 -0
- package/dist/src/memory/recommend.js +269 -0
- package/dist/src/memory/scheduled.js +81 -0
- package/dist/src/memory/taxonomy.js +44 -0
- package/dist/src/mission-protocol/codecs.js +19 -9
- package/dist/src/models/config-loader.js +11 -1
- package/dist/src/models/model-capabilities.v1.json +74 -50
- package/dist/src/models/provider-policy.js +125 -62
- package/dist/src/models/resolver.js +45 -8
- package/dist/src/models/worker-installation.d.mts +7 -0
- package/dist/src/models/worker-installation.mjs +169 -37
- package/dist/src/models/wrapper-route.js +1 -1
- package/dist/src/output-modes/registry.js +8 -7
- package/dist/src/plugin/metadata-validator.js +1 -1
- package/dist/src/providers/deploy-version.d.mts +1 -0
- package/dist/src/providers/deploy-version.mjs +17 -0
- package/dist/src/providers/provider-definitions.js +19 -16
- package/dist/src/providers/skill-ownership.d.mts +11 -2
- package/dist/src/providers/skill-ownership.mjs +35 -17
- package/dist/src/rules/activation.d.mts +11 -2
- package/dist/src/rules/activation.mjs +82 -6
- package/dist/src/rules/inventory.js +1 -1
- package/dist/src/rules/rule-frontmatter.schema.json +2 -0
- package/dist/src/schema/zod-validation.d.mts +6 -0
- package/dist/src/schema/zod-validation.mjs +50 -0
- package/dist/src/serve/executor-registry.js +297 -20
- package/dist/src/serve/executor-token.d.mts +1 -0
- package/dist/src/serve/executor-token.mjs +8 -0
- package/dist/src/serve/shared-host-scheduler.js +1102 -31
- package/dist/src/serve/worker-invocation.js +114 -15
- package/dist/src/sessions/adapters/claude.js +19 -18
- package/dist/src/sessions/adapters/codex.js +10 -9
- package/dist/src/sessions/adapters/copilot.js +14 -13
- package/dist/src/sessions/adapters/cursor.js +19 -18
- package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
- package/dist/src/sessions/adapters/factory.js +11 -10
- package/dist/src/sessions/adapters/generic.js +32 -23
- package/dist/src/sessions/adapters/grokbot.js +35 -35
- package/dist/src/sessions/adapters/hermes.js +20 -19
- package/dist/src/sessions/adapters/muse.js +50 -49
- package/dist/src/sessions/adapters/omp.js +5 -4
- package/dist/src/sessions/adapters/openclaw.js +30 -29
- package/dist/src/sessions/adapters/opencode.js +26 -26
- package/dist/src/sessions/adapters/openhuman.js +24 -23
- package/dist/src/sessions/adapters/pi.js +5 -4
- package/dist/src/sessions/adapters/windsurf.js +8 -8
- package/dist/src/sessions/analytics.js +10 -9
- package/dist/src/sessions/candidates.js +7 -6
- package/dist/src/sessions/contracts.js +63 -53
- package/dist/src/sessions/output-registration.js +11 -10
- package/dist/src/sessions/workspace-discovery.js +3 -4
- package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +42 -6
- package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +106 -10
- package/dist/src/update/checker.d.mts +4 -0
- package/dist/src/update/checker.mjs +14 -0
- package/dist/src/writing/exemplar-selection.js +6 -6
- package/dist/src/writing/voice-evaluation.js +24 -23
- package/dist/src/writing/writer-migration.js +14 -13
- package/dist/src/writing/writer-profile.js +19 -18
- package/dist/src/writing/writing-brief.js +18 -17
- package/dist/src/writing/writing-receipt.js +47 -46
- package/package.json +5 -2
- package/schemas/decision/DecisionReview.schema.json +78 -1
- package/schemas/mission-v1.schema.json +3 -0
- package/tools/agents/deploy-agents.mjs +67 -39
- package/tools/agents/providers/antigravity.mjs +6 -1
- package/tools/agents/providers/base.mjs +19 -13
- package/tools/agents/providers/claude.mjs +1 -1
- package/tools/agents/providers/codex.mjs +50 -56
- package/tools/agents/providers/copilot.mjs +8 -2
- package/tools/agents/providers/cursor.mjs +1 -1
- package/tools/agents/providers/deepseek-harness.mjs +1 -1
- package/tools/agents/providers/factory.mjs +7 -1
- package/tools/agents/providers/grok-build.mjs +18 -2
- package/tools/agents/providers/grokbot.mjs +1 -1
- package/tools/agents/providers/hermes.mjs +7 -13
- package/tools/agents/providers/muse-hooks.mjs +21 -14
- package/tools/agents/providers/muse.mjs +7 -2
- package/tools/agents/providers/omp.mjs +28 -4
- package/tools/agents/providers/opencode.mjs +14 -5
- package/tools/agents/providers/openhuman.mjs +1 -1
- package/tools/agents/providers/pi.mjs +1 -1
- package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
- package/tools/agents/providers/rule-delivery.mjs +263 -71
- package/tools/agents/providers/windsurf.mjs +7 -0
- package/tools/cli/workspace-status.mjs +5 -0
- package/tools/commands/deploy-prompts-codex.mjs +11 -8
- package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
- package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
- package/tools/ralph-external/process-monitor.mjs +7 -4
- package/tools/ralph-external/session-launcher.mjs +280 -32
- package/tools/skills/deploy-skills-codex.mjs +11 -7
- package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
|
@@ -18,7 +18,7 @@ import { parseDecisionJson, parseDecisionYaml } from '../decision/entry.js';
|
|
|
18
18
|
import { validateGateDocument } from '../gates/schema.js';
|
|
19
19
|
import { INDEX_EXTRACTOR_VERSION, INDEX_VERSION, INDEX_DIR, OPERATIONAL_DISCOVERY_TYPES, PHASE_DIRECTORIES, GRAPH_CONFIGS, TEMPLATE_INDEX_EXTENSIONS, resolveGraphScanDir, loadUserGraphConfigs, loadGlobalGraphConfigs, resolveGraphBackendType, } from './types.js';
|
|
20
20
|
import { parseCitationSidecar, citationResultToEdges, buildRefToPathMap } from './citation-parser.js';
|
|
21
|
-
import { writeIndexFile, resolveIndexDir, loadGraphIndexFile } from './index-reader.js';
|
|
21
|
+
import { writeIndexFile, resolveIndexDir, loadGraphIndexFile, publishIndexFiles } from './index-reader.js';
|
|
22
22
|
import { loadManifest, writeManifest, statMatches, makeEntry } from './checksum-manifest.js';
|
|
23
23
|
import { FRAMEWORK_RELATIONS_REVISION } from './framework-source-digest.js';
|
|
24
24
|
import { normalizeOperationalState } from './operational-state.js';
|
|
@@ -26,6 +26,9 @@ import { DEFAULT_PROJECT_AIWG_DIR, resolveProjectAiwgDir, } from '../config/proj
|
|
|
26
26
|
import { normalizeStateTransferProjection } from './state-transfer.js';
|
|
27
27
|
import { resolveIndexWorkers, runIndexPreparation } from './index-preparation.js';
|
|
28
28
|
import { collectGraphIndexFiles, findArtifactFiles, indexPathFor } from './index-files.js';
|
|
29
|
+
import { acquireDirectoryLock } from './prebuilt-build-lock.js';
|
|
30
|
+
import { graphIndexDigest, indexAdapterRegistry, recordIndexVerification } from './index-adapters.js';
|
|
31
|
+
import { loadMemoryRecord } from '../memory/maintenance-record.js';
|
|
29
32
|
function absoluteEntryPath(cwd, entryPath, graph) {
|
|
30
33
|
if ((!graph || graph === 'project') && entryPath.startsWith(`${DEFAULT_PROJECT_AIWG_DIR}/`)) {
|
|
31
34
|
return path.join(resolveProjectAiwgDir(cwd), entryPath.slice(DEFAULT_PROJECT_AIWG_DIR.length + 1));
|
|
@@ -369,6 +372,9 @@ function inferType(data, filePath) {
|
|
|
369
372
|
// Rules/RULES-INDEX.md is a curated index file, not a rule.
|
|
370
373
|
if (basename === 'rules-index' || basename === 'index')
|
|
371
374
|
break;
|
|
375
|
+
// <rule>.core.md is a delivery body (#3450), not a separate rule.
|
|
376
|
+
if (/\.core\.md$/i.test(filePath))
|
|
377
|
+
break;
|
|
372
378
|
if (isMarkdown)
|
|
373
379
|
return 'rule';
|
|
374
380
|
break;
|
|
@@ -836,27 +842,73 @@ function resolveInstallRootHint() {
|
|
|
836
842
|
catch { /* fall through */ }
|
|
837
843
|
return '<aiwg install root>';
|
|
838
844
|
}
|
|
845
|
+
/** Current source identities for rule bindings; no search index or graph is written. */
|
|
846
|
+
export async function collectRuleBindingEntries(cwd, scope) {
|
|
847
|
+
const fileStats = new Map();
|
|
848
|
+
const files = await collectGraphIndexFiles(cwd, undefined, { scope, fileStats });
|
|
849
|
+
return files.flatMap(fullPath => {
|
|
850
|
+
const stat = fileStats.get(fullPath) ?? fs.statSync(fullPath);
|
|
851
|
+
const entry = extractIndexArtifact({ fullPath, relativePath: indexPathFor(cwd, fullPath),
|
|
852
|
+
mtimeMs: stat.mtimeMs, birthtimeMs: stat.birthtimeMs, sizeBytes: stat.size }, true).entry;
|
|
853
|
+
return entry && ['skill', 'agent', 'flow'].includes(entry.type) ? [entry] : [];
|
|
854
|
+
});
|
|
855
|
+
}
|
|
839
856
|
/** Shared serial/worker read, checksum and extraction. No index output or global reduction. */
|
|
840
857
|
export function prepareIndexArtifact(job) {
|
|
858
|
+
const warnings = [];
|
|
859
|
+
const result = extractIndexArtifact(job, false, warnings);
|
|
860
|
+
return { ...result, ...(warnings.length ? { warnings } : {}) };
|
|
861
|
+
}
|
|
862
|
+
function extractIndexArtifact(job, bindingOnly, warnings = []) {
|
|
841
863
|
const { fullPath, relativePath } = job;
|
|
842
864
|
const content = fs.readFileSync(fullPath, 'utf8');
|
|
843
865
|
const checksum = computeChecksum(content);
|
|
844
866
|
if (job.existingChecksum === checksum)
|
|
845
867
|
return { checksum };
|
|
846
868
|
const { data, body } = parseFrontmatter(content);
|
|
869
|
+
let memoryRecord;
|
|
870
|
+
if (data.memoryRecord !== undefined) {
|
|
871
|
+
try {
|
|
872
|
+
memoryRecord = loadMemoryRecord(data.memoryRecord);
|
|
873
|
+
}
|
|
874
|
+
catch (error) {
|
|
875
|
+
warnings.push(`${relativePath}: invalid memoryRecord metadata quarantined: ${error instanceof Error ? error.message : String(error)}`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
const inferredType = inferType(data, relativePath);
|
|
879
|
+
const bindingType = ['skill', 'agent', 'flow'].includes(inferredType);
|
|
880
|
+
// Only YAML flows can promote an otherwise unrelated document to a binding target.
|
|
881
|
+
if (bindingOnly && !bindingType && !/\.ya?ml$/i.test(relativePath))
|
|
882
|
+
return { checksum };
|
|
847
883
|
// YAML Flow documents (workflow.aiwg.io/v1) are pure YAML, not
|
|
848
884
|
// markdown+frontmatter — detect them up front so they classify and
|
|
849
885
|
// become discoverable (#1540).
|
|
850
886
|
const flow = parseFlowDoc(content, relativePath);
|
|
887
|
+
if (bindingOnly && !flow && !bindingType)
|
|
888
|
+
return { checksum };
|
|
851
889
|
const decision = flow ? null : parseDecisionDoc(content, relativePath);
|
|
852
890
|
const gatePack = flow || decision ? null : parseGatePackDoc(content, relativePath);
|
|
853
|
-
|
|
891
|
+
if (bindingOnly && (decision || gatePack))
|
|
892
|
+
return { checksum };
|
|
854
893
|
const physicalType = inferType({ ...data, type: undefined }, relativePath);
|
|
855
894
|
const runbook = flow || decision || gatePack ? null : parseRunbookDoc(data, body, relativePath);
|
|
856
895
|
const schemaDoc = inferredType === 'schema' ? parseSchemaDoc(content, relativePath) : null;
|
|
857
896
|
const title = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.title ?? extractTitle(data, body);
|
|
858
897
|
const phase = typeof data.phase === 'string' ? data.phase : inferPhase(relativePath);
|
|
859
898
|
const type = flow?.type ?? decision?.type ?? gatePack?.type ?? (runbook ? 'runbook' : inferredType);
|
|
899
|
+
// Binding resolution consumes only identity and title. Keep the same canonical
|
|
900
|
+
// classification (including declarative flows and runbooks), without extracting
|
|
901
|
+
// search terms, mentions, links, or constructing a persistent graph.
|
|
902
|
+
if (bindingOnly)
|
|
903
|
+
return { checksum, entry: {
|
|
904
|
+
path: relativePath, type, title, phase,
|
|
905
|
+
...(memoryRecord ? { memoryRecord } : {}),
|
|
906
|
+
name: flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.name
|
|
907
|
+
?? (OPERATIONAL_DISCOVERY_TYPES.includes(type)
|
|
908
|
+
? extractCanonicalName(data, relativePath) : undefined),
|
|
909
|
+
tags: [], dependencies: [], dependents: [], checksum,
|
|
910
|
+
created: '', updated: '', summary: '',
|
|
911
|
+
} };
|
|
860
912
|
const tags = flow?.tags ?? decision?.tags ?? gatePack?.tags ?? (Array.isArray(data.tags) ? data.tags.map(String) : []);
|
|
861
913
|
const summary = flow?.description ?? decision?.description ?? gatePack?.description ?? schemaDoc?.capability ?? runbook?.capability ?? extractSummary(data, body);
|
|
862
914
|
const dependencies = extractMentions(content);
|
|
@@ -882,6 +934,8 @@ export function prepareIndexArtifact(job) {
|
|
|
882
934
|
// remain searchable even when the rendered title strips the hyphen.
|
|
883
935
|
const name = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.name ?? (isDiscoverable ? extractCanonicalName(data, relativePath) : undefined);
|
|
884
936
|
const entry = {
|
|
937
|
+
...(memoryRecord ? { memoryRecord } : {}),
|
|
938
|
+
...(typeof data.memoryCategory === 'string' ? { memoryCategory: data.memoryCategory } : {}),
|
|
885
939
|
path: relativePath,
|
|
886
940
|
type,
|
|
887
941
|
...(kind ? { kind } : {}),
|
|
@@ -910,6 +964,67 @@ export function prepareIndexArtifact(job) {
|
|
|
910
964
|
return { checksum, entry };
|
|
911
965
|
}
|
|
912
966
|
export async function buildIndex(cwd, options = {}) {
|
|
967
|
+
loadUserGraphConfigs(cwd);
|
|
968
|
+
loadGlobalGraphConfigs();
|
|
969
|
+
const scanDirs = options.scope ? [path.join(cwd, options.scope)] : options.graph
|
|
970
|
+
? GRAPH_CONFIGS[options.graph]?.scanDirs.map(dir => resolveGraphScanDir(cwd, dir)) ?? []
|
|
971
|
+
: [resolveProjectAiwgDir(cwd)];
|
|
972
|
+
if (!scanDirs.some(dir => fs.existsSync(dir))) {
|
|
973
|
+
// Keep the owner's missing-root diagnostic/exit path before creating output directories.
|
|
974
|
+
await buildIndexSnapshot(cwd, options, () => '');
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
const directory = options.outputDir
|
|
978
|
+
? path.join(options.outputDir, options.graph ? `.aiwg/.index/${options.graph}` : INDEX_DIR)
|
|
979
|
+
: resolveIndexDir(cwd, options.graph);
|
|
980
|
+
// Manual and background builds acquire the same per-output owner lock.
|
|
981
|
+
const release = await acquireDirectoryLock(`${directory}.build.lock`, {
|
|
982
|
+
timeoutMs: options.lockWaitMs ?? 180_000, reclaimDeadOwner: true,
|
|
983
|
+
});
|
|
984
|
+
let staging;
|
|
985
|
+
const start = Date.now();
|
|
986
|
+
let owned;
|
|
987
|
+
try {
|
|
988
|
+
const adapter = !options.scope && !options.includeIgnored
|
|
989
|
+
? indexAdapterRegistry(cwd).find(item => item.name === (options.graph ?? 'project')) : undefined;
|
|
990
|
+
owned = adapter && directory === path.dirname(adapter.statePath) ? adapter : undefined;
|
|
991
|
+
const observation = { selection: {} };
|
|
992
|
+
const preparePublication = () => {
|
|
993
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
994
|
+
return staging ??= fs.mkdtempSync(path.join(directory, '.build-'));
|
|
995
|
+
};
|
|
996
|
+
await buildIndexSnapshot(cwd, options, preparePublication, owned ? observation : undefined);
|
|
997
|
+
// A receipt hit verifies the existing snapshot directly against the scan's
|
|
998
|
+
// stats. There is no extraction interval to recheck. Real builds still probe
|
|
999
|
+
// again after preparation so concurrent source changes remain pending.
|
|
1000
|
+
const after = owned ? observation.unchanged ? observation.before
|
|
1001
|
+
: await graphIndexDigest(cwd, options.graph, { selection: observation.selection }) : undefined;
|
|
1002
|
+
// All extraction and backend work has succeeded before any live file moves.
|
|
1003
|
+
if (owned)
|
|
1004
|
+
recordIndexVerification(owned, cwd, { before: observation.before, after, wallMs: Date.now() - start }, staging);
|
|
1005
|
+
if (staging)
|
|
1006
|
+
publishIndexFiles(staging, directory);
|
|
1007
|
+
}
|
|
1008
|
+
catch (error) {
|
|
1009
|
+
if (owned) {
|
|
1010
|
+
try {
|
|
1011
|
+
recordIndexVerification(owned, cwd, { error: error instanceof Error ? error.message : String(error), wallMs: Date.now() - start });
|
|
1012
|
+
}
|
|
1013
|
+
catch { /* Preserve the original build failure if recording is unavailable. */ }
|
|
1014
|
+
}
|
|
1015
|
+
throw error;
|
|
1016
|
+
}
|
|
1017
|
+
finally {
|
|
1018
|
+
try {
|
|
1019
|
+
if (staging)
|
|
1020
|
+
fs.rmSync(staging, { recursive: true, force: true });
|
|
1021
|
+
}
|
|
1022
|
+
finally {
|
|
1023
|
+
await release();
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
async function buildIndexSnapshot(cwd, options, preparePublication, observation) {
|
|
913
1028
|
const { force = false, verbose = false, scope, includeIgnored = false, outputDir, graph, explicit = true } = options;
|
|
914
1029
|
const workers = resolveIndexWorkers(options.workers);
|
|
915
1030
|
const startTime = Date.now();
|
|
@@ -979,10 +1094,12 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
979
1094
|
// effectiveOutputCwd is used for backward-compat loadMetadataIndex calls
|
|
980
1095
|
const effectiveOutputCwd = outputDir ?? cwd;
|
|
981
1096
|
if (graph === 'source') {
|
|
1097
|
+
if (observation)
|
|
1098
|
+
observation.before = await graphIndexDigest(cwd, graph, { selection: observation.selection });
|
|
982
1099
|
const { buildSourceGraphIndex } = await import('./source-graph.js');
|
|
983
1100
|
await buildSourceGraphIndex({
|
|
984
1101
|
cwd,
|
|
985
|
-
outputDir:
|
|
1102
|
+
outputDir: preparePublication(),
|
|
986
1103
|
effectiveOutputCwd,
|
|
987
1104
|
verbose,
|
|
988
1105
|
});
|
|
@@ -1001,7 +1118,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1001
1118
|
: loadManifest(indexOutputDir);
|
|
1002
1119
|
const useFilenameMetadata = graphConfig?.nodeStrategy === 'filename-metadata';
|
|
1003
1120
|
const reusable = !useFilenameMetadata && !graphConfig?.metadataSupplements?.length && !graphConfig?.edgeExtraction;
|
|
1004
|
-
const fileStats = !force && reusable ? new Map() : undefined;
|
|
1121
|
+
const fileStats = observation || (!force && reusable) ? new Map() : undefined;
|
|
1005
1122
|
const nextManifestEntries = {};
|
|
1006
1123
|
const manifestStats = {
|
|
1007
1124
|
checked: 0,
|
|
@@ -1011,7 +1128,9 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1011
1128
|
pruned: 0,
|
|
1012
1129
|
};
|
|
1013
1130
|
// Collect files from all scan directories
|
|
1014
|
-
const files = await collectGraphIndexFiles(cwd, graph, { scope, includeIgnored, fileStats });
|
|
1131
|
+
const files = await collectGraphIndexFiles(cwd, graph, { scope, includeIgnored, fileStats, selection: observation?.selection });
|
|
1132
|
+
if (observation)
|
|
1133
|
+
observation.before = await graphIndexDigest(cwd, graph, { files, fileStats });
|
|
1015
1134
|
const entries = {};
|
|
1016
1135
|
const tagIndex = {};
|
|
1017
1136
|
const depGraph = {};
|
|
@@ -1052,7 +1171,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1052
1171
|
};
|
|
1053
1172
|
const entriesDigest = (values) => createHash('sha256').update(JSON.stringify(values)).digest('hex');
|
|
1054
1173
|
const writeBuildManifest = (values) => {
|
|
1055
|
-
writeManifest(
|
|
1174
|
+
writeManifest(publicationDir, { version: 1, generated: new Date().toISOString(), entries: values,
|
|
1056
1175
|
...(reusable ? { receipt: {
|
|
1057
1176
|
indexVersion: INDEX_VERSION, extractorVersion: INDEX_EXTRACTOR_VERSION, buildConfigHash,
|
|
1058
1177
|
sourceRoot: path.resolve(cwd), metadataSignature: fileSignature('metadata.json'), entriesDigest: entriesDigest(values),
|
|
@@ -1086,11 +1205,16 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1086
1205
|
manifestStats.checked = files.length;
|
|
1087
1206
|
manifestStats.statSkipped = files.length;
|
|
1088
1207
|
const buildTimeMs = Date.now() - startTime;
|
|
1208
|
+
// Only stats changes on a receipt hit; its existing atomic writer is
|
|
1209
|
+
// sufficient under the owner lock, without staging or backup copies.
|
|
1089
1210
|
writeIndexFile(effectiveOutputCwd, 'stats.json', { ...stats, builtAt: new Date().toISOString(), buildTimeMs }, indexOutputDir);
|
|
1211
|
+
if (observation)
|
|
1212
|
+
observation.unchanged = true;
|
|
1090
1213
|
reportBuild(buildTimeMs, files.length);
|
|
1091
1214
|
return;
|
|
1092
1215
|
}
|
|
1093
1216
|
}
|
|
1217
|
+
const publicationDir = preparePublication();
|
|
1094
1218
|
const existingIndex = force ? null : outputDir
|
|
1095
1219
|
? loadIndexFromDir(indexOutputDir, 'metadata.json')
|
|
1096
1220
|
: loadGraphIndexFile(effectiveOutputCwd, 'metadata.json', graph);
|
|
@@ -1121,6 +1245,10 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1121
1245
|
...(!force && existingEntries[relativePath] ? { existingChecksum: existingEntries[relativePath].checksum } : {}) });
|
|
1122
1246
|
}
|
|
1123
1247
|
const preparation = await runIndexPreparation(jobs, prepareIndexArtifact, import.meta.url, workers, options.onDiagnostic ?? (message => console.warn(`Index preparation: ${message}`)));
|
|
1248
|
+
for (const result of preparation.results)
|
|
1249
|
+
for (const warning of result.warnings ?? []) {
|
|
1250
|
+
(options.onDiagnostic ?? (message => console.warn(`Index preparation: ${message}`)))(warning);
|
|
1251
|
+
}
|
|
1124
1252
|
const preparedArtifacts = new Map(jobs.map((job, index) => [job.fullPath, preparation.results[index]]));
|
|
1125
1253
|
for (const fullPath of files) {
|
|
1126
1254
|
const relativePath = indexPathFor(cwd, fullPath, graph);
|
|
@@ -1221,14 +1349,15 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1221
1349
|
const projectionNeedsRepair = graph === 'framework' && !relationsReceiptMatches();
|
|
1222
1350
|
if (graph === 'framework') {
|
|
1223
1351
|
const { writeRelations } = await import('./relations-builder.js');
|
|
1224
|
-
|
|
1352
|
+
if (force || !relationsReceiptMatches())
|
|
1353
|
+
await writeRelations(entries, effectiveOutputCwd, publicationDir, true);
|
|
1225
1354
|
}
|
|
1226
1355
|
const buildTimeMs = Date.now() - startTime;
|
|
1227
1356
|
writeIndexFile(effectiveOutputCwd, 'stats.json', {
|
|
1228
1357
|
...stats,
|
|
1229
1358
|
builtAt: new Date().toISOString(),
|
|
1230
1359
|
buildTimeMs,
|
|
1231
|
-
},
|
|
1360
|
+
}, publicationDir);
|
|
1232
1361
|
// Preserve no-op manifest bytes after output-only timestamp drift.
|
|
1233
1362
|
// Renew receipts only for changed source stats or repaired projections.
|
|
1234
1363
|
if (manifestStats.checksumSkipped > 0 || projectionNeedsRepair)
|
|
@@ -1354,7 +1483,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1354
1483
|
}
|
|
1355
1484
|
if (graph === 'framework') {
|
|
1356
1485
|
const { writeRelations } = await import('./relations-builder.js');
|
|
1357
|
-
await writeRelations(entries, effectiveOutputCwd,
|
|
1486
|
+
await writeRelations(entries, effectiveOutputCwd, publicationDir, force || !relationsReceiptMatches(), indexOutputDir);
|
|
1358
1487
|
}
|
|
1359
1488
|
const buildTimeMs = Date.now() - startTime;
|
|
1360
1489
|
// Write index files
|
|
@@ -1366,13 +1495,13 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1366
1495
|
buildTimeMs,
|
|
1367
1496
|
entries,
|
|
1368
1497
|
};
|
|
1369
|
-
writeIndexFile(effectiveOutputCwd, 'metadata.json', index,
|
|
1370
|
-
writeIndexFile(effectiveOutputCwd, 'tags.json', tagIndex,
|
|
1371
|
-
writeIndexFile(effectiveOutputCwd, 'dependencies.json', depGraph,
|
|
1498
|
+
writeIndexFile(effectiveOutputCwd, 'metadata.json', index, publicationDir);
|
|
1499
|
+
writeIndexFile(effectiveOutputCwd, 'tags.json', tagIndex, publicationDir);
|
|
1500
|
+
writeIndexFile(effectiveOutputCwd, 'dependencies.json', depGraph, publicationDir);
|
|
1372
1501
|
// Materialize the configured backend and always retain dependencies.json as
|
|
1373
1502
|
// the stable compatibility/export contract.
|
|
1374
1503
|
const { createGraphBackend } = await import('./graph-backend.js');
|
|
1375
|
-
const persistentPath = backendType === 'sqlite' ? path.join(
|
|
1504
|
+
const persistentPath = backendType === 'sqlite' ? path.join(publicationDir, 'graph.db') : undefined;
|
|
1376
1505
|
let selectedBackend;
|
|
1377
1506
|
try {
|
|
1378
1507
|
selectedBackend = await createGraphBackend(backendType, persistentPath);
|
|
@@ -1430,7 +1559,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1430
1559
|
orphanedArtifacts: orphaned,
|
|
1431
1560
|
mostReferenced,
|
|
1432
1561
|
},
|
|
1433
|
-
},
|
|
1562
|
+
}, publicationDir);
|
|
1434
1563
|
writeBuildManifest(nextManifestEntries);
|
|
1435
1564
|
// Report
|
|
1436
1565
|
reportBuild(buildTimeMs, total);
|
|
@@ -87,9 +87,14 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
|
|
|
87
87
|
throw new Error(`Unknown graph: ${graph}`);
|
|
88
88
|
}
|
|
89
89
|
const artifactRoot = resolveProjectAiwgDir(cwd);
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
const artifactRoots = [path.join(cwd, DEFAULT_PROJECT_AIWG_DIR), artifactRoot];
|
|
91
|
+
// Aggregate owner state contains history, tombstones and transaction payloads.
|
|
92
|
+
// It is not an ordinary artifact: only each owner's public projection is a
|
|
93
|
+
// retrieval surface, never the backing store or maintenance receipts.
|
|
94
|
+
const privateDirs = ['context/compound-memory', 'memory/transactions', 'memory/maintenance'];
|
|
95
|
+
const privateFiles = ['memory/line-memory.meta.json', 'memory/line-memory.config.json', 'memory/line-memory.lock'];
|
|
96
|
+
const excludedDirs = artifactRoots.flatMap(root => [...INDEX_SCRATCH_DIRS, ...privateDirs].map(name => path.resolve(root, name)));
|
|
97
|
+
const excludedPaths = new Set([...excludedDirs, ...artifactRoots.flatMap(root => privateFiles.map(name => path.resolve(root, name)))]);
|
|
93
98
|
// A directory's ancestors are shared by thousands of sibling artifacts.
|
|
94
99
|
// Cache only within this scan so the next build still notices new symlinks.
|
|
95
100
|
const safePaths = new Map();
|
|
@@ -109,7 +114,7 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
|
|
|
109
114
|
}
|
|
110
115
|
const inputPath = file;
|
|
111
116
|
file = path.resolve(file);
|
|
112
|
-
if (excludedDirs.some(dir => process.platform === 'win32'
|
|
117
|
+
if (excludedPaths.has(file) || excludedDirs.some(dir => process.platform === 'win32'
|
|
113
118
|
? pathContains(dir, file) : file === dir || file.startsWith(dir + path.sep)))
|
|
114
119
|
return false;
|
|
115
120
|
if (knownStat && !knownStat.isSymbolicLink() && safePaths.get(path.dirname(file)) === true) {
|
|
@@ -190,6 +195,10 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
|
|
|
190
195
|
}
|
|
191
196
|
}
|
|
192
197
|
let sources = [...files];
|
|
198
|
+
const candidates = [...sources].sort();
|
|
199
|
+
if (options.selection?.files && JSON.stringify(candidates) === JSON.stringify(options.selection.candidates)) {
|
|
200
|
+
return [...options.selection.files];
|
|
201
|
+
}
|
|
193
202
|
if (!options.includeIgnored) {
|
|
194
203
|
// Many projects ignore the whole artifact root (`.aiwg/`). Honoring that
|
|
195
204
|
// would empty the index, so ignore rules apply only beneath a scan root
|
|
@@ -225,6 +234,8 @@ export async function collectGraphIndexFiles(cwd, graph, options = {}) {
|
|
|
225
234
|
if (!selected.has(file))
|
|
226
235
|
options.fileStats.delete(file);
|
|
227
236
|
}
|
|
237
|
+
if (options.selection)
|
|
238
|
+
Object.assign(options.selection, { candidates, files: [...sources] });
|
|
228
239
|
return sources;
|
|
229
240
|
}
|
|
230
241
|
//# sourceMappingURL=index-files.js.map
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
13
|
import { getGraphIndexDir, getProjectIndexRoot } from './types.js';
|
|
14
|
+
import { loadManifest, writeManifest } from './checksum-manifest.js';
|
|
14
15
|
/**
|
|
15
16
|
* Load a JSON file from the index directory
|
|
16
17
|
*
|
|
@@ -109,4 +110,47 @@ export function loadGraphIndexFile(cwd, filename, graph) {
|
|
|
109
110
|
return null;
|
|
110
111
|
}
|
|
111
112
|
}
|
|
113
|
+
/** Publish a prepared snapshot with the existing atomic file writes under the owner lock. */
|
|
114
|
+
export function publishIndexFiles(staging, directory) {
|
|
115
|
+
const files = fs.readdirSync(staging).filter(file => fs.statSync(path.join(staging, file)).isFile())
|
|
116
|
+
.sort((a, b) => Number(a === 'metadata.json') - Number(b === 'metadata.json'));
|
|
117
|
+
const backups = path.join(staging, '.previous');
|
|
118
|
+
fs.mkdirSync(backups);
|
|
119
|
+
for (const file of files) {
|
|
120
|
+
const target = path.join(directory, file);
|
|
121
|
+
if (fs.existsSync(target))
|
|
122
|
+
fs.copyFileSync(target, path.join(backups, file));
|
|
123
|
+
}
|
|
124
|
+
const published = [];
|
|
125
|
+
try {
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
fs.renameSync(path.join(staging, file), path.join(directory, file));
|
|
128
|
+
published.push(file);
|
|
129
|
+
}
|
|
130
|
+
// Receipts bind to final inode signatures, after the atomic renames.
|
|
131
|
+
if (files.includes('checksum-manifest.json')) {
|
|
132
|
+
const manifest = loadManifest(directory);
|
|
133
|
+
const signature = (file) => {
|
|
134
|
+
const stat = fs.statSync(path.join(directory, file), { bigint: true });
|
|
135
|
+
return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(':');
|
|
136
|
+
};
|
|
137
|
+
if (manifest.receipt) {
|
|
138
|
+
manifest.receipt.metadataSignature = signature('metadata.json');
|
|
139
|
+
if (manifest.receipt.relations)
|
|
140
|
+
manifest.receipt.relations.signature = signature('relations.json');
|
|
141
|
+
writeManifest(directory, manifest);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
for (const file of published.reverse()) {
|
|
147
|
+
const backup = path.join(backups, file);
|
|
148
|
+
if (fs.existsSync(backup))
|
|
149
|
+
fs.renameSync(backup, path.join(directory, file));
|
|
150
|
+
else
|
|
151
|
+
fs.rmSync(path.join(directory, file), { force: true });
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
112
156
|
//# sourceMappingURL=index-reader.js.map
|