@aiwg/cli 2026.10.4 → 2026.10.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
- package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
- package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
- package/agentic/code/providers/model-capabilities.v1.json +74 -50
- package/bin/aiwg.mjs +3 -1
- package/dist/src/artifacts/browser-export.js +1 -0
- package/dist/src/artifacts/citation-parser.js +8 -1
- package/dist/src/artifacts/cli.js +72 -9
- package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
- package/dist/src/artifacts/fortemi-core-sync.js +26 -8
- package/dist/src/artifacts/graph-query.js +18 -8
- package/dist/src/artifacts/index-adapters.js +339 -0
- package/dist/src/artifacts/index-builder.js +146 -16
- package/dist/src/artifacts/index-files.js +15 -4
- package/dist/src/artifacts/index-preparation.js +91 -26
- package/dist/src/artifacts/index-reader.js +44 -0
- package/dist/src/artifacts/index-refresh.js +130 -56
- package/dist/src/artifacts/index-status.js +20 -5
- package/dist/src/artifacts/index-worker.mjs +9 -7
- package/dist/src/artifacts/memory-maintenance.js +503 -0
- package/dist/src/artifacts/prebuilt-relations.js +3 -1
- package/dist/src/artifacts/query-engine.js +263 -108
- package/dist/src/artifacts/relations-builder.js +2 -2
- package/dist/src/artifacts/source-graph.js +2 -2
- package/dist/src/artifacts/types.js +9 -1
- package/dist/src/cli/handlers/decision.js +19 -1
- package/dist/src/cli/handlers/guided-setup.js +470 -0
- package/dist/src/cli/handlers/help.js +2 -2
- package/dist/src/cli/handlers/housekeeper.js +1110 -0
- package/dist/src/cli/handlers/index.js +4 -2
- package/dist/src/cli/handlers/init.js +41 -6
- package/dist/src/cli/handlers/local-executor.js +5 -3
- package/dist/src/cli/handlers/mc.js +509 -53
- package/dist/src/cli/handlers/models.js +32 -2
- package/dist/src/cli/handlers/ralph-launcher.js +76 -4
- package/dist/src/cli/handlers/rules.js +1 -1
- package/dist/src/cli/handlers/script-runner.js +73 -2
- package/dist/src/cli/handlers/serve.js +5 -3
- package/dist/src/cli/handlers/sessions.js +40 -6
- package/dist/src/cli/handlers/setup.js +428 -1
- package/dist/src/cli/handlers/skill-lint.js +1 -1
- package/dist/src/cli/handlers/subcommands.js +118 -8
- package/dist/src/cli/handlers/use.js +279 -93
- package/dist/src/cli/handlers/workspace.js +3 -1
- package/dist/src/cli/prompt-utils.js +8 -0
- package/dist/src/cli/router.js +16 -1
- package/dist/src/config/aiwg-config.js +42 -0
- package/dist/src/config/cli.js +7 -3
- package/dist/src/config/decision-config.js +14 -1
- package/dist/src/config/native-sandbox-warnings.js +25 -0
- package/dist/src/contributors/validation.js +6 -5
- package/dist/src/decision/admission.js +7 -3
- package/dist/src/decision/benchmark.js +469 -0
- package/dist/src/decision/ledger.js +22 -3
- package/dist/src/decision/pricing.js +147 -0
- package/dist/src/decision/qualification/gate-evidence.js +18 -15
- package/dist/src/decision/qualification/load-staged.js +539 -0
- package/dist/src/decision/qualification/load.js +109 -33
- package/dist/src/decision/qualification/runner.js +5 -2
- package/dist/src/decision/review/service.js +6 -4
- package/dist/src/decision/review/validate.js +23 -2
- package/dist/src/decision/savings.js +8 -3
- package/dist/src/discovery/index.js +492 -0
- package/dist/src/discovery/interview.js +233 -0
- package/dist/src/discovery/plan-apply.js +571 -0
- package/dist/src/discovery/plan-store.js +171 -0
- package/dist/src/discovery/plan-types.js +2 -0
- package/dist/src/discovery/plan-validation.js +139 -0
- package/dist/src/discovery/types.js +2 -0
- package/dist/src/extensions/commands/definitions.js +49 -9
- package/dist/src/extensions/manifest.js +71 -44
- package/dist/src/extensions/project-local-discovery.js +1 -1
- package/dist/src/extensions/project-quickref.js +18 -17
- package/dist/src/extensions/validation.js +32 -29
- package/dist/src/housekeeper/daemon-config.js +148 -0
- package/dist/src/housekeeper/ipc.js +84 -0
- package/dist/src/housekeeper/lifecycle.js +148 -0
- package/dist/src/housekeeper/platform.js +35 -0
- package/dist/src/housekeeper/status.js +70 -0
- package/dist/src/housekeeper/units.js +114 -0
- package/dist/src/jobs/flow.js +22 -21
- package/dist/src/lint/rule-activation.js +20 -5
- package/dist/src/lint/runner.js +1 -1
- package/dist/src/mcp/cli.mjs +5 -6
- package/dist/src/mcp/config-file.mjs +6 -2
- package/dist/src/mcp/config-layers.mjs +23 -7
- package/dist/src/mcp/profiles.js +18 -31
- package/dist/src/mcp/profiles.mjs +18 -32
- package/dist/src/mcp/registry.js +7 -27
- package/dist/src/mcp/registry.mjs +7 -28
- package/dist/src/mcp/server.mjs +34 -1
- package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
- package/dist/src/mcp/tools/command-run.mjs +3 -2
- package/dist/src/mcp/tools/decision.mjs +49 -48
- package/dist/src/mcp/tools/discovery.mjs +46 -21
- package/dist/src/mcp/tools/orchestration.mjs +22 -22
- package/dist/src/mcp/tools/subsystems.mjs +9 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +17 -4
- package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
- package/dist/src/memory/audit.js +719 -0
- package/dist/src/memory/canonical-context.js +392 -13
- package/dist/src/memory/canonical-maintenance.js +95 -0
- package/dist/src/memory/cli-result.js +22 -0
- package/dist/src/memory/cli.js +176 -0
- package/dist/src/memory/context-pack.js +57 -6
- package/dist/src/memory/cost.js +512 -0
- package/dist/src/memory/firewall-classification.d.mts +7 -0
- package/dist/src/memory/firewall-classification.mjs +32 -0
- package/dist/src/memory/index.js +8 -0
- package/dist/src/memory/line-maintenance.js +68 -0
- package/dist/src/memory/maintenance-cli.js +286 -0
- package/dist/src/memory/maintenance-contract.js +136 -0
- package/dist/src/memory/maintenance-index.js +18 -0
- package/dist/src/memory/maintenance-owner.js +2 -0
- package/dist/src/memory/maintenance-record.js +138 -0
- package/dist/src/memory/maintenance.js +254 -0
- package/dist/src/memory/provider-memory-adapter.js +154 -0
- package/dist/src/memory/recommend-cli.js +83 -0
- package/dist/src/memory/recommend.js +269 -0
- package/dist/src/memory/scheduled.js +81 -0
- package/dist/src/memory/taxonomy.js +44 -0
- package/dist/src/mission-protocol/codecs.js +19 -9
- package/dist/src/models/config-loader.js +11 -1
- package/dist/src/models/model-capabilities.v1.json +74 -50
- package/dist/src/models/provider-policy.js +125 -62
- package/dist/src/models/resolver.js +45 -8
- package/dist/src/models/worker-installation.d.mts +7 -0
- package/dist/src/models/worker-installation.mjs +169 -37
- package/dist/src/models/wrapper-route.js +1 -1
- package/dist/src/output-modes/registry.js +8 -7
- package/dist/src/plugin/metadata-validator.js +1 -1
- package/dist/src/providers/deploy-version.d.mts +1 -0
- package/dist/src/providers/deploy-version.mjs +17 -0
- package/dist/src/providers/provider-definitions.js +19 -16
- package/dist/src/providers/skill-ownership.d.mts +11 -2
- package/dist/src/providers/skill-ownership.mjs +35 -17
- package/dist/src/rules/activation.d.mts +11 -2
- package/dist/src/rules/activation.mjs +82 -6
- package/dist/src/rules/inventory.js +1 -1
- package/dist/src/rules/rule-frontmatter.schema.json +2 -0
- package/dist/src/schema/zod-validation.d.mts +6 -0
- package/dist/src/schema/zod-validation.mjs +50 -0
- package/dist/src/serve/executor-registry.js +297 -20
- package/dist/src/serve/executor-token.d.mts +1 -0
- package/dist/src/serve/executor-token.mjs +8 -0
- package/dist/src/serve/shared-host-scheduler.js +1102 -31
- package/dist/src/serve/worker-invocation.js +114 -15
- package/dist/src/sessions/adapters/claude.js +19 -18
- package/dist/src/sessions/adapters/codex.js +33 -19
- package/dist/src/sessions/adapters/copilot.js +14 -13
- package/dist/src/sessions/adapters/cursor.js +19 -18
- package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
- package/dist/src/sessions/adapters/factory.js +11 -10
- package/dist/src/sessions/adapters/generic.js +32 -23
- package/dist/src/sessions/adapters/grokbot.js +35 -35
- package/dist/src/sessions/adapters/hermes.js +20 -19
- package/dist/src/sessions/adapters/muse.js +50 -49
- package/dist/src/sessions/adapters/omp.js +5 -4
- package/dist/src/sessions/adapters/openclaw.js +30 -29
- package/dist/src/sessions/adapters/opencode.js +26 -26
- package/dist/src/sessions/adapters/openhuman.js +24 -23
- package/dist/src/sessions/adapters/pi.js +5 -4
- package/dist/src/sessions/adapters/windsurf.js +8 -8
- package/dist/src/sessions/analytics.js +10 -9
- package/dist/src/sessions/batch-contracts.js +1 -1
- package/dist/src/sessions/batch-import.js +202 -2
- package/dist/src/sessions/candidates.js +7 -6
- package/dist/src/sessions/contracts.js +73 -54
- package/dist/src/sessions/coverage-preflight.js +26 -1
- package/dist/src/sessions/output-registration.js +11 -10
- package/dist/src/sessions/policy.js +18 -7
- package/dist/src/sessions/refresh.js +2 -3
- package/dist/src/sessions/repository.js +4 -2
- package/dist/src/sessions/workspace-discovery.js +92 -26
- package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
- package/dist/src/smiths/context-pipeline/claude-hook.js +42 -6
- package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
- package/dist/src/smiths/context-pipeline/workspace-context.js +106 -10
- package/dist/src/update/checker.d.mts +4 -0
- package/dist/src/update/checker.mjs +14 -0
- package/dist/src/writing/exemplar-selection.js +6 -6
- package/dist/src/writing/voice-evaluation.js +24 -23
- package/dist/src/writing/writer-migration.js +14 -13
- package/dist/src/writing/writer-profile.js +19 -18
- package/dist/src/writing/writing-brief.js +18 -17
- package/dist/src/writing/writing-receipt.js +47 -46
- package/package.json +5 -2
- package/schemas/decision/DecisionReview.schema.json +78 -1
- package/schemas/mission-v1.schema.json +3 -0
- package/tools/agents/deploy-agents.mjs +67 -39
- package/tools/agents/providers/antigravity.mjs +6 -1
- package/tools/agents/providers/base.mjs +19 -13
- package/tools/agents/providers/claude.mjs +1 -1
- package/tools/agents/providers/codex.mjs +50 -56
- package/tools/agents/providers/copilot.mjs +8 -2
- package/tools/agents/providers/cursor.mjs +1 -1
- package/tools/agents/providers/deepseek-harness.mjs +1 -1
- package/tools/agents/providers/factory.mjs +7 -1
- package/tools/agents/providers/grok-build.mjs +18 -2
- package/tools/agents/providers/grokbot.mjs +1 -1
- package/tools/agents/providers/hermes.mjs +7 -13
- package/tools/agents/providers/muse-hooks.mjs +21 -14
- package/tools/agents/providers/muse.mjs +7 -2
- package/tools/agents/providers/omp.mjs +28 -4
- package/tools/agents/providers/opencode.mjs +14 -5
- package/tools/agents/providers/openhuman.mjs +1 -1
- package/tools/agents/providers/pi.mjs +1 -1
- package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
- package/tools/agents/providers/rule-delivery.mjs +263 -71
- package/tools/agents/providers/windsurf.mjs +7 -0
- package/tools/cli/workspace-status.mjs +5 -0
- package/tools/commands/deploy-prompts-codex.mjs +11 -8
- package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
- package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
- package/tools/ralph-external/process-monitor.mjs +7 -4
- package/tools/ralph-external/session-launcher.mjs +280 -32
- package/tools/skills/deploy-skills-codex.mjs +11 -7
- package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
|
@@ -18,7 +18,7 @@ import * as path from 'node:path';
|
|
|
18
18
|
import { OPERATIONAL_DISCOVERY_TYPES, OPERATIONAL_SHOW_TYPES, isOperationalShowType, loadGlobalGraphConfigs, getGraphIndexDir, } from './types.js';
|
|
19
19
|
import { loadMetadataIndex, loadGraphIndexFile } from './index-reader.js';
|
|
20
20
|
import { bm25Rank } from './fulltext.js';
|
|
21
|
-
import { parseFrontmatter } from './index-builder.js';
|
|
21
|
+
import { parseFrontmatter, parseFlowDoc, extractMarkdownLinks } from './index-builder.js';
|
|
22
22
|
import { applyFacetFusion, diagnoseFacetActivations, } from './discover-facets.js';
|
|
23
23
|
import { recordTypeForEntry, stableRecordId, } from './browser-export.js';
|
|
24
24
|
import { loadProviderModelMetadata } from '../models/provider-models.js';
|
|
@@ -27,6 +27,13 @@ import { operationalStateQueryProjection, } from './operational-state.js';
|
|
|
27
27
|
function normalizeIndexedPath(entryPath) {
|
|
28
28
|
return entryPath.replace(/\\/g, '/');
|
|
29
29
|
}
|
|
30
|
+
/** Historical records stay in the index for explicit show/history lookup. */
|
|
31
|
+
export function isOrdinaryMemoryEntry(entry, _cwd) {
|
|
32
|
+
// Maintenance owners publish dispositions into the index before returning.
|
|
33
|
+
// Retrieval must not read sources: ordinary legacy entries need no memory
|
|
34
|
+
// stamp, and temporary source I/O failures cannot hide active metadata.
|
|
35
|
+
return !entry.memoryRecord || (entry.memoryRecord.lifecycle === 'active' && entry.memoryRecord.placement !== 'archived');
|
|
36
|
+
}
|
|
30
37
|
/**
|
|
31
38
|
* Resolve an index entry's source file path and read its body (frontmatter
|
|
32
39
|
* stripped). Entry paths are normally relative to the project; framework-graph
|
|
@@ -657,6 +664,8 @@ export async function queryIndex(cwd, params, options = {}) {
|
|
|
657
664
|
}
|
|
658
665
|
candidates = allEntries;
|
|
659
666
|
}
|
|
667
|
+
// Retained tombstones are history, never ordinary retrieval results.
|
|
668
|
+
candidates = candidates.filter(entry => isOrdinaryMemoryEntry(entry, cwd));
|
|
660
669
|
// Apply filters
|
|
661
670
|
if (params.type) {
|
|
662
671
|
candidates = candidates.filter(e => e.type === params.type);
|
|
@@ -704,8 +713,10 @@ export async function queryIndex(cwd, params, options = {}) {
|
|
|
704
713
|
}
|
|
705
714
|
process.exit(1);
|
|
706
715
|
}
|
|
707
|
-
|
|
716
|
+
const candidateByPath = new Map(candidates.map(entry => [entry.path, entry]));
|
|
717
|
+
results = queried.results.filter(result => candidateByPath.has(result.path)).map(result => {
|
|
708
718
|
const entry = {
|
|
719
|
+
...(candidateByPath.get(result.path)?.memoryRecord ? { memoryRecord: candidateByPath.get(result.path).memoryRecord } : {}),
|
|
709
720
|
path: result.path,
|
|
710
721
|
type: result.type,
|
|
711
722
|
phase: result.phase,
|
|
@@ -758,7 +769,7 @@ export async function queryIndex(cwd, params, options = {}) {
|
|
|
758
769
|
}
|
|
759
770
|
// Apply limit
|
|
760
771
|
const limit = params.limit ?? 20;
|
|
761
|
-
results = results.slice(0, limit);
|
|
772
|
+
results = results.filter(result => isOrdinaryMemoryEntry(result.entry, cwd)).slice(0, limit);
|
|
762
773
|
const queryTimeMs = Date.now() - startTime;
|
|
763
774
|
// Output
|
|
764
775
|
if (options.json) {
|
|
@@ -1071,6 +1082,7 @@ export async function discoverCapability(cwd, params) {
|
|
|
1071
1082
|
process.exit(1);
|
|
1072
1083
|
}
|
|
1073
1084
|
// Filter by type
|
|
1085
|
+
entries = entries.filter(entry => isOrdinaryMemoryEntry(entry, cwd));
|
|
1074
1086
|
const candidates = entries.filter(e => types.includes(e.type));
|
|
1075
1087
|
// Score (strict overlap gate). Keep the full sorted list so the facet
|
|
1076
1088
|
// fusion below can inject/lift curated capabilities into the top-K before
|
|
@@ -1377,13 +1389,21 @@ async function loadShowEntries(cwd, params, compact = false) {
|
|
|
1377
1389
|
if (compact && resourceSource !== 'web' && (!params.graph || params.graph === 'framework')) {
|
|
1378
1390
|
relations = await ensureRelations(cwd) ?? undefined;
|
|
1379
1391
|
if (relations?.index.artifacts) {
|
|
1380
|
-
const
|
|
1381
|
-
|
|
1382
|
-
|
|
1392
|
+
const reader = relations;
|
|
1393
|
+
const matched = findShowMatches(Object.values(reader.index.artifacts), params.typeFilter ?? [...OPERATIONAL_SHOW_TYPES], params.name.trim());
|
|
1394
|
+
const frameworkEntries = matched.map(entry => {
|
|
1395
|
+
const id = discoveryIdForEntry(entry);
|
|
1396
|
+
const rule = reader.index.rules[id];
|
|
1397
|
+
return withIndexProvenance({ ...entry, phase: '', tags: [], created: '', updated: '', checksum: '', summary: '', dependencies: [], dependents: [],
|
|
1398
|
+
...(rule ? { activation: reader.pinnedRuleIds.has(id) ? 'kernel' : rule.activation ?? undefined,
|
|
1399
|
+
ruleSummary: rule.summary, ruleEnforcement: rule.enforcement ?? undefined, bound_to: rule.bound_to } : {}),
|
|
1400
|
+
}, 'framework');
|
|
1401
|
+
});
|
|
1383
1402
|
const localEntries = params.graph ? [] : defaultCapabilityGraphs(cwd).filter(graph => graph !== 'framework')
|
|
1384
|
-
.flatMap(graph => loadLocalCapabilityEntries(cwd, graph));
|
|
1403
|
+
.flatMap(graph => loadLocalCapabilityEntries(cwd, graph, undefined, true));
|
|
1385
1404
|
const compactEntries = [...localEntries, ...frameworkEntries];
|
|
1386
|
-
if (
|
|
1405
|
+
if ((params.typeFilter?.every(type => ['skill', 'agent', 'flow', 'command', 'rule', 'behavior'].includes(type)))
|
|
1406
|
+
|| findShowMatches(compactEntries, params.typeFilter ?? [...OPERATIONAL_SHOW_TYPES], params.name.trim()).length) {
|
|
1387
1407
|
return { entries: compactEntries, aiwgRoot, resourceSource: 'local', relations };
|
|
1388
1408
|
}
|
|
1389
1409
|
}
|
|
@@ -1482,12 +1502,12 @@ async function loadShowEntries(cwd, params, compact = false) {
|
|
|
1482
1502
|
}
|
|
1483
1503
|
}
|
|
1484
1504
|
else {
|
|
1485
|
-
entries = loadLocalCapabilityEntries(cwd, params.graph);
|
|
1505
|
+
entries = loadLocalCapabilityEntries(cwd, params.graph, undefined, true);
|
|
1486
1506
|
}
|
|
1487
1507
|
return { entries, aiwgRoot, resourceSource: 'local', relations };
|
|
1488
1508
|
}
|
|
1489
1509
|
/** Local index inputs shared by show and exact rule-binding validation (#2938). */
|
|
1490
|
-
export function loadLocalCapabilityEntries(cwd, graph, snapshot) {
|
|
1510
|
+
export function loadLocalCapabilityEntries(cwd, graph, snapshot, includeHistory = false) {
|
|
1491
1511
|
const entries = [];
|
|
1492
1512
|
for (const selectedGraph of graph ? [graph] : defaultCapabilityGraphs(cwd)) {
|
|
1493
1513
|
const key = snapshot && `${selectedGraph}:${getGraphIndexDir(cwd, selectedGraph)}`;
|
|
@@ -1505,7 +1525,7 @@ export function loadLocalCapabilityEntries(cwd, graph, snapshot) {
|
|
|
1505
1525
|
if (legacy)
|
|
1506
1526
|
entries.push(...Object.values(legacy.entries));
|
|
1507
1527
|
}
|
|
1508
|
-
return entries;
|
|
1528
|
+
return includeHistory ? entries : entries.filter(entry => isOrdinaryMemoryEntry(entry, cwd));
|
|
1509
1529
|
}
|
|
1510
1530
|
/** Project/user inputs only; package artifacts come from a validated compact table. */
|
|
1511
1531
|
export function loadLocalOverrideCapabilityEntries(cwd) {
|
|
@@ -1539,7 +1559,7 @@ function findShowMatches(entries, types, needle) {
|
|
|
1539
1559
|
const needleLower = needle.toLowerCase();
|
|
1540
1560
|
const normalizedNeedle = normalizeIndexedPath(needle);
|
|
1541
1561
|
const candidates = entries.filter(e => types.includes(e.type));
|
|
1542
|
-
let matches = candidates.filter(e => discoveryIdForEntry(e) === needle);
|
|
1562
|
+
let matches = needle.startsWith('aiwg:') ? candidates.filter(e => discoveryIdForEntry(e) === needle) : [];
|
|
1543
1563
|
if (matches.length === 0) {
|
|
1544
1564
|
matches = candidates.filter(e => e.path === needle || normalizeIndexedPath(e.path) === normalizedNeedle);
|
|
1545
1565
|
}
|
|
@@ -1933,7 +1953,7 @@ export function compoundGoverningRules(entries, target, relations, phrase = '',
|
|
|
1933
1953
|
const found = new Map();
|
|
1934
1954
|
const add = (rule, why) => {
|
|
1935
1955
|
const activation = rule.activation ?? (rule.type === 'behavior' ? 'kernel' : inferRuleActivation(rule.ruleEnforcement));
|
|
1936
|
-
if (excludeKernel && (activation === 'kernel' || rule.kernel || relations?.pinnedRuleIds.has(discoveryIdForEntry(rule))))
|
|
1956
|
+
if (excludeKernel && (activation === 'kernel' || activation === 'core' || rule.kernel || relations?.pinnedRuleIds.has(discoveryIdForEntry(rule))))
|
|
1937
1957
|
return;
|
|
1938
1958
|
const key = `${rule.type}:${rule.path}`, item = found.get(key) ?? { rule, applicability: [] };
|
|
1939
1959
|
if (!item.applicability.includes(why))
|
|
@@ -1979,8 +1999,8 @@ export function compoundGoverningRules(entries, target, relations, phrase = '',
|
|
|
1979
1999
|
return [...found.values()].sort((a, b) => (a.rule.name ?? a.rule.path).localeCompare(b.rule.name ?? b.rule.path) || a.rule.path.localeCompare(b.rule.path));
|
|
1980
2000
|
}
|
|
1981
2001
|
function boundRuleDescriptor(rule) {
|
|
1982
|
-
return { name: rule.name ?? path.basename(rule.path, '.md'), activation: rule.activation ?? null,
|
|
1983
|
-
summary: rule.ruleSummary ?? '', show_command: `aiwg show ${rule.type} ${rule.name ?? path.basename(rule.path, '.md')}` };
|
|
2002
|
+
return { name: rule.name ?? path.basename(rule.path, '.md'), activation: rule.activation ?? null, enforcement: rule.ruleEnforcement ?? null,
|
|
2003
|
+
summary: (rule.ruleSummary ?? '').replace(/\s+/g, ' ').trim(), show_command: `aiwg show ${rule.type} ${rule.name ?? path.basename(rule.path, '.md')} --body` };
|
|
1984
2004
|
}
|
|
1985
2005
|
/** Counts unique attached contracts; every emitted check is required irrespective of severity. */
|
|
1986
2006
|
export function governingRuleStats(governing) {
|
|
@@ -1998,7 +2018,7 @@ export function governingRuleFields(governing) {
|
|
|
1998
2018
|
return {};
|
|
1999
2019
|
return { bound_rules: governing.map(({ rule }) => boundRuleDescriptor(rule)), rule_stats: governingRuleStats(governing),
|
|
2000
2020
|
required_rule_checks: governing.map(({ rule, applicability }) => ({ rule_id: discoveryIdForEntry(rule),
|
|
2001
|
-
applicability, fetch_command: `aiwg show ${rule.type} ${discoveryIdForEntry(rule)}` })), rule_check_obligation: RULE_CHECK_OBLIGATION };
|
|
2021
|
+
applicability, fetch_command: `aiwg show ${rule.type} ${discoveryIdForEntry(rule)} --body` })), rule_check_obligation: RULE_CHECK_OBLIGATION };
|
|
2002
2022
|
}
|
|
2003
2023
|
function statsText(stats) {
|
|
2004
2024
|
return `rules: ${Object.entries(stats.by_enforcement).map(([level, count]) => `${count} ${level}`).join(', ') || '0'} · activation: ${Object.entries(stats.by_activation).map(([mode, count]) => `${count} ${mode}`).join(', ') || 'none'} · ${stats.required_checks} required checks`;
|
|
@@ -2021,10 +2041,13 @@ export function relationshipRuleStats(entries, target, relations) {
|
|
|
2021
2041
|
}
|
|
2022
2042
|
async function showBindings(entries, target, content, params, read, relations) {
|
|
2023
2043
|
const governing = compoundGoverningRules(entries, target, relations, '', false, false);
|
|
2044
|
+
for (const { rule } of governing)
|
|
2045
|
+
if (relations?.pinnedRuleIds.has(discoveryIdForEntry(rule)))
|
|
2046
|
+
rule.activation = 'kernel';
|
|
2024
2047
|
const rules = governing.map(item => item.rule);
|
|
2025
2048
|
const boundRules = await Promise.all(rules.map(async (rule) => ({
|
|
2026
2049
|
...boundRuleDescriptor(rule),
|
|
2027
|
-
...(params.boundRuleBodies ? { content: await read(rule) } : {}),
|
|
2050
|
+
...((params.boundRuleBodies || params.withRuleBodies) ? { content: await read(rule) } : {}),
|
|
2028
2051
|
})));
|
|
2029
2052
|
// Parse the selected rule for reverse lookup, including corpus-only rules.
|
|
2030
2053
|
const targets = target.type === 'rule' || target.type === 'behavior'
|
|
@@ -2035,24 +2058,227 @@ async function showBindings(entries, target, content, params, read, relations) {
|
|
|
2035
2058
|
const names = Array.isArray(values) ? [...new Set(values.filter((value) => typeof value === 'string' && !!value.trim()))] : [];
|
|
2036
2059
|
return names.length ? [[key, names]] : [];
|
|
2037
2060
|
})) : {};
|
|
2061
|
+
const boundTargets = new Map();
|
|
2062
|
+
if (target.type === 'rule' || target.type === 'behavior') {
|
|
2063
|
+
for (const edge of relations?.targetsForRule(discoveryIdForEntry(target)) ?? []) {
|
|
2064
|
+
boundTargets.set(`${edge.type}:${edge.id}`, { ...edge, name: relations?.index.artifacts?.[edge.id]?.name
|
|
2065
|
+
?? relations?.index.targets[`${edge.type}:${edge.id}`]?.name });
|
|
2066
|
+
}
|
|
2067
|
+
const selectedRule = { ...target, bound_to: boundTo };
|
|
2068
|
+
for (const candidate of entries)
|
|
2069
|
+
if (rulesBoundTo([selectedRule], candidate).length) {
|
|
2070
|
+
const id = discoveryIdForEntry(candidate);
|
|
2071
|
+
boundTargets.set(`${candidate.type}:${id}`, { type: candidate.type, id, name: candidate.name });
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2038
2074
|
let text = '';
|
|
2039
2075
|
if (boundRules.length) {
|
|
2040
|
-
|
|
2076
|
+
// Cards already print these stats in the header; body/section output has no header.
|
|
2077
|
+
const stats = params.body || params.sections?.length ? `${statsText(governingRuleStats(governing))}\n\n` : '';
|
|
2078
|
+
text += `\n## Bound rules\n\n${RULE_CHECK_OBLIGATION}\n\n${stats}` + boundRules.map(rule => `- **${rule.name}** [${rule.activation ?? 'unknown'} / ${rule.enforcement ?? 'unknown'}]: ${rule.summary} (\`${rule.show_command}\`)`
|
|
2041
2079
|
+ ('content' in rule ? `\n\n${rule.content}` : '')).join('\n') + '\n';
|
|
2042
2080
|
}
|
|
2043
2081
|
if (Object.keys(boundTo).length) {
|
|
2044
2082
|
text += '\n## Bound targets\n\n' + Object.entries(boundTo)
|
|
2045
2083
|
.map(([key, names]) => `- ${key}: ${names.map(name => `\`${name}\``).join(', ')}`).join('\n') + '\n';
|
|
2046
2084
|
}
|
|
2085
|
+
if (boundTargets.size)
|
|
2086
|
+
text += '\nResolved targets: ' + [...boundTargets.values()].map(target => `${target.type} ${target.name ?? target.id}`).join(', ') + '\n';
|
|
2047
2087
|
return { fields: {
|
|
2048
2088
|
...governingRuleFields(governing),
|
|
2089
|
+
...(['rule', 'behavior'].includes(target.type) ? { bound_to: boundTo, bound_targets: [...boundTargets.values()] } : {}),
|
|
2049
2090
|
...(boundRules.length ? { bound_rules: boundRules } : {}),
|
|
2050
2091
|
...(Object.keys(boundTo).length ? { bound_to: boundTo } : {}),
|
|
2051
2092
|
}, text };
|
|
2052
2093
|
}
|
|
2094
|
+
/** Sections include their nested headings, stopping at the next peer/ancestor. */
|
|
2095
|
+
function showSections(content) {
|
|
2096
|
+
const body = parseFrontmatter(content).body;
|
|
2097
|
+
const headings = [];
|
|
2098
|
+
let offset = 0;
|
|
2099
|
+
let fence = '';
|
|
2100
|
+
const lines = body.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
2101
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2102
|
+
const line = lines[i];
|
|
2103
|
+
const marker = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
|
2104
|
+
if (marker) {
|
|
2105
|
+
if (!fence)
|
|
2106
|
+
fence = marker[1];
|
|
2107
|
+
else if (marker[1][0] === fence[0] && marker[1].length >= fence.length)
|
|
2108
|
+
fence = '';
|
|
2109
|
+
}
|
|
2110
|
+
else if (!fence) {
|
|
2111
|
+
const atx = line.match(/^ {0,3}(#{1,6})\s+(.+?)(?:\s+#+)?\s*$/);
|
|
2112
|
+
const setext = i + 1 < lines.length && lines[i + 1].match(/^ {0,3}(=+|-+)\s*$/);
|
|
2113
|
+
if (atx)
|
|
2114
|
+
headings.push({ heading: atx[2], level: atx[1].length, start: offset });
|
|
2115
|
+
else if (line.trim() && setext)
|
|
2116
|
+
headings.push({ heading: line.trim(), level: setext[1][0] === '=' ? 1 : 2, start: offset });
|
|
2117
|
+
}
|
|
2118
|
+
offset += line.length;
|
|
2119
|
+
}
|
|
2120
|
+
return headings.map((heading, i) => {
|
|
2121
|
+
const end = headings.slice(i + 1).find(next => next.level <= heading.level)?.start ?? body.length;
|
|
2122
|
+
const text = body.slice(heading.start, end);
|
|
2123
|
+
return { heading: heading.heading, level: heading.level, bytes: Buffer.byteLength(text),
|
|
2124
|
+
lines: text.split('\n').length - (text.endsWith('\n') ? 1 : 0), text };
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
/** List companion directories and explicitly linked resources within the owning bundle.
|
|
2128
|
+
* Resolve paths against that boundary and exclude symlink components. */
|
|
2129
|
+
function showResources(filePath, source, release) {
|
|
2130
|
+
if (release) {
|
|
2131
|
+
const base = path.posix.dirname(filePath);
|
|
2132
|
+
const linked = new Set(extractMarkdownLinks(source).map(link => path.posix.normalize(path.posix.join(base, link))));
|
|
2133
|
+
const boundary = filePath.match(/^(raw\/agentic\/code\/(?:frameworks|addons|extensions)\/[^/]+)\//)?.[1];
|
|
2134
|
+
return [...release.descriptors.values()].filter(descriptor => {
|
|
2135
|
+
const relative = path.posix.relative(base, descriptor.path);
|
|
2136
|
+
const category = /(?:^|\/)(?:templates|scripts|references|resources|assets)\//.test(relative);
|
|
2137
|
+
const inside = relative !== '..' && !relative.startsWith('../');
|
|
2138
|
+
return (inside && category || linked.has(descriptor.path) && (inside || category))
|
|
2139
|
+
&& (!boundary || descriptor.path.startsWith(`${boundary}/`));
|
|
2140
|
+
}).map(descriptor => ({ path: path.posix.relative(base, descriptor.path), bytes: descriptor.size }))
|
|
2141
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
2142
|
+
}
|
|
2143
|
+
const base = path.dirname(filePath);
|
|
2144
|
+
const normalizedBase = normalizeIndexedPath(base);
|
|
2145
|
+
const boundary = normalizedBase.match(/^(.*\/(?:frameworks|addons|extensions)\/[^/]+)(?:\/|$)/)?.[1]
|
|
2146
|
+
?? normalizedBase.match(/^(.*\/\.aiwg)(?:\/|$)/)?.[1] ?? base;
|
|
2147
|
+
const resources = new Map();
|
|
2148
|
+
const walk = (relative) => {
|
|
2149
|
+
const absolute = path.resolve(base, relative);
|
|
2150
|
+
const contained = path.relative(boundary, absolute);
|
|
2151
|
+
if (contained === '..' || contained.startsWith(`..${path.sep}`) || path.isAbsolute(contained))
|
|
2152
|
+
return;
|
|
2153
|
+
let cursor = boundary;
|
|
2154
|
+
let stat;
|
|
2155
|
+
try {
|
|
2156
|
+
for (const component of contained.split(path.sep)) {
|
|
2157
|
+
cursor = path.join(cursor, component);
|
|
2158
|
+
stat = fs.lstatSync(cursor);
|
|
2159
|
+
if (stat.isSymbolicLink())
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
catch {
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
if (stat?.isDirectory()) {
|
|
2167
|
+
for (const name of fs.readdirSync(absolute).sort())
|
|
2168
|
+
walk(path.join(relative, name));
|
|
2169
|
+
}
|
|
2170
|
+
else if (stat?.isFile() && absolute !== filePath) {
|
|
2171
|
+
const key = normalizeIndexedPath(path.relative(base, absolute));
|
|
2172
|
+
resources.set(key, { path: key, bytes: stat.size });
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
for (const directory of ['templates', 'scripts', 'references', 'resources', 'assets'])
|
|
2176
|
+
walk(directory);
|
|
2177
|
+
const { data } = parseFrontmatter(source);
|
|
2178
|
+
const declared = Array.isArray(data.resources) ? data.resources.filter((value) => typeof value === 'string') : [];
|
|
2179
|
+
for (const link of [...extractMarkdownLinks(source), ...declared]) {
|
|
2180
|
+
if (path.isAbsolute(link) || /^[a-z]+:/i.test(link))
|
|
2181
|
+
continue;
|
|
2182
|
+
const relative = path.relative(base, path.resolve(base, link));
|
|
2183
|
+
// Outside the artifact directory, only declared companion categories qualify.
|
|
2184
|
+
if ((relative === '..' || relative.startsWith(`..${path.sep}`)) && !/(?:^|\/)(?:templates|scripts|references|resources|assets)\//.test(normalizeIndexedPath(relative)))
|
|
2185
|
+
continue;
|
|
2186
|
+
walk(relative);
|
|
2187
|
+
}
|
|
2188
|
+
return [...resources.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
2189
|
+
}
|
|
2190
|
+
async function emitShowCard(cwd, aiwgRoot, entry, filePath, source, params, bindings, extra, local, release) {
|
|
2191
|
+
const { data } = parseFrontmatter(source);
|
|
2192
|
+
const flow = entry.type === 'flow' || entry.type === 'runbook' ? parseFlowDoc(source, entry.path) : null;
|
|
2193
|
+
const sections = showSections(source);
|
|
2194
|
+
const selected = (params.sections ?? []).map(heading => {
|
|
2195
|
+
const exact = sections.filter(section => section.heading === heading);
|
|
2196
|
+
const matches = exact.length ? exact : sections.filter(section => section.heading.toLowerCase() === heading.toLowerCase());
|
|
2197
|
+
if (matches.length !== 1)
|
|
2198
|
+
throw new Error(`${matches.length ? 'Ambiguous' : 'Unknown'} heading "${heading}". Headings: ${sections.map(section => section.heading).join(', ') || '(none)'}`);
|
|
2199
|
+
return matches[0].text;
|
|
2200
|
+
});
|
|
2201
|
+
const owner = normalizeIndexedPath(entry.path).match(/(?:^|\/)(?:agentic\/code|\.aiwg)\/(frameworks|addons|extensions)\/([^/]+)\//);
|
|
2202
|
+
const kind = owner ? { frameworks: 'framework', addons: 'addon', extensions: 'extension' }[owner[1]] : null;
|
|
2203
|
+
const installed = local && (owner && aiwgRoot
|
|
2204
|
+
? await isCorpusBundleInstalled(cwd, aiwgRoot, kind, owner[2]) : true);
|
|
2205
|
+
const resources = local || release ? showResources(filePath, source, release) : [];
|
|
2206
|
+
const resource = params.resource ? resources.find(resource => resource.path === params.resource) : undefined;
|
|
2207
|
+
if (params.resource && !resource)
|
|
2208
|
+
throw new Error(`Unknown resource "${params.resource}". Resources: ${resources.map(resource => resource.path).join(', ') || '(none)'}`);
|
|
2209
|
+
const quote = (value) => '"' + value.replace(/["\\$`]/g, '\\$&') + '"';
|
|
2210
|
+
const command = `aiwg show ${entry.type} ${extra.hint ? params.name : discoveryIdForEntry(entry)}`
|
|
2211
|
+
+ (params.graph ? ` --graph ${quote(params.graph)}` : '')
|
|
2212
|
+
+ (params.backend ? ` --backend ${params.backend}` : '')
|
|
2213
|
+
+ (params.resourceSource ? ` --resource-source ${params.resourceSource}` : '')
|
|
2214
|
+
+ (params.aiwgVersion ? ` --aiwg-version ${quote(params.aiwgVersion)}` : '')
|
|
2215
|
+
+ (params.offline ? ' --offline' : '');
|
|
2216
|
+
let resourceBody;
|
|
2217
|
+
if (resource) {
|
|
2218
|
+
if (local)
|
|
2219
|
+
resourceBody = fs.readFileSync(path.join(path.dirname(filePath), resource.path), 'utf8');
|
|
2220
|
+
else if (release) {
|
|
2221
|
+
const { fetchVerifiedRawResource } = await import('../resources/web-release.js');
|
|
2222
|
+
resourceBody = (await fetchVerifiedRawResource(release, path.posix.normalize(path.posix.join(path.posix.dirname(filePath), resource.path)), { ...params.webReleaseOptions, offline: params.offline })).toString('utf8');
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
const localPin = extra.local_pin;
|
|
2226
|
+
const pinNote = localPin ? `Pinned locally: kernel (canonical ${localPin.canonical_activation}; ${localPin.matches.map(match => match.source).join(', ')}).` : '';
|
|
2227
|
+
const contentNotice = 'This is a metadata card. The component body has not been fetched. Before deciding or applying its workflow, read the needed sections or --body using the drill-in commands below. Read full governing rules before governed work; --rules alone lists checks.';
|
|
2228
|
+
const card = {
|
|
2229
|
+
id: discoveryIdForEntry(entry), type: entry.type, name: entry.name ?? String(data.name ?? entry.title),
|
|
2230
|
+
title: entry.title, path: filePath, description: String(data.description ?? data.summary ?? flow?.description ?? entry.capability ?? entry.ruleSummary ?? entry.summary ?? ''),
|
|
2231
|
+
capability: owner ? { kind, name: owner[2] } : null, installed, kernel: entry.kernel ?? false,
|
|
2232
|
+
...(['rule', 'behavior'].includes(entry.type) ? {
|
|
2233
|
+
activation: localPin ? 'kernel' : entry.activation ?? data.activation ?? null, enforcement: data.enforcement ?? entry.ruleEnforcement ?? null,
|
|
2234
|
+
bound_to: data.bound_to ?? entry.bound_to ?? {},
|
|
2235
|
+
} : {}),
|
|
2236
|
+
bound_rules: [], rule_stats: emptyRuleStats(), required_rule_checks: [], rule_check_obligation: RULE_CHECK_OBLIGATION,
|
|
2237
|
+
...bindings.fields,
|
|
2238
|
+
outline: sections.map(({ text: _text, ...metadata }) => metadata), resources,
|
|
2239
|
+
...(!params.body && !selected.length ? { content_notice: contentNotice } : {}),
|
|
2240
|
+
drill_in: {
|
|
2241
|
+
sections: sections.map(section => `${command} --section ${quote(section.heading)}`),
|
|
2242
|
+
rules: `${command} --rules`, rule_bodies: `${command} --rules --with-rule-bodies`,
|
|
2243
|
+
resources: `${command} --resources`, resource: resources.map(resource => `${command} --resource ${quote(resource.path)}`),
|
|
2244
|
+
body: `${command} --body`,
|
|
2245
|
+
},
|
|
2246
|
+
...extra,
|
|
2247
|
+
...(params.body || selected.length ? { content: params.body ? source : selected.join('\n') } : {}),
|
|
2248
|
+
...(resource ? { resource: { ...resource, body: resourceBody } } : {}),
|
|
2249
|
+
};
|
|
2250
|
+
if (params.json) {
|
|
2251
|
+
console.log(JSON.stringify(card, null, 2));
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
if ('content' in card) {
|
|
2255
|
+
if (extra.hint)
|
|
2256
|
+
process.stdout.write(`<!-- AIWG: ${card.installed ? 'installed bundle artifact — not in the selected index.' : 'corpus-only artifact — not in the indexed workspace.'}\n ${extra.hint}\n Origin: ${filePath}\n-->\n\n`);
|
|
2257
|
+
if (entry.script)
|
|
2258
|
+
process.stdout.write(`<!-- AIWG: executable skill — run via: ${buildRunHint(entry)} -->\n`);
|
|
2259
|
+
if (pinNote)
|
|
2260
|
+
process.stdout.write(`${pinNote}\n`);
|
|
2261
|
+
process.stdout.write(card.content);
|
|
2262
|
+
if (!card.content.endsWith('\n'))
|
|
2263
|
+
process.stdout.write('\n');
|
|
2264
|
+
if (bindings.text)
|
|
2265
|
+
process.stdout.write(bindings.text);
|
|
2266
|
+
return;
|
|
2267
|
+
}
|
|
2268
|
+
const lines = [`# ${card.type}: ${card.name}`, `ID: ${card.id}`, card.description,
|
|
2269
|
+
`Owner: ${card.capability ? `${card.capability.kind}:${card.capability.name}` : 'project/unowned'} | installed: ${card.installed}`, statsText(card.rule_stats)];
|
|
2270
|
+
if (pinNote)
|
|
2271
|
+
lines.push(pinNote);
|
|
2272
|
+
if ('activation' in card)
|
|
2273
|
+
lines.push(`Activation: ${card.activation} | enforcement: ${card.enforcement}`, `Bound targets: ${JSON.stringify(card.bound_to)}`);
|
|
2274
|
+
lines.push(contentNotice, bindings.text || RULE_CHECK_OBLIGATION, '\n## Outline', ...card.outline.map(section => `- ${section.heading} (${section.bytes} bytes, ${section.lines} lines)`), '\n## Resources', ...resources.map(resource => `- ${resource.path} (${resource.bytes} bytes)`), '\n## Drill in', ...Object.values(card.drill_in).flat().map(command => `- ${command}`));
|
|
2275
|
+
if (card.resource)
|
|
2276
|
+
lines.push(`\n## Resource: ${card.resource.path}`, card.resource.body);
|
|
2277
|
+
console.log(lines.join('\n'));
|
|
2278
|
+
}
|
|
2053
2279
|
/**
|
|
2054
|
-
*
|
|
2055
|
-
* SKILL.md
|
|
2280
|
+
* Emit a metadata card, with opt-in body/section drill-ins for a
|
|
2281
|
+
* SKILL.md. Consumers don't need to know where AIWG stores skills —
|
|
2056
2282
|
* they pass the skill name and the CLI reads the file from the indexed
|
|
2057
2283
|
* location.
|
|
2058
2284
|
*
|
|
@@ -2074,7 +2300,7 @@ export async function showArtifact(cwd, params) {
|
|
|
2074
2300
|
? params.typeFilter
|
|
2075
2301
|
: [...OPERATIONAL_SHOW_TYPES];
|
|
2076
2302
|
const { entries, aiwgRoot, resourceSource, webRelease, relations, } = await loadShowEntries(cwd, params, true);
|
|
2077
|
-
if (entries.length === 0 && params.backend !== 'fortemi-core') {
|
|
2303
|
+
if (entries.length === 0 && !relations && params.backend !== 'fortemi-core') {
|
|
2078
2304
|
console.error('Error: No artifact index found.');
|
|
2079
2305
|
console.error('Run `aiwg index build --graph framework` (or `aiwg use <framework>`) first.');
|
|
2080
2306
|
process.exit(1);
|
|
@@ -2106,42 +2332,10 @@ export async function showArtifact(cwd, params) {
|
|
|
2106
2332
|
: corpusMatch.bundleKind && corpusMatch.bundleId
|
|
2107
2333
|
? `Run \`aiwg use ${corpusMatch.bundleId}\` to install this ${corpusMatch.bundleKind} into the workspace.`
|
|
2108
2334
|
: 'Run `aiwg index build --graph framework` to refresh the artifact index.';
|
|
2109
|
-
const
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
console.log(JSON.stringify({
|
|
2114
|
-
...bindings.fields,
|
|
2115
|
-
path: corpusMatch.path,
|
|
2116
|
-
type: corpusMatch.type,
|
|
2117
|
-
title: needle,
|
|
2118
|
-
kernel: false,
|
|
2119
|
-
uninstalled: !installed,
|
|
2120
|
-
...(localPin ? { local_pin: localPin } : {}),
|
|
2121
|
-
hint: installHint,
|
|
2122
|
-
content,
|
|
2123
|
-
}, null, 2));
|
|
2124
|
-
return;
|
|
2125
|
-
}
|
|
2126
|
-
// Plain text mode: distinguish a missing index entry from an
|
|
2127
|
-
// uninstalled bundle, then stream the body.
|
|
2128
|
-
const banner = [
|
|
2129
|
-
installed
|
|
2130
|
-
? '<!-- AIWG: installed bundle artifact — not in the selected index.'
|
|
2131
|
-
: '<!-- AIWG: corpus-only artifact — not in the indexed workspace.',
|
|
2132
|
-
` ${installHint}`,
|
|
2133
|
-
' Origin: ' + corpusMatch.path,
|
|
2134
|
-
'-->',
|
|
2135
|
-
'',
|
|
2136
|
-
].join('\n');
|
|
2137
|
-
process.stdout.write(banner);
|
|
2138
|
-
if (localPin)
|
|
2139
|
-
process.stdout.write(`Pinned locally: kernel (canonical ${localPin.canonical_activation}; ${localPin.matches.map(match => match.source).join(', ')}).\n`);
|
|
2140
|
-
process.stdout.write(content);
|
|
2141
|
-
if (!content.endsWith('\n'))
|
|
2142
|
-
process.stdout.write('\n');
|
|
2143
|
-
if (bindings.text)
|
|
2144
|
-
process.stdout.write(bindings.text);
|
|
2335
|
+
const fallbackEntry = { path: path.relative(aiwgRoot, corpusMatch.path), type: corpusMatch.type, name: needle, title: needle };
|
|
2336
|
+
const bindings = await showBindings(entries, fallbackEntry, content, params, rule => fs.readFile(resolveMetadataPath(cwd, aiwgRoot, rule), 'utf8'), relations);
|
|
2337
|
+
await emitShowCard(cwd, aiwgRoot, fallbackEntry, corpusMatch.path, content, params, bindings, { uninstalled: !installed, installed, hint: installHint,
|
|
2338
|
+
...(localPin ? { local_pin: localPin } : {}) }, true);
|
|
2145
2339
|
return;
|
|
2146
2340
|
}
|
|
2147
2341
|
}
|
|
@@ -2242,57 +2436,18 @@ export async function showArtifact(cwd, params) {
|
|
|
2242
2436
|
});
|
|
2243
2437
|
return resolved.bytes.toString('utf8');
|
|
2244
2438
|
}, relations);
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
: null
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
graph: entry.indexGraph ?? null,
|
|
2258
|
-
scope: entry.indexScope ?? null,
|
|
2259
|
-
...(webRelease
|
|
2260
|
-
? {
|
|
2261
|
-
resource_source: 'web',
|
|
2262
|
-
aiwg_selector: webRelease.selector,
|
|
2263
|
-
aiwg_version: webRelease.version,
|
|
2264
|
-
manifest_sha256: webRelease.manifestDigest,
|
|
2265
|
-
manifest_url: webRelease.manifestUrl,
|
|
2266
|
-
}
|
|
2267
|
-
: {}),
|
|
2268
|
-
},
|
|
2269
|
-
// #1227 — surface script-bearing skills so callers can route to
|
|
2270
|
-
// `aiwg run skill <name>` instead of treating SKILL.md as
|
|
2271
|
-
// instructions for the agent to execute itself.
|
|
2272
|
-
...(entry.script
|
|
2273
|
-
? { executable: true, run_hint: buildRunHint(entry) }
|
|
2274
|
-
: {}),
|
|
2275
|
-
...(providerModels ? { providerModels } : {}),
|
|
2276
|
-
...bindings.fields,
|
|
2277
|
-
...(localPin ? { local_pin: localPin } : {}),
|
|
2278
|
-
content,
|
|
2279
|
-
}, null, 2));
|
|
2280
|
-
return;
|
|
2281
|
-
}
|
|
2282
|
-
// Stream the authored content first; append binding guidance only when present.
|
|
2283
|
-
// #1227 — for script-bearing skills, prepend a one-line banner so
|
|
2284
|
-
// agents reading the body immediately see the skill is executable.
|
|
2285
|
-
if (entry.script) {
|
|
2286
|
-
const hint = buildRunHint(entry);
|
|
2287
|
-
process.stdout.write(`<!-- AIWG: executable skill — run via: ${hint} -->\n`);
|
|
2288
|
-
}
|
|
2289
|
-
if (localPin)
|
|
2290
|
-
process.stdout.write(`Pinned locally: kernel (canonical ${localPin.canonical_activation}; ${localPin.matches.map(match => match.source).join(', ')}).\n`);
|
|
2291
|
-
process.stdout.write(content);
|
|
2292
|
-
if (!content.endsWith('\n'))
|
|
2293
|
-
process.stdout.write('\n');
|
|
2294
|
-
if (bindings.text)
|
|
2295
|
-
process.stdout.write(bindings.text);
|
|
2439
|
+
const providerModels = resourceSource === 'local' ? await loadProviderModelMetadata(cwd, aiwgRoot) : null;
|
|
2440
|
+
await emitShowCard(cwd, aiwgRoot, entry, filePath, content, params, bindings, {
|
|
2441
|
+
provenance: {
|
|
2442
|
+
graph: entry.indexGraph ?? null,
|
|
2443
|
+
scope: entry.indexScope ?? null,
|
|
2444
|
+
...(webRelease ? { resource_source: 'web', aiwg_selector: webRelease.selector, aiwg_version: webRelease.version,
|
|
2445
|
+
manifest_sha256: webRelease.manifestDigest, manifest_url: webRelease.manifestUrl } : {}),
|
|
2446
|
+
},
|
|
2447
|
+
...(entry.script ? { executable: true, run_hint: buildRunHint(entry) } : {}),
|
|
2448
|
+
...(providerModels ? { providerModels } : {}),
|
|
2449
|
+
...(localPin ? { local_pin: localPin } : {}),
|
|
2450
|
+
}, resourceSource === 'local', webRelease);
|
|
2296
2451
|
}
|
|
2297
2452
|
export async function showMetadata(cwd, params) {
|
|
2298
2453
|
loadGlobalGraphConfigs();
|
|
@@ -152,11 +152,11 @@ export function buildRelations(entries) {
|
|
|
152
152
|
}
|
|
153
153
|
return index;
|
|
154
154
|
}
|
|
155
|
-
export async function writeRelations(entries, cwd, indexDir, force = false) {
|
|
155
|
+
export async function writeRelations(entries, cwd, indexDir, force = false, existingDir = indexDir) {
|
|
156
156
|
const values = Object.values(entries);
|
|
157
157
|
const fingerprint = relationsFingerprint(values);
|
|
158
158
|
try {
|
|
159
|
-
const existing = JSON.parse(fs.readFileSync(path.join(
|
|
159
|
+
const existing = JSON.parse(fs.readFileSync(path.join(existingDir, 'relations.json'), 'utf8'));
|
|
160
160
|
if (!force && existing.version === RELATIONS_VERSION && existing.$schema === RELATIONS_SCHEMA
|
|
161
161
|
&& existing.fingerprint.version === fingerprint.version && existing.fingerprint.sources === fingerprint.sources
|
|
162
162
|
&& existing.artifacts && existing.rules && existing.targets && existing.capabilities && existing.aliases?.targets && existing.aliases.rules)
|
|
@@ -80,7 +80,7 @@ function shouldSkipDir(dirName, fullPath) {
|
|
|
80
80
|
return true;
|
|
81
81
|
return fullPath.split(path.sep).includes('.aiwg') && fullPath.split(path.sep).includes('.index');
|
|
82
82
|
}
|
|
83
|
-
function collectSourceFiles(cwd) {
|
|
83
|
+
export function collectSourceFiles(cwd, includeAssets = false) {
|
|
84
84
|
const results = [];
|
|
85
85
|
const visit = (dir) => {
|
|
86
86
|
if (!fs.existsSync(dir))
|
|
@@ -91,7 +91,7 @@ function collectSourceFiles(cwd) {
|
|
|
91
91
|
if (!shouldSkipDir(entry.name, fullPath))
|
|
92
92
|
visit(fullPath);
|
|
93
93
|
}
|
|
94
|
-
else if (entry.isFile() && CODE_EXTENSIONS.has(path.extname(entry.name))) {
|
|
94
|
+
else if (entry.isFile() && (CODE_EXTENSIONS.has(path.extname(entry.name)) || (includeAssets && ASSET_EXTENSIONS.has(path.extname(entry.name))))) {
|
|
95
95
|
results.push(fullPath);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -110,7 +110,7 @@ export const INDEX_VERSION = '1.0.0';
|
|
|
110
110
|
* making the serialized index schema incompatible; a mismatch simply forces a
|
|
111
111
|
* one-time content re-extraction during the next incremental build.
|
|
112
112
|
*/
|
|
113
|
-
export const INDEX_EXTRACTOR_VERSION = '2026.10.
|
|
113
|
+
export const INDEX_EXTRACTOR_VERSION = '2026.10.24.1';
|
|
114
114
|
/**
|
|
115
115
|
* Built-in graph definitions
|
|
116
116
|
*/
|
|
@@ -672,6 +672,14 @@ export function loadGlobalGraphConfigs(diagnostics) {
|
|
|
672
672
|
}
|
|
673
673
|
/** Module-scoped guard so the config.yaml deprecation note prints at most once per process. */
|
|
674
674
|
let yamlIndexDeprecationWarned = false;
|
|
675
|
+
/** Reload one workspace's registrations, including removals between daemon runs. */
|
|
676
|
+
export function loadWorkspaceGraphs(workspace, diagnostics) {
|
|
677
|
+
for (const name of Object.keys(GRAPH_CONFIGS))
|
|
678
|
+
delete GRAPH_CONFIGS[name];
|
|
679
|
+
Object.assign(GRAPH_CONFIGS, structuredClone(BUILTIN_GRAPH_CONFIGS));
|
|
680
|
+
loadUserGraphConfigs(workspace, diagnostics);
|
|
681
|
+
loadGlobalGraphConfigs(diagnostics);
|
|
682
|
+
}
|
|
675
683
|
/**
|
|
676
684
|
* Get the index output directory for a given graph type
|
|
677
685
|
*
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { decisionPricing } from '../../decision/pricing.js';
|
|
1
2
|
import { ATTRIBUTION_FIELD_PATTERN, DecisionAskUsageError, decisionCapabilities, DecisionLedgerSinceError, decisionLedgerHistory, decisionLedgerStats, evaluateRequestPath, listPatterns, livePlan, MAX_ATTRIBUTION_FIELD_LENGTH, materializeSyntheticClassificationSetup, parseDecisionLedgerSince, readDecisionContextFile, runDecisionOffloadCheckpoint, runOfflinePattern, setupJev, showPattern, syntheticClassificationSetup, validateDecisionInput, } from '../../decision/driver.js';
|
|
2
3
|
function jsonResult(value, exitCode = 0) {
|
|
3
4
|
return { exitCode, message: JSON.stringify(value, null, 2), rawOutput: true };
|
|
4
5
|
}
|
|
5
6
|
function usage() {
|
|
6
7
|
return [
|
|
7
|
-
'Usage: aiwg decision <capabilities|status|patterns|validate|evaluate|setup|history|stats> [options]',
|
|
8
|
+
'Usage: aiwg decision <capabilities|status|patterns|validate|evaluate|setup|history|stats|pricing> [options]',
|
|
8
9
|
' aiwg decision ask --question "<q>" (--yes-no | --choices a,b,c | --scale 1-5) [--context <text> | --context-file <path> | --context-stdin] [--threshold 0.8] [--timeout-ms 15000] [--passes N] [--escalation-passes N] [--provider <id>] [--session <id>] [--caller <tag>] [--record-text] [--json]',
|
|
9
10
|
'',
|
|
10
11
|
'Commands:',
|
|
@@ -19,6 +20,7 @@ function usage() {
|
|
|
19
20
|
' setup jev [--token-stdin] [--region <r>] [--endpoint <url>] [--verify] [--remove]',
|
|
20
21
|
' ask --question "<q>" (--yes-no | --choices a,b,c | --scale 1-5) [--provider <id>] [--session <id>] [--caller <tag>] [--record-text]',
|
|
21
22
|
' history [--since <date>] [--provider <id>] [--session <id>] [--kind yes-no|choices|scale] [--status answered|fallback] [--caller <tag>|<prefix>* | --no-caller] [--json]',
|
|
23
|
+
' pricing [--refresh --model jev-X.Y.Z] [--dry-run] [--offline | --allow-snapshot] [--json]',
|
|
22
24
|
' stats [--since <date>] [--provider <id>] [--session <id>] [--kind yes-no|choices|scale] [--status answered|fallback] [--caller <tag>|<prefix>* | --no-caller] [--group-by provider|session|kind|caller|passes|escalation] [--json]',
|
|
23
25
|
'',
|
|
24
26
|
'Notes:',
|
|
@@ -314,6 +316,7 @@ async function decisionStats(ctx) {
|
|
|
314
316
|
` jev usage: input=${group.jevUsage.inputTokens} output=${group.jevUsage.outputTokens}`,
|
|
315
317
|
` tokens per answered decision=${group.tokensPerAnsweredDecision?.toFixed(2) ?? '-'}`,
|
|
316
318
|
` llm_tokens_avoided=${group.llm_tokens_avoided} jev_tokens=${group.jev_tokens} cost_weighted_net_usd=${group.cost_weighted_net_usd.toFixed(6)}`,
|
|
319
|
+
...group.ratesUsed.map(rate => ` rates used (${rate.calls} calls): ${JSON.stringify(rate.method)} source=${rate.method.source ?? 'manual'} checked_at=${rate.method.checked_at ?? 'unknown'}`),
|
|
317
320
|
` v1 comparison only: estimated LLM tokens avoided=${group.estimatedLlmTokensAvoidedTotal} net savings=${group.netSavingsTokens}`,
|
|
318
321
|
...(group.legacyReestimatedCalls ? [` v1 records re-estimated=${group.legacyReestimatedCalls} weights=${JSON.stringify(result.legacyEstimationWeights)}`] : []),
|
|
319
322
|
]);
|
|
@@ -331,6 +334,21 @@ async function executeDecision(ctx) {
|
|
|
331
334
|
return await decisionAsk(ctx);
|
|
332
335
|
if (command === 'history')
|
|
333
336
|
return await decisionHistory(ctx);
|
|
337
|
+
if (command === 'pricing') {
|
|
338
|
+
const args = ctx.args.slice(1);
|
|
339
|
+
assertLedgerArgs(args, ['--model'], ['--refresh', '--dry-run', '--offline', '--allow-snapshot', '--json']);
|
|
340
|
+
const result = await decisionPricing({ cwd: ctx.cwd, refresh: args.includes('--refresh'), dryRun: args.includes('--dry-run'),
|
|
341
|
+
offline: args.includes('--offline'), allowSnapshot: args.includes('--allow-snapshot'), model: args.includes('--model') ? takeRequiredOption(args, '--model') : undefined });
|
|
342
|
+
if (args.includes('--json'))
|
|
343
|
+
return jsonResult(result);
|
|
344
|
+
return { exitCode: 0, rawOutput: true, message: [
|
|
345
|
+
`decision.ask.cost: ${JSON.stringify(result.current)}`,
|
|
346
|
+
`Source URL: ${result.sourceUrl}`,
|
|
347
|
+
...('snapshotDate' in result && result.snapshotDate ? [`Bundled snapshot dated ${result.snapshotDate}`] : []),
|
|
348
|
+
...('warning' in result && result.warning ? [`Warning: ${result.warning}`] : []),
|
|
349
|
+
...('diff' in result ? [`${result.written ? 'Written' : 'Dry run; config unchanged'}: ${JSON.stringify(result.diff)}`] : []),
|
|
350
|
+
].join('\n') };
|
|
351
|
+
}
|
|
334
352
|
if (command === 'stats')
|
|
335
353
|
return await decisionStats(ctx);
|
|
336
354
|
if (command === 'patterns') {
|