@oh-my-pi/pi-coding-agent 13.18.0 → 14.0.2
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/CHANGELOG.md +316 -1
- package/package.json +86 -24
- package/scripts/format-prompts.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +24 -0
- package/src/autoresearch/contract.ts +0 -44
- package/src/autoresearch/dashboard.ts +1 -2
- package/src/autoresearch/git.ts +116 -30
- package/src/autoresearch/helpers.ts +49 -0
- package/src/autoresearch/index.ts +28 -187
- package/src/autoresearch/prompt.md +26 -9
- package/src/autoresearch/state.ts +0 -6
- package/src/autoresearch/tools/init-experiment.ts +202 -117
- package/src/autoresearch/tools/log-experiment.ts +123 -178
- package/src/autoresearch/tools/run-experiment.ts +48 -10
- package/src/autoresearch/types.ts +2 -2
- package/src/capability/index.ts +4 -2
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/grep-cli.ts +8 -8
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/read-cli.ts +67 -0
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/update-cli.ts +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/grep.ts +6 -1
- package/src/commands/grievances.ts +20 -0
- package/src/commands/read.ts +33 -0
- package/src/commit/agentic/agent.ts +5 -8
- package/src/commit/agentic/index.ts +22 -26
- package/src/commit/agentic/tools/analyze-file.ts +3 -3
- package/src/commit/agentic/tools/git-file-diff.ts +3 -6
- package/src/commit/agentic/tools/git-hunk.ts +3 -3
- package/src/commit/agentic/tools/git-overview.ts +6 -9
- package/src/commit/agentic/tools/index.ts +6 -8
- package/src/commit/agentic/tools/propose-commit.ts +4 -7
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/split-commit.ts +4 -4
- package/src/commit/agentic/validation.ts +1 -1
- package/src/commit/analysis/conventional.ts +4 -4
- package/src/commit/analysis/summary.ts +3 -3
- package/src/commit/changelog/generate.ts +4 -4
- package/src/commit/changelog/index.ts +5 -9
- package/src/commit/map-reduce/map-phase.ts +4 -4
- package/src/commit/map-reduce/reduce-phase.ts +4 -4
- package/src/commit/pipeline.ts +13 -16
- package/src/config/keybindings.ts +7 -6
- package/src/config/prompt-templates.ts +44 -226
- package/src/config/resolve-config-value.ts +4 -2
- package/src/config/settings-schema.ts +98 -2
- package/src/config/settings.ts +25 -26
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/log-viewer.ts +3 -2
- package/src/discovery/builtin.ts +1 -2
- package/src/discovery/codex.ts +2 -2
- package/src/discovery/github.ts +2 -1
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/opencode.ts +2 -2
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +309 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +454 -0
- package/src/{patch → edit/modes}/hashline.ts +741 -361
- package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
- package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
- package/src/{patch → edit}/normalize.ts +97 -76
- package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
- package/src/exec/bash-executor.ts +4 -2
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +5 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +6 -18
- package/src/extensibility/custom-commands/bundled/review/index.ts +45 -43
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +34 -11
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/loader.ts +9 -4
- package/src/extensibility/extensions/runner.ts +24 -1
- package/src/extensibility/extensions/types.ts +4 -2
- package/src/extensibility/hooks/loader.ts +5 -6
- package/src/extensibility/hooks/types.ts +2 -2
- package/src/extensibility/plugins/doctor.ts +2 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -57
- package/src/extensibility/plugins/marketplace/source-resolver.ts +4 -4
- package/src/extensibility/slash-commands.ts +3 -7
- package/src/index.ts +3 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/ipy/executor.ts +58 -17
- package/src/ipy/gateway-coordinator.ts +6 -4
- package/src/ipy/kernel.ts +45 -22
- package/src/ipy/runtime.ts +2 -2
- package/src/lsp/client.ts +7 -4
- package/src/lsp/clients/lsp-linter-client.ts +4 -4
- package/src/lsp/config.ts +2 -2
- package/src/lsp/defaults.json +688 -154
- package/src/lsp/index.ts +234 -45
- package/src/lsp/lspmux.ts +2 -2
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +12 -1
- package/src/lsp/utils.ts +8 -1
- package/src/main.ts +125 -47
- package/src/memories/index.ts +4 -5
- package/src/modes/acp/acp-agent.ts +563 -163
- package/src/modes/acp/acp-event-mapper.ts +9 -1
- package/src/modes/acp/acp-mode.ts +4 -2
- package/src/modes/components/agent-dashboard.ts +3 -4
- package/src/modes/components/diff.ts +6 -7
- package/src/modes/components/footer.ts +9 -29
- package/src/modes/components/hook-editor.ts +3 -3
- package/src/modes/components/hook-selector.ts +6 -1
- package/src/modes/components/read-tool-group.ts +6 -12
- package/src/modes/components/session-observer-overlay.ts +472 -0
- package/src/modes/components/settings-defs.ts +24 -0
- package/src/modes/components/status-line.ts +15 -61
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/btw-controller.ts +2 -2
- package/src/modes/controllers/command-controller.ts +4 -2
- package/src/modes/controllers/event-controller.ts +59 -2
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +15 -8
- package/src/modes/controllers/selector-controller.ts +26 -0
- package/src/modes/index.ts +20 -2
- package/src/modes/interactive-mode.ts +278 -69
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +178 -13
- package/src/modes/rpc/rpc-mode.ts +73 -3
- package/src/modes/rpc/rpc-types.ts +53 -1
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/shared.ts +0 -42
- package/src/modes/theme/theme.ts +80 -8
- package/src/modes/types.ts +4 -2
- package/src/modes/utils/keybinding-matchers.ts +9 -0
- package/src/prompts/system/custom-system-prompt.md +5 -0
- package/src/prompts/system/system-prompt.md +8 -1
- package/src/prompts/tools/chunk-edit.md +219 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/grep.md +3 -0
- package/src/prompts/tools/lsp.md +5 -5
- package/src/prompts/tools/read-chunk.md +17 -0
- package/src/prompts/tools/read.md +19 -5
- package/src/sdk.ts +216 -165
- package/src/secrets/index.ts +1 -1
- package/src/secrets/obfuscator.ts +25 -17
- package/src/session/agent-session.ts +381 -286
- package/src/session/agent-storage.ts +12 -12
- package/src/session/compaction/branch-summarization.ts +3 -3
- package/src/session/compaction/compaction.ts +5 -6
- package/src/session/compaction/utils.ts +3 -3
- package/src/session/history-storage.ts +62 -19
- package/src/session/messages.ts +3 -3
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-manager.ts +15 -5
- package/src/session/session-storage.ts +4 -2
- package/src/session/streaming-output.ts +1 -1
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +56 -8
- package/src/ssh/connection-manager.ts +2 -2
- package/src/ssh/sshfs-mount.ts +5 -5
- package/src/stt/downloader.ts +4 -4
- package/src/stt/recorder.ts +4 -4
- package/src/stt/transcriber.ts +2 -2
- package/src/system-prompt.ts +25 -13
- package/src/task/agents.ts +5 -6
- package/src/task/commands.ts +2 -5
- package/src/task/executor.ts +32 -4
- package/src/task/index.ts +91 -82
- package/src/task/template.ts +2 -2
- package/src/task/types.ts +25 -0
- package/src/task/worktree.ts +131 -149
- package/src/tools/ask.ts +2 -3
- package/src/tools/ast-edit.ts +7 -7
- package/src/tools/ast-grep.ts +7 -7
- package/src/tools/auto-generated-guard.ts +36 -41
- package/src/tools/await-tool.ts +2 -2
- package/src/tools/bash.ts +5 -23
- package/src/tools/browser.ts +4 -5
- package/src/tools/calculator.ts +2 -3
- package/src/tools/cancel-job.ts +2 -2
- package/src/tools/checkpoint.ts +3 -3
- package/src/tools/debug.ts +1007 -0
- package/src/tools/exit-plan-mode.ts +3 -3
- package/src/tools/fetch.ts +67 -3
- package/src/tools/find.ts +4 -5
- package/src/tools/fs-cache-invalidation.ts +5 -0
- package/src/tools/gemini-image.ts +13 -5
- package/src/tools/gh.ts +130 -308
- package/src/tools/grep.ts +57 -9
- package/src/tools/index.ts +44 -22
- package/src/tools/inspect-image.ts +4 -4
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/python.ts +19 -6
- package/src/tools/read.ts +211 -146
- package/src/tools/render-mermaid.ts +2 -3
- package/src/tools/render-utils.ts +20 -6
- package/src/tools/renderers.ts +3 -1
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +70 -39
- package/src/tools/search-tool-bm25.ts +2 -2
- package/src/tools/ssh.ts +2 -2
- package/src/tools/todo-write.ts +2 -2
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/write.ts +5 -6
- package/src/tui/tree-list.ts +3 -1
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/commit-message-generator.ts +2 -3
- package/src/utils/edit-mode.ts +49 -0
- package/src/utils/external-editor.ts +11 -5
- package/src/utils/file-display-mode.ts +6 -5
- package/src/utils/file-mentions.ts +8 -7
- package/src/utils/git.ts +1400 -0
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/title-generator.ts +2 -3
- package/src/utils/tools-manager.ts +6 -6
- package/src/web/scrapers/choosealicense.ts +1 -1
- package/src/web/search/index.ts +3 -3
- package/src/web/search/render.ts +6 -4
- package/src/autoresearch/command-initialize.md +0 -34
- package/src/commit/git/errors.ts +0 -9
- package/src/commit/git/index.ts +0 -210
- package/src/commit/git/operations.ts +0 -54
- package/src/patch/diff.ts +0 -433
- package/src/patch/index.ts +0 -888
- package/src/patch/parser.ts +0 -532
- package/src/patch/types.ts +0 -292
- package/src/prompts/agents/oracle.md +0 -77
- package/src/tools/gh-cli.ts +0 -125
- package/src/tools/pending-action.ts +0 -49
- package/src/utils/child-process.ts +0 -88
- package/src/utils/frontmatter.ts +0 -117
- package/src/utils/image-input.ts +0 -274
- package/src/utils/mime.ts +0 -53
- package/src/utils/prompt-format.ts +0 -170
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { AgentProgress, SubagentLifecyclePayload, SubagentProgressPayload } from "../task";
|
|
2
|
+
import { TASK_SUBAGENT_LIFECYCLE_CHANNEL, TASK_SUBAGENT_PROGRESS_CHANNEL } from "../task";
|
|
3
|
+
import type { EventBus } from "../utils/event-bus";
|
|
4
|
+
|
|
5
|
+
export interface ObservableSession {
|
|
6
|
+
id: string;
|
|
7
|
+
kind: "main" | "subagent";
|
|
8
|
+
label: string;
|
|
9
|
+
agent?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
status: "active" | "completed" | "failed" | "aborted";
|
|
12
|
+
sessionFile?: string;
|
|
13
|
+
lastUpdate: number;
|
|
14
|
+
/** Latest progress snapshot from the subagent executor */
|
|
15
|
+
progress?: AgentProgress;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const STATUS_MAP: Record<string, ObservableSession["status"]> = {
|
|
19
|
+
started: "active",
|
|
20
|
+
completed: "completed",
|
|
21
|
+
failed: "failed",
|
|
22
|
+
aborted: "aborted",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export class SessionObserverRegistry {
|
|
26
|
+
#sessions = new Map<string, ObservableSession>();
|
|
27
|
+
#listeners = new Set<() => void>();
|
|
28
|
+
#eventBusUnsubscribers: Array<() => void> = [];
|
|
29
|
+
|
|
30
|
+
/** Add a change listener. Returns unsubscribe function. */
|
|
31
|
+
onChange(cb: () => void): () => void {
|
|
32
|
+
this.#listeners.add(cb);
|
|
33
|
+
return () => this.#listeners.delete(cb);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#notifyListeners(): void {
|
|
37
|
+
for (const cb of this.#listeners) cb();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setMainSession(sessionFile?: string): void {
|
|
41
|
+
const existing = this.#sessions.get("main");
|
|
42
|
+
this.#sessions.set("main", {
|
|
43
|
+
id: "main",
|
|
44
|
+
kind: "main",
|
|
45
|
+
label: "Main Session",
|
|
46
|
+
status: "active",
|
|
47
|
+
sessionFile: sessionFile ?? existing?.sessionFile,
|
|
48
|
+
lastUpdate: Date.now(),
|
|
49
|
+
});
|
|
50
|
+
this.#notifyListeners();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getSessions(): ObservableSession[] {
|
|
54
|
+
const sessions = [...this.#sessions.values()];
|
|
55
|
+
sessions.sort((a, b) => {
|
|
56
|
+
if (a.kind === "main") return -1;
|
|
57
|
+
if (b.kind === "main") return 1;
|
|
58
|
+
return a.lastUpdate - b.lastUpdate;
|
|
59
|
+
});
|
|
60
|
+
return sessions;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getActiveSubagentCount(): number {
|
|
64
|
+
let count = 0;
|
|
65
|
+
for (const s of this.#sessions.values()) {
|
|
66
|
+
if (s.kind === "subagent" && s.status === "active") count++;
|
|
67
|
+
}
|
|
68
|
+
return count;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Clear all tracked sessions (e.g. on session switch). Keeps EventBus subscriptions and listeners. */
|
|
72
|
+
resetSessions(): void {
|
|
73
|
+
this.#sessions.clear();
|
|
74
|
+
this.#notifyListeners();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
dispose(): void {
|
|
78
|
+
for (const unsub of this.#eventBusUnsubscribers) unsub();
|
|
79
|
+
this.#eventBusUnsubscribers = [];
|
|
80
|
+
this.#sessions.clear();
|
|
81
|
+
this.#listeners.clear();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
subscribeToEventBus(eventBus: EventBus): void {
|
|
85
|
+
// Dispose previous EventBus subscriptions if called again
|
|
86
|
+
for (const unsub of this.#eventBusUnsubscribers) unsub();
|
|
87
|
+
this.#eventBusUnsubscribers = [];
|
|
88
|
+
|
|
89
|
+
this.#eventBusUnsubscribers.push(
|
|
90
|
+
eventBus.on(TASK_SUBAGENT_LIFECYCLE_CHANNEL, data => {
|
|
91
|
+
const payload = data as SubagentLifecyclePayload;
|
|
92
|
+
const status = STATUS_MAP[payload.status];
|
|
93
|
+
if (!status) return;
|
|
94
|
+
|
|
95
|
+
const existing = this.#sessions.get(payload.id);
|
|
96
|
+
if (existing) {
|
|
97
|
+
existing.status = status;
|
|
98
|
+
existing.lastUpdate = Date.now();
|
|
99
|
+
if (payload.description) existing.description = payload.description;
|
|
100
|
+
if (payload.sessionFile) existing.sessionFile = payload.sessionFile;
|
|
101
|
+
} else {
|
|
102
|
+
this.#sessions.set(payload.id, {
|
|
103
|
+
id: payload.id,
|
|
104
|
+
kind: "subagent",
|
|
105
|
+
label: payload.description ?? `Subagent #${payload.index}`,
|
|
106
|
+
agent: payload.agent,
|
|
107
|
+
description: payload.description,
|
|
108
|
+
status,
|
|
109
|
+
sessionFile: payload.sessionFile,
|
|
110
|
+
lastUpdate: Date.now(),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
this.#notifyListeners();
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
this.#eventBusUnsubscribers.push(
|
|
118
|
+
eventBus.on(TASK_SUBAGENT_PROGRESS_CHANNEL, data => {
|
|
119
|
+
const payload = data as SubagentProgressPayload;
|
|
120
|
+
const progress = payload.progress;
|
|
121
|
+
const id = progress.id;
|
|
122
|
+
const existing = this.#sessions.get(id);
|
|
123
|
+
|
|
124
|
+
if (existing) {
|
|
125
|
+
existing.lastUpdate = Date.now();
|
|
126
|
+
existing.progress = progress;
|
|
127
|
+
if (progress.description) existing.description = progress.description;
|
|
128
|
+
if (payload.sessionFile) existing.sessionFile = payload.sessionFile;
|
|
129
|
+
} else {
|
|
130
|
+
this.#sessions.set(id, {
|
|
131
|
+
id,
|
|
132
|
+
kind: "subagent",
|
|
133
|
+
label: progress.description ?? `Subagent #${payload.index}`,
|
|
134
|
+
agent: payload.agent,
|
|
135
|
+
description: progress.description,
|
|
136
|
+
status: "active",
|
|
137
|
+
sessionFile: payload.sessionFile,
|
|
138
|
+
lastUpdate: Date.now(),
|
|
139
|
+
progress,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
this.#notifyListeners();
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
package/src/modes/shared.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
1
|
import type { TabBarTheme } from "@oh-my-pi/pi-tui";
|
|
4
|
-
import { getProjectDir, isEnoent } from "@oh-my-pi/pi-utils";
|
|
5
2
|
import { theme } from "./theme/theme";
|
|
6
3
|
|
|
7
4
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -31,42 +28,3 @@ export function getTabBarTheme(): TabBarTheme {
|
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
export { parseCommandArgs } from "../utils/command-args";
|
|
34
|
-
|
|
35
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
36
|
-
// Git HEAD Discovery
|
|
37
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
38
|
-
|
|
39
|
-
/** Walk up from the project dir to find .git/HEAD. Returns path and content, or null. */
|
|
40
|
-
export async function findGitHeadPathAsync(): Promise<{ path: string; content: string } | null> {
|
|
41
|
-
let dir = getProjectDir();
|
|
42
|
-
while (true) {
|
|
43
|
-
const gitHeadPath = path.join(dir, ".git", "HEAD");
|
|
44
|
-
try {
|
|
45
|
-
const content = await Bun.file(gitHeadPath).text();
|
|
46
|
-
return { path: gitHeadPath, content };
|
|
47
|
-
} catch (err) {
|
|
48
|
-
if (!isEnoent(err)) throw err;
|
|
49
|
-
}
|
|
50
|
-
const parent = path.dirname(dir);
|
|
51
|
-
if (parent === dir) {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
dir = parent;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Walk up from the project dir to find .git/HEAD. Returns path, or null. */
|
|
59
|
-
export function findGitHeadPathSync(): string | null {
|
|
60
|
-
let dir = getProjectDir();
|
|
61
|
-
while (true) {
|
|
62
|
-
const gitHeadPath = path.join(dir, ".git", "HEAD");
|
|
63
|
-
if (fs.existsSync(gitHeadPath)) {
|
|
64
|
-
return gitHeadPath;
|
|
65
|
-
}
|
|
66
|
-
const parent = path.dirname(dir);
|
|
67
|
-
if (parent === dir) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
dir = parent;
|
|
71
|
-
}
|
|
72
|
-
}
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -4,10 +4,10 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
|
4
4
|
import type { Effort } from "@oh-my-pi/pi-ai";
|
|
5
5
|
import {
|
|
6
6
|
detectMacOSAppearance,
|
|
7
|
+
MacAppearanceObserver,
|
|
7
8
|
type HighlightColors as NativeHighlightColors,
|
|
8
9
|
highlightCode as nativeHighlightCode,
|
|
9
10
|
supportsLanguage as nativeSupportsLanguage,
|
|
10
|
-
startMacAppearanceObserver as startNativeMacObserver,
|
|
11
11
|
} from "@oh-my-pi/pi-natives";
|
|
12
12
|
import type { EditorTheme, MarkdownTheme, SelectListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
|
|
13
13
|
import { adjustHsv, getCustomThemesDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
@@ -1148,9 +1148,14 @@ const langMap: Record<string, SymbolKey> = {
|
|
|
1148
1148
|
sh: "lang.shell",
|
|
1149
1149
|
zsh: "lang.shell",
|
|
1150
1150
|
fish: "lang.shell",
|
|
1151
|
+
powershell: "lang.shell",
|
|
1152
|
+
just: "lang.shell",
|
|
1151
1153
|
shell: "lang.shell",
|
|
1152
1154
|
html: "lang.html",
|
|
1153
1155
|
htm: "lang.html",
|
|
1156
|
+
astro: "lang.html",
|
|
1157
|
+
vue: "lang.html",
|
|
1158
|
+
svelte: "lang.html",
|
|
1154
1159
|
css: "lang.css",
|
|
1155
1160
|
scss: "lang.css",
|
|
1156
1161
|
sass: "lang.css",
|
|
@@ -2057,10 +2062,12 @@ function startMacAppearanceObserver(): void {
|
|
|
2057
2062
|
stopMacAppearanceObserver();
|
|
2058
2063
|
if (!shouldUseMacOSAppearanceFallback()) return;
|
|
2059
2064
|
try {
|
|
2060
|
-
macOSReportedAppearance = detectMacOSAppearance();
|
|
2061
|
-
macObserver =
|
|
2062
|
-
|
|
2063
|
-
|
|
2065
|
+
macOSReportedAppearance = detectMacOSAppearance() ?? undefined;
|
|
2066
|
+
macObserver = MacAppearanceObserver.start((err, appearance) => {
|
|
2067
|
+
if (!err && (appearance === "dark" || appearance === "light")) {
|
|
2068
|
+
macOSReportedAppearance = appearance;
|
|
2069
|
+
reevaluateAutoTheme("macOS fallback");
|
|
2070
|
+
}
|
|
2064
2071
|
});
|
|
2065
2072
|
} catch (err) {
|
|
2066
2073
|
logger.warn("Failed to start macOS appearance observer", { err });
|
|
@@ -2315,69 +2322,132 @@ export function getLanguageFromPath(filePath: string): string | undefined {
|
|
|
2315
2322
|
) {
|
|
2316
2323
|
return "conf";
|
|
2317
2324
|
}
|
|
2325
|
+
if (baseName === "dockerfile" || baseName.startsWith("dockerfile.") || baseName === "containerfile") {
|
|
2326
|
+
return "dockerfile";
|
|
2327
|
+
}
|
|
2328
|
+
if (baseName === "justfile") return "just";
|
|
2329
|
+
if (baseName === "cmakelists.txt") return "cmake";
|
|
2318
2330
|
|
|
2319
2331
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
2320
2332
|
if (!ext) return undefined;
|
|
2321
2333
|
|
|
2322
2334
|
const extToLang: Record<string, string> = {
|
|
2323
2335
|
ts: "typescript",
|
|
2324
|
-
|
|
2336
|
+
cts: "typescript",
|
|
2337
|
+
mts: "typescript",
|
|
2338
|
+
tsx: "tsx",
|
|
2325
2339
|
js: "javascript",
|
|
2326
2340
|
jsx: "javascript",
|
|
2327
2341
|
mjs: "javascript",
|
|
2328
2342
|
cjs: "javascript",
|
|
2329
2343
|
py: "python",
|
|
2344
|
+
pyi: "python",
|
|
2330
2345
|
rb: "ruby",
|
|
2346
|
+
rbw: "ruby",
|
|
2347
|
+
gemspec: "ruby",
|
|
2331
2348
|
rs: "rust",
|
|
2332
2349
|
go: "go",
|
|
2333
2350
|
java: "java",
|
|
2334
2351
|
kt: "kotlin",
|
|
2352
|
+
ktm: "kotlin",
|
|
2353
|
+
kts: "kotlin",
|
|
2335
2354
|
swift: "swift",
|
|
2336
2355
|
c: "c",
|
|
2337
2356
|
h: "c",
|
|
2338
2357
|
cpp: "cpp",
|
|
2339
2358
|
cc: "cpp",
|
|
2340
2359
|
cxx: "cpp",
|
|
2360
|
+
hh: "cpp",
|
|
2341
2361
|
hpp: "cpp",
|
|
2362
|
+
cu: "cpp",
|
|
2363
|
+
ino: "cpp",
|
|
2342
2364
|
cs: "csharp",
|
|
2365
|
+
clj: "clojure",
|
|
2366
|
+
cljc: "clojure",
|
|
2367
|
+
cljs: "clojure",
|
|
2368
|
+
edn: "clojure",
|
|
2343
2369
|
php: "php",
|
|
2344
2370
|
sh: "bash",
|
|
2345
2371
|
bash: "bash",
|
|
2346
2372
|
zsh: "bash",
|
|
2373
|
+
ksh: "bash",
|
|
2374
|
+
bats: "bash",
|
|
2375
|
+
tmux: "bash",
|
|
2376
|
+
cgi: "bash",
|
|
2377
|
+
fcgi: "bash",
|
|
2378
|
+
command: "bash",
|
|
2379
|
+
tool: "bash",
|
|
2347
2380
|
fish: "fish",
|
|
2348
2381
|
ps1: "powershell",
|
|
2382
|
+
psm1: "powershell",
|
|
2349
2383
|
sql: "sql",
|
|
2350
2384
|
html: "html",
|
|
2351
2385
|
htm: "html",
|
|
2386
|
+
xhtml: "html",
|
|
2387
|
+
astro: "astro",
|
|
2388
|
+
vue: "vue",
|
|
2389
|
+
svelte: "svelte",
|
|
2352
2390
|
css: "css",
|
|
2353
2391
|
scss: "scss",
|
|
2354
2392
|
sass: "sass",
|
|
2355
2393
|
less: "less",
|
|
2356
2394
|
json: "json",
|
|
2395
|
+
ipynb: "ipynb",
|
|
2396
|
+
hbs: "handlebars",
|
|
2397
|
+
hsb: "handlebars",
|
|
2398
|
+
handlebars: "handlebars",
|
|
2357
2399
|
yaml: "yaml",
|
|
2358
2400
|
yml: "yaml",
|
|
2359
2401
|
toml: "toml",
|
|
2360
2402
|
xml: "xml",
|
|
2403
|
+
xsl: "xml",
|
|
2404
|
+
xslt: "xml",
|
|
2405
|
+
svg: "xml",
|
|
2406
|
+
plist: "xml",
|
|
2361
2407
|
md: "markdown",
|
|
2362
2408
|
markdown: "markdown",
|
|
2409
|
+
mdx: "markdown",
|
|
2410
|
+
diff: "diff",
|
|
2411
|
+
patch: "diff",
|
|
2363
2412
|
dockerfile: "dockerfile",
|
|
2364
|
-
|
|
2413
|
+
containerfile: "dockerfile",
|
|
2414
|
+
makefile: "make",
|
|
2415
|
+
justfile: "just",
|
|
2416
|
+
mk: "make",
|
|
2417
|
+
mak: "make",
|
|
2365
2418
|
cmake: "cmake",
|
|
2366
2419
|
lua: "lua",
|
|
2420
|
+
jl: "julia",
|
|
2421
|
+
pl: "perl",
|
|
2422
|
+
pm: "perl",
|
|
2367
2423
|
perl: "perl",
|
|
2368
2424
|
r: "r",
|
|
2369
2425
|
scala: "scala",
|
|
2370
|
-
|
|
2426
|
+
sc: "scala",
|
|
2427
|
+
sbt: "scala",
|
|
2371
2428
|
ex: "elixir",
|
|
2372
2429
|
exs: "elixir",
|
|
2373
2430
|
erl: "erlang",
|
|
2374
2431
|
hs: "haskell",
|
|
2432
|
+
nix: "nix",
|
|
2433
|
+
odin: "odin",
|
|
2434
|
+
zig: "zig",
|
|
2435
|
+
star: "starlark",
|
|
2436
|
+
bzl: "starlark",
|
|
2437
|
+
sol: "solidity",
|
|
2438
|
+
v: "verilog",
|
|
2439
|
+
sv: "verilog",
|
|
2440
|
+
svh: "verilog",
|
|
2441
|
+
vh: "verilog",
|
|
2442
|
+
m: "objc",
|
|
2443
|
+
mm: "objc",
|
|
2375
2444
|
ml: "ocaml",
|
|
2376
2445
|
vim: "vim",
|
|
2377
2446
|
graphql: "graphql",
|
|
2378
2447
|
proto: "protobuf",
|
|
2379
2448
|
tf: "hcl",
|
|
2380
2449
|
hcl: "hcl",
|
|
2450
|
+
tfvars: "hcl",
|
|
2381
2451
|
txt: "text",
|
|
2382
2452
|
text: "text",
|
|
2383
2453
|
log: "log",
|
|
@@ -2388,6 +2458,8 @@ export function getLanguageFromPath(filePath: string): string | undefined {
|
|
|
2388
2458
|
conf: "conf",
|
|
2389
2459
|
config: "conf",
|
|
2390
2460
|
properties: "conf",
|
|
2461
|
+
tla: "tlaplus",
|
|
2462
|
+
tlaplus: "tlaplus",
|
|
2391
2463
|
env: "env",
|
|
2392
2464
|
};
|
|
2393
2465
|
|
package/src/modes/types.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { MCPManager } from "../mcp";
|
|
|
14
14
|
import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
|
|
15
15
|
import type { HistoryStorage } from "../session/history-storage";
|
|
16
16
|
import type { SessionContext, SessionManager } from "../session/session-manager";
|
|
17
|
-
import type { ExitPlanModeDetails } from "../tools";
|
|
17
|
+
import type { ExitPlanModeDetails, LspStartupServerInfo } from "../tools";
|
|
18
18
|
import type { AssistantMessageComponent } from "./components/assistant-message";
|
|
19
19
|
import type { BashExecutionComponent } from "./components/bash-execution";
|
|
20
20
|
import type { CustomEditor } from "./components/custom-editor";
|
|
@@ -76,7 +76,7 @@ export interface InteractiveModeContext {
|
|
|
76
76
|
agent: AgentSession["agent"];
|
|
77
77
|
historyStorage?: HistoryStorage;
|
|
78
78
|
mcpManager?: MCPManager;
|
|
79
|
-
lspServers?:
|
|
79
|
+
lspServers?: LspStartupServerInfo[];
|
|
80
80
|
|
|
81
81
|
// State
|
|
82
82
|
isInitialized: boolean;
|
|
@@ -208,6 +208,8 @@ export interface InteractiveModeContext {
|
|
|
208
208
|
showOAuthSelector(mode: "login" | "logout", providerId?: string): Promise<void>;
|
|
209
209
|
showHookConfirm(title: string, message: string): Promise<boolean>;
|
|
210
210
|
showDebugSelector(): void;
|
|
211
|
+
showSessionObserver(): void;
|
|
212
|
+
resetObserverRegistry(): void;
|
|
211
213
|
|
|
212
214
|
// Input handling
|
|
213
215
|
handleCtrlC(): void;
|
|
@@ -19,3 +19,12 @@ export function matchesAppInterrupt(data: string): boolean {
|
|
|
19
19
|
export function matchesSelectCancel(data: string): boolean {
|
|
20
20
|
return getKeybindings().matches(data, "tui.select.cancel");
|
|
21
21
|
}
|
|
22
|
+
|
|
23
|
+
export function matchesAppExternalEditor(data: string): boolean {
|
|
24
|
+
const keybindings = getKeybindings();
|
|
25
|
+
const externalEditorKeys = keybindings.getKeys("app.editor.external");
|
|
26
|
+
if (externalEditorKeys.length > 0) {
|
|
27
|
+
return keybindings.matches(data, "app.editor.external");
|
|
28
|
+
}
|
|
29
|
+
return matchesKey(data, "ctrl+g");
|
|
30
|
+
}
|
|
@@ -61,3 +61,8 @@ You **MUST** read `rule://<name>` when working in that domain.
|
|
|
61
61
|
{{/if}}
|
|
62
62
|
Current date: {{date}}
|
|
63
63
|
Current working directory: {{cwd}}
|
|
64
|
+
{{#if secretsEnabled}}
|
|
65
|
+
<redacted-content>
|
|
66
|
+
Some values in tool output are redacted for security. They appear as `#XXXX#` tokens (4 uppercase-alphanumeric characters wrapped in `#`). These are **not errors** — they are intentional placeholders for sensitive values (API keys, passwords, tokens). Treat them as opaque strings. Do not attempt to decode, fix, or report them as problems.
|
|
67
|
+
</redacted-content>
|
|
68
|
+
{{/if}}
|
|
@@ -109,6 +109,7 @@ Most tools resolve custom protocol URLs to internal resources (not web URLs):
|
|
|
109
109
|
- `artifact://<id>` — Raw artifact content (truncated tool output)
|
|
110
110
|
- `local://<TITLE>.md` — Finalized plan artifact created after `exit_plan_mode` approval
|
|
111
111
|
- `jobs://<job-id>` — Specific job status and result
|
|
112
|
+
- `mcp://<resource-uri>` — MCP resource from a connected server; matched against exact resource URIs first, then RFC 6570 URI templates advertised by connected servers
|
|
112
113
|
- `pi://..` — Internal documentation files about Oh My Pi, you **MUST NOT** read them unless the user asks about omp/pi itself: its SDK, extensions, themes, skills, TUI, keybindings, or configuration
|
|
113
114
|
|
|
114
115
|
In `bash`, URIs auto-resolve to filesystem paths (e.g., `python skill://my-skill/scripts/init.py`).
|
|
@@ -261,7 +262,7 @@ These are inviolable. Violation is system failure.
|
|
|
261
262
|
- You **MUST NOT** suppress tests to make code pass. You **MUST NOT** fabricate outputs not observed.
|
|
262
263
|
- You **MUST NOT** solve the wished-for problem instead of the actual problem.
|
|
263
264
|
- You **MUST NOT** ask for information obtainable from tools, repo context, or files.
|
|
264
|
-
- You **MUST**
|
|
265
|
+
- You **MUST** always design a clean solution. You **MUST NOT** introduce unnecessary backwards compatibiltity layers, no shims, no gradual migration, no bridges to old code unless user explicitly asks for it. Let the errors guide you on what to include in the refactoring. **ALWAYS default to performing full CUTOVER!**
|
|
265
266
|
|
|
266
267
|
# Design Integrity
|
|
267
268
|
|
|
@@ -314,6 +315,12 @@ When a tool call fails, read the full error before doing anything else. When a f
|
|
|
314
315
|
- You **SHOULD** run only tests you added/modified unless asked otherwise.
|
|
315
316
|
- You **MUST NOT** yield without proof when non-trivial work, self-assessment is deceptive: tests, linters, type checks, repro steps… exhaust all external verification.
|
|
316
317
|
|
|
318
|
+
{{#if secretsEnabled}}
|
|
319
|
+
<redacted-content>
|
|
320
|
+
Some values in tool output are redacted for security. They appear as `#XXXX#` tokens (4 uppercase-alphanumeric characters wrapped in `#`). These are **not errors** — they are intentional placeholders for sensitive values (API keys, passwords, tokens). Treat them as opaque strings. Do not attempt to decode, fix, or report them as problems.
|
|
321
|
+
</redacted-content>
|
|
322
|
+
{{/if}}
|
|
323
|
+
|
|
317
324
|
{{SECTION_SEPERATOR "Now"}}
|
|
318
325
|
The current working directory is '{{cwd}}'.
|
|
319
326
|
Today is '{{date}}', and your work begins now. Get it right.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Edits files via syntax-aware chunks. Run `read(path="file.ts")` first. The edit selector is a chunk path, optionally qualified with a region.
|
|
2
|
+
|
|
3
|
+
<rules>
|
|
4
|
+
- **MUST** `read` first. Never invent chunk paths or CRCs. Copy them from the latest `read` output or edit response.
|
|
5
|
+
- `sel` format:
|
|
6
|
+
- insertions: `chunk` or `chunk@region`
|
|
7
|
+
- replacements: `chunk#CRC` or `chunk#CRC@region`
|
|
8
|
+
- Without a `@region` it defaults to the entire chunk including leading trivia. Valid regions: `head`, `body`, `tail`, `decl`.
|
|
9
|
+
- If the exact chunk path is unclear, run `read(path="file", sel="?")` and copy a selector from that listing.
|
|
10
|
+
- Use a single leading space per indent level in `content`. Write content at indent-level 0 — the tool re-indents it to match the chunk's position in the file. For example, to replace `@body` of a method, write the body starting at column 0:
|
|
11
|
+
```
|
|
12
|
+
content: "if (x) {\n return true;\n}"
|
|
13
|
+
```
|
|
14
|
+
The tool adds the correct base indent automatically. Never manually pad with the chunk's own indentation.
|
|
15
|
+
- `@region` only works on container chunks (classes, functions, impl blocks, sections). Do **not** use `@head`/`@body`/`@tail` on leaf chunks (enum variants, fields, single statements) — use the whole chunk instead.
|
|
16
|
+
- `replace` requires the current CRC. Insertions do not.
|
|
17
|
+
- **CRCs change after every edit.** Always use the selectors/CRCs from the most recent `read` or edit response. Never reuse a CRC from a previous edit.
|
|
18
|
+
</rules>
|
|
19
|
+
|
|
20
|
+
<critical>
|
|
21
|
+
You **MUST** use the narrowest region that covers your change. Replacing without a region replaces the **entire chunk including leading comments, decorators, and attributes** — omitting them from `content` deletes them.
|
|
22
|
+
</critical>
|
|
23
|
+
|
|
24
|
+
<regions>
|
|
25
|
+
```
|
|
26
|
+
@head ··· ┊ /// doc comment
|
|
27
|
+
· ┊ #[attr]
|
|
28
|
+
@decl ··· ┊ fn foo(x: i32) {
|
|
29
|
+
@body ·· ┊ body();
|
|
30
|
+
@tail ·· ┊ }
|
|
31
|
+
```
|
|
32
|
+
- `@body` — the interior only. **Use for most edits.**
|
|
33
|
+
- `@head` — leading trivia + signature + opening delimiter.
|
|
34
|
+
- `@tail` — the closing delimiter.
|
|
35
|
+
- `@decl` — everything except leading trivia (signature + body + closing delimiter).
|
|
36
|
+
- *(no region)* — the entire chunk including leading trivia. Same as `@head` + `@body` + `@tail`.
|
|
37
|
+
|
|
38
|
+
For leaf chunks (fields, variants, single-line items), `@body` falls back to the full chunk.
|
|
39
|
+
|
|
40
|
+
`append`/`prepend` without a `@region` inserts _outside_ the chunk. To add children _inside_ a class, struct, enum, or function body, use `@body`:
|
|
41
|
+
- `class_Foo@body` + `append` → adds inside the class before `}`
|
|
42
|
+
- `class_Foo@body` + `prepend` → adds inside the class after `{`
|
|
43
|
+
- `class_Foo` + `append` → adds after the entire class (after `}`)
|
|
44
|
+
</regions>
|
|
45
|
+
|
|
46
|
+
<ops>
|
|
47
|
+
|op|sel|effect|
|
|
48
|
+
|---|---|---|
|
|
49
|
+
|`replace`|`chunk#CRC(@region)?`|rewrite the addressed region|
|
|
50
|
+
|`before`|`chunk(@region)?`|insert before the region span|
|
|
51
|
+
|`after`|`chunk(@region)?`|insert after the region span|
|
|
52
|
+
|`prepend`|`chunk(@region)?`|insert at the start inside the region|
|
|
53
|
+
|`append`|`chunk(@region)?`|insert at the end inside the region|
|
|
54
|
+
</ops>
|
|
55
|
+
|
|
56
|
+
<examples>
|
|
57
|
+
Given this `read` output for `example.ts`:
|
|
58
|
+
```
|
|
59
|
+
| example.ts·34L·ts·#QBMH
|
|
60
|
+
|
|
|
61
|
+
| [<interface_Config#BWTR>]
|
|
62
|
+
1| interface Config {
|
|
63
|
+
| [<interface_Config.field_host#TTMN>]
|
|
64
|
+
2| host: string;
|
|
65
|
+
| [<interface_Config.field_port#QSMH>]
|
|
66
|
+
3| port: number;
|
|
67
|
+
| [<interface_Config.field_debug#JPRR>]
|
|
68
|
+
4| debug: boolean;
|
|
69
|
+
5| }
|
|
70
|
+
|
|
|
71
|
+
| [<class_Counter#HZHY>]
|
|
72
|
+
7| class Counter {
|
|
73
|
+
| [<class_Counter.field_value#QJBY>]
|
|
74
|
+
8| value: number = 0;
|
|
75
|
+
9|
|
|
76
|
+
| [<class_Counter.fn_increment#NQWY>]
|
|
77
|
+
10| increment(): void {
|
|
78
|
+
11| this.value += 1;
|
|
79
|
+
12| }
|
|
80
|
+
13|
|
|
81
|
+
| [<class_Counter.fn_decrement#PMBP>]
|
|
82
|
+
14| decrement(): void {
|
|
83
|
+
15| this.value -= 1;
|
|
84
|
+
16| }
|
|
85
|
+
17|
|
|
86
|
+
| [<class_Counter.fn_toString#ZQZP>]
|
|
87
|
+
18| toString(): string {
|
|
88
|
+
19| return `Counter(${this.value})`;
|
|
89
|
+
20| }
|
|
90
|
+
21| }
|
|
91
|
+
|
|
|
92
|
+
| [<enum_Status#HYQJ>]
|
|
93
|
+
23| enum Status {
|
|
94
|
+
| [<enum_Status.variant_Active#PQNS>]
|
|
95
|
+
24| Active = "ACTIVE",
|
|
96
|
+
| [<enum_Status.variant_Paused#HHNM>]
|
|
97
|
+
25| Paused = "PAUSED",
|
|
98
|
+
| [<enum_Status.variant_Stopped#NHTY>]
|
|
99
|
+
26| Stopped = "STOPPED",
|
|
100
|
+
27| }
|
|
101
|
+
|
|
|
102
|
+
| [<fn_createCounter#PQQY>]
|
|
103
|
+
29| function createCounter(initial: number): Counter {
|
|
104
|
+
30| const counter = new Counter();
|
|
105
|
+
31| counter.value = initial;
|
|
106
|
+
32| return counter;
|
|
107
|
+
33| }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Replace a whole chunk** (rename a function):
|
|
111
|
+
```
|
|
112
|
+
{ "sel": "fn_createCounter#PQQY", "op": "replace", "content": "function makeCounter(start: number): Counter {\n const c = new Counter();\n c.value = start;\n return c;\n}\n" }
|
|
113
|
+
```
|
|
114
|
+
Result — the entire chunk is rewritten:
|
|
115
|
+
```
|
|
116
|
+
function makeCounter(start: number): Counter {
|
|
117
|
+
const c = new Counter();
|
|
118
|
+
c.value = start;
|
|
119
|
+
return c;
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Replace a method body** (`@body`):
|
|
124
|
+
```
|
|
125
|
+
{ "sel": "class_Counter.fn_increment#NQWY@body", "op": "replace", "content": "this.value += 1;\nconsole.log('incremented to', this.value);\n" }
|
|
126
|
+
```
|
|
127
|
+
Result — only the body changes, signature and braces are kept:
|
|
128
|
+
```
|
|
129
|
+
increment(): void {
|
|
130
|
+
this.value += 1;
|
|
131
|
+
console.log('incremented to', this.value);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Replace a function header** (`@head` — signature and doc comment):
|
|
136
|
+
```
|
|
137
|
+
{ "sel": "fn_createCounter#PQQY@head", "op": "replace", "content": "/** Creates a counter with the given start value. */\nfunction createCounter(initial: number, label?: string): Counter {\n" }
|
|
138
|
+
```
|
|
139
|
+
Result — adds a doc comment and updates the signature, body untouched:
|
|
140
|
+
```
|
|
141
|
+
/** Creates a counter with the given start value. */
|
|
142
|
+
function createCounter(initial: number, label?: string): Counter {
|
|
143
|
+
const counter = new Counter();
|
|
144
|
+
counter.value = initial;
|
|
145
|
+
return counter;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Insert before a chunk** (`before`):
|
|
150
|
+
```
|
|
151
|
+
{ "sel": "fn_createCounter", "op": "before", "content": "/** Factory function below. */\n" }
|
|
152
|
+
```
|
|
153
|
+
Result — a comment is inserted before the function:
|
|
154
|
+
```
|
|
155
|
+
/** Factory function below. */
|
|
156
|
+
|
|
157
|
+
function createCounter(initial: number): Counter {
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Insert after a chunk** (`after`):
|
|
161
|
+
```
|
|
162
|
+
{ "sel": "enum_Status", "op": "after", "content": "\nfunction isActive(s: Status): boolean {\n return s === Status.Active;\n}\n" }
|
|
163
|
+
```
|
|
164
|
+
Result — a new function appears after the enum:
|
|
165
|
+
```
|
|
166
|
+
enum Status {
|
|
167
|
+
Active = "ACTIVE",
|
|
168
|
+
Paused = "PAUSED",
|
|
169
|
+
Stopped = "STOPPED",
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function isActive(s: Status): boolean {
|
|
173
|
+
return s === Status.Active;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function createCounter(initial: number): Counter {
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Prepend inside a container** (`@body` + `prepend`):
|
|
180
|
+
```
|
|
181
|
+
{ "sel": "class_Counter@body", "op": "prepend", "content": "label: string = 'default';\n\n" }
|
|
182
|
+
```
|
|
183
|
+
Result — a new field is added at the top of the class body, before existing members:
|
|
184
|
+
```
|
|
185
|
+
class Counter {
|
|
186
|
+
label: string = 'default';
|
|
187
|
+
|
|
188
|
+
value: number = 0;
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Append inside a container** (`@body` + `append`):
|
|
192
|
+
```
|
|
193
|
+
{ "sel": "class_Counter@body", "op": "append", "content": "\nreset(): void {\n this.value = 0;\n}\n" }
|
|
194
|
+
```
|
|
195
|
+
Result — a new method is added at the end of the class body, before the closing `}`:
|
|
196
|
+
```
|
|
197
|
+
toString(): string {
|
|
198
|
+
return `Counter(${this.value})`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
reset(): void {
|
|
202
|
+
this.value = 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Delete a chunk** (`replace` with empty content):
|
|
208
|
+
```
|
|
209
|
+
{ "sel": "class_Counter.fn_toString#ZQZP", "op": "replace", "content": "" }
|
|
210
|
+
```
|
|
211
|
+
Result — the method is removed from the class.
|
|
212
|
+
- Indentation rules (important):
|
|
213
|
+
- Use one leading space for each indent level in canonical chunk-edit content. The tool expands those levels to the file's actual style (2-space, 4-space, tabs, etc.).
|
|
214
|
+
- Do NOT include the chunk's base indentation — only indent relative to the region's opening level.
|
|
215
|
+
- For `@body` of a function: write at column 0, e.g. `"return x;\n"`. The tool adds the correct base indent.
|
|
216
|
+
- For `@head`: write at the chunk's own depth. A class member's head uses `"/** doc */\nstart(): void {"`.
|
|
217
|
+
- For a top-level item: start at zero indent. Write `"function foo() {\n return 1;\n}\n"`.
|
|
218
|
+
- The tool strips common leading indentation from your content as a safety net, so accidental over-indentation is corrected.
|
|
219
|
+
</examples>
|