@dreki-gg/taskman 0.2.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.
@@ -0,0 +1,682 @@
1
+ import { Context, Effect, Layer, Schema } from "effect";
2
+ import * as _$effect_Types0 from "effect/Types";
3
+ import * as _$effect_Cause0 from "effect/Cause";
4
+ import * as _$effect_SchemaAST0 from "effect/SchemaAST";
5
+ import * as _$effect_ParseResult0 from "effect/ParseResult";
6
+ import * as _$effect_Either0 from "effect/Either";
7
+
8
+ //#region src/types.d.ts
9
+ /**
10
+ * Shared types for plan mode.
11
+ */
12
+ type TaskStatus = 'pending' | 'done' | 'skipped' | 'blocked' | 'deferred';
13
+ /** Where a task came from: the original submitted plan, or discovered during execution. */
14
+ type TaskOrigin = 'plan' | 'discovered';
15
+ /**
16
+ * Plan lifecycle status. Only `in-progress` is active; `done`, `superseded`,
17
+ * and `abandoned` are terminal and drop out of active-plan resolution.
18
+ */
19
+ type PlanStatus = 'in-progress' | 'done' | 'superseded' | 'abandoned';
20
+ /** Initiative lifecycle reuses the plan lifecycle literals. */
21
+ type InitiativeStatus = PlanStatus;
22
+ interface TaskRecord {
23
+ _type: 'task';
24
+ id: string;
25
+ description: string;
26
+ details?: string;
27
+ status: TaskStatus;
28
+ /** Defaults to 'plan' when absent (back-compat with older tasks.jsonl files). */
29
+ origin?: TaskOrigin;
30
+ depends_on?: string[];
31
+ notes?: string;
32
+ created_at: string;
33
+ updated_at: string;
34
+ }
35
+ interface TaskMeta {
36
+ _type: 'meta';
37
+ title: string;
38
+ plan_name: string;
39
+ created_at: string;
40
+ /**
41
+ * Git commit (HEAD) the plan was written against, captured at submit time.
42
+ * Optional for back-compat: older tasks.jsonl files predate this field, and
43
+ * it stays undefined when git metadata is unavailable (no repo, no commits).
44
+ */
45
+ base_commit?: string;
46
+ }
47
+ interface PlanData {
48
+ title: string;
49
+ planName: string;
50
+ handoff: string;
51
+ tasks: TaskRecord[];
52
+ /** Git commit the plan was written against; powers the executor drift check. */
53
+ base_commit?: string;
54
+ }
55
+ type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
56
+ interface ExecPendingConfig {
57
+ model: {
58
+ provider: string;
59
+ id: string;
60
+ };
61
+ thinking: string;
62
+ }
63
+ //#endregion
64
+ //#region src/errors.d.ts
65
+ /**
66
+ * Tagged errors for plan-mode disk I/O and JSONL validation.
67
+ *
68
+ * These replace ad-hoc `throw new Error(...)` so storage programs surface
69
+ * typed, inspectable failures. They are mapped back to user-facing strings at
70
+ * the tool boundary via `errorMessage`.
71
+ */
72
+ declare const PlanReadError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
73
+ readonly _tag: "PlanReadError";
74
+ } & Readonly<A>;
75
+ declare class PlanReadError extends PlanReadError_base<{
76
+ readonly path: string;
77
+ readonly cause: unknown;
78
+ }> {
79
+ get message(): string;
80
+ }
81
+ declare const PlanWriteError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
82
+ readonly _tag: "PlanWriteError";
83
+ } & Readonly<A>;
84
+ declare class PlanWriteError extends PlanWriteError_base<{
85
+ readonly path: string;
86
+ readonly cause: unknown;
87
+ }> {
88
+ get message(): string;
89
+ }
90
+ declare const JsonlParseError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
91
+ readonly _tag: "JsonlParseError";
92
+ } & Readonly<A>;
93
+ declare class JsonlParseError extends JsonlParseError_base<{
94
+ readonly path: string;
95
+ readonly line: number;
96
+ readonly cause?: unknown;
97
+ }> {
98
+ get message(): string;
99
+ }
100
+ declare const JsonlValidationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
101
+ readonly _tag: "JsonlValidationError";
102
+ } & Readonly<A>;
103
+ declare class JsonlValidationError extends JsonlValidationError_base<{
104
+ readonly path: string;
105
+ readonly line: number;
106
+ readonly reason: string;
107
+ }> {
108
+ get message(): string;
109
+ }
110
+ declare const MissingMetaRecord_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
111
+ readonly _tag: "MissingMetaRecord";
112
+ } & Readonly<A>;
113
+ declare class MissingMetaRecord extends MissingMetaRecord_base<{
114
+ readonly path: string;
115
+ }> {
116
+ get message(): string;
117
+ }
118
+ declare const TaskNotFound_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
119
+ readonly _tag: "TaskNotFound";
120
+ } & Readonly<A>;
121
+ declare class TaskNotFound extends TaskNotFound_base<{
122
+ readonly planDir: string;
123
+ readonly taskId: string;
124
+ }> {
125
+ get message(): string;
126
+ }
127
+ declare const TasksFileNotFound_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
128
+ readonly _tag: "TasksFileNotFound";
129
+ } & Readonly<A>;
130
+ declare class TasksFileNotFound extends TasksFileNotFound_base<{
131
+ readonly planDir: string;
132
+ }> {
133
+ get message(): string;
134
+ }
135
+ type PlanStorageError = PlanReadError | PlanWriteError | JsonlParseError | JsonlValidationError | MissingMetaRecord | TaskNotFound | TasksFileNotFound;
136
+ declare function causeMessage(cause: unknown): string;
137
+ declare function errorMessage(error: unknown): string;
138
+ /** Convert any error (including tagged errors) into a native Error for the tool boundary. */
139
+ declare function toNativeError(error: unknown): Error;
140
+ //#endregion
141
+ //#region src/schema.d.ts
142
+ declare const TaskStatusSchema: Schema.Literal<["pending", "done", "skipped", "blocked", "deferred"]>;
143
+ declare const TaskOriginSchema: Schema.Literal<["plan", "discovered"]>;
144
+ declare const TaskRecordSchema: Schema.Struct<{
145
+ _type: Schema.Literal<["task"]>;
146
+ id: typeof Schema.String;
147
+ description: typeof Schema.String;
148
+ details: Schema.optional<typeof Schema.String>;
149
+ status: Schema.Literal<["pending", "done", "skipped", "blocked", "deferred"]>;
150
+ origin: Schema.optional<Schema.Literal<["plan", "discovered"]>>;
151
+ depends_on: Schema.optional<Schema.mutable<Schema.Array$<typeof Schema.String>>>;
152
+ notes: Schema.optional<typeof Schema.String>;
153
+ created_at: typeof Schema.String;
154
+ updated_at: typeof Schema.String;
155
+ }>;
156
+ declare const TaskMetaSchema: Schema.Struct<{
157
+ _type: Schema.Literal<["meta"]>;
158
+ title: typeof Schema.String;
159
+ plan_name: typeof Schema.String;
160
+ created_at: typeof Schema.String; /** Optional git commit the plan was written against (back-compat: absent on older plans). */
161
+ base_commit: Schema.optional<typeof Schema.String>;
162
+ }>;
163
+ /** A single tasks.jsonl line is either the meta record or a task record. */
164
+ declare const TasksLineSchema: Schema.Union<[Schema.Struct<{
165
+ _type: Schema.Literal<["meta"]>;
166
+ title: typeof Schema.String;
167
+ plan_name: typeof Schema.String;
168
+ created_at: typeof Schema.String; /** Optional git commit the plan was written against (back-compat: absent on older plans). */
169
+ base_commit: Schema.optional<typeof Schema.String>;
170
+ }>, Schema.Struct<{
171
+ _type: Schema.Literal<["task"]>;
172
+ id: typeof Schema.String;
173
+ description: typeof Schema.String;
174
+ details: Schema.optional<typeof Schema.String>;
175
+ status: Schema.Literal<["pending", "done", "skipped", "blocked", "deferred"]>;
176
+ origin: Schema.optional<Schema.Literal<["plan", "discovered"]>>;
177
+ depends_on: Schema.optional<Schema.mutable<Schema.Array$<typeof Schema.String>>>;
178
+ notes: Schema.optional<typeof Schema.String>;
179
+ created_at: typeof Schema.String;
180
+ updated_at: typeof Schema.String;
181
+ }>]>;
182
+ /**
183
+ * Plan lifecycle statuses.
184
+ * - in-progress: active, tracked, eligible for auto-resolution
185
+ * - done: completed (all tasks resolved)
186
+ * - superseded: closed because another plan absorbed the work
187
+ * - abandoned: closed without shipping (rejected / won't do)
188
+ * Only `in-progress` is treated as active; the rest are terminal.
189
+ */
190
+ declare const PlanStatusSchema: Schema.Literal<["in-progress", "done", "superseded", "abandoned"]>;
191
+ declare const PlanManifestEntrySchema: Schema.Struct<{
192
+ _type: Schema.Literal<["plan"]>;
193
+ name: typeof Schema.String;
194
+ status: Schema.Literal<["in-progress", "done", "superseded", "abandoned"]>;
195
+ title: typeof Schema.String;
196
+ created_at: typeof Schema.String;
197
+ completed_at: Schema.NullOr<typeof Schema.String>; /** Optional human-readable reason, used for terminal statuses. */
198
+ reason: Schema.optional<typeof Schema.String>; /** Parent initiative name (kebab). Absent = standalone flat plan. */
199
+ initiative: Schema.optional<typeof Schema.String>;
200
+ /**
201
+ * Plan-level dependencies: names of plans this plan depends on. Distinct from
202
+ * the task-level `depends_on` above. Cross-initiative references are allowed.
203
+ */
204
+ depends_on: Schema.optional<Schema.mutable<Schema.Array$<typeof Schema.String>>>;
205
+ }>;
206
+ /**
207
+ * Initiative lifecycle statuses reuse the plan lifecycle literals. An
208
+ * initiative's status is a projection of its member plans' statuses, with the
209
+ * same terminal-guard semantics as plans.
210
+ */
211
+ declare const InitiativeStatusSchema: Schema.Literal<["in-progress", "done", "superseded", "abandoned"]>;
212
+ declare const InitiativeManifestEntrySchema: Schema.Struct<{
213
+ _type: Schema.Literal<["initiative"]>;
214
+ name: typeof Schema.String;
215
+ status: Schema.Literal<["in-progress", "done", "superseded", "abandoned"]>;
216
+ title: typeof Schema.String;
217
+ created_at: typeof Schema.String;
218
+ completed_at: Schema.NullOr<typeof Schema.String>; /** Optional human-readable reason, used for terminal statuses. */
219
+ reason: Schema.optional<typeof Schema.String>;
220
+ }>;
221
+ declare const ExecPendingConfigSchema: Schema.Struct<{
222
+ model: Schema.Struct<{
223
+ provider: typeof Schema.String;
224
+ id: typeof Schema.String;
225
+ }>;
226
+ thinking: typeof Schema.String;
227
+ }>;
228
+ declare const decodeTaskRecord: (u: unknown, overrideOptions?: _$effect_SchemaAST0.ParseOptions) => _$effect_Either0.Either<{
229
+ readonly _type: "task";
230
+ readonly id: string;
231
+ readonly description: string;
232
+ readonly details?: string | undefined;
233
+ readonly status: "pending" | "done" | "skipped" | "blocked" | "deferred";
234
+ readonly origin?: "plan" | "discovered" | undefined;
235
+ readonly depends_on?: string[] | undefined;
236
+ readonly notes?: string | undefined;
237
+ readonly created_at: string;
238
+ readonly updated_at: string;
239
+ }, _$effect_ParseResult0.ParseError>;
240
+ declare const decodeTaskMeta: (u: unknown, overrideOptions?: _$effect_SchemaAST0.ParseOptions) => _$effect_Either0.Either<{
241
+ readonly _type: "meta";
242
+ readonly created_at: string;
243
+ readonly title: string;
244
+ readonly plan_name: string;
245
+ readonly base_commit?: string | undefined;
246
+ }, _$effect_ParseResult0.ParseError>;
247
+ declare const decodeTasksLine: (u: unknown, overrideOptions?: _$effect_SchemaAST0.ParseOptions) => _$effect_Either0.Either<{
248
+ readonly _type: "meta";
249
+ readonly created_at: string;
250
+ readonly title: string;
251
+ readonly plan_name: string;
252
+ readonly base_commit?: string | undefined;
253
+ } | {
254
+ readonly _type: "task";
255
+ readonly id: string;
256
+ readonly description: string;
257
+ readonly details?: string | undefined;
258
+ readonly status: "pending" | "done" | "skipped" | "blocked" | "deferred";
259
+ readonly origin?: "plan" | "discovered" | undefined;
260
+ readonly depends_on?: string[] | undefined;
261
+ readonly notes?: string | undefined;
262
+ readonly created_at: string;
263
+ readonly updated_at: string;
264
+ }, _$effect_ParseResult0.ParseError>;
265
+ declare const decodePlanManifestEntry: (u: unknown, overrideOptions?: _$effect_SchemaAST0.ParseOptions) => _$effect_Either0.Either<{
266
+ readonly _type: "plan";
267
+ readonly status: "done" | "in-progress" | "superseded" | "abandoned";
268
+ readonly depends_on?: string[] | undefined;
269
+ readonly created_at: string;
270
+ readonly title: string;
271
+ readonly name: string;
272
+ readonly completed_at: string | null;
273
+ readonly reason?: string | undefined;
274
+ readonly initiative?: string | undefined;
275
+ }, _$effect_ParseResult0.ParseError>;
276
+ declare const decodeInitiativeManifestEntry: (u: unknown, overrideOptions?: _$effect_SchemaAST0.ParseOptions) => _$effect_Either0.Either<{
277
+ readonly _type: "initiative";
278
+ readonly status: "done" | "in-progress" | "superseded" | "abandoned";
279
+ readonly created_at: string;
280
+ readonly title: string;
281
+ readonly name: string;
282
+ readonly completed_at: string | null;
283
+ readonly reason?: string | undefined;
284
+ }, _$effect_ParseResult0.ParseError>;
285
+ declare const decodeExecPendingConfig: (u: unknown, overrideOptions?: _$effect_SchemaAST0.ParseOptions) => _$effect_Either0.Either<{
286
+ readonly model: {
287
+ readonly id: string;
288
+ readonly provider: string;
289
+ };
290
+ readonly thinking: string;
291
+ }, _$effect_ParseResult0.ParseError>;
292
+ //#endregion
293
+ //#region src/effects/filesystem.d.ts
294
+ interface FileSystemService {
295
+ readonly readFileString: (path: string) => Effect.Effect<string, PlanReadError>;
296
+ readonly writeFileString: (path: string, data: string) => Effect.Effect<void, PlanWriteError>;
297
+ readonly writeFileAtomic: (path: string, data: string) => Effect.Effect<void, PlanWriteError>;
298
+ readonly makeDir: (path: string) => Effect.Effect<void, PlanWriteError>;
299
+ readonly listDirectories: (path: string) => Effect.Effect<string[], PlanReadError>;
300
+ readonly removeFile: (path: string) => Effect.Effect<void, PlanWriteError>;
301
+ }
302
+ declare const FileSystem_base: Context.TagClass<FileSystem, "PlanMode/FileSystem", FileSystemService>;
303
+ declare class FileSystem extends FileSystem_base {}
304
+ declare const nodeFileSystemService: FileSystemService;
305
+ //#endregion
306
+ //#region src/effects/runtime.d.ts
307
+ declare function makeRuntimeLayer(): Layer.Layer<FileSystem>;
308
+ /** Build a bridge that runs storage programs against the live filesystem layer. */
309
+ declare function makePlanRuntime(): <A, E>(program: Effect.Effect<A, E, FileSystem>) => Promise<A>;
310
+ type RunPlanIO = ReturnType<typeof makePlanRuntime>;
311
+ //#endregion
312
+ //#region src/storage/task-storage.d.ts
313
+ interface TasksSnapshot {
314
+ meta: TaskMeta;
315
+ tasks: TaskRecord[];
316
+ }
317
+ type ReadError$3 = JsonlParseError | JsonlValidationError | MissingMetaRecord;
318
+ declare function readTasksJsonl(planDir: string): Effect.Effect<TasksSnapshot | undefined, ReadError$3, FileSystem>;
319
+ declare function writeTasksJsonl(planDir: string, meta: TaskMeta, tasks: TaskRecord[]): Effect.Effect<void, PlanWriteError, FileSystem>;
320
+ declare function updateTask(planDir: string, taskId: string, updates: Partial<Omit<TaskRecord, '_type' | 'id' | 'created_at'>>): Effect.Effect<TaskRecord, ReadError$3 | PlanWriteError | TasksFileNotFound | TaskNotFound, FileSystem>;
321
+ //#endregion
322
+ //#region src/storage/plan-storage.d.ts
323
+ declare function saveHandoff(planDir: string, content: string): Effect.Effect<void, PlanWriteError, FileSystem>;
324
+ declare function loadHandoff(planDir: string): Effect.Effect<string | undefined, never, FileSystem>;
325
+ declare function saveInitiative(initiativeDir: string, content: string): Effect.Effect<void, PlanWriteError, FileSystem>;
326
+ //#endregion
327
+ //#region src/storage/plans-manifest.d.ts
328
+ interface PlanManifestEntry {
329
+ _type: 'plan';
330
+ name: string;
331
+ status: PlanStatus;
332
+ title: string;
333
+ created_at: string;
334
+ completed_at: string | null;
335
+ reason?: string;
336
+ /** Parent initiative name (kebab). Absent = standalone flat plan. */
337
+ initiative?: string;
338
+ /** Plan-level dependencies (plan names). Cross-initiative allowed. */
339
+ depends_on?: string[];
340
+ }
341
+ /** A status is terminal (closed) when it is anything other than in-progress. */
342
+ declare function isTerminalStatus(status: PlanStatus): boolean;
343
+ type ReadError$2 = JsonlParseError | JsonlValidationError;
344
+ declare function readPlansManifest(): Effect.Effect<PlanManifestEntry[], ReadError$2, FileSystem>;
345
+ declare function writePlansManifest(entries: PlanManifestEntry[]): Effect.Effect<void, PlanWriteError, FileSystem>;
346
+ interface PlanUpsert {
347
+ status: PlanStatus;
348
+ title?: string;
349
+ reason?: string;
350
+ /** Parent initiative name; preserved when omitted. */
351
+ initiative?: string;
352
+ /** Plan-level dependencies (plan names); preserved when omitted. */
353
+ depends_on?: string[];
354
+ }
355
+ /**
356
+ * Pure transform: upsert `name` into the in-memory `entries` array, preserving
357
+ * created_at / membership / deps from any existing entry. No IO — shared by the
358
+ * locked `upsertPlanEntry` and `reconcilePlanStatus` so both flow through one
359
+ * serialized read-modify-write and never nest locks.
360
+ */
361
+ declare function applyPlanUpsert(entries: PlanManifestEntry[], name: string, updates: PlanUpsert): void;
362
+ /**
363
+ * Serialized read-modify-write of the plans registry. Holds a process-wide lock
364
+ * on the manifest path across the whole read → transform → write so concurrent
365
+ * tool calls cannot clobber each other (lost-update race). `transform` mutates
366
+ * the entries array in place and returns `true` when it changed something
367
+ * (return `false` to skip the rewrite).
368
+ */
369
+ declare function mutatePlansManifest(transform: (entries: PlanManifestEntry[]) => boolean): Effect.Effect<void, ReadError$2 | PlanWriteError, FileSystem>;
370
+ declare function upsertPlanEntry(name: string, updates: PlanUpsert): Effect.Effect<void, ReadError$2 | PlanWriteError, FileSystem>;
371
+ /**
372
+ * Reconcile a plan's registry status from its task state.
373
+ *
374
+ * The registry `status` is a PROJECTION of task state, not a parallel flag.
375
+ * Call this wherever tasks are written so completion is never coupled to a
376
+ * formal in-session execution run (see FEEDBACK #1). `finalizable` means every
377
+ * active task is resolved AND no deferred follow-ups remain.
378
+ *
379
+ * Guard: a manually-set terminal status (`superseded` / `abandoned`) is never
380
+ * auto-overridden — only `in-progress` ⇄ `done` is derived from tasks.
381
+ */
382
+ declare function reconcilePlanStatus(name: string, finalizable: boolean, title?: string): Effect.Effect<void, ReadError$2 | PlanWriteError, FileSystem>;
383
+ //#endregion
384
+ //#region src/storage/initiatives-manifest.d.ts
385
+ interface InitiativeManifestEntry {
386
+ _type: 'initiative';
387
+ name: string;
388
+ status: InitiativeStatus;
389
+ title: string;
390
+ created_at: string;
391
+ completed_at: string | null;
392
+ reason?: string;
393
+ }
394
+ type ReadError$1 = JsonlParseError | JsonlValidationError;
395
+ declare function readInitiativesManifest(): Effect.Effect<InitiativeManifestEntry[], ReadError$1, FileSystem>;
396
+ declare function writeInitiativesManifest(entries: InitiativeManifestEntry[]): Effect.Effect<void, PlanWriteError, FileSystem>;
397
+ interface InitiativeUpsert {
398
+ status: InitiativeStatus;
399
+ title?: string;
400
+ reason?: string;
401
+ }
402
+ /**
403
+ * Pure transform: upsert `name` into the in-memory `entries` array, preserving
404
+ * created_at from any existing entry. No IO — shared by the locked
405
+ * `upsertInitiativeEntry` and `reconcileInitiativeStatus` so both flow through
406
+ * one serialized read-modify-write and never nest locks.
407
+ */
408
+ declare function applyInitiativeUpsert(entries: InitiativeManifestEntry[], name: string, updates: InitiativeUpsert): void;
409
+ /**
410
+ * Serialized read-modify-write of the initiatives registry. Holds a
411
+ * process-wide lock on the manifest path across the whole read → transform →
412
+ * write so concurrent tool calls cannot clobber each other. `transform` may run
413
+ * IO (e.g. read the plans manifest to project status) and mutates the entries
414
+ * array in place, returning `true` when it changed something.
415
+ */
416
+ declare function mutateInitiativesManifest<E, R>(transform: (entries: InitiativeManifestEntry[]) => Effect.Effect<boolean, E, R>): Effect.Effect<void, ReadError$1 | PlanWriteError | E, FileSystem | R>;
417
+ declare function upsertInitiativeEntry(name: string, updates: InitiativeUpsert): Effect.Effect<void, ReadError$1 | PlanWriteError, FileSystem>;
418
+ //#endregion
419
+ //#region src/storage/atomic-write.d.ts
420
+ interface AtomicWriteOptions {
421
+ /** Test seam: file mode for the temporary file. */
422
+ mode?: number;
423
+ }
424
+ /**
425
+ * Atomically write `data` to `path`: write to a temp file, fsync, rename into
426
+ * place, then best-effort fsync the directory. Failures surface as
427
+ * `PlanWriteError`.
428
+ */
429
+ declare function writeFileAtomic(path: string, data: string | Buffer, options?: AtomicWriteOptions): Effect.Effect<void, PlanWriteError>;
430
+ //#endregion
431
+ //#region src/storage/file-lock.d.ts
432
+ /**
433
+ * Run `effect` while holding the single permit for `key`. Concurrent callers
434
+ * with the same key queue and run one at a time; the permit is always released,
435
+ * even on failure or interruption.
436
+ *
437
+ * Do NOT nest `withFileLock` for the same key inside another — the permit is
438
+ * not reentrant and would deadlock. Express composite read-modify-write as one
439
+ * locked section instead.
440
+ */
441
+ declare function withFileLock<A, E, R>(key: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>;
442
+ //#endregion
443
+ //#region src/task-status.d.ts
444
+ declare function deferredTasks(tasks: readonly TaskRecord[]): TaskRecord[];
445
+ /**
446
+ * True when no active work remains — every task is done, skipped, or deferred
447
+ * (nothing pending or blocked).
448
+ */
449
+ declare function activeTasksResolved(tasks: readonly TaskRecord[]): boolean;
450
+ /**
451
+ * True when the plan can be marked complete: active work is resolved AND there
452
+ * are no deferred follow-ups awaiting the user's decision.
453
+ */
454
+ declare function isPlanFinalizable(tasks: readonly TaskRecord[]): boolean;
455
+ /**
456
+ * Reactivate tasks for a resumed run: blocked tasks and deferred follow-ups
457
+ * become pending (mutated in place). Returns true if anything changed.
458
+ */
459
+ declare function reactivateForExecution(tasks: TaskRecord[], timestamp: string): boolean;
460
+ //#endregion
461
+ //#region src/reconcile.d.ts
462
+ interface PlanDriftRow {
463
+ name: string;
464
+ /** Registry status, or `undefined` when there is a task dir but no entry. */
465
+ registryStatus?: PlanStatus;
466
+ title?: string;
467
+ /** Derived from tasks: `done` when finalizable, else `in-progress`. */
468
+ derivedStatus?: 'in-progress' | 'done';
469
+ /** Resolved/total task counts when a tasks.jsonl exists. */
470
+ resolved?: number;
471
+ total?: number;
472
+ /** True when a `tasks.jsonl` snapshot was found for this plan. */
473
+ hasTasks: boolean;
474
+ /**
475
+ * Drift class:
476
+ * - 'status' : registry status disagrees with derived task status
477
+ * - 'registry-only' : registry entry but no tasks.jsonl dir
478
+ * - 'orphan' : tasks.jsonl dir but no registry entry
479
+ * - undefined : in sync
480
+ */
481
+ drift?: 'status' | 'registry-only' | 'orphan';
482
+ /**
483
+ * For `status` drift, the direction the registry would move if projected from
484
+ * tasks:
485
+ * - 'upgrade' : registry `in-progress` → tasks `done` (safe; auto-repaired)
486
+ * - 'downgrade' : registry `done` → tasks `in-progress` (NOT auto-repaired)
487
+ *
488
+ * A downgrade almost always means "work merged but tasks were never marked
489
+ * done" — auto-projecting tasks→registry there would REGRESS a finished plan
490
+ * back to in-progress (the wrong direction). We surface it for a human to
491
+ * resolve by marking the tasks done instead.
492
+ */
493
+ direction?: 'upgrade' | 'downgrade';
494
+ }
495
+ type CollectError = JsonlParseError | JsonlValidationError | MissingMetaRecord;
496
+ /** Walk every plan (registry + task dirs) and classify drift. Pure read. */
497
+ declare function collectPlanDrift(): Effect.Effect<PlanDriftRow[], CollectError, FileSystem>;
498
+ interface InitiativeDriftRow {
499
+ name: string;
500
+ registryStatus: PlanStatus;
501
+ title: string;
502
+ /** Projected from member plans: `done` when finalizable, else `in-progress`. */
503
+ derivedStatus: 'in-progress' | 'done';
504
+ members: number;
505
+ /** 'status' when the registry status disagrees with the projection. */
506
+ drift?: 'status';
507
+ }
508
+ /** Compare each initiative's registry status against its member-plan projection. */
509
+ declare function collectInitiativeDrift(): Effect.Effect<InitiativeDriftRow[], CollectError, FileSystem>;
510
+ /** Repair `status`-class initiative drift by re-projecting from member plans. */
511
+ declare function applyInitiativeReconcile(rows: InitiativeDriftRow[]): Effect.Effect<InitiativeDriftRow[], CollectError | PlanWriteError, FileSystem>;
512
+ /**
513
+ * Repair `status`-class drift by projecting derived status into the registry.
514
+ *
515
+ * Safety: only `upgrade` drift (registry `in-progress` → tasks `done`) is
516
+ * auto-repaired. A `downgrade` (registry `done` → tasks `in-progress`) is
517
+ * reported but NEVER auto-applied — it almost always means work merged without
518
+ * marking tasks done, and projecting tasks→registry there would regress a
519
+ * finished plan. The human resolves it by marking the tasks done instead.
520
+ *
521
+ * Orphans and registry-only rows are likewise reported but not auto-fixed.
522
+ * Returns the rows that were repaired.
523
+ */
524
+ declare function applyReconcile(rows: PlanDriftRow[]): Effect.Effect<PlanDriftRow[], CollectError | PlanWriteError, FileSystem>;
525
+ //#endregion
526
+ //#region src/initiative.d.ts
527
+ interface PlanReadiness {
528
+ name: string;
529
+ /** True when every plan in `depends_on` is `done`. */
530
+ ready: boolean;
531
+ /** Dependency plan names that are not yet `done` (unknown deps count too). */
532
+ blockedBy: string[];
533
+ }
534
+ /**
535
+ * For each `in-progress` plan, whether all of its plan-level dependencies are
536
+ * `done`. Only a `done` dependency unblocks — a missing, in-progress, or
537
+ * terminally-closed (superseded/abandoned) dependency keeps a plan blocked.
538
+ */
539
+ declare function computePlanReadiness(plans: readonly PlanManifestEntry[]): PlanReadiness[];
540
+ /** Member plans of an initiative (linked by name in the plans manifest). */
541
+ declare function membersOf(initiative: string, plans: readonly PlanManifestEntry[]): PlanManifestEntry[];
542
+ /**
543
+ * An initiative is finalizable (`done`) when it has ≥1 member plan AND every
544
+ * member is terminal (no member is `in-progress`). Mirrors the plan-level rule
545
+ * one level up.
546
+ */
547
+ declare function isInitiativeFinalizable(initiative: string, plans: readonly PlanManifestEntry[]): boolean;
548
+ interface InitiativeMemberRow {
549
+ name: string;
550
+ title: string;
551
+ status: PlanStatus;
552
+ /** Present for in-progress members. */
553
+ ready?: boolean;
554
+ blockedBy?: string[];
555
+ }
556
+ interface InitiativeRollup {
557
+ name: string;
558
+ total: number;
559
+ done: number;
560
+ /** Terminal but not done (superseded / abandoned). */
561
+ closed: number;
562
+ inProgress: number;
563
+ ready: number;
564
+ blocked: number;
565
+ members: InitiativeMemberRow[];
566
+ }
567
+ /** Aggregate an initiative's member plans into counts + per-member readiness. */
568
+ declare function initiativeRollup(initiative: string, plans: readonly PlanManifestEntry[]): InitiativeRollup;
569
+ type ReconcileError = JsonlParseError | JsonlValidationError | PlanWriteError;
570
+ /**
571
+ * Re-derive an initiative's registry status from its member plans.
572
+ *
573
+ * Like `reconcilePlanStatus`: only reflects state for a KNOWN initiative (never
574
+ * conjures an entry), and never clobbers a manually-set terminal status
575
+ * (`superseded` / `abandoned`). Only `in-progress` ⇄ `done` is derived.
576
+ */
577
+ declare function reconcileInitiativeStatus(name: string): Effect.Effect<void, ReconcileError, FileSystem>;
578
+ /**
579
+ * Reconcile the initiative that a given plan belongs to (no-op when the plan is
580
+ * standalone). Call this after any plan-status write so the initiative level
581
+ * stays in sync without callers needing to know the parent name.
582
+ */
583
+ declare function reconcileInitiativeForPlan(planName: string): Effect.Effect<void, ReconcileError, FileSystem>;
584
+ //#endregion
585
+ //#region src/resolve.d.ts
586
+ interface ResolvedPlanName {
587
+ /** The resolved bare plan name, when resolvable. */
588
+ planName?: string;
589
+ /** Plan directory (`.plans/<name>`) for the resolved plan. */
590
+ planDir?: string;
591
+ /** In-progress plan names, surfaced when resolution was ambiguous or missed. */
592
+ candidates: string[];
593
+ }
594
+ type ResolveError = JsonlParseError | JsonlValidationError;
595
+ /** Normalize a plan hint (`my-plan` or `.plans/my-plan`) to a bare name. */
596
+ declare function normalizePlanName(hint: string): string;
597
+ declare function resolvePlanByName(opts?: {
598
+ name?: string;
599
+ }): Effect.Effect<ResolvedPlanName, ResolveError, FileSystem>;
600
+ /** Build full plan data (`title, planName, handoff, tasks, base_commit`) from disk. */
601
+ declare function loadPlanData(planDir: string): Effect.Effect<PlanData | undefined, JsonlParseError | JsonlValidationError | MissingMetaRecord, FileSystem>;
602
+ //#endregion
603
+ //#region src/engine.d.ts
604
+ type ReadError = JsonlParseError | JsonlValidationError | MissingMetaRecord;
605
+ type WriteFlowError = ReadError | PlanWriteError | TasksFileNotFound;
606
+ interface UpdatedTaskResult {
607
+ task: TaskRecord;
608
+ finalizable: boolean;
609
+ }
610
+ /**
611
+ * Set a task's status (and optional notes), persist, then re-project registry
612
+ * status. Mirrors the extension's `onTaskUpdated`.
613
+ */
614
+ declare function setTaskStatus(planDir: string, taskId: string, status: TaskStatus, notes?: string): Effect.Effect<UpdatedTaskResult, WriteFlowError | TaskNotFound, FileSystem>;
615
+ interface AddTaskInput {
616
+ description: string;
617
+ reason: string;
618
+ details?: string;
619
+ depends_on?: string[];
620
+ }
621
+ /**
622
+ * Append a discovered follow-up as a `deferred` task, persist, then re-project
623
+ * registry status (a new deferred task can re-open a done plan). Mirrors the
624
+ * extension's `add_task` + `onTaskAdded`.
625
+ */
626
+ declare function appendDeferredTask(planDir: string, input: AddTaskInput): Effect.Effect<TaskRecord, WriteFlowError, FileSystem>;
627
+ declare namespace plans_d_exports {
628
+ export { PlanListItem, SortField, StatusFilter$1 as StatusFilter, filterPlans, formatPlanList, loadPlanListItems, parseListArgs, sortPlans };
629
+ }
630
+ type SortField = 'name' | 'date-asc' | 'date-desc' | 'tasks';
631
+ type StatusFilter$1 = 'all' | PlanStatus;
632
+ interface PlanListItem {
633
+ name: string;
634
+ title: string;
635
+ status: PlanStatus;
636
+ created_at: string;
637
+ completed_at: string | null;
638
+ totalTasks: number;
639
+ doneTasks: number;
640
+ pendingTasks: number;
641
+ }
642
+ declare function filterPlans(plans: PlanListItem[], filter: StatusFilter$1): PlanListItem[];
643
+ declare function sortPlans(plans: PlanListItem[], sort: SortField): PlanListItem[];
644
+ declare function formatPlanList(plans: PlanListItem[], filter: StatusFilter$1, sort: SortField): string;
645
+ declare function loadPlanListItems(): Effect.Effect<PlanListItem[], never, FileSystem>;
646
+ declare function parseListArgs(raw: string): {
647
+ filter: StatusFilter$1;
648
+ sort: SortField;
649
+ };
650
+ declare namespace initiatives_d_exports {
651
+ export { InitiativeListItem, StatusFilter, filterInitiatives, formatInitiativeList, loadInitiativeListItems, parseInitiativeFilter };
652
+ }
653
+ type StatusFilter = 'all' | InitiativeStatus;
654
+ interface InitiativeListItem {
655
+ name: string;
656
+ title: string;
657
+ status: InitiativeStatus;
658
+ created_at: string;
659
+ totalPlans: number;
660
+ donePlans: number;
661
+ ready: number;
662
+ blocked: number;
663
+ }
664
+ declare function filterInitiatives(items: InitiativeListItem[], filter: StatusFilter): InitiativeListItem[];
665
+ declare function formatInitiativeList(items: InitiativeListItem[], filter: StatusFilter): string;
666
+ declare function loadInitiativeListItems(): Effect.Effect<InitiativeListItem[], never, FileSystem>;
667
+ declare function parseInitiativeFilter(raw: string): StatusFilter;
668
+ //#endregion
669
+ //#region src/ids.d.ts
670
+ /**
671
+ * Pure id / name helpers shared by the engine and its consumers.
672
+ */
673
+ declare function toKebabCase(name: string): string;
674
+ /**
675
+ * Generate the next sequential task id (`t-NNN`) given existing ids.
676
+ *
677
+ * Uses the max numeric suffix of `t-<digits>` ids + 1, zero-padded to 3.
678
+ * Falls back to `t-<count+1>` when no ids match the pattern.
679
+ */
680
+ declare function nextTaskId(existingIds: readonly string[]): string;
681
+ //#endregion
682
+ export { AddTaskInput, type ExecPendingConfig, ExecPendingConfigSchema, FileSystem, type FileSystemService, InitiativeDriftRow, initiatives_d_exports as InitiativeListing, type InitiativeManifestEntry, InitiativeManifestEntrySchema, InitiativeMemberRow, InitiativeRollup, type InitiativeStatus, InitiativeStatusSchema, type InitiativeUpsert, JsonlParseError, JsonlValidationError, MissingMetaRecord, type PlanData, PlanDriftRow, plans_d_exports as PlanListing, type PlanManifestEntry, PlanManifestEntrySchema, PlanReadError, PlanReadiness, type PlanStatus, PlanStatusSchema, PlanStorageError, type PlanUpsert, PlanWriteError, ResolvedPlanName, type RunPlanIO, type TaskMeta, TaskMetaSchema, TaskNotFound, type TaskOrigin, TaskOriginSchema, type TaskRecord, TaskRecordSchema, type TaskStatus, TaskStatusSchema, TasksFileNotFound, TasksLineSchema, TasksSnapshot, type ThinkingLevel, UpdatedTaskResult, activeTasksResolved, appendDeferredTask, applyInitiativeReconcile, applyInitiativeUpsert, applyPlanUpsert, applyReconcile, causeMessage, collectInitiativeDrift, collectPlanDrift, computePlanReadiness, decodeExecPendingConfig, decodeInitiativeManifestEntry, decodePlanManifestEntry, decodeTaskMeta, decodeTaskRecord, decodeTasksLine, deferredTasks, errorMessage, initiativeRollup, isInitiativeFinalizable, isPlanFinalizable, isTerminalStatus, loadHandoff, loadPlanData, makePlanRuntime, makeRuntimeLayer, membersOf, mutateInitiativesManifest, mutatePlansManifest, nextTaskId, nodeFileSystemService, normalizePlanName, reactivateForExecution, readInitiativesManifest, readPlansManifest, readTasksJsonl, reconcileInitiativeForPlan, reconcileInitiativeStatus, reconcilePlanStatus, resolvePlanByName, saveHandoff, saveInitiative, setTaskStatus, toKebabCase, toNativeError, updateTask, upsertInitiativeEntry, upsertPlanEntry, withFileLock, writeFileAtomic, writeInitiativesManifest, writePlansManifest, writeTasksJsonl };