takomi 2.1.2 → 2.1.4
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/.pi/README.md +124 -124
- package/.pi/agents/architect.md +15 -15
- package/.pi/agents/coder.md +14 -14
- package/.pi/agents/designer.md +17 -17
- package/.pi/agents/orchestrator.md +22 -22
- package/.pi/agents/reviewer.md +16 -16
- package/.pi/extensions/oauth-router/README.md +125 -125
- package/.pi/extensions/oauth-router/commands.ts +380 -380
- package/.pi/extensions/oauth-router/config.ts +200 -200
- package/.pi/extensions/oauth-router/index.ts +41 -41
- package/.pi/extensions/oauth-router/oauth-flow.ts +154 -154
- package/.pi/extensions/oauth-router/oauth-store.ts +121 -121
- package/.pi/extensions/oauth-router/package.json +14 -14
- package/.pi/extensions/oauth-router/policies.ts +27 -27
- package/.pi/extensions/oauth-router/provider.ts +492 -492
- package/.pi/extensions/oauth-router/scripts/vibe-verify.py +98 -98
- package/.pi/extensions/oauth-router/state.ts +174 -174
- package/.pi/extensions/oauth-router/types.ts +153 -153
- package/.pi/extensions/takomi-runtime/command-text.ts +130 -130
- package/.pi/extensions/takomi-runtime/commands.ts +179 -179
- package/.pi/extensions/takomi-runtime/context-panel.ts +282 -282
- package/.pi/extensions/takomi-runtime/index.ts +1288 -1288
- package/.pi/extensions/takomi-runtime/profile.ts +114 -114
- package/.pi/extensions/takomi-runtime/routing-policy.ts +105 -105
- package/.pi/extensions/takomi-runtime/shared.ts +511 -492
- package/.pi/extensions/takomi-runtime/subagent-controller.ts +364 -364
- package/.pi/extensions/takomi-runtime/subagent-render.ts +501 -501
- package/.pi/extensions/takomi-runtime/subagent-types.ts +90 -83
- package/.pi/extensions/takomi-runtime/ui.ts +133 -133
- package/.pi/extensions/takomi-subagents/agent-aliases.ts +18 -18
- package/.pi/extensions/takomi-subagents/agents.ts +113 -113
- package/.pi/extensions/takomi-subagents/delegation-plan.ts +95 -95
- package/.pi/extensions/takomi-subagents/dispatch-helpers.ts +26 -26
- package/.pi/extensions/takomi-subagents/dispatch.ts +306 -215
- package/.pi/extensions/takomi-subagents/index.ts +76 -75
- package/.pi/extensions/takomi-subagents/live-updates.ts +136 -83
- package/.pi/extensions/takomi-subagents/native-render.ts +5 -142
- package/.pi/extensions/takomi-subagents/pi-subagents-engine.ts +228 -0
- package/.pi/extensions/takomi-subagents/tool-runner.ts +209 -209
- package/.pi/themes/takomi-noir.json +81 -81
- package/package.json +59 -59
- package/src/cli.js +14 -0
- package/src/doctor.js +87 -84
- package/src/pi-harness.js +355 -351
- package/src/pi-installer.js +193 -171
- package/src/pi-takomi-core/index.ts +4 -4
- package/src/pi-takomi-core/orchestration.ts +402 -402
- package/src/pi-takomi-core/routing.ts +93 -93
- package/src/pi-takomi-core/types.ts +173 -173
- package/src/pi-takomi-core/workflows.ts +299 -299
- package/src/skills-installer.js +101 -101
- package/src/update-check.js +140 -0
package/src/pi-harness.js
CHANGED
|
@@ -1,351 +1,355 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { spawn, spawnSync } from 'child_process';
|
|
5
|
-
import pc from 'picocolors';
|
|
6
|
-
import { PATHS } from './utils.js';
|
|
7
|
-
import { STORE_PATH, MANIFEST_PATH } from './store.js';
|
|
8
|
-
|
|
9
|
-
const TAKOMI_PACKAGE_DIR = PATHS.root;
|
|
10
|
-
|
|
11
|
-
const HOME = os.homedir();
|
|
12
|
-
|
|
13
|
-
function getPathEntries() {
|
|
14
|
-
return (process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function commandExists(command) {
|
|
18
|
-
const probe = process.platform === 'win32'
|
|
19
|
-
? spawnSync('where', [command], { stdio: 'pipe', encoding: 'utf8' })
|
|
20
|
-
: spawnSync('which', [command], { stdio: 'pipe', encoding: 'utf8' });
|
|
21
|
-
|
|
22
|
-
if (probe.status === 0) {
|
|
23
|
-
const matches = probe.stdout.split(/\r?\n/).map(v => v.trim()).filter(Boolean);
|
|
24
|
-
const preferred = process.platform === 'win32'
|
|
25
|
-
? matches.find((entry) => /\.(cmd|exe|bat)$/i.test(entry))
|
|
26
|
-
: matches[0];
|
|
27
|
-
return { found: true, path: preferred || matches[0] || null };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
for (const entry of getPathEntries()) {
|
|
31
|
-
const candidate = path.join(entry, process.platform === 'win32' ? `${command}.cmd` : command);
|
|
32
|
-
if (fs.existsSync(candidate)) return { found: true, path: candidate };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return { found: false, path: null };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function getPiAgentRoot(home = HOME) {
|
|
39
|
-
return path.join(home, '.pi', 'agent');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function getPiGlobalTargets(home = HOME) {
|
|
43
|
-
const agentRoot = getPiAgentRoot(home);
|
|
44
|
-
return {
|
|
45
|
-
root: agentRoot,
|
|
46
|
-
extensions: path.join(agentRoot, 'extensions'),
|
|
47
|
-
prompts: path.join(agentRoot, 'prompts'),
|
|
48
|
-
agents: path.join(agentRoot, 'agents'),
|
|
49
|
-
themes: path.join(agentRoot, 'themes'),
|
|
50
|
-
settings: path.join(agentRoot, 'settings.json'),
|
|
51
|
-
takomi: path.join(agentRoot, 'takomi'),
|
|
52
|
-
routingPolicy: path.join(agentRoot, 'takomi', 'model-routing.md'),
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function getProjectPiTargets(cwd = process.cwd()) {
|
|
57
|
-
return {
|
|
58
|
-
root: path.join(cwd, '.pi'),
|
|
59
|
-
extensions: path.join(cwd, '.pi', 'extensions'),
|
|
60
|
-
prompts: path.join(cwd, '.pi', 'prompts'),
|
|
61
|
-
agents: path.join(cwd, '.pi', 'agents'),
|
|
62
|
-
themes: path.join(cwd, '.pi', 'themes'),
|
|
63
|
-
settings: path.join(cwd, '.pi', 'settings.json'),
|
|
64
|
-
takomiProfile: path.join(cwd, '.pi', 'takomi-profile.json'),
|
|
65
|
-
routingPolicy: path.join(cwd, '.pi', 'takomi', 'model-routing.md'),
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function getBundledPiAssetTargets() {
|
|
70
|
-
const root = path.join(PATHS.root, '.pi');
|
|
71
|
-
return {
|
|
72
|
-
root,
|
|
73
|
-
runtime: path.join(root, 'extensions', 'takomi-runtime'),
|
|
74
|
-
subagents: path.join(root, 'extensions', 'takomi-subagents'),
|
|
75
|
-
prompts: path.join(root, 'prompts'),
|
|
76
|
-
agents: path.join(root, 'agents'),
|
|
77
|
-
themes: path.join(root, 'themes'),
|
|
78
|
-
settings: path.join(root, 'settings.json'),
|
|
79
|
-
routingPolicy: path.join(root, 'takomi', 'model-routing.md'),
|
|
80
|
-
profile: path.join(root, 'takomi-profile.json'),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function getPackageJson() {
|
|
85
|
-
try {
|
|
86
|
-
return await fs.readJson(PATHS.packageJson);
|
|
87
|
-
} catch {
|
|
88
|
-
return {};
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function getPackageFileEntries() {
|
|
93
|
-
const pkg = await getPackageJson();
|
|
94
|
-
return Array.isArray(pkg.files) ? pkg.files : [];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function getGlobalNodeModulesRoot(home = HOME) {
|
|
98
|
-
if (process.platform === 'win32') {
|
|
99
|
-
return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'npm', 'node_modules');
|
|
100
|
-
}
|
|
101
|
-
return path.join(home, '.npm-global', 'lib', 'node_modules');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export async function inspectPiSubagentsDependency(home = HOME) {
|
|
105
|
-
const pkg = await getPackageJson();
|
|
106
|
-
const declaredVersion = pkg?.dependencies?.['pi-subagents'] || null;
|
|
107
|
-
const localPackageJson = path.join(TAKOMI_PACKAGE_DIR, 'node_modules', 'pi-subagents', 'package.json');
|
|
108
|
-
const globalPackageJson = path.join(getGlobalNodeModulesRoot(home), 'pi-subagents', 'package.json');
|
|
109
|
-
|
|
110
|
-
let localVersion = null;
|
|
111
|
-
let globalVersion = null;
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
if (await fs.pathExists(localPackageJson)) {
|
|
115
|
-
const localPkg = await fs.readJson(localPackageJson);
|
|
116
|
-
localVersion = localPkg.version || null;
|
|
117
|
-
}
|
|
118
|
-
} catch {}
|
|
119
|
-
|
|
120
|
-
try {
|
|
121
|
-
if (await fs.pathExists(globalPackageJson)) {
|
|
122
|
-
const globalPkg = await fs.readJson(globalPackageJson);
|
|
123
|
-
globalVersion = globalPkg.version || null;
|
|
124
|
-
}
|
|
125
|
-
} catch {}
|
|
126
|
-
|
|
127
|
-
const bin = commandExists('pi-subagents');
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
declaredVersion,
|
|
131
|
-
localInstalled: Boolean(localVersion),
|
|
132
|
-
localVersion,
|
|
133
|
-
localPackageJson,
|
|
134
|
-
globalInstalled: Boolean(globalVersion),
|
|
135
|
-
globalVersion,
|
|
136
|
-
globalPackageJson,
|
|
137
|
-
binaryInstalled: bin.found,
|
|
138
|
-
binaryPath: bin.path,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export async function detectPiCommand() {
|
|
143
|
-
const binary = commandExists('pi');
|
|
144
|
-
if (!binary.found) {
|
|
145
|
-
return { installed: false, path: null, version: null };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const versionProbe = process.platform === 'win32'
|
|
149
|
-
? spawnSync('powershell', ['-NoProfile', '-Command', `& '${(binary.path || 'pi').replace(/'/g, "''")}' --version`], { stdio: 'pipe', encoding: 'utf8' })
|
|
150
|
-
: spawnSync(binary.path || 'pi', ['--version'], { stdio: 'pipe', encoding: 'utf8' });
|
|
151
|
-
const output = [versionProbe.stdout, versionProbe.stderr].filter(Boolean).join('\n').trim();
|
|
152
|
-
return {
|
|
153
|
-
installed: true,
|
|
154
|
-
path: binary.path,
|
|
155
|
-
version: versionProbe.status === 0 ? (output || null) : null,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export async function inspectBundledPiAssets() {
|
|
160
|
-
const targets = getBundledPiAssetTargets();
|
|
161
|
-
const checks = {
|
|
162
|
-
root: await fs.pathExists(targets.root),
|
|
163
|
-
runtime: await fs.pathExists(targets.runtime),
|
|
164
|
-
subagents: await fs.pathExists(targets.subagents),
|
|
165
|
-
prompts: await fs.pathExists(targets.prompts),
|
|
166
|
-
agents: await fs.pathExists(targets.agents),
|
|
167
|
-
themes: await fs.pathExists(targets.themes),
|
|
168
|
-
settings: await fs.pathExists(targets.settings),
|
|
169
|
-
routingPolicy: await fs.pathExists(targets.routingPolicy),
|
|
170
|
-
profile: await fs.pathExists(targets.profile),
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const packageReady = await fs.pathExists(targets.root);
|
|
174
|
-
const packageFiles = await getPackageFileEntries();
|
|
175
|
-
const packageIncluded = packageFiles.includes('.pi') || packageFiles.some((entry) => entry.startsWith('.pi/')) || packageReady;
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
targets,
|
|
179
|
-
checks,
|
|
180
|
-
packageReady,
|
|
181
|
-
packageIncluded,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export async function inspectInstalledTakomiPiHarness(home = HOME) {
|
|
186
|
-
const targets = getPiGlobalTargets(home);
|
|
187
|
-
const runtime = path.join(targets.extensions, 'takomi-runtime');
|
|
188
|
-
const subagents = path.join(targets.extensions, 'takomi-subagents');
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
{
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
{
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { spawn, spawnSync } from 'child_process';
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
import { PATHS } from './utils.js';
|
|
7
|
+
import { STORE_PATH, MANIFEST_PATH } from './store.js';
|
|
8
|
+
|
|
9
|
+
const TAKOMI_PACKAGE_DIR = PATHS.root;
|
|
10
|
+
|
|
11
|
+
const HOME = os.homedir();
|
|
12
|
+
|
|
13
|
+
function getPathEntries() {
|
|
14
|
+
return (process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function commandExists(command) {
|
|
18
|
+
const probe = process.platform === 'win32'
|
|
19
|
+
? spawnSync('where', [command], { stdio: 'pipe', encoding: 'utf8' })
|
|
20
|
+
: spawnSync('which', [command], { stdio: 'pipe', encoding: 'utf8' });
|
|
21
|
+
|
|
22
|
+
if (probe.status === 0) {
|
|
23
|
+
const matches = probe.stdout.split(/\r?\n/).map(v => v.trim()).filter(Boolean);
|
|
24
|
+
const preferred = process.platform === 'win32'
|
|
25
|
+
? matches.find((entry) => /\.(cmd|exe|bat)$/i.test(entry))
|
|
26
|
+
: matches[0];
|
|
27
|
+
return { found: true, path: preferred || matches[0] || null };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const entry of getPathEntries()) {
|
|
31
|
+
const candidate = path.join(entry, process.platform === 'win32' ? `${command}.cmd` : command);
|
|
32
|
+
if (fs.existsSync(candidate)) return { found: true, path: candidate };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { found: false, path: null };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getPiAgentRoot(home = HOME) {
|
|
39
|
+
return path.join(home, '.pi', 'agent');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getPiGlobalTargets(home = HOME) {
|
|
43
|
+
const agentRoot = getPiAgentRoot(home);
|
|
44
|
+
return {
|
|
45
|
+
root: agentRoot,
|
|
46
|
+
extensions: path.join(agentRoot, 'extensions'),
|
|
47
|
+
prompts: path.join(agentRoot, 'prompts'),
|
|
48
|
+
agents: path.join(agentRoot, 'agents'),
|
|
49
|
+
themes: path.join(agentRoot, 'themes'),
|
|
50
|
+
settings: path.join(agentRoot, 'settings.json'),
|
|
51
|
+
takomi: path.join(agentRoot, 'takomi'),
|
|
52
|
+
routingPolicy: path.join(agentRoot, 'takomi', 'model-routing.md'),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function getProjectPiTargets(cwd = process.cwd()) {
|
|
57
|
+
return {
|
|
58
|
+
root: path.join(cwd, '.pi'),
|
|
59
|
+
extensions: path.join(cwd, '.pi', 'extensions'),
|
|
60
|
+
prompts: path.join(cwd, '.pi', 'prompts'),
|
|
61
|
+
agents: path.join(cwd, '.pi', 'agents'),
|
|
62
|
+
themes: path.join(cwd, '.pi', 'themes'),
|
|
63
|
+
settings: path.join(cwd, '.pi', 'settings.json'),
|
|
64
|
+
takomiProfile: path.join(cwd, '.pi', 'takomi-profile.json'),
|
|
65
|
+
routingPolicy: path.join(cwd, '.pi', 'takomi', 'model-routing.md'),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getBundledPiAssetTargets() {
|
|
70
|
+
const root = path.join(PATHS.root, '.pi');
|
|
71
|
+
return {
|
|
72
|
+
root,
|
|
73
|
+
runtime: path.join(root, 'extensions', 'takomi-runtime'),
|
|
74
|
+
subagents: path.join(root, 'extensions', 'takomi-subagents'),
|
|
75
|
+
prompts: path.join(root, 'prompts'),
|
|
76
|
+
agents: path.join(root, 'agents'),
|
|
77
|
+
themes: path.join(root, 'themes'),
|
|
78
|
+
settings: path.join(root, 'settings.json'),
|
|
79
|
+
routingPolicy: path.join(root, 'takomi', 'model-routing.md'),
|
|
80
|
+
profile: path.join(root, 'takomi-profile.json'),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function getPackageJson() {
|
|
85
|
+
try {
|
|
86
|
+
return await fs.readJson(PATHS.packageJson);
|
|
87
|
+
} catch {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function getPackageFileEntries() {
|
|
93
|
+
const pkg = await getPackageJson();
|
|
94
|
+
return Array.isArray(pkg.files) ? pkg.files : [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getGlobalNodeModulesRoot(home = HOME) {
|
|
98
|
+
if (process.platform === 'win32') {
|
|
99
|
+
return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'npm', 'node_modules');
|
|
100
|
+
}
|
|
101
|
+
return path.join(home, '.npm-global', 'lib', 'node_modules');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function inspectPiSubagentsDependency(home = HOME) {
|
|
105
|
+
const pkg = await getPackageJson();
|
|
106
|
+
const declaredVersion = pkg?.dependencies?.['pi-subagents'] || null;
|
|
107
|
+
const localPackageJson = path.join(TAKOMI_PACKAGE_DIR, 'node_modules', 'pi-subagents', 'package.json');
|
|
108
|
+
const globalPackageJson = path.join(getGlobalNodeModulesRoot(home), 'pi-subagents', 'package.json');
|
|
109
|
+
|
|
110
|
+
let localVersion = null;
|
|
111
|
+
let globalVersion = null;
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
if (await fs.pathExists(localPackageJson)) {
|
|
115
|
+
const localPkg = await fs.readJson(localPackageJson);
|
|
116
|
+
localVersion = localPkg.version || null;
|
|
117
|
+
}
|
|
118
|
+
} catch {}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
if (await fs.pathExists(globalPackageJson)) {
|
|
122
|
+
const globalPkg = await fs.readJson(globalPackageJson);
|
|
123
|
+
globalVersion = globalPkg.version || null;
|
|
124
|
+
}
|
|
125
|
+
} catch {}
|
|
126
|
+
|
|
127
|
+
const bin = commandExists('pi-subagents');
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
declaredVersion,
|
|
131
|
+
localInstalled: Boolean(localVersion),
|
|
132
|
+
localVersion,
|
|
133
|
+
localPackageJson,
|
|
134
|
+
globalInstalled: Boolean(globalVersion),
|
|
135
|
+
globalVersion,
|
|
136
|
+
globalPackageJson,
|
|
137
|
+
binaryInstalled: bin.found,
|
|
138
|
+
binaryPath: bin.path,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function detectPiCommand() {
|
|
143
|
+
const binary = commandExists('pi');
|
|
144
|
+
if (!binary.found) {
|
|
145
|
+
return { installed: false, path: null, version: null };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const versionProbe = process.platform === 'win32'
|
|
149
|
+
? spawnSync('powershell', ['-NoProfile', '-Command', `& '${(binary.path || 'pi').replace(/'/g, "''")}' --version`], { stdio: 'pipe', encoding: 'utf8' })
|
|
150
|
+
: spawnSync(binary.path || 'pi', ['--version'], { stdio: 'pipe', encoding: 'utf8' });
|
|
151
|
+
const output = [versionProbe.stdout, versionProbe.stderr].filter(Boolean).join('\n').trim();
|
|
152
|
+
return {
|
|
153
|
+
installed: true,
|
|
154
|
+
path: binary.path,
|
|
155
|
+
version: versionProbe.status === 0 ? (output || null) : null,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function inspectBundledPiAssets() {
|
|
160
|
+
const targets = getBundledPiAssetTargets();
|
|
161
|
+
const checks = {
|
|
162
|
+
root: await fs.pathExists(targets.root),
|
|
163
|
+
runtime: await fs.pathExists(targets.runtime),
|
|
164
|
+
subagents: await fs.pathExists(targets.subagents),
|
|
165
|
+
prompts: await fs.pathExists(targets.prompts),
|
|
166
|
+
agents: await fs.pathExists(targets.agents),
|
|
167
|
+
themes: await fs.pathExists(targets.themes),
|
|
168
|
+
settings: await fs.pathExists(targets.settings),
|
|
169
|
+
routingPolicy: await fs.pathExists(targets.routingPolicy),
|
|
170
|
+
profile: await fs.pathExists(targets.profile),
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const packageReady = await fs.pathExists(targets.root);
|
|
174
|
+
const packageFiles = await getPackageFileEntries();
|
|
175
|
+
const packageIncluded = packageFiles.includes('.pi') || packageFiles.some((entry) => entry.startsWith('.pi/')) || packageReady;
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
targets,
|
|
179
|
+
checks,
|
|
180
|
+
packageReady,
|
|
181
|
+
packageIncluded,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function inspectInstalledTakomiPiHarness(home = HOME) {
|
|
186
|
+
const targets = getPiGlobalTargets(home);
|
|
187
|
+
const runtime = path.join(targets.extensions, 'takomi-runtime');
|
|
188
|
+
const subagents = path.join(targets.extensions, 'takomi-subagents');
|
|
189
|
+
const core = path.join(path.dirname(targets.root), 'src', 'pi-takomi-core');
|
|
190
|
+
const piSubagentsModule = path.join(targets.root, 'node_modules', 'pi-subagents');
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
targets,
|
|
194
|
+
runtimeInstalled: await fs.pathExists(runtime),
|
|
195
|
+
subagentsInstalled: await fs.pathExists(subagents),
|
|
196
|
+
coreInstalled: await fs.pathExists(core),
|
|
197
|
+
piSubagentsModuleInstalled: await fs.pathExists(piSubagentsModule),
|
|
198
|
+
promptsInstalled: await fs.pathExists(targets.prompts),
|
|
199
|
+
agentsInstalled: await fs.pathExists(targets.agents),
|
|
200
|
+
themesInstalled: await fs.pathExists(targets.themes),
|
|
201
|
+
settingsPresent: await fs.pathExists(targets.settings),
|
|
202
|
+
routingPolicyPresent: await fs.pathExists(targets.routingPolicy),
|
|
203
|
+
manifestPresent: await fs.pathExists(MANIFEST_PATH),
|
|
204
|
+
storePresent: await fs.pathExists(STORE_PATH),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function runCommand(command, args) {
|
|
209
|
+
return spawnSync(command, args, { stdio: 'pipe', encoding: 'utf8', shell: process.platform === 'win32' });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export async function ensurePiInstalled() {
|
|
213
|
+
const before = await detectPiCommand();
|
|
214
|
+
if (before.installed) {
|
|
215
|
+
return { ok: true, changed: false, report: 'Pi already available.' };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const attempts = [
|
|
219
|
+
{ command: 'npm', args: ['install', '-g', '@mariozechner/pi-coding-agent'] },
|
|
220
|
+
{ command: 'npm.cmd', args: ['install', '-g', '@mariozechner/pi-coding-agent'] },
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
let lastError = 'Unknown install failure.';
|
|
224
|
+
for (const attempt of attempts) {
|
|
225
|
+
const result = runCommand(attempt.command, attempt.args);
|
|
226
|
+
if (result.status === 0) {
|
|
227
|
+
const after = await detectPiCommand();
|
|
228
|
+
if (after.installed) {
|
|
229
|
+
return {
|
|
230
|
+
ok: true,
|
|
231
|
+
changed: true,
|
|
232
|
+
report: (result.stdout || result.stderr || 'Installed Pi.').trim(),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
lastError = 'npm install reported success, but pi was still not detected.';
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
lastError = [result.stdout, result.stderr].filter(Boolean).join('\n').trim() || lastError;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return { ok: false, changed: false, report: lastError };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function printPiInstallResult(result) {
|
|
245
|
+
if (result.ok) {
|
|
246
|
+
console.log(pc.green(`✔ ${result.changed ? 'Installed' : 'Validated'} Pi`));
|
|
247
|
+
if (result.report) console.log(pc.dim(result.report.split(/\r?\n/).slice(-4).join('\n')));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
console.log(pc.red('✗ Failed to install Pi'));
|
|
251
|
+
if (result.report) console.log(pc.dim(result.report.split(/\r?\n/).slice(-8).join('\n')));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export async function ensurePiSubagentsInstalled() {
|
|
255
|
+
const before = await inspectPiSubagentsDependency();
|
|
256
|
+
if (before.localInstalled || before.globalInstalled) {
|
|
257
|
+
return { ok: true, changed: false, report: 'pi-subagents already available.' };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const attempts = [
|
|
261
|
+
{ command: 'npm', args: ['install', '-g', 'pi-subagents'] },
|
|
262
|
+
{ command: 'npm.cmd', args: ['install', '-g', 'pi-subagents'] },
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
let lastError = 'Unknown install failure.';
|
|
266
|
+
for (const attempt of attempts) {
|
|
267
|
+
const result = runCommand(attempt.command, attempt.args);
|
|
268
|
+
if (result.status === 0) {
|
|
269
|
+
const after = await inspectPiSubagentsDependency();
|
|
270
|
+
if (after.localInstalled || after.globalInstalled) {
|
|
271
|
+
return {
|
|
272
|
+
ok: true,
|
|
273
|
+
changed: true,
|
|
274
|
+
report: (result.stdout || result.stderr || 'Installed pi-subagents.').trim(),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
lastError = 'npm install reported success, but pi-subagents was still not detected.';
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
lastError = [result.stdout, result.stderr].filter(Boolean).join('\n').trim() || lastError;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return { ok: false, changed: false, report: lastError };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function printPiSubagentsInstallResult(result) {
|
|
287
|
+
if (result.ok) {
|
|
288
|
+
console.log(pc.green(`✔ ${result.changed ? 'Installed' : 'Validated'} pi-subagents`));
|
|
289
|
+
if (result.report) console.log(pc.dim(result.report.split(/\r?\n/).slice(-4).join('\n')));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
console.log(pc.red('✗ Failed to install pi-subagents'));
|
|
293
|
+
if (result.report) console.log(pc.dim(result.report.split(/\r?\n/).slice(-8).join('\n')));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export async function inspectPiHarnessEnvironment(cwd = process.cwd()) {
|
|
297
|
+
const pi = await detectPiCommand();
|
|
298
|
+
const bundled = await inspectBundledPiAssets();
|
|
299
|
+
const installed = await inspectInstalledTakomiPiHarness();
|
|
300
|
+
const piSubagents = await inspectPiSubagentsDependency();
|
|
301
|
+
const project = getProjectPiTargets(cwd);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
pi,
|
|
305
|
+
bundled,
|
|
306
|
+
installed,
|
|
307
|
+
piSubagents,
|
|
308
|
+
project: {
|
|
309
|
+
targets: project,
|
|
310
|
+
settingsPresent: await fs.pathExists(project.settings),
|
|
311
|
+
routingPolicyPresent: await fs.pathExists(project.routingPolicy),
|
|
312
|
+
profilePresent: await fs.pathExists(project.takomiProfile),
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export async function launchTakomiHarness(cwd = process.cwd()) {
|
|
318
|
+
const report = await inspectPiHarnessEnvironment(cwd);
|
|
319
|
+
|
|
320
|
+
if (!report.pi.installed) {
|
|
321
|
+
console.log(pc.red('Pi is not installed.'));
|
|
322
|
+
console.log(pc.dim('Run: takomi install pi'));
|
|
323
|
+
return 1;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (!report.installed.runtimeInstalled || !report.installed.subagentsInstalled) {
|
|
327
|
+
console.log(pc.red('Takomi Pi harness is not fully installed.'));
|
|
328
|
+
console.log(pc.dim('Run: takomi install pi'));
|
|
329
|
+
return 1;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const env = {
|
|
333
|
+
...process.env,
|
|
334
|
+
TAKOMI_HARNESS: '1',
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
return await new Promise((resolve) => {
|
|
338
|
+
const child = process.platform === 'win32'
|
|
339
|
+
? spawn('cmd.exe', ['/d', '/s', '/c', 'pi'], {
|
|
340
|
+
cwd,
|
|
341
|
+
stdio: 'inherit',
|
|
342
|
+
env,
|
|
343
|
+
shell: false,
|
|
344
|
+
})
|
|
345
|
+
: spawn(report.pi.path || 'pi', [], {
|
|
346
|
+
cwd,
|
|
347
|
+
stdio: 'inherit',
|
|
348
|
+
env,
|
|
349
|
+
shell: false,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
child.on('close', (code) => resolve(code ?? 0));
|
|
353
|
+
child.on('error', () => resolve(1));
|
|
354
|
+
});
|
|
355
|
+
}
|