@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.
- package/README.md +23 -8
- package/THIRD_PARTY_NOTICES.md +35 -0
- package/agentic/code/providers/capability-matrix.yaml +3 -3
- package/bin/aiwg.mjs +125 -0
- package/dist/src/artifacts/backends/graphology-backend.js +4 -3
- package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
- package/dist/src/artifacts/cli.js +2 -2
- package/dist/src/artifacts/corpus-tools/cli.js +27 -0
- package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
- package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
- package/dist/src/artifacts/discover-facets.js +2 -2
- package/dist/src/artifacts/embedding-index.js +9 -8
- package/dist/src/artifacts/graph-backend.js +2 -2
- package/dist/src/artifacts/move.js +40 -2
- package/dist/src/artifacts/query-engine.js +21 -7
- package/dist/src/artifacts/repair.js +47 -0
- package/dist/src/artifacts/types.js +3 -3
- package/dist/src/cli/command-log.js +2 -2
- package/dist/src/cli/handlers/artifacts.js +50 -1
- package/dist/src/cli/handlers/cost-report.js +71 -0
- package/dist/src/cli/handlers/evidence.js +78 -0
- package/dist/src/cli/handlers/help.js +9 -0
- package/dist/src/cli/handlers/index.js +8 -3
- package/dist/src/cli/handlers/local-executor.js +4 -3
- package/dist/src/cli/handlers/refresh.js +20 -8
- package/dist/src/cli/handlers/regenerate.js +22 -12
- package/dist/src/cli/handlers/serve.js +15 -36
- package/dist/src/cli/handlers/sessions.js +5 -4
- package/dist/src/cli/handlers/setup-manifest.js +8 -1
- package/dist/src/cli/handlers/subcommands.js +30 -0
- package/dist/src/cli/handlers/use.js +272 -89
- package/dist/src/cli/handlers/utilities.js +149 -0
- package/dist/src/cli/handlers/workspace.js +10 -0
- package/dist/src/cli/help-generator.js +2 -1
- package/dist/src/cli/regenerate-selector.js +94 -0
- package/dist/src/cli/router.js +4 -1
- package/dist/src/cli/services/deployment-verification.js +596 -0
- package/dist/src/cli/skill-usage.js +2 -2
- package/dist/src/cli/workflow-orchestrator.js +1 -1
- package/dist/src/cli/workspace-signals.js +2 -2
- package/dist/src/config/aiwg-config.js +54 -27
- package/dist/src/config/cli.js +3 -3
- package/dist/src/config/project-artifacts-health.js +2 -0
- package/dist/src/config/project-artifacts-health.mjs +123 -0
- package/dist/src/config/project-artifacts-runtime.mjs +16 -0
- package/dist/src/config/project-artifacts.js +2 -1
- package/dist/src/cost/fleet-report.js +329 -0
- package/dist/src/evidence/bundle.js +256 -0
- package/dist/src/extensions/commands/definitions.js +77 -25
- package/dist/src/extensions/deployment-registration.js +6 -4
- package/dist/src/extensions/project-local-doctor.js +10 -6
- package/dist/src/extensions/project-local-gitignore.js +8 -4
- package/dist/src/extensions/project-quickref.js +197 -10
- package/dist/src/features/catalog.js +26 -0
- package/dist/src/features/cli.js +1 -3
- package/dist/src/features/runtime.js +17 -1
- package/dist/src/issues/cli.js +91 -7
- package/dist/src/mcp/server.mjs +1 -1
- package/dist/src/ops/registry.js +2 -2
- package/dist/src/policy/authorization.js +2 -2
- package/dist/src/providers/capability-matrix.yaml +3 -3
- package/dist/src/providers/provider-definitions.js +7 -5
- package/dist/src/providers/provider-definitions.mjs +1 -1
- package/dist/src/serve/pty-bridge.js +2 -8
- package/dist/src/serve/screen-reader.js +3 -6
- package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
- package/dist/src/smiths/context-pipeline/finalization.js +18 -5
- package/dist/src/smiths/context-pipeline/generator.js +2 -2
- package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
- package/package.json +2 -1
- package/tools/agents/deploy-agents.mjs +10 -11
- package/tools/agents/providers/base.mjs +47 -5
- package/tools/agents/providers/openclaw.mjs +5 -2
- package/tools/agents/providers/windsurf.mjs +13 -24
- package/tools/plugin/package-plugins.mjs +53 -0
- 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>] [--
|
|
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
|
|
284
|
-
'
|
|
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: '
|
|
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>
|
|
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
|
|
1355
|
-
version: '1.
|
|
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
|
|
2436
|
-
version: '1.
|
|
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:
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
124
|
-
|
|
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
|
-
|
|
127
|
-
|
|
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
|
}
|