@chamba/core 0.1.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/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/index.d.ts +433 -0
- package/dist/index.js +1218 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 chamba contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @chamba/core
|
|
2
|
+
|
|
3
|
+
Pure core logic for [chamba](https://github.com/thelord07/chamba): workspace scanner,
|
|
4
|
+
plan generator + heuristic reviewer, git worktree manager, Obsidian context/vault
|
|
5
|
+
writer, and a filesystem memory store.
|
|
6
|
+
|
|
7
|
+
- **No Node APIs directly** — all OS access goes through ports (`FilesystemPort`,
|
|
8
|
+
`ProcessPort`, `ClockPort`), so it's testable and runtime-agnostic.
|
|
9
|
+
- **No LLM** — chamba never calls a model.
|
|
10
|
+
- Node implementations of the ports live in
|
|
11
|
+
[`@chamba/adapters`](https://www.npmjs.com/package/@chamba/adapters).
|
|
12
|
+
|
|
13
|
+
Most users want the [`@chamba/mcp`](https://www.npmjs.com/package/@chamba/mcp) server,
|
|
14
|
+
not this library directly.
|
|
15
|
+
|
|
16
|
+
## License
|
|
17
|
+
|
|
18
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time behind a port so date-dependent logic (plan filenames, vault notes) is
|
|
3
|
+
* testable and `@chamba/core` stays free of ambient `Date` calls.
|
|
4
|
+
*/
|
|
5
|
+
interface ClockPort {
|
|
6
|
+
now(): Date;
|
|
7
|
+
/** Current date as `YYYY-MM-DD`. */
|
|
8
|
+
today(): string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** A single entry returned when listing a directory. */
|
|
12
|
+
interface DirEntry {
|
|
13
|
+
name: string;
|
|
14
|
+
isDirectory: boolean;
|
|
15
|
+
isFile: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Filesystem access behind a port so `@chamba/core` never imports `node:fs`.
|
|
19
|
+
* Node implementation lives in `@chamba/adapters`; an in-memory implementation
|
|
20
|
+
* for tests lives in `@chamba/core/testing`.
|
|
21
|
+
*/
|
|
22
|
+
interface FilesystemPort {
|
|
23
|
+
readFile(path: string): Promise<string>;
|
|
24
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
25
|
+
readDir(path: string): Promise<DirEntry[]>;
|
|
26
|
+
exists(path: string): Promise<boolean>;
|
|
27
|
+
/** Create a directory and any missing parents (recursive). */
|
|
28
|
+
mkdir(path: string): Promise<void>;
|
|
29
|
+
/** Remove a file or directory (recursive); a no-op if it doesn't exist. */
|
|
30
|
+
remove(path: string): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Memory {
|
|
34
|
+
key: string;
|
|
35
|
+
content: string;
|
|
36
|
+
tags: string[];
|
|
37
|
+
/** ISO timestamp of first write. */
|
|
38
|
+
createdAt: string;
|
|
39
|
+
/** ISO timestamp of last write (set when the memory is appended to). */
|
|
40
|
+
updatedAt: string;
|
|
41
|
+
/** Path to the markdown file on disk. */
|
|
42
|
+
path: string;
|
|
43
|
+
}
|
|
44
|
+
interface RememberInput {
|
|
45
|
+
key: string;
|
|
46
|
+
content: string;
|
|
47
|
+
tags?: string[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Persistent, human-editable memory across sessions. Implementations store one
|
|
51
|
+
* markdown file per memory (never JSON or a DB), so the user can read and edit
|
|
52
|
+
* them by hand.
|
|
53
|
+
*/
|
|
54
|
+
interface MemoryStore {
|
|
55
|
+
remember(input: RememberInput): Promise<Memory>;
|
|
56
|
+
recall(query: string): Promise<Memory[]>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Directory (relative to root) where memories live. */
|
|
60
|
+
declare const MEMORY_DIR = ".chamba/memory";
|
|
61
|
+
/**
|
|
62
|
+
* Filesystem-backed `MemoryStore`. One markdown file per memory under
|
|
63
|
+
* `.chamba/memory/<slug>.md`, with YAML-ish frontmatter (key, tags, createdAt,
|
|
64
|
+
* updatedAt). Re-remembering an existing key appends a timestamped section
|
|
65
|
+
* instead of overwriting. Search is case-insensitive substring over key, tags
|
|
66
|
+
* and content.
|
|
67
|
+
*/
|
|
68
|
+
declare class FilesystemMemoryStore implements MemoryStore {
|
|
69
|
+
private readonly fs;
|
|
70
|
+
private readonly clock;
|
|
71
|
+
private readonly root;
|
|
72
|
+
constructor(fs: FilesystemPort, clock: ClockPort, root: string);
|
|
73
|
+
private dir;
|
|
74
|
+
private pathFor;
|
|
75
|
+
remember(input: RememberInput): Promise<Memory>;
|
|
76
|
+
recall(query: string): Promise<Memory[]>;
|
|
77
|
+
private read;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface NoteFields {
|
|
81
|
+
title: string;
|
|
82
|
+
/** `YYYY-MM-DD`. */
|
|
83
|
+
date: string;
|
|
84
|
+
tags: string[];
|
|
85
|
+
/** Markdown body the model produced (summary, plan, decisions, etc.). */
|
|
86
|
+
body: string;
|
|
87
|
+
}
|
|
88
|
+
/** Turn a title into a filesystem- and Obsidian-friendly slug. */
|
|
89
|
+
declare function slugify(title: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* Render a vault note: valid YAML frontmatter (parseable by Obsidian) followed
|
|
92
|
+
* by the model's markdown body. Override this template by passing your own body
|
|
93
|
+
* structure — chamba only owns the frontmatter and the title heading.
|
|
94
|
+
*/
|
|
95
|
+
declare function renderNote(fields: NoteFields): string;
|
|
96
|
+
|
|
97
|
+
interface WriteNoteInput {
|
|
98
|
+
vaultPath: string;
|
|
99
|
+
title: string;
|
|
100
|
+
content: string;
|
|
101
|
+
/** Subfolder slug under `proyectos/`; defaults to the title slug. */
|
|
102
|
+
projectSlug?: string;
|
|
103
|
+
tags?: string[];
|
|
104
|
+
}
|
|
105
|
+
interface WriteNoteResult {
|
|
106
|
+
notePath: string;
|
|
107
|
+
}
|
|
108
|
+
/** Subfolder inside the vault where chamba writes its summaries. */
|
|
109
|
+
declare const VAULT_NOTES_DIR = "proyectos";
|
|
110
|
+
/**
|
|
111
|
+
* Write a structured summary note into an Obsidian vault at
|
|
112
|
+
* `<vault>/proyectos/<date>-<slug>.md` with valid YAML frontmatter.
|
|
113
|
+
*/
|
|
114
|
+
declare class VaultWriter {
|
|
115
|
+
private readonly fs;
|
|
116
|
+
private readonly clock;
|
|
117
|
+
constructor(fs: FilesystemPort, clock: ClockPort);
|
|
118
|
+
write(input: WriteNoteInput): Promise<WriteNoteResult>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Relative path (from workspace root) of the chamba workspace file. */
|
|
122
|
+
declare const WORKSPACE_DIR = ".chamba";
|
|
123
|
+
declare const WORKSPACE_FILE = "workspace.md";
|
|
124
|
+
declare const WORKSPACE_RELATIVE_PATH = ".chamba/workspace.md";
|
|
125
|
+
interface ProjectRef {
|
|
126
|
+
name: string;
|
|
127
|
+
/** Path relative to the workspace root; `.` for the root project. */
|
|
128
|
+
path: string;
|
|
129
|
+
language?: string;
|
|
130
|
+
framework?: string;
|
|
131
|
+
}
|
|
132
|
+
interface Workspace {
|
|
133
|
+
root: string;
|
|
134
|
+
description: string;
|
|
135
|
+
languages: string[];
|
|
136
|
+
framework?: string;
|
|
137
|
+
conventions: string[];
|
|
138
|
+
projects: ProjectRef[];
|
|
139
|
+
/** Top-level directory names (without trailing slash), sorted. */
|
|
140
|
+
folderMap: string[];
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Render a `Workspace` to the markdown that gets written to
|
|
144
|
+
* `.chamba/workspace.md`. Deterministic on purpose — no timestamps — so that
|
|
145
|
+
* `workspace_reload` produces meaningful diffs instead of churn.
|
|
146
|
+
*/
|
|
147
|
+
declare function renderWorkspaceMarkdown(ws: Workspace): string;
|
|
148
|
+
|
|
149
|
+
type IssueSeverity = 'error' | 'warning';
|
|
150
|
+
interface Issue {
|
|
151
|
+
code: string;
|
|
152
|
+
severity: IssueSeverity;
|
|
153
|
+
message: string;
|
|
154
|
+
}
|
|
155
|
+
interface ValidatePlanInput {
|
|
156
|
+
plan: string;
|
|
157
|
+
task: string;
|
|
158
|
+
context?: string;
|
|
159
|
+
workspace?: Workspace;
|
|
160
|
+
}
|
|
161
|
+
interface ValidationResult {
|
|
162
|
+
issues: Issue[];
|
|
163
|
+
suggestions: string[];
|
|
164
|
+
riskFlags: string[];
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Validate a plan with programmatic heuristics — NO LLM. The editor's model
|
|
168
|
+
* already reasons; chamba only checks the plan's structure for known anti-patterns.
|
|
169
|
+
*/
|
|
170
|
+
declare function validatePlan(input: ValidatePlanInput): ValidationResult;
|
|
171
|
+
|
|
172
|
+
interface PlanReview {
|
|
173
|
+
approved: boolean;
|
|
174
|
+
issues: Issue[];
|
|
175
|
+
suggestions: string[];
|
|
176
|
+
riskFlags: string[];
|
|
177
|
+
}
|
|
178
|
+
interface ReviewInput {
|
|
179
|
+
plan: string;
|
|
180
|
+
task: string;
|
|
181
|
+
context?: string;
|
|
182
|
+
workspace?: Workspace;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Heuristic plan reviewer. Runs `validatePlan` and approves only when there are
|
|
186
|
+
* no error-severity issues. Warnings and risk flags are surfaced but don't block.
|
|
187
|
+
* NO LLM — the editor's model decides what to do with the review.
|
|
188
|
+
*/
|
|
189
|
+
declare class Reviewer {
|
|
190
|
+
review(input: ReviewInput): PlanReview;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
type WorkerKind = 'implementer' | 'tester' | 'reviewer';
|
|
194
|
+
interface SubtaskSpec {
|
|
195
|
+
title: string;
|
|
196
|
+
worker: WorkerKind;
|
|
197
|
+
description: string;
|
|
198
|
+
filesLikelyTouched: string[];
|
|
199
|
+
}
|
|
200
|
+
interface GeneratePlanInput {
|
|
201
|
+
task: string;
|
|
202
|
+
context?: string;
|
|
203
|
+
workspace?: Workspace;
|
|
204
|
+
}
|
|
205
|
+
/** Default subtask scaffold every plan starts from. The model refines these. */
|
|
206
|
+
declare function suggestSubtasks(): SubtaskSpec[];
|
|
207
|
+
/** Seed "files likely touched" from the workspace map (top-level dirs/projects). */
|
|
208
|
+
declare function suggestFilesLikelyTouched(workspace?: Workspace): string[];
|
|
209
|
+
/**
|
|
210
|
+
* Produce a structured plan *template* (not a finished plan). The editor's model
|
|
211
|
+
* fills the placeholders. chamba never writes the actual plan — it only provides
|
|
212
|
+
* the skeleton and, later, reviews it. No LLM involved.
|
|
213
|
+
*/
|
|
214
|
+
declare function generatePlanTemplate(input: GeneratePlanInput): string;
|
|
215
|
+
|
|
216
|
+
interface ProcessResult {
|
|
217
|
+
stdout: string;
|
|
218
|
+
stderr: string;
|
|
219
|
+
exitCode: number;
|
|
220
|
+
}
|
|
221
|
+
interface ProcessExecOptions {
|
|
222
|
+
cwd?: string;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Run external processes behind a port so `@chamba/core` never imports
|
|
226
|
+
* `node:child_process`. Used from Phase 5 onward for git worktrees.
|
|
227
|
+
*/
|
|
228
|
+
interface ProcessPort {
|
|
229
|
+
exec(command: string, args: string[], options?: ProcessExecOptions): Promise<ProcessResult>;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
interface RecordedCall {
|
|
233
|
+
command: string;
|
|
234
|
+
args: string[];
|
|
235
|
+
cwd?: string;
|
|
236
|
+
}
|
|
237
|
+
type ProcessHandler = (command: string, args: string[]) => Partial<ProcessResult>;
|
|
238
|
+
/**
|
|
239
|
+
* In-memory `ProcessPort` for tests. Records every call and returns whatever the
|
|
240
|
+
* handler produces (defaults to exit 0, empty output).
|
|
241
|
+
*/
|
|
242
|
+
declare class FakeProcess implements ProcessPort {
|
|
243
|
+
private readonly handler;
|
|
244
|
+
readonly calls: RecordedCall[];
|
|
245
|
+
constructor(handler?: ProcessHandler);
|
|
246
|
+
exec(command: string, args: string[], options?: ProcessExecOptions): Promise<ProcessResult>;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* In-memory `FilesystemPort` for tests (CLAUDE.md: tests de IO usan FilesystemPort
|
|
251
|
+
* en memoria). Paths are posix-style. Construct with a map of `path -> contents`.
|
|
252
|
+
*/
|
|
253
|
+
declare class MemoryFilesystem implements FilesystemPort {
|
|
254
|
+
private readonly files;
|
|
255
|
+
private readonly dirs;
|
|
256
|
+
constructor(initial?: Record<string, string>);
|
|
257
|
+
private norm;
|
|
258
|
+
private set;
|
|
259
|
+
readFile(path: string): Promise<string>;
|
|
260
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
261
|
+
exists(path: string): Promise<boolean>;
|
|
262
|
+
mkdir(path: string): Promise<void>;
|
|
263
|
+
remove(path: string): Promise<void>;
|
|
264
|
+
readDir(path: string): Promise<DirEntry[]>;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Minimal posix-style path helpers. `@chamba/core` avoids `node:path` so it can
|
|
269
|
+
* run in non-Node runtimes; all internal paths use `/` as separator.
|
|
270
|
+
*/
|
|
271
|
+
declare function joinPath(...parts: string[]): string;
|
|
272
|
+
declare function basename(path: string): string;
|
|
273
|
+
declare function dirname(path: string): string;
|
|
274
|
+
/** Extension including the dot (e.g. `.ts`), or `''` if none. */
|
|
275
|
+
declare function extname(path: string): string;
|
|
276
|
+
|
|
277
|
+
interface RelevantNote {
|
|
278
|
+
path: string;
|
|
279
|
+
/** Number of keyword hits that made this note relevant. */
|
|
280
|
+
score: number;
|
|
281
|
+
/** First line that matched a keyword, trimmed. */
|
|
282
|
+
snippet: string;
|
|
283
|
+
}
|
|
284
|
+
interface ContextBuildInput {
|
|
285
|
+
workspace: Workspace;
|
|
286
|
+
task: string;
|
|
287
|
+
/** When set, search this Obsidian vault for notes relevant to the task. */
|
|
288
|
+
vaultPath?: string;
|
|
289
|
+
/** Soft cap on the produced context, in estimated tokens (~4 chars/token). */
|
|
290
|
+
maxTokens?: number;
|
|
291
|
+
}
|
|
292
|
+
interface BuiltContext {
|
|
293
|
+
context: string;
|
|
294
|
+
relevantNotes: string[];
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Build the context block injected into the editor model's reasoning: a summary
|
|
298
|
+
* of the workspace plus, when a vault is present, the notes most relevant to the
|
|
299
|
+
* task (simple keyword search — semantic search is V2).
|
|
300
|
+
*/
|
|
301
|
+
declare class ContextBuilder {
|
|
302
|
+
private readonly fs;
|
|
303
|
+
constructor(fs: FilesystemPort);
|
|
304
|
+
build(input: ContextBuildInput): Promise<BuiltContext>;
|
|
305
|
+
private workspaceSection;
|
|
306
|
+
private notesSection;
|
|
307
|
+
private searchNotes;
|
|
308
|
+
private collectMarkdown;
|
|
309
|
+
private tryRead;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Line-based diff (LCS) producing a readable unified-ish output:
|
|
314
|
+
* ` ` unchanged, `- ` removed, `+ ` added.
|
|
315
|
+
*
|
|
316
|
+
* Used by `workspace_reload` to show the model what a re-scan would change,
|
|
317
|
+
* without ever overwriting the user's hand-edited `workspace.md`.
|
|
318
|
+
*/
|
|
319
|
+
declare function diffLines(oldText: string, newText: string): string;
|
|
320
|
+
/** True when the two texts are identical line-for-line. */
|
|
321
|
+
declare function textsEqual(oldText: string, newText: string): boolean;
|
|
322
|
+
|
|
323
|
+
interface VaultDetection {
|
|
324
|
+
found: boolean;
|
|
325
|
+
path?: string;
|
|
326
|
+
noteCount?: number;
|
|
327
|
+
}
|
|
328
|
+
interface DetectOptions {
|
|
329
|
+
/** Explicit vault path (e.g. from CHAMBA_OBSIDIAN_VAULT_PATH). Authoritative. */
|
|
330
|
+
explicitPath?: string;
|
|
331
|
+
/** Directories to probe for a `.obsidian/` marker when no explicit path. */
|
|
332
|
+
searchRoots?: string[];
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Detect an Obsidian vault. An explicit path wins (the user set it on purpose);
|
|
336
|
+
* otherwise probe common roots for a `.obsidian/` directory. `@chamba/core` has
|
|
337
|
+
* no `os`/`fs`, so candidate roots are passed in by the caller.
|
|
338
|
+
*/
|
|
339
|
+
declare class ObsidianDetector {
|
|
340
|
+
private readonly fs;
|
|
341
|
+
constructor(fs: FilesystemPort);
|
|
342
|
+
detect(opts: DetectOptions): Promise<VaultDetection>;
|
|
343
|
+
private countNotes;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
declare class WorkspaceScanner {
|
|
347
|
+
private readonly fs;
|
|
348
|
+
constructor(fs: FilesystemPort);
|
|
349
|
+
scan(root: string): Promise<Workspace>;
|
|
350
|
+
private loadIgnoreRules;
|
|
351
|
+
private walk;
|
|
352
|
+
private detectLanguages;
|
|
353
|
+
private detectProjects;
|
|
354
|
+
private readProject;
|
|
355
|
+
private detectConventions;
|
|
356
|
+
private detectDescription;
|
|
357
|
+
private findReadme;
|
|
358
|
+
private tryRead;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
interface BranchNameInput {
|
|
362
|
+
/** `YYYY-MM-DD`. */
|
|
363
|
+
date: string;
|
|
364
|
+
taskSlug: string;
|
|
365
|
+
workerId: string;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Sanitize a value into a single git-ref-safe path component: lowercase, no
|
|
369
|
+
* spaces, no characters git forbids in refs (`~^:?*[\` etc.), no leading/trailing
|
|
370
|
+
* dots or dashes.
|
|
371
|
+
*/
|
|
372
|
+
declare function slugifyForGit(value: string): string;
|
|
373
|
+
/** Branch convention: `chamba/<date>-<task-slug>/<worker-id>`. */
|
|
374
|
+
declare function buildBranchName(input: BranchNameInput): string;
|
|
375
|
+
/** Worktree directory relative to the repo root. */
|
|
376
|
+
declare function worktreeRelativePath(taskSlug: string, workerId: string): string;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Detect whether a directory is inside a git work tree, via
|
|
380
|
+
* `git rev-parse --is-inside-work-tree`. Caches per root for the session.
|
|
381
|
+
*/
|
|
382
|
+
declare class GitDetector {
|
|
383
|
+
private readonly process;
|
|
384
|
+
private readonly cache;
|
|
385
|
+
constructor(process: ProcessPort);
|
|
386
|
+
isGitRepo(root: string): Promise<boolean>;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
declare class WorktreeError extends Error {
|
|
390
|
+
readonly name = "WorktreeError";
|
|
391
|
+
}
|
|
392
|
+
interface WorktreeHandle {
|
|
393
|
+
branch: string;
|
|
394
|
+
path: string;
|
|
395
|
+
taskSlug: string;
|
|
396
|
+
workerId: string;
|
|
397
|
+
}
|
|
398
|
+
interface CreateWorktreeInput {
|
|
399
|
+
root: string;
|
|
400
|
+
taskSlug: string;
|
|
401
|
+
workerId: string;
|
|
402
|
+
/** `YYYY-MM-DD` for the branch name. */
|
|
403
|
+
date: string;
|
|
404
|
+
baseBranch?: string;
|
|
405
|
+
}
|
|
406
|
+
interface ListedWorktree {
|
|
407
|
+
path: string;
|
|
408
|
+
head?: string;
|
|
409
|
+
branch?: string;
|
|
410
|
+
}
|
|
411
|
+
interface CleanupResult {
|
|
412
|
+
removed: boolean;
|
|
413
|
+
branchKept: boolean;
|
|
414
|
+
mergeSuggestion: string;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Manage git worktrees for isolated parallel work.
|
|
418
|
+
*
|
|
419
|
+
* Safety guarantees (see CLAUDE.md gotchas):
|
|
420
|
+
* - `cleanup` removes the worktree directory but NEVER deletes the branch and
|
|
421
|
+
* NEVER merges. The branch stays for the human to review and merge by hand.
|
|
422
|
+
* - `git worktree remove` is run WITHOUT `--force`, so a dirty worktree fails
|
|
423
|
+
* loudly instead of silently discarding work.
|
|
424
|
+
*/
|
|
425
|
+
declare class WorktreeManager {
|
|
426
|
+
private readonly process;
|
|
427
|
+
constructor(process: ProcessPort);
|
|
428
|
+
create(input: CreateWorktreeInput): Promise<WorktreeHandle>;
|
|
429
|
+
list(root: string): Promise<ListedWorktree[]>;
|
|
430
|
+
cleanup(root: string, branch: string): Promise<CleanupResult>;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export { type BranchNameInput, type BuiltContext, type CleanupResult, type ClockPort, type ContextBuildInput, ContextBuilder, type CreateWorktreeInput, type DetectOptions, type DirEntry, FakeProcess, FilesystemMemoryStore, type FilesystemPort, type GeneratePlanInput, GitDetector, type Issue, type IssueSeverity, type ListedWorktree, MEMORY_DIR, type Memory, MemoryFilesystem, type MemoryStore, type NoteFields, ObsidianDetector, type PlanReview, type ProcessExecOptions, type ProcessHandler, type ProcessPort, type ProcessResult, type ProjectRef, type RecordedCall, type RelevantNote, type RememberInput, type ReviewInput, Reviewer, type SubtaskSpec, VAULT_NOTES_DIR, type ValidatePlanInput, type ValidationResult, type VaultDetection, VaultWriter, WORKSPACE_DIR, WORKSPACE_FILE, WORKSPACE_RELATIVE_PATH, type WorkerKind, type Workspace, WorkspaceScanner, WorktreeError, type WorktreeHandle, WorktreeManager, type WriteNoteInput, type WriteNoteResult, basename, buildBranchName, diffLines, dirname, extname, generatePlanTemplate, joinPath, renderNote, renderWorkspaceMarkdown, slugify, slugifyForGit, suggestFilesLikelyTouched, suggestSubtasks, textsEqual, validatePlan, worktreeRelativePath };
|