@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
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { createHash } from 'crypto';
|
|
9
9
|
import { access, mkdir, readFile, readdir, rm, writeFile } from 'fs/promises';
|
|
10
|
-
import { dirname, isAbsolute, join, resolve } from 'path';
|
|
10
|
+
import { basename, dirname, isAbsolute, join, resolve } from 'path';
|
|
11
11
|
import { homedir } from 'os';
|
|
12
12
|
import { z } from 'zod';
|
|
13
13
|
import { getProviderDefinition, normalizeProviderDefinitionId, } from '../providers/provider-definitions.js';
|
|
14
14
|
import { OPERATIONAL_SHOW_TYPES } from '../artifacts/types.js';
|
|
15
15
|
import { projectAiwgPath } from '../config/project-artifacts.js';
|
|
16
16
|
import { appendAiwgSourceTrackBlock } from './project-local-gitignore.js';
|
|
17
|
+
import { discoverProjectLocalBundles } from './project-local-discovery.js';
|
|
18
|
+
import { enumerateBundleArtifacts } from './shadow-resolver.js';
|
|
17
19
|
const OWNERSHIP_MARKER = '.aiwg-project-quickref.json';
|
|
18
20
|
const ShowHintSchema = z.object({
|
|
19
21
|
type: z.enum(OPERATIONAL_SHOW_TYPES),
|
|
@@ -35,6 +37,25 @@ export const ProjectQuickrefSchema = z.object({
|
|
|
35
37
|
precedence: z.string().min(1).max(1024),
|
|
36
38
|
entries: z.array(QuickrefEntrySchema).min(1).max(50),
|
|
37
39
|
}).strict();
|
|
40
|
+
const QuickrefOverrideSchema = z.object({
|
|
41
|
+
title: z.string().min(1).max(128).optional(),
|
|
42
|
+
summary: z.string().min(1).max(512).optional(),
|
|
43
|
+
discover: z.array(z.string().min(1).max(256)).max(10).optional(),
|
|
44
|
+
show: z.array(ShowHintSchema).max(20).optional(),
|
|
45
|
+
hidden: z.boolean().optional(),
|
|
46
|
+
order: z.number().int().optional(),
|
|
47
|
+
}).strict();
|
|
48
|
+
export const ProjectQuickrefConfigSchema = z.object({
|
|
49
|
+
version: z.literal('1'),
|
|
50
|
+
project: ProjectQuickrefSchema.shape.project.optional(),
|
|
51
|
+
precedence: z.string().min(1).max(1024).optional(),
|
|
52
|
+
entries: z.array(QuickrefEntrySchema).max(50).default([]),
|
|
53
|
+
discovery: z.object({
|
|
54
|
+
enabled: z.boolean().default(true),
|
|
55
|
+
excludeBundles: z.array(z.string().min(1)).max(200).default([]),
|
|
56
|
+
overrides: z.record(z.string(), QuickrefOverrideSchema).default({}),
|
|
57
|
+
}).strict().default({ enabled: true, excludeBundles: [], overrides: {} }),
|
|
58
|
+
}).strict();
|
|
38
59
|
function sha256(content) {
|
|
39
60
|
return createHash('sha256').update(content).digest('hex');
|
|
40
61
|
}
|
|
@@ -78,6 +99,158 @@ export async function loadProjectQuickref(projectDir) {
|
|
|
78
99
|
return { sourcePath, errors: [`${sourcePath}: invalid JSON: ${error.message}`], exists: true };
|
|
79
100
|
}
|
|
80
101
|
}
|
|
102
|
+
function slug(value) {
|
|
103
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
104
|
+
return normalized || 'project';
|
|
105
|
+
}
|
|
106
|
+
async function inferredProject(projectDir) {
|
|
107
|
+
let packageName = basename(resolve(projectDir));
|
|
108
|
+
let description = `Managed project-specific orientation for ${packageName}.`;
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(await readFile(join(projectDir, 'package.json'), 'utf8'));
|
|
111
|
+
if (parsed.name)
|
|
112
|
+
packageName = parsed.name;
|
|
113
|
+
if (parsed.description)
|
|
114
|
+
description = parsed.description.slice(0, 512);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// package metadata is optional
|
|
118
|
+
}
|
|
119
|
+
const id = slug(packageName);
|
|
120
|
+
const name = packageName
|
|
121
|
+
.replace(/^@[^/]+\//, '')
|
|
122
|
+
.split(/[-_]/)
|
|
123
|
+
.filter(Boolean)
|
|
124
|
+
.map(part => part[0]?.toUpperCase() + part.slice(1))
|
|
125
|
+
.join(' ') || 'Project';
|
|
126
|
+
return { id, name, description };
|
|
127
|
+
}
|
|
128
|
+
async function loadManagedConfig(projectDir) {
|
|
129
|
+
const path = projectAiwgPath(projectDir, 'quickref.config.json');
|
|
130
|
+
const raw = await readIfPresent(path);
|
|
131
|
+
if (raw === null)
|
|
132
|
+
return { path };
|
|
133
|
+
let json;
|
|
134
|
+
try {
|
|
135
|
+
json = JSON.parse(raw);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw new Error(`${path}: invalid JSON: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
const parsed = ProjectQuickrefConfigSchema.safeParse(json);
|
|
141
|
+
if (!parsed.success) {
|
|
142
|
+
throw new Error(parsed.error.issues.map(issue => `${path}: ${issue.path.join('.') || '(root)'}: ${issue.message}`).join('\n'));
|
|
143
|
+
}
|
|
144
|
+
return { path, config: parsed.data };
|
|
145
|
+
}
|
|
146
|
+
function dedupe(values) {
|
|
147
|
+
const seen = new Set();
|
|
148
|
+
return values.filter(value => {
|
|
149
|
+
const key = value.trim().toLowerCase();
|
|
150
|
+
if (!key || seen.has(key))
|
|
151
|
+
return false;
|
|
152
|
+
seen.add(key);
|
|
153
|
+
return true;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function dedupeShow(values) {
|
|
157
|
+
const seen = new Set();
|
|
158
|
+
return values.filter(value => {
|
|
159
|
+
const key = `${value.type}:${value.name.toLowerCase()}`;
|
|
160
|
+
if (seen.has(key))
|
|
161
|
+
return false;
|
|
162
|
+
seen.add(key);
|
|
163
|
+
return true;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/** Resolve legacy operator input or synthesize a managed definition from project-local bundles. */
|
|
167
|
+
export async function resolveProjectQuickref(projectDir) {
|
|
168
|
+
const legacy = await loadProjectQuickref(projectDir);
|
|
169
|
+
if (legacy.exists) {
|
|
170
|
+
if (!legacy.definition)
|
|
171
|
+
throw new Error(legacy.errors.join('\n'));
|
|
172
|
+
return {
|
|
173
|
+
definition: legacy.definition,
|
|
174
|
+
exists: true,
|
|
175
|
+
sourcePath: legacy.sourcePath,
|
|
176
|
+
provenance: 'legacy',
|
|
177
|
+
warnings: [],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const managed = await loadManagedConfig(projectDir);
|
|
181
|
+
const discovery = await discoverProjectLocalBundles(projectDir);
|
|
182
|
+
if (discovery.errors.length > 0) {
|
|
183
|
+
throw new Error(discovery.errors.map(error => `${error.path}: ${error.field}: ${error.actual}`).join('\n'));
|
|
184
|
+
}
|
|
185
|
+
if (!managed.config && discovery.bundles.length === 0) {
|
|
186
|
+
return { exists: false, sourcePath: managed.path, provenance: 'managed', warnings: [] };
|
|
187
|
+
}
|
|
188
|
+
const config = managed.config ?? ProjectQuickrefConfigSchema.parse({ version: '1' });
|
|
189
|
+
const excluded = new Set(config.discovery.excludeBundles.map(id => id.toLowerCase()));
|
|
190
|
+
const warnings = [];
|
|
191
|
+
const candidates = [];
|
|
192
|
+
if (config.discovery.enabled) {
|
|
193
|
+
for (const bundle of discovery.bundles) {
|
|
194
|
+
if (excluded.has(bundle.id.toLowerCase()))
|
|
195
|
+
continue;
|
|
196
|
+
const override = config.discovery.overrides[bundle.id];
|
|
197
|
+
if (override?.hidden)
|
|
198
|
+
continue;
|
|
199
|
+
const artifacts = (await enumerateBundleArtifacts(bundle.artifactPath ?? bundle.bundlePath))
|
|
200
|
+
.sort((a, b) => a.type.localeCompare(b.type) || a.id.localeCompare(b.id));
|
|
201
|
+
const inferredShow = dedupeShow(artifacts.map(artifact => ({
|
|
202
|
+
type: artifact.type,
|
|
203
|
+
name: artifact.id,
|
|
204
|
+
}))).slice(0, 8);
|
|
205
|
+
if (artifacts.length > inferredShow.length && inferredShow.length === 8) {
|
|
206
|
+
warnings.push(`${bundle.id}: show hints truncated from ${artifacts.length} artifacts to 8`);
|
|
207
|
+
}
|
|
208
|
+
let discover = override?.discover ?? dedupe([
|
|
209
|
+
bundle.id,
|
|
210
|
+
bundle.manifest.name,
|
|
211
|
+
...(bundle.manifest.keywords ?? []),
|
|
212
|
+
]).slice(0, 3);
|
|
213
|
+
const show = override?.show ? dedupeShow(override.show) : inferredShow;
|
|
214
|
+
if (discover.length === 0 && show.length === 0)
|
|
215
|
+
discover = [bundle.id];
|
|
216
|
+
candidates.push({
|
|
217
|
+
order: override?.order ?? 0,
|
|
218
|
+
type: bundle.type,
|
|
219
|
+
id: bundle.id,
|
|
220
|
+
entry: {
|
|
221
|
+
title: override?.title ?? bundle.manifest.name,
|
|
222
|
+
summary: override?.summary ?? bundle.manifest.description,
|
|
223
|
+
discover,
|
|
224
|
+
show,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
candidates.sort((a, b) => a.order - b.order || a.type.localeCompare(b.type) || a.id.localeCompare(b.id));
|
|
230
|
+
const discoveredEntries = candidates.map(candidate => candidate.entry);
|
|
231
|
+
const entries = [...discoveredEntries, ...config.entries];
|
|
232
|
+
if (entries.length > 50)
|
|
233
|
+
warnings.push(`quickref entries truncated from ${entries.length} to 50`);
|
|
234
|
+
const bounded = entries.slice(0, 50);
|
|
235
|
+
if (bounded.length === 0) {
|
|
236
|
+
return { exists: false, sourcePath: managed.path, provenance: 'managed', warnings };
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
definition: {
|
|
240
|
+
version: '1',
|
|
241
|
+
project: config.project ?? await inferredProject(projectDir),
|
|
242
|
+
precedence: config.precedence ?? 'Use project-local capabilities before generic AIWG workflows when they apply.',
|
|
243
|
+
entries: bounded,
|
|
244
|
+
},
|
|
245
|
+
exists: true,
|
|
246
|
+
sourcePath: managed.path,
|
|
247
|
+
provenance: 'managed',
|
|
248
|
+
warnings,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export async function hasProjectQuickref(projectDir) {
|
|
252
|
+
return (await resolveProjectQuickref(projectDir)).exists;
|
|
253
|
+
}
|
|
81
254
|
export function renderProjectQuickref(definition) {
|
|
82
255
|
const skillName = projectQuickrefSkillName(definition.project.id);
|
|
83
256
|
const lines = [
|
|
@@ -111,11 +284,9 @@ export function renderProjectQuickref(definition) {
|
|
|
111
284
|
return lines.join('\n');
|
|
112
285
|
}
|
|
113
286
|
export async function generateProjectQuickref(projectDir, options = {}) {
|
|
114
|
-
const loaded = await
|
|
115
|
-
if (!loaded.exists)
|
|
287
|
+
const loaded = await resolveProjectQuickref(projectDir);
|
|
288
|
+
if (!loaded.exists || !loaded.definition)
|
|
116
289
|
throw new Error(`Project quickref source not found: ${loaded.sourcePath}`);
|
|
117
|
-
if (!loaded.definition)
|
|
118
|
-
throw new Error(loaded.errors.join('\n'));
|
|
119
290
|
if (!options.dryRun)
|
|
120
291
|
await appendAiwgSourceTrackBlock(projectDir);
|
|
121
292
|
const skillName = projectQuickrefSkillName(loaded.definition.project.id);
|
|
@@ -127,6 +298,14 @@ export async function generateProjectQuickref(projectDir, options = {}) {
|
|
|
127
298
|
await mkdir(dirname(outputPath), { recursive: true });
|
|
128
299
|
await writeFile(outputPath, content, 'utf8');
|
|
129
300
|
}
|
|
301
|
+
if (!options.dryRun && loaded.provenance === 'managed') {
|
|
302
|
+
const snapshotPath = projectAiwgPath(projectDir, 'generated', 'project-quickref', 'definition.json');
|
|
303
|
+
const snapshot = JSON.stringify(loaded.definition, null, 2) + '\n';
|
|
304
|
+
if (await readIfPresent(snapshotPath) !== snapshot) {
|
|
305
|
+
await mkdir(dirname(snapshotPath), { recursive: true });
|
|
306
|
+
await writeFile(snapshotPath, snapshot, 'utf8');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
130
309
|
if (!options.dryRun) {
|
|
131
310
|
const generatedRoot = projectAiwgPath(projectDir, 'generated', 'project-quickref');
|
|
132
311
|
try {
|
|
@@ -149,6 +328,8 @@ export async function generateProjectQuickref(projectDir, options = {}) {
|
|
|
149
328
|
content,
|
|
150
329
|
changed,
|
|
151
330
|
dryRun: options.dryRun ?? false,
|
|
331
|
+
provenance: loaded.provenance,
|
|
332
|
+
warnings: loaded.warnings,
|
|
152
333
|
};
|
|
153
334
|
}
|
|
154
335
|
function resolveProviderSkillsRoot(provider, projectDir, homeDir) {
|
|
@@ -206,9 +387,9 @@ async function findStaleOwnedQuickrefs(root, projectDir, keepName, global) {
|
|
|
206
387
|
}
|
|
207
388
|
export async function deployProjectQuickref(projectDir, provider, options = {}) {
|
|
208
389
|
const generated = await generateProjectQuickref(projectDir, { dryRun: options.dryRun });
|
|
209
|
-
const loaded = await
|
|
390
|
+
const loaded = await resolveProjectQuickref(projectDir);
|
|
210
391
|
if (!loaded.definition)
|
|
211
|
-
throw new Error(loaded.
|
|
392
|
+
throw new Error(`Project quickref source not found: ${loaded.sourcePath}`);
|
|
212
393
|
const target = resolveProviderSkillsRoot(provider, projectDir, options.homeDir ?? homedir());
|
|
213
394
|
const targetDir = join(target.root, generated.skillName);
|
|
214
395
|
const targetPath = join(targetDir, 'SKILL.md');
|
|
@@ -259,11 +440,17 @@ export async function deployProjectQuickref(projectDir, provider, options = {})
|
|
|
259
440
|
};
|
|
260
441
|
}
|
|
261
442
|
export async function auditProjectQuickref(projectDir, providers, options = {}) {
|
|
262
|
-
|
|
443
|
+
let loaded;
|
|
444
|
+
try {
|
|
445
|
+
loaded = await resolveProjectQuickref(projectDir);
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
return { exists: true, errors: [error.message], drift: [] };
|
|
449
|
+
}
|
|
263
450
|
if (!loaded.exists)
|
|
264
|
-
return { exists: false, errors:
|
|
451
|
+
return { exists: false, errors: [], drift: [] };
|
|
265
452
|
if (!loaded.definition)
|
|
266
|
-
return { exists: true, errors:
|
|
453
|
+
return { exists: true, errors: ['project quickref definition unavailable'], drift: [] };
|
|
267
454
|
const content = renderProjectQuickref(loaded.definition);
|
|
268
455
|
const skillName = projectQuickrefSkillName(loaded.definition.project.id);
|
|
269
456
|
const generatedPath = projectAiwgPath(projectDir, 'generated', 'project-quickref', skillName, 'SKILL.md');
|
|
@@ -70,6 +70,32 @@ export const FEATURE_CATALOG = [
|
|
|
70
70
|
],
|
|
71
71
|
cost: '~3 MB — pure JS, no native deps',
|
|
72
72
|
},
|
|
73
|
+
{
|
|
74
|
+
name: 'graph',
|
|
75
|
+
description: 'Graphology backend for rich artifact traversal and graph operators',
|
|
76
|
+
packages: ['graphology', 'graphology-operators', 'graphology-traversal'],
|
|
77
|
+
packageSpecs: {
|
|
78
|
+
graphology: '0.26.0',
|
|
79
|
+
'graphology-operators': '1.6.0',
|
|
80
|
+
'graphology-traversal': '0.3.0',
|
|
81
|
+
},
|
|
82
|
+
enables: [
|
|
83
|
+
'index.graphBackend: graphology',
|
|
84
|
+
'in-memory attributed graph traversal and operator workflows',
|
|
85
|
+
],
|
|
86
|
+
cost: '~2 MB — pure JS, no native deps',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'terminal',
|
|
90
|
+
description: 'Headless terminal state parsing for PTY orchestration and auditing',
|
|
91
|
+
packages: ['@xterm/headless'],
|
|
92
|
+
packageSpecs: { '@xterm/headless': '6.0.0' },
|
|
93
|
+
enables: [
|
|
94
|
+
'structured screen-state parsing for PTY orchestrators',
|
|
95
|
+
'auditable terminal snapshots without a browser DOM',
|
|
96
|
+
],
|
|
97
|
+
cost: '~1 MB — pure JS, no native deps',
|
|
98
|
+
},
|
|
73
99
|
];
|
|
74
100
|
/** Native packages require a successful runtime load, not just package files. */
|
|
75
101
|
export const NATIVE_FEATURE_PACKAGES = new Set(FEATURE_CATALOG.flatMap(feature => feature.scriptPackages ?? []));
|
package/dist/src/features/cli.js
CHANGED
|
@@ -29,9 +29,7 @@ export async function main(args) {
|
|
|
29
29
|
case 'remove':
|
|
30
30
|
console.error(`Error: \`aiwg features ${subcommand}\` not yet implemented (#1219 Cycle 3).`);
|
|
31
31
|
console.error('');
|
|
32
|
-
console.error('
|
|
33
|
-
console.error(' cd <aiwg install root>');
|
|
34
|
-
console.error(' npm install <packages> # see `aiwg features info <name>` for the package list');
|
|
32
|
+
console.error('No files were changed. Feature removal must use a future AIWG CLI release.');
|
|
35
33
|
process.exit(1);
|
|
36
34
|
break;
|
|
37
35
|
case 'help':
|
|
@@ -11,7 +11,23 @@ export async function loadFeaturePackage(name) {
|
|
|
11
11
|
}
|
|
12
12
|
catch (featureError) {
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
const resolved = createRequire(import.meta.url).resolve(name);
|
|
15
|
+
return await import(pathToFileURL(resolved).href);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw featureError;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Synchronous counterpart for optional CommonJS packages used by constructors. */
|
|
23
|
+
export function requireFeaturePackage(name) {
|
|
24
|
+
const featureRequire = createRequire(path.join(getFeaturesRoot(), 'package.json'));
|
|
25
|
+
try {
|
|
26
|
+
return featureRequire(name);
|
|
27
|
+
}
|
|
28
|
+
catch (featureError) {
|
|
29
|
+
try {
|
|
30
|
+
return createRequire(import.meta.url)(name);
|
|
15
31
|
}
|
|
16
32
|
catch {
|
|
17
33
|
throw featureError;
|
package/dist/src/issues/cli.js
CHANGED
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
* @issue #1462
|
|
9
9
|
*/
|
|
10
10
|
import { readFile, writeFile } from 'fs/promises';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { pathToFileURL } from 'node:url';
|
|
13
|
+
import { getFrameworkRoot } from '../channel/manager.mjs';
|
|
14
|
+
import { readAiwgConfig } from '../config/aiwg-config.js';
|
|
11
15
|
import { LocalIssueProviderCore, buildExternalIssueSnapshotFromLocal, buildLocalIssueConflictReport, createLiveIssueClient, localIssueRoot, parseCommentIdMappings, parseExternalIssueSnapshot, } from './index.js';
|
|
12
16
|
export async function main(args, cwd = process.cwd()) {
|
|
13
17
|
const parsed = parseArgs(args);
|
|
@@ -27,7 +31,11 @@ export async function main(args, cwd = process.cwd()) {
|
|
|
27
31
|
return;
|
|
28
32
|
case 'new':
|
|
29
33
|
case 'create':
|
|
30
|
-
await handleNew(issues, parsed);
|
|
34
|
+
await handleNew(issues, parsed, cwd);
|
|
35
|
+
return;
|
|
36
|
+
case 'plan':
|
|
37
|
+
case 'draft-plan':
|
|
38
|
+
await handlePlan(parsed, cwd);
|
|
31
39
|
return;
|
|
32
40
|
case 'list':
|
|
33
41
|
case 'ls':
|
|
@@ -68,12 +76,19 @@ async function handleInit(issues, args) {
|
|
|
68
76
|
});
|
|
69
77
|
printJsonOrText(args, config, `Initialized local issues with prefix ${config.issue_key.prefix}`);
|
|
70
78
|
}
|
|
71
|
-
async function
|
|
79
|
+
async function loadIssueComposer() {
|
|
80
|
+
const frameworkRoot = await getFrameworkRoot();
|
|
81
|
+
const modulePath = path.join(frameworkRoot, 'tools', 'issues', 'policy-boundary-composer.mjs');
|
|
82
|
+
return import(pathToFileURL(modulePath).href);
|
|
83
|
+
}
|
|
84
|
+
async function buildIssuePlan(args, cwd) {
|
|
72
85
|
const title = stringFlag(args, 'title') ?? args.positional[1];
|
|
73
86
|
if (!title)
|
|
74
|
-
throw new Error('Usage: aiwg issue
|
|
87
|
+
throw new Error('Usage: aiwg issue plan --title <title> [--body <text>|--body-file <path>]');
|
|
75
88
|
const body = await readBody(args);
|
|
76
|
-
const
|
|
89
|
+
const config = await readAiwgConfig(cwd);
|
|
90
|
+
const composer = await loadIssueComposer();
|
|
91
|
+
return composer.planIssueDraft({
|
|
77
92
|
title,
|
|
78
93
|
body,
|
|
79
94
|
type: stringFlag(args, 'type'),
|
|
@@ -81,8 +96,76 @@ async function handleNew(issues, args) {
|
|
|
81
96
|
labels: csvFlag(args, 'label'),
|
|
82
97
|
assignees: csvFlag(args, 'assignee'),
|
|
83
98
|
author: stringFlag(args, 'author'),
|
|
84
|
-
});
|
|
85
|
-
|
|
99
|
+
}, { threatAssessment: config?.security?.threatAssessment });
|
|
100
|
+
}
|
|
101
|
+
async function handlePlan(args, cwd) {
|
|
102
|
+
const plan = await buildIssuePlan(args, cwd);
|
|
103
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
104
|
+
}
|
|
105
|
+
async function handleNew(issues, args, cwd) {
|
|
106
|
+
const plan = await buildIssuePlan(args, cwd);
|
|
107
|
+
if (plan.disposition === 'blocked') {
|
|
108
|
+
throw new Error(`Issue draft rejected by ${plan.blockingRule}; no issue was written. Run aiwg issue plan to inspect suggested segments.`);
|
|
109
|
+
}
|
|
110
|
+
const authorizationDigest = stringFlag(args, 'authorize-policy');
|
|
111
|
+
if (plan.authorizationRequired && authorizationDigest !== plan.digest) {
|
|
112
|
+
throw new Error(`Issue draft requires explicit policy authorization. Review 'aiwg issue plan', then pass --authorize-policy ${plan.digest}. No issue was written.`);
|
|
113
|
+
}
|
|
114
|
+
const composer = await loadIssueComposer();
|
|
115
|
+
const records = new Map();
|
|
116
|
+
const adapter = {
|
|
117
|
+
async findByMarker(marker) {
|
|
118
|
+
const listed = await issues.listIssues({ limit: 10_000 });
|
|
119
|
+
for (const entry of listed.issues) {
|
|
120
|
+
const record = await issues.getIssue(entry.id, { body: true, comments: 0 });
|
|
121
|
+
if (record.body.includes(marker)) {
|
|
122
|
+
records.set(record.fields.id, record);
|
|
123
|
+
return { id: record.fields.id, reference: record.fields.id, body: record.body };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
},
|
|
128
|
+
async create(draft) {
|
|
129
|
+
const record = await issues.createIssue({
|
|
130
|
+
title: draft.title,
|
|
131
|
+
body: draft.body,
|
|
132
|
+
type: stringFlag(args, 'type'),
|
|
133
|
+
priority: stringFlag(args, 'priority'),
|
|
134
|
+
labels: draft.labels,
|
|
135
|
+
assignees: draft.assignees,
|
|
136
|
+
author: stringFlag(args, 'author'),
|
|
137
|
+
});
|
|
138
|
+
records.set(record.fields.id, record);
|
|
139
|
+
return { id: record.fields.id, reference: record.fields.id, body: record.body };
|
|
140
|
+
},
|
|
141
|
+
async update(issue, patch) {
|
|
142
|
+
const record = records.get(issue.id) ?? await issues.getIssue(issue.id, { body: true, comments: 0 });
|
|
143
|
+
const references = [...patch.body.matchAll(/(?:Depends on|Related):\s+([A-Z][A-Z0-9_-]*-\d+)/g)]
|
|
144
|
+
.map((match) => match[1])
|
|
145
|
+
.filter((reference) => reference !== issue.id);
|
|
146
|
+
await issues.updateIssueFields(issue.id, {
|
|
147
|
+
links: {
|
|
148
|
+
...record.fields.links,
|
|
149
|
+
related: [...new Set([...record.fields.links.related, ...references])],
|
|
150
|
+
parent: patch.body.match(/Depends on:\s+([A-Z][A-Z0-9_-]*-\d+)/)?.[1] ?? record.fields.links.parent,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
await issues.commentIssue(issue.id, patch.body.slice(patch.body.indexOf('## Related')), {
|
|
154
|
+
author: stringFlag(args, 'author') ?? 'aiwg',
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
const result = await composer.executeIssuePlan(plan, adapter, { authorizationDigest });
|
|
159
|
+
if (result.status !== 'created') {
|
|
160
|
+
throw new Error(`Issue creation ${result.status}: ${result.error ?? 'review the recovery envelope'}\n${JSON.stringify(result.recovery ?? result, null, 2)}`);
|
|
161
|
+
}
|
|
162
|
+
if (args.flags.has('json') || result.created.length > 1) {
|
|
163
|
+
console.log(JSON.stringify(result, null, 2));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const issue = result.created[0];
|
|
167
|
+
console.log(`Created ${issue.reference}: ${plan.segments[0].title}`);
|
|
168
|
+
}
|
|
86
169
|
}
|
|
87
170
|
async function handleList(issues, args) {
|
|
88
171
|
const limit = stringFlag(args, 'limit');
|
|
@@ -313,7 +396,8 @@ function csvFlag(args, name) {
|
|
|
313
396
|
function printUsage() {
|
|
314
397
|
console.log(`Usage:
|
|
315
398
|
aiwg issue init [--prefix KEY] [--padding N]
|
|
316
|
-
aiwg issue
|
|
399
|
+
aiwg issue plan --title "..." [--body "..."] [--body-file path] [--json]
|
|
400
|
+
aiwg issue new --title "..." [--body "..."] [--body-file path] [--authorize-policy DIGEST]
|
|
317
401
|
aiwg issue list [--status open] [--label bug] [--search text] [--limit 20] [--json]
|
|
318
402
|
aiwg issue show <KEY> [--comments last:10|all]
|
|
319
403
|
aiwg issue import --from gitea|github --snapshot-file path [--json]
|
package/dist/src/mcp/server.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @see https://modelcontextprotocol.io/specification/2025-11-25
|
|
8
8
|
* @architecture @.aiwg/architecture/software-architecture-doc.md - Section 2.1 CLI Layer
|
|
9
9
|
* @reference @docs/references/REF-066-mcp-specification-2025.md
|
|
10
|
-
* @cli @docs/
|
|
10
|
+
* @cli @docs/cli/agent-usage.md - mcp serve command
|
|
11
11
|
* @frameworks @agentic/code/frameworks/sdlc-complete/
|
|
12
12
|
* @frameworks @agentic/code/frameworks/media-marketing-kit/
|
|
13
13
|
*/
|
package/dist/src/ops/registry.js
CHANGED
|
@@ -13,7 +13,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
|
13
13
|
import { execSync } from 'child_process';
|
|
14
14
|
import { resolveConfigDir } from '../config/user-config.js';
|
|
15
15
|
import { resolveWorkspace } from '../config/workspace.js';
|
|
16
|
-
import {
|
|
16
|
+
import { projectControlPath } from '../config/project-artifacts.js';
|
|
17
17
|
/**
|
|
18
18
|
* Extension type abbreviations
|
|
19
19
|
*/
|
|
@@ -455,7 +455,7 @@ export class OpsRegistry {
|
|
|
455
455
|
throw new Error(`Workspace "${name}" not found`);
|
|
456
456
|
}
|
|
457
457
|
let canonical = null;
|
|
458
|
-
const canonicalConfigPath =
|
|
458
|
+
const canonicalConfigPath = projectControlPath(workspace.home, 'aiwg.config');
|
|
459
459
|
let canonicalDeclared = false;
|
|
460
460
|
if (existsSync(canonicalConfigPath)) {
|
|
461
461
|
try {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { access, copyFile, mkdir, readFile, rename } from 'node:fs/promises';
|
|
10
10
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
11
11
|
import { load as loadYaml } from 'js-yaml';
|
|
12
|
-
import { projectAiwgPath } from '../config/project-artifacts.js';
|
|
12
|
+
import { projectAiwgPath, projectControlPath } from '../config/project-artifacts.js';
|
|
13
13
|
/**
|
|
14
14
|
* Export provider mappings without inventing provider grants. Stable IDs,
|
|
15
15
|
* principals, scopes, resources, actions, and conditions survive unchanged;
|
|
@@ -375,7 +375,7 @@ export async function archiveLegacyPermissionManifests(projectDir) {
|
|
|
375
375
|
return archived;
|
|
376
376
|
}
|
|
377
377
|
export async function backupConfig(projectDir) {
|
|
378
|
-
const source =
|
|
378
|
+
const source = projectControlPath(projectDir, 'aiwg.config');
|
|
379
379
|
const backupDir = projectAiwgPath(projectDir, 'backups');
|
|
380
380
|
await mkdir(backupDir, { recursive: true });
|
|
381
381
|
const content = await readFile(source);
|
|
@@ -323,9 +323,9 @@ providers:
|
|
|
323
323
|
aggregated_output: true
|
|
324
324
|
|
|
325
325
|
windsurf:
|
|
326
|
-
display_name:
|
|
327
|
-
status:
|
|
328
|
-
# Windsurf
|
|
326
|
+
display_name: Devin Desktop
|
|
327
|
+
status: stable
|
|
328
|
+
# Devin Desktop uses the Windsurf compatibility surface and requires a VS Code host — daemon not supported
|
|
329
329
|
daemon_tier: unsupported
|
|
330
330
|
daemon_pty_adapter: false
|
|
331
331
|
artifact_paths:
|
|
@@ -704,11 +704,13 @@ const BUILT_IN_SEEDS = [
|
|
|
704
704
|
},
|
|
705
705
|
{
|
|
706
706
|
id: 'windsurf',
|
|
707
|
-
|
|
707
|
+
displayName: 'Devin Desktop',
|
|
708
|
+
status: 'stable',
|
|
709
|
+
aliases: ['devin', 'devin-desktop', 'devin-local', 'cascade'],
|
|
708
710
|
builtIn: true,
|
|
709
711
|
surfaces: {
|
|
710
|
-
primary: '
|
|
711
|
-
compatibility: ['devin-desktop', 'devin-local', 'cascade'],
|
|
712
|
+
primary: 'devin',
|
|
713
|
+
compatibility: ['devin-desktop', 'windsurf', 'devin-local', 'cascade'],
|
|
712
714
|
precedence: ['.devin/rules/', '.windsurf/rules/', 'AGENTS.md', '.windsurfrules'],
|
|
713
715
|
related: [
|
|
714
716
|
{
|
|
@@ -716,7 +718,7 @@ const BUILT_IN_SEEDS = [
|
|
|
716
718
|
displayName: 'Devin Desktop',
|
|
717
719
|
relationship: 'same-provider',
|
|
718
720
|
deployable: true,
|
|
719
|
-
aliases: ['windsurf', 'cascade'],
|
|
721
|
+
aliases: ['devin', 'windsurf', 'cascade'],
|
|
720
722
|
paths: {
|
|
721
723
|
rules: ['.devin/rules/*.md', '.windsurf/rules/*.md'],
|
|
722
724
|
skills: [],
|
|
@@ -724,7 +726,7 @@ const BUILT_IN_SEEDS = [
|
|
|
724
726
|
legacy: ['.windsurfrules'],
|
|
725
727
|
},
|
|
726
728
|
notes: [
|
|
727
|
-
'Devin Desktop is the
|
|
729
|
+
'Devin Desktop is the current product name; --provider devin is preferred and --provider windsurf remains a deprecated compatibility id.',
|
|
728
730
|
'.devin/rules is preferred by Devin Desktop, but AIWG keeps .devin/ as ignored local provider output and currently emits the compatibility surface through .windsurf/ plus AGENTS.md.',
|
|
729
731
|
],
|
|
730
732
|
},
|
|
@@ -288,16 +288,10 @@ async function spawnSandboxWsPty(session, agentId, wsEndpoint, opts) {
|
|
|
288
288
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
289
289
|
let wsMod;
|
|
290
290
|
try {
|
|
291
|
-
wsMod = await (
|
|
291
|
+
wsMod = await loadFeaturePackage('ws');
|
|
292
292
|
}
|
|
293
293
|
catch {
|
|
294
|
-
|
|
295
|
-
// @ts-expect-error Optional runtime dependency; this workspace does not ship @types/ws.
|
|
296
|
-
wsMod = await import('ws');
|
|
297
|
-
}
|
|
298
|
-
catch {
|
|
299
|
-
throw new Error('ws package required for sandbox PTY bridge. Run: npm install ws');
|
|
300
|
-
}
|
|
294
|
+
throw new Error('WebSocket support is required for the sandbox PTY bridge; run `aiwg features install webserver`');
|
|
301
295
|
}
|
|
302
296
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
303
297
|
const WS = wsMod.WebSocket ?? wsMod.default?.WebSocket ?? wsMod.default;
|
|
@@ -10,15 +10,12 @@
|
|
|
10
10
|
* @see src/serve/pty-bridge.ts — PTY WebSocket bridge
|
|
11
11
|
*/
|
|
12
12
|
import { EventEmitter } from 'events';
|
|
13
|
-
import {
|
|
14
|
-
// @xterm/headless is an optional dependency. Load it once at module level
|
|
15
|
-
// using createRequire so we can import the CJS bundle from ESM.
|
|
16
|
-
const _require = createRequire(import.meta.url);
|
|
13
|
+
import { requireFeaturePackage } from '../features/runtime.js';
|
|
17
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
15
|
let XtermTerminal;
|
|
19
16
|
try {
|
|
20
17
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
-
const xterm =
|
|
18
|
+
const xterm = requireFeaturePackage('@xterm/headless');
|
|
22
19
|
XtermTerminal = xterm.Terminal;
|
|
23
20
|
}
|
|
24
21
|
catch {
|
|
@@ -48,7 +45,7 @@ export class ScreenReader {
|
|
|
48
45
|
disposed = false;
|
|
49
46
|
constructor(opts) {
|
|
50
47
|
if (!XtermTerminal) {
|
|
51
|
-
throw new Error('
|
|
48
|
+
throw new Error('Terminal screen parsing is unavailable; run `aiwg features install terminal`');
|
|
52
49
|
}
|
|
53
50
|
this._rows = opts?.rows ?? 24;
|
|
54
51
|
this._cols = opts?.cols ?? 80;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { promises as fs } from 'node:fs';
|
|
18
18
|
import * as path from 'node:path';
|
|
19
|
-
import {
|
|
19
|
+
import { projectControlPath } from '../../config/project-artifacts.js';
|
|
20
20
|
import { buildParallelismSection, replaceOrAppendParallelismBlock } from './parallelism-section.js';
|
|
21
21
|
import { buildContextFinalizationBlock, replaceOrAppendFinalizationBlock } from './finalization.js';
|
|
22
22
|
import { buildExternalLinksSection, replaceOrAppendExternalLinksBlock, } from './external-links-section.js';
|
|
@@ -79,7 +79,7 @@ export async function buildAiwgMdContent(projectPath, stagedClaudeMdContent) {
|
|
|
79
79
|
return replaceOrAppendExternalLinksBlock(withFinalization, externalLinksSection);
|
|
80
80
|
}
|
|
81
81
|
// Fallback stub.
|
|
82
|
-
const normalizedPath =
|
|
82
|
+
const normalizedPath = projectControlPath(projectPath, 'AIWG.md');
|
|
83
83
|
const normalizedRelative = path.relative(projectPath, normalizedPath).replace(/\\/g, '/');
|
|
84
84
|
const normalizedLink = normalizedRelative.startsWith('.') ? normalizedRelative : `./${normalizedRelative}`;
|
|
85
85
|
const stub = [
|