agentplane 0.2.2 → 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.
- package/assets/AGENTS.md +58 -32
- package/assets/framework.manifest.json +89 -0
- package/dist/adapters/clock/system-clock-adapter.d.ts +5 -0
- package/dist/adapters/clock/system-clock-adapter.d.ts.map +1 -0
- package/dist/adapters/clock/system-clock-adapter.js +5 -0
- package/dist/adapters/fs/node-fs-adapter.d.ts +15 -0
- package/dist/adapters/fs/node-fs-adapter.d.ts.map +1 -0
- package/dist/adapters/fs/node-fs-adapter.js +47 -0
- package/dist/adapters/git/git-context-adapter.d.ts +21 -0
- package/dist/adapters/git/git-context-adapter.d.ts.map +1 -0
- package/dist/adapters/git/git-context-adapter.js +27 -0
- package/dist/adapters/index.d.ts +13 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/task-backend/task-backend-adapter.d.ts +12 -0
- package/dist/adapters/task-backend/task-backend-adapter.d.ts.map +1 -0
- package/dist/adapters/task-backend/task-backend-adapter.js +22 -0
- package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/local-backend.js +39 -34
- package/dist/backends/task-index.d.ts +9 -3
- package/dist/backends/task-index.d.ts.map +1 -1
- package/dist/backends/task-index.js +64 -14
- package/dist/cli/cli-error.d.ts +9 -0
- package/dist/cli/cli-error.d.ts.map +1 -0
- package/dist/cli/cli-error.js +13 -0
- package/dist/cli/http.d.ts.map +1 -1
- package/dist/cli/http.js +18 -6
- package/dist/cli/parse/lifecycle.d.ts.map +1 -1
- package/dist/cli/parse/lifecycle.js +6 -1
- package/dist/cli/run-cli/command-catalog.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog.js +8 -0
- package/dist/cli/run-cli/commands/init/conflicts.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/conflicts.js +2 -1
- package/dist/cli/run-cli/commands/init/write-agents.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/write-agents.js +27 -4
- package/dist/cli/run-cli/commands/init/write-config.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/write-config.js +0 -4
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +14 -5
- package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
- package/dist/cli/run-cli.test-helpers.js +35 -1
- package/dist/commands/branch/internal/work-validate.d.ts.map +1 -1
- package/dist/commands/branch/internal/work-validate.js +13 -4
- package/dist/commands/branch/status.d.ts.map +1 -1
- package/dist/commands/branch/status.js +9 -4
- package/dist/commands/doctor.command.d.ts +8 -0
- package/dist/commands/doctor.command.d.ts.map +1 -0
- package/dist/commands/doctor.command.js +137 -0
- package/dist/commands/guard/impl/allow.d.ts.map +1 -1
- package/dist/commands/guard/impl/allow.js +7 -2
- package/dist/commands/guard/impl/close-message.d.ts.map +1 -1
- package/dist/commands/guard/impl/close-message.js +7 -2
- package/dist/commands/pr/check.d.ts.map +1 -1
- package/dist/commands/pr/check.js +7 -2
- package/dist/commands/pr/integrate/artifacts.d.ts.map +1 -1
- package/dist/commands/pr/integrate/artifacts.js +6 -1
- package/dist/commands/pr/integrate/internal/merge.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/merge.js +7 -6
- package/dist/commands/pr/integrate/internal/prepare.d.ts.map +1 -1
- package/dist/commands/pr/integrate/internal/prepare.js +9 -4
- package/dist/commands/pr/integrate/verify.d.ts.map +1 -1
- package/dist/commands/pr/integrate/verify.js +3 -1
- package/dist/commands/pr/note.d.ts.map +1 -1
- package/dist/commands/pr/note.js +13 -4
- package/dist/commands/pr/open.d.ts.map +1 -1
- package/dist/commands/pr/open.js +8 -3
- package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
- package/dist/commands/recipes/impl/apply.js +2 -1
- package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/explain.js +2 -1
- package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/info.js +2 -1
- package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/install.js +5 -4
- package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/remove.js +2 -1
- package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
- package/dist/commands/scenario/impl/commands.js +8 -7
- package/dist/commands/shared/git-ops.d.ts.map +1 -1
- package/dist/commands/shared/git-ops.js +4 -3
- package/dist/commands/shared/task-store.d.ts.map +1 -1
- package/dist/commands/shared/task-store.js +7 -2
- package/dist/commands/task/list.command.d.ts.map +1 -1
- package/dist/commands/task/list.command.js +4 -5
- package/dist/commands/task/migrate-doc.d.ts.map +1 -1
- package/dist/commands/task/migrate-doc.js +2 -1
- package/dist/commands/task/new.command.d.ts.map +1 -1
- package/dist/commands/task/new.command.js +2 -8
- package/dist/commands/task/rebuild-index.command.d.ts +6 -0
- package/dist/commands/task/rebuild-index.command.d.ts.map +1 -0
- package/dist/commands/task/rebuild-index.command.js +18 -0
- package/dist/commands/task/shared.d.ts.map +1 -1
- package/dist/commands/task/shared.js +15 -6
- package/dist/commands/upgrade.command.d.ts.map +1 -1
- package/dist/commands/upgrade.command.js +52 -4
- package/dist/commands/upgrade.d.ts +10 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +337 -95
- package/dist/policy/engine.d.ts +21 -0
- package/dist/policy/engine.d.ts.map +1 -0
- package/dist/policy/engine.js +32 -0
- package/dist/ports/clock-port.d.ts +4 -0
- package/dist/ports/clock-port.d.ts.map +1 -0
- package/dist/ports/clock-port.js +1 -0
- package/dist/ports/fs-port.d.ts +18 -0
- package/dist/ports/fs-port.d.ts.map +1 -0
- package/dist/ports/fs-port.js +1 -0
- package/dist/ports/git-port.d.ts +18 -0
- package/dist/ports/git-port.d.ts.map +1 -0
- package/dist/ports/git-port.js +1 -0
- package/dist/ports/task-backend-port.d.ts +8 -0
- package/dist/ports/task-backend-port.d.ts.map +1 -0
- package/dist/ports/task-backend-port.js +1 -0
- package/dist/usecases/context/resolve-context.d.ts +14 -0
- package/dist/usecases/context/resolve-context.d.ts.map +1 -0
- package/dist/usecases/context/resolve-context.js +13 -0
- package/dist/usecases/task/task-list-usecase.d.ts +9 -0
- package/dist/usecases/task/task-list-usecase.d.ts.map +1 -0
- package/dist/usecases/task/task-list-usecase.js +17 -0
- package/dist/usecases/task/task-new-usecase.d.ts +9 -0
- package/dist/usecases/task/task-new-usecase.d.ts.map +1 -0
- package/dist/usecases/task/task-new-usecase.js +17 -0
- 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
|
|
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
|
-
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
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-
|
|
100
|
+
### Outside-workspace
|
|
89
101
|
|
|
90
|
-
Outside-
|
|
102
|
+
Outside-workspace reading/writing is **always prohibited** unless the user explicitly approves it (regardless of `require_network`).
|
|
91
103
|
|
|
92
|
-
Outside-
|
|
104
|
+
Outside-workspace includes (non-exhaustive):
|
|
93
105
|
|
|
94
|
-
- reading/writing outside the
|
|
106
|
+
- reading/writing outside the workspace (`~`, `/etc`, global configs)
|
|
95
107
|
- modifying keychains, ssh keys, credential stores
|
|
96
|
-
- any tool that mutates outside-
|
|
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
|
|
159
|
+
Before any planning or execution, ORCHESTRATOR must:
|
|
143
160
|
|
|
144
|
-
1.
|
|
145
|
-
2.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
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-
|
|
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
|
|
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
|
-
|
|
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-
|
|
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 @@
|
|
|
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,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;
|
|
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
|
|
47
|
-
|
|
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
|
|
52
|
+
const nextById = {};
|
|
53
|
+
const nextByPath = {};
|
|
51
54
|
const seen = new Set();
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
67
|
+
return null;
|
|
62
68
|
}
|
|
63
69
|
if (!stats.isFile())
|
|
64
|
-
|
|
70
|
+
return null;
|
|
65
71
|
const cached = cachedByPath.get(readme);
|
|
66
72
|
if (cached?.mtimeMs === stats.mtimeMs) {
|
|
67
|
-
|
|
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
|
-
|
|
80
|
+
return null;
|
|
85
81
|
}
|
|
86
82
|
let parsed;
|
|
87
83
|
try {
|
|
88
84
|
parsed = parseTaskReadme(text);
|
|
89
85
|
}
|
|
90
86
|
catch {
|
|
91
|
-
|
|
87
|
+
return null;
|
|
92
88
|
}
|
|
93
89
|
const fm = parsed.frontmatter;
|
|
94
90
|
if (!isRecord(fm) || Object.keys(fm).length === 0)
|
|
95
|
-
|
|
96
|
-
const taskId = (typeof fm.id === "string" ? fm.id :
|
|
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
|
-
|
|
111
|
-
|
|
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:
|
|
119
|
+
await saveTaskIndex(indexPath, { schema_version: 2, byId: nextById, byPath: nextByPath });
|
|
115
120
|
}
|
|
116
121
|
catch {
|
|
117
122
|
// Best-effort cache; ignore failures.
|