@pi-unipi/utility 2.6.1 → 2.9.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/README.md +11 -77
- package/package.json +12 -12
- package/src/commands.ts +4 -5
- package/src/herdr-sync.ts +136 -0
- package/src/index.ts +14 -72
- package/src/lifecycle/cleanup.ts +2 -16
- package/src/{diff/settings.ts → settings.ts} +42 -80
- package/src/tools/env.ts +0 -0
- package/src/tui/name-badge-state.ts +18 -2
- package/src/tui/name-badge.ts +5 -4
- package/src/tui/util-settings-tui.ts +41 -224
- package/src/cache/ttl-cache.ts +0 -311
- package/src/diff/highlighter.ts +0 -352
- package/src/diff/parser.ts +0 -191
- package/src/diff/renderer.ts +0 -395
- package/src/diff/theme.ts +0 -316
- package/src/diff/wrapper.ts +0 -339
- package/src/display/capabilities.ts +0 -214
- package/src/display/width.ts +0 -226
- package/src/tools/batch.ts +0 -229
- package/src/tui/badge-settings-tui.ts +0 -388
- package/src/tui/badge-settings.ts +0 -67
- package/src/tui/settings-inspector.ts +0 -303
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @pi-unipi/utility
|
|
2
2
|
|
|
3
|
-
Environment info, diagnostics, cleanup,
|
|
4
|
-
|
|
5
|
-
The diff rendering is the standout feature — Shiki-powered syntax-highlighted diffs for `write` and `edit` tool output. Side-by-side view for edits, unified view for writes, with color presets and auto-fallback on narrow terminals.
|
|
3
|
+
Environment info, diagnostics, cleanup, and the session name badge. The grab-bag package for maintaining your development environment and keeping an eye on provider cache behavior.
|
|
6
4
|
|
|
7
5
|
## Commands
|
|
8
6
|
|
|
@@ -15,7 +13,9 @@ The diff rendering is the standout feature — Shiki-powered syntax-highlighted
|
|
|
15
13
|
| `/unipi:reload` | Explain how to reload extensions |
|
|
16
14
|
| `/unipi:name-badge` | Toggle name badge overlay |
|
|
17
15
|
| `/unipi:badge-gen` | Generate session name via LLM and enable badge |
|
|
18
|
-
| `/unipi:
|
|
16
|
+
| `/unipi:badge-name` | Set the session name shown in the badge |
|
|
17
|
+
| `/unipi:badge-settings` | Configure badge behavior |
|
|
18
|
+
| `/unipi:util-settings` | Unified settings TUI (badge) |
|
|
19
19
|
| `/unipi:prefix-cache` | Show privacy-safe request-prefix transitions and provider cache token counters |
|
|
20
20
|
|
|
21
21
|
### Examples
|
|
@@ -31,7 +31,7 @@ The diff rendering is the standout feature — Shiki-powered syntax-highlighted
|
|
|
31
31
|
|
|
32
32
|
## Special Triggers
|
|
33
33
|
|
|
34
|
-
Utility registers with the info-screen dashboard, showing module status and diagnostic results. The footer subscribes to utility events for its extension status segment.
|
|
34
|
+
Utility registers with the info-screen dashboard, showing module status and diagnostic results. The footer subscribes to utility events for its extension status segment. When Herdr is present, the badge module syncs the session name to the pane title.
|
|
35
35
|
|
|
36
36
|
## Provider prefix-cache diagnostics
|
|
37
37
|
|
|
@@ -41,71 +41,31 @@ The fingerprints are keyed HMAC-SHA-256 values using a random in-memory key that
|
|
|
41
41
|
|
|
42
42
|
`/unipi:cleanup` includes private `~/.unipi/tool-results/` artifacts in the normal temporary-file retention policy (7 days by default). Use dry-run mode to review candidates before deletion.
|
|
43
43
|
|
|
44
|
-
The diff rendering feature wraps Pi's built-in `write` and `edit` tools. When enabled, these tools show syntax-highlighted diffs instead of plain output. This is a transparent replacement — the agent doesn't need to know about it.
|
|
45
|
-
|
|
46
44
|
## Agent Tools
|
|
47
45
|
|
|
48
46
|
| Tool | Description |
|
|
49
47
|
|------|-------------|
|
|
50
|
-
| `ctx_batch` | Atomic batch execution with rollback support |
|
|
51
48
|
| `ctx_env` | Environment inspection for debugging |
|
|
52
|
-
| `
|
|
53
|
-
| `edit` | Edit file with split/unified diff view (when diff enabled) |
|
|
54
|
-
|
|
55
|
-
### Batch Execution
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { BatchBuilder } from "@pi-unipi/utility/tools/batch";
|
|
59
|
-
|
|
60
|
-
const report = await new BatchBuilder()
|
|
61
|
-
.addCommand("search", { query: "refactor" })
|
|
62
|
-
.addTool("memory_search", { query: "patterns" })
|
|
63
|
-
.withOptions({ failFast: true, commandTimeoutMs: 30000 })
|
|
64
|
-
.execute(myExecutor);
|
|
65
|
-
|
|
66
|
-
if (!report.success) {
|
|
67
|
-
console.log("Failed:", report.results.find(r => !r.success)?.error);
|
|
68
|
-
}
|
|
69
|
-
```
|
|
49
|
+
| `set_session_name` | Set the session name for badge display (when badge agent-tool is enabled) |
|
|
70
50
|
|
|
71
51
|
## Configurables
|
|
72
52
|
|
|
73
|
-
###
|
|
53
|
+
### Name Badge
|
|
74
54
|
|
|
75
55
|
```
|
|
76
56
|
/unipi:util-settings # Open unified settings TUI
|
|
77
57
|
```
|
|
78
58
|
|
|
79
|
-
Or edit `.unipi/config/util-settings.json` directly:
|
|
59
|
+
Or edit `.unipi/config/util-settings.json` directly (migrated automatically from the legacy `.unipi/config/badge.json` on first read):
|
|
80
60
|
|
|
81
61
|
```json
|
|
82
62
|
{
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"theme": "default",
|
|
86
|
-
"shikiTheme": "github-dark",
|
|
87
|
-
"splitMinWidth": 150
|
|
63
|
+
"badge": {
|
|
64
|
+
"badgeEnabled": true
|
|
88
65
|
}
|
|
89
66
|
}
|
|
90
67
|
```
|
|
91
68
|
|
|
92
|
-
| Setting | Default | Options |
|
|
93
|
-
|---------|---------|---------|
|
|
94
|
-
| `enabled` | true | true/false |
|
|
95
|
-
| `theme` | "default" | default, midnight, subtle, neon |
|
|
96
|
-
| `shikiTheme` | "github-dark" | github-dark, dracula, one-dark-pro, catppuccin-mocha, nord, tokyo-night |
|
|
97
|
-
| `splitMinWidth` | 150 | Minimum terminal width for split view |
|
|
98
|
-
|
|
99
|
-
Environment variable overrides: `DIFF_ADD_BG`, `DIFF_REM_BG`, etc.
|
|
100
|
-
|
|
101
|
-
Features:
|
|
102
|
-
- Split view (side-by-side) for `edit`, auto-falls back to unified on narrow terminals
|
|
103
|
-
- Unified view (stacked) for `write` overwrites
|
|
104
|
-
- LRU cache (192 entries) for Shiki highlights
|
|
105
|
-
- Large diff fallback (skip highlighting above 80k chars)
|
|
106
|
-
|
|
107
|
-
### Name Badge
|
|
108
|
-
|
|
109
69
|
The badge is a persistent HUD overlay in the top-right corner showing the current session name. It auto-restores visibility on session restart.
|
|
110
70
|
|
|
111
71
|
## Programmatic API
|
|
@@ -113,35 +73,9 @@ The badge is a persistent HUD overlay in the top-right corner showing the curren
|
|
|
113
73
|
| Module | Path | Description |
|
|
114
74
|
|--------|------|-------------|
|
|
115
75
|
| ProcessLifecycle | `lifecycle/process` | Parent PID polling, orphan detection, signal handlers |
|
|
116
|
-
| cleanupStale | `lifecycle/cleanup` | Stale DB/temp/session
|
|
117
|
-
| TTLCache | `cache/ttl-cache` | Memory or SQLite-backed TTL cache |
|
|
76
|
+
| cleanupStale | `lifecycle/cleanup` | Stale DB/temp/session cleanup with dry-run |
|
|
118
77
|
| AnalyticsCollector | `analytics/collector` | Privacy-respecting event collection with daily rollup |
|
|
119
78
|
| runDiagnostics | `diagnostics/engine` | Cross-module health checks with plugin architecture |
|
|
120
|
-
| detectCapabilities | `display/capabilities` | Terminal feature detection (color, Nerd Font, unicode) |
|
|
121
|
-
| Width Utilities | `display/width` | ANSI-aware clamp, wrap, collapse, pad, center |
|
|
122
|
-
|
|
123
|
-
### TTL Cache
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
import { TTLCache } from "@pi-unipi/utility/cache/ttl-cache";
|
|
127
|
-
|
|
128
|
-
const cache = new TTLCache({ defaultTtlMs: 60000 });
|
|
129
|
-
await cache.set("key", { data: "value" });
|
|
130
|
-
const value = await cache.get("key");
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### Terminal Capabilities
|
|
134
|
-
|
|
135
|
-
```typescript
|
|
136
|
-
import { detectCapabilities, getIcon } from "@pi-unipi/utility/display/capabilities";
|
|
137
|
-
|
|
138
|
-
const caps = detectCapabilities();
|
|
139
|
-
console.log("Nerd Font:", caps.nerdFont);
|
|
140
|
-
console.log("Truecolor:", caps.truecolor);
|
|
141
|
-
console.log(getIcon("", "[OK]")); // Uses Nerd Font if available
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Capability detection distinguishes basic color, xterm-256-compatible terminals, and truecolor-capable terminals. Apple Terminal is treated as color-capable but not truecolor-capable so renderers can fall back safely.
|
|
145
79
|
|
|
146
80
|
## Privacy
|
|
147
81
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/utility",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Utility commands and tools for Pi coding agent
|
|
3
|
+
"version": "2.9.0",
|
|
4
|
+
"description": "Utility commands and tools for Pi coding agent \u2014 lifecycle, diagnostics, cache, analytics, display, batch execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"license": "MIT",
|
|
@@ -36,24 +36,24 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@pi-unipi/core": "2.
|
|
40
|
-
"diff": "^7.0.0",
|
|
41
|
-
"shiki": "^4.0.2"
|
|
42
|
-
},
|
|
43
|
-
"devDependencies": {
|
|
44
|
-
"@types/diff": "^7.0.2"
|
|
39
|
+
"@pi-unipi/core": "2.9.0"
|
|
45
40
|
},
|
|
41
|
+
"devDependencies": {},
|
|
46
42
|
"peerDependencies": {
|
|
47
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
48
|
-
"@earendil-works/pi-tui": "^0.
|
|
43
|
+
"@earendil-works/pi-coding-agent": "^0.84.0",
|
|
44
|
+
"@earendil-works/pi-tui": "^0.84.0",
|
|
49
45
|
"typebox": "^1.1.38"
|
|
50
46
|
},
|
|
51
47
|
"scripts": {
|
|
52
48
|
"test": "npx tsx --test tests/**/*.test.ts"
|
|
53
49
|
},
|
|
54
50
|
"pi": {
|
|
55
|
-
"extensions": [
|
|
56
|
-
|
|
51
|
+
"extensions": [
|
|
52
|
+
"./src/index.ts"
|
|
53
|
+
],
|
|
54
|
+
"skills": [
|
|
55
|
+
"./skills"
|
|
56
|
+
],
|
|
57
57
|
"prompts": [],
|
|
58
58
|
"themes": []
|
|
59
59
|
}
|
package/src/commands.ts
CHANGED
|
@@ -21,8 +21,7 @@ import { cleanupStale, formatCleanupReport } from "./lifecycle/cleanup.js";
|
|
|
21
21
|
import { runDiagnostics, formatDiagnosticsReport } from "./diagnostics/engine.js";
|
|
22
22
|
import { getEnvironmentInfo, formatEnvironmentInfo } from "./tools/env.js";
|
|
23
23
|
import type { NameBadgeState } from "./tui/name-badge-state.js";
|
|
24
|
-
import { readBadgeSettings, updateBadgeSetting, formatBadgeSettings } from "./
|
|
25
|
-
import { BadgeSettingsTui } from "./tui/badge-settings-tui.js";
|
|
24
|
+
import { readBadgeSettings, updateBadgeSetting, formatBadgeSettings } from "./settings.js";
|
|
26
25
|
import { UtilSettingsTui } from "./tui/util-settings-tui.js";
|
|
27
26
|
|
|
28
27
|
/** Send a markdown response via pi.sendMessage */
|
|
@@ -77,14 +76,14 @@ export function registerNameBadgeCommands(
|
|
|
77
76
|
|
|
78
77
|
// ─── /unipi:badge-toggle — configure badge settings ─────────────────────
|
|
79
78
|
pi.registerCommand(`${UNIPI_PREFIX}${UTILITY_COMMANDS.BADGE_TOGGLE}`, {
|
|
80
|
-
description: "Configure badge settings (autoGen, badgeEnabled, agentTool)",
|
|
79
|
+
description: "Configure badge settings (autoGen, badgeEnabled, agentTool, herdrSync)",
|
|
81
80
|
handler: async (args: string, ctx: ExtensionContext) => {
|
|
82
81
|
// Parse args: /unipi:badge-settings [key] [on|off]
|
|
83
82
|
const parts = args.trim().split(/\s+/);
|
|
84
83
|
if (parts.length >= 2 && parts[0]) {
|
|
85
|
-
const key = parts[0] as "autoGen" | "badgeEnabled" | "agentTool";
|
|
84
|
+
const key = parts[0] as "autoGen" | "badgeEnabled" | "agentTool" | "herdrSync";
|
|
86
85
|
const value = parts[1]?.toLowerCase();
|
|
87
|
-
if ("autoGen|badgeEnabled|agentTool".includes(key)) {
|
|
86
|
+
if ("autoGen|badgeEnabled|agentTool|herdrSync".includes(key)) {
|
|
88
87
|
const boolValue = value === "on" || value === "true" || value === "1";
|
|
89
88
|
updateBadgeSetting(key, boolValue);
|
|
90
89
|
ctx.ui.notify(`Badge ${key} set to ${boolValue}`, "info");
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/utility — Herdr pane title sync
|
|
3
|
+
*
|
|
4
|
+
* When running inside a Herdr pane, pushes the pi session name to Herdr's
|
|
5
|
+
* socket API (`pane.report_metadata` with `title`). Herdr displays the title
|
|
6
|
+
* in the pane border / agent panel — always visible, never scrolls away.
|
|
7
|
+
*
|
|
8
|
+
* This is the scroll-proof session name display: the in-TUI badge overlay
|
|
9
|
+
* scrolls with content (pi renders to the primary buffer), but Herdr's pane
|
|
10
|
+
* border is drawn by Herdr itself outside the terminal scrollback.
|
|
11
|
+
*
|
|
12
|
+
* Detection: Herdr sets HERDR_ENV=1 and HERDR_SOCKET_PATH + HERDR_PANE_ID
|
|
13
|
+
* in every pane it spawns (same env the official herdr-pi integration uses).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createConnection } from "node:net";
|
|
17
|
+
|
|
18
|
+
/** Push timeout — never block the agent on a slow socket. */
|
|
19
|
+
const SEND_TIMEOUT_MS = 500;
|
|
20
|
+
|
|
21
|
+
export interface HerdrEnv {
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
socketPath?: string;
|
|
24
|
+
paneId?: string;
|
|
25
|
+
tabId?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Read Herdr environment (call once per session_start, cheap). */
|
|
29
|
+
export function detectHerdr(): HerdrEnv {
|
|
30
|
+
const env = process.env.HERDR_ENV;
|
|
31
|
+
const socketPath = process.env.HERDR_SOCKET_PATH;
|
|
32
|
+
const paneId = process.env.HERDR_PANE_ID;
|
|
33
|
+
const tabId = process.env.HERDR_TAB_ID;
|
|
34
|
+
if (env === "1" && socketPath && paneId) {
|
|
35
|
+
return { enabled: true, socketPath, paneId, tabId };
|
|
36
|
+
}
|
|
37
|
+
return { enabled: false };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sendRequest(socketPath: string, request: unknown): Promise<void> {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
let done = false;
|
|
43
|
+
const finish = () => {
|
|
44
|
+
if (done) return;
|
|
45
|
+
done = true;
|
|
46
|
+
socket.destroy();
|
|
47
|
+
resolve();
|
|
48
|
+
};
|
|
49
|
+
const socket = createConnection(socketPath);
|
|
50
|
+
socket.on("error", finish);
|
|
51
|
+
socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`));
|
|
52
|
+
socket.on("data", finish);
|
|
53
|
+
socket.on("end", finish);
|
|
54
|
+
const timeout = setTimeout(finish, SEND_TIMEOUT_MS);
|
|
55
|
+
timeout.unref?.();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let reportSeq = Date.now();
|
|
60
|
+
|
|
61
|
+
/** Only rename the tab when its label is a default numeric one (1-3 digits) — never clobber a user-set name. */
|
|
62
|
+
const DEFAULT_TAB_LABEL = /^\d{1,3}$/;
|
|
63
|
+
|
|
64
|
+
async function fetchJson(socketPath: string, method: string, params: Record<string, unknown>, extract: (result: any) => Record<string, unknown> | null): Promise<Record<string, unknown> | null> {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
let done = false;
|
|
67
|
+
const finish = (value: Record<string, unknown> | null) => {
|
|
68
|
+
if (done) return;
|
|
69
|
+
done = true;
|
|
70
|
+
socket.destroy();
|
|
71
|
+
resolve(value);
|
|
72
|
+
};
|
|
73
|
+
const socket = createConnection(socketPath);
|
|
74
|
+
let buffer = "";
|
|
75
|
+
socket.on("error", () => finish(null));
|
|
76
|
+
socket.on("connect", () =>
|
|
77
|
+
socket.write(
|
|
78
|
+
`${JSON.stringify({ id: `unipi:fetch:${method}:${Date.now()}`, method, params })}\n`,
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
socket.on("data", (chunk) => {
|
|
82
|
+
buffer += chunk.toString();
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(buffer);
|
|
85
|
+
finish(extract(parsed?.result));
|
|
86
|
+
} catch {
|
|
87
|
+
/* keep buffering */
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
socket.on("end", () => finish(null));
|
|
91
|
+
const timeout = setTimeout(() => finish(null), SEND_TIMEOUT_MS);
|
|
92
|
+
timeout.unref?.();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Push the session name to Herdr as the pane title, and as the tab label
|
|
98
|
+
* when the tab still has its default numeric label.
|
|
99
|
+
* Fire-and-forget: never throws, resolves after send timeout at worst.
|
|
100
|
+
*/
|
|
101
|
+
export async function syncPaneTitle(env: HerdrEnv, sessionName: string | null): Promise<void> {
|
|
102
|
+
if (!env.enabled || !env.socketPath || !env.paneId) return;
|
|
103
|
+
reportSeq += 1;
|
|
104
|
+
const params: Record<string, unknown> = {
|
|
105
|
+
pane_id: env.paneId,
|
|
106
|
+
source: "unipi:badge",
|
|
107
|
+
seq: reportSeq,
|
|
108
|
+
};
|
|
109
|
+
if (sessionName) {
|
|
110
|
+
params.title = sessionName;
|
|
111
|
+
} else {
|
|
112
|
+
params.clear_title = true;
|
|
113
|
+
}
|
|
114
|
+
// Best effort — errors are swallowed by sendRequest's error handler.
|
|
115
|
+
await sendRequest(env.socketPath, {
|
|
116
|
+
id: `unipi:badge:title:${Date.now()}:${Math.random().toString(36).slice(2)}`,
|
|
117
|
+
method: "pane.report_metadata",
|
|
118
|
+
params,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Also sync the tab label so the name is visible in the tab bar (single-pane
|
|
122
|
+
// tabs don't render pane borders, so the pane title alone is invisible there).
|
|
123
|
+
// Only rename when the label is still the default numeric one — a user-set
|
|
124
|
+
// name always wins.
|
|
125
|
+
if (sessionName && env.tabId) {
|
|
126
|
+
const tab = await fetchJson(env.socketPath, "tab.get", { tab_id: env.tabId }, (r) => r?.tab ?? null);
|
|
127
|
+
const label = tab?.label;
|
|
128
|
+
if (typeof label !== "string" || DEFAULT_TAB_LABEL.test(label)) {
|
|
129
|
+
await sendRequest(env.socketPath, {
|
|
130
|
+
id: `unipi:badge:tab:${Date.now()}:${Math.random().toString(36).slice(2)}`,
|
|
131
|
+
method: "tab.rename",
|
|
132
|
+
params: { tab_id: env.tabId, label: sessionName },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Comprehensive utilities suite for Pi coding agent:
|
|
5
5
|
* - Commands: continue, reload, status, cleanup, env, doctor, badge
|
|
6
|
-
* - Tools:
|
|
6
|
+
* - Tools: ctx_env, set_session_name
|
|
7
7
|
* - Lifecycle: process management, stale cleanup
|
|
8
|
-
* - Cache: TTL cache with optional persistence
|
|
9
8
|
* - Analytics: lightweight event collection
|
|
10
9
|
* - Diagnostics: cross-module health checks
|
|
11
|
-
* -
|
|
12
|
-
* - TUI: settings
|
|
10
|
+
* - Prefix-cache observability: privacy-safe provider cache stats
|
|
11
|
+
* - TUI: util settings, name badge (incl. Herdr pane title sync)
|
|
13
12
|
*/
|
|
14
13
|
|
|
15
14
|
import { dirname } from "node:path";
|
|
@@ -26,16 +25,14 @@ import {
|
|
|
26
25
|
} from "@pi-unipi/core";
|
|
27
26
|
import { registerUtilityCommands, registerNameBadgeCommands } from "./commands.js";
|
|
28
27
|
import { NameBadgeState } from "./tui/name-badge-state.js";
|
|
29
|
-
import { readBadgeSettings } from "./
|
|
30
|
-
import { readDiffSettings } from "./diff/settings.js";
|
|
31
|
-
import { registerEnhancedWriteTool, registerEnhancedEditTool } from "./diff/wrapper.js";
|
|
28
|
+
import { readBadgeSettings } from "./settings.js";
|
|
32
29
|
import { getLifecycle } from "./lifecycle/process.js";
|
|
33
30
|
import { getAnalyticsCollector } from "./analytics/collector.js";
|
|
34
31
|
import { registerInfoScreen } from "./info-screen.js";
|
|
35
32
|
import { PrefixCacheTracker, formatPrefixCacheStats } from "./prefix-cache.js";
|
|
36
33
|
|
|
37
34
|
/** Re-export readBadgeSettings for cross-package use */
|
|
38
|
-
export { readBadgeSettings } from "./
|
|
35
|
+
export { readBadgeSettings } from "./settings.js";
|
|
39
36
|
|
|
40
37
|
/** Package version */
|
|
41
38
|
const VERSION = getPackageVersion(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -155,12 +152,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
155
152
|
// Restore name badge if it was visible in previous session
|
|
156
153
|
await nameBadgeState.restore(pi, ctx);
|
|
157
154
|
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
155
|
+
// Auto-show badge on session start if enabled in settings and UI is available.
|
|
156
|
+
// The badge shows "Set a name" placeholder until a name is generated.
|
|
157
|
+
// Previously the badge only showed after the first agent_end, which meant it
|
|
158
|
+
// was absent on restart until the user sent a message.
|
|
159
|
+
if (ctx?.hasUI && !nameBadgeState.isVisible()) {
|
|
160
|
+
const badgeSettings = readBadgeSettings();
|
|
161
|
+
if (badgeSettings.badgeEnabled) {
|
|
162
|
+
await nameBadgeState.show(pi, ctx);
|
|
163
|
+
}
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
// Write model cache for TUI components
|
|
@@ -274,64 +274,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
274
274
|
* Register utility tools.
|
|
275
275
|
*/
|
|
276
276
|
function registerUtilityTools(pi: ExtensionAPI, nameBadgeState: NameBadgeState): void {
|
|
277
|
-
// ctx_batch — atomic batch execution
|
|
278
|
-
pi.registerTool({
|
|
279
|
-
name: UTILITY_TOOLS.BATCH,
|
|
280
|
-
label: "Batch Execute",
|
|
281
|
-
description:
|
|
282
|
-
"Execute a batch of commands atomically with rollback support. " +
|
|
283
|
-
"Accepts an array of {type, name, args} objects. " +
|
|
284
|
-
"Options: failFast (default true), commandTimeoutMs, totalTimeoutMs.",
|
|
285
|
-
promptSnippet: "Run multiple commands as an atomic batch.",
|
|
286
|
-
parameters: {
|
|
287
|
-
type: "object",
|
|
288
|
-
properties: {
|
|
289
|
-
commands: {
|
|
290
|
-
type: "array",
|
|
291
|
-
items: {
|
|
292
|
-
type: "object",
|
|
293
|
-
properties: {
|
|
294
|
-
type: { type: "string", enum: ["command", "tool", "search"] },
|
|
295
|
-
name: { type: "string" },
|
|
296
|
-
args: { type: "object" },
|
|
297
|
-
},
|
|
298
|
-
required: ["type", "name"],
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
options: {
|
|
302
|
-
type: "object",
|
|
303
|
-
properties: {
|
|
304
|
-
failFast: { type: "boolean" },
|
|
305
|
-
commandTimeoutMs: { type: "number" },
|
|
306
|
-
totalTimeoutMs: { type: "number" },
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
required: ["commands"],
|
|
311
|
-
},
|
|
312
|
-
async execute(_toolCallId, params) {
|
|
313
|
-
const { commands, options } = params as unknown as {
|
|
314
|
-
commands: Array<{ type: string; name: string; args?: Record<string, unknown> }>;
|
|
315
|
-
options?: Record<string, unknown>;
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
// Tool implementation delegates to batch executor
|
|
319
|
-
// The actual executor must be provided by the host
|
|
320
|
-
return {
|
|
321
|
-
content: [
|
|
322
|
-
{
|
|
323
|
-
type: "text",
|
|
324
|
-
text:
|
|
325
|
-
"ctx_batch requires a command executor from the host environment. " +
|
|
326
|
-
`Received ${commands.length} commands. ` +
|
|
327
|
-
"Use BatchBuilder or executeBatch() directly in code.",
|
|
328
|
-
},
|
|
329
|
-
],
|
|
330
|
-
details: { commands, options },
|
|
331
|
-
};
|
|
332
|
-
},
|
|
333
|
-
});
|
|
334
|
-
|
|
335
277
|
// ctx_env — environment info
|
|
336
278
|
pi.registerTool({
|
|
337
279
|
name: UTILITY_TOOLS.ENV,
|
package/src/lifecycle/cleanup.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Cleans stale DBs, temp files, old sessions across all unipi modules.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { existsSync, statSync, readdirSync, unlinkSync,
|
|
7
|
+
import { existsSync, statSync, readdirSync, unlinkSync, rmSync } from "node:fs";
|
|
8
8
|
import { join, resolve, basename } from "node:path";
|
|
9
9
|
import { homedir, tmpdir } from "node:os";
|
|
10
10
|
import type { CleanupReport, CleanupResult, CleanupOptions } from "../types.js";
|
|
@@ -229,21 +229,7 @@ function cleanSessions(options: Required<CleanupOptions>): CleanupResult {
|
|
|
229
229
|
result.paths.push(fullPath);
|
|
230
230
|
if (!options.dryRun) {
|
|
231
231
|
try {
|
|
232
|
-
|
|
233
|
-
const removeRecursive = (dir: string) => {
|
|
234
|
-
const items = readdirSync(dir);
|
|
235
|
-
for (const item of items) {
|
|
236
|
-
const itemPath = join(dir, item);
|
|
237
|
-
const itemStats = statSync(itemPath);
|
|
238
|
-
if (itemStats.isDirectory()) {
|
|
239
|
-
removeRecursive(itemPath);
|
|
240
|
-
} else {
|
|
241
|
-
unlinkSync(itemPath);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
rmdirSync(dir);
|
|
245
|
-
};
|
|
246
|
-
removeRecursive(fullPath);
|
|
232
|
+
rmSync(fullPath, { recursive: true, force: true });
|
|
247
233
|
result.removed++;
|
|
248
234
|
} catch {
|
|
249
235
|
// Best effort
|