pi-ultracode 0.6.0 → 0.7.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.zh-CN.md +5 -0
- package/extensions/ultracode.ts +14 -4
- package/package.json +1 -1
- package/src/commands.ts +29 -4
- package/src/mode.ts +7 -7
- package/src/preferences.ts +50 -0
package/README.zh-CN.md
CHANGED
|
@@ -66,10 +66,15 @@ Pi 会自行判断工作流是否有帮助。小任务仍可继续使用普通
|
|
|
66
66
|
| `/ultracode deep` | 固定使用高保障深度验证 |
|
|
67
67
|
| `/ultracode off` | 关闭且不改变父代理 effort |
|
|
68
68
|
| `/ultracode status` | 查看配置的语义深度模式 |
|
|
69
|
+
| `/ultracode default on` | 全局默认开启,新会话使用 `auto` |
|
|
70
|
+
| `/ultracode default off` | 取消默认开启 |
|
|
71
|
+
| `/ultracode default` | 查看全局启动默认值 |
|
|
69
72
|
| `/workflows` 或 `F6` | 打开工作流浏览器 |
|
|
70
73
|
| `/workflows <runId>` | 打开指定运行 |
|
|
71
74
|
| `/workflows abort` | 中止活动运行 |
|
|
72
75
|
|
|
76
|
+
`/ultracode default on|off` 只修改启动默认值,不切换当前会话的模式,也不改变 effort。设置保存在 `~/.pi/agent/ultracode.json`(遵循 `PI_CODING_AGENT_DIR`),跨项目和重启生效;未设置时默认关闭。启动时,只有当前分支没有保存 Ultracode 模式才应用默认值,已有的 `off` 或深度选择在 reload、resume、fork 后继续保留。显式 `pi --ultracode` 仍可启用 `auto`。如需立即开启当前会话,执行 `/ultracode auto`。
|
|
77
|
+
|
|
73
78
|
按 `Esc` 可取消正在运行的工作流。在 Pi 的 fullscreen TUI 中,请在工作流详情内使用 `Ctrl+PageUp`、`Ctrl+PageDown` 和 `Ctrl+End`。
|
|
74
79
|
|
|
75
80
|
## 分析深度
|
package/extensions/ultracode.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
9
9
|
import { createWorkflowTool, type WorkflowToolDeps } from "../src/workflow/tool.ts";
|
|
10
10
|
import { UltracodeMode, type ThinkingPreferenceStore } from "../src/mode.ts";
|
|
11
11
|
import { registerCommands } from "../src/commands.ts";
|
|
12
|
+
import { UltracodePreferences, type UltracodePreferenceStore } from "../src/preferences.ts";
|
|
12
13
|
import { WorkflowRegistry } from "../src/workflow/registry.ts";
|
|
13
14
|
|
|
14
15
|
export interface ThinkingPreferenceContext {
|
|
@@ -17,6 +18,7 @@ export interface ThinkingPreferenceContext {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export interface UltracodeExtensionDeps extends Partial<WorkflowToolDeps> {
|
|
21
|
+
preferences?: UltracodePreferenceStore;
|
|
20
22
|
/** @deprecated Parent effort is user-owned; retained for source compatibility. */
|
|
21
23
|
createThinkingPreferenceStore?: (
|
|
22
24
|
context: ThinkingPreferenceContext,
|
|
@@ -27,6 +29,7 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
|
|
|
27
29
|
const mode = new UltracodeMode("workflow");
|
|
28
30
|
const {
|
|
29
31
|
createThinkingPreferenceStore: _unusedThinkingPreferenceStore,
|
|
32
|
+
preferences = new UltracodePreferences(),
|
|
30
33
|
...workflowDeps
|
|
31
34
|
} = extraDeps;
|
|
32
35
|
|
|
@@ -38,7 +41,7 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
|
|
|
38
41
|
});
|
|
39
42
|
pi.registerTool(workflowTool);
|
|
40
43
|
|
|
41
|
-
registerCommands(pi, mode, registry);
|
|
44
|
+
registerCommands(pi, mode, registry, preferences);
|
|
42
45
|
|
|
43
46
|
// Opt-in via CLI flag: `pi --ultracode`.
|
|
44
47
|
pi.registerFlag("ultracode", {
|
|
@@ -65,16 +68,23 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
|
|
|
65
68
|
|
|
66
69
|
pi.on("session_start", async (_event, ctx) => {
|
|
67
70
|
// Restore persisted mode state across reload / resume / fork.
|
|
71
|
+
let hasSavedMode = true;
|
|
68
72
|
try {
|
|
69
73
|
// Mode entries are branch-local; discarded future branches must not win.
|
|
70
|
-
mode.restore(pi, ctx.sessionManager.getBranch() as any);
|
|
74
|
+
hasSavedMode = mode.restore(pi, ctx.sessionManager.getBranch() as any);
|
|
71
75
|
} catch {
|
|
72
|
-
//
|
|
76
|
+
// Do not apply a startup default when branch restoration failed.
|
|
73
77
|
}
|
|
74
78
|
if (!mode.isEnabled() && pi.getFlag?.("ultracode") === true) {
|
|
75
79
|
mode.enable(pi, "auto");
|
|
80
|
+
} else if (!hasSavedMode && !mode.isEnabled()) {
|
|
81
|
+
try {
|
|
82
|
+
if (preferences.getDefaultEnabled()) mode.enable(pi, "auto");
|
|
83
|
+
} catch (error) {
|
|
84
|
+
ctx.ui.notify(`Failed to read Ultracode default: ${String(error)}`, "warning");
|
|
85
|
+
}
|
|
76
86
|
}
|
|
77
|
-
// Registration makes
|
|
87
|
+
// Registration makes tools discoverable; activation follows the chosen mode.
|
|
78
88
|
mode.syncWorkflowTool(pi);
|
|
79
89
|
if (ctx.hasUI) {
|
|
80
90
|
ctx.ui.setStatus(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-ultracode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Adaptive semantic-depth workflow orchestration for Pi, with focused, standard, and deep modes, isolated subagents, worktree delivery, structured output, and durable resume.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
package/src/commands.ts
CHANGED
|
@@ -5,15 +5,21 @@
|
|
|
5
5
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { ACTIVE_ULTRACODE_MODES, isActiveUltracodeMode } from "./depth.ts";
|
|
7
7
|
import type { UltracodeMode } from "./mode.ts";
|
|
8
|
+
import type { UltracodePreferenceStore } from "./preferences.ts";
|
|
8
9
|
import type { WorkflowRegistry } from "./workflow/registry.ts";
|
|
9
10
|
import { workflowRunsDir } from "./workflow/tool.ts";
|
|
10
11
|
import { openWorkflowOverlay } from "./workflow/workflow-overlay.ts";
|
|
11
12
|
|
|
12
|
-
export function registerCommands(
|
|
13
|
+
export function registerCommands(
|
|
14
|
+
pi: ExtensionAPI,
|
|
15
|
+
mode: UltracodeMode,
|
|
16
|
+
registry: WorkflowRegistry,
|
|
17
|
+
preferences: UltracodePreferenceStore,
|
|
18
|
+
): void {
|
|
13
19
|
pi.registerCommand("ultracode", {
|
|
14
|
-
description: "Toggle
|
|
20
|
+
description: "Toggle Ultracode, select auto|focused|standard|deep|off|status, or set default on|off",
|
|
15
21
|
getArgumentCompletions(prefix: string) {
|
|
16
|
-
return [...ACTIVE_ULTRACODE_MODES, "off", "status"]
|
|
22
|
+
return [...ACTIVE_ULTRACODE_MODES, "off", "status", "default", "default on", "default off"]
|
|
17
23
|
.filter((value) => value.startsWith(prefix))
|
|
18
24
|
.map((value) => ({ value, label: value }));
|
|
19
25
|
},
|
|
@@ -37,6 +43,25 @@ export function registerCommands(pi: ExtensionAPI, mode: UltracodeMode, registry
|
|
|
37
43
|
return;
|
|
38
44
|
}
|
|
39
45
|
|
|
46
|
+
if (sub === "default") {
|
|
47
|
+
const value = parts[1]?.toLowerCase();
|
|
48
|
+
if (parts.length > 2 || (value !== undefined && value !== "on" && value !== "off")) {
|
|
49
|
+
ctx.ui.notify("Usage: /ultracode default [on|off]", "error");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
if (value !== undefined) preferences.setDefaultEnabled(value === "on");
|
|
54
|
+
const enabled = preferences.getDefaultEnabled();
|
|
55
|
+
ctx.ui.notify(
|
|
56
|
+
`Ultracode default ${enabled ? "on (auto)" : "off"} — global startup preference; current session unchanged.`,
|
|
57
|
+
"info",
|
|
58
|
+
);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
ctx.ui.notify(`Failed to ${value === undefined ? "read" : "save"} Ultracode default: ${String(error)}`, "error");
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
40
65
|
if (parts.length > 1) {
|
|
41
66
|
ctx.ui.notify(ultracodeUsage(), "error");
|
|
42
67
|
return;
|
|
@@ -102,5 +127,5 @@ export function registerCommands(pi: ExtensionAPI, mode: UltracodeMode, registry
|
|
|
102
127
|
}
|
|
103
128
|
|
|
104
129
|
function ultracodeUsage(): string {
|
|
105
|
-
return "Usage: /ultracode [auto|focused|standard|deep|off|status]";
|
|
130
|
+
return "Usage: /ultracode [auto|focused|standard|deep|off|status] or /ultracode default [on|off]";
|
|
106
131
|
}
|
package/src/mode.ts
CHANGED
|
@@ -152,17 +152,15 @@ export class UltracodeMode {
|
|
|
152
152
|
|
|
153
153
|
/** Turn Ultracode off without changing the parent thinking level. */
|
|
154
154
|
disable(pi: ExtensionAPI): void {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
155
|
+
// Persist an explicit off even when already disabled, so startup defaults
|
|
156
|
+
// cannot override this session's choice on reload or resume.
|
|
159
157
|
this.mode = "off";
|
|
160
158
|
this.suspended = false;
|
|
161
159
|
this.syncWorkflowTool(pi);
|
|
162
160
|
this.persist(pi);
|
|
163
161
|
}
|
|
164
162
|
|
|
165
|
-
/** Restore
|
|
163
|
+
/** Restore branch-local state; return whether a saved mode was found. */
|
|
166
164
|
restore(
|
|
167
165
|
pi: ExtensionAPI,
|
|
168
166
|
entries: Array<{
|
|
@@ -171,7 +169,7 @@ export class UltracodeMode {
|
|
|
171
169
|
data?: unknown;
|
|
172
170
|
thinkingLevel?: unknown;
|
|
173
171
|
}>,
|
|
174
|
-
):
|
|
172
|
+
): boolean {
|
|
175
173
|
let latestData: unknown;
|
|
176
174
|
for (const entry of entries) {
|
|
177
175
|
if (entry.type === "custom" && entry.customType === MODE_ENTRY_TYPE && entry.data) {
|
|
@@ -179,9 +177,11 @@ export class UltracodeMode {
|
|
|
179
177
|
}
|
|
180
178
|
}
|
|
181
179
|
|
|
182
|
-
|
|
180
|
+
const state = parsePersistedModeState(latestData);
|
|
181
|
+
this.mode = state?.mode ?? "off";
|
|
183
182
|
this.suspended = false;
|
|
184
183
|
this.syncWorkflowTool(pi);
|
|
184
|
+
return state !== undefined;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
/** Append the configured semantic-depth policy to the turn's system prompt. */
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Global Ultracode startup preferences, separate from Pi's own settings. */
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
|
|
7
|
+
export interface UltracodePreferenceStore {
|
|
8
|
+
getDefaultEnabled(): boolean;
|
|
9
|
+
setDefaultEnabled(enabled: boolean): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class UltracodePreferences implements UltracodePreferenceStore {
|
|
13
|
+
private readonly path: string;
|
|
14
|
+
|
|
15
|
+
constructor(path = join(getAgentDir(), "ultracode.json")) {
|
|
16
|
+
this.path = path;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getDefaultEnabled(): boolean {
|
|
20
|
+
return this.read().defaultEnabled === true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
setDefaultEnabled(enabled: boolean): void {
|
|
24
|
+
const settings = { ...this.read(), defaultEnabled: enabled };
|
|
25
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
26
|
+
const temporaryPath = `${this.path}.${randomUUID()}.tmp`;
|
|
27
|
+
try {
|
|
28
|
+
writeFileSync(temporaryPath, `${JSON.stringify(settings, null, 2)}\n`, { flag: "wx", mode: 0o600 });
|
|
29
|
+
renameSync(temporaryPath, this.path);
|
|
30
|
+
} finally {
|
|
31
|
+
rmSync(temporaryPath, { force: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private read(): Record<string, unknown> {
|
|
36
|
+
let content: string;
|
|
37
|
+
try {
|
|
38
|
+
content = readFileSync(this.path, "utf8");
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return {};
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
const value: unknown = JSON.parse(content);
|
|
44
|
+
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
45
|
+
|| ("defaultEnabled" in value && typeof value.defaultEnabled !== "boolean")) {
|
|
46
|
+
throw new Error(`Invalid Ultracode preferences in ${this.path}: expected an object with a boolean defaultEnabled.`);
|
|
47
|
+
}
|
|
48
|
+
return value as Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
}
|