@aiwg/cli 2026.10.3 → 2026.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/agentic/code/providers/model-capabilities.v1.json +96 -4
- package/bin/aiwg.mjs +50 -27
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +69 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +286 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +130 -0
- package/dist/src/artifacts/index-refresh.js +591 -0
- package/dist/src/artifacts/index-worker.mjs +11 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +19 -2
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +19 -12
- package/dist/src/cli/handlers/diagnose.js +4 -3
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +4 -1
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/models.js +104 -8
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/refresh.js +12 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +1 -2
- package/dist/src/cli/handlers/script-runner.js +3 -2
- package/dist/src/cli/handlers/session.js +19 -9
- package/dist/src/cli/handlers/sessions.js +137 -43
- package/dist/src/cli/handlers/setup-manifest.js +39 -44
- package/dist/src/cli/handlers/setup.js +41 -0
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +57 -2
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +294 -55
- package/dist/src/cli/handlers/utilities.js +26 -10
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +87 -37
- package/dist/src/config/cli.js +111 -5
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +2 -1
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +13 -8
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +2 -15
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +5 -13
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +131 -28
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/gates/providers/loader.js +61 -17
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +14 -2
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +78 -3
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +7 -1
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +36 -11
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +1 -1
- package/dist/src/mcp/helpers.mjs +3 -2
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/registry.js +3 -93
- package/dist/src/mcp/registry.mjs +3 -87
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +37 -375
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tools/decision.mjs +12 -11
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +12 -2
- package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
- package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
- package/dist/src/mission-protocol/codecs.js +8 -0
- package/dist/src/models/config-loader.js +7 -2
- package/dist/src/models/model-capabilities.v1.json +96 -4
- package/dist/src/models/model-discovery.js +76 -11
- package/dist/src/models/provider-policy.js +101 -7
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/resolver.js +12 -10
- package/dist/src/models/worker-direct-readiness.js +65 -44
- package/dist/src/models/worker-dispatch-offer.js +3 -3
- package/dist/src/models/worker-health-readiness.js +16 -3
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +135 -28
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +43 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +12 -4
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/mission-conductor.js +38 -12
- package/dist/src/serve/worker-invocation.js +30 -10
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/sessions/adapter-registry.js +23 -19
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +95 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +10 -10
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot.js +24 -13
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
- package/dist/src/sessions/adapters/muse.js +150 -93
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +29 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +16 -8
- package/dist/src/sessions/batch-import.js +252 -12
- package/dist/src/sessions/contracts.js +50 -3
- package/dist/src/sessions/coverage-preflight.js +27 -2
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +60 -38
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +145 -26
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +105 -0
- package/dist/src/sessions/repository.js +151 -54
- package/dist/src/sessions/workspace-discovery.js +292 -60
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/package.json +15 -2
- package/schemas/models/model-catalog.v1.schema.json +1 -1
- package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
- package/tools/_resolve-impl.mjs +13 -15
- package/tools/agents/deploy-agents.mjs +49 -21
- package/tools/agents/providers/base.mjs +104 -134
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/hermes.mjs +57 -47
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +6 -4
- package/tools/providers/omp-transport.mjs +4 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
- package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
- package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
|
@@ -16,13 +16,15 @@ import { load as loadYaml } from 'js-yaml';
|
|
|
16
16
|
import { validateDecisionDocument } from '../decision/validate.js';
|
|
17
17
|
import { parseDecisionJson, parseDecisionYaml } from '../decision/entry.js';
|
|
18
18
|
import { validateGateDocument } from '../gates/schema.js';
|
|
19
|
-
import {
|
|
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
21
|
import { writeIndexFile, resolveIndexDir, loadGraphIndexFile } from './index-reader.js';
|
|
22
22
|
import { loadManifest, writeManifest, statMatches, makeEntry } from './checksum-manifest.js';
|
|
23
|
+
import { FRAMEWORK_RELATIONS_REVISION } from './framework-source-digest.js';
|
|
23
24
|
import { normalizeOperationalState } from './operational-state.js';
|
|
24
25
|
import { DEFAULT_PROJECT_AIWG_DIR, resolveProjectAiwgDir, } from '../config/project-artifacts.js';
|
|
25
26
|
import { normalizeStateTransferProjection } from './state-transfer.js';
|
|
27
|
+
import { resolveIndexWorkers, runIndexPreparation } from './index-preparation.js';
|
|
26
28
|
import { collectGraphIndexFiles, findArtifactFiles, indexPathFor } from './index-files.js';
|
|
27
29
|
function absoluteEntryPath(cwd, entryPath, graph) {
|
|
28
30
|
if ((!graph || graph === 'project') && entryPath.startsWith(`${DEFAULT_PROJECT_AIWG_DIR}/`)) {
|
|
@@ -56,6 +58,30 @@ export function parseFrontmatter(content) {
|
|
|
56
58
|
return { data: {}, body: content };
|
|
57
59
|
}
|
|
58
60
|
}
|
|
61
|
+
/** Read the target rule schema tolerantly while older rules remain untagged. */
|
|
62
|
+
export function extractRuleBindings(data, body = '') {
|
|
63
|
+
const bound_to = {};
|
|
64
|
+
const raw = data.bound_to;
|
|
65
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
|
|
66
|
+
for (const key of ['skills', 'agents', 'flows']) {
|
|
67
|
+
const targets = raw[key];
|
|
68
|
+
if (Array.isArray(targets)) {
|
|
69
|
+
const names = [...new Set(targets.filter((name) => typeof name === 'string' && name.trim().length > 0))];
|
|
70
|
+
if (names.length)
|
|
71
|
+
bound_to[key] = names;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const enforcement = typeof data.enforcement === 'string' ? data.enforcement
|
|
76
|
+
: body.match(/^\*\*Enforcement Level\*\*:\s*([A-Za-z]+)/m)?.[1];
|
|
77
|
+
return {
|
|
78
|
+
...(enforcement ? { ruleEnforcement: enforcement.trim().toLowerCase() } : {}),
|
|
79
|
+
...(Array.isArray(data.paths) ? { rulePaths: data.paths.filter((value) => typeof value === 'string') } : {}),
|
|
80
|
+
...(typeof data.activation === 'string' ? { activation: data.activation } : {}),
|
|
81
|
+
...(typeof data.summary === 'string' ? { ruleSummary: data.summary } : {}),
|
|
82
|
+
...(Object.keys(bound_to).length ? { bound_to } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
59
85
|
/**
|
|
60
86
|
* Extract @-mention references from content
|
|
61
87
|
*/
|
|
@@ -810,29 +836,100 @@ function resolveInstallRootHint() {
|
|
|
810
836
|
catch { /* fall through */ }
|
|
811
837
|
return '<aiwg install root>';
|
|
812
838
|
}
|
|
839
|
+
/** Shared serial/worker read, checksum and extraction. No index output or global reduction. */
|
|
840
|
+
export function prepareIndexArtifact(job) {
|
|
841
|
+
const { fullPath, relativePath } = job;
|
|
842
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
843
|
+
const checksum = computeChecksum(content);
|
|
844
|
+
if (job.existingChecksum === checksum)
|
|
845
|
+
return { checksum };
|
|
846
|
+
const { data, body } = parseFrontmatter(content);
|
|
847
|
+
// YAML Flow documents (workflow.aiwg.io/v1) are pure YAML, not
|
|
848
|
+
// markdown+frontmatter — detect them up front so they classify and
|
|
849
|
+
// become discoverable (#1540).
|
|
850
|
+
const flow = parseFlowDoc(content, relativePath);
|
|
851
|
+
const decision = flow ? null : parseDecisionDoc(content, relativePath);
|
|
852
|
+
const gatePack = flow || decision ? null : parseGatePackDoc(content, relativePath);
|
|
853
|
+
const inferredType = inferType(data, relativePath);
|
|
854
|
+
const physicalType = inferType({ ...data, type: undefined }, relativePath);
|
|
855
|
+
const runbook = flow || decision || gatePack ? null : parseRunbookDoc(data, body, relativePath);
|
|
856
|
+
const schemaDoc = inferredType === 'schema' ? parseSchemaDoc(content, relativePath) : null;
|
|
857
|
+
const title = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.title ?? extractTitle(data, body);
|
|
858
|
+
const phase = typeof data.phase === 'string' ? data.phase : inferPhase(relativePath);
|
|
859
|
+
const type = flow?.type ?? decision?.type ?? gatePack?.type ?? (runbook ? 'runbook' : inferredType);
|
|
860
|
+
const tags = flow?.tags ?? decision?.tags ?? gatePack?.tags ?? (Array.isArray(data.tags) ? data.tags.map(String) : []);
|
|
861
|
+
const summary = flow?.description ?? decision?.description ?? gatePack?.description ?? schemaDoc?.capability ?? runbook?.capability ?? extractSummary(data, body);
|
|
862
|
+
const dependencies = extractMentions(content);
|
|
863
|
+
const markdownLinks = extractMarkdownLinks(content);
|
|
864
|
+
// Discovery metadata (#1214, #1540, #1792) — meaningful for operational
|
|
865
|
+
// AIWG artifact kinds. Kept undefined on document types so the index file
|
|
866
|
+
// stays small for the common case.
|
|
867
|
+
const isDiscoverable = OPERATIONAL_DISCOVERY_TYPES.includes(type);
|
|
868
|
+
// Declarative processes have no trigger phrases — they rely on their
|
|
869
|
+
// capability and structure-aware search terms.
|
|
870
|
+
const triggers = isDiscoverable && !flow ? extractTriggers(body, data) : undefined;
|
|
871
|
+
const capability = flow?.capability ?? decision?.capability ?? gatePack?.capability ?? schemaDoc?.capability ?? runbook?.capability ?? (isDiscoverable ? extractCapability(data, body) : undefined);
|
|
872
|
+
const kind = flow?.kind ?? decision?.kind ?? gatePack?.kind ?? runbook?.kind;
|
|
873
|
+
const sourceType = runbook && physicalType !== 'runbook' ? physicalType : undefined;
|
|
874
|
+
const searchTerms = flow?.searchTerms ?? decision?.searchTerms ?? gatePack?.searchTerms ?? schemaDoc?.searchTerms ?? runbook?.searchTerms;
|
|
875
|
+
const kernel = data.kernel === true || data.kernel === 'true' ? true : undefined;
|
|
876
|
+
// Script entrypoint metadata is meaningful for skills only (#1227).
|
|
877
|
+
const script = type === 'skill' ? extractSkillScript(data) : undefined;
|
|
878
|
+
const operationalState = normalizeOperationalState(data.operational_state);
|
|
879
|
+
const stateTransfer = normalizeStateTransferProjection(data.state_transfer);
|
|
880
|
+
// Canonical short name (#1233) — used by the scorer to floor exact-name
|
|
881
|
+
// queries to 1.0 so hyphenated kernel-skill names like `aiwg-doctor`
|
|
882
|
+
// remain searchable even when the rendered title strips the hyphen.
|
|
883
|
+
const name = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.name ?? (isDiscoverable ? extractCanonicalName(data, relativePath) : undefined);
|
|
884
|
+
const entry = {
|
|
885
|
+
path: relativePath,
|
|
886
|
+
type,
|
|
887
|
+
...(kind ? { kind } : {}),
|
|
888
|
+
...(sourceType ? { sourceType } : {}),
|
|
889
|
+
phase,
|
|
890
|
+
title,
|
|
891
|
+
tags,
|
|
892
|
+
created: typeof data.created === 'string' ? data.created : new Date(job.birthtimeMs).toISOString(),
|
|
893
|
+
updated: new Date(job.mtimeMs).toISOString(),
|
|
894
|
+
checksum,
|
|
895
|
+
summary,
|
|
896
|
+
dependencies,
|
|
897
|
+
...(markdownLinks.length > 0 ? { markdownLinks } : {}),
|
|
898
|
+
dependents: [], // Computed after all entries are processed
|
|
899
|
+
...(name ? { name } : {}),
|
|
900
|
+
...(triggers && triggers.length > 0 ? { triggers } : {}),
|
|
901
|
+
...(capability ? { capability } : {}),
|
|
902
|
+
...(searchTerms && searchTerms.length > 0 ? { searchTerms } : {}),
|
|
903
|
+
...(kernel ? { kernel } : {}),
|
|
904
|
+
...(script ? { script } : {}),
|
|
905
|
+
...(type === 'rule' || type === 'behavior' ? extractRuleBindings(data, body) :
|
|
906
|
+
Array.isArray(data.paths) ? { governedPaths: data.paths.filter((value) => typeof value === 'string') } : {}),
|
|
907
|
+
...(operationalState ? { operationalState } : {}),
|
|
908
|
+
...(stateTransfer ? { stateTransfer } : {}),
|
|
909
|
+
};
|
|
910
|
+
return { checksum, entry };
|
|
911
|
+
}
|
|
813
912
|
export async function buildIndex(cwd, options = {}) {
|
|
814
|
-
const { force = false, verbose = false, scope, outputDir, graph, explicit = true } = options;
|
|
913
|
+
const { force = false, verbose = false, scope, includeIgnored = false, outputDir, graph, explicit = true } = options;
|
|
914
|
+
const workers = resolveIndexWorkers(options.workers);
|
|
815
915
|
const startTime = Date.now();
|
|
916
|
+
const log = options.quiet ? (..._args) => { } : console.log;
|
|
816
917
|
// Ensure user-defined graphs are loaded
|
|
817
918
|
loadUserGraphConfigs(cwd);
|
|
818
919
|
loadGlobalGraphConfigs();
|
|
819
920
|
// Determine scan directories based on graph type
|
|
820
921
|
const graphConfig = graph ? GRAPH_CONFIGS[graph] : undefined;
|
|
821
922
|
let scanDirs;
|
|
822
|
-
let fileExtensions;
|
|
823
923
|
if (scope) {
|
|
824
924
|
// Explicit scope overrides graph config
|
|
825
925
|
scanDirs = [path.join(cwd, scope)];
|
|
826
|
-
fileExtensions = [...DEFAULT_INDEX_EXTENSIONS];
|
|
827
926
|
}
|
|
828
927
|
else if (graphConfig) {
|
|
829
928
|
scanDirs = graphConfig.scanDirs.map(d => resolveGraphScanDir(cwd, d));
|
|
830
|
-
fileExtensions = graphConfig.extensions;
|
|
831
929
|
}
|
|
832
930
|
else {
|
|
833
931
|
// Default: scan .aiwg/ (backward compatible)
|
|
834
932
|
scanDirs = [resolveProjectAiwgDir(cwd)];
|
|
835
|
-
fileExtensions = [...DEFAULT_INDEX_EXTENSIONS];
|
|
836
933
|
}
|
|
837
934
|
// Verify at least one scan directory exists
|
|
838
935
|
const existingDirs = scanDirs.filter(d => fs.existsSync(d));
|
|
@@ -851,16 +948,16 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
851
948
|
// required directories" sends the operator looking in the wrong place (#2530).
|
|
852
949
|
if (graph === 'framework') {
|
|
853
950
|
const installRoot = resolveInstallRootHint();
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
951
|
+
log('The framework graph indexes the AIWG corpus and can only be built at the');
|
|
952
|
+
log('install root, not in a consumer project. Build it there, then sync here:');
|
|
953
|
+
log('');
|
|
954
|
+
log(` cd ${installRoot} && aiwg index build --graph framework --force`);
|
|
955
|
+
log(` cd ${cwd} && aiwg index sync --backend fortemi-core --graph framework`);
|
|
956
|
+
log('');
|
|
957
|
+
log("As an immediate workaround, discovery also works with '--backend local'.");
|
|
861
958
|
}
|
|
862
959
|
else {
|
|
863
|
-
|
|
960
|
+
log('Run this command from a project with the required directories.');
|
|
864
961
|
}
|
|
865
962
|
process.exit(1);
|
|
866
963
|
}
|
|
@@ -890,24 +987,21 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
890
987
|
verbose,
|
|
891
988
|
});
|
|
892
989
|
const buildTimeMs = Date.now() - startTime;
|
|
893
|
-
|
|
894
|
-
|
|
990
|
+
log(`Source graph built in ${buildTimeMs}ms`);
|
|
991
|
+
log(` Output: ${INDEX_DIR}/source/`);
|
|
895
992
|
return;
|
|
896
993
|
}
|
|
897
994
|
// Load existing index for incremental updates
|
|
898
|
-
const
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
const canReuseExisting = existingIndex?.version === INDEX_VERSION
|
|
904
|
-
&& existingIndex.extractorVersion === INDEX_EXTRACTOR_VERSION;
|
|
905
|
-
const existingEntries = canReuseExisting ? existingIndex.entries : {};
|
|
906
|
-
// Load checksum manifest for fast stat-based change detection (#794).
|
|
907
|
-
// When --force is set we skip the manifest entirely and rebuild everything.
|
|
908
|
-
const manifest = force || !canReuseExisting
|
|
995
|
+
const backendType = resolveGraphBackendType(graph);
|
|
996
|
+
const buildConfigHash = createHash('sha256')
|
|
997
|
+
.update(JSON.stringify({ graphConfig, backendType }))
|
|
998
|
+
.digest('hex');
|
|
999
|
+
const storedManifest = force
|
|
909
1000
|
? { version: 1, generated: '', entries: {} }
|
|
910
1001
|
: loadManifest(indexOutputDir);
|
|
1002
|
+
const useFilenameMetadata = graphConfig?.nodeStrategy === 'filename-metadata';
|
|
1003
|
+
const reusable = !useFilenameMetadata && !graphConfig?.metadataSupplements?.length && !graphConfig?.edgeExtraction;
|
|
1004
|
+
const fileStats = !force && reusable ? new Map() : undefined;
|
|
911
1005
|
const nextManifestEntries = {};
|
|
912
1006
|
const manifestStats = {
|
|
913
1007
|
checked: 0,
|
|
@@ -917,16 +1011,117 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
917
1011
|
pruned: 0,
|
|
918
1012
|
};
|
|
919
1013
|
// Collect files from all scan directories
|
|
920
|
-
const files = scope
|
|
921
|
-
? existingDirs.flatMap(dir => findArtifactFiles(dir, fileExtensions))
|
|
922
|
-
: await collectGraphIndexFiles(cwd, graph);
|
|
1014
|
+
const files = await collectGraphIndexFiles(cwd, graph, { scope, includeIgnored, fileStats });
|
|
923
1015
|
const entries = {};
|
|
924
1016
|
const tagIndex = {};
|
|
925
1017
|
const depGraph = {};
|
|
926
1018
|
let newCount = 0;
|
|
927
1019
|
let updatedCount = 0;
|
|
928
1020
|
let unchangedCount = 0;
|
|
929
|
-
const
|
|
1021
|
+
const reportBuild = (buildTimeMs, total) => {
|
|
1022
|
+
log(`Artifact index built in ${buildTimeMs}ms`);
|
|
1023
|
+
log(` Indexed ${newCount} new, updated ${updatedCount}, unchanged ${unchangedCount}`);
|
|
1024
|
+
log(` Total: ${total} artifacts`);
|
|
1025
|
+
if (!force && manifestStats.checked > 0) {
|
|
1026
|
+
const fastPath = manifestStats.statSkipped;
|
|
1027
|
+
const slowPath = manifestStats.checked - manifestStats.statSkipped;
|
|
1028
|
+
log(` Change detection: ${manifestStats.checked} checked, ${fastPath} skipped via stat, ${slowPath} content-read, ${manifestStats.reindexed} re-indexed`);
|
|
1029
|
+
if (manifestStats.pruned > 0) {
|
|
1030
|
+
log(` Pruned ${manifestStats.pruned} stale manifest entries (files no longer on disk)`);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
const displayDir = graph ? indexOutputDir : `${INDEX_DIR}/`;
|
|
1034
|
+
log(` Output: ${displayDir}`);
|
|
1035
|
+
};
|
|
1036
|
+
const requiredFiles = ['tags.json', 'dependencies.json'];
|
|
1037
|
+
if (backendType === 'sqlite')
|
|
1038
|
+
requiredFiles.push('graph.db');
|
|
1039
|
+
const fileSignature = (filename) => {
|
|
1040
|
+
try {
|
|
1041
|
+
const stat = fs.statSync(path.join(indexOutputDir, filename), { bigint: true });
|
|
1042
|
+
return [stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].join(':');
|
|
1043
|
+
}
|
|
1044
|
+
catch {
|
|
1045
|
+
return '';
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
const relationsReceiptMatches = () => {
|
|
1049
|
+
const projection = storedManifest.receipt?.relations;
|
|
1050
|
+
return graph !== 'framework' || Boolean(projection?.revision === FRAMEWORK_RELATIONS_REVISION
|
|
1051
|
+
&& projection.signature && projection.signature === fileSignature('relations.json'));
|
|
1052
|
+
};
|
|
1053
|
+
const entriesDigest = (values) => createHash('sha256').update(JSON.stringify(values)).digest('hex');
|
|
1054
|
+
const writeBuildManifest = (values) => {
|
|
1055
|
+
writeManifest(indexOutputDir, { version: 1, generated: new Date().toISOString(), entries: values,
|
|
1056
|
+
...(reusable ? { receipt: {
|
|
1057
|
+
indexVersion: INDEX_VERSION, extractorVersion: INDEX_EXTRACTOR_VERSION, buildConfigHash,
|
|
1058
|
+
sourceRoot: path.resolve(cwd), metadataSignature: fileSignature('metadata.json'), entriesDigest: entriesDigest(values),
|
|
1059
|
+
...(graph === 'framework' ? { relations: { revision: FRAMEWORK_RELATIONS_REVISION, signature: fileSignature('relations.json') } } : {}),
|
|
1060
|
+
} } : {}),
|
|
1061
|
+
});
|
|
1062
|
+
};
|
|
1063
|
+
// A successful receipt proves the manifest belongs to this exact metadata
|
|
1064
|
+
// file. Still enumerate/stat every source and verify the complete file set;
|
|
1065
|
+
// unchanged builds need not parse the much larger metadata/dependency data.
|
|
1066
|
+
const receipt = storedManifest.receipt;
|
|
1067
|
+
if (!force && reusable && storedManifest.generated && receipt
|
|
1068
|
+
&& receipt.indexVersion === INDEX_VERSION && receipt.extractorVersion === INDEX_EXTRACTOR_VERSION
|
|
1069
|
+
&& receipt.buildConfigHash === buildConfigHash && receipt.sourceRoot === path.resolve(cwd)
|
|
1070
|
+
&& receipt.metadataSignature && receipt.metadataSignature === fileSignature('metadata.json')
|
|
1071
|
+
&& relationsReceiptMatches()
|
|
1072
|
+
&& receipt.entriesDigest === entriesDigest(storedManifest.entries)
|
|
1073
|
+
&& files.length === Object.keys(storedManifest.entries).length
|
|
1074
|
+
&& requiredFiles.every(filename => fs.existsSync(path.join(indexOutputDir, filename)))) {
|
|
1075
|
+
const stats = loadIndexFromDir(indexOutputDir, 'stats.json');
|
|
1076
|
+
if (stats?.version === INDEX_VERSION && stats.extractorVersion === INDEX_EXTRACTOR_VERSION
|
|
1077
|
+
&& stats.totalArtifacts === files.length && files.every(file => {
|
|
1078
|
+
try {
|
|
1079
|
+
return statMatches(fileStats?.get(file) ?? fs.statSync(file), storedManifest.entries[indexPathFor(cwd, file, graph)]);
|
|
1080
|
+
}
|
|
1081
|
+
catch {
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
1084
|
+
})) {
|
|
1085
|
+
unchangedCount = files.length;
|
|
1086
|
+
manifestStats.checked = files.length;
|
|
1087
|
+
manifestStats.statSkipped = files.length;
|
|
1088
|
+
const buildTimeMs = Date.now() - startTime;
|
|
1089
|
+
writeIndexFile(effectiveOutputCwd, 'stats.json', { ...stats, builtAt: new Date().toISOString(), buildTimeMs }, indexOutputDir);
|
|
1090
|
+
reportBuild(buildTimeMs, files.length);
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
const existingIndex = force ? null : outputDir
|
|
1095
|
+
? loadIndexFromDir(indexOutputDir, 'metadata.json')
|
|
1096
|
+
: loadGraphIndexFile(effectiveOutputCwd, 'metadata.json', graph);
|
|
1097
|
+
const canReuseExisting = existingIndex?.version === INDEX_VERSION
|
|
1098
|
+
&& existingIndex.extractorVersion === INDEX_EXTRACTOR_VERSION && existingIndex.buildConfigHash === buildConfigHash;
|
|
1099
|
+
const existingEntries = canReuseExisting ? existingIndex.entries : {};
|
|
1100
|
+
const manifest = canReuseExisting ? storedManifest : { version: 1, generated: '', entries: {} };
|
|
1101
|
+
// Establish the complete changed-file batch before any extraction/reduction.
|
|
1102
|
+
// Warm stat hits never create threads or read source contents.
|
|
1103
|
+
const preparationStats = new Map();
|
|
1104
|
+
const jobs = [];
|
|
1105
|
+
if (!useFilenameMetadata)
|
|
1106
|
+
for (const fullPath of files) {
|
|
1107
|
+
let stat;
|
|
1108
|
+
try {
|
|
1109
|
+
stat = fileStats?.get(fullPath) ?? fs.statSync(fullPath);
|
|
1110
|
+
}
|
|
1111
|
+
catch (error) {
|
|
1112
|
+
if (error.code === 'ENOENT')
|
|
1113
|
+
continue;
|
|
1114
|
+
throw error;
|
|
1115
|
+
}
|
|
1116
|
+
preparationStats.set(fullPath, stat);
|
|
1117
|
+
const relativePath = indexPathFor(cwd, fullPath, graph), previous = manifest.entries[relativePath];
|
|
1118
|
+
if (!force && statMatches(stat, previous) && existingEntries[relativePath]?.checksum === previous?.checksum)
|
|
1119
|
+
continue;
|
|
1120
|
+
jobs.push({ fullPath, relativePath, mtimeMs: stat.mtimeMs, birthtimeMs: stat.birthtimeMs, sizeBytes: stat.size,
|
|
1121
|
+
...(!force && existingEntries[relativePath] ? { existingChecksum: existingEntries[relativePath].checksum } : {}) });
|
|
1122
|
+
}
|
|
1123
|
+
const preparation = await runIndexPreparation(jobs, prepareIndexArtifact, import.meta.url, workers, options.onDiagnostic ?? (message => console.warn(`Index preparation: ${message}`)));
|
|
1124
|
+
const preparedArtifacts = new Map(jobs.map((job, index) => [job.fullPath, preparation.results[index]]));
|
|
930
1125
|
for (const fullPath of files) {
|
|
931
1126
|
const relativePath = indexPathFor(cwd, fullPath, graph);
|
|
932
1127
|
let entry;
|
|
@@ -944,7 +1139,7 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
944
1139
|
entries[relativePath] = existingEntries[relativePath];
|
|
945
1140
|
unchangedCount++;
|
|
946
1141
|
if (verbose)
|
|
947
|
-
|
|
1142
|
+
log(` unchanged: ${relativePath}`);
|
|
948
1143
|
continue;
|
|
949
1144
|
}
|
|
950
1145
|
entry = buildFilenameMetadataEntry(relativePath, fullPath, graphConfig?.filenamePattern);
|
|
@@ -956,13 +1151,13 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
956
1151
|
// This is the fast path: unchanged files don't touch the filesystem beyond fs.statSync.
|
|
957
1152
|
let stat;
|
|
958
1153
|
try {
|
|
959
|
-
stat = fs.statSync(fullPath);
|
|
1154
|
+
stat = preparationStats.get(fullPath) ?? fs.statSync(fullPath);
|
|
960
1155
|
}
|
|
961
1156
|
catch (err) {
|
|
962
1157
|
if (err.code === 'ENOENT') {
|
|
963
1158
|
manifestStats.pruned++;
|
|
964
1159
|
if (verbose)
|
|
965
|
-
|
|
1160
|
+
log(` skipped missing: ${relativePath}`);
|
|
966
1161
|
continue;
|
|
967
1162
|
}
|
|
968
1163
|
throw err;
|
|
@@ -976,98 +1171,70 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
976
1171
|
unchangedCount++;
|
|
977
1172
|
manifestStats.statSkipped++;
|
|
978
1173
|
if (verbose)
|
|
979
|
-
|
|
1174
|
+
log(` unchanged (stat): ${relativePath}`);
|
|
980
1175
|
continue;
|
|
981
1176
|
}
|
|
982
|
-
|
|
983
|
-
const
|
|
984
|
-
const checksum = computeChecksum(content);
|
|
985
|
-
// Skip unchanged files in incremental mode (checksum matched despite stat drift)
|
|
1177
|
+
const prepared = preparedArtifacts.get(fullPath);
|
|
1178
|
+
const { checksum } = prepared;
|
|
986
1179
|
if (!force && existingEntries[relativePath]?.checksum === checksum) {
|
|
987
1180
|
entries[relativePath] = existingEntries[relativePath];
|
|
988
|
-
// Update manifest with current stat so Phase 1 succeeds next time.
|
|
989
1181
|
nextManifestEntries[relativePath] = makeEntry(checksum, stat);
|
|
990
1182
|
unchangedCount++;
|
|
991
1183
|
manifestStats.checksumSkipped++;
|
|
992
1184
|
if (verbose)
|
|
993
|
-
|
|
1185
|
+
log(` unchanged (checksum): ${relativePath}`);
|
|
994
1186
|
continue;
|
|
995
1187
|
}
|
|
996
1188
|
manifestStats.reindexed++;
|
|
997
1189
|
nextManifestEntries[relativePath] = makeEntry(checksum, stat);
|
|
998
|
-
|
|
999
|
-
// YAML Flow documents (workflow.aiwg.io/v1) are pure YAML, not
|
|
1000
|
-
// markdown+frontmatter — detect them up front so they classify and
|
|
1001
|
-
// become discoverable (#1540).
|
|
1002
|
-
const flow = parseFlowDoc(content, relativePath);
|
|
1003
|
-
const decision = flow ? null : parseDecisionDoc(content, relativePath);
|
|
1004
|
-
const gatePack = flow || decision ? null : parseGatePackDoc(content, relativePath);
|
|
1005
|
-
const inferredType = inferType(data, relativePath);
|
|
1006
|
-
const physicalType = inferType({ ...data, type: undefined }, relativePath);
|
|
1007
|
-
const runbook = flow || decision || gatePack ? null : parseRunbookDoc(data, body, relativePath);
|
|
1008
|
-
const schemaDoc = inferredType === 'schema' ? parseSchemaDoc(content, relativePath) : null;
|
|
1009
|
-
const title = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.title ?? extractTitle(data, body);
|
|
1010
|
-
const phase = typeof data.phase === 'string' ? data.phase : inferPhase(relativePath);
|
|
1011
|
-
const type = flow?.type ?? decision?.type ?? gatePack?.type ?? (runbook ? 'runbook' : inferredType);
|
|
1012
|
-
const tags = flow?.tags ?? decision?.tags ?? gatePack?.tags ?? (Array.isArray(data.tags) ? data.tags.map(String) : []);
|
|
1013
|
-
const summary = flow?.description ?? decision?.description ?? gatePack?.description ?? schemaDoc?.capability ?? runbook?.capability ?? extractSummary(data, body);
|
|
1014
|
-
const dependencies = extractMentions(content);
|
|
1015
|
-
const markdownLinks = extractMarkdownLinks(content);
|
|
1016
|
-
// Discovery metadata (#1214, #1540, #1792) — meaningful for operational
|
|
1017
|
-
// AIWG artifact kinds. Kept undefined on document types so the index file
|
|
1018
|
-
// stays small for the common case.
|
|
1019
|
-
const isDiscoverable = OPERATIONAL_DISCOVERY_TYPES.includes(type);
|
|
1020
|
-
// Declarative processes have no trigger phrases — they rely on their
|
|
1021
|
-
// capability and structure-aware search terms.
|
|
1022
|
-
const triggers = isDiscoverable && !flow ? extractTriggers(body, data) : undefined;
|
|
1023
|
-
const capability = flow?.capability ?? decision?.capability ?? gatePack?.capability ?? schemaDoc?.capability ?? runbook?.capability ?? (isDiscoverable ? extractCapability(data, body) : undefined);
|
|
1024
|
-
const kind = flow?.kind ?? decision?.kind ?? gatePack?.kind ?? runbook?.kind;
|
|
1025
|
-
const sourceType = runbook && physicalType !== 'runbook' ? physicalType : undefined;
|
|
1026
|
-
const searchTerms = flow?.searchTerms ?? decision?.searchTerms ?? gatePack?.searchTerms ?? schemaDoc?.searchTerms ?? runbook?.searchTerms;
|
|
1027
|
-
const kernel = data.kernel === true || data.kernel === 'true' ? true : undefined;
|
|
1028
|
-
// Script entrypoint metadata is meaningful for skills only (#1227).
|
|
1029
|
-
const script = type === 'skill' ? extractSkillScript(data) : undefined;
|
|
1030
|
-
const operationalState = normalizeOperationalState(data.operational_state);
|
|
1031
|
-
const stateTransfer = normalizeStateTransferProjection(data.state_transfer);
|
|
1032
|
-
// Canonical short name (#1233) — used by the scorer to floor exact-name
|
|
1033
|
-
// queries to 1.0 so hyphenated kernel-skill names like `aiwg-doctor`
|
|
1034
|
-
// remain searchable even when the rendered title strips the hyphen.
|
|
1035
|
-
const name = flow?.name ?? decision?.name ?? gatePack?.name ?? schemaDoc?.name ?? (isDiscoverable ? extractCanonicalName(data, relativePath) : undefined);
|
|
1036
|
-
entry = {
|
|
1037
|
-
path: relativePath,
|
|
1038
|
-
type,
|
|
1039
|
-
...(kind ? { kind } : {}),
|
|
1040
|
-
...(sourceType ? { sourceType } : {}),
|
|
1041
|
-
phase,
|
|
1042
|
-
title,
|
|
1043
|
-
tags,
|
|
1044
|
-
created: typeof data.created === 'string' ? data.created : stat.birthtime.toISOString(),
|
|
1045
|
-
updated: stat.mtime.toISOString(),
|
|
1046
|
-
checksum,
|
|
1047
|
-
summary,
|
|
1048
|
-
dependencies,
|
|
1049
|
-
...(markdownLinks.length > 0 ? { markdownLinks } : {}),
|
|
1050
|
-
dependents: [], // Computed after all entries are processed
|
|
1051
|
-
...(name ? { name } : {}),
|
|
1052
|
-
...(triggers && triggers.length > 0 ? { triggers } : {}),
|
|
1053
|
-
...(capability ? { capability } : {}),
|
|
1054
|
-
...(searchTerms && searchTerms.length > 0 ? { searchTerms } : {}),
|
|
1055
|
-
...(kernel ? { kernel } : {}),
|
|
1056
|
-
...(script ? { script } : {}),
|
|
1057
|
-
...(operationalState ? { operationalState } : {}),
|
|
1058
|
-
...(stateTransfer ? { stateTransfer } : {}),
|
|
1059
|
-
};
|
|
1190
|
+
entry = prepared.entry;
|
|
1060
1191
|
}
|
|
1061
1192
|
entries[relativePath] = entry;
|
|
1062
1193
|
if (existingEntries[relativePath]) {
|
|
1063
1194
|
updatedCount++;
|
|
1064
1195
|
if (verbose)
|
|
1065
|
-
|
|
1196
|
+
log(` updated: ${relativePath}`);
|
|
1066
1197
|
}
|
|
1067
1198
|
else {
|
|
1068
1199
|
newCount++;
|
|
1069
1200
|
if (verbose)
|
|
1070
|
-
|
|
1201
|
+
log(` new: ${relativePath}`);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
// Reuse derived indexes only when their complete input set is unchanged.
|
|
1205
|
+
// Supplements can live outside the scanned files, so they must still be read.
|
|
1206
|
+
// Configured edge extractors must run to discard stale cached edges even
|
|
1207
|
+
// when source checksums match.
|
|
1208
|
+
const total = Object.keys(entries).length;
|
|
1209
|
+
if (canReuseExisting && !useFilenameMetadata && !graphConfig?.metadataSupplements?.length
|
|
1210
|
+
&& !graphConfig?.edgeExtraction
|
|
1211
|
+
&& newCount === 0 && updatedCount === 0
|
|
1212
|
+
&& total === Object.keys(existingEntries).length
|
|
1213
|
+
&& manifest.generated && total === Object.keys(manifest.entries).length
|
|
1214
|
+
&& manifestStats.pruned === 0) {
|
|
1215
|
+
const stats = loadIndexFromDir(indexOutputDir, 'stats.json');
|
|
1216
|
+
if (stats?.version === INDEX_VERSION && stats.extractorVersion === INDEX_EXTRACTOR_VERSION
|
|
1217
|
+
&& stats.totalArtifacts === total
|
|
1218
|
+
&& requiredFiles.every(filename => fs.existsSync(path.join(indexOutputDir, filename)))) {
|
|
1219
|
+
// A timestamp-only source change needs a manifest refresh, but does not
|
|
1220
|
+
// invalidate metadata, tags, dependencies, or the persistent backend.
|
|
1221
|
+
const projectionNeedsRepair = graph === 'framework' && !relationsReceiptMatches();
|
|
1222
|
+
if (graph === 'framework') {
|
|
1223
|
+
const { writeRelations } = await import('./relations-builder.js');
|
|
1224
|
+
await writeRelations(entries, effectiveOutputCwd, indexOutputDir, force || !relationsReceiptMatches());
|
|
1225
|
+
}
|
|
1226
|
+
const buildTimeMs = Date.now() - startTime;
|
|
1227
|
+
writeIndexFile(effectiveOutputCwd, 'stats.json', {
|
|
1228
|
+
...stats,
|
|
1229
|
+
builtAt: new Date().toISOString(),
|
|
1230
|
+
buildTimeMs,
|
|
1231
|
+
}, indexOutputDir);
|
|
1232
|
+
// Preserve no-op manifest bytes after output-only timestamp drift.
|
|
1233
|
+
// Renew receipts only for changed source stats or repaired projections.
|
|
1234
|
+
if (manifestStats.checksumSkipped > 0 || projectionNeedsRepair)
|
|
1235
|
+
writeBuildManifest(nextManifestEntries);
|
|
1236
|
+
reportBuild(buildTimeMs, total);
|
|
1237
|
+
return;
|
|
1071
1238
|
}
|
|
1072
1239
|
}
|
|
1073
1240
|
// Apply metadata supplements if configured (enriches filename-metadata nodes from sidecars)
|
|
@@ -1084,13 +1251,18 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1084
1251
|
}
|
|
1085
1252
|
// Build dependency graph and compute dependents
|
|
1086
1253
|
let markdownLinkEdgeCount = 0;
|
|
1254
|
+
const entryPaths = Object.keys(entries);
|
|
1255
|
+
const dependencyPaths = new Map();
|
|
1087
1256
|
for (const entry of Object.values(entries)) {
|
|
1088
1257
|
if (!depGraph[entry.path]) {
|
|
1089
1258
|
depGraph[entry.path] = { upstream: [], downstream: [] };
|
|
1090
1259
|
}
|
|
1091
1260
|
for (const dep of entry.dependencies) {
|
|
1092
1261
|
// Normalize: check if referenced path exists in the index
|
|
1093
|
-
|
|
1262
|
+
if (!dependencyPaths.has(dep)) {
|
|
1263
|
+
dependencyPaths.set(dep, entryPaths.find(p => p === dep || p.endsWith(dep)));
|
|
1264
|
+
}
|
|
1265
|
+
const normalizedDep = dependencyPaths.get(dep);
|
|
1094
1266
|
if (normalizedDep && normalizedDep !== entry.path) {
|
|
1095
1267
|
addDependencyEdge(depGraph, entries, entry.path, normalizedDep, 'depends-on');
|
|
1096
1268
|
}
|
|
@@ -1173,18 +1345,23 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1173
1345
|
.filter(edge => !canonicalOutgoing.has(edge)).length,
|
|
1174
1346
|
};
|
|
1175
1347
|
if (verbose && citationEdgeCount > 0) {
|
|
1176
|
-
|
|
1348
|
+
log(` citation edges: ${citationEdgeCount}`);
|
|
1177
1349
|
}
|
|
1178
1350
|
}
|
|
1179
1351
|
// Deduplicate dependents
|
|
1180
1352
|
for (const entry of Object.values(entries)) {
|
|
1181
1353
|
entry.dependents = [...new Set(entry.dependents)];
|
|
1182
1354
|
}
|
|
1355
|
+
if (graph === 'framework') {
|
|
1356
|
+
const { writeRelations } = await import('./relations-builder.js');
|
|
1357
|
+
await writeRelations(entries, effectiveOutputCwd, indexOutputDir, force || !relationsReceiptMatches());
|
|
1358
|
+
}
|
|
1183
1359
|
const buildTimeMs = Date.now() - startTime;
|
|
1184
1360
|
// Write index files
|
|
1185
1361
|
const index = {
|
|
1186
1362
|
version: INDEX_VERSION,
|
|
1187
1363
|
extractorVersion: INDEX_EXTRACTOR_VERSION,
|
|
1364
|
+
buildConfigHash,
|
|
1188
1365
|
builtAt: new Date().toISOString(),
|
|
1189
1366
|
buildTimeMs,
|
|
1190
1367
|
entries,
|
|
@@ -1195,8 +1372,6 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1195
1372
|
// Materialize the configured backend and always retain dependencies.json as
|
|
1196
1373
|
// the stable compatibility/export contract.
|
|
1197
1374
|
const { createGraphBackend } = await import('./graph-backend.js');
|
|
1198
|
-
const { resolveGraphBackendType } = await import('./types.js');
|
|
1199
|
-
const backendType = resolveGraphBackendType(graph);
|
|
1200
1375
|
const persistentPath = backendType === 'sqlite' ? path.join(indexOutputDir, 'graph.db') : undefined;
|
|
1201
1376
|
let selectedBackend;
|
|
1202
1377
|
try {
|
|
@@ -1210,15 +1385,9 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1210
1385
|
// The next manifest contains entries for every file we processed this build.
|
|
1211
1386
|
// Files that disappeared from disk are pruned; the resulting manifest is
|
|
1212
1387
|
// written atomically.
|
|
1213
|
-
const nextManifest = {
|
|
1214
|
-
version: 1,
|
|
1215
|
-
generated: new Date().toISOString(),
|
|
1216
|
-
entries: nextManifestEntries,
|
|
1217
|
-
};
|
|
1218
1388
|
manifestStats.pruned = Object.keys(manifest.entries).length - Object.keys(nextManifestEntries).length;
|
|
1219
1389
|
if (manifestStats.pruned < 0)
|
|
1220
1390
|
manifestStats.pruned = 0;
|
|
1221
|
-
writeManifest(indexOutputDir, nextManifest);
|
|
1222
1391
|
// Write stats
|
|
1223
1392
|
const downstreamEdges = Object.values(depGraph).reduce((sum, node) => sum + node.downstream.length, 0);
|
|
1224
1393
|
const adjacencyEntries = Object.values(depGraph).reduce((sum, node) => sum + node.upstream.length + node.downstream.length, 0);
|
|
@@ -1262,22 +1431,8 @@ export async function buildIndex(cwd, options = {}) {
|
|
|
1262
1431
|
mostReferenced,
|
|
1263
1432
|
},
|
|
1264
1433
|
}, indexOutputDir);
|
|
1434
|
+
writeBuildManifest(nextManifestEntries);
|
|
1265
1435
|
// Report
|
|
1266
|
-
|
|
1267
|
-
console.log(`Artifact index built in ${buildTimeMs}ms`);
|
|
1268
|
-
console.log(` Indexed ${newCount} new, updated ${updatedCount}, unchanged ${unchangedCount}`);
|
|
1269
|
-
console.log(` Total: ${total} artifacts`);
|
|
1270
|
-
// Report manifest-driven change detection stats (only meaningful when !force
|
|
1271
|
-
// and we actually went through the content-read path for at least one file)
|
|
1272
|
-
if (!force && manifestStats.checked > 0) {
|
|
1273
|
-
const fastPath = manifestStats.statSkipped;
|
|
1274
|
-
const slowPath = manifestStats.checked - manifestStats.statSkipped;
|
|
1275
|
-
console.log(` Change detection: ${manifestStats.checked} checked, ${fastPath} skipped via stat, ${slowPath} content-read, ${manifestStats.reindexed} re-indexed`);
|
|
1276
|
-
if (manifestStats.pruned > 0) {
|
|
1277
|
-
console.log(` Pruned ${manifestStats.pruned} stale manifest entries (files no longer on disk)`);
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
const displayDir = graph ? indexOutputDir : `${INDEX_DIR}/`;
|
|
1281
|
-
console.log(` Output: ${displayDir}`);
|
|
1436
|
+
reportBuild(buildTimeMs, total);
|
|
1282
1437
|
}
|
|
1283
1438
|
//# sourceMappingURL=index-builder.js.map
|