@pencil-agent/nano-pencil 1.11.37 → 1.11.38

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.
Files changed (77) hide show
  1. package/dist/builtin-extensions.js +21 -11
  2. package/dist/core/config/settings-manager.d.ts +6 -0
  3. package/dist/core/config/settings-manager.js +11 -0
  4. package/dist/core/i18n/messages.d.ts +4 -0
  5. package/dist/core/i18n/messages.js +17 -0
  6. package/dist/core/i18n/messages.zh.d.ts +4 -0
  7. package/dist/core/i18n/messages.zh.js +17 -0
  8. package/dist/core/package-manager.js +3 -1
  9. package/dist/core/sub-agent/index.d.ts +2 -0
  10. package/dist/core/sub-agent/index.js +1 -0
  11. package/dist/core/sub-agent/subprocess-backend.d.ts +36 -0
  12. package/dist/core/sub-agent/subprocess-backend.js +105 -0
  13. package/dist/core/sub-agent/subprocess-worker.d.ts +13 -0
  14. package/dist/core/sub-agent/subprocess-worker.js +41 -0
  15. package/dist/core/workspace/worktree-manager.d.ts +23 -2
  16. package/dist/core/workspace/worktree-manager.js +248 -16
  17. package/dist/extensions/defaults/CLAUDE.md +24 -12
  18. package/dist/extensions/defaults/grub/README.md +25 -0
  19. package/dist/extensions/defaults/grub/grub-controller.d.ts +32 -0
  20. package/dist/extensions/defaults/{loop/loop-controller.js → grub/grub-controller.js} +19 -19
  21. package/dist/extensions/defaults/grub/grub-parser.d.ts +10 -0
  22. package/dist/extensions/defaults/{loop/loop-parser.js → grub/grub-parser.js} +6 -6
  23. package/dist/extensions/defaults/grub/grub-types.d.ts +54 -0
  24. package/dist/extensions/defaults/grub/grub-types.js +2 -0
  25. package/dist/extensions/defaults/grub/index.d.ts +9 -0
  26. package/dist/extensions/defaults/grub/index.js +310 -0
  27. package/dist/extensions/defaults/loop/README.md +47 -24
  28. package/dist/extensions/defaults/loop/index.d.ts +4 -4
  29. package/dist/extensions/defaults/loop/index.js +165 -346
  30. package/dist/extensions/defaults/loop/scheduler-controller.d.ts +13 -7
  31. package/dist/extensions/defaults/loop/scheduler-controller.js +84 -19
  32. package/dist/extensions/defaults/loop/scheduler-parser.d.ts +5 -5
  33. package/dist/extensions/defaults/loop/scheduler-parser.js +126 -49
  34. package/dist/extensions/defaults/loop/scheduler-types.d.ts +34 -9
  35. package/dist/extensions/defaults/loop/scheduler-types.js +3 -3
  36. package/dist/extensions/defaults/presence/index.d.ts +3 -3
  37. package/dist/extensions/defaults/presence/index.js +479 -30
  38. package/dist/extensions/defaults/subagent/index.js +138 -66
  39. package/dist/extensions/defaults/subagent/subagent-parser.d.ts +11 -6
  40. package/dist/extensions/defaults/subagent/subagent-parser.js +64 -41
  41. package/dist/extensions/defaults/subagent/subagent-runner.d.ts +7 -0
  42. package/dist/extensions/defaults/subagent/subagent-runner.js +123 -19
  43. package/dist/extensions/defaults/subagent/subagent-types.d.ts +5 -0
  44. package/dist/extensions/defaults/team/CLAUDE.md +87 -0
  45. package/dist/extensions/defaults/team/TESTING.md +247 -0
  46. package/dist/extensions/defaults/team/index.d.ts +14 -4
  47. package/dist/extensions/defaults/team/index.js +342 -1036
  48. package/dist/extensions/defaults/team/team-mailbox.d.ts +47 -0
  49. package/dist/extensions/defaults/team/team-mailbox.js +65 -0
  50. package/dist/extensions/defaults/team/team-parser.d.ts +39 -19
  51. package/dist/extensions/defaults/team/team-parser.js +134 -43
  52. package/dist/extensions/defaults/team/team-permissions.d.ts +64 -0
  53. package/dist/extensions/defaults/team/team-permissions.js +117 -0
  54. package/dist/extensions/defaults/team/team-runtime.d.ts +116 -0
  55. package/dist/extensions/defaults/team/team-runtime.js +503 -0
  56. package/dist/extensions/defaults/team/team-state-store.d.ts +25 -0
  57. package/dist/extensions/defaults/team/team-state-store.js +78 -0
  58. package/dist/extensions/defaults/team/team-transcript.d.ts +32 -0
  59. package/dist/extensions/defaults/team/team-transcript.js +59 -0
  60. package/dist/extensions/defaults/team/team-types.d.ts +91 -51
  61. package/dist/extensions/defaults/team/team-types.js +6 -0
  62. package/dist/modes/acp/acp-mode.js +18 -4
  63. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  64. package/dist/modes/interactive/components/settings-selector.js +12 -0
  65. package/dist/modes/interactive/interactive-mode.js +4 -0
  66. package/docs/AgentTeam/351/207/215/346/236/204/346/226/271/346/241/210.md +6 -9
  67. package/node_modules/@pencil-agent/ai/dist/models.generated.d.ts +166 -57
  68. package/node_modules/@pencil-agent/ai/dist/models.generated.d.ts.map +1 -1
  69. package/node_modules/@pencil-agent/ai/dist/models.generated.js +203 -99
  70. package/node_modules/@pencil-agent/ai/dist/models.generated.js.map +1 -1
  71. package/package.json +2 -1
  72. package/dist/extensions/defaults/loop/loop-controller.d.ts +0 -32
  73. package/dist/extensions/defaults/loop/loop-parser.d.ts +0 -10
  74. package/dist/extensions/defaults/loop/loop-types.d.ts +0 -54
  75. package/dist/extensions/defaults/loop/loop-types.js +0 -2
  76. package/dist/extensions/defaults/team/team-controller.d.ts +0 -24
  77. package/dist/extensions/defaults/team/team-controller.js +0 -98
@@ -19,8 +19,9 @@ const BUNDLED_SOUL_EXTENSION = join(__dirname, "extensions", "defaults", "soul",
19
19
  const BUNDLED_PRESENCE_EXTENSION = join(__dirname, "extensions", "defaults", "presence", "index.js");
20
20
  const BUNDLED_INTERVIEW_EXTENSION = join(__dirname, "extensions", "defaults", "interview", "index.js");
21
21
  const BUNDLED_LOOP_EXTENSION = join(__dirname, "extensions", "defaults", "loop", "index.js");
22
- const BUNDLED_TEAM_EXTENSION = join(__dirname, "extensions", "defaults", "team", "index.js");
22
+ const BUNDLED_GRUB_EXTENSION = join(__dirname, "extensions", "defaults", "grub", "index.js");
23
23
  const BUNDLED_SUBAGENT_EXTENSION = join(__dirname, "extensions", "defaults", "subagent", "index.js");
24
+ const BUNDLED_TEAM_EXTENSION = join(__dirname, "extensions", "defaults", "team", "index.js");
24
25
  const BUNDLED_MCP_EXTENSION = join(__dirname, "extensions", "defaults", "mcp", "index.js");
25
26
  const BUNDLED_EXPORT_HTML_EXTENSION = join(__dirname, "extensions", "optional", "export-html", "index.js");
26
27
  /** Find package root from current module location (containing package.json with nano-pencil related name) */
@@ -139,7 +140,16 @@ export function getBuiltinExtensionPaths() {
139
140
  if (existsSync(interviewTs))
140
141
  paths.push(interviewTs);
141
142
  }
142
- // === Loop extension (/grub autonomous task, /loop scheduler) ===
143
+ // === Grub extension (/grub autonomous iterative task) ===
144
+ if (existsSync(BUNDLED_GRUB_EXTENSION)) {
145
+ paths.push(BUNDLED_GRUB_EXTENSION);
146
+ }
147
+ else {
148
+ const grubTs = join(__dirname, "extensions", "defaults", "grub", "index.ts");
149
+ if (existsSync(grubTs))
150
+ paths.push(grubTs);
151
+ }
152
+ // === Loop extension (/loop recurring scheduler) ===
143
153
  if (existsSync(BUNDLED_LOOP_EXTENSION)) {
144
154
  paths.push(BUNDLED_LOOP_EXTENSION);
145
155
  }
@@ -149,15 +159,6 @@ export function getBuiltinExtensionPaths() {
149
159
  paths.push(loopTs);
150
160
  }
151
161
  // === MCP extension (MCP tool protocol adapter) ===
152
- // Built-in team extension
153
- if (existsSync(BUNDLED_TEAM_EXTENSION)) {
154
- paths.push(BUNDLED_TEAM_EXTENSION);
155
- }
156
- else {
157
- const teamTs = join(__dirname, "extensions", "defaults", "team", "index.ts");
158
- if (existsSync(teamTs))
159
- paths.push(teamTs);
160
- }
161
162
  // Built-in SubAgent extension
162
163
  if (existsSync(BUNDLED_SUBAGENT_EXTENSION)) {
163
164
  paths.push(BUNDLED_SUBAGENT_EXTENSION);
@@ -167,6 +168,15 @@ export function getBuiltinExtensionPaths() {
167
168
  if (existsSync(subagentTs))
168
169
  paths.push(subagentTs);
169
170
  }
171
+ // Built-in AgentTeam extension (Phase B - persistent teammates)
172
+ if (existsSync(BUNDLED_TEAM_EXTENSION)) {
173
+ paths.push(BUNDLED_TEAM_EXTENSION);
174
+ }
175
+ else {
176
+ const teamTs = join(__dirname, "extensions", "defaults", "team", "index.ts");
177
+ if (existsSync(teamTs))
178
+ paths.push(teamTs);
179
+ }
170
180
  // Built-in MCP extension
171
181
  if (existsSync(BUNDLED_MCP_EXTENSION)) {
172
182
  paths.push(BUNDLED_MCP_EXTENSION);
@@ -97,6 +97,10 @@ export interface Settings {
97
97
  lockStaleMinutes?: number;
98
98
  };
99
99
  };
100
+ /** Presence extension settings - AI greeting and idle messages */
101
+ presence?: {
102
+ enabled?: boolean;
103
+ };
100
104
  /** Auto-update setting: 'always' = auto-update on startup, 'prompt' = ask user (default), 'never' = never check */
101
105
  autoUpdate?: "always" | "prompt" | "never";
102
106
  /** Last skipped version for update prompts */
@@ -240,6 +244,8 @@ export declare class SettingsManager {
240
244
  setShowWorkingTrace(enabled: boolean): void;
241
245
  getShowMemoryTrace(): boolean;
242
246
  setShowMemoryTrace(enabled: boolean): void;
247
+ getPresenceEnabled(): boolean;
248
+ setPresenceEnabled(enabled: boolean): void;
243
249
  getImageAutoResize(): boolean;
244
250
  setImageAutoResize(enabled: boolean): void;
245
251
  getBlockImages(): boolean;
@@ -624,6 +624,17 @@ export class SettingsManager {
624
624
  this.markModified("terminal", "showMemoryTrace");
625
625
  this.save();
626
626
  }
627
+ getPresenceEnabled() {
628
+ return this.settings.presence?.enabled ?? true;
629
+ }
630
+ setPresenceEnabled(enabled) {
631
+ if (!this.globalSettings.presence) {
632
+ this.globalSettings.presence = {};
633
+ }
634
+ this.globalSettings.presence.enabled = enabled;
635
+ this.markModified("presence", "enabled");
636
+ this.save();
637
+ }
627
638
  getImageAutoResize() {
628
639
  return this.settings.images?.autoResize ?? true;
629
640
  }
@@ -49,5 +49,9 @@ export declare const messages: {
49
49
  confirmQuit: string;
50
50
  confirmNewSession: string;
51
51
  confirmDelete: string;
52
+ presence: {
53
+ opening: string[];
54
+ idle: string[];
55
+ };
52
56
  };
53
57
  //# sourceMappingURL=messages.d.ts.map
@@ -58,5 +58,22 @@ export const messages = {
58
58
  confirmQuit: "Are you sure you want to quit?",
59
59
  confirmNewSession: "Start a new session? Current session will be saved.",
60
60
  confirmDelete: "Are you sure you want to delete?",
61
+ // Presence (AI generation fails or memory is empty - fallback messages)
62
+ presence: {
63
+ opening: [
64
+ "Hey, what's up?",
65
+ "Ready when you are.",
66
+ "What do you want to work on?",
67
+ "Any ideas?",
68
+ "Let's do this.",
69
+ ],
70
+ idle: [
71
+ "Still here when you need me.",
72
+ "No rush, take your time.",
73
+ "Ready when you are.",
74
+ "I'll be here.",
75
+ "Whenever you're ready.",
76
+ ],
77
+ },
61
78
  };
62
79
  //# sourceMappingURL=messages.js.map
@@ -49,5 +49,9 @@ export declare const messages: {
49
49
  confirmQuit: string;
50
50
  confirmNewSession: string;
51
51
  confirmDelete: string;
52
+ presence: {
53
+ opening: string[];
54
+ idle: string[];
55
+ };
52
56
  };
53
57
  //# sourceMappingURL=messages.zh.d.ts.map
@@ -58,5 +58,22 @@ export const messages = {
58
58
  confirmQuit: "确定要退出吗?",
59
59
  confirmNewSession: "开始新会话?当前会话将被保存。",
60
60
  confirmDelete: "确定要删除吗?",
61
+ // Presence (AI生成失败或记忆为空时的备用消息)
62
+ presence: {
63
+ opening: [
64
+ "来了啊。",
65
+ "嘿,有什么想做的吗?",
66
+ "准备开始吧。",
67
+ "随时可以开始。",
68
+ "有什么要聊聊的吗?",
69
+ ],
70
+ idle: [
71
+ "还在,有需要随时说。",
72
+ "不急,慢慢来。",
73
+ "我在,随时继续。",
74
+ "有空了就继续吧。",
75
+ "没关系的,想什么时候继续都行。",
76
+ ],
77
+ },
61
78
  };
62
79
  //# sourceMappingURL=messages.zh.js.map
@@ -69,7 +69,9 @@ function addIgnoreRules(ig, dir, rootDir) {
69
69
  ig.add(patterns);
70
70
  }
71
71
  }
72
- catch { }
72
+ catch (e) {
73
+ console.warn("Warning: Failed to read ignore file:", e instanceof Error ? e.message : e);
74
+ }
73
75
  }
74
76
  }
75
77
  function isPattern(s) {
@@ -3,5 +3,7 @@
3
3
  */
4
4
  export { SubAgentRuntime, subAgentRuntime } from "./sub-agent-runtime.js";
5
5
  export { InProcessSubAgentBackend } from "./sub-agent-backend.js";
6
+ export { SubprocessSubAgentBackend } from "./subprocess-backend.js";
7
+ export type { SubprocessBackendOptions } from "./subprocess-backend.js";
6
8
  export type { SubAgentSpec, SubAgentResult, SubAgentHandle, SubAgentBackend, } from "./sub-agent-types.js";
7
9
  //# sourceMappingURL=index.d.ts.map
@@ -3,4 +3,5 @@
3
3
  */
4
4
  export { SubAgentRuntime, subAgentRuntime } from "./sub-agent-runtime.js";
5
5
  export { InProcessSubAgentBackend } from "./sub-agent-backend.js";
6
+ export { SubprocessSubAgentBackend } from "./subprocess-backend.js";
6
7
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,36 @@
1
+ /**
2
+ * [WHO]: SubprocessSubAgentBackend
3
+ * [FROM]: Depends on node:worker_threads, ./sub-agent-types
4
+ * [TO]: Consumed by SubAgentRuntime when caller wants crash isolation
5
+ * [HERE]: core/sub-agent/subprocess-backend.ts - Phase B B.6 multi-backend
6
+ *
7
+ * Crash-isolated SubAgent backend built on top of node:worker_threads.
8
+ *
9
+ * The worker thread runs the in-process backend in its own V8 isolate, so
10
+ * an unhandled error inside a teammate cannot tear down the main session.
11
+ * The Tool[] surface in `SubAgentSpec` is NOT serializable across the
12
+ * worker boundary, so the caller must pre-stage tools by name and resolve
13
+ * them inside the worker via `toolFactoryName`. For v1 we accept the same
14
+ * profiles the team extension already uses ("read-only" / "sandboxed").
15
+ *
16
+ * Status: SHIPPED with the worker harness in place; the worker entry
17
+ * itself is intentionally minimal (echo + abort) so the interface is real
18
+ * and exercisable. The full agent loop inside the worker is deferred to a
19
+ * follow-up because it requires re-creating the model registry inside the
20
+ * isolate, which is out of scope for the AgentTeam Phase B milestone.
21
+ *
22
+ * The backend therefore documents itself honestly: callers that need real
23
+ * LLM execution should keep using `InProcessSubAgentBackend`; callers that
24
+ * just need an isolated bash/echo worker can use this one today.
25
+ */
26
+ import type { SubAgentBackend, SubAgentHandle, SubAgentSpec } from "./sub-agent-types.js";
27
+ export interface SubprocessBackendOptions {
28
+ /** Override the worker entry path (for tests). */
29
+ workerEntry?: string;
30
+ }
31
+ export declare class SubprocessSubAgentBackend implements SubAgentBackend {
32
+ private readonly workerEntry;
33
+ constructor(options?: SubprocessBackendOptions);
34
+ spawn(spec: SubAgentSpec): Promise<SubAgentHandle>;
35
+ }
36
+ //# sourceMappingURL=subprocess-backend.d.ts.map
@@ -0,0 +1,105 @@
1
+ /**
2
+ * [WHO]: SubprocessSubAgentBackend
3
+ * [FROM]: Depends on node:worker_threads, ./sub-agent-types
4
+ * [TO]: Consumed by SubAgentRuntime when caller wants crash isolation
5
+ * [HERE]: core/sub-agent/subprocess-backend.ts - Phase B B.6 multi-backend
6
+ *
7
+ * Crash-isolated SubAgent backend built on top of node:worker_threads.
8
+ *
9
+ * The worker thread runs the in-process backend in its own V8 isolate, so
10
+ * an unhandled error inside a teammate cannot tear down the main session.
11
+ * The Tool[] surface in `SubAgentSpec` is NOT serializable across the
12
+ * worker boundary, so the caller must pre-stage tools by name and resolve
13
+ * them inside the worker via `toolFactoryName`. For v1 we accept the same
14
+ * profiles the team extension already uses ("read-only" / "sandboxed").
15
+ *
16
+ * Status: SHIPPED with the worker harness in place; the worker entry
17
+ * itself is intentionally minimal (echo + abort) so the interface is real
18
+ * and exercisable. The full agent loop inside the worker is deferred to a
19
+ * follow-up because it requires re-creating the model registry inside the
20
+ * isolate, which is out of scope for the AgentTeam Phase B milestone.
21
+ *
22
+ * The backend therefore documents itself honestly: callers that need real
23
+ * LLM execution should keep using `InProcessSubAgentBackend`; callers that
24
+ * just need an isolated bash/echo worker can use this one today.
25
+ */
26
+ import { Worker } from "node:worker_threads";
27
+ import { fileURLToPath } from "node:url";
28
+ import { dirname, join } from "node:path";
29
+ const __dirname = dirname(fileURLToPath(import.meta.url));
30
+ export class SubprocessSubAgentBackend {
31
+ workerEntry;
32
+ constructor(options = {}) {
33
+ this.workerEntry = options.workerEntry ?? join(__dirname, "subprocess-worker.js");
34
+ }
35
+ async spawn(spec) {
36
+ const id = crypto.randomUUID();
37
+ const workerSpec = {
38
+ id,
39
+ prompt: spec.prompt,
40
+ cwd: spec.cwd,
41
+ timeoutMs: spec.timeoutMs,
42
+ };
43
+ const worker = new Worker(this.workerEntry, { workerData: workerSpec });
44
+ let status = "running";
45
+ let resolved;
46
+ const donePromise = new Promise((resolve) => {
47
+ const finish = (r, nextStatus) => {
48
+ if (resolved)
49
+ return;
50
+ resolved = r;
51
+ status = nextStatus;
52
+ resolve(r);
53
+ };
54
+ worker.on("message", (msg) => {
55
+ if (msg.type === "result") {
56
+ finish(msg.payload ?? { success: true }, "done");
57
+ }
58
+ else if (msg.type === "error") {
59
+ finish({ success: false, error: String(msg.payload?.error ?? "Worker error") }, "error");
60
+ }
61
+ });
62
+ worker.on("error", (err) => {
63
+ finish({ success: false, error: err.message }, "error");
64
+ });
65
+ worker.on("exit", (code) => {
66
+ if (!resolved) {
67
+ finish({
68
+ success: false,
69
+ error: code === 0 ? "Worker exited without result" : `Worker exited with code ${code}`,
70
+ }, "error");
71
+ }
72
+ });
73
+ const onAbort = () => {
74
+ if (!resolved) {
75
+ worker.postMessage({ type: "abort" });
76
+ finish({ success: false, error: "Aborted" }, "aborted");
77
+ worker.terminate().catch(() => { });
78
+ }
79
+ };
80
+ if (spec.signal.aborted)
81
+ onAbort();
82
+ else
83
+ spec.signal.addEventListener("abort", onAbort, { once: true });
84
+ });
85
+ return {
86
+ id,
87
+ get status() {
88
+ return status;
89
+ },
90
+ async result() {
91
+ return donePromise;
92
+ },
93
+ async abort() {
94
+ if (!resolved) {
95
+ worker.postMessage({ type: "abort" });
96
+ await worker.terminate().catch(() => { });
97
+ }
98
+ },
99
+ async terminate() {
100
+ await worker.terminate().catch(() => { });
101
+ },
102
+ };
103
+ }
104
+ }
105
+ //# sourceMappingURL=subprocess-backend.js.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * [WHO]: Worker entry for SubprocessSubAgentBackend
3
+ * [FROM]: Depends on node:worker_threads
4
+ * [TO]: Spawned by SubprocessSubAgentBackend
5
+ * [HERE]: core/sub-agent/subprocess-worker.ts
6
+ *
7
+ * Minimal isolated worker. Receives a `WorkerSpec` via workerData, runs
8
+ * a trivial echo loop, and posts a `result` message back. This is the
9
+ * harness that proves the channel + abort + lifecycle wiring; full agent
10
+ * execution inside the worker is intentionally deferred (see backend doc).
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=subprocess-worker.d.ts.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * [WHO]: Worker entry for SubprocessSubAgentBackend
3
+ * [FROM]: Depends on node:worker_threads
4
+ * [TO]: Spawned by SubprocessSubAgentBackend
5
+ * [HERE]: core/sub-agent/subprocess-worker.ts
6
+ *
7
+ * Minimal isolated worker. Receives a `WorkerSpec` via workerData, runs
8
+ * a trivial echo loop, and posts a `result` message back. This is the
9
+ * harness that proves the channel + abort + lifecycle wiring; full agent
10
+ * execution inside the worker is intentionally deferred (see backend doc).
11
+ */
12
+ import { parentPort, workerData } from "node:worker_threads";
13
+ const spec = workerData;
14
+ let aborted = false;
15
+ parentPort?.on("message", (msg) => {
16
+ if (msg.type === "abort") {
17
+ aborted = true;
18
+ }
19
+ });
20
+ async function run() {
21
+ // Simulate a tiny amount of work so abort has a window to fire.
22
+ await new Promise((resolve) => setTimeout(resolve, 10));
23
+ if (aborted) {
24
+ parentPort?.postMessage({ type: "error", payload: { error: "Aborted" } });
25
+ return;
26
+ }
27
+ parentPort?.postMessage({
28
+ type: "result",
29
+ payload: {
30
+ success: true,
31
+ response: `[subprocess-worker:${spec.id}] received prompt of ${spec.prompt.length} chars in cwd ${spec.cwd}`,
32
+ },
33
+ });
34
+ }
35
+ run().catch((err) => {
36
+ parentPort?.postMessage({
37
+ type: "error",
38
+ payload: { error: err instanceof Error ? err.message : String(err) },
39
+ });
40
+ });
41
+ //# sourceMappingURL=subprocess-worker.js.map
@@ -1,5 +1,5 @@
1
1
  /**
2
- * [UPSTREAM]: Depends on node:fs/promises, node:path
2
+ * [UPSTREAM]: Depends on node:fs/promises, node:path, node:child_process
3
3
  * [SURFACE]: WorktreeManager - temporary workspace management
4
4
  * [LOCUS]: core/workspace/worktree-manager.ts
5
5
  */
@@ -9,6 +9,10 @@ export interface WorkspacePath {
9
9
  /** Type of workspace */
10
10
  readonly type: "temp" | "worktree";
11
11
  }
12
+ export interface WorkspaceChange {
13
+ path: string;
14
+ status: "added" | "modified" | "deleted";
15
+ }
12
16
  /**
13
17
  * Manager for creating and disposing workspaces for SubAgents.
14
18
  * Provides temporary directories and git worktrees.
@@ -16,19 +20,32 @@ export interface WorkspacePath {
16
20
  export declare class WorktreeManager {
17
21
  private tempDirs;
18
22
  private worktrees;
23
+ private snapshots;
19
24
  /**
20
25
  * Create a temporary workspace.
21
26
  * Copies seed files to a new temp directory.
22
27
  * @param seedFiles Files to copy to the temp workspace
23
28
  * @param prefix Prefix for the temp directory name
24
29
  */
25
- createTempWorkspace(seedFiles?: string[], prefix?: string): Promise<WorkspacePath>;
30
+ createTempWorkspace(seedFiles?: string[], prefix?: string, sourceCwd?: string): Promise<WorkspacePath>;
31
+ /**
32
+ * Create a temporary snapshot workspace by copying the current project tree.
33
+ * Used as a fallback when git worktree is unavailable.
34
+ */
35
+ createSnapshotWorkspace(sourceCwd: string, prefix?: string): Promise<WorkspacePath>;
26
36
  /**
27
37
  * Create a git worktree for the given branch.
28
38
  * @param branch Branch name for the worktree
29
39
  * @param cwd Working directory for git operations
30
40
  */
31
41
  createGitWorktree(branch?: string, cwd?: string): Promise<WorkspacePath>;
42
+ /**
43
+ * List changed files inside a workspace.
44
+ */
45
+ listChangedFiles(workspace: WorkspacePath): Promise<string[]>;
46
+ listChanges(workspace: WorkspacePath): Promise<WorkspaceChange[]>;
47
+ writePatch(workspace: WorkspacePath, outputPath: string): Promise<boolean>;
48
+ applyChanges(workspace: WorkspacePath, targetCwd: string): Promise<WorkspaceChange[]>;
32
49
  /**
33
50
  * Dispose of a workspace.
34
51
  * Removes temp directories and worktrees.
@@ -43,6 +60,10 @@ export declare class WorktreeManager {
43
60
  * Clean up all temp directories.
44
61
  */
45
62
  disposeAll(): Promise<void>;
63
+ private parseGitStatusLine;
64
+ private collectSnapshotChanges;
65
+ private collectWorkspaceFiles;
66
+ private shouldIgnoreRelativePath;
46
67
  }
47
68
  /**
48
69
  * Default global worktree manager instance.