takomi 2.1.2 → 2.1.3
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 +492 -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 +83 -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 +215 -215
- package/.pi/extensions/takomi-subagents/index.ts +75 -75
- package/.pi/extensions/takomi-subagents/live-updates.ts +83 -83
- package/.pi/extensions/takomi-subagents/native-render.ts +174 -174
- 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/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/pi-installer.js
CHANGED
|
@@ -1,171 +1,193 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import crypto from 'crypto';
|
|
5
|
-
import pc from 'picocolors';
|
|
6
|
-
import { PATHS } from './utils.js';
|
|
7
|
-
import { getPiGlobalTargets } from './pi-harness.js';
|
|
8
|
-
|
|
9
|
-
const HOME = os.homedir();
|
|
10
|
-
const TAKOMI_HOME = path.join(HOME, '.takomi');
|
|
11
|
-
export const PI_MANIFEST_PATH = path.join(TAKOMI_HOME, 'pi-manifest.json');
|
|
12
|
-
|
|
13
|
-
function sha256(value) {
|
|
14
|
-
return crypto.createHash('sha256').update(value).digest('hex');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function hashPath(targetPath) {
|
|
18
|
-
if (!await fs.pathExists(targetPath)) return null;
|
|
19
|
-
const stat = await fs.stat(targetPath);
|
|
20
|
-
if (stat.isFile()) {
|
|
21
|
-
const buf = await fs.readFile(targetPath);
|
|
22
|
-
return sha256(buf);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const entries = [];
|
|
26
|
-
async function walk(dir, prefix = '') {
|
|
27
|
-
const names = (await fs.readdir(dir)).sort();
|
|
28
|
-
for (const name of names) {
|
|
29
|
-
const full = path.join(dir, name);
|
|
30
|
-
const rel = path.join(prefix, name).replace(/\\/g, '/');
|
|
31
|
-
const st = await fs.stat(full);
|
|
32
|
-
if (st.isDirectory()) {
|
|
33
|
-
entries.push(`dir:${rel}`);
|
|
34
|
-
await walk(full, rel);
|
|
35
|
-
} else {
|
|
36
|
-
const buf = await fs.readFile(full);
|
|
37
|
-
entries.push(`file:${rel}:${sha256(buf)}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
await walk(targetPath);
|
|
42
|
-
return sha256(entries.join('\n'));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function pathIsSameSymlink(dest, src) {
|
|
46
|
-
try {
|
|
47
|
-
const stat = await fs.lstat(dest);
|
|
48
|
-
if (!stat.isSymbolicLink()) return false;
|
|
49
|
-
const target = await fs.readlink(dest);
|
|
50
|
-
const resolvedTarget = path.resolve(path.dirname(dest), target);
|
|
51
|
-
return path.resolve(src) === resolvedTarget;
|
|
52
|
-
} catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function prepareOwnedTarget(src, dest) {
|
|
58
|
-
if (await pathIsSameSymlink(dest, src)) return 'symlink';
|
|
59
|
-
if (!await fs.pathExists(dest)) return 'copy';
|
|
60
|
-
const stat = await fs.lstat(dest);
|
|
61
|
-
if (stat.isSymbolicLink()) {
|
|
62
|
-
await fs.remove(dest);
|
|
63
|
-
return 'copy';
|
|
64
|
-
}
|
|
65
|
-
return 'copy';
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function copyOwnedDirectory(src, dest) {
|
|
69
|
-
await fs.ensureDir(path.dirname(dest));
|
|
70
|
-
const mode = await prepareOwnedTarget(src, dest);
|
|
71
|
-
if (mode === 'symlink') return hashPath(src);
|
|
72
|
-
await fs.copy(src, dest, { overwrite: true });
|
|
73
|
-
return hashPath(dest);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async function copyOwnedFile(src, dest) {
|
|
77
|
-
await fs.ensureDir(path.dirname(dest));
|
|
78
|
-
const mode = await prepareOwnedTarget(src, dest);
|
|
79
|
-
if (mode === 'symlink') return hashPath(src);
|
|
80
|
-
await fs.copy(src, dest, { overwrite: true });
|
|
81
|
-
return hashPath(dest);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export async function readPiInstallManifest() {
|
|
85
|
-
try {
|
|
86
|
-
if (await fs.pathExists(PI_MANIFEST_PATH)) return await fs.readJson(PI_MANIFEST_PATH);
|
|
87
|
-
} catch {}
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export async function writePiInstallManifest(manifest) {
|
|
92
|
-
await fs.ensureDir(TAKOMI_HOME);
|
|
93
|
-
await fs.writeJson(PI_MANIFEST_PATH, manifest, { spaces: 2 });
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function installPiHarnessAssets(version = 'unknown') {
|
|
97
|
-
const srcRoot = PATHS.pi;
|
|
98
|
-
const targets = getPiGlobalTargets();
|
|
99
|
-
|
|
100
|
-
const copied = {};
|
|
101
|
-
|
|
102
|
-
const readmeSrc = path.join(srcRoot, 'README.md');
|
|
103
|
-
if (await fs.pathExists(readmeSrc)) {
|
|
104
|
-
copied.readme = await copyOwnedFile(readmeSrc, path.join(targets.root, 'README.md'));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const extensionNames = ['takomi-runtime', 'takomi-subagents', 'oauth-router'];
|
|
108
|
-
for (const name of extensionNames) {
|
|
109
|
-
const src = path.join(srcRoot, 'extensions', name);
|
|
110
|
-
if (await fs.pathExists(src)) {
|
|
111
|
-
copied[`extension:${name}`] = await copyOwnedDirectory(src, path.join(targets.extensions, name));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import crypto from 'crypto';
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
import { PATHS } from './utils.js';
|
|
7
|
+
import { getPiGlobalTargets } from './pi-harness.js';
|
|
8
|
+
|
|
9
|
+
const HOME = os.homedir();
|
|
10
|
+
const TAKOMI_HOME = path.join(HOME, '.takomi');
|
|
11
|
+
export const PI_MANIFEST_PATH = path.join(TAKOMI_HOME, 'pi-manifest.json');
|
|
12
|
+
|
|
13
|
+
function sha256(value) {
|
|
14
|
+
return crypto.createHash('sha256').update(value).digest('hex');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function hashPath(targetPath) {
|
|
18
|
+
if (!await fs.pathExists(targetPath)) return null;
|
|
19
|
+
const stat = await fs.stat(targetPath);
|
|
20
|
+
if (stat.isFile()) {
|
|
21
|
+
const buf = await fs.readFile(targetPath);
|
|
22
|
+
return sha256(buf);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const entries = [];
|
|
26
|
+
async function walk(dir, prefix = '') {
|
|
27
|
+
const names = (await fs.readdir(dir)).sort();
|
|
28
|
+
for (const name of names) {
|
|
29
|
+
const full = path.join(dir, name);
|
|
30
|
+
const rel = path.join(prefix, name).replace(/\\/g, '/');
|
|
31
|
+
const st = await fs.stat(full);
|
|
32
|
+
if (st.isDirectory()) {
|
|
33
|
+
entries.push(`dir:${rel}`);
|
|
34
|
+
await walk(full, rel);
|
|
35
|
+
} else {
|
|
36
|
+
const buf = await fs.readFile(full);
|
|
37
|
+
entries.push(`file:${rel}:${sha256(buf)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
await walk(targetPath);
|
|
42
|
+
return sha256(entries.join('\n'));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function pathIsSameSymlink(dest, src) {
|
|
46
|
+
try {
|
|
47
|
+
const stat = await fs.lstat(dest);
|
|
48
|
+
if (!stat.isSymbolicLink()) return false;
|
|
49
|
+
const target = await fs.readlink(dest);
|
|
50
|
+
const resolvedTarget = path.resolve(path.dirname(dest), target);
|
|
51
|
+
return path.resolve(src) === resolvedTarget;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function prepareOwnedTarget(src, dest) {
|
|
58
|
+
if (await pathIsSameSymlink(dest, src)) return 'symlink';
|
|
59
|
+
if (!await fs.pathExists(dest)) return 'copy';
|
|
60
|
+
const stat = await fs.lstat(dest);
|
|
61
|
+
if (stat.isSymbolicLink()) {
|
|
62
|
+
await fs.remove(dest);
|
|
63
|
+
return 'copy';
|
|
64
|
+
}
|
|
65
|
+
return 'copy';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function copyOwnedDirectory(src, dest) {
|
|
69
|
+
await fs.ensureDir(path.dirname(dest));
|
|
70
|
+
const mode = await prepareOwnedTarget(src, dest);
|
|
71
|
+
if (mode === 'symlink') return hashPath(src);
|
|
72
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
73
|
+
return hashPath(dest);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function copyOwnedFile(src, dest) {
|
|
77
|
+
await fs.ensureDir(path.dirname(dest));
|
|
78
|
+
const mode = await prepareOwnedTarget(src, dest);
|
|
79
|
+
if (mode === 'symlink') return hashPath(src);
|
|
80
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
81
|
+
return hashPath(dest);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function readPiInstallManifest() {
|
|
85
|
+
try {
|
|
86
|
+
if (await fs.pathExists(PI_MANIFEST_PATH)) return await fs.readJson(PI_MANIFEST_PATH);
|
|
87
|
+
} catch {}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function writePiInstallManifest(manifest) {
|
|
92
|
+
await fs.ensureDir(TAKOMI_HOME);
|
|
93
|
+
await fs.writeJson(PI_MANIFEST_PATH, manifest, { spaces: 2 });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function installPiHarnessAssets(version = 'unknown') {
|
|
97
|
+
const srcRoot = PATHS.pi;
|
|
98
|
+
const targets = getPiGlobalTargets();
|
|
99
|
+
|
|
100
|
+
const copied = {};
|
|
101
|
+
|
|
102
|
+
const readmeSrc = path.join(srcRoot, 'README.md');
|
|
103
|
+
if (await fs.pathExists(readmeSrc)) {
|
|
104
|
+
copied.readme = await copyOwnedFile(readmeSrc, path.join(targets.root, 'README.md'));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const extensionNames = ['takomi-runtime', 'takomi-subagents', 'oauth-router'];
|
|
108
|
+
for (const name of extensionNames) {
|
|
109
|
+
const src = path.join(srcRoot, 'extensions', name);
|
|
110
|
+
if (await fs.pathExists(src)) {
|
|
111
|
+
copied[`extension:${name}`] = await copyOwnedDirectory(src, path.join(targets.extensions, name));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Pi loads extensions from ~/.pi/agent/extensions. The runtime imports the
|
|
116
|
+
// shared Takomi core via ../../../src/pi-takomi-core, which resolves to
|
|
117
|
+
// ~/.pi/src/pi-takomi-core from an installed extension path.
|
|
118
|
+
const coreSrc = path.join(PATHS.root, 'src', 'pi-takomi-core');
|
|
119
|
+
const coreDest = path.join(path.dirname(targets.root), 'src', 'pi-takomi-core');
|
|
120
|
+
if (await fs.pathExists(coreSrc)) {
|
|
121
|
+
copied['core:pi-takomi-core'] = await copyOwnedDirectory(coreSrc, coreDest);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Keep Pi extension module resolution self-contained for clean machines.
|
|
125
|
+
// Extension files import pi-subagents internals, so place the package where
|
|
126
|
+
// Node can resolve it while loading ~/.pi/agent/extensions/*.
|
|
127
|
+
const piSubagentsSrc = path.join(PATHS.root, 'node_modules', 'pi-subagents');
|
|
128
|
+
const piSubagentsDest = path.join(targets.root, 'node_modules', 'pi-subagents');
|
|
129
|
+
if (await fs.pathExists(piSubagentsSrc)) {
|
|
130
|
+
copied['node_module:pi-subagents'] = await copyOwnedDirectory(piSubagentsSrc, piSubagentsDest);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const owned = {
|
|
134
|
+
prompts: { src: path.join(srcRoot, 'prompts'), dest: targets.prompts, type: 'dir' },
|
|
135
|
+
agents: { src: path.join(srcRoot, 'agents'), dest: targets.agents, type: 'dir' },
|
|
136
|
+
themes: { src: path.join(srcRoot, 'themes'), dest: targets.themes, type: 'dir' },
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
for (const [key, entry] of Object.entries(owned)) {
|
|
140
|
+
if (!await fs.pathExists(entry.src)) continue;
|
|
141
|
+
copied[key] = entry.type === 'dir'
|
|
142
|
+
? await copyOwnedDirectory(entry.src, entry.dest)
|
|
143
|
+
: await copyOwnedFile(entry.src, entry.dest);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const manifest = {
|
|
147
|
+
takomiVersion: version,
|
|
148
|
+
installedAt: new Date().toISOString(),
|
|
149
|
+
targetRoot: targets.root,
|
|
150
|
+
owned: copied,
|
|
151
|
+
preserved: {
|
|
152
|
+
settings: targets.settings,
|
|
153
|
+
routingPolicy: targets.routingPolicy,
|
|
154
|
+
userStateDir: targets.takomi,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
await writePiInstallManifest(manifest);
|
|
159
|
+
return { targets, manifest };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function syncPiHarnessAssets(version = 'unknown') {
|
|
163
|
+
return installPiHarnessAssets(version);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function validatePiHarnessInstall() {
|
|
167
|
+
const targets = getPiGlobalTargets();
|
|
168
|
+
return {
|
|
169
|
+
runtime: await fs.pathExists(path.join(targets.extensions, 'takomi-runtime')),
|
|
170
|
+
subagents: await fs.pathExists(path.join(targets.extensions, 'takomi-subagents')),
|
|
171
|
+
oauthRouter: await fs.pathExists(path.join(targets.extensions, 'oauth-router')),
|
|
172
|
+
prompts: await fs.pathExists(targets.prompts),
|
|
173
|
+
agents: await fs.pathExists(targets.agents),
|
|
174
|
+
themes: await fs.pathExists(targets.themes),
|
|
175
|
+
readme: await fs.pathExists(path.join(targets.root, 'README.md')),
|
|
176
|
+
core: await fs.pathExists(path.join(path.dirname(targets.root), 'src', 'pi-takomi-core')),
|
|
177
|
+
piSubagentsModule: await fs.pathExists(path.join(targets.root, 'node_modules', 'pi-subagents')),
|
|
178
|
+
settingsPreserved: !await fs.pathExists(path.join(PATHS.pi, 'settings.json')) || true,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function printPiInstallSummary(result, validation) {
|
|
183
|
+
console.log(pc.green('\n✔ Installed Takomi Pi harness assets'));
|
|
184
|
+
console.log(pc.white(` Root: ${result.targets.root}`));
|
|
185
|
+
console.log(pc.white(` Manifest: ${PI_MANIFEST_PATH}`));
|
|
186
|
+
console.log(pc.white(` Extensions: ${validation.runtime && validation.subagents && validation.oauthRouter ? 'ok' : 'check needed'}`));
|
|
187
|
+
console.log(pc.white(` Prompts: ${validation.prompts ? 'ok' : 'missing'}`));
|
|
188
|
+
console.log(pc.white(` Agents: ${validation.agents ? 'ok' : 'missing'}`));
|
|
189
|
+
console.log(pc.white(` Themes: ${validation.themes ? 'ok' : 'missing'}`));
|
|
190
|
+
console.log(pc.white(` Core: ${validation.core ? 'ok' : 'missing'}`));
|
|
191
|
+
console.log(pc.white(` Subagents: ${validation.piSubagentsModule ? 'ok' : 'missing module'}`));
|
|
192
|
+
console.log(pc.dim('\nPreserved user-owned config: settings.json, takomi/model-routing.md, runtime session state.'));
|
|
193
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from "./types";
|
|
2
|
-
export * from "./workflows";
|
|
3
|
-
export * from "./routing";
|
|
4
|
-
export * from "./orchestration";
|
|
1
|
+
export * from "./types";
|
|
2
|
+
export * from "./workflows";
|
|
3
|
+
export * from "./routing";
|
|
4
|
+
export * from "./orchestration";
|