@aiwg/cli 0.0.0-bootstrap.0 → 2026.7.17
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 +21 -1
- package/bin/aiwg.mjs +349 -0
- package/dist/src/a2a/agent-card.js +145 -0
- package/dist/src/a2a/client.js +411 -0
- package/dist/src/a2a/hitl-cli.js +171 -0
- package/dist/src/a2a/hitl-driver.js +370 -0
- package/dist/src/a2a/hitl.js +181 -0
- package/dist/src/a2a/http.js +193 -0
- package/dist/src/a2a/jcs.js +128 -0
- package/dist/src/a2a/jws.js +187 -0
- package/dist/src/a2a/types.js +24 -0
- package/dist/src/a2a/webhook.js +240 -0
- package/dist/src/activity-log/cli.js +348 -0
- package/dist/src/activity-log/parser.js +81 -0
- package/dist/src/activity-log/types.js +50 -0
- package/dist/src/agents/agent-deployer.js +367 -0
- package/dist/src/agents/agent-packager.js +271 -0
- package/dist/src/agents/agent-validator.js +271 -0
- package/dist/src/agents/grounding/knowledge-base.js +161 -0
- package/dist/src/agents/grounding/types.js +12 -0
- package/dist/src/agents/index.js +10 -0
- package/dist/src/agents/packaged-agent-inventory.js +75 -0
- package/dist/src/agents/types.js +7 -0
- package/dist/src/api/index.d.ts +10 -0
- package/dist/src/api/index.js +10 -0
- package/dist/src/artifacts/address-parser.js +73 -0
- package/dist/src/artifacts/audit/cli.js +183 -0
- package/dist/src/artifacts/audit/drift.js +122 -0
- package/dist/src/artifacts/audit/types.js +12 -0
- package/dist/src/artifacts/backends/graphology-backend.js +160 -0
- package/dist/src/artifacts/backends/json-backend.js +129 -0
- package/dist/src/artifacts/backends/sqlite-backend.js +199 -0
- package/dist/src/artifacts/browser-export.js +622 -0
- package/dist/src/artifacts/capability-resolver.js +94 -0
- package/dist/src/artifacts/checksum-manifest.js +91 -0
- package/dist/src/artifacts/citation-parser.js +162 -0
- package/dist/src/artifacts/cli.js +1462 -0
- package/dist/src/artifacts/corpus-tools/citation-densify.js +311 -0
- package/dist/src/artifacts/corpus-tools/cli.js +393 -0
- package/dist/src/artifacts/corpus-tools/corpus-graph.js +225 -0
- package/dist/src/artifacts/corpus-tools/curator.js +186 -0
- package/dist/src/artifacts/corpus-tools/discovery-log.js +70 -0
- package/dist/src/artifacts/corpus-tools/funder-network.js +141 -0
- package/dist/src/artifacts/corpus-tools/induction-audit.js +233 -0
- package/dist/src/artifacts/corpus-tools/integrity-scan.js +202 -0
- package/dist/src/artifacts/corpus-tools/profile-communities.js +166 -0
- package/dist/src/artifacts/corpus-tools/profile-edges.js +72 -0
- package/dist/src/artifacts/corpus-tools/profile-embed.js +113 -0
- package/dist/src/artifacts/corpus-tools/profile-generate-fm.js +167 -0
- package/dist/src/artifacts/corpus-tools/profile-generate.js +181 -0
- package/dist/src/artifacts/corpus-tools/profile-metrics.js +98 -0
- package/dist/src/artifacts/corpus-tools/profile-status.js +86 -0
- package/dist/src/artifacts/corpus-tools/profile-temporal.js +139 -0
- package/dist/src/artifacts/corpus-tools/radar-init.js +93 -0
- package/dist/src/artifacts/corpus-tools/radar-report.js +118 -0
- package/dist/src/artifacts/corpus-tools/radar-shared.js +189 -0
- package/dist/src/artifacts/corpus-tools/radar-status.js +68 -0
- package/dist/src/artifacts/corpus-tools/sidecar-lint.js +233 -0
- package/dist/src/artifacts/corpus-tools/sidecar-repair.js +277 -0
- package/dist/src/artifacts/corpus-tools/snapshot.js +591 -0
- package/dist/src/artifacts/corpus-tools/source-types.js +164 -0
- package/dist/src/artifacts/corpus-tools/vision-extract.js +243 -0
- package/dist/src/artifacts/corpus-views/build.js +126 -0
- package/dist/src/artifacts/corpus-views/corpus-config.js +124 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +437 -0
- package/dist/src/artifacts/corpus-views/renderers.js +353 -0
- package/dist/src/artifacts/corpus-views/taxonomies.js +131 -0
- package/dist/src/artifacts/dep-graph.js +173 -0
- package/dist/src/artifacts/discover-facets.js +375 -0
- package/dist/src/artifacts/embedding-index.js +354 -0
- package/dist/src/artifacts/enrichment/cli.js +240 -0
- package/dist/src/artifacts/enrichment/prompt.js +47 -0
- package/dist/src/artifacts/enrichment/store.js +90 -0
- package/dist/src/artifacts/enrichment/types.js +12 -0
- package/dist/src/artifacts/fortemi-core-query-adapter.js +475 -0
- package/dist/src/artifacts/fortemi-core-sync.js +251 -0
- package/dist/src/artifacts/fortemi-shard-export.js +44 -0
- package/dist/src/artifacts/fulltext.js +108 -0
- package/dist/src/artifacts/graph-backend.js +51 -0
- package/dist/src/artifacts/graph-query.js +247 -0
- package/dist/src/artifacts/hybrid-query.js +225 -0
- package/dist/src/artifacts/index-builder.js +1089 -0
- package/dist/src/artifacts/index-reader.js +112 -0
- package/dist/src/artifacts/index-status.js +193 -0
- package/dist/src/artifacts/legacy-index-migration.js +169 -0
- package/dist/src/artifacts/move.js +154 -0
- package/dist/src/artifacts/operational-state.js +239 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +49 -0
- package/dist/src/artifacts/query-engine.js +1955 -0
- package/dist/src/artifacts/source-graph.js +529 -0
- package/dist/src/artifacts/stats.js +169 -0
- package/dist/src/artifacts/types.js +606 -0
- package/dist/src/artifacts/views/cli.js +293 -0
- package/dist/src/artifacts/views/definition.js +170 -0
- package/dist/src/artifacts/views/store.js +149 -0
- package/dist/src/artifacts/views/types.js +8 -0
- package/dist/src/artifacts/watcher.js +255 -0
- package/dist/src/catalog/builtin-models.json +636 -0
- package/dist/src/catalog/cli.js +228 -0
- package/dist/src/catalog/cli.mjs +289 -0
- package/dist/src/catalog/index.js +15 -0
- package/dist/src/catalog/loader.js +217 -0
- package/dist/src/catalog/loader.mjs +251 -0
- package/dist/src/catalog/sources.json +41 -0
- package/dist/src/catalog/types.js +8 -0
- package/dist/src/channel/manager.mjs +504 -0
- package/dist/src/cli/agent-spawn.js +178 -0
- package/dist/src/cli/cli-extension-loader.js +245 -0
- package/dist/src/cli/command-log.js +275 -0
- package/dist/src/cli/config-loader.js +232 -0
- package/dist/src/cli/env.js +82 -0
- package/dist/src/cli/errors.js +150 -0
- package/dist/src/cli/find-package-root.js +40 -0
- package/dist/src/cli/git-hooks.js +250 -0
- package/dist/src/cli/handlers/agentcard.js +206 -0
- package/dist/src/cli/handlers/artifacts.js +76 -0
- package/dist/src/cli/handlers/best-practices-audit.js +280 -0
- package/dist/src/cli/handlers/cockpit.js +137 -0
- package/dist/src/cli/handlers/command-log.js +37 -0
- package/dist/src/cli/handlers/daemon.js +59 -0
- package/dist/src/cli/handlers/diagnose.js +300 -0
- package/dist/src/cli/handlers/execution-mode.js +105 -0
- package/dist/src/cli/handlers/feedback.js +341 -0
- package/dist/src/cli/handlers/help.js +147 -0
- package/dist/src/cli/handlers/index.js +302 -0
- package/dist/src/cli/handlers/init.js +179 -0
- package/dist/src/cli/handlers/install.js +129 -0
- package/dist/src/cli/handlers/issues.js +46 -0
- package/dist/src/cli/handlers/lint.js +42 -0
- package/dist/src/cli/handlers/local-executor.js +315 -0
- package/dist/src/cli/handlers/marketplace.js +168 -0
- package/dist/src/cli/handlers/mc.js +1010 -0
- package/dist/src/cli/handlers/models.js +352 -0
- package/dist/src/cli/handlers/packages.js +155 -0
- package/dist/src/cli/handlers/ralph-launcher.js +717 -0
- package/dist/src/cli/handlers/ralph.js +575 -0
- package/dist/src/cli/handlers/refresh.js +432 -0
- package/dist/src/cli/handlers/regenerate.js +336 -0
- package/dist/src/cli/handlers/repo-access.js +109 -0
- package/dist/src/cli/handlers/run.js +165 -0
- package/dist/src/cli/handlers/runtime-info.js +301 -0
- package/dist/src/cli/handlers/sandbox.js +197 -0
- package/dist/src/cli/handlers/scaffolding.js +149 -0
- package/dist/src/cli/handlers/script-runner.js +121 -0
- package/dist/src/cli/handlers/sdlc-accelerate.js +148 -0
- package/dist/src/cli/handlers/serve.js +1835 -0
- package/dist/src/cli/handlers/session.js +348 -0
- package/dist/src/cli/handlers/setup.js +440 -0
- package/dist/src/cli/handlers/skill-lint.js +367 -0
- package/dist/src/cli/handlers/skill-usage.js +74 -0
- package/dist/src/cli/handlers/steward.js +567 -0
- package/dist/src/cli/handlers/subcommands.js +1702 -0
- package/dist/src/cli/handlers/team.js +337 -0
- package/dist/src/cli/handlers/types.js +11 -0
- package/dist/src/cli/handlers/use.js +2772 -0
- package/dist/src/cli/handlers/utilities.js +703 -0
- package/dist/src/cli/handlers/version.js +134 -0
- package/dist/src/cli/handlers/workspace-context.js +93 -0
- package/dist/src/cli/handlers/workspace.js +126 -0
- package/dist/src/cli/help-generator.js +178 -0
- package/dist/src/cli/hooks/builtin/activity-log-hook.js +126 -0
- package/dist/src/cli/hooks/executor.js +96 -0
- package/dist/src/cli/hooks/index.js +15 -0
- package/dist/src/cli/hooks/registry.js +143 -0
- package/dist/src/cli/hooks/types.js +13 -0
- package/dist/src/cli/log.js +492 -0
- package/dist/src/cli/project-isolation/detect.js +70 -0
- package/dist/src/cli/project-isolation/index.js +7 -0
- package/dist/src/cli/project-isolation/signals.js +17 -0
- package/dist/src/cli/project-isolation/warning.js +102 -0
- package/dist/src/cli/prompt-utils.js +155 -0
- package/dist/src/cli/provider-deployment-plan.js +126 -0
- package/dist/src/cli/provider-resolution.js +119 -0
- package/dist/src/cli/router.js +305 -0
- package/dist/src/cli/scope-resolver.js +392 -0
- package/dist/src/cli/skill-usage.js +616 -0
- package/dist/src/cli/ui.js +224 -0
- package/dist/src/cli/watch-service.js +203 -0
- package/dist/src/cli/workflow-orchestrator.js +478 -0
- package/dist/src/cli/workspace-signals.js +321 -0
- package/dist/src/community/footer.js +14 -0
- package/dist/src/community/links.js +123 -0
- package/dist/src/community/milestones.js +46 -0
- package/dist/src/community/nudge-policy.js +93 -0
- package/dist/src/config/aiwg-config.js +883 -0
- package/dist/src/config/cli.js +703 -0
- package/dist/src/config/gitignore.js +156 -0
- package/dist/src/config/project-artifacts.js +76 -0
- package/dist/src/config/user-config.js +428 -0
- package/dist/src/config/user-registry.js +127 -0
- package/dist/src/config/workspace.js +276 -0
- package/dist/src/contributors/detect.js +39 -0
- package/dist/src/contributors/discover.js +172 -0
- package/dist/src/contributors/heuristic.js +186 -0
- package/dist/src/contributors/index.js +11 -0
- package/dist/src/contributors/types.js +13 -0
- package/dist/src/contributors/validation.js +116 -0
- package/dist/src/data/community.schema.json +43 -0
- package/dist/src/data/community.yaml +16 -0
- package/dist/src/extensions/capability-index.js +270 -0
- package/dist/src/extensions/claude-hooks-installer.js +268 -0
- package/dist/src/extensions/commands/definitions.js +3485 -0
- package/dist/src/extensions/deployment-registration.js +463 -0
- package/dist/src/extensions/index.js +22 -0
- package/dist/src/extensions/loader.js +132 -0
- package/dist/src/extensions/managed-marker.js +100 -0
- package/dist/src/extensions/manifest.js +196 -0
- package/dist/src/extensions/project-local-activity.js +114 -0
- package/dist/src/extensions/project-local-discovery.js +261 -0
- package/dist/src/extensions/project-local-doctor.js +281 -0
- package/dist/src/extensions/project-local-gitignore.js +176 -0
- package/dist/src/extensions/project-local-paths.js +121 -0
- package/dist/src/extensions/project-local-promote.js +218 -0
- package/dist/src/extensions/project-local-remove.js +373 -0
- package/dist/src/extensions/project-local-scaffold.js +252 -0
- package/dist/src/extensions/project-quickref.js +291 -0
- package/dist/src/extensions/registry.js +368 -0
- package/dist/src/extensions/shadow-resolver.js +271 -0
- package/dist/src/extensions/types.js +86 -0
- package/dist/src/extensions/upstream-registry.js +195 -0
- package/dist/src/extensions/validation.js +631 -0
- package/dist/src/features/catalog.js +88 -0
- package/dist/src/features/cli.js +197 -0
- package/dist/src/features/installer.js +57 -0
- package/dist/src/features/paths.js +14 -0
- package/dist/src/features/runtime.js +21 -0
- package/dist/src/features/status.js +134 -0
- package/dist/src/hooks/laziness-detection.js +532 -0
- package/dist/src/intake/git-history-analyzer.js +486 -0
- package/dist/src/intake/index.js +9 -0
- package/dist/src/issues/cli.js +327 -0
- package/dist/src/issues/index.js +5 -0
- package/dist/src/issues/live.js +154 -0
- package/dist/src/issues/local.js +666 -0
- package/dist/src/issues/sync.js +143 -0
- package/dist/src/issues/types.js +2 -0
- package/dist/src/issues/workflows.js +164 -0
- package/dist/src/kb/cli.js +173 -0
- package/dist/src/lint/cli.js +171 -0
- package/dist/src/lint/loader.js +225 -0
- package/dist/src/lint/reporters.js +141 -0
- package/dist/src/lint/runner.js +364 -0
- package/dist/src/lint/types.js +10 -0
- package/dist/src/marketplace/cache.js +110 -0
- package/dist/src/marketplace/identifier.js +63 -0
- package/dist/src/marketplace/registry.js +36 -0
- package/dist/src/marketplace/sources/clawhub.js +53 -0
- package/dist/src/marketplace/sources/git.js +45 -0
- package/dist/src/marketplace/types.js +9 -0
- package/dist/src/mcp/adapters/codex-runtime.js +224 -0
- package/dist/src/mcp/cli.mjs +1197 -0
- package/dist/src/mcp/elicitation.mjs +149 -0
- package/dist/src/mcp/helpers.mjs +287 -0
- package/dist/src/mcp/index.mjs +11 -0
- package/dist/src/mcp/profiles.js +263 -0
- package/dist/src/mcp/profiles.mjs +360 -0
- package/dist/src/mcp/registry.js +376 -0
- package/dist/src/mcp/registry.mjs +387 -0
- package/dist/src/mcp/server.mjs +633 -0
- package/dist/src/mcp/test-simple.mjs +271 -0
- package/dist/src/mcp/tools/command-run.mjs +101 -0
- package/dist/src/mcp/tools/discovery.mjs +291 -0
- package/dist/src/mcp/tools/interaction.mjs +87 -0
- package/dist/src/mcp/tools/orchestration.mjs +320 -0
- package/dist/src/mcp/tools/subsystems.mjs +640 -0
- package/dist/src/memory/cli.js +166 -0
- package/dist/src/memory/project-registry.js +282 -0
- package/dist/src/metrics/artifact-metrics.js +184 -0
- package/dist/src/metrics/context-budget.js +174 -0
- package/dist/src/metrics/token-counter.js +133 -0
- package/dist/src/models/config-loader.js +228 -0
- package/dist/src/models/index.js +22 -0
- package/dist/src/models/model-capabilities.v1.json +120 -0
- package/dist/src/models/model-catalog.v1.json +96 -0
- package/dist/src/models/model-discovery.js +521 -0
- package/dist/src/models/provider-models.js +73 -0
- package/dist/src/models/provider-policy.js +273 -0
- package/dist/src/models/resolver.js +245 -0
- package/dist/src/models/router.js +70 -0
- package/dist/src/models/types.js +8 -0
- package/dist/src/models/wrapper-deployment.js +178 -0
- package/dist/src/models/wrapper-route.js +54 -0
- package/dist/src/ops/cli.js +268 -0
- package/dist/src/ops/registry.js +623 -0
- package/dist/src/packages/adapters/clawhub.js +113 -0
- package/dist/src/packages/adapters/git.js +153 -0
- package/dist/src/packages/adapters/gitea.js +93 -0
- package/dist/src/packages/adapters/github.js +69 -0
- package/dist/src/packages/adapters/local-cache.js +89 -0
- package/dist/src/packages/package-registry.js +143 -0
- package/dist/src/packages/registry.js +238 -0
- package/dist/src/packages/types.js +11 -0
- package/dist/src/plugin/cross-framework-ops.js +475 -0
- package/dist/src/plugin/framework-config-loader.js +235 -0
- package/dist/src/plugin/framework-detector.js +186 -0
- package/dist/src/plugin/framework-isolator.js +341 -0
- package/dist/src/plugin/framework-migration.js +371 -0
- package/dist/src/plugin/metadata-validator.js +906 -0
- package/dist/src/plugin/plugin-installer.js +436 -0
- package/dist/src/plugin/plugin-status.js +369 -0
- package/dist/src/plugin/plugin-uninstaller.js +473 -0
- package/dist/src/plugin/registry-validator.js +344 -0
- package/dist/src/plugin/skill-command-translator.js +415 -0
- package/dist/src/plugin/workspace-creator.js +189 -0
- package/dist/src/plugin/workspace-migrator.js +821 -0
- package/dist/src/policy/authorization.js +390 -0
- package/dist/src/policy/repo-access.js +215 -0
- package/dist/src/provenance/cli.js +16 -0
- package/dist/src/providers/capability-matrix.js +300 -0
- package/dist/src/providers/capability-matrix.yaml +514 -0
- package/dist/src/providers/provider-definitions.js +977 -0
- package/dist/src/providers/provider-definitions.mjs +159 -0
- package/dist/src/providers/provider-inventory.js +169 -0
- package/dist/src/quality/feedback-ab.js +174 -0
- package/dist/src/quality/feedback-collector.js +176 -0
- package/dist/src/quality/feedback-tracker.js +123 -0
- package/dist/src/quality/patterns/adr.json +24 -0
- package/dist/src/quality/patterns/sad.json +24 -0
- package/dist/src/quality/patterns/test-plan.json +24 -0
- package/dist/src/quality/patterns/use-case.json +24 -0
- package/dist/src/quality/scoring.js +157 -0
- package/dist/src/reflections/cli.js +15 -0
- package/dist/src/release/plan.js +139 -0
- package/dist/src/research/cache/manager.js +227 -0
- package/dist/src/research/clients/arxiv.js +156 -0
- package/dist/src/research/clients/base.js +197 -0
- package/dist/src/research/clients/crossref.js +112 -0
- package/dist/src/research/clients/semantic-scholar.js +126 -0
- package/dist/src/research/clients/unpaywall.js +87 -0
- package/dist/src/research/index.js +14 -0
- package/dist/src/research/query-cli.js +336 -0
- package/dist/src/research/services/acquisition.js +313 -0
- package/dist/src/research/services/archival.js +225 -0
- package/dist/src/research/services/citation.js +279 -0
- package/dist/src/research/services/discovery.js +243 -0
- package/dist/src/research/services/documentation.js +269 -0
- package/dist/src/research/services/index.js +14 -0
- package/dist/src/research/services/provenance.js +279 -0
- package/dist/src/research/services/quality.js +370 -0
- package/dist/src/research/services/types.js +7 -0
- package/dist/src/research/storage-cli.js +20 -0
- package/dist/src/research/types.js +47 -0
- package/dist/src/resources/index.d.ts +2 -0
- package/dist/src/resources/index.js +2 -0
- package/dist/src/resources/web-release.d.ts +89 -0
- package/dist/src/resources/web-release.js +882 -0
- package/dist/src/rlm/cache/cli.js +162 -0
- package/dist/src/rlm/cache/hash.js +35 -0
- package/dist/src/rlm/cache/store.js +170 -0
- package/dist/src/rlm/cache/types.js +8 -0
- package/dist/src/rlm/cli.js +650 -0
- package/dist/src/serve/a2a-terminal-observer.js +120 -0
- package/dist/src/serve/agent-router.js +205 -0
- package/dist/src/serve/dispatch-router.js +171 -0
- package/dist/src/serve/executor-registry.js +715 -0
- package/dist/src/serve/mission-conductor.js +177 -0
- package/dist/src/serve/orchestrator-adapter.js +113 -0
- package/dist/src/serve/orchestrator-override.js +94 -0
- package/dist/src/serve/orchestrator-pty.js +133 -0
- package/dist/src/serve/pty-bridge.js +799 -0
- package/dist/src/serve/sandbox-registry.js +832 -0
- package/dist/src/serve/screen-reader.js +228 -0
- package/dist/src/serve/stack-adapters.js +68 -0
- package/dist/src/serve/telemetry.js +143 -0
- package/dist/src/skills/adapters/clawhub.js +250 -0
- package/dist/src/skills/adapters/local.js +335 -0
- package/dist/src/skills/adapters/openclaw.js +316 -0
- package/dist/src/skills/cli.js +334 -0
- package/dist/src/skills/registry.js +117 -0
- package/dist/src/skills/run.js +259 -0
- package/dist/src/skills/runtime.js +94 -0
- package/dist/src/skills/types.js +10 -0
- package/dist/src/smiths/agentsmith/demo.js +116 -0
- package/dist/src/smiths/agentsmith/examples.js +300 -0
- package/dist/src/smiths/agentsmith/generator.js +503 -0
- package/dist/src/smiths/agentsmith/index.js +11 -0
- package/dist/src/smiths/agentsmith/types.js +8 -0
- package/dist/src/smiths/commandsmith/example.js +220 -0
- package/dist/src/smiths/commandsmith/generator.js +288 -0
- package/dist/src/smiths/commandsmith/index.js +41 -0
- package/dist/src/smiths/commandsmith/templates.js +296 -0
- package/dist/src/smiths/commandsmith/types.js +7 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +104 -0
- package/dist/src/smiths/context-pipeline/allowlist.js +195 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +127 -0
- package/dist/src/smiths/context-pipeline/discovery.js +187 -0
- package/dist/src/smiths/context-pipeline/external-links-section.js +49 -0
- package/dist/src/smiths/context-pipeline/finalization.js +147 -0
- package/dist/src/smiths/context-pipeline/generator.js +477 -0
- package/dist/src/smiths/context-pipeline/index.js +30 -0
- package/dist/src/smiths/context-pipeline/legacy-inject.js +103 -0
- package/dist/src/smiths/context-pipeline/managed-hook.js +95 -0
- package/dist/src/smiths/context-pipeline/overflow.js +212 -0
- package/dist/src/smiths/context-pipeline/parallelism-section.js +94 -0
- package/dist/src/smiths/context-pipeline/provider-policy.js +64 -0
- package/dist/src/smiths/context-pipeline/sanitizer.js +93 -0
- package/dist/src/smiths/context-pipeline/types.js +23 -0
- package/dist/src/smiths/context-pipeline/workspace-context.js +988 -0
- package/dist/src/smiths/hook-bridge/codex-translator.js +129 -0
- package/dist/src/smiths/hook-bridge/copilot-translator.js +64 -0
- package/dist/src/smiths/hook-bridge/factory-translator.js +43 -0
- package/dist/src/smiths/hook-bridge/hermes-translator.js +88 -0
- package/dist/src/smiths/hook-bridge/index.js +79 -0
- package/dist/src/smiths/hook-bridge/loader.js +116 -0
- package/dist/src/smiths/hook-bridge/shim.js +89 -0
- package/dist/src/smiths/hook-bridge/types.js +31 -0
- package/dist/src/smiths/index.js +39 -0
- package/dist/src/smiths/mcpsmith/analyzers/cli-analyzer.js +390 -0
- package/dist/src/smiths/mcpsmith/example.js +119 -0
- package/dist/src/smiths/mcpsmith/generator.js +236 -0
- package/dist/src/smiths/mcpsmith/index.js +17 -0
- package/dist/src/smiths/mcpsmith/types.js +10 -0
- package/dist/src/smiths/platform-paths.js +101 -0
- package/dist/src/smiths/skillsmith/codex-sidecar.js +85 -0
- package/dist/src/smiths/skillsmith/collision-detector.js +249 -0
- package/dist/src/smiths/skillsmith/demo.js +83 -0
- package/dist/src/smiths/skillsmith/examples.js +161 -0
- package/dist/src/smiths/skillsmith/generator.js +316 -0
- package/dist/src/smiths/skillsmith/index.js +12 -0
- package/dist/src/smiths/skillsmith/namespace-adapter.js +180 -0
- package/dist/src/smiths/skillsmith/platform-resolver.js +157 -0
- package/dist/src/smiths/skillsmith/types.js +9 -0
- package/dist/src/smiths/toolsmith/index.js +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +710 -0
- package/dist/src/smiths/toolsmith/types.js +8 -0
- package/dist/src/storage/backends/fortemi.js +227 -0
- package/dist/src/storage/backends/fs.js +134 -0
- package/dist/src/storage/backends/logseq.js +309 -0
- package/dist/src/storage/backends/obsidian.js +233 -0
- package/dist/src/storage/cli.js +468 -0
- package/dist/src/storage/config.js +272 -0
- package/dist/src/storage/index.js +108 -0
- package/dist/src/storage/subsystem-cli.js +201 -0
- package/dist/src/storage/types.js +33 -0
- package/dist/src/testing/corpus/corpus-builder.js +383 -0
- package/dist/src/testing/corpus/ground-truth-manager.js +460 -0
- package/dist/src/testing/corpus/index.js +10 -0
- package/dist/src/testing/fixtures/index.js +9 -0
- package/dist/src/testing/fixtures/test-data-factory.js +472 -0
- package/dist/src/testing/generators/index.js +11 -0
- package/dist/src/testing/generators/test-case-generator.js +393 -0
- package/dist/src/testing/generators/test-code-generator.js +384 -0
- package/dist/src/testing/generators/use-case-parser.js +325 -0
- package/dist/src/testing/index.js +11 -0
- package/dist/src/tracker/capability-protocol.js +118 -0
- package/dist/src/update/checker.mjs +396 -0
- package/dist/src/update/notifier.mjs +235 -0
- package/dist/src/writing/content-diversifier.js +804 -0
- package/dist/src/writing/example-generator.js +959 -0
- package/dist/src/writing/example-templates.js +99 -0
- package/dist/src/writing/pattern-library.js +486 -0
- package/dist/src/writing/patterns/banned-phrases.json +4413 -0
- package/dist/src/writing/patterns/formulaic-structures.json +1300 -0
- package/dist/src/writing/patterns/generic-adjectives.json +1510 -0
- package/dist/src/writing/patterns/hedging-language.json +2096 -0
- package/dist/src/writing/patterns/transition-words.json +1285 -0
- package/dist/src/writing/patterns/weak-verbs.json +1112 -0
- package/dist/src/writing/prompt-optimizer.js +660 -0
- package/dist/src/writing/prompt-templates.js +583 -0
- package/dist/src/writing/scoring-config-loader.js +168 -0
- package/dist/src/writing/validation-engine.js +595 -0
- package/dist/src/writing/validation-rules.js +380 -0
- package/dist/src/writing/voice-analyzer.js +439 -0
- package/dist/src/writing/voice-calibration.js +661 -0
- package/dist/src/writing/voice-profiles.json +588 -0
- package/package.json +64 -9
|
@@ -0,0 +1,1089 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact Index Builder
|
|
3
|
+
*
|
|
4
|
+
* Scans .aiwg/ directories, extracts metadata from artifact frontmatter,
|
|
5
|
+
* computes checksums, extracts @-mention dependencies, and builds a
|
|
6
|
+
* structured index at .aiwg/.index/.
|
|
7
|
+
*
|
|
8
|
+
* @implements #415
|
|
9
|
+
* @source @src/artifacts/types.ts
|
|
10
|
+
* @tests @test/unit/artifacts/index-builder.test.ts
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { createHash } from 'crypto';
|
|
15
|
+
import { load as loadYaml } from 'js-yaml';
|
|
16
|
+
import { DEFAULT_INDEX_EXTENSIONS, INDEX_EXTRACTOR_VERSION, INDEX_VERSION, INDEX_DIR, OPERATIONAL_DISCOVERY_TYPES, PHASE_DIRECTORIES, GRAPH_CONFIGS, TEMPLATE_INDEX_EXTENSIONS, resolveGraphScanDir, loadUserGraphConfigs, loadGlobalGraphConfigs, } from './types.js';
|
|
17
|
+
import { parseCitationSidecar, citationResultToEdges, buildRefToPathMap } from './citation-parser.js';
|
|
18
|
+
import { writeIndexFile, resolveIndexDir, loadGraphIndexFile } from './index-reader.js';
|
|
19
|
+
import { loadManifest, writeManifest, statMatches, makeEntry } from './checksum-manifest.js';
|
|
20
|
+
import { workspaceLinkedFiles } from '../smiths/context-pipeline/workspace-context.js';
|
|
21
|
+
import { normalizeOperationalState } from './operational-state.js';
|
|
22
|
+
import { DEFAULT_PROJECT_AIWG_DIR, resolveProjectAiwgDir, } from '../config/project-artifacts.js';
|
|
23
|
+
function pathContains(parent, child) {
|
|
24
|
+
const relative = path.relative(parent, child);
|
|
25
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
26
|
+
}
|
|
27
|
+
function toPosixPath(value) {
|
|
28
|
+
return value.split(path.sep).join('/');
|
|
29
|
+
}
|
|
30
|
+
function indexPathFor(cwd, fullPath, graph) {
|
|
31
|
+
if (!graph || graph === 'project') {
|
|
32
|
+
const artifactRoot = resolveProjectAiwgDir(cwd);
|
|
33
|
+
if (pathContains(artifactRoot, fullPath)) {
|
|
34
|
+
const relative = toPosixPath(path.relative(artifactRoot, fullPath));
|
|
35
|
+
return relative ? `${DEFAULT_PROJECT_AIWG_DIR}/${relative}` : DEFAULT_PROJECT_AIWG_DIR;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const rel = path.relative(cwd, fullPath);
|
|
39
|
+
if (!rel.startsWith('..') && !path.isAbsolute(rel))
|
|
40
|
+
return toPosixPath(rel);
|
|
41
|
+
return fullPath;
|
|
42
|
+
}
|
|
43
|
+
function absoluteEntryPath(cwd, entryPath, graph) {
|
|
44
|
+
if ((!graph || graph === 'project') && entryPath.startsWith(`${DEFAULT_PROJECT_AIWG_DIR}/`)) {
|
|
45
|
+
return path.join(resolveProjectAiwgDir(cwd), entryPath.slice(DEFAULT_PROJECT_AIWG_DIR.length + 1));
|
|
46
|
+
}
|
|
47
|
+
return path.isAbsolute(entryPath) ? entryPath : path.join(cwd, entryPath);
|
|
48
|
+
}
|
|
49
|
+
function loadIndexFromDir(indexDir, filename) {
|
|
50
|
+
const filePath = path.join(indexDir, filename);
|
|
51
|
+
if (!fs.existsSync(filePath))
|
|
52
|
+
return null;
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parse YAML frontmatter from markdown content
|
|
62
|
+
*/
|
|
63
|
+
export function parseFrontmatter(content) {
|
|
64
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
65
|
+
if (!match)
|
|
66
|
+
return { data: {}, body: content };
|
|
67
|
+
try {
|
|
68
|
+
const data = (loadYaml(match[1]) ?? {});
|
|
69
|
+
return { data, body: match[2] };
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return { data: {}, body: content };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Extract @-mention references from content
|
|
77
|
+
*/
|
|
78
|
+
export function extractMentions(content) {
|
|
79
|
+
const mentions = new Set();
|
|
80
|
+
// Match @path/to/file.ext, @.aiwg/path, and framework references rooted at
|
|
81
|
+
// @$AIWG_ROOT/. The latter must normalize to repo-relative paths so framework
|
|
82
|
+
// skill references resolve to their rule files in the graph index.
|
|
83
|
+
const pattern = /@(?:\$AIWG_ROOT\/)?(\.?aiwg\/[\w./-]+|[a-zA-Z][\w./-]+\.\w+)/g;
|
|
84
|
+
let match;
|
|
85
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
86
|
+
mentions.add(match[1]);
|
|
87
|
+
}
|
|
88
|
+
return Array.from(mentions);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Extract title from content (first # heading or frontmatter title)
|
|
92
|
+
*/
|
|
93
|
+
function extractTitle(data, body) {
|
|
94
|
+
if (typeof data.title === 'string')
|
|
95
|
+
return data.title;
|
|
96
|
+
const headingMatch = body.match(/^#\s+(.+)$/m);
|
|
97
|
+
return headingMatch ? headingMatch[1].trim() : 'Untitled';
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Extract canonical short name (#1233) used for exact-name search match.
|
|
101
|
+
*
|
|
102
|
+
* Priority:
|
|
103
|
+
* 1. frontmatter `name:` field if present
|
|
104
|
+
* 2. parent directory basename for skills/agents/commands/rules layouts
|
|
105
|
+
* (skills conventionally live in `<...>/skills/<name>/SKILL.md`)
|
|
106
|
+
* 3. filename without extension as final fallback
|
|
107
|
+
*
|
|
108
|
+
* The result is the literal slug a user would type to find the artifact —
|
|
109
|
+
* `aiwg-doctor`, `intake-wizard`, `flow-deploy-to-production` — preserving
|
|
110
|
+
* hyphens so the scorer can match queries that contain them.
|
|
111
|
+
*/
|
|
112
|
+
function extractCanonicalName(data, relativePath) {
|
|
113
|
+
if (typeof data.name === 'string' && data.name.trim().length > 0) {
|
|
114
|
+
return data.name.trim();
|
|
115
|
+
}
|
|
116
|
+
const filename = path.basename(relativePath);
|
|
117
|
+
// For slug layouts: <type>s/<name>/<TYPE>.md
|
|
118
|
+
const isCanonicalLayout = /^(SKILL|AGENT|COMMAND|RULE|BEHAVIOR)\.md$/i.test(filename);
|
|
119
|
+
if (isCanonicalLayout) {
|
|
120
|
+
return path.basename(path.dirname(relativePath));
|
|
121
|
+
}
|
|
122
|
+
return path.basename(filename, path.extname(filename));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Extract summary from content (first 500 chars of description or body)
|
|
126
|
+
*/
|
|
127
|
+
function extractSummary(data, body) {
|
|
128
|
+
if (typeof data.description === 'string')
|
|
129
|
+
return data.description.slice(0, 500);
|
|
130
|
+
// Skip headings, get first paragraph
|
|
131
|
+
const lines = body.split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
132
|
+
return lines.slice(0, 5).join(' ').slice(0, 500).trim();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Determine SDLC phase from file path
|
|
136
|
+
*/
|
|
137
|
+
function inferPhase(filePath) {
|
|
138
|
+
for (const [phase, dir] of Object.entries(PHASE_DIRECTORIES)) {
|
|
139
|
+
if (filePath.startsWith(dir))
|
|
140
|
+
return phase;
|
|
141
|
+
}
|
|
142
|
+
return 'other';
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Determine artifact type from frontmatter or filename.
|
|
146
|
+
*
|
|
147
|
+
* Path-based detection for AIWG artifact kinds (#1214) takes precedence
|
|
148
|
+
* over the legacy basename heuristics so `agentic/code/.../skills/foo/SKILL.md`
|
|
149
|
+
* always lands as `type: 'skill'` regardless of frontmatter.
|
|
150
|
+
*/
|
|
151
|
+
function inferType(data, filePath) {
|
|
152
|
+
if (typeof data.type === 'string')
|
|
153
|
+
return data.type;
|
|
154
|
+
// Normalize separators so matchers are cross-platform.
|
|
155
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
156
|
+
const basename = path.basename(filePath, path.extname(filePath)).toLowerCase();
|
|
157
|
+
// Nearest-type-dir-ancestor classification (#1221 audit).
|
|
158
|
+
//
|
|
159
|
+
// The AIWG corpus uses many different nesting depths for artifacts:
|
|
160
|
+
// frameworks/<f>/skills/<slug>/SKILL.md
|
|
161
|
+
// frameworks/<f>/extensions/<sub>/skills/<slug>/SKILL.md
|
|
162
|
+
// frameworks/<f>/templates/hermes/skills/<slug>/SKILL.md
|
|
163
|
+
// frameworks/<f>/elaboration/agents/<n>.md (research-complete)
|
|
164
|
+
// addons/<a>/agents/<n>.md
|
|
165
|
+
// addons/<a>/prompts/agents/<n>.md (aiwg-utils)
|
|
166
|
+
// addons/<a>/skills/skills/SKILL.md (aiwg-utils self-skill)
|
|
167
|
+
// addons/<a>/templates/<n>.md
|
|
168
|
+
// addons/<a>/behaviors/<n>.md
|
|
169
|
+
// addons/<a>/hooks/<n>.md
|
|
170
|
+
// extensions/<e>/skills/<n>.md (flat — no slug dir)
|
|
171
|
+
// extensions/<e>/rules/<n>.md
|
|
172
|
+
// agentic/code/behaviors/<sub>/... (top-level behaviors)
|
|
173
|
+
//
|
|
174
|
+
// Walk the path segments from the file upward; the first segment matching
|
|
175
|
+
// a known artifact-type directory determines the kind. This handles every
|
|
176
|
+
// nested layout uniformly.
|
|
177
|
+
const segments = normalized.split('/');
|
|
178
|
+
const skipBasenames = new Set(['readme', 'rules-index', 'index']);
|
|
179
|
+
const isMarkdown = /\.md$/i.test(filePath);
|
|
180
|
+
const isTemplateAsset = TEMPLATE_INDEX_EXTENSIONS.some(ext => normalized.endsWith(ext));
|
|
181
|
+
if (!skipBasenames.has(basename) && (isMarkdown || isTemplateAsset)) {
|
|
182
|
+
// Look at directory segments only (exclude the file itself).
|
|
183
|
+
for (let i = segments.length - 2; i >= 0; i--) {
|
|
184
|
+
const seg = segments[i];
|
|
185
|
+
switch (seg) {
|
|
186
|
+
case 'skills': {
|
|
187
|
+
// Skills come in two layouts:
|
|
188
|
+
// slug-style: skills/<slug>/SKILL.md → file is SKILL.md
|
|
189
|
+
// flat-style: skills/<name>.md → file is directly under skills/
|
|
190
|
+
//
|
|
191
|
+
// Do not classify nested reference/support markdown under
|
|
192
|
+
// skills/<slug>/... as standalone skills; those files should remain
|
|
193
|
+
// ordinary artifacts so discovery does not advertise them as
|
|
194
|
+
// invokable capabilities.
|
|
195
|
+
const afterSkills = segments.length - i - 1;
|
|
196
|
+
const isSlugSkill = basename === 'skill' && afterSkills === 2;
|
|
197
|
+
const isFlatSkill = afterSkills === 1;
|
|
198
|
+
if (isSlugSkill || isFlatSkill)
|
|
199
|
+
return 'skill';
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case 'agents':
|
|
203
|
+
if (isMarkdown)
|
|
204
|
+
return 'agent';
|
|
205
|
+
break;
|
|
206
|
+
case 'commands':
|
|
207
|
+
if (isMarkdown)
|
|
208
|
+
return 'command';
|
|
209
|
+
break;
|
|
210
|
+
case 'rules':
|
|
211
|
+
// Rules/RULES-INDEX.md is a curated index file, not a rule.
|
|
212
|
+
if (basename === 'rules-index' || basename === 'index')
|
|
213
|
+
break;
|
|
214
|
+
if (isMarkdown)
|
|
215
|
+
return 'rule';
|
|
216
|
+
break;
|
|
217
|
+
case 'templates':
|
|
218
|
+
return 'template';
|
|
219
|
+
case 'behaviors':
|
|
220
|
+
if (isMarkdown)
|
|
221
|
+
return 'behavior';
|
|
222
|
+
break;
|
|
223
|
+
case 'hooks':
|
|
224
|
+
if (isMarkdown)
|
|
225
|
+
return 'hook';
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Legacy SDLC artifact heuristics (existing behavior preserved).
|
|
231
|
+
if (basename.startsWith('uc-') || basename.includes('use-case'))
|
|
232
|
+
return 'use-case';
|
|
233
|
+
if (basename.startsWith('adr-') || basename.includes('adr'))
|
|
234
|
+
return 'adr';
|
|
235
|
+
if (basename.startsWith('tp-') || basename.includes('test-plan'))
|
|
236
|
+
return 'test-plan';
|
|
237
|
+
if (basename.startsWith('tc-') || basename.includes('test-case'))
|
|
238
|
+
return 'test-case';
|
|
239
|
+
if (basename.startsWith('tm-') || basename.includes('threat'))
|
|
240
|
+
return 'threat-model';
|
|
241
|
+
if (basename.startsWith('nfr-') || basename.includes('nfr'))
|
|
242
|
+
return 'nfr';
|
|
243
|
+
if (basename.includes('sad') || basename.includes('architecture'))
|
|
244
|
+
return 'architecture';
|
|
245
|
+
if (basename.includes('risk'))
|
|
246
|
+
return 'risk';
|
|
247
|
+
if (basename.includes('deploy'))
|
|
248
|
+
return 'deployment';
|
|
249
|
+
return 'document';
|
|
250
|
+
}
|
|
251
|
+
const RUNBOOK_ACTION_HEADING = /\b(procedure|steps?|recovery steps?|installation|setup|start|stop|containment|remediation|response actions?|execution)\b/i;
|
|
252
|
+
const RUNBOOK_CONTROL_HEADING = /\b(prerequisites?|verification|validation|rollback|health check|troubleshooting|monitoring|escalation|post-recovery|evidence|postmortem|diagnosis)\b/i;
|
|
253
|
+
function markdownSections(body) {
|
|
254
|
+
const matches = [...body.matchAll(/^#{2,4}\s+(.+)$/gm)];
|
|
255
|
+
return matches.map((match, index) => ({
|
|
256
|
+
heading: match[1].replace(/\s+#+\s*$/, '').trim(),
|
|
257
|
+
body: body.slice((match.index ?? 0) + match[0].length, matches[index + 1]?.index ?? body.length).trim(),
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
function compactSectionText(text, maxLength = 240) {
|
|
261
|
+
const blocks = text
|
|
262
|
+
.replace(/```[\s\S]*?```/g, ' ')
|
|
263
|
+
.split(/\n\s*\n/)
|
|
264
|
+
.map(block => block
|
|
265
|
+
.replace(/^\s*[-*+]\s+/gm, '')
|
|
266
|
+
.replace(/^\s*\|.*\|\s*$/gm, '')
|
|
267
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
268
|
+
.replace(/\s+/g, ' ')
|
|
269
|
+
.trim())
|
|
270
|
+
.filter(Boolean);
|
|
271
|
+
const first = blocks.find(block => !block.startsWith('#'));
|
|
272
|
+
return first ? first.slice(0, maxLength) : undefined;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Detect procedural Markdown runbooks without promoting every runbook-shaped
|
|
276
|
+
* template. Explicit `type: runbook` is authoritative; otherwise a runbook
|
|
277
|
+
* marker must be accompanied by both an action section and a control/validation
|
|
278
|
+
* section. The extracted terms favor the operational sections people search
|
|
279
|
+
* for (procedure, verification, rollback, diagnosis, remediation, and so on).
|
|
280
|
+
*/
|
|
281
|
+
export function parseRunbookDoc(data, body, filePath) {
|
|
282
|
+
if (!/\.md$/i.test(filePath))
|
|
283
|
+
return null;
|
|
284
|
+
const title = extractTitle(data, body);
|
|
285
|
+
const explicit = typeof data.type === 'string' && data.type.toLowerCase() === 'runbook';
|
|
286
|
+
const hasMarker = /\brun[- ]?book\b/i.test(`${filePath} ${title}`);
|
|
287
|
+
const sections = markdownSections(body);
|
|
288
|
+
const headings = sections.map(section => section.heading);
|
|
289
|
+
const hasAction = headings.some(heading => RUNBOOK_ACTION_HEADING.test(heading));
|
|
290
|
+
const hasControl = headings.some(heading => RUNBOOK_CONTROL_HEADING.test(heading));
|
|
291
|
+
if (!explicit && !(hasMarker && hasAction && hasControl))
|
|
292
|
+
return null;
|
|
293
|
+
const preferredCapability = sections.find(section => /\b(purpose|overview|objective|intent|scope)\b/i.test(section.heading));
|
|
294
|
+
const lifecycleHeadings = sections
|
|
295
|
+
.map(section => section.heading)
|
|
296
|
+
.filter(heading => RUNBOOK_ACTION_HEADING.test(heading) || RUNBOOK_CONTROL_HEADING.test(heading));
|
|
297
|
+
const lifecycleCapability = lifecycleHeadings.length > 0
|
|
298
|
+
? `${title}: ${lifecycleHeadings.slice(0, 8).join('; ')}`.slice(0, 240)
|
|
299
|
+
: undefined;
|
|
300
|
+
const capability = (typeof data.description === 'string' ? data.description.trim().slice(0, 240) : undefined)
|
|
301
|
+
?? (preferredCapability ? compactSectionText(preferredCapability.body) : undefined)
|
|
302
|
+
?? lifecycleCapability
|
|
303
|
+
?? extractCapability(data, body);
|
|
304
|
+
const terms = new Set(['runbook']);
|
|
305
|
+
const relevantHeading = new RegExp(`${RUNBOOK_ACTION_HEADING.source}|${RUNBOOK_CONTROL_HEADING.source}|\\b(purpose|overview|objective|intent|scope|incident classification|communication|monitoring|symptoms?)\\b`, 'i');
|
|
306
|
+
for (const section of sections) {
|
|
307
|
+
if (!relevantHeading.test(section.heading))
|
|
308
|
+
continue;
|
|
309
|
+
terms.add(section.heading);
|
|
310
|
+
const compact = compactSectionText(section.body, 320);
|
|
311
|
+
if (compact)
|
|
312
|
+
terms.add(compact);
|
|
313
|
+
if (terms.size >= 40)
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
kind: 'Runbook',
|
|
318
|
+
capability,
|
|
319
|
+
searchTerms: [...terms],
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Extract trigger phrases from a SKILL.md / agent body.
|
|
324
|
+
*
|
|
325
|
+
* Skills declare alternate activation phrases under a `## Triggers`
|
|
326
|
+
* heading; the body typically lists them as bullet points. This
|
|
327
|
+
* function pulls each bullet's leading phrase (the part before any
|
|
328
|
+
* `→` arrow or em-dash explanation), lowercased and trimmed.
|
|
329
|
+
*
|
|
330
|
+
* Returns an empty array when no `## Triggers` section is found —
|
|
331
|
+
* non-skill artifacts get `triggers: undefined` after this is wired.
|
|
332
|
+
*
|
|
333
|
+
* @implements #1214
|
|
334
|
+
*/
|
|
335
|
+
export function extractTriggers(body, frontmatter) {
|
|
336
|
+
const phrases = [];
|
|
337
|
+
const declaredTriggers = Array.isArray(frontmatter?.triggers)
|
|
338
|
+
? frontmatter.triggers
|
|
339
|
+
: [];
|
|
340
|
+
for (const trigger of declaredTriggers) {
|
|
341
|
+
if (typeof trigger !== 'string')
|
|
342
|
+
continue;
|
|
343
|
+
const phrase = trigger.trim().toLowerCase();
|
|
344
|
+
if (phrase.length === 0)
|
|
345
|
+
continue;
|
|
346
|
+
if (phrase.length > 200)
|
|
347
|
+
continue;
|
|
348
|
+
phrases.push(phrase);
|
|
349
|
+
}
|
|
350
|
+
// Find a triggers heading (case-insensitive). Accepted variants:
|
|
351
|
+
// ## Triggers
|
|
352
|
+
// ## Natural Language Triggers (used by orchestration skills)
|
|
353
|
+
// ## Activation Phrases (alternate vocabulary)
|
|
354
|
+
// ## When to invoke (intent-style)
|
|
355
|
+
// Capture the section content until the next `## ` heading or EOF.
|
|
356
|
+
// Note: avoid the multi-line `m` flag with `$` — `$` would match
|
|
357
|
+
// every line terminator and stop capture at the first blank line.
|
|
358
|
+
const sectionMatch = body.match(/(?:^|\n)##\s+(?:(?:Natural\s+Language\s+)?Triggers|Activation\s+Phrases|When\s+to\s+invoke)\b[^\n]*\n([\s\S]*?)(?=\n##\s|$)/i);
|
|
359
|
+
if (!sectionMatch)
|
|
360
|
+
return [...new Set(phrases)];
|
|
361
|
+
const section = sectionMatch[1];
|
|
362
|
+
for (const rawLine of section.split('\n')) {
|
|
363
|
+
const line = rawLine.trim();
|
|
364
|
+
if (!line.startsWith('-') && !line.startsWith('*') && !line.startsWith('+'))
|
|
365
|
+
continue;
|
|
366
|
+
// Strip the bullet marker, optional surrounding quotes, and split on
|
|
367
|
+
// common explanation separators ("→", " — ", " - " when followed by
|
|
368
|
+
// explanatory text).
|
|
369
|
+
let phrase = line.replace(/^[-*+]\s+/, '').trim();
|
|
370
|
+
// Drop any leading quote characters
|
|
371
|
+
phrase = phrase.replace(/^["“”'`]+/, '').trim();
|
|
372
|
+
// Cut at the first explanation separator
|
|
373
|
+
const sepMatch = phrase.match(/^(.*?)\s*(?:→|—|--|\s-\s)/);
|
|
374
|
+
if (sepMatch)
|
|
375
|
+
phrase = sepMatch[1];
|
|
376
|
+
// Strip trailing quotes / colons
|
|
377
|
+
phrase = phrase.replace(/["“”'`:.]+\s*$/, '').trim();
|
|
378
|
+
if (phrase.length === 0)
|
|
379
|
+
continue;
|
|
380
|
+
if (phrase.length > 200)
|
|
381
|
+
continue; // Reject pathological lines
|
|
382
|
+
phrases.push(phrase.toLowerCase());
|
|
383
|
+
}
|
|
384
|
+
return [...new Set(phrases)];
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Extract a capability summary for a skill/agent/command/rule.
|
|
388
|
+
*
|
|
389
|
+
* Prefers the frontmatter `description` field (used uniformly across
|
|
390
|
+
* AIWG SKILL.md / agent files). Falls back to the first non-heading
|
|
391
|
+
* paragraph of the body. Capped at 240 chars so the index stays
|
|
392
|
+
* token-tight when surfaced via `aiwg index discover`.
|
|
393
|
+
*
|
|
394
|
+
* @implements #1214
|
|
395
|
+
*/
|
|
396
|
+
export function extractCapability(data, body) {
|
|
397
|
+
if (typeof data.description === 'string' && data.description.trim().length > 0) {
|
|
398
|
+
return data.description.trim().slice(0, 240);
|
|
399
|
+
}
|
|
400
|
+
// Fallback: first non-empty paragraph that isn't a heading.
|
|
401
|
+
const stripped = body.replace(/^---\n[\s\S]*?\n---\n/, '');
|
|
402
|
+
for (const block of stripped.split(/\n\s*\n/)) {
|
|
403
|
+
const trimmed = block.trim();
|
|
404
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
405
|
+
continue;
|
|
406
|
+
return trimmed.replace(/\s+/g, ' ').slice(0, 240);
|
|
407
|
+
}
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Extract a SkillScriptSpec from skill frontmatter (#1227).
|
|
412
|
+
*
|
|
413
|
+
* The `script:` block is optional — only skills with a backing executable
|
|
414
|
+
* declare it. Schema:
|
|
415
|
+
*
|
|
416
|
+
* script:
|
|
417
|
+
* entrypoint: scripts/voice_loader.py # required, relative to skill dir
|
|
418
|
+
* runtime: python3 # required (node|python3|bash|...)
|
|
419
|
+
* cwd: project-root # optional, default project-root
|
|
420
|
+
* argsHint: "--voice <name> --input <path>" # optional UX hint
|
|
421
|
+
*
|
|
422
|
+
* Returns undefined when the block is absent or malformed. Malformed
|
|
423
|
+
* blocks are silently dropped — index builder logs a warning so authors
|
|
424
|
+
* see it, but the artifact still indexes as a non-executable skill.
|
|
425
|
+
*/
|
|
426
|
+
export function extractSkillScript(data) {
|
|
427
|
+
const raw = data.script;
|
|
428
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
429
|
+
return undefined;
|
|
430
|
+
const block = raw;
|
|
431
|
+
const entrypoint = typeof block.entrypoint === 'string' ? block.entrypoint.trim() : '';
|
|
432
|
+
const runtime = typeof block.runtime === 'string' ? block.runtime.trim() : '';
|
|
433
|
+
if (!entrypoint || !runtime)
|
|
434
|
+
return undefined;
|
|
435
|
+
const cwdRaw = typeof block.cwd === 'string' ? block.cwd.trim() : 'project-root';
|
|
436
|
+
const cwd = cwdRaw === 'skill-dir' || cwdRaw === 'aiwg-root' ? cwdRaw : 'project-root';
|
|
437
|
+
const argsHint = typeof block.argsHint === 'string' ? block.argsHint.trim() : undefined;
|
|
438
|
+
return {
|
|
439
|
+
entrypoint,
|
|
440
|
+
runtime,
|
|
441
|
+
cwd,
|
|
442
|
+
...(argsHint ? { argsHint } : {}),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Compute truncated SHA-256 checksum (16 hex chars)
|
|
447
|
+
*/
|
|
448
|
+
function computeChecksum(content) {
|
|
449
|
+
return createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Convert Python-style named capture groups (?P<name>...) to JS-style (?<name>...)
|
|
453
|
+
*/
|
|
454
|
+
export function normalizeNamedCaptures(pattern) {
|
|
455
|
+
return pattern.replace(/\(\?P</g, '(?<');
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Build a MetadataEntry from filename regex captures instead of file content.
|
|
459
|
+
* Used when graphConfig.nodeStrategy === 'filename-metadata'.
|
|
460
|
+
*
|
|
461
|
+
* @implements #723
|
|
462
|
+
*/
|
|
463
|
+
export function buildFilenameMetadataEntry(relativePath, fullPath, filenamePattern) {
|
|
464
|
+
const basename = path.basename(fullPath);
|
|
465
|
+
let captures = {};
|
|
466
|
+
if (filenamePattern) {
|
|
467
|
+
const normalizedPattern = normalizeNamedCaptures(filenamePattern);
|
|
468
|
+
const regex = new RegExp(normalizedPattern);
|
|
469
|
+
const match = basename.match(regex);
|
|
470
|
+
if (match?.groups) {
|
|
471
|
+
captures = match.groups;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const ref = captures.ref ? `REF-${captures.ref}` : '';
|
|
475
|
+
const titleParts = [ref, captures.author, captures.year, captures.slug].filter(Boolean);
|
|
476
|
+
const title = titleParts.length > 0 ? titleParts.join(' — ') : basename;
|
|
477
|
+
// Use file stat for timestamps; checksum is based on filename (content not read)
|
|
478
|
+
const stat = fs.statSync(fullPath);
|
|
479
|
+
const checksum = createHash('sha256').update(basename).digest('hex').slice(0, 16);
|
|
480
|
+
return {
|
|
481
|
+
path: relativePath,
|
|
482
|
+
type: captures.ref ? 'paper' : 'document',
|
|
483
|
+
phase: 'other',
|
|
484
|
+
title,
|
|
485
|
+
tags: [],
|
|
486
|
+
created: stat.birthtime.toISOString(),
|
|
487
|
+
updated: stat.mtime.toISOString(),
|
|
488
|
+
checksum,
|
|
489
|
+
summary: '',
|
|
490
|
+
dependencies: [],
|
|
491
|
+
dependents: [],
|
|
492
|
+
// Store captures as a non-standard field for downstream consumers
|
|
493
|
+
...(Object.keys(captures).length > 0 ? { captures } : {}),
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Apply metadata supplements — merge frontmatter fields from sidecar files.
|
|
498
|
+
*
|
|
499
|
+
* @implements #723
|
|
500
|
+
*/
|
|
501
|
+
function applyMetadataSupplements(entries, supplements, cwd) {
|
|
502
|
+
for (const supplement of supplements) {
|
|
503
|
+
const sidecarDir = path.join(cwd, supplement.scanDir);
|
|
504
|
+
if (!fs.existsSync(sidecarDir))
|
|
505
|
+
continue;
|
|
506
|
+
if (!supplement.matchOn || !supplement.nodeKey)
|
|
507
|
+
continue;
|
|
508
|
+
// Parse matchOn: "frontmatter.ref" -> field "ref"
|
|
509
|
+
const matchField = supplement.matchOn.replace(/^frontmatter\./, '');
|
|
510
|
+
// Build a map of sidecar files: matchField value -> frontmatter data
|
|
511
|
+
const sidecarFiles = findArtifactFiles(sidecarDir, ['.md', '.yaml', '.json']);
|
|
512
|
+
const sidecarMap = new Map();
|
|
513
|
+
for (const sidecarPath of sidecarFiles) {
|
|
514
|
+
const content = fs.readFileSync(sidecarPath, 'utf-8');
|
|
515
|
+
const { data } = parseFrontmatter(content);
|
|
516
|
+
const matchValue = data[matchField];
|
|
517
|
+
if (typeof matchValue === 'string') {
|
|
518
|
+
sidecarMap.set(matchValue, data);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// Match entries to sidecars and merge fields
|
|
522
|
+
for (const entry of Object.values(entries)) {
|
|
523
|
+
const entryCaptures = entry.captures;
|
|
524
|
+
if (!entryCaptures)
|
|
525
|
+
continue;
|
|
526
|
+
const nodeValue = entryCaptures[supplement.nodeKey];
|
|
527
|
+
if (!nodeValue)
|
|
528
|
+
continue;
|
|
529
|
+
// Build the full ref ID for matching (e.g., captures.ref="008" -> "REF-008")
|
|
530
|
+
const matchValue = supplement.nodeKey === 'ref' ? `REF-${nodeValue}` : nodeValue;
|
|
531
|
+
const sidecarData = sidecarMap.get(matchValue);
|
|
532
|
+
if (!sidecarData)
|
|
533
|
+
continue;
|
|
534
|
+
for (const field of supplement.mergeFields) {
|
|
535
|
+
const value = sidecarData[field];
|
|
536
|
+
if (value === undefined)
|
|
537
|
+
continue;
|
|
538
|
+
if (field === 'title' && typeof value === 'string') {
|
|
539
|
+
entry.title = value;
|
|
540
|
+
}
|
|
541
|
+
else if (field === 'tags' && Array.isArray(value)) {
|
|
542
|
+
entry.tags = [...new Set([...entry.tags, ...value.map(String)])];
|
|
543
|
+
}
|
|
544
|
+
else if (field === 'authors' && typeof value === 'string') {
|
|
545
|
+
entry.summary = value;
|
|
546
|
+
}
|
|
547
|
+
// Other fields stored via captures — downstream consumers can access them
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Recursively find all indexable files under a directory
|
|
554
|
+
*/
|
|
555
|
+
function findArtifactFiles(dir, extensions = [...DEFAULT_INDEX_EXTENSIONS]) {
|
|
556
|
+
const results = [];
|
|
557
|
+
if (!fs.existsSync(dir))
|
|
558
|
+
return results;
|
|
559
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
560
|
+
for (const entry of entries) {
|
|
561
|
+
const fullPath = path.join(dir, entry.name);
|
|
562
|
+
if (entry.isSymbolicLink() && !fs.existsSync(fullPath)) {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (entry.isDirectory()) {
|
|
566
|
+
// Skip hidden dirs and .index
|
|
567
|
+
if (entry.name.startsWith('.'))
|
|
568
|
+
continue;
|
|
569
|
+
results.push(...findArtifactFiles(fullPath, extensions));
|
|
570
|
+
}
|
|
571
|
+
else if (extensions.some(ext => entry.name.endsWith(ext))) {
|
|
572
|
+
results.push(fullPath);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return results;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Build the artifact index
|
|
579
|
+
*/
|
|
580
|
+
/**
|
|
581
|
+
* Detect an AIWG workflow "Flow" document — the declarative YAML metalanguage
|
|
582
|
+
* (`apiVersion: workflow.aiwg.io/v1`, the legacy `ops.aiwg.io/v1` alias, and
|
|
583
|
+
* domain-extension namespaces like `<domain>.workflow.aiwg.io/v1`). Pure-YAML
|
|
584
|
+
* Flow docs carry no markdown frontmatter, so the standard frontmatter path
|
|
585
|
+
* leaves them unclassified and invisible to discovery. This surfaces them as
|
|
586
|
+
* `type: 'flow'` with discovery metadata drawn from `metadata.name`,
|
|
587
|
+
* `spec.description`, and `metadata.labels` (#1540). Returns null for any file
|
|
588
|
+
* that isn't a recognizable Flow document.
|
|
589
|
+
*/
|
|
590
|
+
export function parseFlowDoc(content, filePath) {
|
|
591
|
+
if (!/\.ya?ml$/i.test(filePath))
|
|
592
|
+
return null;
|
|
593
|
+
let doc;
|
|
594
|
+
try {
|
|
595
|
+
doc = loadYaml(content);
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
if (!doc || typeof doc !== 'object')
|
|
601
|
+
return null;
|
|
602
|
+
const d = doc;
|
|
603
|
+
const api = typeof d.apiVersion === 'string' ? d.apiVersion : '';
|
|
604
|
+
// core (workflow.aiwg.io/v1), the forward Flows alias (flow.aiwg.io/v1,
|
|
605
|
+
// #1536), the legacy alias (ops.aiwg.io/v1), and any domain-extension
|
|
606
|
+
// namespace ending in one of those.
|
|
607
|
+
const isFlow = /(^|\.)(workflow|flow|ops)\.aiwg\.io\/v1$/.test(api);
|
|
608
|
+
if (!isFlow)
|
|
609
|
+
return null;
|
|
610
|
+
const kind = typeof d.kind === 'string' ? d.kind.trim() : '';
|
|
611
|
+
// The namespace alone is insufficient: other domain schemas may end in an
|
|
612
|
+
// `ops.aiwg.io` suffix. Accept only the workflow metalanguage resource
|
|
613
|
+
// families and their Workflow/Flow/Ops aliases.
|
|
614
|
+
const isFlowKind = /^(?:(?:Workflow|Flow|Ops)?(?:Capability|Playbook|Inventory|Target|Gate|Role|Extension|Runbook))$/.test(kind);
|
|
615
|
+
if (!isFlowKind)
|
|
616
|
+
return null;
|
|
617
|
+
const meta = (d.metadata && typeof d.metadata === 'object' ? d.metadata : {});
|
|
618
|
+
const spec = (d.spec && typeof d.spec === 'object' ? d.spec : {});
|
|
619
|
+
const labelMap = meta.labels && typeof meta.labels === 'object'
|
|
620
|
+
? meta.labels
|
|
621
|
+
: {};
|
|
622
|
+
const labels = Object.values(labelMap).map(String);
|
|
623
|
+
const name = typeof meta.name === 'string' ? meta.name : undefined;
|
|
624
|
+
const description = typeof spec.description === 'string' ? spec.description : undefined;
|
|
625
|
+
const searchTerms = new Set([kind, ...Object.keys(labelMap), ...labels]);
|
|
626
|
+
const excludedKeys = new Set(['body', 'content', 'script', 'source', 'template']);
|
|
627
|
+
const collectTerms = (value, key = '', depth = 0) => {
|
|
628
|
+
if (depth > 7 || searchTerms.size >= 80)
|
|
629
|
+
return;
|
|
630
|
+
if (typeof value === 'string') {
|
|
631
|
+
const compact = value.replace(/\s+/g, ' ').trim();
|
|
632
|
+
if (compact && compact.length <= 320)
|
|
633
|
+
searchTerms.add(compact);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (Array.isArray(value)) {
|
|
637
|
+
for (const item of value)
|
|
638
|
+
collectTerms(item, key, depth + 1);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (!value || typeof value !== 'object')
|
|
642
|
+
return;
|
|
643
|
+
for (const [childKey, childValue] of Object.entries(value)) {
|
|
644
|
+
if (excludedKeys.has(childKey.toLowerCase()))
|
|
645
|
+
continue;
|
|
646
|
+
if (/^(id|name|kind|agent|capability|command|action|expect|description|role|inventory|target|group|schema|rollback[_-]?capability)$/i.test(childKey)) {
|
|
647
|
+
searchTerms.add(childKey.replace(/[_-]+/g, ' '));
|
|
648
|
+
}
|
|
649
|
+
collectTerms(childValue, childKey, depth + 1);
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
collectTerms(spec);
|
|
653
|
+
const fallbackTerms = [...searchTerms]
|
|
654
|
+
.filter(term => term !== kind && term !== name)
|
|
655
|
+
.slice(0, 4);
|
|
656
|
+
const capability = description
|
|
657
|
+
?? `${kind.replace(/([a-z])([A-Z])/g, '$1 $2')} ${name ?? ''}${fallbackTerms.length > 0 ? `: ${fallbackTerms.join('; ')}` : ''}`
|
|
658
|
+
.replace(/\s+/g, ' ')
|
|
659
|
+
.trim()
|
|
660
|
+
.slice(0, 240);
|
|
661
|
+
return {
|
|
662
|
+
type: /Runbook$/.test(kind) ? 'runbook' : 'flow',
|
|
663
|
+
kind,
|
|
664
|
+
name,
|
|
665
|
+
description,
|
|
666
|
+
capability,
|
|
667
|
+
tags: labels,
|
|
668
|
+
searchTerms: [...searchTerms],
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
export async function buildIndex(cwd, options = {}) {
|
|
672
|
+
const { force = false, verbose = false, scope, outputDir, graph, explicit = true } = options;
|
|
673
|
+
const startTime = Date.now();
|
|
674
|
+
// Ensure user-defined graphs are loaded
|
|
675
|
+
loadUserGraphConfigs(cwd);
|
|
676
|
+
loadGlobalGraphConfigs();
|
|
677
|
+
// Determine scan directories based on graph type
|
|
678
|
+
const graphConfig = graph ? GRAPH_CONFIGS[graph] : undefined;
|
|
679
|
+
let scanDirs;
|
|
680
|
+
let fileExtensions;
|
|
681
|
+
if (scope) {
|
|
682
|
+
// Explicit scope overrides graph config
|
|
683
|
+
scanDirs = [path.join(cwd, scope)];
|
|
684
|
+
fileExtensions = [...DEFAULT_INDEX_EXTENSIONS];
|
|
685
|
+
}
|
|
686
|
+
else if (graphConfig) {
|
|
687
|
+
scanDirs = graphConfig.scanDirs.map(d => resolveGraphScanDir(cwd, d));
|
|
688
|
+
fileExtensions = graphConfig.extensions;
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
// Default: scan .aiwg/ (backward compatible)
|
|
692
|
+
scanDirs = [resolveProjectAiwgDir(cwd)];
|
|
693
|
+
fileExtensions = [...DEFAULT_INDEX_EXTENSIONS];
|
|
694
|
+
}
|
|
695
|
+
// Verify at least one scan directory exists
|
|
696
|
+
const existingDirs = scanDirs.filter(d => fs.existsSync(d));
|
|
697
|
+
if (existingDirs.length === 0) {
|
|
698
|
+
// If this graph was auto-selected rather than explicitly requested via --graph,
|
|
699
|
+
// skip gracefully — docs/corpus repos should not be forced to have every
|
|
700
|
+
// built-in or user-defined graph root when `--all` asks for the available set.
|
|
701
|
+
if (!explicit) {
|
|
702
|
+
const relDirs = scanDirs.map(d => path.relative(cwd, d)).join(', ');
|
|
703
|
+
console.warn(`Warning: ${graph} graph: scan directories not found (${relDirs}), skipping`);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
console.error(`Error: No scan directories found: ${scanDirs.join(', ')}`);
|
|
707
|
+
console.log('Run this command from a project with the required directories.');
|
|
708
|
+
process.exit(1);
|
|
709
|
+
}
|
|
710
|
+
// Determine output index directory
|
|
711
|
+
let indexOutputDir;
|
|
712
|
+
if (outputDir) {
|
|
713
|
+
// Test/custom override — write to outputDir/.aiwg/.index/ (or graph subdir)
|
|
714
|
+
indexOutputDir = graph
|
|
715
|
+
? path.join(outputDir, '.aiwg', '.index', graph)
|
|
716
|
+
: path.join(outputDir, INDEX_DIR);
|
|
717
|
+
}
|
|
718
|
+
else if (graph) {
|
|
719
|
+
indexOutputDir = resolveIndexDir(cwd, graph);
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
indexOutputDir = resolveIndexDir(cwd);
|
|
723
|
+
}
|
|
724
|
+
fs.mkdirSync(indexOutputDir, { recursive: true });
|
|
725
|
+
// effectiveOutputCwd is used for backward-compat loadMetadataIndex calls
|
|
726
|
+
const effectiveOutputCwd = outputDir ?? cwd;
|
|
727
|
+
if (graph === 'source') {
|
|
728
|
+
const { buildSourceGraphIndex } = await import('./source-graph.js');
|
|
729
|
+
await buildSourceGraphIndex({
|
|
730
|
+
cwd,
|
|
731
|
+
outputDir: indexOutputDir,
|
|
732
|
+
effectiveOutputCwd,
|
|
733
|
+
verbose,
|
|
734
|
+
});
|
|
735
|
+
const buildTimeMs = Date.now() - startTime;
|
|
736
|
+
console.log(`Source graph built in ${buildTimeMs}ms`);
|
|
737
|
+
console.log(` Output: ${INDEX_DIR}/source/`);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
// Load existing index for incremental updates
|
|
741
|
+
const existingIndex = force
|
|
742
|
+
? null
|
|
743
|
+
: outputDir
|
|
744
|
+
? loadIndexFromDir(indexOutputDir, 'metadata.json')
|
|
745
|
+
: loadGraphIndexFile(effectiveOutputCwd, 'metadata.json', graph);
|
|
746
|
+
const canReuseExisting = existingIndex?.version === INDEX_VERSION
|
|
747
|
+
&& existingIndex.extractorVersion === INDEX_EXTRACTOR_VERSION;
|
|
748
|
+
const existingEntries = canReuseExisting ? existingIndex.entries : {};
|
|
749
|
+
// Load checksum manifest for fast stat-based change detection (#794).
|
|
750
|
+
// When --force is set we skip the manifest entirely and rebuild everything.
|
|
751
|
+
const manifest = force || !canReuseExisting
|
|
752
|
+
? { version: 1, generated: '', entries: {} }
|
|
753
|
+
: loadManifest(indexOutputDir);
|
|
754
|
+
const nextManifestEntries = {};
|
|
755
|
+
const manifestStats = {
|
|
756
|
+
checked: 0,
|
|
757
|
+
statSkipped: 0,
|
|
758
|
+
checksumSkipped: 0,
|
|
759
|
+
reindexed: 0,
|
|
760
|
+
pruned: 0,
|
|
761
|
+
};
|
|
762
|
+
// Collect files from all scan directories
|
|
763
|
+
const files = [];
|
|
764
|
+
for (const dir of existingDirs) {
|
|
765
|
+
files.push(...findArtifactFiles(dir, fileExtensions));
|
|
766
|
+
}
|
|
767
|
+
// WORKSPACE.md is the root of the project context graph. Index it and its
|
|
768
|
+
// local Markdown-linked nodes without copying them into provider trees.
|
|
769
|
+
if (!scope && (!graph || graph === 'project')) {
|
|
770
|
+
const workspacePath = path.join(cwd, 'WORKSPACE.md');
|
|
771
|
+
const contextFiles = [
|
|
772
|
+
...(fs.existsSync(workspacePath) ? [workspacePath] : []),
|
|
773
|
+
...await workspaceLinkedFiles(cwd),
|
|
774
|
+
];
|
|
775
|
+
for (const contextFile of contextFiles) {
|
|
776
|
+
if (fileExtensions.some((extension) => contextFile.endsWith(extension)) && !files.includes(contextFile)) {
|
|
777
|
+
files.push(contextFile);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
const entries = {};
|
|
782
|
+
const tagIndex = {};
|
|
783
|
+
const depGraph = {};
|
|
784
|
+
let newCount = 0;
|
|
785
|
+
let updatedCount = 0;
|
|
786
|
+
let unchangedCount = 0;
|
|
787
|
+
const useFilenameMetadata = graphConfig?.nodeStrategy === 'filename-metadata';
|
|
788
|
+
for (const fullPath of files) {
|
|
789
|
+
const relativePath = indexPathFor(cwd, fullPath, graph);
|
|
790
|
+
let entry;
|
|
791
|
+
if (useFilenameMetadata) {
|
|
792
|
+
// Filename-metadata strategy: derive metadata from filename, skip content read.
|
|
793
|
+
// The checksum manifest doesn't help here (no content read to skip) but we still
|
|
794
|
+
// preserve any existing manifest entry so cross-graph builds don't drop it.
|
|
795
|
+
const checksum = createHash('sha256').update(path.basename(fullPath)).digest('hex').slice(0, 16);
|
|
796
|
+
const existingManifestEntry = manifest.entries[relativePath];
|
|
797
|
+
if (existingManifestEntry) {
|
|
798
|
+
nextManifestEntries[relativePath] = existingManifestEntry;
|
|
799
|
+
}
|
|
800
|
+
// Skip unchanged files in incremental mode
|
|
801
|
+
if (!force && existingEntries[relativePath]?.checksum === checksum) {
|
|
802
|
+
entries[relativePath] = existingEntries[relativePath];
|
|
803
|
+
unchangedCount++;
|
|
804
|
+
if (verbose)
|
|
805
|
+
console.log(` unchanged: ${relativePath}`);
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
entry = buildFilenameMetadataEntry(relativePath, fullPath, graphConfig?.filenamePattern);
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
// Default strategy: read content, parse frontmatter
|
|
812
|
+
manifestStats.checked++;
|
|
813
|
+
// Phase 1: stat-based quick filter — skip content read if mtime+size match manifest.
|
|
814
|
+
// This is the fast path: unchanged files don't touch the filesystem beyond fs.statSync.
|
|
815
|
+
let stat;
|
|
816
|
+
try {
|
|
817
|
+
stat = fs.statSync(fullPath);
|
|
818
|
+
}
|
|
819
|
+
catch (err) {
|
|
820
|
+
if (err.code === 'ENOENT') {
|
|
821
|
+
manifestStats.pruned++;
|
|
822
|
+
if (verbose)
|
|
823
|
+
console.log(` skipped missing: ${relativePath}`);
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
throw err;
|
|
827
|
+
}
|
|
828
|
+
const manifestEntry = manifest.entries[relativePath];
|
|
829
|
+
if (!force && statMatches(stat, manifestEntry) && existingEntries[relativePath]?.checksum === manifestEntry?.checksum) {
|
|
830
|
+
// Stat matches manifest AND the stored index entry references the same checksum.
|
|
831
|
+
// Safe to reuse both without reading the file.
|
|
832
|
+
entries[relativePath] = existingEntries[relativePath];
|
|
833
|
+
nextManifestEntries[relativePath] = manifestEntry;
|
|
834
|
+
unchangedCount++;
|
|
835
|
+
manifestStats.statSkipped++;
|
|
836
|
+
if (verbose)
|
|
837
|
+
console.log(` unchanged (stat): ${relativePath}`);
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
// Phase 2: content read + checksum verification for files that looked changed.
|
|
841
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
842
|
+
const checksum = computeChecksum(content);
|
|
843
|
+
// Skip unchanged files in incremental mode (checksum matched despite stat drift)
|
|
844
|
+
if (!force && existingEntries[relativePath]?.checksum === checksum) {
|
|
845
|
+
entries[relativePath] = existingEntries[relativePath];
|
|
846
|
+
// Update manifest with current stat so Phase 1 succeeds next time.
|
|
847
|
+
nextManifestEntries[relativePath] = makeEntry(checksum, stat);
|
|
848
|
+
unchangedCount++;
|
|
849
|
+
manifestStats.checksumSkipped++;
|
|
850
|
+
if (verbose)
|
|
851
|
+
console.log(` unchanged (checksum): ${relativePath}`);
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
manifestStats.reindexed++;
|
|
855
|
+
nextManifestEntries[relativePath] = makeEntry(checksum, stat);
|
|
856
|
+
const { data, body } = parseFrontmatter(content);
|
|
857
|
+
// YAML Flow documents (workflow.aiwg.io/v1) are pure YAML, not
|
|
858
|
+
// markdown+frontmatter — detect them up front so they classify and
|
|
859
|
+
// become discoverable (#1540).
|
|
860
|
+
const flow = parseFlowDoc(content, relativePath);
|
|
861
|
+
const inferredType = inferType(data, relativePath);
|
|
862
|
+
const physicalType = inferType({ ...data, type: undefined }, relativePath);
|
|
863
|
+
const runbook = flow ? null : parseRunbookDoc(data, body, relativePath);
|
|
864
|
+
const title = flow?.name ?? extractTitle(data, body);
|
|
865
|
+
const phase = typeof data.phase === 'string' ? data.phase : inferPhase(relativePath);
|
|
866
|
+
const type = flow?.type ?? (runbook ? 'runbook' : inferredType);
|
|
867
|
+
const tags = flow ? flow.tags : (Array.isArray(data.tags) ? data.tags.map(String) : []);
|
|
868
|
+
const summary = flow?.description ?? runbook?.capability ?? extractSummary(data, body);
|
|
869
|
+
const dependencies = extractMentions(content);
|
|
870
|
+
// Discovery metadata (#1214, #1540, #1792) — meaningful for operational
|
|
871
|
+
// AIWG artifact kinds. Kept undefined on document types so the index file
|
|
872
|
+
// stays small for the common case.
|
|
873
|
+
const isDiscoverable = OPERATIONAL_DISCOVERY_TYPES.includes(type);
|
|
874
|
+
// Declarative processes have no trigger phrases — they rely on their
|
|
875
|
+
// capability and structure-aware search terms.
|
|
876
|
+
const triggers = isDiscoverable && !flow ? extractTriggers(body, data) : undefined;
|
|
877
|
+
const capability = flow?.capability ?? runbook?.capability ?? (isDiscoverable ? extractCapability(data, body) : undefined);
|
|
878
|
+
const kind = flow?.kind ?? runbook?.kind;
|
|
879
|
+
const sourceType = runbook && physicalType !== 'runbook' ? physicalType : undefined;
|
|
880
|
+
const searchTerms = flow?.searchTerms ?? runbook?.searchTerms;
|
|
881
|
+
const kernel = data.kernel === true || data.kernel === 'true' ? true : undefined;
|
|
882
|
+
// Script entrypoint metadata is meaningful for skills only (#1227).
|
|
883
|
+
const script = type === 'skill' ? extractSkillScript(data) : undefined;
|
|
884
|
+
const operationalState = normalizeOperationalState(data.operational_state);
|
|
885
|
+
// Canonical short name (#1233) — used by the scorer to floor exact-name
|
|
886
|
+
// queries to 1.0 so hyphenated kernel-skill names like `aiwg-doctor`
|
|
887
|
+
// remain searchable even when the rendered title strips the hyphen.
|
|
888
|
+
const name = flow ? flow.name : (isDiscoverable ? extractCanonicalName(data, relativePath) : undefined);
|
|
889
|
+
entry = {
|
|
890
|
+
path: relativePath,
|
|
891
|
+
type,
|
|
892
|
+
...(kind ? { kind } : {}),
|
|
893
|
+
...(sourceType ? { sourceType } : {}),
|
|
894
|
+
phase,
|
|
895
|
+
title,
|
|
896
|
+
tags,
|
|
897
|
+
created: typeof data.created === 'string' ? data.created : stat.birthtime.toISOString(),
|
|
898
|
+
updated: stat.mtime.toISOString(),
|
|
899
|
+
checksum,
|
|
900
|
+
summary,
|
|
901
|
+
dependencies,
|
|
902
|
+
dependents: [], // Computed after all entries are processed
|
|
903
|
+
...(name ? { name } : {}),
|
|
904
|
+
...(triggers && triggers.length > 0 ? { triggers } : {}),
|
|
905
|
+
...(capability ? { capability } : {}),
|
|
906
|
+
...(searchTerms && searchTerms.length > 0 ? { searchTerms } : {}),
|
|
907
|
+
...(kernel ? { kernel } : {}),
|
|
908
|
+
...(script ? { script } : {}),
|
|
909
|
+
...(operationalState ? { operationalState } : {}),
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
entries[relativePath] = entry;
|
|
913
|
+
if (existingEntries[relativePath]) {
|
|
914
|
+
updatedCount++;
|
|
915
|
+
if (verbose)
|
|
916
|
+
console.log(` updated: ${relativePath}`);
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
newCount++;
|
|
920
|
+
if (verbose)
|
|
921
|
+
console.log(` new: ${relativePath}`);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
// Apply metadata supplements if configured (enriches filename-metadata nodes from sidecars)
|
|
925
|
+
if (graphConfig?.metadataSupplements?.length) {
|
|
926
|
+
applyMetadataSupplements(entries, graphConfig.metadataSupplements, cwd);
|
|
927
|
+
}
|
|
928
|
+
// Build tag reverse index
|
|
929
|
+
for (const entry of Object.values(entries)) {
|
|
930
|
+
for (const tag of entry.tags) {
|
|
931
|
+
if (!tagIndex[tag])
|
|
932
|
+
tagIndex[tag] = [];
|
|
933
|
+
tagIndex[tag].push(entry.path);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
// Build dependency graph and compute dependents
|
|
937
|
+
for (const entry of Object.values(entries)) {
|
|
938
|
+
if (!depGraph[entry.path]) {
|
|
939
|
+
depGraph[entry.path] = { upstream: [], downstream: [] };
|
|
940
|
+
}
|
|
941
|
+
for (const dep of entry.dependencies) {
|
|
942
|
+
// Normalize: check if referenced path exists in the index
|
|
943
|
+
const normalizedDep = Object.keys(entries).find(p => p === dep || p.endsWith(dep));
|
|
944
|
+
if (normalizedDep && normalizedDep !== entry.path) {
|
|
945
|
+
const upEdge = { path: normalizedDep, type: 'depends-on' };
|
|
946
|
+
depGraph[entry.path].upstream.push(upEdge);
|
|
947
|
+
if (!depGraph[normalizedDep]) {
|
|
948
|
+
depGraph[normalizedDep] = { upstream: [], downstream: [] };
|
|
949
|
+
}
|
|
950
|
+
const downEdge = { path: entry.path, type: 'depends-on' };
|
|
951
|
+
depGraph[normalizedDep].downstream.push(downEdge);
|
|
952
|
+
// Also update the dependents field on the target entry
|
|
953
|
+
if (entries[normalizedDep]) {
|
|
954
|
+
entries[normalizedDep].dependents.push(entry.path);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
// Run citation sidecar edge extraction if configured
|
|
960
|
+
if (graphConfig?.edgeExtraction?.parser === 'citation-sidecar') {
|
|
961
|
+
// Build REF-XXX → path map from all entries with ref frontmatter
|
|
962
|
+
const entryFrontmatter = new Map();
|
|
963
|
+
for (const entryPath of Object.keys(entries)) {
|
|
964
|
+
const fullPath = absoluteEntryPath(cwd, entryPath, graph);
|
|
965
|
+
if (fs.existsSync(fullPath)) {
|
|
966
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
967
|
+
const { data } = parseFrontmatter(content);
|
|
968
|
+
entryFrontmatter.set(entryPath, data);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
const refToPath = buildRefToPathMap(entryFrontmatter);
|
|
972
|
+
// Parse each entry as a citation sidecar and extract edges
|
|
973
|
+
let citationEdgeCount = 0;
|
|
974
|
+
for (const entryPath of Object.keys(entries)) {
|
|
975
|
+
const fullPath = absoluteEntryPath(cwd, entryPath, graph);
|
|
976
|
+
if (!fs.existsSync(fullPath))
|
|
977
|
+
continue;
|
|
978
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
979
|
+
const result = parseCitationSidecar(content);
|
|
980
|
+
if (!result)
|
|
981
|
+
continue;
|
|
982
|
+
const edges = citationResultToEdges(result, refToPath);
|
|
983
|
+
if (!depGraph[entryPath]) {
|
|
984
|
+
depGraph[entryPath] = { upstream: [], downstream: [] };
|
|
985
|
+
}
|
|
986
|
+
// Add upstream "cites" edges
|
|
987
|
+
for (const edge of edges.upstream) {
|
|
988
|
+
depGraph[entryPath].upstream.push(edge);
|
|
989
|
+
// Add reciprocal downstream edge on the target
|
|
990
|
+
if (!depGraph[edge.path]) {
|
|
991
|
+
depGraph[edge.path] = { upstream: [], downstream: [] };
|
|
992
|
+
}
|
|
993
|
+
depGraph[edge.path].downstream.push({ path: entryPath, type: 'cites' });
|
|
994
|
+
citationEdgeCount++;
|
|
995
|
+
}
|
|
996
|
+
// Add downstream "cited-by" edges
|
|
997
|
+
for (const edge of edges.downstream) {
|
|
998
|
+
depGraph[entryPath].downstream.push(edge);
|
|
999
|
+
// Add reciprocal upstream edge on the source
|
|
1000
|
+
if (!depGraph[edge.path]) {
|
|
1001
|
+
depGraph[edge.path] = { upstream: [], downstream: [] };
|
|
1002
|
+
}
|
|
1003
|
+
depGraph[edge.path].upstream.push({ path: entryPath, type: 'cited-by' });
|
|
1004
|
+
citationEdgeCount++;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (verbose && citationEdgeCount > 0) {
|
|
1008
|
+
console.log(` citation edges: ${citationEdgeCount}`);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
// Deduplicate dependents
|
|
1012
|
+
for (const entry of Object.values(entries)) {
|
|
1013
|
+
entry.dependents = [...new Set(entry.dependents)];
|
|
1014
|
+
}
|
|
1015
|
+
const buildTimeMs = Date.now() - startTime;
|
|
1016
|
+
// Write index files
|
|
1017
|
+
const index = {
|
|
1018
|
+
version: INDEX_VERSION,
|
|
1019
|
+
extractorVersion: INDEX_EXTRACTOR_VERSION,
|
|
1020
|
+
builtAt: new Date().toISOString(),
|
|
1021
|
+
buildTimeMs,
|
|
1022
|
+
entries,
|
|
1023
|
+
};
|
|
1024
|
+
writeIndexFile(effectiveOutputCwd, 'metadata.json', index, indexOutputDir);
|
|
1025
|
+
writeIndexFile(effectiveOutputCwd, 'tags.json', tagIndex, indexOutputDir);
|
|
1026
|
+
writeIndexFile(effectiveOutputCwd, 'dependencies.json', depGraph, indexOutputDir);
|
|
1027
|
+
// Update and persist the checksum manifest for faster future builds (#794).
|
|
1028
|
+
// The next manifest contains entries for every file we processed this build.
|
|
1029
|
+
// Files that disappeared from disk are pruned; the resulting manifest is
|
|
1030
|
+
// written atomically.
|
|
1031
|
+
const nextManifest = {
|
|
1032
|
+
version: 1,
|
|
1033
|
+
generated: new Date().toISOString(),
|
|
1034
|
+
entries: nextManifestEntries,
|
|
1035
|
+
};
|
|
1036
|
+
manifestStats.pruned = Object.keys(manifest.entries).length - Object.keys(nextManifestEntries).length;
|
|
1037
|
+
if (manifestStats.pruned < 0)
|
|
1038
|
+
manifestStats.pruned = 0;
|
|
1039
|
+
writeManifest(indexOutputDir, nextManifest);
|
|
1040
|
+
// Write stats
|
|
1041
|
+
const totalEdges = Object.values(depGraph).reduce((sum, node) => sum + node.downstream.length, 0);
|
|
1042
|
+
const orphaned = Object.entries(depGraph).filter(([, node]) => node.upstream.length === 0 && node.downstream.length === 0).length;
|
|
1043
|
+
const mostReferenced = Object.entries(depGraph)
|
|
1044
|
+
.map(([p, node]) => ({ path: p, count: node.downstream.length }))
|
|
1045
|
+
.sort((a, b) => b.count - a.count)[0] ?? null;
|
|
1046
|
+
const byPhase = {};
|
|
1047
|
+
const byType = {};
|
|
1048
|
+
const tagDist = {};
|
|
1049
|
+
for (const entry of Object.values(entries)) {
|
|
1050
|
+
byPhase[entry.phase] = (byPhase[entry.phase] || 0) + 1;
|
|
1051
|
+
byType[entry.type] = (byType[entry.type] || 0) + 1;
|
|
1052
|
+
for (const tag of entry.tags) {
|
|
1053
|
+
tagDist[tag] = (tagDist[tag] || 0) + 1;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
writeIndexFile(effectiveOutputCwd, 'stats.json', {
|
|
1057
|
+
version: INDEX_VERSION,
|
|
1058
|
+
extractorVersion: INDEX_EXTRACTOR_VERSION,
|
|
1059
|
+
builtAt: new Date().toISOString(),
|
|
1060
|
+
buildTimeMs,
|
|
1061
|
+
totalArtifacts: Object.keys(entries).length,
|
|
1062
|
+
byPhase,
|
|
1063
|
+
byType,
|
|
1064
|
+
tagDistribution: tagDist,
|
|
1065
|
+
graphMetrics: {
|
|
1066
|
+
totalEdges,
|
|
1067
|
+
orphanedArtifacts: orphaned,
|
|
1068
|
+
mostReferenced,
|
|
1069
|
+
},
|
|
1070
|
+
}, indexOutputDir);
|
|
1071
|
+
// Report
|
|
1072
|
+
const total = Object.keys(entries).length;
|
|
1073
|
+
console.log(`Artifact index built in ${buildTimeMs}ms`);
|
|
1074
|
+
console.log(` Indexed ${newCount} new, updated ${updatedCount}, unchanged ${unchangedCount}`);
|
|
1075
|
+
console.log(` Total: ${total} artifacts`);
|
|
1076
|
+
// Report manifest-driven change detection stats (only meaningful when !force
|
|
1077
|
+
// and we actually went through the content-read path for at least one file)
|
|
1078
|
+
if (!force && manifestStats.checked > 0) {
|
|
1079
|
+
const fastPath = manifestStats.statSkipped;
|
|
1080
|
+
const slowPath = manifestStats.checked - manifestStats.statSkipped;
|
|
1081
|
+
console.log(` Change detection: ${manifestStats.checked} checked, ${fastPath} skipped via stat, ${slowPath} content-read, ${manifestStats.reindexed} re-indexed`);
|
|
1082
|
+
if (manifestStats.pruned > 0) {
|
|
1083
|
+
console.log(` Pruned ${manifestStats.pruned} stale manifest entries (files no longer on disk)`);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
const displayDir = graph ? indexOutputDir : `${INDEX_DIR}/`;
|
|
1087
|
+
console.log(` Output: ${displayDir}`);
|
|
1088
|
+
}
|
|
1089
|
+
//# sourceMappingURL=index-builder.js.map
|