@aiwg/cli 2026.8.7 → 2026.8.10

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.
Files changed (76) hide show
  1. package/README.md +23 -8
  2. package/THIRD_PARTY_NOTICES.md +35 -0
  3. package/agentic/code/providers/capability-matrix.yaml +3 -3
  4. package/bin/aiwg.mjs +125 -0
  5. package/dist/src/artifacts/backends/graphology-backend.js +4 -3
  6. package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
  7. package/dist/src/artifacts/cli.js +2 -2
  8. package/dist/src/artifacts/corpus-tools/cli.js +27 -0
  9. package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
  10. package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
  11. package/dist/src/artifacts/discover-facets.js +2 -2
  12. package/dist/src/artifacts/embedding-index.js +9 -8
  13. package/dist/src/artifacts/graph-backend.js +2 -2
  14. package/dist/src/artifacts/move.js +40 -2
  15. package/dist/src/artifacts/query-engine.js +21 -7
  16. package/dist/src/artifacts/repair.js +47 -0
  17. package/dist/src/artifacts/types.js +3 -3
  18. package/dist/src/cli/command-log.js +2 -2
  19. package/dist/src/cli/handlers/artifacts.js +50 -1
  20. package/dist/src/cli/handlers/cost-report.js +71 -0
  21. package/dist/src/cli/handlers/evidence.js +78 -0
  22. package/dist/src/cli/handlers/help.js +9 -0
  23. package/dist/src/cli/handlers/index.js +8 -3
  24. package/dist/src/cli/handlers/local-executor.js +4 -3
  25. package/dist/src/cli/handlers/refresh.js +20 -8
  26. package/dist/src/cli/handlers/regenerate.js +22 -12
  27. package/dist/src/cli/handlers/serve.js +15 -36
  28. package/dist/src/cli/handlers/sessions.js +5 -4
  29. package/dist/src/cli/handlers/setup-manifest.js +8 -1
  30. package/dist/src/cli/handlers/subcommands.js +30 -0
  31. package/dist/src/cli/handlers/use.js +272 -89
  32. package/dist/src/cli/handlers/utilities.js +149 -0
  33. package/dist/src/cli/handlers/workspace.js +10 -0
  34. package/dist/src/cli/help-generator.js +2 -1
  35. package/dist/src/cli/regenerate-selector.js +94 -0
  36. package/dist/src/cli/router.js +4 -1
  37. package/dist/src/cli/services/deployment-verification.js +596 -0
  38. package/dist/src/cli/skill-usage.js +2 -2
  39. package/dist/src/cli/workflow-orchestrator.js +1 -1
  40. package/dist/src/cli/workspace-signals.js +2 -2
  41. package/dist/src/config/aiwg-config.js +54 -27
  42. package/dist/src/config/cli.js +3 -3
  43. package/dist/src/config/project-artifacts-health.js +2 -0
  44. package/dist/src/config/project-artifacts-health.mjs +123 -0
  45. package/dist/src/config/project-artifacts-runtime.mjs +16 -0
  46. package/dist/src/config/project-artifacts.js +2 -1
  47. package/dist/src/cost/fleet-report.js +329 -0
  48. package/dist/src/evidence/bundle.js +256 -0
  49. package/dist/src/extensions/commands/definitions.js +77 -25
  50. package/dist/src/extensions/deployment-registration.js +6 -4
  51. package/dist/src/extensions/project-local-doctor.js +10 -6
  52. package/dist/src/extensions/project-local-gitignore.js +8 -4
  53. package/dist/src/extensions/project-quickref.js +197 -10
  54. package/dist/src/features/catalog.js +26 -0
  55. package/dist/src/features/cli.js +1 -3
  56. package/dist/src/features/runtime.js +17 -1
  57. package/dist/src/issues/cli.js +91 -7
  58. package/dist/src/mcp/server.mjs +1 -1
  59. package/dist/src/ops/registry.js +2 -2
  60. package/dist/src/policy/authorization.js +2 -2
  61. package/dist/src/providers/capability-matrix.yaml +3 -3
  62. package/dist/src/providers/provider-definitions.js +7 -5
  63. package/dist/src/providers/provider-definitions.mjs +1 -1
  64. package/dist/src/serve/pty-bridge.js +2 -8
  65. package/dist/src/serve/screen-reader.js +3 -6
  66. package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
  67. package/dist/src/smiths/context-pipeline/finalization.js +18 -5
  68. package/dist/src/smiths/context-pipeline/generator.js +2 -2
  69. package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
  70. package/package.json +2 -1
  71. package/tools/agents/deploy-agents.mjs +10 -11
  72. package/tools/agents/providers/base.mjs +47 -5
  73. package/tools/agents/providers/openclaw.mjs +5 -2
  74. package/tools/agents/providers/windsurf.mjs +13 -24
  75. package/tools/plugin/package-plugins.mjs +53 -0
  76. package/tools/skills/deploy-skills-codex.mjs +21 -5
@@ -0,0 +1,256 @@
1
+ /** Portable, self-verifying local evidence bundles. @issue #2039 */
2
+ import { createHash } from 'node:crypto';
3
+ import { promises as fs } from 'node:fs';
4
+ import path from 'node:path';
5
+ const SCHEMA_VERSION = 'aiwg.evidence.bundle/v1';
6
+ const RESTRICTED_KEY = /(?:^|_)(?:content|terminal|prompt|environment|env|credential|secret|password|authorization|bearer|token|private_key|certificate|restricted_(?:url|uri|link))(?:$|_)/i;
7
+ const ALLOWED_POLICY_KEYS = new Set(['restricted_content_grants']);
8
+ const EVIDENCE_ROLES = new Set(['activity-export', 'report', 'source', 'eval-config', 'provenance']);
9
+ function sha256(value) {
10
+ return createHash('sha256').update(value).digest('hex');
11
+ }
12
+ function containsRestricted(value) {
13
+ if (Array.isArray(value))
14
+ return value.some(containsRestricted);
15
+ if (!value || typeof value !== 'object')
16
+ return false;
17
+ return Object.entries(value).some(([key, child]) => (!ALLOWED_POLICY_KEYS.has(key.toLowerCase()) && RESTRICTED_KEY.test(key)) || containsRestricted(child));
18
+ }
19
+ function numeric(value) {
20
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null;
21
+ }
22
+ function stringArray(value) {
23
+ return Array.isArray(value) && value.every(item => typeof item === 'string') ? [...value].sort() : null;
24
+ }
25
+ function activitySummary(value) {
26
+ const reasons = [];
27
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
28
+ throw new Error('activity export is not an object; refusing to copy unassessed activity evidence');
29
+ }
30
+ if (containsRestricted(value))
31
+ throw new Error('activity export contains prohibited credential or restricted-content fields');
32
+ const envelope = value;
33
+ const manifest = envelope.manifest ?? {};
34
+ const completeness = envelope.completeness ?? manifest.completeness ?? {};
35
+ const coverage = envelope.coverage ?? manifest.coverage ?? [];
36
+ const collectors = envelope.collectors ?? manifest.collectors ?? {};
37
+ const redaction = envelope.redaction ?? manifest.redaction ?? {};
38
+ const grants = envelope.restricted_content_grants ?? manifest.restricted_content_grants;
39
+ const summary = {
40
+ coverage_label: typeof envelope.coverage_label === 'string' ? envelope.coverage_label
41
+ : typeof manifest.coverage_label === 'string' ? manifest.coverage_label
42
+ : Array.isArray(coverage) && coverage.length ? coverage.map(String).sort().join(',') : null,
43
+ sequence_gaps: numeric(completeness.sequence_gaps ?? manifest.sequence_gaps),
44
+ durable_loss: typeof completeness.durable_loss === 'boolean' ? completeness.durable_loss
45
+ : typeof manifest.durable_loss === 'boolean' ? manifest.durable_loss : null,
46
+ dropped_events: numeric(completeness.dropped_events ?? manifest.dropped_events),
47
+ stale_collectors: stringArray(collectors.stale ?? manifest.stale_collectors),
48
+ clock_uncertainty: typeof manifest.clock_uncertainty === 'string' || typeof manifest.clock_uncertainty === 'number'
49
+ ? manifest.clock_uncertainty : null,
50
+ redaction_status: typeof redaction.status === 'string' ? redaction.status
51
+ : typeof manifest.redaction_status === 'string' ? manifest.redaction_status : null,
52
+ restricted_content_grants: stringArray(grants),
53
+ signature_key_id: typeof manifest.key_id === 'string' ? manifest.key_id : null,
54
+ signed_merkle_root: typeof manifest.merkle_root === 'string' ? manifest.merkle_root : null,
55
+ };
56
+ for (const [key, value] of Object.entries(summary)) {
57
+ if (value === null)
58
+ reasons.push(`activity evidence is missing ${key}`);
59
+ }
60
+ if ((summary.sequence_gaps ?? 0) > 0)
61
+ reasons.push(`activity evidence reports ${summary.sequence_gaps} sequence gap(s)`);
62
+ if (summary.durable_loss === true)
63
+ reasons.push('activity evidence reports durable loss');
64
+ if ((summary.dropped_events ?? 0) > 0)
65
+ reasons.push(`activity evidence reports ${summary.dropped_events} dropped event(s)`);
66
+ if (summary.stale_collectors?.length)
67
+ reasons.push(`activity evidence reports stale collectors: ${summary.stale_collectors.join(', ')}`);
68
+ if (summary.redaction_status && summary.redaction_status !== 'complete')
69
+ reasons.push(`activity evidence redaction status is ${summary.redaction_status}`);
70
+ return { summary, reasons };
71
+ }
72
+ function emptyActivity() {
73
+ return {
74
+ coverage_label: null,
75
+ sequence_gaps: null,
76
+ durable_loss: null,
77
+ dropped_events: null,
78
+ stale_collectors: null,
79
+ clock_uncertainty: null,
80
+ redaction_status: null,
81
+ restricted_content_grants: null,
82
+ signature_key_id: null,
83
+ signed_merkle_root: null,
84
+ };
85
+ }
86
+ function checkpoint(members) {
87
+ let chain = sha256(`${SCHEMA_VERSION}\n`);
88
+ for (const member of [...members].sort((a, b) => a.path.localeCompare(b.path))) {
89
+ chain = sha256(`${chain}\n${member.role}\n${member.path}\n${member.sha256}\n`);
90
+ }
91
+ return chain;
92
+ }
93
+ function safeName(file, used) {
94
+ const parsed = path.parse(file);
95
+ const base = parsed.name.replace(/[^A-Za-z0-9._-]/g, '-') || 'member';
96
+ const extension = parsed.ext.replace(/[^A-Za-z0-9.]/g, '');
97
+ let candidate = `${base}${extension}`;
98
+ let suffix = 2;
99
+ while (used.has(candidate))
100
+ candidate = `${base}-${suffix++}${extension}`;
101
+ used.add(candidate);
102
+ return candidate;
103
+ }
104
+ export async function createEvidenceBundle(options) {
105
+ const output = path.resolve(options.output);
106
+ const membersDirectory = path.join(output, 'members');
107
+ const members = [];
108
+ const used = new Set();
109
+ const incompleteReasons = [];
110
+ let activity = emptyActivity();
111
+ const prepared = [];
112
+ for (const input of options.inputs) {
113
+ const source = path.resolve(input.file);
114
+ let content;
115
+ try {
116
+ content = await fs.readFile(source);
117
+ }
118
+ catch (error) {
119
+ incompleteReasons.push(`${input.role} member unavailable: ${path.basename(source)} (${error.code ?? 'read-error'})`);
120
+ continue;
121
+ }
122
+ if (input.role === 'activity-export') {
123
+ let parsed;
124
+ try {
125
+ parsed = JSON.parse(content.toString('utf8'));
126
+ }
127
+ catch {
128
+ throw new Error('activity export is not valid JSON; refusing to copy unassessed activity evidence');
129
+ }
130
+ const assessed = activitySummary(parsed);
131
+ activity = assessed.summary;
132
+ incompleteReasons.push(...assessed.reasons);
133
+ }
134
+ prepared.push({ input, source, content });
135
+ }
136
+ await fs.mkdir(path.dirname(output), { recursive: true });
137
+ await fs.mkdir(output);
138
+ await fs.mkdir(membersDirectory);
139
+ for (const { input, source, content } of prepared) {
140
+ const name = safeName(source, used);
141
+ const relative = path.posix.join('members', name);
142
+ await fs.writeFile(path.join(output, relative), content, { flag: 'wx' });
143
+ members.push({ path: relative, role: input.role, source_name: path.basename(source), sha256: sha256(content), bytes: content.length });
144
+ }
145
+ const roles = new Set(members.map(member => member.role));
146
+ for (const role of ['activity-export', 'report', 'source', 'eval-config', 'provenance']) {
147
+ if (!roles.has(role))
148
+ incompleteReasons.push(`bundle has no ${role} member`);
149
+ }
150
+ if (!Object.keys(options.modelVersions ?? {}).length)
151
+ incompleteReasons.push('model versions are missing');
152
+ if (!Object.keys(options.toolVersions ?? {}).length)
153
+ incompleteReasons.push('tool versions are missing');
154
+ const notRunReason = options.notRunReason?.trim() || null;
155
+ const status = (options.checkOnly && notRunReason)
156
+ ? 'not-run' : incompleteReasons.length ? 'incomplete' : 'complete';
157
+ const manifest = {
158
+ schema_version: SCHEMA_VERSION,
159
+ status,
160
+ incomplete_reasons: [...new Set(incompleteReasons)].sort(),
161
+ not_run_reason: status === 'not-run' ? notRunReason : null,
162
+ created_at: (options.now ?? new Date()).toISOString(),
163
+ model_versions: Object.fromEntries(Object.entries(options.modelVersions ?? {}).sort()),
164
+ tool_versions: Object.fromEntries(Object.entries(options.toolVersions ?? {}).sort()),
165
+ activity,
166
+ members: members.sort((a, b) => a.path.localeCompare(b.path)),
167
+ verifier: {
168
+ algorithm: 'sha256',
169
+ checkpoint: 'sorted-member-hash-chain',
170
+ root: checkpoint(members),
171
+ command: 'aiwg evidence verify <bundle>',
172
+ },
173
+ };
174
+ await fs.writeFile(path.join(output, 'evidence-manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`, { flag: 'wx' });
175
+ return manifest;
176
+ }
177
+ export async function verifyEvidenceBundle(bundle, expectedRoot) {
178
+ const root = path.resolve(bundle);
179
+ const errors = [];
180
+ const warnings = [];
181
+ let manifest;
182
+ try {
183
+ manifest = JSON.parse(await fs.readFile(path.join(root, 'evidence-manifest.json'), 'utf8'));
184
+ }
185
+ catch {
186
+ return { valid: false, status: 'invalid', errors: ['manifest is missing or invalid JSON'], warnings, root: null };
187
+ }
188
+ if (manifest.schema_version !== SCHEMA_VERSION)
189
+ errors.push(`unsupported schema_version ${String(manifest.schema_version)}`);
190
+ if (!['complete', 'incomplete', 'not-run'].includes(manifest.status))
191
+ errors.push(`unsupported status ${String(manifest.status)}`);
192
+ if (!Array.isArray(manifest.members)) {
193
+ errors.push('manifest members must be an array');
194
+ manifest.members = [];
195
+ }
196
+ const declaredPaths = new Set();
197
+ for (const member of manifest.members) {
198
+ if (!member
199
+ || typeof member.path !== 'string'
200
+ || !/^members\/[A-Za-z0-9._-]+$/.test(member.path)
201
+ || !EVIDENCE_ROLES.has(member.role)
202
+ || !/^[0-9a-f]{64}$/.test(member.sha256)
203
+ || !Number.isInteger(member.bytes)
204
+ || member.bytes < 0) {
205
+ errors.push('manifest contains a malformed member');
206
+ continue;
207
+ }
208
+ if (declaredPaths.has(member.path))
209
+ errors.push(`member is declared more than once: ${member.path}`);
210
+ declaredPaths.add(member.path);
211
+ const target = path.resolve(root, member.path);
212
+ if (!target.startsWith(`${root}${path.sep}`)) {
213
+ errors.push(`member escapes bundle root: ${member.path}`);
214
+ continue;
215
+ }
216
+ try {
217
+ const metadata = await fs.lstat(target);
218
+ if (!metadata.isFile() || metadata.isSymbolicLink()) {
219
+ errors.push(`member is not a regular file: ${member.path}`);
220
+ continue;
221
+ }
222
+ const content = await fs.readFile(target);
223
+ if (content.length !== member.bytes)
224
+ errors.push(`member size changed: ${member.path}`);
225
+ if (sha256(content) !== member.sha256)
226
+ errors.push(`member hash changed: ${member.path}`);
227
+ }
228
+ catch {
229
+ errors.push(`member is missing: ${member.path}`);
230
+ }
231
+ }
232
+ try {
233
+ const actualNames = await fs.readdir(path.join(root, 'members'));
234
+ for (const name of actualNames) {
235
+ const relative = path.posix.join('members', name);
236
+ if (!declaredPaths.has(relative))
237
+ errors.push(`bundle contains an undeclared member: ${relative}`);
238
+ }
239
+ }
240
+ catch {
241
+ if (manifest.members.length)
242
+ errors.push('bundle members directory is missing');
243
+ }
244
+ const checkpointMembers = manifest.members.filter(member => member && typeof member.path === 'string' && typeof member.role === 'string' && typeof member.sha256 === 'string');
245
+ const computed = checkpoint(checkpointMembers);
246
+ if (computed !== manifest.verifier?.root)
247
+ errors.push('bundle checkpoint does not match manifest members');
248
+ if (expectedRoot && computed !== expectedRoot)
249
+ errors.push('bundle checkpoint does not match expected root');
250
+ if (manifest.status === 'incomplete')
251
+ warnings.push(...(manifest.incomplete_reasons ?? []));
252
+ if (manifest.status === 'not-run')
253
+ warnings.push(`NOT RUN: ${manifest.not_run_reason ?? 'reason unavailable'}`);
254
+ return { valid: errors.length === 0, status: errors.length ? 'invalid' : manifest.status, errors, warnings, root: computed };
255
+ }
256
+ //# sourceMappingURL=bundle.js.map
@@ -104,6 +104,27 @@ export const doctorCommand = {
104
104
  metadata: {
105
105
  type: 'skill',
106
106
  triggerPhrases: ['doctor', 'check health', 'diagnose', 'troubleshoot installation'],
107
+ commandHint: {
108
+ template: 'utility',
109
+ argumentHint: '[--deployment] [--provider <p>] [--bundle <id>] [--scope project|user] [--json]',
110
+ allowedTools: ['Read', 'Bash'],
111
+ },
112
+ },
113
+ };
114
+ export const contextFirewallCommand = {
115
+ id: 'context-firewall',
116
+ type: 'skill',
117
+ name: 'Context Firewall',
118
+ description: 'Audit provider context and manage its reviewed digest baseline',
119
+ version: '1.0.0',
120
+ capabilities: ['cli', 'diagnostics', 'security', 'context-budget', 'memory-review'],
121
+ keywords: ['context', 'memory', 'firewall', 'baseline', 'poisoning', 'budget', 'trust'],
122
+ category: 'maintenance',
123
+ platforms: { claude: 'full', generic: 'full' },
124
+ deployment: { pathTemplate: '.{platform}/commands/{id}.md', core: true },
125
+ metadata: {
126
+ type: 'skill',
127
+ triggerPhrases: ['audit context', 'context firewall', 'review memory baseline', 'context budget'],
107
128
  commandHint: {
108
129
  template: 'utility',
109
130
  allowedTools: ['Read', 'Bash'],
@@ -274,14 +295,15 @@ export const useCommand = {
274
295
  triggerPhrases: ['use framework', 'deploy framework', 'install framework', 'use sdlc', 'use addon'],
275
296
  commandHint: {
276
297
  template: 'orchestration',
277
- argumentHint: '<framework|addon> [--provider <p>] [--prefix <dir>] [--profile <name>] [--harness-agents <list>]',
298
+ argumentHint: '<framework|addon> [--provider <p>] [--scope project|user] [--dry-run] [--json]',
278
299
  allowedTools: ['Read', 'Write', 'Bash', 'Glob'],
279
300
  executionSteps: [
280
301
  'Validate framework name',
281
302
  'Check dependencies',
282
303
  'Deploy framework files',
283
- 'Register in framework registry',
284
- 'Deploy platform-specific adaptations',
304
+ 'Register installed state and deploy platform-specific adaptations',
305
+ 'Refresh the applicable capability index and canonical context',
306
+ 'Verify required invariants and report readiness or repair findings',
285
307
  ],
286
308
  },
287
309
  },
@@ -729,10 +751,10 @@ export const issueCommand = {
729
751
  id: 'issue',
730
752
  type: 'skill',
731
753
  name: 'Issue',
732
- description: 'Manage local project issues under .aiwg/issues/',
754
+ description: 'Policy-plan and manage local project issues under .aiwg/issues/',
733
755
  version: '1.0.0',
734
756
  capabilities: ['cli', 'project', 'issues', 'local-provider'],
735
- keywords: ['issue', 'issues', 'local', 'tracker', 'tickets'],
757
+ keywords: ['issue', 'issues', 'local', 'tracker', 'tickets', 'policy', 'segmentation'],
736
758
  category: 'project',
737
759
  platforms: {
738
760
  claude: 'full',
@@ -744,10 +766,10 @@ export const issueCommand = {
744
766
  },
745
767
  metadata: {
746
768
  type: 'skill',
747
- triggerPhrases: ['local issues', 'issue init', 'issue list', 'issue create'],
769
+ triggerPhrases: ['local issues', 'issue init', 'issue list', 'issue create', 'plan issue draft'],
748
770
  commandHint: {
749
771
  template: 'utility',
750
- argumentHint: '<init|new|list|show|comment|close|index> [--provider local]',
772
+ argumentHint: '<init|plan|new|list|show|comment|close|index> [--provider local]',
751
773
  allowedTools: ['Read', 'Write', 'Bash'],
752
774
  },
753
775
  },
@@ -1325,8 +1347,8 @@ export const artifactsCommand = {
1325
1347
  name: 'Project Artifacts',
1326
1348
  description: 'Move or inspect the configured project AIWG artifact root',
1327
1349
  version: '1.0.0',
1328
- capabilities: ['cli', 'artifacts', 'relocation', 'configuration', 'index'],
1329
- keywords: ['artifacts', 'aiwg artifacts', 'move .aiwg', 'relocate .aiwg', 'rename .aiwg', 'artifact root', 'AIWG_ARTIFACTS_PATH'],
1350
+ capabilities: ['cli', 'artifacts', 'relocation', 'repair', 'configuration', 'index'],
1351
+ keywords: ['artifacts', 'aiwg artifacts', 'move .aiwg', 'relocate .aiwg', 'rename .aiwg', 'repair artifact root', 'external corpus', 'split root', 'artifact root', 'AIWG_ARTIFACTS_PATH'],
1330
1352
  category: 'index',
1331
1353
  platforms: {
1332
1354
  claude: 'full',
@@ -1338,10 +1360,10 @@ export const artifactsCommand = {
1338
1360
  },
1339
1361
  metadata: {
1340
1362
  type: 'skill',
1341
- triggerPhrases: ['move .aiwg', 'relocate .aiwg', 'rename .aiwg', 'move aiwg artifacts', 'artifact root'],
1363
+ triggerPhrases: ['move .aiwg', 'relocate .aiwg', 'rename .aiwg', 'move aiwg artifacts', 'repair external corpus', 'externalize corpus', 'artifact root'],
1342
1364
  commandHint: {
1343
1365
  template: 'utility',
1344
- argumentHint: 'move --to <path> [--from <path>] [--dry-run]',
1366
+ argumentHint: 'move|attach|repair [--to <path>] [--dry-run|--apply]',
1345
1367
  allowedTools: ['Read', 'Write', 'Bash'],
1346
1368
  },
1347
1369
  },
@@ -1351,10 +1373,10 @@ export const corpusCommand = {
1351
1373
  id: 'corpus',
1352
1374
  type: 'skill',
1353
1375
  name: 'Research Corpus Tools',
1354
- description: 'Research-corpus tools (radar-init, radar-status, radar-report)',
1355
- version: '1.0.0',
1356
- capabilities: ['cli', 'research', 'corpus', 'radar', 'freshness', 'profiles', 'discovery'],
1357
- keywords: ['corpus', 'radar', 'freshness', 'staleness', 'radar-init', 'radar-status', 'radar-report', 'profile-status', 'profile-generate', 'profile-metrics', 'profile-temporal', 'profile-communities', 'h-index', 'PageRank', 'CD-index', 'centrality', 'community detection', 'hot streak', 'funder-network', 'funder analytics', 'co-funding', 'novelty bias', 'curator-status', 'curator-init', 'discovery-log', 'PROF-S', 'source tracking', 'discovery provenance', 'stale profiles', 'hub authors', 'refresh cadence', 'GRADE trajectory'],
1376
+ description: 'Research-corpus tools for freshness, profiles, provenance, and retrieval benchmarks',
1377
+ version: '1.1.0',
1378
+ capabilities: ['cli', 'research', 'corpus', 'radar', 'freshness', 'profiles', 'discovery', 'retrieval-benchmark'],
1379
+ keywords: ['corpus', 'radar', 'freshness', 'staleness', 'radar-init', 'radar-status', 'radar-report', 'profile-status', 'profile-generate', 'profile-metrics', 'profile-temporal', 'profile-communities', 'h-index', 'PageRank', 'CD-index', 'centrality', 'community detection', 'hot streak', 'funder-network', 'funder analytics', 'co-funding', 'novelty bias', 'curator-status', 'curator-init', 'discovery-log', 'PROF-S', 'source tracking', 'discovery provenance', 'stale profiles', 'hub authors', 'refresh cadence', 'GRADE trajectory', 'retrieval-lab', 'BM25', 'vector retrieval', 'concept graph', 'PPR', 'RRF', 'source selection'],
1358
1380
  category: 'index',
1359
1381
  platforms: {
1360
1382
  claude: 'full',
@@ -1366,10 +1388,10 @@ export const corpusCommand = {
1366
1388
  },
1367
1389
  metadata: {
1368
1390
  type: 'skill',
1369
- triggerPhrases: ['radar', 'radar status', 'radar report', 'scaffold radar', 'stale radars', 'corpus freshness', 'stale profiles', 'profile status', 'curator yield', 'discovery source', 'log discovery'],
1391
+ triggerPhrases: ['radar', 'radar status', 'radar report', 'scaffold radar', 'stale radars', 'corpus freshness', 'stale profiles', 'profile status', 'curator yield', 'discovery source', 'log discovery', 'benchmark corpus retrieval', 'hybrid retrieval lab'],
1370
1392
  commandHint: {
1371
1393
  template: 'utility',
1372
- argumentHint: '<radar-*|profile-*|curator-*|discovery-log|funder-network> [options]',
1394
+ argumentHint: '<radar-*|profile-*|curator-*|discovery-log|funder-network|retrieval-lab> [options]',
1373
1395
  allowedTools: ['Read', 'Glob', 'Grep', 'Write'],
1374
1396
  },
1375
1397
  },
@@ -1522,7 +1544,7 @@ export const featuresCommand = {
1522
1544
  description: 'List, inspect, and (eventually) install AIWG\'s optional runtime features',
1523
1545
  version: '1.0.0',
1524
1546
  capabilities: ['cli', 'maintenance', 'install', 'optional-deps'],
1525
- keywords: ['features', 'optional', 'install', 'embeddings', 'sqlite', 'pty', 'webserver'],
1547
+ keywords: ['features', 'optional', 'install', 'embeddings', 'sqlite', 'pty', 'webserver', 'graph', 'terminal'],
1526
1548
  category: 'maintenance',
1527
1549
  platforms: {
1528
1550
  claude: 'full',
@@ -2428,14 +2450,42 @@ export const teamCommand = {
2428
2450
  },
2429
2451
  };
2430
2452
  // Cost & Metrics Commands
2453
+ export const evidenceCommand = {
2454
+ id: 'evidence',
2455
+ type: 'skill',
2456
+ name: 'Evidence',
2457
+ description: 'Export and verify portable evaluation evidence bundles',
2458
+ version: '1.0.0',
2459
+ capabilities: ['cli', 'evidence', 'provenance', 'verification', 'evaluation'],
2460
+ keywords: ['evidence', 'bundle', 'provenance', 'verify', 'evaluation', 'activity-export'],
2461
+ category: 'metrics',
2462
+ platforms: {
2463
+ claude: 'full',
2464
+ generic: 'full',
2465
+ },
2466
+ deployment: {
2467
+ pathTemplate: '.{platform}/commands/{id}.md',
2468
+ core: false,
2469
+ },
2470
+ metadata: {
2471
+ type: 'skill',
2472
+ triggerPhrases: ['evidence bundle', 'export evidence', 'verify evidence', 'evaluation provenance'],
2473
+ commandHint: {
2474
+ template: 'utility',
2475
+ allowedTools: ['Read', 'Bash'],
2476
+ argumentHint: '<export|verify> [options]',
2477
+ cliDisabled: false,
2478
+ },
2479
+ },
2480
+ };
2431
2481
  export const costReportCommand = {
2432
2482
  id: 'cost-report',
2433
2483
  type: 'skill',
2434
2484
  name: 'Cost Report',
2435
- description: 'Generate token cost and spending report for workflows',
2436
- version: '1.0.0',
2437
- capabilities: ['cli', 'metrics', 'cost-tracking', 'reporting'],
2438
- keywords: ['cost', 'report', 'tokens', 'spending', 'budget', 'metrics'],
2485
+ description: 'Generate session or OpenRouter fleet cost and spending reports',
2486
+ version: '1.1.0',
2487
+ capabilities: ['cli', 'metrics', 'cost-tracking', 'reporting', 'fleet', 'openrouter'],
2488
+ keywords: ['cost', 'report', 'tokens', 'spending', 'budget', 'metrics', 'fleet', 'openrouter'],
2439
2489
  category: 'metrics',
2440
2490
  platforms: {
2441
2491
  claude: 'full',
@@ -2447,11 +2497,11 @@ export const costReportCommand = {
2447
2497
  },
2448
2498
  metadata: {
2449
2499
  type: 'skill',
2450
- triggerPhrases: ['cost report', 'show costs', 'token spending', 'cost summary', 'budget report'],
2500
+ triggerPhrases: ['cost report', 'show costs', 'token spending', 'cost summary', 'budget report', 'fleet spend'],
2451
2501
  commandHint: {
2452
2502
  template: 'utility',
2453
2503
  allowedTools: ['Read', 'Bash'],
2454
- cliDisabled: true,
2504
+ cliDisabled: false,
2455
2505
  },
2456
2506
  },
2457
2507
  };
@@ -3481,6 +3531,7 @@ export const commandDefinitions = [
3481
3531
  versionCommand,
3482
3532
  authCommand,
3483
3533
  doctorCommand,
3534
+ contextFirewallCommand,
3484
3535
  updateCommand,
3485
3536
  refreshCommand,
3486
3537
  regenerateCommand,
@@ -3561,7 +3612,8 @@ export const commandDefinitions = [
3561
3612
  stewardCommand,
3562
3613
  // Agent Teams (1)
3563
3614
  teamCommand,
3564
- // Metrics (3)
3615
+ // Metrics (4)
3616
+ evidenceCommand,
3565
3617
  costReportCommand,
3566
3618
  costHistoryCommand,
3567
3619
  metricsTokensCommand,
@@ -425,14 +425,15 @@ export async function scanDeployedBehaviors(behaviorsPath, provider, cwd = proce
425
425
  * ```
426
426
  */
427
427
  export async function registerDeployedExtensions(registry, options) {
428
- const { agentsPath, skillsPath, behaviorsPath, provider, cwd } = options;
428
+ const { agentsPath, skillsPath, behaviorsPath, provider, cwd, quiet = false } = options;
429
429
  // Scan and register agents
430
430
  if (agentsPath) {
431
431
  const agents = await scanDeployedAgents(agentsPath, provider, cwd);
432
432
  for (const agent of agents) {
433
433
  registry.register(agent);
434
434
  }
435
- console.log(`Registered ${agents.length} agents from ${agentsPath}`);
435
+ if (!quiet)
436
+ console.log(`Registered ${agents.length} agents from ${agentsPath}`);
436
437
  }
437
438
  // Scan and register skills
438
439
  if (skillsPath) {
@@ -446,7 +447,8 @@ export async function registerDeployedExtensions(registry, options) {
446
447
  total += skills.length;
447
448
  perPath.push(`${skills.length} from ${pathToScan}`);
448
449
  }
449
- console.log(`Registered ${total} skills (${perPath.join(', ')})`);
450
+ if (!quiet)
451
+ console.log(`Registered ${total} skills (${perPath.join(', ')})`);
450
452
  }
451
453
  // Scan and register behaviors (#609)
452
454
  if (behaviorsPath) {
@@ -454,7 +456,7 @@ export async function registerDeployedExtensions(registry, options) {
454
456
  for (const behavior of behaviors) {
455
457
  registry.register(behavior);
456
458
  }
457
- if (behaviors.length > 0) {
459
+ if (!quiet && behaviors.length > 0) {
458
460
  console.log(`Registered ${behaviors.length} behaviors from ${behaviorsPath}`);
459
461
  }
460
462
  }
@@ -54,12 +54,15 @@ export async function buildProjectLocalDoctorSection(opts) {
54
54
  const quickrefAudit = await auditProjectQuickref(projectDir, config?.providers ?? []);
55
55
  const quickrefErrors = [...quickrefAudit.errors];
56
56
  if (quickrefAudit.exists) {
57
- const quickrefRelPath = projectRelativePathIfInside(projectDir, projectAiwgPath(projectDir, 'quickref.json'));
58
- const ignored = quickrefRelPath
59
- ? await checkBundleManifestIgnored(projectDir, quickrefRelPath)
60
- : null;
61
- if (ignored === true && quickrefRelPath) {
62
- quickrefErrors.push(`${quickrefRelPath} is ignored by git; canonical project quickref source must be committed`);
57
+ for (const name of ['quickref.json', 'quickref.config.json']) {
58
+ const sourcePath = projectAiwgPath(projectDir, name);
59
+ const quickrefRelPath = projectRelativePathIfInside(projectDir, sourcePath);
60
+ const ignored = quickrefRelPath
61
+ ? await checkBundleManifestIgnored(projectDir, quickrefRelPath)
62
+ : null;
63
+ if (ignored === true && quickrefRelPath) {
64
+ quickrefErrors.push(`${quickrefRelPath} is ignored by git; operator project quickref input must be committed`);
65
+ }
63
66
  }
64
67
  }
65
68
  // No project-local content → no section at all
@@ -236,6 +239,7 @@ export async function buildProjectLocalDoctorSection(opts) {
236
239
  }
237
240
  lines.push(' Project-local bundle source should be tracked. Add to .gitignore:');
238
241
  lines.push(' !.aiwg/quickref.json');
242
+ lines.push(' !.aiwg/quickref.config.json');
239
243
  lines.push(' !.aiwg/addons/');
240
244
  lines.push(' !.aiwg/extensions/');
241
245
  lines.push(' !.aiwg/frameworks/');
@@ -39,6 +39,7 @@ export const AIWG_GITIGNORE_BLOCK = [
39
39
  AIWG_GITIGNORE_SENTINEL,
40
40
  '!.aiwg/aiwg.config',
41
41
  '!.aiwg/quickref.json',
42
+ '!.aiwg/quickref.config.json',
42
43
  '!.aiwg/addons/',
43
44
  '!.aiwg/extensions/',
44
45
  '!.aiwg/frameworks/',
@@ -120,11 +121,14 @@ export async function appendAiwgSourceTrackBlock(projectDir) {
120
121
  // runs.
121
122
  if (report.hasManagedBlock) {
122
123
  const path = join(projectDir, '.gitignore');
123
- const existing = await readFile(path, 'utf8');
124
- if (!existing.split(/\r?\n/).some(line => line.trim() === '!.aiwg/quickref.json')) {
124
+ let existing = await readFile(path, 'utf8');
125
+ const required = ['!.aiwg/quickref.json', '!.aiwg/quickref.config.json'];
126
+ const missing = required.filter(negation => !existing.split(/\r?\n/).some(line => line.trim() === negation));
127
+ if (missing.length > 0) {
125
128
  const sep = existing.endsWith('\n') ? '' : '\n';
126
- await writeFile(path, `${existing}${sep}!.aiwg/quickref.json\n`, 'utf8');
127
- return { added: true, reason: 'updated managed block to track .aiwg/quickref.json' };
129
+ existing = `${existing}${sep}${missing.join('\n')}\n`;
130
+ await writeFile(path, existing, 'utf8');
131
+ return { added: true, reason: `updated managed block to track ${missing.join(', ')}` };
128
132
  }
129
133
  return { added: false, reason: 'block already present — no change' };
130
134
  }