@aiwg/cli 2026.9.0 → 2026.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agentic/code/providers/capability-matrix.yaml +41 -0
- package/agentic/code/providers/model-capabilities.v1.json +11 -0
- package/agentic/code/providers/model-catalog.v1.json +8 -0
- package/agentic/code/providers/pi/aiwg-bridge.ts +26 -0
- package/bin/aiwg.mjs +15 -3
- package/dist/src/api/index.d.ts +3 -0
- package/dist/src/api/index.js +3 -0
- package/dist/src/auth/credential-store.js +6 -0
- package/dist/src/channel/manager.mjs +2 -2
- package/dist/src/cli/handlers/dataset.js +186 -0
- package/dist/src/cli/handlers/help.js +2 -1
- package/dist/src/cli/handlers/index.js +5 -1
- package/dist/src/cli/handlers/init.js +1 -0
- package/dist/src/cli/handlers/output-mode.js +18 -1
- package/dist/src/cli/handlers/run.js +11 -3
- package/dist/src/cli/handlers/schema.js +221 -0
- package/dist/src/cli/handlers/sessions.js +30 -12
- package/dist/src/cli/handlers/steward.js +11 -1
- package/dist/src/cli/handlers/use.js +6 -2
- package/dist/src/cli/hooks/builtin/activity-log-hook.js +6 -0
- package/dist/src/cli/router.js +1 -1
- package/dist/src/cli/scope-resolver.js +22 -0
- package/dist/src/dataset/adapter-sdk.d.ts +41 -0
- package/dist/src/dataset/adapter-sdk.js +147 -0
- package/dist/src/dataset/adapter-types.d.ts +179 -0
- package/dist/src/dataset/adapter-types.js +2 -0
- package/dist/src/dataset/adapters.d.ts +104 -0
- package/dist/src/dataset/adapters.js +518 -0
- package/dist/src/dataset/conformance-types.d.ts +84 -0
- package/dist/src/dataset/conformance-types.js +3 -0
- package/dist/src/dataset/conformance.d.ts +13 -0
- package/dist/src/dataset/conformance.js +90 -0
- package/dist/src/dataset/contracts.d.ts +17 -0
- package/dist/src/dataset/contracts.js +236 -0
- package/dist/src/dataset/file-orchestration-repository.d.ts +19 -0
- package/dist/src/dataset/file-orchestration-repository.js +68 -0
- package/dist/src/dataset/fortemi-execution-bridge.d.ts +33 -0
- package/dist/src/dataset/fortemi-execution-bridge.js +35 -0
- package/dist/src/dataset/index.d.ts +21 -0
- package/dist/src/dataset/index.js +21 -0
- package/dist/src/dataset/ledger-types.d.ts +141 -0
- package/dist/src/dataset/ledger-types.js +2 -0
- package/dist/src/dataset/ledger.d.ts +27 -0
- package/dist/src/dataset/ledger.js +100 -0
- package/dist/src/dataset/local-execution-backend.d.ts +10 -0
- package/dist/src/dataset/local-execution-backend.js +32 -0
- package/dist/src/dataset/orchestration-repository.d.ts +29 -0
- package/dist/src/dataset/orchestration-repository.js +34 -0
- package/dist/src/dataset/orchestration-service.d.ts +56 -0
- package/dist/src/dataset/orchestration-service.js +466 -0
- package/dist/src/dataset/orchestration-types.d.ts +83 -0
- package/dist/src/dataset/orchestration-types.js +2 -0
- package/dist/src/dataset/presentation.d.ts +3 -0
- package/dist/src/dataset/presentation.js +8 -0
- package/dist/src/dataset/projections.d.ts +42 -0
- package/dist/src/dataset/projections.js +192 -0
- package/dist/src/dataset/schema-governance.d.ts +71 -0
- package/dist/src/dataset/schema-governance.js +135 -0
- package/dist/src/dataset/standards-types.d.ts +66 -0
- package/dist/src/dataset/standards-types.js +10 -0
- package/dist/src/dataset/standards.d.ts +13 -0
- package/dist/src/dataset/standards.js +291 -0
- package/dist/src/dataset/types.d.ts +258 -0
- package/dist/src/dataset/types.js +2 -0
- package/dist/src/extensions/commands/definitions.js +27 -1
- package/dist/src/installation/manager.mjs +5 -1
- package/dist/src/models/model-capabilities.v1.json +11 -0
- package/dist/src/models/model-catalog.v1.json +8 -0
- package/dist/src/models/model-discovery.js +32 -0
- package/dist/src/models/provider-policy.js +3 -2
- package/dist/src/output-modes/index.js +4 -0
- package/dist/src/output-modes/registry.js +68 -24
- package/dist/src/output-modes/runtime.js +10 -8
- package/dist/src/plugin/skill-command-translator.js +1 -0
- package/dist/src/providers/capability-matrix.yaml +41 -0
- package/dist/src/providers/provider-definitions.js +72 -0
- package/dist/src/providers/provider-inventory.js +1 -0
- package/dist/src/schema/catalog.js +234 -0
- package/dist/src/schema/compatibility.js +42 -0
- package/dist/src/schema/diagnostics.js +36 -0
- package/dist/src/schema/index.js +8 -0
- package/dist/src/schema/policy.js +58 -0
- package/dist/src/schema/resolver.js +76 -0
- package/dist/src/schema/types.js +2 -0
- package/dist/src/schema/validator.js +82 -0
- package/dist/src/sessions/adapters/pi.js +141 -0
- package/dist/src/sessions/contracts.js +1 -1
- package/dist/src/sessions/index.js +1 -0
- package/dist/src/sessions/workspace-discovery.js +10 -0
- package/dist/src/storage/backends/fortemi.js +6 -0
- package/dist/src/storage/config.js +18 -4
- package/dist/src/storage/fortemi-qualification.js +106 -0
- package/dist/src/storage/index.js +1 -0
- package/dist/src/storage/types.js +1 -1
- package/package.json +3 -1
- package/schemas/dataset/conformance-manifest.v1.schema.json +35 -0
- package/schemas/dataset/conformance-receipt.v1.schema.json +22 -0
- package/schemas/dataset/dataset-contracts.v1.schema.json +117 -0
- package/schemas/dataset/dataset-deprecations.v1.schema.json +37 -0
- package/schemas/dataset/dataset-schema-governance.v1.schema.json +92 -0
- package/schemas/dataset/dataset-standards-exchange.v1.schema.json +59 -0
- package/schemas/dataset/profiles/openlineage-1.0.0.schema.json +15 -0
- package/schemas/dataset/profiles/prov-json-20130430.schema.json +12 -0
- package/schemas/dataset/run-ledger.v1.schema.json +39 -0
- package/schemas/dataset/source-adapter.v1.schema.json +112 -0
- package/tools/agents/deploy-agents.mjs +9 -3
- package/tools/agents/providers/pi.mjs +176 -0
|
@@ -52,6 +52,14 @@
|
|
|
52
52
|
},
|
|
53
53
|
"sourceUrl": "https://opencode.ai/docs/models", "verifiedAt": "2026-07-20"
|
|
54
54
|
},
|
|
55
|
+
"pi": {
|
|
56
|
+
"roles": {
|
|
57
|
+
"reasoning": { "id": "configured/reasoning", "status": "unverified", "observed": false },
|
|
58
|
+
"coding": { "id": "configured/coding", "status": "unverified", "observed": false },
|
|
59
|
+
"efficiency": { "id": "configured/efficiency", "status": "unverified", "observed": false }
|
|
60
|
+
},
|
|
61
|
+
"sourceUrl": "https://github.com/earendil-works/pi/tree/main/packages/coding-agent#providers--models", "verifiedAt": "2026-09-04"
|
|
62
|
+
},
|
|
55
63
|
"warp": {
|
|
56
64
|
"roles": {
|
|
57
65
|
"reasoning": { "id": "profile-selected", "status": "unverified", "observed": false },
|
|
@@ -67,6 +67,13 @@ export const PROVIDER_DISCOVERY_DECISIONS = {
|
|
|
67
67
|
reason: 'OpenHuman profiles accept semantic model hints but expose no standardized local model-list command.',
|
|
68
68
|
documentation: 'https://github.com/roctinam/openhuman',
|
|
69
69
|
},
|
|
70
|
+
pi: {
|
|
71
|
+
provider: 'pi',
|
|
72
|
+
status: 'native',
|
|
73
|
+
interface: 'pi --list-models',
|
|
74
|
+
reason: 'Pi exposes the configured provider/model catalog through a read-only non-interactive table.',
|
|
75
|
+
documentation: 'https://github.com/earendil-works/pi/tree/main/packages/coding-agent#cli-reference',
|
|
76
|
+
},
|
|
70
77
|
warp: {
|
|
71
78
|
provider: 'warp',
|
|
72
79
|
status: 'unsupported',
|
|
@@ -161,6 +168,30 @@ export async function discoverOpenCodeModels(command = 'opencode', runner = runM
|
|
|
161
168
|
: {}),
|
|
162
169
|
};
|
|
163
170
|
}
|
|
171
|
+
export async function discoverPiModels(command = 'pi', runner = runModelDiscoveryCommand) {
|
|
172
|
+
const observedAt = new Date().toISOString();
|
|
173
|
+
const [version, result] = await Promise.all([
|
|
174
|
+
runtimeVersion(command, runner),
|
|
175
|
+
runner(command, ['--list-models'], { cwd: tmpdir(), timeoutMs: 15_000 }),
|
|
176
|
+
]);
|
|
177
|
+
if (result.exitCode !== 0) {
|
|
178
|
+
const error = result.stderr.trim() || `Pi --list-models exited ${result.exitCode}`;
|
|
179
|
+
return { provider: 'pi', source: 'native', observedAt,
|
|
180
|
+
...(version ? { runtimeVersion: version } : {}), accountScope: 'local-runtime',
|
|
181
|
+
models: [], errorKind: classifyDiscoveryError(error), error };
|
|
182
|
+
}
|
|
183
|
+
const lines = result.stdout.split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
|
184
|
+
const models = lines.slice(1).flatMap(line => {
|
|
185
|
+
const columns = line.split(/\s{2,}/);
|
|
186
|
+
if (columns.length < 2 || !/^[a-z0-9][a-z0-9._-]*$/i.test(columns[0]))
|
|
187
|
+
return [];
|
|
188
|
+
return [{ id: `${columns[0]}/${columns[1]}` }];
|
|
189
|
+
});
|
|
190
|
+
return { provider: 'pi', source: 'native', observedAt,
|
|
191
|
+
...(version ? { runtimeVersion: version } : {}), accountScope: 'local-runtime', models,
|
|
192
|
+
...(models.length === 0 ? { errorKind: 'invalid-output',
|
|
193
|
+
error: 'Pi returned no parseable provider/model rows.' } : {}) };
|
|
194
|
+
}
|
|
164
195
|
export async function discoverOpenClawModels(command = 'openclaw', runner = runModelDiscoveryCommand) {
|
|
165
196
|
const observedAt = new Date().toISOString();
|
|
166
197
|
const [version, result] = await Promise.all([
|
|
@@ -455,6 +486,7 @@ export async function resolveDynamicModelCatalog(options) {
|
|
|
455
486
|
codex: () => discoverCodexModels(),
|
|
456
487
|
opencode: () => discoverOpenCodeModels(),
|
|
457
488
|
openclaw: () => discoverOpenClawModels(),
|
|
489
|
+
pi: () => discoverPiModels(),
|
|
458
490
|
};
|
|
459
491
|
const providerDiscovery = {};
|
|
460
492
|
for (const provider of available) {
|
|
@@ -27,7 +27,7 @@ const capabilityData = requireModelResource('model-capabilities.v1.json');
|
|
|
27
27
|
const catalogData = requireModelResource('model-catalog.v1.json');
|
|
28
28
|
const ProviderSchema = z.enum([
|
|
29
29
|
'claude', 'codex', 'copilot', 'cursor', 'factory', 'hermes',
|
|
30
|
-
'opencode', 'openclaw', 'openhuman', 'warp', 'windsurf',
|
|
30
|
+
'opencode', 'openclaw', 'openhuman', 'pi', 'warp', 'windsurf',
|
|
31
31
|
]);
|
|
32
32
|
const OutcomeSchema = z.enum([
|
|
33
33
|
'native', 'compiled', 'inherited', 'global-only', 'informational', 'unsupported',
|
|
@@ -107,7 +107,7 @@ export function loadProviderModelCapabilities() {
|
|
|
107
107
|
const expected = new Set(ProviderSchema.options);
|
|
108
108
|
const actual = new Set(Object.keys(registryCache.providers));
|
|
109
109
|
if (actual.size !== expected.size || [...expected].some(id => !actual.has(id))) {
|
|
110
|
-
throw new Error('Provider model capability registry must cover all
|
|
110
|
+
throw new Error('Provider model capability registry must cover all 12 providers');
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
return registryCache;
|
|
@@ -159,6 +159,7 @@ function fieldNames(provider) {
|
|
|
159
159
|
case 'copilot':
|
|
160
160
|
case 'cursor':
|
|
161
161
|
case 'opencode': return { model: 'model' };
|
|
162
|
+
case 'pi': return { model: 'model', effort: 'thinking' };
|
|
162
163
|
case 'openhuman': return { model: 'model_hint' };
|
|
163
164
|
case 'openclaw': return { model: 'subagents.model' };
|
|
164
165
|
default: return {};
|
|
@@ -1,23 +1,57 @@
|
|
|
1
1
|
import { access, mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { constants } from 'node:fs';
|
|
3
|
-
import {
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import { dirname, extname, join, resolve } from 'node:path';
|
|
6
6
|
import { parse, stringify } from 'yaml';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { resolveUserConfigDir } from '../config/user-config-dir.mjs';
|
|
9
|
+
const MODE_ID_PATTERN = /^[a-z0-9][a-z0-9.-]*$/;
|
|
7
10
|
const PROTECTED = ['code', 'commands', 'citations', 'quoted-text', 'identifiers', 'machine-readable-blocks'];
|
|
8
11
|
const STAGE_ORDER = ['semantic', 'voice', 'controlled-language', 'structure', 'presentation'];
|
|
12
|
+
const modeIdSchema = z.string().regex(MODE_ID_PATTERN, 'must start with a lowercase letter or number and contain only lowercase letters, numbers, dots, or hyphens');
|
|
13
|
+
const uniqueModeIds = z.array(modeIdSchema).refine(values => new Set(values).size === values.length, 'must not contain duplicates');
|
|
14
|
+
const outputModeProfileSchema = z.object({
|
|
15
|
+
id: modeIdSchema,
|
|
16
|
+
version: z.string().min(1),
|
|
17
|
+
description: z.string().min(1),
|
|
18
|
+
kind: z.enum(['voice', 'controlled-language', 'structure', 'presentation']),
|
|
19
|
+
stage: z.enum(STAGE_ORDER),
|
|
20
|
+
order: z.number().int().optional(),
|
|
21
|
+
instructions: z.string(),
|
|
22
|
+
provenance: z.object({ source: z.string().min(1), license: z.string().min(1) }).strict(),
|
|
23
|
+
validation: z.object({
|
|
24
|
+
level: z.enum(['advisory', 'validated', 'conformance']),
|
|
25
|
+
hook: z.string().min(1).optional(),
|
|
26
|
+
standardVersion: z.string().min(1).optional(),
|
|
27
|
+
}).strict(),
|
|
28
|
+
compatible: uniqueModeIds.optional(),
|
|
29
|
+
conflicts: uniqueModeIds.optional(),
|
|
30
|
+
requires: uniqueModeIds.optional(),
|
|
31
|
+
supersedes: uniqueModeIds.optional(),
|
|
32
|
+
protectedContent: z.array(z.enum(PROTECTED)).refine(values => new Set(values).size === values.length, 'must not contain duplicates').optional(),
|
|
33
|
+
contextCost: z.number().int().nonnegative().optional(),
|
|
34
|
+
mergeStrategy: z.literal('weighted-voice').optional(),
|
|
35
|
+
}).strict().superRefine((profile, ctx) => {
|
|
36
|
+
if (profile.validation.level !== 'advisory' && !profile.validation.hook) {
|
|
37
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['validation', 'hook'], message: `${profile.validation.level} modes require a validator hook` });
|
|
38
|
+
}
|
|
39
|
+
if (profile.validation.level === 'conformance' && !profile.validation.standardVersion) {
|
|
40
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['validation', 'standardVersion'], message: 'conformance modes require a standard version' });
|
|
41
|
+
}
|
|
42
|
+
});
|
|
9
43
|
const BUILTINS = [
|
|
10
44
|
{
|
|
11
45
|
id: 'unaltered', version: '1.0.0', description: 'No-op mode; preserves the provider output path unchanged.',
|
|
12
46
|
kind: 'presentation', stage: 'presentation', order: -1000, instructions: '',
|
|
13
47
|
provenance: { source: 'AIWG', license: 'MIT' }, validation: { level: 'advisory' }, contextCost: 0,
|
|
14
|
-
protectedContent: PROTECTED,
|
|
48
|
+
protectedContent: [...PROTECTED],
|
|
15
49
|
},
|
|
16
50
|
{
|
|
17
51
|
id: 'wittgenstein-inspired', version: '1.0.0', description: 'Concise, proposition-oriented stylistic profile; not impersonation or attribution.',
|
|
18
52
|
kind: 'voice', stage: 'voice', order: 100, instructions: 'Prefer concise propositions, clarify terms in use, and expose category errors. Do not imitate or attribute text to Ludwig Wittgenstein.',
|
|
19
53
|
provenance: { source: 'AIWG original style guidance', license: 'MIT' }, validation: { level: 'advisory' }, contextCost: 48,
|
|
20
|
-
protectedContent: PROTECTED,
|
|
54
|
+
protectedContent: [...PROTECTED],
|
|
21
55
|
},
|
|
22
56
|
{
|
|
23
57
|
id: 'asd-ste', version: '1.0.0', description: 'Operator-configured ASD Simplified Technical English adapter.',
|
|
@@ -25,13 +59,13 @@ const BUILTINS = [
|
|
|
25
59
|
instructions: 'Apply only operator-supplied ASD-STE rules and approved terminology. Without licensed rules and a configured validator, describe output as advisory and never claim conformance.',
|
|
26
60
|
provenance: { source: 'AIWG adapter; standard content supplied by operator', license: 'MIT adapter only' },
|
|
27
61
|
validation: { level: 'advisory', standardVersion: 'operator-configured' }, contextCost: 64,
|
|
28
|
-
protectedContent: PROTECTED,
|
|
62
|
+
protectedContent: [...PROTECTED],
|
|
29
63
|
},
|
|
30
64
|
];
|
|
31
65
|
function profileDirs(cwd) {
|
|
32
66
|
return [
|
|
33
67
|
{ dir: join(cwd, '.aiwg', 'output-modes'), source: 'project' },
|
|
34
|
-
{ dir: join(
|
|
68
|
+
{ dir: join(resolveUserConfigDir(), 'output-modes'), source: 'user' },
|
|
35
69
|
];
|
|
36
70
|
}
|
|
37
71
|
async function readable(path) {
|
|
@@ -43,31 +77,33 @@ async function readable(path) {
|
|
|
43
77
|
return false;
|
|
44
78
|
}
|
|
45
79
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
80
|
+
export function validateOutputModeProfile(value, path = '<profile>') {
|
|
81
|
+
const result = outputModeProfileSchema.safeParse(value);
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
const details = result.error.issues
|
|
84
|
+
.map(issue => `${issue.path.length ? issue.path.join('.') : 'profile'}: ${issue.message}`)
|
|
85
|
+
.join('; ');
|
|
86
|
+
throw new Error(`Invalid output mode profile at ${path}: ${details}`);
|
|
53
87
|
}
|
|
54
|
-
|
|
55
|
-
throw new Error(`Invalid output mode kind in ${path}: ${p.kind}`);
|
|
56
|
-
if (!STAGE_ORDER.includes(String(p.stage)))
|
|
57
|
-
throw new Error(`Invalid output mode stage in ${path}: ${p.stage}`);
|
|
58
|
-
return p;
|
|
88
|
+
return result.data;
|
|
59
89
|
}
|
|
60
90
|
async function loadDirectory(dir, source) {
|
|
61
91
|
if (!(await readable(dir)))
|
|
62
92
|
return [];
|
|
63
93
|
const result = [];
|
|
94
|
+
const seen = new Map();
|
|
64
95
|
for (const name of (await readdir(dir)).sort()) {
|
|
65
96
|
if (!['.yaml', '.yml', '.json'].includes(extname(name)))
|
|
66
97
|
continue;
|
|
67
98
|
const sourcePath = join(dir, name);
|
|
68
99
|
const raw = await readFile(sourcePath, 'utf8');
|
|
69
100
|
const value = extname(name) === '.json' ? JSON.parse(raw) : parse(raw);
|
|
70
|
-
|
|
101
|
+
const profile = validateOutputModeProfile(value, sourcePath);
|
|
102
|
+
const previous = seen.get(profile.id);
|
|
103
|
+
if (previous)
|
|
104
|
+
throw new Error(`Duplicate output mode '${profile.id}' in ${previous} and ${sourcePath}. Keep one definition per scope.`);
|
|
105
|
+
seen.set(profile.id, sourcePath);
|
|
106
|
+
result.push({ ...profile, source, sourcePath });
|
|
71
107
|
}
|
|
72
108
|
return result;
|
|
73
109
|
}
|
|
@@ -84,7 +120,7 @@ async function loadVoiceAdapters(frameworkRoot) {
|
|
|
84
120
|
id, version: String(voice.version ?? '1.0.0'), description: String(voice.description ?? `Adapted voice profile: ${id}`),
|
|
85
121
|
kind: 'voice', stage: 'voice', order: 100, instructions: `Apply the existing voice profile '${id}' through voice-apply.`,
|
|
86
122
|
provenance: { source: sourcePath, license: String(voice.license ?? 'project license') },
|
|
87
|
-
validation: { level: 'advisory' }, protectedContent: PROTECTED, contextCost: 32,
|
|
123
|
+
validation: { level: 'advisory' }, protectedContent: [...PROTECTED], contextCost: 32,
|
|
88
124
|
mergeStrategy: 'weighted-voice', source: 'voice-adapter', sourcePath,
|
|
89
125
|
});
|
|
90
126
|
}
|
|
@@ -115,7 +151,10 @@ export async function readOutputModeState(cwd, scope) {
|
|
|
115
151
|
if (!(await readable(path)))
|
|
116
152
|
return { version: 1, modes: [] };
|
|
117
153
|
const value = parse(await readFile(path, 'utf8'));
|
|
118
|
-
|
|
154
|
+
if (!value || value.version !== 1 || !Array.isArray(value.modes) || value.modes.some(mode => typeof mode !== 'string' || !MODE_ID_PATTERN.test(mode))) {
|
|
155
|
+
throw new Error(`Invalid output mode state at ${path}: expected version 1 and an array of valid mode IDs.`);
|
|
156
|
+
}
|
|
157
|
+
return { version: 1, modes: [...new Set(value.modes)] };
|
|
119
158
|
}
|
|
120
159
|
export async function writeOutputModeState(cwd, scope, modes) {
|
|
121
160
|
const path = statePath(cwd, scope);
|
|
@@ -123,11 +162,16 @@ export async function writeOutputModeState(cwd, scope, modes) {
|
|
|
123
162
|
await writeFile(path, stringify({ version: 1, modes }), 'utf8');
|
|
124
163
|
return path;
|
|
125
164
|
}
|
|
126
|
-
export async function resolveOutputModes(cwd, frameworkRoot, invocation = []) {
|
|
165
|
+
export async function resolveOutputModes(cwd, frameworkRoot, invocation = [], overrides = {}) {
|
|
127
166
|
const registry = await loadOutputModeRegistry(cwd, frameworkRoot);
|
|
128
|
-
const project = await readOutputModeState(cwd, 'project');
|
|
129
|
-
const session = await readOutputModeState(cwd, 'session');
|
|
130
|
-
const
|
|
167
|
+
const project = overrides.project ?? (await readOutputModeState(cwd, 'project')).modes;
|
|
168
|
+
const session = overrides.session ?? (await readOutputModeState(cwd, 'session')).modes;
|
|
169
|
+
for (const [scope, modes] of [['project', project], ['session', session], ['invocation', invocation]]) {
|
|
170
|
+
if (!Array.isArray(modes) || modes.some(id => typeof id !== 'string' || !MODE_ID_PATTERN.test(id))) {
|
|
171
|
+
throw new Error(`Invalid ${scope} output mode selection: expected valid mode IDs.`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const selected = [...project.map(id => ({ id, scope: 'project' })), ...session.map(id => ({ id, scope: 'session' })), ...invocation.map(id => ({ id, scope: 'invocation' }))];
|
|
131
175
|
const effective = new Map();
|
|
132
176
|
const diagnostics = [];
|
|
133
177
|
for (const item of selected) {
|
|
@@ -16,8 +16,11 @@ function protectedPattern(classes) {
|
|
|
16
16
|
function protect(content, classes) {
|
|
17
17
|
const literals = [];
|
|
18
18
|
const pattern = protectedPattern(classes);
|
|
19
|
+
let tokenPrefix = '\uE000AIWG_OUTPUT_MODE_';
|
|
20
|
+
while (content.includes(tokenPrefix))
|
|
21
|
+
tokenPrefix += '_';
|
|
19
22
|
const protectedContent = pattern ? content.replace(pattern, value => {
|
|
20
|
-
const token =
|
|
23
|
+
const token = `${tokenPrefix}${literals.length}\uE001`;
|
|
21
24
|
literals.push({ token, value });
|
|
22
25
|
return token;
|
|
23
26
|
}) : content;
|
|
@@ -26,9 +29,10 @@ function protect(content, classes) {
|
|
|
26
29
|
function restore(content, literals, mode) {
|
|
27
30
|
let restored = content;
|
|
28
31
|
for (const literal of literals) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
const occurrences = restored.split(literal.token).length - 1;
|
|
33
|
+
if (occurrences !== 1)
|
|
34
|
+
throw new Error(`Output mode '${mode}' modified, removed, or duplicated a protected literal.`);
|
|
35
|
+
restored = restored.replace(literal.token, literal.value);
|
|
32
36
|
}
|
|
33
37
|
return restored;
|
|
34
38
|
}
|
|
@@ -39,9 +43,10 @@ export async function applyOutputModes(input, modes, options) {
|
|
|
39
43
|
const diagnostics = [];
|
|
40
44
|
const applied = [];
|
|
41
45
|
for (const mode of modes) {
|
|
42
|
-
const snapshot = content;
|
|
43
46
|
const masked = protect(content, mode.protectedContent ?? []);
|
|
44
47
|
const transformed = await options.transform(masked.content, mode);
|
|
48
|
+
if (typeof transformed !== 'string')
|
|
49
|
+
throw new Error(`Output mode '${mode.id}' transform returned a non-string result.`);
|
|
45
50
|
content = restore(transformed, masked.literals, mode.id);
|
|
46
51
|
if (mode.validation.level !== 'advisory') {
|
|
47
52
|
if (!options.validate)
|
|
@@ -54,9 +59,6 @@ export async function applyOutputModes(input, modes, options) {
|
|
|
54
59
|
return { content: input, diagnostics, applied, fallback: 'unaltered' };
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
|
-
// A transform may only change semantic presentation, never return an absent result.
|
|
58
|
-
if (typeof content !== 'string')
|
|
59
|
-
content = snapshot;
|
|
60
62
|
applied.push(mode.id);
|
|
61
63
|
}
|
|
62
64
|
return { content, diagnostics, applied, fallback: 'none' };
|
|
@@ -470,6 +470,47 @@ providers:
|
|
|
470
470
|
deploy_target: mixed
|
|
471
471
|
aggregated_output: true
|
|
472
472
|
|
|
473
|
+
pi:
|
|
474
|
+
display_name: Pi Coding Agent
|
|
475
|
+
aliases:
|
|
476
|
+
- pi-coding-agent
|
|
477
|
+
status: experimental
|
|
478
|
+
daemon_tier: unsupported
|
|
479
|
+
daemon_pty_adapter: false
|
|
480
|
+
artifact_paths:
|
|
481
|
+
agents: .agents/skills/
|
|
482
|
+
commands: .pi/prompts/
|
|
483
|
+
skills: .pi/skills/
|
|
484
|
+
skills_cross_agent: .agents/skills/
|
|
485
|
+
rules: AGENTS.md
|
|
486
|
+
behaviors: .pi/extensions/
|
|
487
|
+
native_features:
|
|
488
|
+
cron: false
|
|
489
|
+
agent_teams: false
|
|
490
|
+
tasks: false
|
|
491
|
+
mcp: false
|
|
492
|
+
behaviors: true
|
|
493
|
+
mission_control: false
|
|
494
|
+
daemon: false
|
|
495
|
+
emulation:
|
|
496
|
+
cron: external-trigger
|
|
497
|
+
agent_teams: aiwg-mc
|
|
498
|
+
tasks: aiwg-mc
|
|
499
|
+
mcp: null
|
|
500
|
+
behaviors: null
|
|
501
|
+
mission_control: aiwg-mc
|
|
502
|
+
daemon: null
|
|
503
|
+
hook_wiring:
|
|
504
|
+
at_link_support: false
|
|
505
|
+
fallback: full-inject
|
|
506
|
+
context_file: AGENTS.md
|
|
507
|
+
interaction:
|
|
508
|
+
structured_questions: none
|
|
509
|
+
fallback: markdown
|
|
510
|
+
notes: Pi does not expose a built-in structured question tool.
|
|
511
|
+
deploy_target: project
|
|
512
|
+
aggregated_output: false
|
|
513
|
+
|
|
473
514
|
# Feature definitions — documents what each feature key means and
|
|
474
515
|
# what emulation strategies are available when native support is absent.
|
|
475
516
|
features:
|
|
@@ -21,6 +21,7 @@ const ProviderDefinitionSchema = z.object({
|
|
|
21
21
|
'opencode',
|
|
22
22
|
'openclaw',
|
|
23
23
|
'openhuman',
|
|
24
|
+
'pi',
|
|
24
25
|
'warp',
|
|
25
26
|
'windsurf',
|
|
26
27
|
'generic',
|
|
@@ -131,6 +132,7 @@ export const PROVIDER_IDS = [
|
|
|
131
132
|
'opencode',
|
|
132
133
|
'openclaw',
|
|
133
134
|
'openhuman',
|
|
135
|
+
'pi',
|
|
134
136
|
'warp',
|
|
135
137
|
'windsurf',
|
|
136
138
|
'generic',
|
|
@@ -191,6 +193,17 @@ const CONTEXT_CONTRACTS = {
|
|
|
191
193
|
bootstrapTargets: ['AGENTS.md'], maxContextBytes: null, recommendedMaxLines: null, nestedContext: false, support: 'degraded',
|
|
192
194
|
verification: { method: 'AIWG mixed-scope adapter contract; host loading remains capability-dependent', source: 'agentic/code/providers/capability-matrix.yaml', lastVerified: VERIFIED_ON },
|
|
193
195
|
},
|
|
196
|
+
pi: {
|
|
197
|
+
startupFiles: ['AGENTS.override.md', 'AGENTS.md', 'CLAUDE.md'],
|
|
198
|
+
precedence: ['provider/system', 'root-to-cwd context chain', 'AGENTS.override.md supersedes same-directory AGENTS.md and CLAUDE.md'],
|
|
199
|
+
loadMode: 'prose-directive', includeSyntax: null, configRegistration: null,
|
|
200
|
+
bootstrapTargets: ['AGENTS.md'], maxContextBytes: null, recommendedMaxLines: null, nestedContext: true, support: 'supported',
|
|
201
|
+
verification: {
|
|
202
|
+
method: 'Pi coding-agent resource loader and official README context-file documentation',
|
|
203
|
+
source: 'https://github.com/earendil-works/pi/blob/79680533c6b898894f2d2421c7f640b212d3dfdd/packages/coding-agent/README.md#context-files',
|
|
204
|
+
lastVerified: '2026-09-03',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
194
207
|
warp: {
|
|
195
208
|
startupFiles: ['WARP.md', 'AGENTS.md'], precedence: ['provider/system', 'subdirectory rule', 'root rule', 'global rule'],
|
|
196
209
|
loadMode: 'prose-directive', includeSyntax: null, configRegistration: null,
|
|
@@ -656,6 +669,65 @@ const BUILT_IN_SEEDS = [
|
|
|
656
669
|
},
|
|
657
670
|
matrixRef: 'openhuman',
|
|
658
671
|
},
|
|
672
|
+
{
|
|
673
|
+
id: 'pi',
|
|
674
|
+
aliases: ['pi-coding-agent'],
|
|
675
|
+
builtIn: true,
|
|
676
|
+
surfaces: {
|
|
677
|
+
primary: 'pi',
|
|
678
|
+
compatibility: ['pi-coding-agent'],
|
|
679
|
+
precedence: ['AGENTS.override.md', 'AGENTS.md', '.agents/skills/', '.pi/skills/', '.pi/prompts/'],
|
|
680
|
+
related: [],
|
|
681
|
+
},
|
|
682
|
+
detection: {
|
|
683
|
+
// PI_CODING_AGENT_DIR only relocates configuration; it is deliberately
|
|
684
|
+
// not an active-runtime marker. Availability is proven by process or executable.
|
|
685
|
+
env: [],
|
|
686
|
+
process: ['pi'],
|
|
687
|
+
capabilityId: 'pi',
|
|
688
|
+
},
|
|
689
|
+
paths: {
|
|
690
|
+
artifacts: {
|
|
691
|
+
agents: '.agents/skills',
|
|
692
|
+
commands: '.pi/prompts',
|
|
693
|
+
skills: '.pi/.aiwg/skills',
|
|
694
|
+
rules: null,
|
|
695
|
+
behaviors: '.pi/extensions',
|
|
696
|
+
},
|
|
697
|
+
kernelSkills: '.agents/skills',
|
|
698
|
+
contextDiscovery: {
|
|
699
|
+
agents: '.agents/skills',
|
|
700
|
+
skills: '.agents/skills',
|
|
701
|
+
rules: null,
|
|
702
|
+
behaviors: '.pi/extensions',
|
|
703
|
+
},
|
|
704
|
+
configFile: 'AGENTS.md',
|
|
705
|
+
contextFiles: { aiwgMd: true, agentsMd: true, claudeMdHook: false, hookFile: null, contextFile: 'AGENTS.md' },
|
|
706
|
+
},
|
|
707
|
+
smithPaths: {
|
|
708
|
+
agents: '.agents/skills',
|
|
709
|
+
commands: '.pi/prompts',
|
|
710
|
+
skills: '.pi/skills',
|
|
711
|
+
rules: null,
|
|
712
|
+
fileExtension: '.md',
|
|
713
|
+
configFile: 'AGENTS.md',
|
|
714
|
+
aggregated: false,
|
|
715
|
+
},
|
|
716
|
+
skillNamespace: {
|
|
717
|
+
deploymentGroup: 'deep-recursion',
|
|
718
|
+
pathType: 'project',
|
|
719
|
+
skillsBaseDir: '.pi/skills',
|
|
720
|
+
subdirLayout: true,
|
|
721
|
+
},
|
|
722
|
+
adapters: {
|
|
723
|
+
agentFormat: 'agents-md',
|
|
724
|
+
hookBridge: null,
|
|
725
|
+
mcpInjection: null,
|
|
726
|
+
contextAggregation: 'agents-md',
|
|
727
|
+
ruleFormat: 'agents-md-section',
|
|
728
|
+
},
|
|
729
|
+
matrixRef: 'pi',
|
|
730
|
+
},
|
|
659
731
|
{
|
|
660
732
|
id: 'warp',
|
|
661
733
|
aliases: [],
|