@dreki-gg/pi-plan-mode 0.26.0 → 0.26.1

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 (57) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/extensions/plan-mode/__tests__/initiative-status.test.ts +5 -5
  3. package/extensions/plan-mode/__tests__/plan-references-context.test.ts +4 -4
  4. package/extensions/plan-mode/__tests__/reconcile-plans.test.ts +3 -3
  5. package/extensions/plan-mode/__tests__/resolve-plan.test.ts +4 -4
  6. package/extensions/plan-mode/__tests__/revise-plan.test.ts +4 -4
  7. package/extensions/plan-mode/__tests__/submit-initiative.test.ts +2 -2
  8. package/extensions/plan-mode/__tests__/submit-plan.test.ts +3 -3
  9. package/extensions/plan-mode/__tests__/update-initiative.test.ts +2 -2
  10. package/extensions/plan-mode/__tests__/update-plan.test.ts +2 -2
  11. package/extensions/plan-mode/__tests__/utils.test.ts +2 -1
  12. package/extensions/plan-mode/commands/list-initiatives.ts +19 -109
  13. package/extensions/plan-mode/commands/list-plans.ts +21 -168
  14. package/extensions/plan-mode/exec-pending.ts +59 -0
  15. package/extensions/plan-mode/index.ts +8 -7
  16. package/extensions/plan-mode/references/context.ts +5 -5
  17. package/extensions/plan-mode/references/plan-index.ts +1 -1
  18. package/extensions/plan-mode/resolve-plan.ts +4 -4
  19. package/extensions/plan-mode/resume.ts +6 -5
  20. package/extensions/plan-mode/tools/add-task.ts +1 -1
  21. package/extensions/plan-mode/tools/initiative-status.ts +6 -6
  22. package/extensions/plan-mode/tools/preview-prototype.ts +3 -3
  23. package/extensions/plan-mode/tools/reconcile-plans.ts +2 -2
  24. package/extensions/plan-mode/tools/revise-plan.ts +7 -7
  25. package/extensions/plan-mode/tools/submit-initiative.ts +4 -4
  26. package/extensions/plan-mode/tools/submit-plan.ts +7 -7
  27. package/extensions/plan-mode/tools/update-initiative.ts +2 -2
  28. package/extensions/plan-mode/tools/update-plan.ts +3 -3
  29. package/extensions/plan-mode/types.ts +17 -59
  30. package/extensions/plan-mode/utils.ts +2 -29
  31. package/package.json +2 -1
  32. package/extensions/plan-mode/__tests__/atomic-write.test.ts +0 -45
  33. package/extensions/plan-mode/__tests__/concurrent-writes.test.ts +0 -85
  34. package/extensions/plan-mode/__tests__/initiative-readiness.test.ts +0 -159
  35. package/extensions/plan-mode/__tests__/initiatives-manifest.test.ts +0 -89
  36. package/extensions/plan-mode/__tests__/list-initiatives.test.ts +0 -59
  37. package/extensions/plan-mode/__tests__/list-plans.test.ts +0 -253
  38. package/extensions/plan-mode/__tests__/plan-storage.test.ts +0 -57
  39. package/extensions/plan-mode/__tests__/plans-manifest.test.ts +0 -120
  40. package/extensions/plan-mode/__tests__/reconcile.test.ts +0 -188
  41. package/extensions/plan-mode/__tests__/schema.test.ts +0 -334
  42. package/extensions/plan-mode/__tests__/task-status.test.ts +0 -74
  43. package/extensions/plan-mode/__tests__/task-storage.test.ts +0 -101
  44. package/extensions/plan-mode/effects/filesystem.ts +0 -65
  45. package/extensions/plan-mode/effects/runtime.ts +0 -24
  46. package/extensions/plan-mode/errors.ts +0 -105
  47. package/extensions/plan-mode/initiative.ts +0 -172
  48. package/extensions/plan-mode/plan-storage.ts +0 -6
  49. package/extensions/plan-mode/reconcile.ts +0 -235
  50. package/extensions/plan-mode/schema.ts +0 -99
  51. package/extensions/plan-mode/storage/atomic-write.ts +0 -75
  52. package/extensions/plan-mode/storage/file-lock.ts +0 -49
  53. package/extensions/plan-mode/storage/initiatives-manifest.ts +0 -154
  54. package/extensions/plan-mode/storage/plan-storage.ts +0 -88
  55. package/extensions/plan-mode/storage/plans-manifest.ts +0 -174
  56. package/extensions/plan-mode/storage/task-storage.ts +0 -111
  57. package/extensions/plan-mode/task-status.ts +0 -46
@@ -1,111 +0,0 @@
1
- import { Effect, Either, Option } from 'effect';
2
- import { join } from 'node:path';
3
- import { FileSystem } from '../effects/filesystem.js';
4
- import {
5
- JsonlParseError,
6
- JsonlValidationError,
7
- MissingMetaRecord,
8
- PlanWriteError,
9
- TaskNotFound,
10
- TasksFileNotFound,
11
- } from '../errors.js';
12
- import { decodeTasksLine } from '../schema.js';
13
- import type { TaskMeta, TaskRecord } from '../types.js';
14
- import { withFileLock } from './file-lock.js';
15
-
16
- const TASKS_FILE = 'tasks.jsonl';
17
-
18
- export interface TasksSnapshot {
19
- meta: TaskMeta;
20
- tasks: TaskRecord[];
21
- }
22
-
23
- type ReadError = JsonlParseError | JsonlValidationError | MissingMetaRecord;
24
-
25
- export function readTasksJsonl(
26
- planDir: string,
27
- ): Effect.Effect<TasksSnapshot | undefined, ReadError, FileSystem> {
28
- const path = join(planDir, TASKS_FILE);
29
- return Effect.gen(function* () {
30
- const fs = yield* FileSystem;
31
- // A read failure (missing or unreadable file) is treated as "no snapshot".
32
- const maybeText = yield* Effect.option(fs.readFileString(path));
33
- if (Option.isNone(maybeText)) return undefined;
34
-
35
- const text = maybeText.value;
36
- if (!text.trim()) return yield* Effect.fail(new MissingMetaRecord({ path }));
37
-
38
- let meta: TaskMeta | undefined;
39
- const tasks: TaskRecord[] = [];
40
- for (const [index, raw] of text.split(/\r?\n/).entries()) {
41
- if (!raw.trim()) continue;
42
- const line = index + 1;
43
-
44
- let parsed: unknown;
45
- try {
46
- parsed = JSON.parse(raw);
47
- } catch (cause) {
48
- return yield* Effect.fail(new JsonlParseError({ path, line, cause }));
49
- }
50
-
51
- const decoded = decodeTasksLine(parsed);
52
- if (Either.isLeft(decoded)) {
53
- return yield* Effect.fail(
54
- new JsonlValidationError({ path, line, reason: decoded.left.message }),
55
- );
56
- }
57
-
58
- const record = decoded.right;
59
- if (record._type === 'meta') meta = record;
60
- else tasks.push(record);
61
- }
62
-
63
- if (!meta) return yield* Effect.fail(new MissingMetaRecord({ path }));
64
- return { meta, tasks };
65
- });
66
- }
67
-
68
- export function writeTasksJsonl(
69
- planDir: string,
70
- meta: TaskMeta,
71
- tasks: TaskRecord[],
72
- ): Effect.Effect<void, PlanWriteError, FileSystem> {
73
- return Effect.gen(function* () {
74
- const fs = yield* FileSystem;
75
- yield* fs.makeDir(planDir);
76
- const content = [meta, ...tasks].map((record) => JSON.stringify(record)).join('\n') + '\n';
77
- yield* fs.writeFileAtomic(join(planDir, TASKS_FILE), content);
78
- });
79
- }
80
-
81
- export function updateTask(
82
- planDir: string,
83
- taskId: string,
84
- updates: Partial<Omit<TaskRecord, '_type' | 'id' | 'created_at'>>,
85
- ): Effect.Effect<
86
- TaskRecord,
87
- ReadError | PlanWriteError | TasksFileNotFound | TaskNotFound,
88
- FileSystem
89
- > {
90
- // Serialize the read-modify-write so concurrent task updates to the same plan
91
- // (e.g. parallel update_task / update_tasks calls) cannot clobber each other.
92
- return withFileLock(
93
- join(planDir, TASKS_FILE),
94
- Effect.gen(function* () {
95
- const snapshot = yield* readTasksJsonl(planDir);
96
- if (!snapshot) return yield* Effect.fail(new TasksFileNotFound({ planDir }));
97
-
98
- const index = snapshot.tasks.findIndex((task) => task.id === taskId);
99
- if (index === -1) return yield* Effect.fail(new TaskNotFound({ planDir, taskId }));
100
-
101
- const updated: TaskRecord = {
102
- ...snapshot.tasks[index],
103
- ...updates,
104
- updated_at: new Date().toISOString(),
105
- };
106
- snapshot.tasks[index] = updated;
107
- yield* writeTasksJsonl(planDir, snapshot.meta, snapshot.tasks);
108
- return updated;
109
- }),
110
- );
111
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Pure helpers for reasoning about task status at execution checkpoints.
3
- *
4
- * `deferred` tasks are discovered follow-ups kept out of the active queue, so
5
- * they are excluded from "resolved" checks but block plan finalization.
6
- */
7
-
8
- import type { TaskRecord } from './types.js';
9
-
10
- export function deferredTasks(tasks: readonly TaskRecord[]): TaskRecord[] {
11
- return tasks.filter((task) => task.status === 'deferred');
12
- }
13
-
14
- /**
15
- * True when no active work remains — every task is done, skipped, or deferred
16
- * (nothing pending or blocked).
17
- */
18
- export function activeTasksResolved(tasks: readonly TaskRecord[]): boolean {
19
- return tasks.every(
20
- (task) => task.status === 'done' || task.status === 'skipped' || task.status === 'deferred',
21
- );
22
- }
23
-
24
- /**
25
- * True when the plan can be marked complete: active work is resolved AND there
26
- * are no deferred follow-ups awaiting the user's decision.
27
- */
28
- export function isPlanFinalizable(tasks: readonly TaskRecord[]): boolean {
29
- return activeTasksResolved(tasks) && !tasks.some((task) => task.status === 'deferred');
30
- }
31
-
32
- /**
33
- * Reactivate tasks for a resumed run: blocked tasks and deferred follow-ups
34
- * become pending (mutated in place). Returns true if anything changed.
35
- */
36
- export function reactivateForExecution(tasks: TaskRecord[], timestamp: string): boolean {
37
- let changed = false;
38
- for (const task of tasks) {
39
- if (task.status === 'blocked' || task.status === 'deferred') {
40
- task.status = 'pending';
41
- task.updated_at = timestamp;
42
- changed = true;
43
- }
44
- }
45
- return changed;
46
- }