@heyhuynhgiabuu/pi-task 0.1.5 → 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.
Files changed (48) hide show
  1. package/CHANGELOG.md +116 -4
  2. package/README.md +16 -11
  3. package/dist/constants.d.ts +4 -0
  4. package/dist/constants.js +4 -0
  5. package/dist/conversation.d.ts +76 -21
  6. package/dist/conversation.js +280 -70
  7. package/dist/helpers.d.ts +8 -8
  8. package/dist/helpers.js +34 -15
  9. package/dist/index.d.ts +6 -23
  10. package/dist/index.js +233 -634
  11. package/dist/lifecycle/completion.d.ts +3 -0
  12. package/dist/lifecycle/completion.js +50 -0
  13. package/dist/lifecycle/index.d.ts +5 -0
  14. package/dist/lifecycle/index.js +5 -0
  15. package/dist/lifecycle/polling.d.ts +16 -0
  16. package/dist/lifecycle/polling.js +61 -0
  17. package/dist/lifecycle/restore.d.ts +2 -0
  18. package/dist/lifecycle/restore.js +34 -0
  19. package/dist/lifecycle/toolStats.d.ts +2 -0
  20. package/dist/lifecycle/toolStats.js +17 -0
  21. package/dist/lifecycle/widget.d.ts +8 -0
  22. package/dist/lifecycle/widget.js +75 -0
  23. package/dist/session-text.d.ts +11 -2
  24. package/dist/session-text.js +78 -2
  25. package/dist/subagent/buildArgv.d.ts +1 -0
  26. package/dist/subagent/buildArgv.js +1 -1
  27. package/dist/subagent/runSdk.js +50 -26
  28. package/dist/subagent/tmux.d.ts +12 -9
  29. package/dist/subagent/tmux.js +107 -44
  30. package/dist/subagent/waitCompletion.d.ts +5 -5
  31. package/dist/subagent/waitCompletion.js +32 -41
  32. package/dist/task-widget.d.ts +21 -0
  33. package/dist/task-widget.js +122 -0
  34. package/dist/tool/index.d.ts +5 -0
  35. package/dist/tool/index.js +5 -0
  36. package/dist/tool/prompt.d.ts +8 -0
  37. package/dist/tool/prompt.js +17 -0
  38. package/dist/tool/renderCall.d.ts +3 -0
  39. package/dist/tool/renderCall.js +12 -0
  40. package/dist/tool/renderResult.d.ts +8 -0
  41. package/dist/tool/renderResult.js +51 -0
  42. package/dist/tool/schema.d.ts +8 -0
  43. package/dist/tool/schema.js +24 -0
  44. package/dist/tool/taskComplete.d.ts +8 -0
  45. package/dist/tool/taskComplete.js +65 -0
  46. package/dist/types.d.ts +54 -0
  47. package/dist/types.js +1 -0
  48. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,114 @@ All notable changes to `@heyhuynhgiabuu/pi-task` are documented here.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.2.0] — 2026-06-25
8
+
9
+ ### Changed
10
+
11
+ - **Modular refactor of `src/`.** The single-file `index.ts` is now a thin
12
+ wiring layer; the implementation is split across focused modules:
13
+ - `src/tool/` — `renderCall`, `renderResult`, `taskComplete`, `prompt`,
14
+ `schema`.
15
+ - `src/lifecycle/` — `polling`, `completion`, `toolStats`, `widget`,
16
+ `restore`.
17
+ - `src/subagent/` — `buildArgv`, `runSdk`, `tmux`, `waitCompletion`.
18
+ - `src/conversation.ts` — `findJsonlSessionByName`, registry and
19
+ `task-session-history` helpers.
20
+ - `src/constants.ts` — `BACKGROUND_CHECK_MS`, `COUNT_POLL_MS`,
21
+ `TASK_TIMEOUT_MS`, `MAX_POLL_ERRORS`.
22
+ - `src/types.ts` — `BackgroundTask`, `RegistryEntry`,
23
+ `TaskSessionHistoryEntry`, `TaskDetails`.
24
+ - **Session JSONL is now the single source of truth for task results.**
25
+ `RESULT.md` is no longer read for completion detection or result text —
26
+ the final assistant message in `~/.pi/agent/sessions/.../<id>.jsonl`
27
+ is the authoritative result. This removes mid-write `EACCES` and
28
+ "stale truncated `RESULT.md`" failure modes entirely.
29
+ - **Completion detection is gated on `stopReason`.** `hasAgentFinished()`
30
+ in `src/session-text.ts` only treats an assistant message as final when
31
+ its `stopReason` is `stop`, `endTurn`, `length`, `error`, or `aborted`.
32
+ `toolUse` mid-turn streaming text is correctly ignored.
33
+ - **Background polling is hardened.**
34
+ - `checkInFlight` guard prevents overlapping poll ticks (no more
35
+ double-completion races on the `backgroundTasks` map).
36
+ - `MAX_POLL_ERRORS = 3` per-task counter absorbs transient filesystem
37
+ errors; a single rejected `readFile` no longer orphans a task.
38
+ - Try/catch around `checkTaskCompletion()` keeps the interval alive on
39
+ one-off failures.
40
+ - **Reordered completion check flow.** Session JSONL is consulted before
41
+ pane liveness, so `remain-on-exit` panes no longer block detection.
42
+
43
+ ### Added
44
+
45
+ - `renderCall` / `renderResult` / task-complete renderers with **Ctrl+O
46
+ expand/collapse** (via `keyHint("app.tools.expand")`) on the `task`
47
+ tool. Foreground results show stats + preview; expanded shows the full
48
+ result text. The keybinding hint falls back to `Ctrl+O` if the
49
+ `app.tools.expand` keybinding is not registered.
50
+ - **Foreground real-time tool-call progress.** The foreground `execute`
51
+ path now polls the session file and emits `_onUpdate` callbacks while
52
+ waiting, so the parent pane shows a live `${n} tool calls` count
53
+ alongside the spawned subagent pane.
54
+
55
+ ### Fixed
56
+
57
+ - The "scout - Description" / "scout — Description" duplicate header in
58
+ foreground results: `renderResult` no longer re-renders the header
59
+ that `renderCall` already rendered.
60
+ - The `( to expand)` empty-keybinding hint: now falls back to a plain
61
+ `Ctrl+O to expand` label when `keyText("app.tools.expand")` is empty.
62
+
63
+ ### Verified
64
+
65
+ - `npm run typecheck` passes
66
+ - `npm run build` passes
67
+ - `npm run smoke` passes
68
+ - `npm pack --dry-run` succeeds
69
+
70
+ ## [0.1.6] — 2026-06-25
71
+
72
+ ### Changed
73
+
74
+ - Per-task data is now in flat files at the top of `.pi/artifacts/`.
75
+ No per-task subdirs, no `<task-id>` paths. The pikit canonical
76
+ files (TODO.md, PLAN.md, PROGRESS.md, DECISIONS.md) are flat at the
77
+ same level; pi-task files now sit alongside them.
78
+ - Refined the task TUI widget and background completion rendering:
79
+ foreground/background task stats now use consistent colors, background
80
+ completion summaries use a padded themed result block, completed
81
+ background widgets no longer duplicate the main-pane completion, and
82
+ final tool-call counts now match the live widget count.
83
+
84
+ ### Layout
85
+
86
+ - `.pi/artifacts/TASKS.md` — one `### <task-id>` block per task, with
87
+ H4 subsections for `#### Metadata` (JSON) and `#### Result`.
88
+ - `.pi/artifacts/task-sessions.json` — registry mapping
89
+ `conversation_id` to `{ task_id, session_file }`. Renamed from
90
+ the v0.1.5 `task-conversations.json`.
91
+ - The subagent's session is auto-saved by pi at
92
+ `~/.pi/agent/sessions/<cwd>/<session-id>.jsonl`. pi-task reads
93
+ the last assistant message from there to populate `#### Result`
94
+ in `TASKS.md`. The subagent's final assistant message IS the
95
+ result; no separate result file is required.
96
+
97
+ ### Removed
98
+
99
+ - `.pi/artifacts/task-<id>/` per-task subdirs (and the
100
+ `metadata.json` + `SESSION.md` + `sessions/` files inside them).
101
+ All per-task data lives in `TASKS.md` blocks now.
102
+ - `.pi/artifacts/task-conversations.json` — replaced by
103
+ `task-sessions.json`.
104
+ - The `taskArtifactName(taskId)` / `taskIdFromArtifactName(name)`
105
+ helpers and the `getArtifactsDir(piDir)` / `getTaskDir(piDir)` /
106
+ `getTaskRunsDir(piDir)` helpers.
107
+
108
+ ### Verified
109
+
110
+ - `npm test` passes
111
+ - `npm run typecheck` passes
112
+ - `npm run build` passes
113
+ - `npm run smoke` passes
114
+
7
115
  ## [0.1.4] — 2026-06-21
8
116
 
9
117
  ### Fixed
@@ -76,7 +184,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
76
184
  - `npm view @heyhuynhgiabuu/pi-task@0.1.2 pi` returns
77
185
  `{ extensions: [ './dist/index.js' ] }`
78
186
 
79
- [0.1.2]: https://github.com/buddingnewinsights/pi-task/releases/tag/v0.1.2
187
+ [0.1.2]: https://github.com/heyhuynhgiabuu/pi-task/releases/tag/v0.1.2
80
188
 
81
189
  ## [0.1.1] — 2025
82
190
 
@@ -116,6 +224,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
116
224
 
117
225
  See the git history: `git log --oneline -- CHANGELOG.md`.
118
226
 
119
- [0.1.1]: https://github.com/buddingnewinsights/pi-task/releases/tag/v0.1.1
120
- [Keep a Changelog]: https://keepachangelog.com/
121
- [Semantic Versioning]: https://semver.org/
227
+ [0.1.1]: https://github.com/heyhuynhgiabuu/pi-task/releases/tag/v0.1.1
228
+ [0.1.4]: https://github.com/heyhuynhgiabuu/pi-task/releases/tag/v0.1.4
229
+ [0.1.5]: https://github.com/heyhuynhgiabuu/pi-task/releases/tag/v0.1.5
230
+ [0.2.0]: https://github.com/heyhuynhgiabuu/pi-task/releases/tag/v0.2.0
231
+ [0.1.6]: https://github.com/heyhuynhgiabuu/pi-task/releases/tag/v0.1.6
232
+ [Keep a Changelog]: https://keepachangelog.com/
233
+ [Semantic Versioning]: https://semver.org/
package/README.md CHANGED
@@ -66,20 +66,25 @@ Durable specialist conversation:
66
66
  }
67
67
  ```
68
68
 
69
- `conversation_id` maps to one existing `task-<id>` artifact under `.pi/artifacts/` and reuses its `sessions/` directory on later calls. This is for scoped specialist memory, e.g. a reusable research assistant. Use `/task-sessions` to list known durable conversations.
69
+ `conversation_id` maps to a durable subagent run. Reused across calls
70
+ to keep specialist memory, e.g. a reusable research assistant.
71
+ Use `/task-sessions` to list known durable conversations.
70
72
 
71
- Stored files:
73
+ Stored files (all flat at the top of `.pi/artifacts/`, no
74
+ per-task subdirs):
72
75
 
73
- ```
74
- .pi/artifacts/task-registry.json
75
- .pi/artifacts/task-<id>/CONTEXT.md
76
- .pi/artifacts/task-<id>/RESULT.md
77
- .pi/artifacts/task-<id>/SESSION.md
78
- .pi/artifacts/task-<id>/metadata.json
79
- .pi/artifacts/task-<id>/sessions/
80
- ```
76
+ ```
77
+ .pi/artifacts/TASKS.md # one ### <task-id> block per task
78
+ .pi/artifacts/task-sessions.json # conversation_id -> { task_id, session_file }
79
+ ```
80
+
81
+ The subagent's session is auto-saved by pi at
82
+ `~/.pi/agent/sessions/<cwd>/<session-id>.jsonl`. pi-task reads
83
+ the last assistant message from there to populate
84
+ `#### Result` in `TASKS.md`. The subagent's final message IS
85
+ the result; no separate result file is required.
81
86
 
82
- Note: true conversation resume requires the tmux/CLI backend so Pi can reopen the saved subagent session. SDK fallback can run one-shot tasks, but it cannot resume a prior Pi session.
87
+ Note: true conversation resume requires the tmux/CLI backend so Pi can reopen the saved subagent session. SDK fallback can run one-shot tasks, but it cannot resume a prior Pi session.
83
88
 
84
89
  ## Agent precedence
85
90
 
@@ -0,0 +1,4 @@
1
+ export declare const BACKGROUND_CHECK_MS = 10000;
2
+ export declare const COUNT_POLL_MS = 3000;
3
+ export declare const TASK_TIMEOUT_MS: number;
4
+ export declare const MAX_POLL_ERRORS = 3;
@@ -0,0 +1,4 @@
1
+ export const BACKGROUND_CHECK_MS = 10_000; // poll every 10 sec
2
+ export const COUNT_POLL_MS = 3_000; // update toolcall counts every 3 sec
3
+ export const TASK_TIMEOUT_MS = 30 * 60 * 1_000; // 30 minutes
4
+ export const MAX_POLL_ERRORS = 3; // consecutive poll failures before giving up on a task
@@ -1,39 +1,94 @@
1
+ import type { RegistryEntry, TaskSessionHistoryEntry } from "./types.js";
1
2
  /**
2
3
  * Conversational subagent helpers.
3
4
  *
4
- * Durable subagent conversations reuse the existing
5
- * `.pi/artifacts/task-<id>/` artifact convention and add a small
6
- * `conversation_id` -> `task-<id>` registry under the same artifacts dir.
5
+ * Per-task data lives in `.pi/artifacts/TASKS.md` as `### <task-id>` blocks.
6
+ * A small `task-sessions.json` registry in the same directory maps
7
+ * `conversation_id` to the auto-saved session file path so the
8
+ * subagent can be resumed later.
9
+ *
10
+ * The subagent's session is auto-saved by pi at
11
+ * `~/.pi/agent/sessions/<cwd>/<session-id>.jsonl`. pi-task does not
12
+ * maintain its own session storage.
13
+ *
14
+ * All artifacts live flat at the top of `.pi/artifacts/`, alongside the
15
+ * pikit canonical files (TODO.md, PLAN.md, PROGRESS.md, DECISIONS.md).
16
+ * No subdirs. No per-task paths.
7
17
  */
18
+ export declare const TASKS_FILE = "TASKS.md";
19
+ export declare const TASK_SESSIONS_REGISTRY_FILE = "task-sessions.json";
8
20
  export interface ConversationMetadata {
9
21
  conversation_id: string;
10
22
  task_id: string;
11
- artifact: string;
12
23
  agent_type: string;
13
- session_dir: string;
14
- session_name: string;
24
+ session_file: string;
15
25
  created_at: string;
16
26
  last_used_at: string;
17
27
  last_prompt?: string;
18
28
  }
19
- export type ConversationRegistry = Record<string, string>;
20
- export declare const CONVERSATION_REGISTRY_FILE = "task-conversations.json";
21
- export declare function getArtifactsDir(piDir: string): string;
22
- export declare function getConversationRegistryPath(piDir: string): string;
23
- export declare function taskArtifactName(taskId: string): string;
24
- export declare function taskIdFromArtifactName(artifactName: string): string;
29
+ export type TaskSessionsRegistry = Record<string, {
30
+ task_id: string;
31
+ session_file: string;
32
+ }>;
33
+ export declare function getTasksFilePath(piDir: string): string;
34
+ export declare function getTaskSessionsRegistryPath(piDir: string): string;
35
+ export declare function readRegistry(piDir: string): RegistryEntry[];
36
+ export declare function writeRegistry(piDir: string, entries: RegistryEntry[]): void;
37
+ export declare function readTaskSessionHistory(piDir: string): TaskSessionHistoryEntry[];
38
+ export declare function writeTaskSessionHistory(piDir: string, entries: TaskSessionHistoryEntry[]): void;
39
+ export declare function upsertTaskSessionHistory(piDir: string, entry: TaskSessionHistoryEntry): void;
40
+ export declare function findTaskSessionHistory(piDir: string, idOrSessionName: string): TaskSessionHistoryEntry | undefined;
41
+ export declare function findJsonlSessionByName(piDir: string, sessionName: string, agentType: string): TaskSessionHistoryEntry | undefined;
25
42
  export declare function normalizeConversationId(value: unknown): string | undefined;
26
- export declare function readConversationRegistry(piDir: string): ConversationRegistry;
27
- export declare function writeConversationRegistry(piDir: string, registry: ConversationRegistry): void;
28
- export declare function readConversationMetadata(metadataPath: string): ConversationMetadata | undefined;
29
- export declare function buildSessionCard(metadata: ConversationMetadata): string;
30
- export declare function writeConversationArtifacts(options: {
31
- taskDir: string;
43
+ export declare function readTaskSessionsRegistry(piDir: string): TaskSessionsRegistry;
44
+ export declare function writeTaskSessionsRegistry(piDir: string, registry: TaskSessionsRegistry): void;
45
+ /**
46
+ * Find a `### <task-id>` block in TASKS.md. Returns the block content
47
+ * (everything between the heading and the next H3 or EOF) plus the
48
+ * status line if present. Returns undefined if no block exists.
49
+ */
50
+ export declare function readTaskBlock(piDir: string, taskId: string): {
51
+ status: string | null;
52
+ body: string;
53
+ } | undefined;
54
+ export declare function listTaskBlocks(piDir: string): Map<string, {
55
+ status: string | null;
56
+ body: string;
57
+ }>;
58
+ /**
59
+ * Append or update a `### <task-id>` block in TASKS.md. If the block
60
+ * already exists, its body is replaced. Otherwise, the block is
61
+ * appended at the end of the file.
62
+ */
63
+ export declare function writeTaskBlock(options: {
64
+ piDir: string;
65
+ taskId: string;
66
+ status: "active" | "done" | "abandoned";
67
+ updated: string;
68
+ body: string;
69
+ }): void;
70
+ export declare function parseMetadataFromBody(body: string | undefined): {
71
+ created_at?: string;
72
+ last_used_at?: string;
73
+ agent_type?: string;
74
+ session_file?: string;
75
+ conversation_id?: string;
76
+ last_prompt?: string;
77
+ } | undefined;
78
+ export interface WriteTaskBlockInput {
79
+ piDir: string;
32
80
  taskId: string;
33
81
  conversationId: string;
34
82
  agentType: string;
35
- sessionDir: string;
36
- sessionName: string;
83
+ sessionFile: string;
37
84
  prompt: string;
38
- }): ConversationMetadata;
85
+ result: string;
86
+ resultLabel?: string;
87
+ }
88
+ /**
89
+ * Persist a completed task: write (or update) the `### <task-id>` block
90
+ * in TASKS.md with metadata and result as H4 subsections. Also updates
91
+ * the task-sessions registry.
92
+ */
93
+ export declare function writeConversationArtifacts(input: WriteTaskBlockInput): ConversationMetadata;
39
94
  export declare function renderConversationSessions(piDir: string): string;