@aiwg/cli 2026.10.1 → 2026.10.4
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/capability-matrix.yaml +270 -0
- package/agentic/code/providers/model-capabilities.v1.json +3183 -24
- package/bin/aiwg.mjs +135 -47
- package/dist/src/a2a/codecs.js +9 -0
- 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 +57 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +107 -15
- 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 +285 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +65 -0
- package/dist/src/artifacts/index-refresh.js +576 -0
- package/dist/src/artifacts/index-worker.mjs +9 -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/repair.js +9 -4
- 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 +78 -12
- package/dist/src/cli/child-process-result.js +40 -0
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/env.js +38 -16
- package/dist/src/cli/flag-parsing.js +236 -0
- 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 +218 -5
- package/dist/src/cli/handlers/diagnose.js +13 -5
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/feedback.js +14 -5
- 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 +248 -30
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/mc.js +23 -0
- package/dist/src/cli/handlers/models.js +169 -10
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/ralph.js +3 -0
- package/dist/src/cli/handlers/refresh.js +28 -0
- package/dist/src/cli/handlers/regenerate.js +14 -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 +25 -11
- package/dist/src/cli/handlers/script-runner.js +19 -8
- package/dist/src/cli/handlers/session.js +406 -62
- package/dist/src/cli/handlers/sessions.js +243 -76
- package/dist/src/cli/handlers/setup-manifest.js +474 -79
- package/dist/src/cli/handlers/setup.js +180 -39
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +60 -3
- package/dist/src/cli/handlers/team.js +18 -8
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +307 -55
- package/dist/src/cli/handlers/utilities.js +125 -26
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/prompt-utils.js +72 -0
- package/dist/src/cli/provider-resolution.js +11 -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 +349 -35
- package/dist/src/config/cli.js +256 -23
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -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-health.mjs +14 -3
- 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 +38 -11
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/adapters/jev.js +75 -27
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +14 -7
- 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 +25 -6
- 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 +15 -14
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/index.js +1 -0
- package/dist/src/decision/interop.js +60 -8
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +942 -0
- 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/decision/validate.js +6 -3
- package/dist/src/effects/dispatch-verifier.js +252 -0
- package/dist/src/effects/index.js +1 -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/features/installer.js +2 -2
- package/dist/src/flow/graph-metadata.js +5 -0
- package/dist/src/gates/providers/loader.js +64 -18
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +15 -3
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +95 -5
- 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 +366 -56
- 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 +620 -85
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-file.mjs +279 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/config-layers.mjs +348 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/credentials.mjs +484 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +17 -27
- package/dist/src/mcp/helpers.mjs +109 -4
- 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/omp-config.mjs +50 -29
- package/dist/src/mcp/profiles.js +97 -23
- package/dist/src/mcp/profiles.mjs +99 -27
- package/dist/src/mcp/registry.js +248 -131
- package/dist/src/mcp/registry.mjs +169 -100
- 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 +127 -82
- 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-string.mjs +9 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tool-filters.mjs +357 -0
- package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
- package/dist/src/mcp/tools/decision.mjs +22 -3
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +50 -2
- package/dist/src/mcp/tools/subsystems.mjs +39 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +2856 -0
- package/dist/src/mcp/tools/worker-shell-control.mjs +289 -0
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
- package/dist/src/memory/project-registry.js +2 -2
- package/dist/src/mission-protocol/codecs.js +647 -1
- package/dist/src/mission-protocol/types.js +110 -0
- package/dist/src/mission-protocol/worker-status.js +2 -0
- package/dist/src/models/config-loader.js +76 -14
- package/dist/src/models/grok-build-models.js +93 -41
- package/dist/src/models/model-capabilities.v1.json +3183 -24
- package/dist/src/models/model-discovery.js +931 -67
- package/dist/src/models/provider-policy.js +719 -11
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/qualification-digest.mjs +17 -0
- package/dist/src/models/resolver.js +261 -0
- package/dist/src/models/worker-direct-readiness.js +550 -0
- package/dist/src/models/worker-dispatch-offer.js +169 -0
- package/dist/src/models/worker-health-readiness.js +144 -0
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +986 -0
- package/dist/src/models/wrapper-route.js +18 -0
- package/dist/src/ops/registry.js +9 -14
- package/dist/src/plugins/standalone-packager.js +4 -3
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +430 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/cli-command.mjs +75 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/capability-matrix.js +8 -0
- package/dist/src/providers/capability-matrix.yaml +270 -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 +53 -23
- 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/provider-definitions.js +9 -1
- package/dist/src/providers/provider-definitions.mjs +9 -2
- 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/executor-registry.js +16 -24
- package/dist/src/serve/fleet-mission-conductor.js +9 -0
- package/dist/src/serve/mission-conductor.js +47 -12
- package/dist/src/serve/shared-host-scheduler.js +2 -0
- package/dist/src/serve/worker-invocation.js +558 -0
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/serve/worker-stdin-frame.mjs +107 -0
- package/dist/src/sessions/adapter-registry.js +49 -0
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +82 -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 +43 -9
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
- package/dist/src/sessions/adapters/grokbot.js +434 -24
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +128 -0
- package/dist/src/sessions/adapters/muse.js +200 -104
- 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 +30 -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 +25 -5
- package/dist/src/sessions/batch-import.js +72 -51
- package/dist/src/sessions/candidates.js +47 -11
- package/dist/src/sessions/contracts.js +75 -2
- package/dist/src/sessions/coverage-preflight.js +134 -0
- package/dist/src/sessions/discovery.js +3 -0
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/grok-build-cli.js +67 -5
- 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 +84 -46
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +133 -25
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +106 -0
- package/dist/src/sessions/repository.js +199 -63
- package/dist/src/sessions/workspace-discovery.js +315 -88
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/adapters/clawhub.js +10 -8
- package/dist/src/skills/adapters/openclaw.js +10 -7
- 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 +195 -14
- package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
- package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
- package/dist/src/smiths/mcpsmith/example.js +1 -1
- 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 +85 -31
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/logseq.js +68 -7
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
- package/dist/src/testing/generators/test-code-generator.js +57 -30
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/uhp/client.js +11 -1
- package/dist/src/uhp/mission.js +5 -0
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/checker.mjs +5 -5
- package/dist/src/update/notifier.mjs +54 -2
- 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/dist/src/writing/fidelity.js +18 -1
- package/package.json +32 -2
- package/schemas/mission-v1.schema.json +2429 -0
- package/schemas/models/model-catalog.v1.schema.json +301 -0
- package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
- package/schemas/models/model-policy.v1.schema.json +14 -0
- package/schemas/models/provider-model-capabilities.v1.schema.json +1072 -0
- package/tools/_resolve-impl.mjs +39 -2
- package/tools/agents/deploy-agents.mjs +64 -23
- package/tools/agents/providers/base.mjs +153 -166
- 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/grokbot.mjs +11 -12
- package/tools/agents/providers/hermes.mjs +62 -49
- package/tools/agents/providers/omp.mjs +5 -2
- package/tools/agents/providers/openclaw-translator.mjs +19 -5
- 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 +360 -0
- package/tools/providers/omp-transport.mjs +194 -0
- package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
- package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
- package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
- package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
- package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
- package/tools/ralph-external/lib/pi-adapter.mjs +329 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +748 -0
- 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 +439 -0
- 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 +2916 -0
|
@@ -177,6 +177,7 @@ export function runGrokInspect(envOrOptions = process.env) {
|
|
|
177
177
|
// npm exposes Grok through a .cmd shim on Windows; Node requires a shell
|
|
178
178
|
// for .cmd/.bat launchers. Native .exe installs stay shell-free.
|
|
179
179
|
shell: process.platform === 'win32' && /\.(?:cmd|bat)$/i.test(binary),
|
|
180
|
+
killSignal: 'SIGKILL',
|
|
180
181
|
});
|
|
181
182
|
if (result.error && result.error.code === 'ENOENT') {
|
|
182
183
|
return { status: 'absent', remediation: ABSENT_REMEDIATION };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface OmpAgentOptions {
|
|
2
|
+
diagnostics?: string[];
|
|
3
|
+
quiet?: boolean;
|
|
4
|
+
modelsConfig?: Record<string, any>;
|
|
5
|
+
modelConfig?: Record<string, any>;
|
|
6
|
+
}
|
|
7
|
+
export function transformAgent(src: string, content: string, opts?: OmpAgentOptions): string;
|
|
8
|
+
export function mapModel(model: string | undefined, config?: Record<string, any>): string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function diagnoseOmpRuntime(options?: {cwd?: string; env?: NodeJS.ProcessEnv; binary?: string; runner?: (...args: any[]) => Promise<{stdout: string}>}): Promise<{executable: string; available: boolean; version: string | null; paths: ReturnType<typeof import('./omp-paths.mjs').resolveOmpPaths>; reason: string | null}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface OmpPathOptions {
|
|
2
|
+
env?: NodeJS.ProcessEnv;
|
|
3
|
+
home?: string;
|
|
4
|
+
cwd?: string;
|
|
5
|
+
platform?: string;
|
|
6
|
+
exists?: (path: string) => boolean;
|
|
7
|
+
}
|
|
8
|
+
export function normalizeOmpProfile(value?: string): string | undefined;
|
|
9
|
+
export function resolveOmpPaths(options?: OmpPathOptions): {
|
|
10
|
+
profile?: string; configRoot: string; agentDir: string; projectDir: string;
|
|
11
|
+
resourceDirs: { agents: string; skills: string; commands: string; rules: string; behaviors: string };
|
|
12
|
+
dataDir: string; stateDir: string; cacheDir: string; sessionsDir: string;
|
|
13
|
+
};
|
|
@@ -362,7 +362,15 @@ const BUILT_IN_SEEDS = [
|
|
|
362
362
|
builtIn: true,
|
|
363
363
|
surfaces: { primary: 'claude-code', compatibility: ['claude'], precedence: ['.claude/', 'CLAUDE.md'], related: [] },
|
|
364
364
|
detection: {
|
|
365
|
-
|
|
365
|
+
// CLAUDECODE / CLAUDE_CODE_ENTRYPOINT / CLAUDE_CODE_DESKTOP_APP_VERSION
|
|
366
|
+
// are the markers current Claude Code (CLI and Desktop Code tab)
|
|
367
|
+
// actually sets; confirmed empirically in both a CLI session and a
|
|
368
|
+
// Desktop Code-tab session (docs/planning/claude-desktop-provider/
|
|
369
|
+
// assessment-and-issue-plan.md §2.1). `CLAUDE_CODE_VERSION` is not set
|
|
370
|
+
// by current Claude Code, and `ANTHROPIC_API_KEY` is a false-positive
|
|
371
|
+
// signal — any non-Claude session with that key set would otherwise
|
|
372
|
+
// identify as `claude` (#2627).
|
|
373
|
+
env: ['CLAUDECODE', 'CLAUDE_CODE_ENTRYPOINT', 'CLAUDE_CODE_DESKTOP_APP_VERSION'],
|
|
366
374
|
process: ['claude-code', 'claude'],
|
|
367
375
|
capabilityId: 'claude-code',
|
|
368
376
|
runtimeEnvPriority: 100,
|
|
@@ -26,9 +26,9 @@ const MCP_INJECTION_DEFINITIONS = [
|
|
|
26
26
|
providerId: 'claude-code',
|
|
27
27
|
includeInSupportedProviders: true,
|
|
28
28
|
configFormat: 'json',
|
|
29
|
-
serverConfigFormat: '
|
|
29
|
+
serverConfigFormat: 'claude-code',
|
|
30
30
|
serversKey: 'mcpServers',
|
|
31
|
-
configPath: { scope: 'project', path: '.
|
|
31
|
+
configPath: { scope: 'project', path: '.mcp.json' },
|
|
32
32
|
supportsEphemeral: true,
|
|
33
33
|
},
|
|
34
34
|
},
|
|
@@ -178,10 +178,17 @@ export function listMcpInjectProviderIds() {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
export function resolveMcpConfigPath(provider, projectDir = '.', options = {}) {
|
|
181
|
+
if (normalizeRuntimeProviderId(provider) === 'codex' && process.env.CODEX_HOME) {
|
|
182
|
+
return resolve(process.env.CODEX_HOME, 'config.toml');
|
|
183
|
+
}
|
|
181
184
|
if (normalizeRuntimeProviderId(provider) === 'antigravity' && options.scope === 'user') {
|
|
182
185
|
const home = process.env.HOME || process.env.USERPROFILE || homedir();
|
|
183
186
|
return resolve(home, '.gemini/config/mcp_config.json');
|
|
184
187
|
}
|
|
188
|
+
if (normalizeRuntimeProviderId(provider) === 'claude-code' && options.scope === 'user') {
|
|
189
|
+
const home = process.env.HOME || process.env.USERPROFILE || homedir();
|
|
190
|
+
return resolve(home, '.claude.json');
|
|
191
|
+
}
|
|
185
192
|
if (normalizeRuntimeProviderId(provider) === 'omp' && options.scope === 'user') {
|
|
186
193
|
return resolve(resolveOmpPaths(options).agentDir, 'mcp.json');
|
|
187
194
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface SkillReceipt {
|
|
2
|
+
skillSource?: string;
|
|
3
|
+
managed?: Record<string, { hash: string; frameworkSlug?: string; source?: string; version?: string }>;
|
|
4
|
+
}
|
|
5
|
+
export function extractFrameworkSlug(source: string): string | null;
|
|
6
|
+
export function sameSkillOwner(receipt: SkillReceipt | null, skillSource: string, frameworkSlug?: string | null): boolean;
|
|
7
|
+
export function canDeploySkill(receipt: SkillReceipt | null, skillSource: string, opts?: { force?: boolean }): boolean;
|
|
8
|
+
export function pruneRecordedSkillFiles(skillDir: string, managed: SkillReceipt['managed'], desired: Set<string>, opts?: { dryRun?: boolean }): void;
|
|
9
|
+
export function releaseSkillOwnership(skillDir: string, owner: { frameworkSlug?: string; skillSource?: string; sourceRoot?: string }, opts?: { dryRun?: boolean }): boolean | null;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
/** Identify a bundled owner independently of the installation prefix. */
|
|
6
|
+
export function extractFrameworkSlug(source) {
|
|
7
|
+
if (typeof source !== 'string') return null;
|
|
8
|
+
return source.replace(/\\/g, '/').match(/agentic\/code\/(?:frameworks|addons)\/([^/]+)\//)?.[1] ?? null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function sourceSegment(source) {
|
|
12
|
+
if (!source) return null;
|
|
13
|
+
const normalized = source.replace(/\\/g, '/');
|
|
14
|
+
return normalized.match(/(?:agentic\/code|\.aiwg)\/(?:frameworks|addons|extensions)\/.+$/)?.[0]
|
|
15
|
+
?? normalized.match(/[^/]+\/skills\/[^/]+$/)?.[0] ?? null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Slugs survive install moves; legacy receipts fall back to source identity. */
|
|
19
|
+
export function sameSkillOwner(receipt, skillSource, frameworkSlug = extractFrameworkSlug(`${skillSource}/SKILL.md`)) {
|
|
20
|
+
const existingSlug = receipt?.managed?.['SKILL.md']?.frameworkSlug;
|
|
21
|
+
if (existingSlug && frameworkSlug) return existingSlug === frameworkSlug;
|
|
22
|
+
if (receipt?.skillSource && path.resolve(receipt.skillSource) === path.resolve(skillSource)) return true;
|
|
23
|
+
if (existingSlug || frameworkSlug || !receipt?.skillSource) return false;
|
|
24
|
+
const segment = sourceSegment(skillSource);
|
|
25
|
+
return Boolean(segment && path.basename(receipt.skillSource) === path.basename(skillSource)
|
|
26
|
+
&& sourceSegment(receipt.skillSource) === segment);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Frameworks outrank addons; peers retain ownership unless explicitly forced. */
|
|
30
|
+
export function canDeploySkill(receipt, skillSource, { force = false } = {}) {
|
|
31
|
+
const owner = receipt?.managed?.['SKILL.md']?.frameworkSlug || receipt?.skillSource;
|
|
32
|
+
if (!owner || sameSkillOwner(receipt, skillSource)) return true;
|
|
33
|
+
const incoming = extractFrameworkSlug(`${skillSource}/SKILL.md`) || skillSource;
|
|
34
|
+
const isFramework = /(?:^|\/)agentic\/code\/frameworks\//.test(skillSource.replace(/\\/g, '/'));
|
|
35
|
+
const wasAddon = /(?:^|\/)agentic\/code\/addons\//.test((receipt.skillSource || '').replace(/\\/g, '/'));
|
|
36
|
+
const replace = force || (isFramework && wasAddon);
|
|
37
|
+
console.warn(`Skill deploy collision: '${path.basename(skillSource)}' is owned by ${owner}; ${incoming} ${replace ? 'taking ownership' : 'skipped'}`);
|
|
38
|
+
return replace;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Remove unchanged recorded files, allowing symlinks above (but never inside) the skill root. */
|
|
42
|
+
export function pruneRecordedSkillFiles(skillDir, managed, desired, opts = {}) {
|
|
43
|
+
if (opts.dryRun) return;
|
|
44
|
+
for (const [relative, record] of Object.entries(managed || {})) {
|
|
45
|
+
if (desired.has(relative)) continue;
|
|
46
|
+
const target = path.resolve(skillDir, relative);
|
|
47
|
+
if (!target.startsWith(`${path.resolve(skillDir)}${path.sep}`)) continue;
|
|
48
|
+
try {
|
|
49
|
+
const expected = path.resolve(fs.realpathSync(skillDir), relative);
|
|
50
|
+
let current = target;
|
|
51
|
+
let symlink = false;
|
|
52
|
+
while (current !== path.resolve(skillDir)) {
|
|
53
|
+
if (fs.lstatSync(current).isSymbolicLink()) { symlink = true; break; }
|
|
54
|
+
current = path.dirname(current);
|
|
55
|
+
}
|
|
56
|
+
if (symlink || fs.realpathSync(target) !== expected || !fs.lstatSync(target).isFile()) continue;
|
|
57
|
+
if (`sha256:${createHash('sha256').update(fs.readFileSync(target)).digest('hex')}` !== record.hash) continue;
|
|
58
|
+
fs.unlinkSync(target);
|
|
59
|
+
let parent = path.dirname(target);
|
|
60
|
+
while (parent !== path.resolve(skillDir)) {
|
|
61
|
+
try { fs.rmdirSync(parent); } catch { break; }
|
|
62
|
+
parent = path.dirname(parent);
|
|
63
|
+
}
|
|
64
|
+
} catch { /* missing, modified, or unreadable files are left alone */ }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Release only the receipt's owner. Return null for foreign/unrecorded skills. */
|
|
69
|
+
export function releaseSkillOwnership(skillDir, owner, opts = {}) {
|
|
70
|
+
if (!fs.existsSync(skillDir) || !fs.lstatSync(skillDir).isDirectory()) return null;
|
|
71
|
+
const manifestPath = path.join(skillDir, '.aiwg-manifest.json');
|
|
72
|
+
let receipt;
|
|
73
|
+
try { receipt = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); } catch { return null; }
|
|
74
|
+
if (!receipt.managed?.['SKILL.md']) return null;
|
|
75
|
+
const slug = receipt.managed['SKILL.md'].frameworkSlug;
|
|
76
|
+
// A bundle owns every receipt whose source lies inside its root, wherever
|
|
77
|
+
// its layout (plugin payloads included) puts the skill directory.
|
|
78
|
+
const insideRoot = owner.sourceRoot && receipt.skillSource
|
|
79
|
+
&& !path.relative(path.resolve(owner.sourceRoot), path.resolve(receipt.skillSource)).startsWith('..')
|
|
80
|
+
&& !path.isAbsolute(path.relative(path.resolve(owner.sourceRoot), path.resolve(receipt.skillSource)));
|
|
81
|
+
if (!insideRoot && (slug ? slug !== owner.frameworkSlug
|
|
82
|
+
: !owner.skillSource || !sameSkillOwner(receipt, owner.skillSource, null))) return null;
|
|
83
|
+
if (opts.dryRun) return false;
|
|
84
|
+
pruneRecordedSkillFiles(skillDir, receipt.managed, new Set(), opts);
|
|
85
|
+
fs.rmSync(manifestPath, { force: true });
|
|
86
|
+
fs.rmSync(path.join(skillDir, '.aiwg-managed'), { force: true });
|
|
87
|
+
try { fs.rmdirSync(skillDir); return true; } catch { return false; }
|
|
88
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type RuleActivation = 'kernel' | 'summary' | 'bound' | 'paths' | 'on-demand';
|
|
2
|
+
export type RuleBindingKey = 'skills' | 'agents' | 'flows';
|
|
3
|
+
export interface RuleBindings { skills: string[]; agents: string[]; flows: string[] }
|
|
4
|
+
export interface RuleFrontmatter extends Record<string, unknown> {
|
|
5
|
+
activation: RuleActivation;
|
|
6
|
+
tagged: boolean;
|
|
7
|
+
summary?: string;
|
|
8
|
+
bound_to: RuleBindings;
|
|
9
|
+
paths?: string[];
|
|
10
|
+
enforcedBy?: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface RuleDiagnostic { severity: 'error' | 'warn' | 'info'; code: string; message: string }
|
|
13
|
+
export type RuleValidationResult =
|
|
14
|
+
| { valid: true; diagnostics: RuleDiagnostic[]; rule: RuleFrontmatter }
|
|
15
|
+
| { valid: false; diagnostics: RuleDiagnostic[]; rule?: never };
|
|
16
|
+
export const RULE_ACTIVATIONS: readonly RuleActivation[];
|
|
17
|
+
export const RULE_BINDING_TYPES: Readonly<{ skills: 'skill'; agents: 'agent'; flows: 'flow' }>;
|
|
18
|
+
export const ruleFrontmatterSchema: Record<string, unknown>;
|
|
19
|
+
export function inferRuleActivation(enforcement: unknown): 'kernel' | 'on-demand';
|
|
20
|
+
export function validateRuleFrontmatter(data: unknown): RuleValidationResult;
|
|
21
|
+
export function parseRuleFrontmatter(content: string): RuleValidationResult & { data: unknown; body: string };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Shared source frontmatter contract for lint, deployment, and show/MCP (#2938). */
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import Ajv from 'ajv';
|
|
4
|
+
import { parse } from 'yaml';
|
|
5
|
+
|
|
6
|
+
export const RULE_ACTIVATIONS = Object.freeze(['kernel', 'summary', 'bound', 'paths', 'on-demand']);
|
|
7
|
+
export const RULE_BINDING_TYPES = Object.freeze({ skills: 'skill', agents: 'agent', flows: 'flow' });
|
|
8
|
+
export const ruleFrontmatterSchema = JSON.parse(readFileSync(new URL('./rule-frontmatter.schema.json', import.meta.url), 'utf8'));
|
|
9
|
+
const ajv = new Ajv({ strict: true, allErrors: true });
|
|
10
|
+
const validate = ajv.compile(ruleFrontmatterSchema);
|
|
11
|
+
// Legacy paths/applies_to/scope never become activation inputs during migration.
|
|
12
|
+
const validateLegacy = ajv.compile({ type: 'object', properties: ruleFrontmatterSchema.properties });
|
|
13
|
+
|
|
14
|
+
export function inferRuleActivation(enforcement) {
|
|
15
|
+
return typeof enforcement === 'string' && ['medium', 'low'].includes(enforcement.trim().toLowerCase())
|
|
16
|
+
? 'on-demand' : 'kernel';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Validate a parsed YAML mapping. Invalid explicit activation never falls back. */
|
|
20
|
+
export function validateRuleFrontmatter(data) {
|
|
21
|
+
const diagnostics = [];
|
|
22
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) {
|
|
23
|
+
return { valid: false, diagnostics: [{ severity: 'error', code: 'frontmatter', message: 'Rule frontmatter must be a mapping.' }] };
|
|
24
|
+
}
|
|
25
|
+
const tagged = Object.hasOwn(data, 'activation');
|
|
26
|
+
const checker = tagged ? validate : validateLegacy;
|
|
27
|
+
if (!checker(data)) {
|
|
28
|
+
for (const error of checker.errors ?? []) diagnostics.push({
|
|
29
|
+
severity: 'error', code: 'frontmatter',
|
|
30
|
+
message: `${error.instancePath || '/'} ${error.message}${error.params.missingProperty ? `: ${error.params.missingProperty}` : ''}`,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (!tagged) diagnostics.push({ severity: 'warn', code: 'missing-activation',
|
|
34
|
+
message: `Missing activation; inferred ${inferRuleActivation(data.enforcement)} delivery from legacy enforcement (migration fallback).` });
|
|
35
|
+
if (Object.hasOwn(data, 'enforcedBy')) diagnostics.push({ severity: 'info', code: 'reserved-enforcedBy',
|
|
36
|
+
message: 'enforcedBy is opaque reserved metadata (#2839); it has no effect.' });
|
|
37
|
+
const valid = !diagnostics.some(item => item.severity === 'error');
|
|
38
|
+
return {
|
|
39
|
+
valid, diagnostics,
|
|
40
|
+
...(valid ? { rule: {
|
|
41
|
+
...data,
|
|
42
|
+
activation: tagged ? data.activation : inferRuleActivation(data.enforcement),
|
|
43
|
+
tagged,
|
|
44
|
+
...(typeof data.summary === 'string' ? { summary: data.summary.trim() } : {}),
|
|
45
|
+
bound_to: { skills: [], agents: [], flows: [], ...data.bound_to },
|
|
46
|
+
} } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Parse complete Markdown source, preserving authored body and opaque metadata. */
|
|
51
|
+
export function parseRuleFrontmatter(content) {
|
|
52
|
+
const source = content.replace(/^\uFEFF/, '');
|
|
53
|
+
if (!/^---\r?\n/.test(source)) return { ...validateRuleFrontmatter({}), data: {}, body: content };
|
|
54
|
+
const match = source.match(/^---\r?\n([\s\S]*?)^---[ \t]*\r?$(?:\n)?/m);
|
|
55
|
+
if (!match) return { valid: false, diagnostics: [{ severity: 'error', code: 'frontmatter', message: 'Unterminated rule frontmatter.' }], data: {}, body: content };
|
|
56
|
+
try {
|
|
57
|
+
const data = match[1].trim() ? parse(match[1]) : {};
|
|
58
|
+
return { ...validateRuleFrontmatter(data), data, body: source.slice(match[0].length) };
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return { valid: false, diagnostics: [{ severity: 'error', code: 'frontmatter', message: `Invalid rule YAML: ${error.message}` }], data: {}, body: content };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RuleDiagnostic } from './activation.mjs';
|
|
2
|
+
export interface RulePinSelector { rules?: string[]; enforcement?: Array<'critical' | 'high' | 'medium' | 'low'>; capabilities?: string[]; group?: string; all?: true }
|
|
3
|
+
export interface RulesConfig { inline_budget_tokens?: number; always_load?: RulePinSelector[]; pin_groups?: Record<string, string[]> }
|
|
4
|
+
export interface InlineBudgetConfig { rules?: RulesConfig }
|
|
5
|
+
export interface StartupContributor { path: string; bytes?: number; content?: string }
|
|
6
|
+
export interface InlineBudgetReport {
|
|
7
|
+
provider: string;
|
|
8
|
+
limit: number;
|
|
9
|
+
selected_bytes: number;
|
|
10
|
+
selected_tokens: number;
|
|
11
|
+
estimate: string;
|
|
12
|
+
disabled: boolean;
|
|
13
|
+
exceeded: boolean;
|
|
14
|
+
contributors: Array<{ path: string; bytes: number; estimated_tokens: number }>;
|
|
15
|
+
}
|
|
16
|
+
export interface InlineBudgetOptions {
|
|
17
|
+
provider: string;
|
|
18
|
+
files: readonly StartupContributor[];
|
|
19
|
+
config?: InlineBudgetConfig;
|
|
20
|
+
env?: Record<string, string | undefined>;
|
|
21
|
+
reportOnly?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export const DEFAULT_RULES_INLINE_BUDGET_TOKENS: 64000;
|
|
24
|
+
export function validateRulesConfig(rules: unknown): string[];
|
|
25
|
+
export function resolveRulesInlineBudget(config?: InlineBudgetConfig, env?: Record<string, string | undefined>): number;
|
|
26
|
+
export function guardRulesInlineBudget(options: InlineBudgetOptions): { report: InlineBudgetReport; diagnostics: RuleDiagnostic[]; passed: boolean };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Configurable lint guard only. Call with the complete projected startup set. */
|
|
2
|
+
export const DEFAULT_RULES_INLINE_BUDGET_TOKENS = 64000;
|
|
3
|
+
|
|
4
|
+
export function validateRulesConfig(rules) {
|
|
5
|
+
if (rules === undefined) return [];
|
|
6
|
+
if (rules === null || typeof rules !== 'object' || Array.isArray(rules)) return ['rules: must be an object'];
|
|
7
|
+
const errors = Object.keys(rules).filter(key => !['inline_budget_tokens', 'always_load', 'pin_groups'].includes(key)).map(key => `rules.${key}: unknown field`);
|
|
8
|
+
if (rules.inline_budget_tokens !== undefined && (!Number.isSafeInteger(rules.inline_budget_tokens) || rules.inline_budget_tokens < 0)) {
|
|
9
|
+
errors.push('rules.inline_budget_tokens: must be a non-negative safe integer');
|
|
10
|
+
}
|
|
11
|
+
const strings = value => Array.isArray(value) && value.length > 0 && value.every(id => typeof id === 'string' && id.trim() && !['__proto__', 'prototype', 'constructor'].includes(id));
|
|
12
|
+
if (rules.pin_groups !== undefined) {
|
|
13
|
+
if (!rules.pin_groups || typeof rules.pin_groups !== 'object' || Array.isArray(rules.pin_groups)) errors.push('rules.pin_groups: must be an object');
|
|
14
|
+
else for (const [name, ids] of Object.entries(rules.pin_groups)) {
|
|
15
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name) || ['__proto__', 'prototype', 'constructor'].includes(name) || !strings(ids)) errors.push(`rules.pin_groups.${name}: must contain capability ids`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (rules.always_load !== undefined) {
|
|
19
|
+
if (!Array.isArray(rules.always_load)) errors.push('rules.always_load: must be an array');
|
|
20
|
+
else for (const selector of rules.always_load) {
|
|
21
|
+
if (!selector || typeof selector !== 'object' || Array.isArray(selector) || !Object.keys(selector).length) { errors.push('rules.always_load: each selector must be a non-empty object'); continue; }
|
|
22
|
+
for (const key of Object.keys(selector)) if (!['rules', 'enforcement', 'capabilities', 'group', 'all'].includes(key)) errors.push(`rules.always_load.${key}: unknown selector field`);
|
|
23
|
+
for (const key of ['rules', 'enforcement', 'capabilities']) if (selector[key] !== undefined && !strings(selector[key])) errors.push(`rules.always_load.${key}: must be a non-empty array of ids`);
|
|
24
|
+
if (Array.isArray(selector.enforcement) && selector.enforcement.some(tier => !['critical', 'high', 'medium', 'low'].includes(tier))) errors.push('rules.always_load.enforcement: expected critical, high, medium, low');
|
|
25
|
+
if (selector.group !== undefined && (typeof selector.group !== 'string' || !/^[a-zA-Z0-9_-]+$/.test(selector.group) || ['__proto__', 'prototype', 'constructor'].includes(selector.group))) errors.push('rules.always_load.group: expected a group name');
|
|
26
|
+
if (selector.all !== undefined && (selector.all !== true || Object.keys(selector).length !== 1)) errors.push('rules.always_load.all: true must be the only selector field');
|
|
27
|
+
if (selector.group !== undefined && selector.capabilities !== undefined) errors.push('rules.always_load: group and capabilities are mutually exclusive');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return errors;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function resolveRulesInlineBudget(config = {}, env = process.env) {
|
|
34
|
+
const errors = validateRulesConfig(config.rules);
|
|
35
|
+
if (errors.length) throw new Error(errors.join('\n'));
|
|
36
|
+
const override = env.AIWG_RULES_INLINE_BUDGET_TOKENS;
|
|
37
|
+
if (override !== undefined) {
|
|
38
|
+
if (!/^\d+$/.test(override) || !Number.isSafeInteger(Number(override))) {
|
|
39
|
+
throw new Error('AIWG_RULES_INLINE_BUDGET_TOKENS: must be a non-negative safe integer');
|
|
40
|
+
}
|
|
41
|
+
return Number(override);
|
|
42
|
+
}
|
|
43
|
+
return config.rules?.inline_budget_tokens ?? DEFAULT_RULES_INLINE_BUDGET_TOKENS;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** No filesystem writes or activation/selection changes. Provider hard caps are separate. */
|
|
47
|
+
export function guardRulesInlineBudget({ provider, files, config = {}, env = process.env, reportOnly = false }) {
|
|
48
|
+
if (typeof provider !== 'string' || !provider.trim() || !Array.isArray(files)) {
|
|
49
|
+
throw new Error('Startup budget requires a provider and the complete projected files array.');
|
|
50
|
+
}
|
|
51
|
+
const limit = resolveRulesInlineBudget(config, env);
|
|
52
|
+
const contributors = files.map(file => {
|
|
53
|
+
if (!file || typeof file.path !== 'string' || !file.path.trim()) throw new Error('Startup contributor requires a path');
|
|
54
|
+
const bytes = file.bytes ?? (typeof file.content === 'string' ? Buffer.byteLength(file.content, 'utf8') : undefined);
|
|
55
|
+
if (!Number.isSafeInteger(bytes) || bytes < 0) throw new Error(`Invalid startup byte count for ${file.path}`);
|
|
56
|
+
return { path: file.path, bytes, estimated_tokens: bytes / 4 };
|
|
57
|
+
}).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path));
|
|
58
|
+
const selected_bytes = contributors.reduce((total, file) => total + file.bytes, 0);
|
|
59
|
+
if (!Number.isSafeInteger(selected_bytes)) throw new Error('Startup byte total exceeds safe integer range');
|
|
60
|
+
const selected_tokens = selected_bytes / 4;
|
|
61
|
+
const exceeded = limit > 0 && selected_tokens > limit;
|
|
62
|
+
const report = { provider, limit, selected_bytes, selected_tokens, estimate: 'UTF-8 bytes / 4', disabled: limit === 0, exceeded, contributors };
|
|
63
|
+
const diagnostics = exceeded ? [{ severity: reportOnly ? 'warn' : 'error', code: 'inline-budget',
|
|
64
|
+
message: `Rules startup budget exceeded for ${provider}: ${selected_tokens} estimated tokens (${selected_bytes} bytes) selected; limit ${limit}. Contributors: ${contributors.map(file => `${file.path}: ${file.bytes} bytes (${file.estimated_tokens} tokens)`).join(', ')}. Activation-selected content is preserved.` }] : [];
|
|
65
|
+
return { report, diagnostics, passed: !exceeded || reportOnly };
|
|
66
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { loadRelations } from '../artifacts/relations.js';
|
|
4
|
+
import { parseRuleFrontmatter, inferRuleActivation } from './activation.mjs';
|
|
5
|
+
import { capabilityId, loadRulePinConfigs, resolveRulePins } from './pins.mjs';
|
|
6
|
+
import { ruleFiles, rulePinState } from './pin-state.js';
|
|
7
|
+
import { readUserRegistry } from '../config/user-registry.js';
|
|
8
|
+
function includedByAll(root, cap) {
|
|
9
|
+
for (const kind of ['frameworks', 'addons', 'extensions']) {
|
|
10
|
+
const dir = path.join(root, 'agentic/code', kind, cap);
|
|
11
|
+
if (!fs.existsSync(dir) || cap === 'aiwg-dev')
|
|
12
|
+
continue;
|
|
13
|
+
const file = path.join(dir, 'manifest.json');
|
|
14
|
+
const manifest = fs.existsSync(file) ? JSON.parse(fs.readFileSync(file, 'utf8')) : {};
|
|
15
|
+
return !manifest.devOnly && !manifest.explicitInstall;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
/** Read source metadata only. Catalog membership is availability, not applicability. */
|
|
20
|
+
export async function ruleInventory(cwd, root, options) {
|
|
21
|
+
const configs = loadRulePinConfigs(cwd);
|
|
22
|
+
const selected = options.scope === 'user' ? { project: {}, user: configs.user } : configs;
|
|
23
|
+
const state = rulePinState(cwd, root, selected, true, false);
|
|
24
|
+
const relations = loadRelations(cwd, cwd, false) ?? loadRelations(root, cwd, false);
|
|
25
|
+
const userRegistry = await readUserRegistry();
|
|
26
|
+
const inventory = [...state.inventory];
|
|
27
|
+
if (options.scope === 'catalog') {
|
|
28
|
+
const known = new Set(inventory.map(rule => rule.path));
|
|
29
|
+
for (const kind of ['frameworks', 'addons', 'extensions', 'plugins']) {
|
|
30
|
+
const base = path.join(root, 'agentic/code', kind);
|
|
31
|
+
if (!fs.existsSync(base))
|
|
32
|
+
continue;
|
|
33
|
+
for (const entry of fs.readdirSync(base, { withFileTypes: true })) {
|
|
34
|
+
if (!entry.isDirectory())
|
|
35
|
+
continue;
|
|
36
|
+
for (const file of ruleFiles(path.join(base, entry.name, 'rules'))) {
|
|
37
|
+
if (known.has(file))
|
|
38
|
+
continue;
|
|
39
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
40
|
+
const parsed = parseRuleFrontmatter(content);
|
|
41
|
+
if (!parsed.valid)
|
|
42
|
+
throw new Error(`${file}: ${parsed.diagnostics.map(d => d.message).join('; ')}`);
|
|
43
|
+
const data = parsed.data;
|
|
44
|
+
const enforcement = String(data.enforcement ?? content.match(/^\*\*Enforcement Level\*\*:\s*([A-Za-z]+)/m)?.[1] ?? '').toLowerCase() || null;
|
|
45
|
+
inventory.push({ id: file, path: file, name: String(data.name || path.basename(file, '.md')), capability: entry.name,
|
|
46
|
+
activation: parsed.rule.tagged ? parsed.rule.activation : inferRuleActivation(enforcement), enforcement });
|
|
47
|
+
known.add(file);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const warnings = [...state.warnings];
|
|
53
|
+
if (!relations)
|
|
54
|
+
warnings.push('Relationship index unavailable; resolved bindings and indexed missing-source checks are unknown.');
|
|
55
|
+
if (options.provider)
|
|
56
|
+
warnings.push('Provider filter uses recorded capability deployments; missing records do not prove non-delivery.');
|
|
57
|
+
for (const capability of state.installed)
|
|
58
|
+
if (!state.inventory.some(rule => capabilityId(rule.capability) === capability)) {
|
|
59
|
+
warnings.push(`No rule sources found for installed capability '${capability}'; it may have no rules or its source may be unavailable.`);
|
|
60
|
+
}
|
|
61
|
+
if (relations)
|
|
62
|
+
for (const item of Object.values(relations.index.rules)) {
|
|
63
|
+
if (item.type !== 'rule' || !state.installed.includes(capabilityId(item.capability) ?? ''))
|
|
64
|
+
continue;
|
|
65
|
+
const file = path.resolve(root, item.path);
|
|
66
|
+
if (!fs.existsSync(file)) {
|
|
67
|
+
const message = `Installed indexed rule is missing: ${file}`;
|
|
68
|
+
if (options.bodies)
|
|
69
|
+
throw new Error(message);
|
|
70
|
+
warnings.push(message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const source = new Map(inventory.map(rule => {
|
|
74
|
+
const content = fs.readFileSync(rule.path, 'utf8');
|
|
75
|
+
const parsed = parseRuleFrontmatter(content);
|
|
76
|
+
if (!parsed.valid)
|
|
77
|
+
throw new Error(`${rule.path}: ${parsed.diagnostics.map(d => d.message).join('; ')}`);
|
|
78
|
+
const data = parsed.data;
|
|
79
|
+
const enforcement = String(data.enforcement ?? content.match(/^\*\*Enforcement Level\*\*:\s*([A-Za-z]+)/m)?.[1] ?? '').toLowerCase() || null;
|
|
80
|
+
const activation = parsed.rule.tagged ? parsed.rule.activation : inferRuleActivation(enforcement);
|
|
81
|
+
if (enforcement !== rule.enforcement)
|
|
82
|
+
warnings.push(`Stale enforcement metadata for ${rule.path}; canonical source used`);
|
|
83
|
+
if (activation !== rule.activation)
|
|
84
|
+
warnings.push(`Stale activation metadata for ${rule.path}; canonical source used`);
|
|
85
|
+
return [rule.path, { content, parsed, enforcement, activation }];
|
|
86
|
+
}));
|
|
87
|
+
// Resolve pins against source metadata, not potentially stale relation tiers.
|
|
88
|
+
const canonicalRules = inventory.map(rule => ({ ...rule, enforcement: source.get(rule.path).enforcement, activation: source.get(rule.path).activation }));
|
|
89
|
+
const canonicalPins = resolveRulePins({ rules: canonicalRules, configs: selected, installed: state.installed });
|
|
90
|
+
const pins = new Map(canonicalPins.rules.map(rule => [rule.path, rule]));
|
|
91
|
+
const rules = inventory.flatMap(rule => {
|
|
92
|
+
const cap = capabilityId(rule.capability) ?? '';
|
|
93
|
+
if (options.capability && !options.capability.some(value => capabilityId(value) === cap))
|
|
94
|
+
return [];
|
|
95
|
+
const pin = pins.get(rule.path);
|
|
96
|
+
const { content, parsed, enforcement, activation: canonical } = source.get(rule.path);
|
|
97
|
+
if (options.enforcement && !options.enforcement.includes(enforcement ?? ''))
|
|
98
|
+
return [];
|
|
99
|
+
const effective = pin?.activation ?? canonical;
|
|
100
|
+
if (options.activation && !options.activation.includes(effective))
|
|
101
|
+
return [];
|
|
102
|
+
const relation = relations && Object.entries(relations.index.rules).find(([, item]) => path.resolve(root, item.path) === rule.path);
|
|
103
|
+
const stableId = relation?.[0] ?? null;
|
|
104
|
+
const installed = state.installed.includes(cap);
|
|
105
|
+
const layers = [
|
|
106
|
+
{ source: 'user-registry', entries: userRegistry.installed },
|
|
107
|
+
{ source: 'user-config', entries: configs.user.installed ?? {} },
|
|
108
|
+
...(options.scope === 'user' ? [] : [{ source: 'workspace-config', entries: configs.project.installed ?? {} }]),
|
|
109
|
+
];
|
|
110
|
+
const hasBulkMembership = includedByAll(root, cap);
|
|
111
|
+
const configuredBy = layers.filter(layer => layer.entries[cap] || (hasBulkMembership && layer.entries.all)).map(layer => layer.source);
|
|
112
|
+
const entries = layers.flatMap(layer => [layer.entries[cap], ...(hasBulkMembership ? [layer.entries.all] : [])]);
|
|
113
|
+
const configuredProviders = [...new Set(entries.flatMap(entry => Object.keys(entry?.deployedTo ?? {})))].sort();
|
|
114
|
+
if (options.provider && !configuredProviders.includes(options.provider))
|
|
115
|
+
return [];
|
|
116
|
+
return [{ id: stableId, name: rule.name, capability: cap, source: rule.path, summary: parsed.rule.summary ?? null,
|
|
117
|
+
configured: installed, configured_by: configuredBy, applicable: installed ? 'conditional' : 'not-configured',
|
|
118
|
+
canonical_activation: canonical, effective_activation: installed ? effective : null,
|
|
119
|
+
enforcement, pin_reasons: pin?.matches ?? [],
|
|
120
|
+
bindings: relation ? relation[1].targets : null, binding_evidence: relation ? 'relationship-index' : 'unknown', bound_to: parsed.rule.bound_to, paths: parsed.rule.paths ?? [],
|
|
121
|
+
fetch_command: stableId ? `aiwg show rule ${stableId}` : null,
|
|
122
|
+
configured_providers: configuredProviders, delivery_evidence: configuredProviders.length ? 'capability deployment recorded; per-rule delivery unverified' : 'none', delivery: 'unknown', session_loaded: 'unknown',
|
|
123
|
+
...(options.bodies ? { content } : {}) }];
|
|
124
|
+
}).sort((a, b) => a.capability.localeCompare(b.capability) || a.source.localeCompare(b.source));
|
|
125
|
+
return { scope: options.scope, scope_description: options.scope === 'workspace' ? 'current repository/workspace and user configuration' :
|
|
126
|
+
options.scope === 'user' ? 'user configuration and user installation registry' : options.scope === 'installation' ? 'configured installed capabilities' : 'available installation catalog; configured flags are contextual',
|
|
127
|
+
rules, warnings };
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=inventory.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Shared read-only inventory for pin commands, discovery, status, doctor and show. */
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { loadRelations } from '../artifacts/relations.js';
|
|
5
|
+
import { parseRuleFrontmatter, inferRuleActivation } from './activation.mjs';
|
|
6
|
+
import { loadRulePinConfigs, installedRuleCapabilities, resolveRulePins, capabilityId } from './pins.mjs';
|
|
7
|
+
export function ruleFiles(dir) {
|
|
8
|
+
if (!fs.existsSync(dir))
|
|
9
|
+
return [];
|
|
10
|
+
return fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => entry.isDirectory()
|
|
11
|
+
? ruleFiles(path.join(dir, entry.name)) : entry.name.endsWith('.md') && !/^RULES-(INDEX|ONDEMAND)/.test(entry.name) ? [path.join(dir, entry.name)] : []);
|
|
12
|
+
}
|
|
13
|
+
function localRuleSource(cwd, localPath) {
|
|
14
|
+
const wrapper = path.resolve(cwd, localPath);
|
|
15
|
+
const manifest = path.join(wrapper, 'manifest.json');
|
|
16
|
+
if (!fs.existsSync(manifest))
|
|
17
|
+
return wrapper;
|
|
18
|
+
const payload = JSON.parse(fs.readFileSync(manifest, 'utf8')).pluginConfig?.payloadPath;
|
|
19
|
+
if (!payload)
|
|
20
|
+
return wrapper;
|
|
21
|
+
const source = path.resolve(wrapper, payload);
|
|
22
|
+
const relative = path.relative(wrapper, source);
|
|
23
|
+
if (!relative || relative.startsWith(`..${path.sep}`) || relative === '..' || path.isAbsolute(relative))
|
|
24
|
+
throw new Error(`${manifest}: plugin payload must remain beneath its wrapper`);
|
|
25
|
+
const realRelative = path.relative(fs.realpathSync(wrapper), fs.realpathSync(source));
|
|
26
|
+
if (realRelative === '..' || realRelative.startsWith(`..${path.sep}`) || path.isAbsolute(realRelative))
|
|
27
|
+
throw new Error(`${manifest}: plugin payload escapes its wrapper`);
|
|
28
|
+
return source;
|
|
29
|
+
}
|
|
30
|
+
export function rulePinState(cwd, root, configs = loadRulePinConfigs(cwd), requireInventory = false, cacheSourceDigest = true) {
|
|
31
|
+
const installed = installedRuleCapabilities(configs);
|
|
32
|
+
if (!requireInventory && !configs.project.rules?.always_load?.length && !configs.user.rules?.always_load?.length) {
|
|
33
|
+
return { ...resolveRulePins({ rules: [], configs, installed }), installed, configs, inventory: [] };
|
|
34
|
+
}
|
|
35
|
+
const relations = loadRelations(cwd, cwd, cacheSourceDigest) ?? loadRelations(root, cwd, cacheSourceDigest);
|
|
36
|
+
const rules = [];
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
if (relations)
|
|
39
|
+
for (const [id, rule] of Object.entries(relations.index.rules)) {
|
|
40
|
+
if (!installed.includes(capabilityId(rule.capability) ?? '') || rule.type !== 'rule')
|
|
41
|
+
continue;
|
|
42
|
+
const file = path.resolve(root, rule.path);
|
|
43
|
+
// A stale sidecar must never retain an uninstalled or removed body.
|
|
44
|
+
if (!fs.existsSync(file))
|
|
45
|
+
continue;
|
|
46
|
+
seen.add(file);
|
|
47
|
+
rules.push({ ...rule, id, indexedPath: rule.path, path: file, file });
|
|
48
|
+
}
|
|
49
|
+
for (const capability of installed) {
|
|
50
|
+
const entry = configs.project.installed?.[capability];
|
|
51
|
+
const dirs = entry?.localPath ? [localRuleSource(cwd, entry.localPath)]
|
|
52
|
+
: ['frameworks', 'addons', 'extensions', 'plugins'].map(kind => path.join(root, 'agentic/code', kind, capability));
|
|
53
|
+
for (const dir of dirs)
|
|
54
|
+
for (const file of ruleFiles(path.join(dir, 'rules'))) {
|
|
55
|
+
if (seen.has(file))
|
|
56
|
+
continue;
|
|
57
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
58
|
+
const parsed = parseRuleFrontmatter(content);
|
|
59
|
+
if (!parsed.valid)
|
|
60
|
+
throw new Error(`${file}: ${parsed.diagnostics.map(d => d.message).join('; ')}`);
|
|
61
|
+
const data = parsed.data;
|
|
62
|
+
const enforcement = String(data.enforcement ?? content.match(/^\*\*Enforcement Level\*\*:\s*([A-Za-z]+)/m)?.[1] ?? '').toLowerCase() || null;
|
|
63
|
+
rules.push({ id: file, name: String(data.name || path.basename(file, '.md')), path: file, file,
|
|
64
|
+
indexedPath: path.relative(root, file), capability, enforcement,
|
|
65
|
+
activation: parsed.rule.tagged ? parsed.rule.activation : inferRuleActivation(enforcement) });
|
|
66
|
+
seen.add(file);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// The source fallback covers project-local bundles or a fresh install with no sidecar.
|
|
70
|
+
const effective = resolveRulePins({ rules, configs, installed, relations });
|
|
71
|
+
return { ...effective, installed, configs, inventory: rules };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=pin-state.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RulePinSelector, RulesConfig } from './inline-budget.mjs';
|
|
2
|
+
import type { RelationsReader } from '../artifacts/relations.js';
|
|
3
|
+
export interface PinConfig { installed?: Record<string, unknown>; providers?: string[]; rules?: RulesConfig }
|
|
4
|
+
export interface PinConfigs { project: PinConfig; user: PinConfig }
|
|
5
|
+
export interface PinRule { id: string; name: string; path: string; capability: string | null; activation: string | null; enforcement: string | null; type?: string; file?: string; indexedPath?: string; behavior?: boolean }
|
|
6
|
+
export interface PinnedRule extends PinRule { canonical_activation: string | null; activation: 'kernel'; matches: Array<{ source: 'user' | 'project'; selector: RulePinSelector }> }
|
|
7
|
+
export const RULE_PIN_TIERS: string[];
|
|
8
|
+
export function rulePinConfigPath(cwd: string, source?: 'user' | 'project', userConfigDir?: string): string;
|
|
9
|
+
export function loadRulePinConfigs(cwd: string, project?: PinConfig, userConfigDir?: string): PinConfigs;
|
|
10
|
+
export function capabilityId(id: string | null): string | null;
|
|
11
|
+
export function installedRuleCapabilities(configs: PinConfigs): string[];
|
|
12
|
+
export function validateRulePinCapabilities(rules: unknown, installed: string[], groups?: Record<string, string[]>): string[];
|
|
13
|
+
export function selectorKey(selector: RulePinSelector): string;
|
|
14
|
+
export function resolveRulePins(options: { rules: PinRule[]; configs: PinConfigs; installed?: string[]; relations?: RelationsReader | null }): { rules: PinnedRule[]; warnings: string[] };
|