omegon 0.6.0
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/.gitattributes +3 -0
- package/AGENTS.md +16 -0
- package/LICENSE +15 -0
- package/README.md +289 -0
- package/bin/pi.mjs +30 -0
- package/extensions/00-secrets/index.ts +1126 -0
- package/extensions/01-auth/auth.ts +401 -0
- package/extensions/01-auth/index.ts +289 -0
- package/extensions/auto-compact.ts +42 -0
- package/extensions/bootstrap/deps.ts +291 -0
- package/extensions/bootstrap/index.ts +811 -0
- package/extensions/chronos/chronos.sh +487 -0
- package/extensions/chronos/index.ts +148 -0
- package/extensions/cleave/assessment.ts +754 -0
- package/extensions/cleave/bridge.ts +31 -0
- package/extensions/cleave/conflicts.ts +250 -0
- package/extensions/cleave/dispatcher.ts +808 -0
- package/extensions/cleave/guardrails.ts +426 -0
- package/extensions/cleave/index.ts +3121 -0
- package/extensions/cleave/lifecycle-emitter.ts +20 -0
- package/extensions/cleave/openspec.ts +811 -0
- package/extensions/cleave/planner.ts +260 -0
- package/extensions/cleave/review.ts +579 -0
- package/extensions/cleave/skills.ts +355 -0
- package/extensions/cleave/types.ts +261 -0
- package/extensions/cleave/workspace.ts +861 -0
- package/extensions/cleave/worktree.ts +243 -0
- package/extensions/core-renderers.ts +253 -0
- package/extensions/dashboard/context-gauge.ts +58 -0
- package/extensions/dashboard/file-watch.ts +14 -0
- package/extensions/dashboard/footer.ts +1145 -0
- package/extensions/dashboard/git.ts +185 -0
- package/extensions/dashboard/index.ts +478 -0
- package/extensions/dashboard/memory-audit.ts +34 -0
- package/extensions/dashboard/overlay-data.ts +705 -0
- package/extensions/dashboard/overlay.ts +365 -0
- package/extensions/dashboard/render-utils.ts +54 -0
- package/extensions/dashboard/types.ts +191 -0
- package/extensions/dashboard/uri-helper.ts +45 -0
- package/extensions/debug.ts +69 -0
- package/extensions/defaults.ts +282 -0
- package/extensions/design-tree/dashboard-state.ts +161 -0
- package/extensions/design-tree/design-card.ts +362 -0
- package/extensions/design-tree/index.ts +2130 -0
- package/extensions/design-tree/lifecycle-emitter.ts +41 -0
- package/extensions/design-tree/tree.ts +1607 -0
- package/extensions/design-tree/types.ts +163 -0
- package/extensions/distill.ts +127 -0
- package/extensions/effort/index.ts +395 -0
- package/extensions/effort/tiers.ts +146 -0
- package/extensions/effort/types.ts +105 -0
- package/extensions/lib/git-state.ts +227 -0
- package/extensions/lib/local-models.ts +157 -0
- package/extensions/lib/model-preferences.ts +51 -0
- package/extensions/lib/model-routing.ts +720 -0
- package/extensions/lib/operator-fallback.ts +205 -0
- package/extensions/lib/operator-profile.ts +360 -0
- package/extensions/lib/slash-command-bridge.ts +253 -0
- package/extensions/lib/typebox-helpers.ts +16 -0
- package/extensions/local-inference/index.ts +727 -0
- package/extensions/mcp-bridge/README.md +220 -0
- package/extensions/mcp-bridge/index.ts +951 -0
- package/extensions/mcp-bridge/lib.ts +365 -0
- package/extensions/mcp-bridge/mcp.json +3 -0
- package/extensions/mcp-bridge/package.json +11 -0
- package/extensions/model-budget.ts +752 -0
- package/extensions/offline-driver.ts +403 -0
- package/extensions/openspec/archive-gate.ts +164 -0
- package/extensions/openspec/branch-cleanup.ts +64 -0
- package/extensions/openspec/dashboard-state.ts +50 -0
- package/extensions/openspec/index.ts +1917 -0
- package/extensions/openspec/lifecycle-emitter.ts +65 -0
- package/extensions/openspec/lifecycle-files.ts +70 -0
- package/extensions/openspec/lifecycle.ts +50 -0
- package/extensions/openspec/reconcile.ts +187 -0
- package/extensions/openspec/spec.ts +1385 -0
- package/extensions/openspec/types.ts +98 -0
- package/extensions/project-memory/DESIGN-global-mind.md +198 -0
- package/extensions/project-memory/README.md +202 -0
- package/extensions/project-memory/api-types.ts +382 -0
- package/extensions/project-memory/compaction-policy.ts +29 -0
- package/extensions/project-memory/core.ts +164 -0
- package/extensions/project-memory/embeddings.ts +230 -0
- package/extensions/project-memory/extraction-v2.ts +861 -0
- package/extensions/project-memory/factstore.ts +2177 -0
- package/extensions/project-memory/index.ts +3459 -0
- package/extensions/project-memory/injection-metrics.ts +91 -0
- package/extensions/project-memory/jsonl-io.ts +12 -0
- package/extensions/project-memory/lifecycle.ts +331 -0
- package/extensions/project-memory/migration.ts +293 -0
- package/extensions/project-memory/package.json +9 -0
- package/extensions/project-memory/sci-renderers.ts +7 -0
- package/extensions/project-memory/template.ts +103 -0
- package/extensions/project-memory/triggers.ts +52 -0
- package/extensions/project-memory/types.ts +102 -0
- package/extensions/render/composition/fonts/Inter-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Inter-Regular.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Regular.ttf +0 -0
- package/extensions/render/composition/package-lock.json +534 -0
- package/extensions/render/composition/package.json +22 -0
- package/extensions/render/composition/render.mjs +246 -0
- package/extensions/render/composition/test-comp.tsx +87 -0
- package/extensions/render/composition/types.ts +24 -0
- package/extensions/render/excalidraw/UPSTREAM.md +81 -0
- package/extensions/render/excalidraw/elements.ts +764 -0
- package/extensions/render/excalidraw/index.ts +66 -0
- package/extensions/render/excalidraw/types.ts +223 -0
- package/extensions/render/excalidraw-renderer/pyproject.toml +8 -0
- package/extensions/render/excalidraw-renderer/render_excalidraw.py +182 -0
- package/extensions/render/excalidraw-renderer/render_template.html +59 -0
- package/extensions/render/index.ts +830 -0
- package/extensions/render/native-diagrams/index.ts +57 -0
- package/extensions/render/native-diagrams/motifs.ts +542 -0
- package/extensions/render/native-diagrams/raster.ts +8 -0
- package/extensions/render/native-diagrams/scene.ts +75 -0
- package/extensions/render/native-diagrams/spec.ts +204 -0
- package/extensions/render/native-diagrams/svg.ts +116 -0
- package/extensions/sci-ui.ts +304 -0
- package/extensions/session-log.ts +174 -0
- package/extensions/shared-state.ts +146 -0
- package/extensions/spinner-verbs.ts +91 -0
- package/extensions/style.ts +281 -0
- package/extensions/terminal-title.ts +191 -0
- package/extensions/tool-profile/index.ts +291 -0
- package/extensions/tool-profile/profiles.ts +290 -0
- package/extensions/types.d.ts +9 -0
- package/extensions/vault/index.ts +185 -0
- package/extensions/version-check.ts +90 -0
- package/extensions/view/index.ts +859 -0
- package/extensions/view/uri-resolver.ts +148 -0
- package/extensions/web-search/index.ts +182 -0
- package/extensions/web-search/providers.ts +121 -0
- package/extensions/web-ui/index.ts +110 -0
- package/extensions/web-ui/server.ts +265 -0
- package/extensions/web-ui/state.ts +462 -0
- package/extensions/web-ui/static/index.html +145 -0
- package/extensions/web-ui/types.ts +284 -0
- package/package.json +76 -0
- package/prompts/init.md +75 -0
- package/prompts/new-repo.md +54 -0
- package/prompts/oci-login.md +56 -0
- package/prompts/status.md +50 -0
- package/settings.json +4 -0
- package/skills/cleave/SKILL.md +218 -0
- package/skills/git/SKILL.md +209 -0
- package/skills/git/_reference/ci-validation.md +204 -0
- package/skills/oci/SKILL.md +338 -0
- package/skills/openspec/SKILL.md +346 -0
- package/skills/pi-extensions/SKILL.md +191 -0
- package/skills/pi-tui/SKILL.md +517 -0
- package/skills/python/SKILL.md +189 -0
- package/skills/rust/SKILL.md +268 -0
- package/skills/security/SKILL.md +206 -0
- package/skills/style/SKILL.md +264 -0
- package/skills/typescript/SKILL.md +225 -0
- package/skills/vault/SKILL.md +102 -0
- package/themes/alpharius-legacy.json +85 -0
- package/themes/alpharius.conf +59 -0
- package/themes/alpharius.json +88 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cleave/worktree — Git worktree operations for child isolation.
|
|
3
|
+
*
|
|
4
|
+
* Each child gets its own git worktree on a separate branch,
|
|
5
|
+
* preventing file conflicts during parallel execution.
|
|
6
|
+
* Completed branches are merged back to base after harvesting.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { mkdirSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import type { ExtensionAPI } from "@cwilson613/pi-coding-agent";
|
|
13
|
+
|
|
14
|
+
/** Base directory for cleave worktrees. */
|
|
15
|
+
const WORKTREE_HOME = join(homedir(), ".pi", "cleave", "wt");
|
|
16
|
+
|
|
17
|
+
export interface WorktreeInfo {
|
|
18
|
+
path: string;
|
|
19
|
+
branch: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the current git branch name.
|
|
24
|
+
*/
|
|
25
|
+
export async function getCurrentBranch(
|
|
26
|
+
pi: ExtensionAPI,
|
|
27
|
+
repoPath: string,
|
|
28
|
+
): Promise<string> {
|
|
29
|
+
const result = await pi.exec("git", ["branch", "--show-current"], {
|
|
30
|
+
cwd: repoPath,
|
|
31
|
+
timeout: 5_000,
|
|
32
|
+
});
|
|
33
|
+
const branch = result.stdout.trim();
|
|
34
|
+
if (!branch) {
|
|
35
|
+
// Detached HEAD — get the SHA
|
|
36
|
+
const sha = await pi.exec("git", ["rev-parse", "--short", "HEAD"], {
|
|
37
|
+
cwd: repoPath,
|
|
38
|
+
timeout: 5_000,
|
|
39
|
+
});
|
|
40
|
+
return sha.stdout.trim();
|
|
41
|
+
}
|
|
42
|
+
return branch;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Ensure the working tree is clean (no uncommitted changes).
|
|
47
|
+
* Throws if dirty.
|
|
48
|
+
*/
|
|
49
|
+
export async function ensureCleanWorktree(
|
|
50
|
+
pi: ExtensionAPI,
|
|
51
|
+
repoPath: string,
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
const result = await pi.exec("git", ["status", "--porcelain"], {
|
|
54
|
+
cwd: repoPath,
|
|
55
|
+
timeout: 5_000,
|
|
56
|
+
});
|
|
57
|
+
if (result.stdout.trim()) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"Working tree has uncommitted changes. Commit or stash before cleaving.\n" +
|
|
60
|
+
result.stdout.trim(),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create a git worktree for a child task.
|
|
67
|
+
*
|
|
68
|
+
* Creates a new branch from baseBranch and checks it out in a
|
|
69
|
+
* worktree directory adjacent to the repo.
|
|
70
|
+
*/
|
|
71
|
+
export async function createWorktree(
|
|
72
|
+
pi: ExtensionAPI,
|
|
73
|
+
repoPath: string,
|
|
74
|
+
childLabel: string,
|
|
75
|
+
childId: number,
|
|
76
|
+
baseBranch: string,
|
|
77
|
+
): Promise<WorktreeInfo> {
|
|
78
|
+
// Sanitize childLabel to prevent path traversal via / or ..
|
|
79
|
+
const safeLabel = childLabel.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/^\.+/, "") || "task";
|
|
80
|
+
const branch = `cleave/${childId}-${safeLabel}`;
|
|
81
|
+
// Worktree goes in ~/.pi/cleave/wt/ to avoid polluting the repo or its parent
|
|
82
|
+
mkdirSync(WORKTREE_HOME, { recursive: true });
|
|
83
|
+
const worktreePath = join(WORKTREE_HOME, `${childId}-${safeLabel}`);
|
|
84
|
+
|
|
85
|
+
// Delete branch if it already exists (leftover from a previous run)
|
|
86
|
+
await pi.exec("git", ["branch", "-D", branch], {
|
|
87
|
+
cwd: repoPath,
|
|
88
|
+
timeout: 5_000,
|
|
89
|
+
}).catch(() => {}); // ignore if it doesn't exist
|
|
90
|
+
|
|
91
|
+
// Remove stale worktree path if it exists
|
|
92
|
+
await pi.exec("rm", ["-rf", worktreePath], { timeout: 5_000 }).catch(() => {});
|
|
93
|
+
|
|
94
|
+
// Create worktree with new branch from base
|
|
95
|
+
const result = await pi.exec(
|
|
96
|
+
"git",
|
|
97
|
+
["worktree", "add", "-b", branch, worktreePath, baseBranch],
|
|
98
|
+
{ cwd: repoPath, timeout: 30_000 },
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (result.code !== 0) {
|
|
102
|
+
throw new Error(`Failed to create worktree: ${result.stderr}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { path: worktreePath, branch };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Merge a child's branch back into the base branch.
|
|
110
|
+
*
|
|
111
|
+
* Returns { success, conflictFiles } — does NOT abort on conflict,
|
|
112
|
+
* leaving the merge state for manual resolution.
|
|
113
|
+
*/
|
|
114
|
+
export async function mergeBranch(
|
|
115
|
+
pi: ExtensionAPI,
|
|
116
|
+
repoPath: string,
|
|
117
|
+
childBranch: string,
|
|
118
|
+
baseBranch: string,
|
|
119
|
+
): Promise<{ success: boolean; conflictFiles: string[]; error?: string }> {
|
|
120
|
+
// Checkout base branch
|
|
121
|
+
let result = await pi.exec("git", ["checkout", baseBranch], {
|
|
122
|
+
cwd: repoPath,
|
|
123
|
+
timeout: 10_000,
|
|
124
|
+
});
|
|
125
|
+
if (result.code !== 0) {
|
|
126
|
+
return { success: false, conflictFiles: [], error: `Failed to checkout ${baseBranch}: ${result.stderr}` };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Attempt merge
|
|
130
|
+
result = await pi.exec("git", ["merge", "--no-ff", childBranch, "-m", `merge: cleave child ${childBranch}`], {
|
|
131
|
+
cwd: repoPath,
|
|
132
|
+
timeout: 30_000,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (result.code === 0) {
|
|
136
|
+
return { success: true, conflictFiles: [] };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Merge conflict — detect which files
|
|
140
|
+
const statusResult = await pi.exec("git", ["diff", "--name-only", "--diff-filter=U"], {
|
|
141
|
+
cwd: repoPath,
|
|
142
|
+
timeout: 5_000,
|
|
143
|
+
});
|
|
144
|
+
const conflictFiles = statusResult.stdout.trim().split("\n").filter(Boolean);
|
|
145
|
+
|
|
146
|
+
// Abort the merge to leave repo in clean state
|
|
147
|
+
await pi.exec("git", ["merge", "--abort"], { cwd: repoPath, timeout: 5_000 }).catch(() => {});
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
conflictFiles,
|
|
152
|
+
error: `Merge conflict in ${conflictFiles.length} file(s)`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Remove cleave worktree directories without deleting branches.
|
|
158
|
+
* Used on merge failure to free disk space while preserving branches
|
|
159
|
+
* for manual conflict resolution.
|
|
160
|
+
*/
|
|
161
|
+
export async function pruneWorktreeDirs(
|
|
162
|
+
pi: ExtensionAPI,
|
|
163
|
+
repoPath: string,
|
|
164
|
+
): Promise<void> {
|
|
165
|
+
const result = await pi.exec("git", ["worktree", "list", "--porcelain"], {
|
|
166
|
+
cwd: repoPath,
|
|
167
|
+
timeout: 5_000,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
for (const line of result.stdout.split("\n")) {
|
|
171
|
+
if (line.startsWith("worktree ")) {
|
|
172
|
+
const wtPath = line.replace("worktree ", "").trim();
|
|
173
|
+
if (wtPath.startsWith(WORKTREE_HOME) || wtPath.includes(".cleave-wt-")) {
|
|
174
|
+
await pi.exec("git", ["worktree", "remove", "--force", wtPath], {
|
|
175
|
+
cwd: repoPath,
|
|
176
|
+
timeout: 10_000,
|
|
177
|
+
}).catch(() => {});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
await pi.exec("git", ["worktree", "prune"], {
|
|
183
|
+
cwd: repoPath,
|
|
184
|
+
timeout: 5_000,
|
|
185
|
+
}).catch(() => {});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Clean up all cleave worktrees and their branches.
|
|
190
|
+
*/
|
|
191
|
+
export async function cleanupWorktrees(
|
|
192
|
+
pi: ExtensionAPI,
|
|
193
|
+
repoPath: string,
|
|
194
|
+
): Promise<void> {
|
|
195
|
+
// List worktrees
|
|
196
|
+
const result = await pi.exec("git", ["worktree", "list", "--porcelain"], {
|
|
197
|
+
cwd: repoPath,
|
|
198
|
+
timeout: 5_000,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const lines = result.stdout.split("\n");
|
|
202
|
+
const worktreePaths: string[] = [];
|
|
203
|
+
|
|
204
|
+
for (const line of lines) {
|
|
205
|
+
if (line.startsWith("worktree ")) {
|
|
206
|
+
const wtPath = line.replace("worktree ", "").trim();
|
|
207
|
+
// Match worktrees in the cleave wt directory or legacy .cleave-wt- locations
|
|
208
|
+
if (wtPath.startsWith(WORKTREE_HOME) || wtPath.includes(".cleave-wt-")) {
|
|
209
|
+
worktreePaths.push(wtPath);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Remove each worktree
|
|
215
|
+
for (const wtPath of worktreePaths) {
|
|
216
|
+
await pi.exec("git", ["worktree", "remove", "--force", wtPath], {
|
|
217
|
+
cwd: repoPath,
|
|
218
|
+
timeout: 10_000,
|
|
219
|
+
}).catch(() => {});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Prune stale worktree references
|
|
223
|
+
await pi.exec("git", ["worktree", "prune"], {
|
|
224
|
+
cwd: repoPath,
|
|
225
|
+
timeout: 5_000,
|
|
226
|
+
}).catch(() => {});
|
|
227
|
+
|
|
228
|
+
// Delete orphaned cleave/* branches
|
|
229
|
+
const branchResult = await pi.exec(
|
|
230
|
+
"git", ["branch", "--list", "cleave/*"],
|
|
231
|
+
{ cwd: repoPath, timeout: 5_000 },
|
|
232
|
+
);
|
|
233
|
+
const branches = branchResult.stdout
|
|
234
|
+
.split("\n")
|
|
235
|
+
.map((b) => b.trim().replace(/^\*\s*/, ""))
|
|
236
|
+
.filter(Boolean);
|
|
237
|
+
for (const branch of branches) {
|
|
238
|
+
await pi.exec("git", ["branch", "-D", branch], {
|
|
239
|
+
cwd: repoPath,
|
|
240
|
+
timeout: 5_000,
|
|
241
|
+
}).catch(() => {});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Tool Renderers — Sci-UI rendering for pi built-in tools.
|
|
3
|
+
*
|
|
4
|
+
* Uses registerToolRenderer() to attach renderCall/renderResult
|
|
5
|
+
* to built-in tools (bash, read, edit, write) without replacing them.
|
|
6
|
+
*
|
|
7
|
+
* The built-in renderers handle syntax highlighting, diffs, and streaming —
|
|
8
|
+
* we only override the COLLAPSED view to match the Sci-UI visual language.
|
|
9
|
+
* Expanded views fall through to the built-in renderer.
|
|
10
|
+
*/
|
|
11
|
+
import type { ExtensionAPI } from "@cwilson613/pi-coding-agent";
|
|
12
|
+
import { sciCall, sciOk, sciErr, sciLoading } from "./sci-ui.ts";
|
|
13
|
+
|
|
14
|
+
/** Shorten a file path for display — keep last 2-3 segments. */
|
|
15
|
+
function shortenPath(p: string | null | undefined, maxLen = 55): string {
|
|
16
|
+
if (!p) return "…";
|
|
17
|
+
if (p.length <= maxLen) return p;
|
|
18
|
+
const parts = p.split("/");
|
|
19
|
+
// Show last 3 segments at most
|
|
20
|
+
const tail = parts.slice(-3).join("/");
|
|
21
|
+
return tail.length <= maxLen ? tail : "…" + p.slice(-(maxLen - 1));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function coreRenderers(pi: ExtensionAPI): void {
|
|
25
|
+
// ── Read ──────────────────────────────────────────────────────────────
|
|
26
|
+
pi.registerToolRenderer("read", {
|
|
27
|
+
renderCall(args: any, theme: any) {
|
|
28
|
+
const p = shortenPath(args?.file_path ?? args?.path);
|
|
29
|
+
let range = "";
|
|
30
|
+
if (args?.offset != null || args?.limit != null) {
|
|
31
|
+
const start = args.offset ?? 1;
|
|
32
|
+
const end = args.limit != null ? start + args.limit - 1 : "";
|
|
33
|
+
range = `:${start}${end ? `-${end}` : ""}`;
|
|
34
|
+
}
|
|
35
|
+
return sciCall("read", `${p}${range}`, theme);
|
|
36
|
+
},
|
|
37
|
+
// renderResult omitted — built-in handles syntax highlighting + truncation
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ── Edit ──────────────────────────────────────────────────────────────
|
|
41
|
+
pi.registerToolRenderer("edit", {
|
|
42
|
+
renderCall(args: any, theme: any) {
|
|
43
|
+
const p = shortenPath(args?.file_path ?? args?.path);
|
|
44
|
+
// Show the size of the change: lines changed
|
|
45
|
+
const oldLines = (args?.old_text ?? args?.oldText ?? "").split("\n").length;
|
|
46
|
+
const newLines = (args?.new_text ?? args?.newText ?? "").split("\n").length;
|
|
47
|
+
const delta = newLines - oldLines;
|
|
48
|
+
const deltaStr = delta === 0 ? `${oldLines}L` : delta > 0 ? `+${delta}L` : `${delta}L`;
|
|
49
|
+
return sciCall("edit", `${p} (${deltaStr})`, theme);
|
|
50
|
+
},
|
|
51
|
+
// renderResult omitted — built-in handles diff rendering
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ── Write ─────────────────────────────────────────────────────────────
|
|
55
|
+
pi.registerToolRenderer("write", {
|
|
56
|
+
renderCall(args: any, theme: any) {
|
|
57
|
+
const p = shortenPath(args?.file_path ?? args?.path);
|
|
58
|
+
const content = args?.content ?? "";
|
|
59
|
+
const lines = content.split("\n").length;
|
|
60
|
+
return sciCall("write", `${p} (${lines}L)`, theme);
|
|
61
|
+
},
|
|
62
|
+
// renderResult omitted — built-in handles syntax highlighting
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ── Bash ──────────────────────────────────────────────────────────────
|
|
66
|
+
pi.registerToolRenderer("bash", {
|
|
67
|
+
renderCall(args: any, theme: any) {
|
|
68
|
+
const cmd = args?.command ?? "";
|
|
69
|
+
// Truncate long commands
|
|
70
|
+
const display = cmd.length > 70 ? cmd.slice(0, 67) + "…" : cmd;
|
|
71
|
+
return sciCall("bash", display, theme);
|
|
72
|
+
},
|
|
73
|
+
// renderResult omitted — built-in handles output display + truncation
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ── Grep ──────────────────────────────────────────────────────────────
|
|
77
|
+
pi.registerToolRenderer("grep", {
|
|
78
|
+
renderCall(args: any, theme: any) {
|
|
79
|
+
const pattern = args?.pattern ?? "";
|
|
80
|
+
const p = shortenPath(args?.path);
|
|
81
|
+
const glob = args?.glob ? ` (${args.glob})` : "";
|
|
82
|
+
return sciCall("grep", `/${pattern}/ in ${p}${glob}`, theme);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// ── Find ──────────────────────────────────────────────────────────────
|
|
87
|
+
pi.registerToolRenderer("find", {
|
|
88
|
+
renderCall(args: any, theme: any) {
|
|
89
|
+
const pattern = args?.pattern ?? "";
|
|
90
|
+
const p = shortenPath(args?.path);
|
|
91
|
+
return sciCall("find", `${pattern} in ${p}`, theme);
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ── Ls ─────────────────────────────────────────────────────────────────
|
|
96
|
+
pi.registerToolRenderer("ls", {
|
|
97
|
+
renderCall(args: any, theme: any) {
|
|
98
|
+
const p = shortenPath(args?.path || ".");
|
|
99
|
+
return sciCall("ls", p, theme);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ── View ──────────────────────────────────────────────────────────────
|
|
104
|
+
pi.registerToolRenderer("view", {
|
|
105
|
+
renderCall(args: any, theme: any) {
|
|
106
|
+
const p = shortenPath(args?.path);
|
|
107
|
+
const page = args?.page ? ` p${args.page}` : "";
|
|
108
|
+
return sciCall("view", `${p}${page}`, theme);
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// ── Web Search ────────────────────────────────────────────────────────
|
|
113
|
+
pi.registerToolRenderer("web_search", {
|
|
114
|
+
renderCall(args: any, theme: any) {
|
|
115
|
+
const query = args?.query ?? "";
|
|
116
|
+
const mode = args?.mode ?? "quick";
|
|
117
|
+
const display = query.length > 55 ? query.slice(0, 52) + "…" : query;
|
|
118
|
+
const modeTag = mode !== "quick" ? ` [${mode}]` : "";
|
|
119
|
+
return sciCall("web_search", `${display}${modeTag}`, theme);
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ── Chronos ───────────────────────────────────────────────────────────
|
|
124
|
+
pi.registerToolRenderer("chronos", {
|
|
125
|
+
renderCall(args: any, theme: any) {
|
|
126
|
+
const sub = args?.subcommand ?? "week";
|
|
127
|
+
const expr = args?.expression ? ` "${args.expression}"` : "";
|
|
128
|
+
return sciCall("chronos", `${sub}${expr}`, theme);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ── Render Diagram (D2) ───────────────────────────────────────────────
|
|
133
|
+
pi.registerToolRenderer("render_diagram", {
|
|
134
|
+
renderCall(args: any, theme: any) {
|
|
135
|
+
const title = args?.title ?? "diagram";
|
|
136
|
+
return sciCall("render_diagram", title, theme);
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// ── Render Native Diagram ─────────────────────────────────────────────
|
|
141
|
+
pi.registerToolRenderer("render_native_diagram", {
|
|
142
|
+
renderCall(args: any, theme: any) {
|
|
143
|
+
const title = args?.title ?? "diagram";
|
|
144
|
+
return sciCall("render_native_diagram", title, theme);
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ── Render Excalidraw ─────────────────────────────────────────────────
|
|
149
|
+
pi.registerToolRenderer("render_excalidraw", {
|
|
150
|
+
renderCall(args: any, theme: any) {
|
|
151
|
+
const p = shortenPath(args?.path);
|
|
152
|
+
return sciCall("render_excalidraw", p, theme);
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ── Generate Image Local ──────────────────────────────────────────────
|
|
157
|
+
pi.registerToolRenderer("generate_image_local", {
|
|
158
|
+
renderCall(args: any, theme: any) {
|
|
159
|
+
const prompt = args?.prompt ?? "";
|
|
160
|
+
const preset = args?.preset ?? "schnell";
|
|
161
|
+
const display = prompt.length > 50 ? prompt.slice(0, 47) + "…" : prompt;
|
|
162
|
+
return sciCall("generate_image_local", `${display} [${preset}]`, theme);
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ── Render Composition Still ──────────────────────────────────────────
|
|
167
|
+
pi.registerToolRenderer("render_composition_still", {
|
|
168
|
+
renderCall(args: any, theme: any) {
|
|
169
|
+
const p = shortenPath(args?.composition_path);
|
|
170
|
+
const frame = args?.frame != null ? ` f${args.frame}` : "";
|
|
171
|
+
return sciCall("render_composition_still", `${p}${frame}`, theme);
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// ── Render Composition Video ──────────────────────────────────────────
|
|
176
|
+
pi.registerToolRenderer("render_composition_video", {
|
|
177
|
+
renderCall(args: any, theme: any) {
|
|
178
|
+
const p = shortenPath(args?.composition_path);
|
|
179
|
+
const frames = args?.duration_in_frames ?? "?";
|
|
180
|
+
const fmt = args?.format ?? "gif";
|
|
181
|
+
return sciCall("render_composition_video", `${p} (${frames}f, ${fmt})`, theme);
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ── Model Tier ────────────────────────────────────────────────────────
|
|
186
|
+
pi.registerToolRenderer("set_model_tier", {
|
|
187
|
+
renderCall(args: any, theme: any) {
|
|
188
|
+
const tier = args?.tier ?? "?";
|
|
189
|
+
return sciCall("set_model_tier", `→ ${tier}`, theme);
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ── Thinking Level ────────────────────────────────────────────────────
|
|
194
|
+
pi.registerToolRenderer("set_thinking_level", {
|
|
195
|
+
renderCall(args: any, theme: any) {
|
|
196
|
+
const level = args?.level ?? "?";
|
|
197
|
+
return sciCall("set_thinking_level", `→ ${level}`, theme);
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// ── Ask Local Model ───────────────────────────────────────────────────
|
|
202
|
+
pi.registerToolRenderer("ask_local_model", {
|
|
203
|
+
renderCall(args: any, theme: any) {
|
|
204
|
+
const model = args?.model ?? "auto";
|
|
205
|
+
const prompt = args?.prompt ?? "";
|
|
206
|
+
const display = prompt.length > 45 ? prompt.slice(0, 42) + "…" : prompt;
|
|
207
|
+
return sciCall("ask_local_model", `[${model}] ${display}`, theme);
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// ── Manage Ollama ─────────────────────────────────────────────────────
|
|
212
|
+
pi.registerToolRenderer("manage_ollama", {
|
|
213
|
+
renderCall(args: any, theme: any) {
|
|
214
|
+
const action = args?.action ?? "?";
|
|
215
|
+
const model = args?.model ? ` ${args.model}` : "";
|
|
216
|
+
return sciCall("manage_ollama", `${action}${model}`, theme);
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// ── List Local Models ─────────────────────────────────────────────────
|
|
221
|
+
pi.registerToolRenderer("list_local_models", {
|
|
222
|
+
renderCall(_args: any, theme: any) {
|
|
223
|
+
return sciCall("list_local_models", "inventory", theme);
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// ── Switch Offline Driver ─────────────────────────────────────────────
|
|
228
|
+
pi.registerToolRenderer("switch_to_offline_driver", {
|
|
229
|
+
renderCall(args: any, theme: any) {
|
|
230
|
+
const reason = args?.reason ?? "";
|
|
231
|
+
const display = reason.length > 50 ? reason.slice(0, 47) + "…" : reason;
|
|
232
|
+
return sciCall("switch_to_offline_driver", display, theme);
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// ── Manage Tools ──────────────────────────────────────────────────────
|
|
237
|
+
pi.registerToolRenderer("manage_tools", {
|
|
238
|
+
renderCall(args: any, theme: any) {
|
|
239
|
+
const action = args?.action ?? "list";
|
|
240
|
+
const tools = args?.tools?.join(", ") ?? "";
|
|
241
|
+
const profile = args?.profile ?? "";
|
|
242
|
+
const detail = tools || profile || "";
|
|
243
|
+
return sciCall("manage_tools", `${action}${detail ? ` ${detail}` : ""}`, theme);
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// ── Whoami ────────────────────────────────────────────────────────────
|
|
248
|
+
pi.registerToolRenderer("whoami", {
|
|
249
|
+
renderCall(_args: any, theme: any) {
|
|
250
|
+
return sciCall("whoami", "check auth", theme);
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { MemoryInjectionMetrics } from "../project-memory/injection-metrics.ts";
|
|
2
|
+
|
|
3
|
+
export interface ContextGaugeInput {
|
|
4
|
+
percent: number | null | undefined;
|
|
5
|
+
contextWindow: number;
|
|
6
|
+
memoryTokenEstimate: number;
|
|
7
|
+
turns: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ContextGaugeModel {
|
|
11
|
+
state: "known" | "unknown";
|
|
12
|
+
turns: number;
|
|
13
|
+
contextWindow: number;
|
|
14
|
+
percent: number | null;
|
|
15
|
+
memoryPercent: number;
|
|
16
|
+
otherPercent: number;
|
|
17
|
+
memoryBlocks: number;
|
|
18
|
+
otherBlocks: number;
|
|
19
|
+
freeBlocks: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function buildContextGaugeModel(input: ContextGaugeInput, barWidth: number): ContextGaugeModel {
|
|
23
|
+
const contextWindow = input.contextWindow;
|
|
24
|
+
const pct = input.percent ?? null;
|
|
25
|
+
|
|
26
|
+
if (pct === null) {
|
|
27
|
+
return {
|
|
28
|
+
state: "unknown",
|
|
29
|
+
turns: input.turns,
|
|
30
|
+
contextWindow,
|
|
31
|
+
percent: null,
|
|
32
|
+
memoryPercent: 0,
|
|
33
|
+
otherPercent: 0,
|
|
34
|
+
memoryBlocks: 0,
|
|
35
|
+
otherBlocks: 0,
|
|
36
|
+
freeBlocks: barWidth,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const memoryPercent = contextWindow > 0 ? (input.memoryTokenEstimate / contextWindow) * 100 : 0;
|
|
41
|
+
const otherPercent = Math.max(0, pct - memoryPercent);
|
|
42
|
+
const memoryBlocks = memoryPercent > 0 ? Math.ceil((memoryPercent / 100) * barWidth) : 0;
|
|
43
|
+
const otherBlocks = otherPercent > 0 ? Math.ceil((otherPercent / 100) * barWidth) : 0;
|
|
44
|
+
const totalFilled = Math.min(memoryBlocks + otherBlocks, barWidth);
|
|
45
|
+
const freeBlocks = barWidth - totalFilled;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
state: "known",
|
|
49
|
+
turns: input.turns,
|
|
50
|
+
contextWindow,
|
|
51
|
+
percent: pct,
|
|
52
|
+
memoryPercent,
|
|
53
|
+
otherPercent,
|
|
54
|
+
memoryBlocks,
|
|
55
|
+
otherBlocks,
|
|
56
|
+
freeBlocks,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
|
|
3
|
+
export function shouldRefreshDesignTreeForPath(filePath: string, docsDir: string): boolean {
|
|
4
|
+
const rel = path.relative(docsDir, filePath);
|
|
5
|
+
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return false;
|
|
6
|
+
return rel.endsWith(".md");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function shouldRefreshOpenSpecForPath(filePath: string, repoRoot: string): boolean {
|
|
10
|
+
const openspecDir = path.join(repoRoot, "openspec");
|
|
11
|
+
const rel = path.relative(openspecDir, filePath);
|
|
12
|
+
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return false;
|
|
13
|
+
return rel.endsWith(".md");
|
|
14
|
+
}
|