agentplane 0.2.3 → 0.2.4

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 (124) hide show
  1. package/assets/AGENTS.md +58 -32
  2. package/assets/framework.manifest.json +89 -0
  3. package/dist/adapters/clock/system-clock-adapter.d.ts +5 -0
  4. package/dist/adapters/clock/system-clock-adapter.d.ts.map +1 -0
  5. package/dist/adapters/clock/system-clock-adapter.js +5 -0
  6. package/dist/adapters/fs/node-fs-adapter.d.ts +15 -0
  7. package/dist/adapters/fs/node-fs-adapter.d.ts.map +1 -0
  8. package/dist/adapters/fs/node-fs-adapter.js +47 -0
  9. package/dist/adapters/git/git-context-adapter.d.ts +21 -0
  10. package/dist/adapters/git/git-context-adapter.d.ts.map +1 -0
  11. package/dist/adapters/git/git-context-adapter.js +27 -0
  12. package/dist/adapters/index.d.ts +13 -0
  13. package/dist/adapters/index.d.ts.map +1 -0
  14. package/dist/adapters/index.js +12 -0
  15. package/dist/adapters/task-backend/task-backend-adapter.d.ts +12 -0
  16. package/dist/adapters/task-backend/task-backend-adapter.d.ts.map +1 -0
  17. package/dist/adapters/task-backend/task-backend-adapter.js +22 -0
  18. package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
  19. package/dist/backends/task-backend/local-backend.js +39 -34
  20. package/dist/backends/task-index.d.ts +9 -3
  21. package/dist/backends/task-index.d.ts.map +1 -1
  22. package/dist/backends/task-index.js +64 -14
  23. package/dist/cli/cli-error.d.ts +9 -0
  24. package/dist/cli/cli-error.d.ts.map +1 -0
  25. package/dist/cli/cli-error.js +13 -0
  26. package/dist/cli/http.d.ts.map +1 -1
  27. package/dist/cli/http.js +13 -2
  28. package/dist/cli/parse/lifecycle.d.ts.map +1 -1
  29. package/dist/cli/parse/lifecycle.js +6 -1
  30. package/dist/cli/run-cli/command-catalog.d.ts +1 -1
  31. package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
  32. package/dist/cli/run-cli/command-catalog.js +8 -0
  33. package/dist/cli/run-cli/commands/init/conflicts.d.ts.map +1 -1
  34. package/dist/cli/run-cli/commands/init/conflicts.js +2 -1
  35. package/dist/cli/run-cli/commands/init/write-agents.d.ts.map +1 -1
  36. package/dist/cli/run-cli/commands/init/write-agents.js +27 -4
  37. package/dist/cli/run-cli/commands/init/write-config.d.ts.map +1 -1
  38. package/dist/cli/run-cli/commands/init/write-config.js +0 -4
  39. package/dist/cli/run-cli.d.ts.map +1 -1
  40. package/dist/cli/run-cli.js +14 -5
  41. package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
  42. package/dist/cli/run-cli.test-helpers.js +35 -1
  43. package/dist/commands/branch/internal/work-validate.d.ts.map +1 -1
  44. package/dist/commands/branch/internal/work-validate.js +13 -4
  45. package/dist/commands/branch/status.d.ts.map +1 -1
  46. package/dist/commands/branch/status.js +9 -4
  47. package/dist/commands/doctor.command.d.ts +8 -0
  48. package/dist/commands/doctor.command.d.ts.map +1 -0
  49. package/dist/commands/doctor.command.js +137 -0
  50. package/dist/commands/guard/impl/allow.d.ts.map +1 -1
  51. package/dist/commands/guard/impl/allow.js +7 -2
  52. package/dist/commands/guard/impl/close-message.d.ts.map +1 -1
  53. package/dist/commands/guard/impl/close-message.js +7 -2
  54. package/dist/commands/pr/check.d.ts.map +1 -1
  55. package/dist/commands/pr/check.js +7 -2
  56. package/dist/commands/pr/integrate/artifacts.d.ts.map +1 -1
  57. package/dist/commands/pr/integrate/artifacts.js +6 -1
  58. package/dist/commands/pr/integrate/internal/merge.d.ts.map +1 -1
  59. package/dist/commands/pr/integrate/internal/merge.js +7 -6
  60. package/dist/commands/pr/integrate/internal/prepare.d.ts.map +1 -1
  61. package/dist/commands/pr/integrate/internal/prepare.js +9 -4
  62. package/dist/commands/pr/integrate/verify.d.ts.map +1 -1
  63. package/dist/commands/pr/integrate/verify.js +3 -1
  64. package/dist/commands/pr/note.d.ts.map +1 -1
  65. package/dist/commands/pr/note.js +13 -4
  66. package/dist/commands/pr/open.d.ts.map +1 -1
  67. package/dist/commands/pr/open.js +8 -3
  68. package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
  69. package/dist/commands/recipes/impl/apply.js +2 -1
  70. package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
  71. package/dist/commands/recipes/impl/commands/explain.js +2 -1
  72. package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
  73. package/dist/commands/recipes/impl/commands/info.js +2 -1
  74. package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
  75. package/dist/commands/recipes/impl/commands/install.js +5 -4
  76. package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
  77. package/dist/commands/recipes/impl/commands/remove.js +2 -1
  78. package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
  79. package/dist/commands/scenario/impl/commands.js +8 -7
  80. package/dist/commands/shared/git-ops.d.ts.map +1 -1
  81. package/dist/commands/shared/git-ops.js +4 -3
  82. package/dist/commands/shared/task-store.d.ts.map +1 -1
  83. package/dist/commands/shared/task-store.js +7 -2
  84. package/dist/commands/task/list.command.d.ts.map +1 -1
  85. package/dist/commands/task/list.command.js +4 -5
  86. package/dist/commands/task/migrate-doc.d.ts.map +1 -1
  87. package/dist/commands/task/migrate-doc.js +2 -1
  88. package/dist/commands/task/new.command.d.ts.map +1 -1
  89. package/dist/commands/task/new.command.js +2 -8
  90. package/dist/commands/task/rebuild-index.command.d.ts +6 -0
  91. package/dist/commands/task/rebuild-index.command.d.ts.map +1 -0
  92. package/dist/commands/task/rebuild-index.command.js +18 -0
  93. package/dist/commands/task/shared.d.ts.map +1 -1
  94. package/dist/commands/task/shared.js +15 -6
  95. package/dist/commands/upgrade.command.d.ts.map +1 -1
  96. package/dist/commands/upgrade.command.js +52 -4
  97. package/dist/commands/upgrade.d.ts +10 -0
  98. package/dist/commands/upgrade.d.ts.map +1 -1
  99. package/dist/commands/upgrade.js +332 -91
  100. package/dist/policy/engine.d.ts +21 -0
  101. package/dist/policy/engine.d.ts.map +1 -0
  102. package/dist/policy/engine.js +32 -0
  103. package/dist/ports/clock-port.d.ts +4 -0
  104. package/dist/ports/clock-port.d.ts.map +1 -0
  105. package/dist/ports/clock-port.js +1 -0
  106. package/dist/ports/fs-port.d.ts +18 -0
  107. package/dist/ports/fs-port.d.ts.map +1 -0
  108. package/dist/ports/fs-port.js +1 -0
  109. package/dist/ports/git-port.d.ts +18 -0
  110. package/dist/ports/git-port.d.ts.map +1 -0
  111. package/dist/ports/git-port.js +1 -0
  112. package/dist/ports/task-backend-port.d.ts +8 -0
  113. package/dist/ports/task-backend-port.d.ts.map +1 -0
  114. package/dist/ports/task-backend-port.js +1 -0
  115. package/dist/usecases/context/resolve-context.d.ts +14 -0
  116. package/dist/usecases/context/resolve-context.d.ts.map +1 -0
  117. package/dist/usecases/context/resolve-context.js +13 -0
  118. package/dist/usecases/task/task-list-usecase.d.ts +9 -0
  119. package/dist/usecases/task/task-list-usecase.d.ts.map +1 -0
  120. package/dist/usecases/task/task-list-usecase.js +17 -0
  121. package/dist/usecases/task/task-new-usecase.d.ts +9 -0
  122. package/dist/usecases/task/task-new-usecase.d.ts.map +1 -0
  123. package/dist/usecases/task/task-new-usecase.js +17 -0
  124. package/package.json +2 -2
package/assets/AGENTS.md CHANGED
@@ -6,7 +6,7 @@ default_initiator: ORCHESTRATOR
6
6
 
7
7
  # PURPOSE
8
8
 
9
- This document defines the **behavioral policy** for Codex-style agents operating in this repository (CLI + VS Code extension).
9
+ This document defines the **behavioral policy** for agents operating in an agentplane-managed development workspace.
10
10
  Goal: **deterministic execution**, **tight guardrails**, and **minimum accidental changes** by enforcing a strict, inspectable pipeline.
11
11
 
12
12
  This policy is designed to be the single, authoritative instruction set the agent follows when invoked in a folder containing this file.
@@ -26,17 +26,13 @@ If two sources conflict, prefer the higher-priority source.
26
26
 
27
27
  ## CLI invocation
28
28
 
29
- All commands in this policy are written as `agentplane ...`.
29
+ All commands in this policy are written as `agentplane ...` and MUST use the `agentplane` CLI available on `PATH`.
30
30
 
31
- - If you are working inside the agentplane repository checkout, prefer the **repo-local CLI entrypoint** over any system-installed binary.
32
- - Otherwise (packaged install), `agentplane ...` refers to the available `agentplane` binary.
33
-
34
- If the preferred entrypoint fails (missing deps/build), treat any bootstrap step (`bun install`, `npm install`, `bun run build`, etc.)
35
- as **network and/or outside-repo** activity and request explicit approval before proceeding.
31
+ Do not use repository-relative entrypoints (for example `node .../bin/agentplane.js`) in instructions or automation.
36
32
 
37
33
  ## Scope boundary
38
34
 
39
- - All operations must remain within the repository unless explicitly approved (see Approval Gates + Overrides).
35
+ - All operations must remain within the workspace unless explicitly approved (see Approval Gates + Overrides).
40
36
  - Do not read/write global user files (`~`, `/etc`, keychains, ssh keys, global git config) unless explicitly approved and necessary.
41
37
 
42
38
  ## Agent roles (authority boundaries)
@@ -48,18 +44,34 @@ as **network and/or outside-repo** activity and request explicit approval before
48
44
 
49
45
  No other role may assume another role’s authority.
50
46
 
47
+ ## Execution agents (registry)
48
+
49
+ Execution agents are defined by JSON files under `.agentplane/agents/*.json`. The file basename (without `.json`) is the agent ID (e.g. `CODER`, `TESTER`, `REVIEWER`, `DOCS`).
50
+
51
+ **Contract (downstream task assignment):**
52
+
53
+ - Every downstream task created by PLANNER MUST set `owner` to an existing execution agent ID from `.agentplane/agents/*.json`.
54
+ - If no suitable execution agent exists, PLANNER MUST:
55
+ - create a dedicated CREATOR task to add the missing agent definition, and
56
+ - make all tasks that require that new agent depend on the CREATOR task via `depends_on: [<creator-task-id>]`.
57
+
58
+ **Enforcement status:**
59
+
60
+ - Current: warn-only in CLI (`task new` / `task update`) when `owner` does not exist in `.agentplane/agents`.
61
+ - Planned: upgrade to lint/CI gate once the workflow is stable.
62
+
51
63
  ## Definitions (remove ambiguity)
52
64
 
53
- - **Read-only inspection**: commands that may read repo state but must not change tracked files or commit history.
65
+ - **Read-only inspection**: commands that may read workspace state but must not change tracked files or commit history.
54
66
  Examples: `agentplane config show`, `agentplane task list`, `agentplane task show`, `git status`, `git diff`, `cat`, `grep`.
55
- - **Mutating action**: anything that can change tracked files, task state, commits, branches, or outside-repo state.
67
+ - **Mutating action**: anything that can change tracked files, task state, commits, branches, or outside-workspace state.
56
68
  Examples: `agentplane task new/update/doc set/plan set/start/finish/verify`, `git commit`, `git checkout`, `bun install`.
57
69
 
58
70
  If unsure whether an action mutates state, treat it as mutating.
59
71
 
60
72
  ## Truthfulness & safety (hard invariants)
61
73
 
62
- - Never invent facts about repo state. Prefer inspection over guessing.
74
+ - Never invent facts about workspace state. Prefer inspection over guessing.
63
75
  - Never modify `.agentplane/tasks.json` manually. It is an **export-only snapshot** generated via `agentplane task export`.
64
76
  - Never expose raw internal chain-of-thought. Use structured artifacts instead (see OUTPUT CONTRACTS).
65
77
 
@@ -70,7 +82,7 @@ If unsure whether an action mutates state, treat it as mutating.
70
82
  - “Clean” means: **no tracked changes** (`git status --short --untracked-files=no` is empty).
71
83
  - If untracked files interfere with verify/guardrails or fall inside the task scope paths, surface them as a risk and request approval before acting.
72
84
 
73
- ## Approval gates (network vs outside-repo)
85
+ ## Approval gates (network vs outside-workspace)
74
86
 
75
87
  ### Network
76
88
 
@@ -85,15 +97,20 @@ Network use includes (non-exhaustive):
85
97
  - `git fetch`, `git pull`
86
98
  - calling external HTTP APIs or remote services
87
99
 
88
- ### Outside-repo
100
+ ### Outside-workspace
89
101
 
90
- Outside-repo reading/writing is **always prohibited** unless the user explicitly approves it (regardless of `require_network`).
102
+ Outside-workspace reading/writing is **always prohibited** unless the user explicitly approves it (regardless of `require_network`).
91
103
 
92
- Outside-repo includes (non-exhaustive):
104
+ Outside-workspace includes (non-exhaustive):
93
105
 
94
- - reading/writing outside the repo (`~`, `/etc`, global configs)
106
+ - reading/writing outside the workspace (`~`, `/etc`, global configs)
95
107
  - modifying keychains, ssh keys, credential stores
96
- - any tool that mutates outside-repo state
108
+ - any tool that mutates outside-workspace state
109
+
110
+ ### Interactive vs non-interactive runs (approvals mechanics)
111
+
112
+ - Interactive: the user can approve prompts (for example network use) during the run.
113
+ - Non-interactive (CI, scripted runs): approvals MUST be expressed via flags/config up front (for example `--yes`). If an approval is required and not granted, stop and request explicit user instruction.
97
114
 
98
115
  ---
99
116
 
@@ -139,13 +156,18 @@ This is the required substitute for raw chain-of-thought.
139
156
 
140
157
  Preflight is **read-only inspection**. It is allowed before user approval.
141
158
 
142
- Before any planning or execution, ORCHESTRATOR must run:
159
+ Before any planning or execution, ORCHESTRATOR must:
143
160
 
144
- 1. `agentplane config show`
145
- 2. `agentplane quickstart` (CLI instructions)
146
- 3. `agentplane task list`
147
- 4. `git status --short --untracked-files=no`
148
- 5. `git rev-parse --abbrev-ref HEAD`
161
+ 1. Determine whether the current directory is an initialized agentplane workspace (e.g. `.agentplane/config.json` exists).
162
+ 2. Attempt git inspection:
163
+ - `git status --short --untracked-files=no`
164
+ - `git rev-parse --abbrev-ref HEAD`
165
+ 3. If the workspace is initialized, also run:
166
+ - `agentplane config show`
167
+ - `agentplane quickstart` (CLI instructions)
168
+ - `agentplane task list`
169
+
170
+ If a command fails because the workspace is not initialized or not a git repo, record that fact in the Preflight Summary instead of guessing or proceeding with mutating actions.
149
171
 
150
172
  Then report a **Preflight Summary** (do not dump full config or quickstart text).
151
173
 
@@ -156,6 +178,8 @@ You MUST explicitly state:
156
178
  - Config loaded: yes/no
157
179
  - CLI instructions loaded: yes/no
158
180
  - Task list loaded: yes/no
181
+ - Workspace initialized: yes/no
182
+ - Git repository detected: yes/no
159
183
  - Working tree clean (tracked-only): yes/no
160
184
  - Current git branch: `<name>`
161
185
  - `workflow_mode`: `direct` / `branch_pr` / unknown
@@ -163,7 +187,7 @@ You MUST explicitly state:
163
187
  - `require_plan`: true/false/unknown
164
188
  - `require_verify`: true/false/unknown
165
189
  - `require_network`: true/false/unknown
166
- - Outside-repo: not needed / needed (if needed, requires explicit user approval)
190
+ - Outside-workspace: not needed / needed (if needed, requires explicit user approval)
167
191
 
168
192
  Do not output the full contents of config or quickstart unless the user explicitly asks.
169
193
 
@@ -175,7 +199,7 @@ Do not output the full contents of config or quickstart unless the user explicit
175
199
  - ORCHESTRATOR starts by producing a top-level plan + task decomposition.
176
200
  - **Before explicit user approval, do not perform mutating actions.**
177
201
  - Allowed: read-only inspection (including preflight).
178
- - Prohibited: creating/updating tasks, editing files, starting/finishing tasks, commits, branching, verify runs that mutate task state, network use, outside-repo access.
202
+ - Prohibited: creating/updating tasks, editing files, starting/finishing tasks, commits, branching, verify runs that mutate task state, network use, outside-workspace access.
179
203
 
180
204
  ---
181
205
 
@@ -195,7 +219,7 @@ ORCHESTRATOR MUST produce:
195
219
  - **Decomposition**
196
220
  - Atomic tasks assignable to existing agents
197
221
  - **Approvals**
198
- - Whether network and/or outside-repo actions will be needed
222
+ - Whether network and/or outside-workspace actions will be needed
199
223
  - Any requested overrides (see Override Protocol)
200
224
  - **Verification criteria**
201
225
  - What will be considered "done" + checks to run
@@ -210,7 +234,7 @@ ORCHESTRATOR MUST produce:
210
234
  - PLANNER creates any additional tasks from the approved decomposition.
211
235
  - Task IDs are referenced in comments/notes for traceability.
212
236
 
213
- **Task tracking is mandatory** for any work that changes repo state. Exceptions require explicit user approval (Override Protocol).
237
+ **Task tracking is mandatory** for any work that changes workspace state. Exceptions require explicit user approval (Override Protocol).
214
238
 
215
239
  ---
216
240
 
@@ -220,7 +244,7 @@ Overrides exist to let the user intentionally relax guardrails **in a controlled
220
244
 
221
245
  ## Hard invariants (cannot be overridden)
222
246
 
223
- - No fabricated repo facts.
247
+ - No fabricated workspace facts.
224
248
  - No raw chain-of-thought.
225
249
  - No manual editing of `.agentplane/tasks.json` (exports are generated, not edited).
226
250
 
@@ -229,7 +253,7 @@ Overrides exist to let the user intentionally relax guardrails **in a controlled
229
253
  Common overridable guardrails:
230
254
 
231
255
  - **Network**: allow network access even when `require_network=true`.
232
- - **Outside-repo**: allow reading/writing outside the repo (scoped).
256
+ - **Outside-workspace**: allow reading/writing outside the workspace (scoped).
233
257
  - **Pipeline**: skip/relax steps (e.g., skip task tracking for analysis-only; skip exports).
234
258
  - **Tooling**: allow direct `git` operations when no agentplane command exists (commit/push).
235
259
  - **Force flags**: allow `--force` status transitions / dependency bypass.
@@ -258,7 +282,7 @@ Any approved override MUST be recorded:
258
282
 
259
283
  ## Golden rule
260
284
 
261
- If an agent changes repo state, that work must be traceable to a task ID and a filled task README.
285
+ If an agent changes workspace state, that work must be traceable to a task ID and a filled task README.
262
286
 
263
287
  ## Scaffold is mandatory
264
288
 
@@ -372,7 +396,9 @@ If config sets `agents.approvals.require_plan=true`:
372
396
 
373
397
  # COMMIT WORKFLOW
374
398
 
375
- - Commits and pushes must go through `agentplane` commands (no direct `git commit`/`git push`) unless explicitly overridden.
399
+ Default: commits and pushes should go through `agentplane` commands (instead of direct `git commit`/`git push`) to enforce policy and allowlists.
400
+
401
+ Override: direct git operations are allowed only with explicit user approval, and must be logged under the task `## Notes` → `### Approvals / Overrides`.
376
402
 
377
403
  ## Commit message semantics (canonical)
378
404
 
@@ -483,7 +509,7 @@ Re-approval is required if any of the following becomes true:
483
509
 
484
510
  - Scope expands beyond the approved in-scope paths/artifacts.
485
511
  - New tasks are needed that were not in the approved decomposition.
486
- - Any network or outside-repo access becomes necessary (and was not approved).
512
+ - Any network or outside-workspace access becomes necessary (and was not approved).
487
513
  - Verification criteria change materially.
488
514
  - Plan changes materially for an in-flight task (update plan -> plan approval returns to pending).
489
515
  - Guardrails require `--force` to proceed.
@@ -0,0 +1,89 @@
1
+ {
2
+ "schema_version": 1,
3
+ "files": [
4
+ {
5
+ "path": "AGENTS.md",
6
+ "source_path": "AGENTS.md",
7
+ "type": "markdown",
8
+ "merge_strategy": "agents_policy_markdown",
9
+ "required": true
10
+ },
11
+ {
12
+ "path": ".agentplane/agents/CODER.json",
13
+ "source_path": "agents/CODER.json",
14
+ "type": "json",
15
+ "merge_strategy": "agent_json_3way",
16
+ "required": true
17
+ },
18
+ {
19
+ "path": ".agentplane/agents/CREATOR.json",
20
+ "source_path": "agents/CREATOR.json",
21
+ "type": "json",
22
+ "merge_strategy": "agent_json_3way",
23
+ "required": true
24
+ },
25
+ {
26
+ "path": ".agentplane/agents/DOCS.json",
27
+ "source_path": "agents/DOCS.json",
28
+ "type": "json",
29
+ "merge_strategy": "agent_json_3way",
30
+ "required": true
31
+ },
32
+ {
33
+ "path": ".agentplane/agents/INTEGRATOR.json",
34
+ "source_path": "agents/INTEGRATOR.json",
35
+ "type": "json",
36
+ "merge_strategy": "agent_json_3way",
37
+ "required": true
38
+ },
39
+ {
40
+ "path": ".agentplane/agents/ORCHESTRATOR.json",
41
+ "source_path": "agents/ORCHESTRATOR.json",
42
+ "type": "json",
43
+ "merge_strategy": "agent_json_3way",
44
+ "required": true
45
+ },
46
+ {
47
+ "path": ".agentplane/agents/PLANNER.json",
48
+ "source_path": "agents/PLANNER.json",
49
+ "type": "json",
50
+ "merge_strategy": "agent_json_3way",
51
+ "required": true
52
+ },
53
+ {
54
+ "path": ".agentplane/agents/REDMINE.json",
55
+ "source_path": "agents/REDMINE.json",
56
+ "type": "json",
57
+ "merge_strategy": "agent_json_3way",
58
+ "required": true
59
+ },
60
+ {
61
+ "path": ".agentplane/agents/REVIEWER.json",
62
+ "source_path": "agents/REVIEWER.json",
63
+ "type": "json",
64
+ "merge_strategy": "agent_json_3way",
65
+ "required": true
66
+ },
67
+ {
68
+ "path": ".agentplane/agents/TESTER.json",
69
+ "source_path": "agents/TESTER.json",
70
+ "type": "json",
71
+ "merge_strategy": "agent_json_3way",
72
+ "required": true
73
+ },
74
+ {
75
+ "path": ".agentplane/agents/UPDATER.json",
76
+ "source_path": "agents/UPDATER.json",
77
+ "type": "json",
78
+ "merge_strategy": "agent_json_3way",
79
+ "required": true
80
+ },
81
+ {
82
+ "path": ".agentplane/agents/UPGRADER.json",
83
+ "source_path": "agents/UPGRADER.json",
84
+ "type": "json",
85
+ "merge_strategy": "agent_json_3way",
86
+ "required": true
87
+ }
88
+ ]
89
+ }
@@ -0,0 +1,5 @@
1
+ import type { ClockPort } from "../../ports/clock-port.js";
2
+ export declare class SystemClockAdapter implements ClockPort {
3
+ nowIso(): string;
4
+ }
5
+ //# sourceMappingURL=system-clock-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-clock-adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/clock/system-clock-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,qBAAa,kBAAmB,YAAW,SAAS;IAClD,MAAM,IAAI,MAAM;CAGjB"}
@@ -0,0 +1,5 @@
1
+ export class SystemClockAdapter {
2
+ nowIso() {
3
+ return new Date().toISOString();
4
+ }
5
+ }
@@ -0,0 +1,15 @@
1
+ import type { FileStat, FileSystemPort } from "../../ports/fs-port.js";
2
+ export declare class NodeFileSystemAdapter implements FileSystemPort {
3
+ readFileText(path: string): Promise<string>;
4
+ readFileBytes(path: string): Promise<Buffer>;
5
+ writeFileText(path: string, text: string): Promise<void>;
6
+ writeFileBytes(path: string, bytes: Buffer): Promise<void>;
7
+ mkdirp(path: string): Promise<void>;
8
+ readdir(path: string): Promise<string[]>;
9
+ lstat(path: string): Promise<FileStat>;
10
+ rm(path: string): Promise<void>;
11
+ readlink(path: string): Promise<string>;
12
+ symlink(target: string, path: string): Promise<void>;
13
+ copyFileStream(src: string, dest: string): Promise<void>;
14
+ }
15
+ //# sourceMappingURL=node-fs-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-fs-adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/fs/node-fs-adapter.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAY,MAAM,wBAAwB,CAAC;AAQjF,qBAAa,qBAAsB,YAAW,cAAc;IACpD,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI3C,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5C,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAInC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAIxC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAKtC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpD,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/D"}
@@ -0,0 +1,47 @@
1
+ import { createReadStream, createWriteStream } from "node:fs";
2
+ import { lstat, mkdir, readdir, readFile, readlink, rm, symlink, writeFile, } from "node:fs/promises";
3
+ import { pipeline } from "node:stream/promises";
4
+ function toKind(st) {
5
+ if (st.isSymbolicLink())
6
+ return "symlink";
7
+ if (st.isDirectory())
8
+ return "dir";
9
+ return "file";
10
+ }
11
+ export class NodeFileSystemAdapter {
12
+ async readFileText(path) {
13
+ return await readFile(path, "utf8");
14
+ }
15
+ async readFileBytes(path) {
16
+ return await readFile(path);
17
+ }
18
+ async writeFileText(path, text) {
19
+ await writeFile(path, text, "utf8");
20
+ }
21
+ async writeFileBytes(path, bytes) {
22
+ await writeFile(path, bytes);
23
+ }
24
+ async mkdirp(path) {
25
+ await mkdir(path, { recursive: true });
26
+ }
27
+ async readdir(path) {
28
+ return await readdir(path);
29
+ }
30
+ async lstat(path) {
31
+ const st = await lstat(path);
32
+ return { kind: toKind(st), mtimeMs: st.mtimeMs };
33
+ }
34
+ async rm(path) {
35
+ await rm(path, { recursive: true, force: true });
36
+ }
37
+ async readlink(path) {
38
+ return await readlink(path);
39
+ }
40
+ async symlink(target, path) {
41
+ await symlink(target, path);
42
+ }
43
+ // Utility for large copies without buffering the entire file.
44
+ async copyFileStream(src, dest) {
45
+ await pipeline(createReadStream(src), createWriteStream(dest));
46
+ }
47
+ }
@@ -0,0 +1,21 @@
1
+ import type { GitPort } from "../../ports/git-port.js";
2
+ import type { GitContext } from "../../commands/shared/git-context.js";
3
+ export declare class GitContextAdapter implements GitPort {
4
+ private readonly inner;
5
+ constructor(inner: GitContext);
6
+ statusChangedPaths(): Promise<string[]>;
7
+ statusStagedPaths(): Promise<string[]>;
8
+ statusUnstagedTrackedPaths(): Promise<string[]>;
9
+ headCommit(): Promise<string>;
10
+ headHashSubject(): Promise<{
11
+ hash: string;
12
+ subject: string;
13
+ }>;
14
+ stage(paths: string[]): Promise<void>;
15
+ commit(opts: {
16
+ message: string;
17
+ body?: string;
18
+ env?: NodeJS.ProcessEnv;
19
+ }): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=git-context-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-context-adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/git/git-context-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAEvE,qBAAa,iBAAkB,YAAW,OAAO;IAC/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;gBAEvB,KAAK,EAAE,UAAU;IAI7B,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIvC,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAItC,0BAA0B,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAI/C,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,eAAe;;;;IAIf,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC,MAAM,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAGzF"}
@@ -0,0 +1,27 @@
1
+ export class GitContextAdapter {
2
+ inner;
3
+ constructor(inner) {
4
+ this.inner = inner;
5
+ }
6
+ statusChangedPaths() {
7
+ return this.inner.statusChangedPaths();
8
+ }
9
+ statusStagedPaths() {
10
+ return this.inner.statusStagedPaths();
11
+ }
12
+ statusUnstagedTrackedPaths() {
13
+ return this.inner.statusUnstagedTrackedPaths();
14
+ }
15
+ headCommit() {
16
+ return this.inner.headCommit();
17
+ }
18
+ headHashSubject() {
19
+ return this.inner.headHashSubject();
20
+ }
21
+ stage(paths) {
22
+ return this.inner.stage(paths);
23
+ }
24
+ commit(opts) {
25
+ return this.inner.commit(opts);
26
+ }
27
+ }
@@ -0,0 +1,13 @@
1
+ import type { CommandContext } from "../commands/shared/task-backend.js";
2
+ import { SystemClockAdapter } from "./clock/system-clock-adapter.js";
3
+ import { NodeFileSystemAdapter } from "./fs/node-fs-adapter.js";
4
+ import { GitContextAdapter } from "./git/git-context-adapter.js";
5
+ import { TaskBackendAdapter } from "./task-backend/task-backend-adapter.js";
6
+ export type Adapters = {
7
+ fs: NodeFileSystemAdapter;
8
+ git: GitContextAdapter;
9
+ tasks: TaskBackendAdapter;
10
+ clock: SystemClockAdapter;
11
+ };
12
+ export declare function buildAdapters(ctx: CommandContext): Adapters;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAE5E,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,qBAAqB,CAAC;IAC1B,GAAG,EAAE,iBAAiB,CAAC;IACvB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,KAAK,EAAE,kBAAkB,CAAC;CAC3B,CAAC;AAEF,wBAAgB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,QAAQ,CAO3D"}
@@ -0,0 +1,12 @@
1
+ import { SystemClockAdapter } from "./clock/system-clock-adapter.js";
2
+ import { NodeFileSystemAdapter } from "./fs/node-fs-adapter.js";
3
+ import { GitContextAdapter } from "./git/git-context-adapter.js";
4
+ import { TaskBackendAdapter } from "./task-backend/task-backend-adapter.js";
5
+ export function buildAdapters(ctx) {
6
+ return {
7
+ fs: new NodeFileSystemAdapter(),
8
+ git: new GitContextAdapter(ctx.git),
9
+ tasks: new TaskBackendAdapter(ctx),
10
+ clock: new SystemClockAdapter(),
11
+ };
12
+ }
@@ -0,0 +1,12 @@
1
+ import type { TaskData } from "../../backends/task-backend.js";
2
+ import type { TaskBackendPort } from "../../ports/task-backend-port.js";
3
+ import type { CommandContext } from "../../commands/shared/task-backend.js";
4
+ export declare class TaskBackendAdapter implements TaskBackendPort {
5
+ private readonly ctx;
6
+ constructor(ctx: CommandContext);
7
+ listTasks(): Promise<TaskData[]>;
8
+ getTask(id: string): Promise<TaskData | null>;
9
+ writeTask(task: TaskData): Promise<void>;
10
+ exportTasksJson(path: string): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=task-backend-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-backend-adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/task-backend/task-backend-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAE5E,qBAAa,kBAAmB,YAAW,eAAe;IACxD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAiB;gBAEzB,GAAG,EAAE,cAAc;IAI/B,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAIhC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAI7C,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO7C"}
@@ -0,0 +1,22 @@
1
+ export class TaskBackendAdapter {
2
+ ctx;
3
+ constructor(ctx) {
4
+ this.ctx = ctx;
5
+ }
6
+ listTasks() {
7
+ return this.ctx.taskBackend.listTasks();
8
+ }
9
+ getTask(id) {
10
+ return this.ctx.taskBackend.getTask(id);
11
+ }
12
+ writeTask(task) {
13
+ return this.ctx.taskBackend.writeTask(task);
14
+ }
15
+ exportTasksJson(path) {
16
+ const backend = this.ctx.taskBackend;
17
+ if (!backend.exportTasksJson) {
18
+ throw new Error("Backend does not support exportTasksJson");
19
+ }
20
+ return backend.exportTasksJson(path);
21
+ }
22
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"local-backend.d.ts","sourceRoot":"","sources":["../../../src/backends/task-backend/local-backend.ts"],"names":[],"mappings":"AAqBA,OAAO,EAiBL,KAAK,WAAW,EAChB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAErB,qBAAa,YAAa,YAAW,WAAW;IAC9C,EAAE,SAAW;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;gBAEN,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAKrD,cAAc,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAqB3E,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IA4EhC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAoBjD,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC;IAKzD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO3C,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAyExC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB1E,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBvE,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C,cAAc,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAoF/D,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAIzD"}
1
+ {"version":3,"file":"local-backend.d.ts","sourceRoot":"","sources":["../../../src/backends/task-backend/local-backend.ts"],"names":[],"mappings":"AAqBA,OAAO,EAiBL,KAAK,WAAW,EAChB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAErB,qBAAa,YAAa,YAAW,WAAW;IAC9C,EAAE,SAAW;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;gBAEN,QAAQ,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAKrD,cAAc,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAqB3E,SAAS,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IA2FhC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAoBjD,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC;IAKzD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO3C,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAyExC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB1E,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBvE,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C,cAAc,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAoF/D,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAIzD"}
@@ -43,75 +43,80 @@ export class LocalBackend {
43
43
  const cachedIndex = await loadTaskIndex(indexPath);
44
44
  const cachedByPath = new Map();
45
45
  if (cachedIndex) {
46
- for (const entry of cachedIndex.tasks) {
47
- cachedByPath.set(entry.readmePath, entry);
46
+ for (const [readmePath, taskId] of Object.entries(cachedIndex.byPath)) {
47
+ const entry = cachedIndex.byId[taskId];
48
+ if (entry)
49
+ cachedByPath.set(readmePath, entry);
48
50
  }
49
51
  }
50
- const nextIndex = [];
52
+ const nextById = {};
53
+ const nextByPath = {};
51
54
  const seen = new Set();
52
- for (const entry of entries) {
53
- if (!entry.isDirectory())
54
- continue;
55
- const readme = path.join(this.root, entry.name, "README.md");
55
+ // Deterministic ordering helps both users and tests; keep I/O bounded to avoid storms.
56
+ const dirs = entries
57
+ .filter((e) => e.isDirectory())
58
+ .map((e) => e.name)
59
+ .toSorted();
60
+ const results = await mapLimit(dirs, 32, async (dirName) => {
61
+ const readme = path.join(this.root, dirName, "README.md");
56
62
  let stats;
57
63
  try {
58
64
  stats = await stat(readme);
59
65
  }
60
66
  catch {
61
- continue;
67
+ return null;
62
68
  }
63
69
  if (!stats.isFile())
64
- continue;
70
+ return null;
65
71
  const cached = cachedByPath.get(readme);
66
72
  if (cached?.mtimeMs === stats.mtimeMs) {
67
- const taskId = cached.task.id.trim();
68
- if (taskId) {
69
- validateTaskId(taskId);
70
- if (seen.has(taskId)) {
71
- throw new Error(`Duplicate task id in local backend: ${taskId}`);
72
- }
73
- seen.add(taskId);
74
- }
75
- tasks.push(cached.task);
76
- nextIndex.push(cached);
77
- continue;
73
+ return { task: cached.task, index: cached, mtimeMs: stats.mtimeMs, readme };
78
74
  }
79
75
  let text = "";
80
76
  try {
81
77
  text = await readFile(readme, "utf8");
82
78
  }
83
79
  catch {
84
- continue;
80
+ return null;
85
81
  }
86
82
  let parsed;
87
83
  try {
88
84
  parsed = parseTaskReadme(text);
89
85
  }
90
86
  catch {
91
- continue;
87
+ return null;
92
88
  }
93
89
  const fm = parsed.frontmatter;
94
90
  if (!isRecord(fm) || Object.keys(fm).length === 0)
95
- continue;
96
- const taskId = (typeof fm.id === "string" ? fm.id : entry.name).trim();
97
- if (taskId) {
98
- validateTaskId(taskId);
99
- if (seen.has(taskId)) {
100
- throw new Error(`Duplicate task id in local backend: ${taskId}`);
101
- }
102
- seen.add(taskId);
103
- }
91
+ return null;
92
+ const taskId = (typeof fm.id === "string" ? fm.id : dirName).trim();
104
93
  const task = taskRecordToData({
105
94
  id: taskId,
106
95
  frontmatter: fm,
107
96
  body: parsed.body,
108
97
  readmePath: readme,
109
98
  });
110
- tasks.push(task);
111
- nextIndex.push(buildTaskIndexEntry(task, readme, stats.mtimeMs));
99
+ return { task, index: null, mtimeMs: stats.mtimeMs, readme };
100
+ });
101
+ for (const res of results) {
102
+ if (!res)
103
+ continue;
104
+ const taskId = res.task.id.trim();
105
+ if (taskId) {
106
+ validateTaskId(taskId);
107
+ if (seen.has(taskId))
108
+ throw new Error(`Duplicate task id in local backend: ${taskId}`);
109
+ seen.add(taskId);
110
+ }
111
+ tasks.push(res.task);
112
+ if (taskId) {
113
+ const entry = res.index ?? buildTaskIndexEntry(res.task, res.readme, res.mtimeMs);
114
+ nextById[taskId] = entry;
115
+ nextByPath[entry.readmePath] = taskId;
116
+ }
112
117
  }
113
118
  try {
114
- await saveTaskIndex(indexPath, { schema_version: 1, tasks: nextIndex });
119
+ await saveTaskIndex(indexPath, { schema_version: 2, byId: nextById, byPath: nextByPath });
115
120
  }
116
121
  catch {
117
122
  // Best-effort cache; ignore failures.