trekoon 0.3.1 → 0.3.2
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/.agents/skills/trekoon/SKILL.md +126 -14
- package/.agents/skills/trekoon/reference/execution-with-team.md +213 -0
- package/.agents/skills/trekoon/reference/execution.md +210 -0
- package/.agents/skills/trekoon/reference/planning.md +244 -0
- package/README.md +20 -9
- package/docs/ai-agents.md +59 -26
- package/package.json +2 -2
- package/src/board/assets/app.js +5 -0
- package/src/board/assets/components/EpicsOverview.js +13 -0
- package/src/board/assets/components/Workspace.js +27 -12
- package/src/board/assets/components/helpers.js +3 -2
- package/src/board/assets/runtime/delegation.js +69 -1
- package/src/board/assets/state/actions.js +27 -1
- package/src/board/assets/state/store.js +37 -8
- package/src/board/assets/state/utils.js +42 -0
- package/src/board/assets/styles/board.css +68 -0
- package/src/commands/skills.ts +39 -32
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Planning Reference
|
|
2
|
+
|
|
3
|
+
Write implementation plans directly into Trekoon as epics with task/subtask
|
|
4
|
+
DAGs. Plans must be directly executable without re-interpretation.
|
|
5
|
+
|
|
6
|
+
**Clarify ambiguity upfront.** If the plan has unclear requirements or meaningful
|
|
7
|
+
tradeoffs, ask the user before writing. Present options with clear tradeoffs.
|
|
8
|
+
Use multi-select for independent features that can be combined; use single-select
|
|
9
|
+
for mutually exclusive choices.
|
|
10
|
+
|
|
11
|
+
## Planning data model
|
|
12
|
+
|
|
13
|
+
- **Epic** = full feature outcome and constraints.
|
|
14
|
+
- **Task** = one complete subsystem/domain work unit that can be owned by one
|
|
15
|
+
agent.
|
|
16
|
+
- **Subtask** = concrete implementation/test/verification step under a task.
|
|
17
|
+
- **Dependency edge** = strict prerequisite only (do not add "nice to have"
|
|
18
|
+
dependencies).
|
|
19
|
+
|
|
20
|
+
All entities start in `todo`. See the status machine in the main SKILL.md.
|
|
21
|
+
|
|
22
|
+
Plan implications:
|
|
23
|
+
- Never set initial status to anything other than `todo` in create commands.
|
|
24
|
+
- Task descriptions should reference valid transitions when documenting
|
|
25
|
+
completion flow (e.g., "todo -> in_progress -> done", not "todo -> done").
|
|
26
|
+
- `task done` auto-transitions through `in_progress`, but `task update` does
|
|
27
|
+
not — plan descriptions should note this for agents.
|
|
28
|
+
|
|
29
|
+
## Information-dense writing standard
|
|
30
|
+
|
|
31
|
+
### Epic title
|
|
32
|
+
|
|
33
|
+
Use a functional, outcome-oriented format:
|
|
34
|
+
|
|
35
|
+
`<Product/Area>: <deliverable> to <user/system outcome> (<key constraint>)`
|
|
36
|
+
|
|
37
|
+
Example: `Checkout: add idempotent payment capture to prevent duplicate charges
|
|
38
|
+
(Stripe + retries)`
|
|
39
|
+
|
|
40
|
+
### Epic description
|
|
41
|
+
|
|
42
|
+
Include:
|
|
43
|
+
|
|
44
|
+
1. **Goal & why now**
|
|
45
|
+
2. **In scope** (specific capabilities)
|
|
46
|
+
3. **Out of scope** (explicit exclusions)
|
|
47
|
+
4. **Success criteria** (testable outcomes)
|
|
48
|
+
5. **Risks/constraints** (data migration, latency budgets, auth boundaries)
|
|
49
|
+
6. **Verification gates** (tests, manual checks, perf/security checks)
|
|
50
|
+
|
|
51
|
+
### Task title
|
|
52
|
+
|
|
53
|
+
Use a structure that encodes subsystem + action + outcome:
|
|
54
|
+
|
|
55
|
+
`[<Subsystem>] <verb> <artifact/interface> to <observable outcome>`
|
|
56
|
+
|
|
57
|
+
Example: `[API/Auth] issue refresh-token rotation endpoint to invalidate
|
|
58
|
+
replayed sessions`
|
|
59
|
+
|
|
60
|
+
### Task description
|
|
61
|
+
|
|
62
|
+
Must include:
|
|
63
|
+
|
|
64
|
+
- concrete scope and affected paths/symbols
|
|
65
|
+
- acceptance criteria (observable behavior)
|
|
66
|
+
- required tests/verification commands
|
|
67
|
+
- integration constraints (contracts, backward compatibility)
|
|
68
|
+
- explicit "can run in parallel with ..." or "blocked by ..." note
|
|
69
|
+
|
|
70
|
+
**File scope** — declare explicitly so the agent doesn't waste tokens exploring:
|
|
71
|
+
|
|
72
|
+
- **Target files**: files to create or modify
|
|
73
|
+
- **Read-first files**: files the agent should read for context
|
|
74
|
+
- **Do-not-touch**: paths that parallel agents own — prevents merge conflicts
|
|
75
|
+
|
|
76
|
+
**Context loading hints** — point the agent to existing patterns:
|
|
77
|
+
|
|
78
|
+
- Reference a concrete file as the pattern to mirror
|
|
79
|
+
- Name the specific function/class/export to extend or integrate with
|
|
80
|
+
- State project conventions rather than assuming the agent will discover them
|
|
81
|
+
|
|
82
|
+
**Owner assignment** — when the plan has clear subsystem lanes, assign owners in
|
|
83
|
+
task descriptions so the executor knows which agent/person owns each task:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
Owner: auth-lane
|
|
87
|
+
Can run in parallel with: billing-lane tasks
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Example task description:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Implement refresh-token rotation endpoint.
|
|
94
|
+
|
|
95
|
+
Target files: src/auth/refresh.ts (new), src/auth/refresh.test.ts (new)
|
|
96
|
+
Read first: src/auth/login.ts (follow same handler pattern)
|
|
97
|
+
Do not touch: src/billing/* (owned by billing-lane)
|
|
98
|
+
|
|
99
|
+
Follow the handler pattern in login.ts: schema validation -> service call ->
|
|
100
|
+
response mapping.
|
|
101
|
+
|
|
102
|
+
Acceptance: POST /auth/refresh returns new token pair, invalidates old token.
|
|
103
|
+
Verify: bun test src/auth/refresh.test.ts
|
|
104
|
+
Owner: auth-lane
|
|
105
|
+
Can run in parallel with: billing-lane. Blocked by: task-types (needs AuthToken).
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Subtask title/description
|
|
109
|
+
|
|
110
|
+
- Titles are imperative and specific, not generic.
|
|
111
|
+
- Description states exact artifact and completion signal.
|
|
112
|
+
- Use subtasks for real units, not filler checklist noise.
|
|
113
|
+
- Inherit file scope from parent task — only override if different files.
|
|
114
|
+
|
|
115
|
+
## Parallelism & dependencies
|
|
116
|
+
|
|
117
|
+
Model execution lanes intentionally:
|
|
118
|
+
|
|
119
|
+
- Tasks with no dependency edge between them are parallel candidates.
|
|
120
|
+
- Tasks in different subsystems should usually run in parallel.
|
|
121
|
+
- Tasks in the same subsystem should be combined or sequenced.
|
|
122
|
+
- Keep task groups to ~3-4 tasks per active subsystem lane.
|
|
123
|
+
|
|
124
|
+
Dependency policy:
|
|
125
|
+
|
|
126
|
+
1. Add an edge only for hard prerequisites.
|
|
127
|
+
2. Prefer task-to-task dependencies; use subtask dependencies only when required.
|
|
128
|
+
3. Validate acyclic graph assumptions before finalizing.
|
|
129
|
+
|
|
130
|
+
## Assign owners after creation
|
|
131
|
+
|
|
132
|
+
After creating tasks, assign ownership for multi-agent execution:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
trekoon --toon task update <task-id> --owner auth-lane
|
|
136
|
+
trekoon --toon task update <task-id> --owner billing-lane
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Validate the plan
|
|
140
|
+
|
|
141
|
+
After creating the epic, validate before handing off to execution.
|
|
142
|
+
|
|
143
|
+
### Check progress structure
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
trekoon --toon epic progress <epic-id>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Verify: total count matches expectations, all tasks are in `todo`, ready count
|
|
150
|
+
equals the number of tasks with no dependencies.
|
|
151
|
+
|
|
152
|
+
### Run suggest to confirm sanity
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
trekoon --toon suggest --epic <epic-id>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`suggest` will surface issues: sync gaps, recovery needs, or unexpected blocker
|
|
159
|
+
states. If it suggests claiming a task, the plan's dependency graph is valid and
|
|
160
|
+
execution-ready.
|
|
161
|
+
|
|
162
|
+
### Verify dependency graph
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
trekoon --toon task ready --epic <epic-id> --limit 50
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Confirm that the expected first-wave tasks appear as ready candidates and
|
|
169
|
+
second-wave tasks appear as blocked with the right dependencies.
|
|
170
|
+
|
|
171
|
+
## Plan output and handoff
|
|
172
|
+
|
|
173
|
+
After creating the epic and validating, present a summary to the user. This
|
|
174
|
+
summary is the primary handoff artifact — it must be self-contained and
|
|
175
|
+
actionable.
|
|
176
|
+
|
|
177
|
+
### ID rules
|
|
178
|
+
|
|
179
|
+
- **Always use full UUIDs** for epic and task IDs. Never use temp-keys
|
|
180
|
+
(`task-truthy`, `@task-api`, etc.) in the summary — those are ephemeral
|
|
181
|
+
creation-time references that do not exist in the database.
|
|
182
|
+
- IDs must be copy-friendly: render them in monospace/code formatting so the
|
|
183
|
+
user can select and copy a UUID directly.
|
|
184
|
+
|
|
185
|
+
### Summary structure
|
|
186
|
+
|
|
187
|
+
1. **Epic ID + title** — displayed prominently at the top, e.g.:
|
|
188
|
+
```
|
|
189
|
+
Epic: <full-uuid>
|
|
190
|
+
Title: <epic title>
|
|
191
|
+
```
|
|
192
|
+
2. Tasks grouped by wave/batch with columns: full UUID, title, owner/lane
|
|
193
|
+
3. Dependencies shown per task (using full UUIDs or task titles, not temp-keys)
|
|
194
|
+
4. Verification gate (commands to run after all tasks complete)
|
|
195
|
+
|
|
196
|
+
### Example format
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
Epic: 904b3129-be2d-4b20-8030-537dc327491a
|
|
200
|
+
Title: Checkout: add idempotent payment capture
|
|
201
|
+
|
|
202
|
+
Wave 1 (parallel)
|
|
203
|
+
| ID | Task | Owner |
|
|
204
|
+
|--------------------------------------|-------------------------------|--------------|
|
|
205
|
+
| c12c9746-dbae-4660-bcbb-ebe660cb7054 | [API] Payment capture endpoint| api-lane |
|
|
206
|
+
| 4f0848f3-538a-44d3-8415-5bb16cf3f39e | [UI] Checkout button states | ui-lane |
|
|
207
|
+
|
|
208
|
+
Wave 2 (depends on wave 1)
|
|
209
|
+
| ID | Task | Depends on |
|
|
210
|
+
|--------------------------------------|-------------------------------|--------------------------------------|
|
|
211
|
+
| 8a76afac-155d-45b3-b205-df2e4ef8988b | [API] Retry logic | c12c9746-dbae-4660-bcbb-ebe660cb7054 |
|
|
212
|
+
|
|
213
|
+
Verification: bun run build && bun run test
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Execution handoff contract
|
|
217
|
+
|
|
218
|
+
Every plan must be directly executable without re-interpretation. Include these
|
|
219
|
+
in task descriptions:
|
|
220
|
+
|
|
221
|
+
1. **Lane/subsystem ownership** (`[Subsystem] ...` in title, `--owner` set).
|
|
222
|
+
2. **Dependency intent** (explicit `blocked by ...` / `can run in parallel
|
|
223
|
+
with ...`).
|
|
224
|
+
3. **Verification evidence** (exact commands and expected outcome).
|
|
225
|
+
4. **Completion semantics** (`done` means verified and handoff-ready; use
|
|
226
|
+
`blocked` with reason when not).
|
|
227
|
+
5. **Stable contract** (execution appends progress notes rather than rewriting
|
|
228
|
+
original plan text unless the plan itself is wrong).
|
|
229
|
+
|
|
230
|
+
## Quality rules
|
|
231
|
+
|
|
232
|
+
1. **No markdown plan files** as source of truth.
|
|
233
|
+
2. **No vague titles** ("Refactor stuff", "Fix bugs").
|
|
234
|
+
3. **Descriptions must be implementation-usable** by another agent without
|
|
235
|
+
guessing.
|
|
236
|
+
4. **Every task must define completion evidence** (tests/manual checks).
|
|
237
|
+
5. **Parallelism must be explicit** (not implied).
|
|
238
|
+
6. **Status transitions must be valid** — never describe a `todo -> done` flow.
|
|
239
|
+
7. **Owners should be assigned** when multiple execution lanes exist.
|
|
240
|
+
|
|
241
|
+
## Large initiatives
|
|
242
|
+
|
|
243
|
+
For large scope, create multiple epics with explicit cross-epic boundaries. Use
|
|
244
|
+
dependencies within each epic DAG and keep each epic executable in bounded time.
|
package/README.md
CHANGED
|
@@ -129,18 +129,29 @@ For the full lifecycle and examples, read [Quickstart](docs/quickstart.md) and
|
|
|
129
129
|
|
|
130
130
|
## AI skill
|
|
131
131
|
|
|
132
|
-
Trekoon ships with a
|
|
133
|
-
|
|
132
|
+
Trekoon ships with a self-contained `trekoon` skill for AI agents. One skill
|
|
133
|
+
covers the full plan-to-completion workflow:
|
|
134
134
|
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
-
|
|
140
|
-
|
|
135
|
+
- **Command reference** — `--toon` defaults, status machine, bulk planning,
|
|
136
|
+
append-based progress logging
|
|
137
|
+
- **Planning** — decomposition into epic/task/subtask DAGs, writing standard,
|
|
138
|
+
file scopes, owner assignment, dependency modeling
|
|
139
|
+
- **Execution** — graph building, lane grouping, sub-agent dispatch, task done
|
|
140
|
+
orchestration, verification
|
|
141
|
+
- **Agent Teams** — TeamCreate/SendMessage pattern for parallel Claude Code
|
|
142
|
+
instances (requires `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=true`)
|
|
143
|
+
|
|
144
|
+
The skill accepts arguments for quick entity-scoped actions:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
/trekoon → load the skill
|
|
148
|
+
/trekoon <id> → show status and next steps for an entity
|
|
149
|
+
/trekoon <id> execute → start executing the entity's epic
|
|
150
|
+
/trekoon <id> plan → decompose into tasks/subtasks/deps
|
|
151
|
+
```
|
|
141
152
|
|
|
142
153
|
Read [AI agents and the Trekoon skill](docs/ai-agents.md) for installation,
|
|
143
|
-
editor linking,
|
|
154
|
+
editor linking, and example prompts.
|
|
144
155
|
|
|
145
156
|
## Read next
|
|
146
157
|
|
package/docs/ai-agents.md
CHANGED
|
@@ -15,9 +15,21 @@ agent to:
|
|
|
15
15
|
- preview scoped replace before `--apply`
|
|
16
16
|
- treat `.trekoon` as shared repo-scoped operational state
|
|
17
17
|
|
|
18
|
-
The
|
|
18
|
+
The skill ships with bundled reference guides for planning and execution so the
|
|
19
|
+
agent can handle the full plan-to-completion workflow from a single skill:
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
```
|
|
22
|
+
.agents/skills/trekoon/
|
|
23
|
+
SKILL.md ← command reference, status machine, agent loop
|
|
24
|
+
reference/
|
|
25
|
+
planning.md ← decomposition, writing standard, validation
|
|
26
|
+
execution.md ← graph building, lane dispatch, verification
|
|
27
|
+
execution-with-team.md ← Agent Teams pattern (Claude Code only)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The agent reads the relevant reference file on demand — `planning.md` when asked
|
|
31
|
+
to plan, `execution.md` when asked to execute, `execution-with-team.md` when Agent
|
|
32
|
+
Teams are available.
|
|
21
33
|
|
|
22
34
|
## Install the skill
|
|
23
35
|
|
|
@@ -46,25 +58,41 @@ Path behavior:
|
|
|
46
58
|
- `--to <path>` changes only the editor link root
|
|
47
59
|
- `--allow-outside-repo` is for intentional external links
|
|
48
60
|
|
|
49
|
-
##
|
|
61
|
+
## Using the skill with arguments
|
|
62
|
+
|
|
63
|
+
The skill accepts an optional entity ID and action text:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
/trekoon → loads the skill normally
|
|
67
|
+
/trekoon <id> → resolves the entity, shows status and next steps
|
|
68
|
+
/trekoon <id> analyze → runs epic progress + suggest, reports findings
|
|
69
|
+
/trekoon <id> execute → starts the execution loop for the entity's epic
|
|
70
|
+
/trekoon <id> plan the implementation → decomposes into tasks/subtasks/deps
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The skill resolves the ID as an epic, task, or subtask. For tasks and subtasks,
|
|
74
|
+
it scopes session/suggest/progress calls to the parent epic automatically.
|
|
75
|
+
|
|
76
|
+
## Skill stack
|
|
50
77
|
|
|
51
|
-
|
|
52
|
-
|
|
78
|
+
The `trekoon` skill is self-contained for the full plan-to-completion workflow.
|
|
79
|
+
It bundles planning methodology, execution orchestration, and the command
|
|
80
|
+
reference in one install.
|
|
53
81
|
|
|
54
|
-
|
|
82
|
+
For specialized needs, these optional companion skills add value:
|
|
83
|
+
|
|
84
|
+
| Job | Skill | When to use |
|
|
55
85
|
| --- | --- | --- |
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
| Execute the plan in dependency order | `executing-plans` | Helps the agent work through the backlog without losing the sequence |
|
|
59
|
-
| Clarify architecture before planning | `architecting-systems` | Useful when boundaries or ownership are still fuzzy |
|
|
86
|
+
| Clarify architecture before planning | `architecting-systems` | Boundaries or ownership are still fuzzy |
|
|
87
|
+
| Specialized code review | `code-review-expert` | Want structured review before closing an epic |
|
|
60
88
|
|
|
61
|
-
In practice, the flow is
|
|
89
|
+
In practice, the flow is:
|
|
62
90
|
|
|
63
|
-
1.
|
|
64
|
-
2.
|
|
65
|
-
3.
|
|
66
|
-
4.
|
|
67
|
-
5.
|
|
91
|
+
1. `/trekoon` — load the skill
|
|
92
|
+
2. Plan the work (skill reads `reference/planning.md` internally)
|
|
93
|
+
3. Create or update the Trekoon graph
|
|
94
|
+
4. Execute the plan (skill reads `reference/execution.md` internally)
|
|
95
|
+
5. Update progress, blockers, and completion state as work moves forward
|
|
68
96
|
|
|
69
97
|
## Default execution loop for agents
|
|
70
98
|
|
|
@@ -161,29 +189,34 @@ Notes:
|
|
|
161
189
|
|
|
162
190
|
## Tell the agent exactly what to do
|
|
163
191
|
|
|
164
|
-
These prompts work well because they are explicit about the
|
|
165
|
-
expected loop.
|
|
192
|
+
These prompts work well because they are explicit about the expected workflow.
|
|
166
193
|
|
|
167
194
|
### Plan first, then create the backlog
|
|
168
195
|
|
|
169
196
|
```text
|
|
170
|
-
|
|
171
|
-
then
|
|
197
|
+
/trekoon — plan this feature as one epic with tasks, subtasks, and dependencies,
|
|
198
|
+
then create the graph in Trekoon.
|
|
172
199
|
```
|
|
173
200
|
|
|
174
201
|
### Execute an existing backlog
|
|
175
202
|
|
|
176
203
|
```text
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
204
|
+
/trekoon <epic-id> execute
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Or more explicitly:
|
|
208
|
+
|
|
209
|
+
```text
|
|
210
|
+
/trekoon — run session, take the next ready task, do the work, append progress
|
|
211
|
+
notes, mark it done, and repeat until there are no ready tasks or you hit a
|
|
212
|
+
blocker.
|
|
180
213
|
```
|
|
181
214
|
|
|
182
|
-
###
|
|
215
|
+
### Plan and execute end to end
|
|
183
216
|
|
|
184
217
|
```text
|
|
185
|
-
|
|
186
|
-
|
|
218
|
+
/trekoon — plan this feature, create the backlog, then execute the tasks in
|
|
219
|
+
dependency order until the epic is complete.
|
|
187
220
|
```
|
|
188
221
|
|
|
189
222
|
## Keep reads small and mutations safe
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trekoon",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "AI-first local issue tracker CLI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"docs",
|
|
35
35
|
"src",
|
|
36
36
|
"src/board/assets",
|
|
37
|
-
".agents/skills/trekoon
|
|
37
|
+
".agents/skills/trekoon",
|
|
38
38
|
"README.md",
|
|
39
39
|
"LICENSE"
|
|
40
40
|
],
|
package/src/board/assets/app.js
CHANGED
|
@@ -555,8 +555,13 @@ export async function bootLegacyBoard(options = {}) {
|
|
|
555
555
|
submitCreateSubtask: (id, data) => actions.submitCreateSubtask(id, data),
|
|
556
556
|
addDependency: (src, data) => actions.addDependency(src, data),
|
|
557
557
|
dropTaskStatus: (id, status) => actions.dropTaskStatus(id, status),
|
|
558
|
+
getTaskStatus: (id) => actions.getTaskStatus(id),
|
|
558
559
|
changeEpicStatus: (epicId, status) => actions.changeEpicStatus(epicId, status),
|
|
559
560
|
bulkSetStatus: (epicId, status) => actions.bulkSetStatus(epicId, status),
|
|
561
|
+
toggleEpicStatusFilter: (status) => actions.toggleEpicStatusFilter(status),
|
|
562
|
+
toggleTaskStatusFilter: (status) => actions.toggleTaskStatusFilter(status),
|
|
563
|
+
resetEpicFilter: () => actions.resetEpicFilter(),
|
|
564
|
+
resetTaskFilter: () => actions.resetTaskFilter(),
|
|
560
565
|
handleKeydown: (event) => actions.handleKeydown(event),
|
|
561
566
|
});
|
|
562
567
|
|
|
@@ -3,7 +3,10 @@ import {
|
|
|
3
3
|
panelClasses,
|
|
4
4
|
renderEmptyState,
|
|
5
5
|
sectionLabelClasses,
|
|
6
|
+
STATUS_LABELS,
|
|
7
|
+
STATUS_ORDER,
|
|
6
8
|
} from "./helpers.js";
|
|
9
|
+
import { DEFAULT_STATUS_FILTER } from "../state/store.js";
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Render the epics overview HTML.
|
|
@@ -17,6 +20,9 @@ import {
|
|
|
17
20
|
function render(props) {
|
|
18
21
|
const { visibleEpics, selectedEpicId, store } = props;
|
|
19
22
|
|
|
23
|
+
const epicStatusFilter = store.epicStatusFilter || { ...DEFAULT_STATUS_FILTER };
|
|
24
|
+
const isNonDefault = STATUS_ORDER.some(s => epicStatusFilter[s] !== DEFAULT_STATUS_FILTER[s]);
|
|
25
|
+
|
|
20
26
|
return `
|
|
21
27
|
<div class="board-root board-root--epics">
|
|
22
28
|
<section class="board-overview ${panelClasses("board-overview--dense p-4 sm:p-5")}" aria-label="Epics overview">
|
|
@@ -31,6 +37,13 @@ function render(props) {
|
|
|
31
37
|
<span class="board-chip board-chip--neutral">${store.snapshot.tasks.length} total tasks</span>
|
|
32
38
|
${store.isMutating ? '<span class="board-chip board-chip--neutral">Saving\u2026</span>' : ""}
|
|
33
39
|
</div>
|
|
40
|
+
<div class="board-filter-bar">
|
|
41
|
+
${STATUS_ORDER.map(status => {
|
|
42
|
+
const active = epicStatusFilter[status] !== false;
|
|
43
|
+
return `<button type="button" class="board-filter-pill ${active ? 'board-filter-pill--active' : 'board-filter-pill--inactive'} board-filter-pill--${status}" data-toggle-epic-status-filter="${status}" aria-pressed="${active}" title="${active ? 'Hide' : 'Show'} ${STATUS_LABELS[status]} epics">${STATUS_LABELS[status]}</button>`;
|
|
44
|
+
}).join('')}
|
|
45
|
+
${isNonDefault ? `<button type="button" class="board-filter-pill board-filter-pill--reset" data-reset-epic-filter title="Reset filters to defaults">Reset</button>` : ''}
|
|
46
|
+
</div>
|
|
34
47
|
</header>
|
|
35
48
|
<div class="board-table board-table--epics">
|
|
36
49
|
<div class="board-table__header board-table__header--epics hidden md:grid">
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
STATUS_LABELS,
|
|
21
21
|
STATUS_ORDER,
|
|
22
22
|
} from "./helpers.js";
|
|
23
|
-
import { orderEpicsNewestFirst } from "../state/store.js";
|
|
24
|
-
import { VIEW_MODES } from "../state/utils.js";
|
|
23
|
+
import { DEFAULT_STATUS_FILTER, orderEpicsNewestFirst } from "../state/store.js";
|
|
24
|
+
import { getSelectableStatuses, VIEW_MODES } from "../state/utils.js";
|
|
25
25
|
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
// Workspace header
|
|
@@ -58,6 +58,9 @@ function renderWorkspaceHeader(props) {
|
|
|
58
58
|
visibleTasks,
|
|
59
59
|
} = props;
|
|
60
60
|
|
|
61
|
+
const taskStatusFilter = store.taskStatusFilter || { ...DEFAULT_STATUS_FILTER };
|
|
62
|
+
const isTaskFilterNonDefault = STATUS_ORDER.some(s => taskStatusFilter[s] !== DEFAULT_STATUS_FILTER[s]);
|
|
63
|
+
|
|
61
64
|
const description = selectedEpic.description?.trim() || "";
|
|
62
65
|
const inlineSelect = `${fieldClasses()} !py-1 !px-2 !text-xs !min-h-0 !rounded-xl`;
|
|
63
66
|
const epicStatusTooltip = "Change this epic's overall status.";
|
|
@@ -79,7 +82,7 @@ function renderWorkspaceHeader(props) {
|
|
|
79
82
|
<label class="board-wh__control-label">
|
|
80
83
|
<span class="board-wh__control-text">Epic status</span>
|
|
81
84
|
<select class="${inlineSelect}" name="status" aria-label="Epic status" title="${escapeHtml(epicStatusTooltip)}" ${store.isMutating ? "disabled" : ""}>
|
|
82
|
-
${
|
|
85
|
+
${getSelectableStatuses(selectedEpic.status).map(s => `<option value="${escapeHtml(s)}" ${selectedEpic.status === s ? 'selected' : ''}>${escapeHtml(STATUS_LABELS[s] ?? s)}</option>`).join('')}
|
|
83
86
|
</select>
|
|
84
87
|
</label>
|
|
85
88
|
</form>
|
|
@@ -114,6 +117,14 @@ function renderWorkspaceHeader(props) {
|
|
|
114
117
|
<span class="${neutralChipClasses()}">${visibleTasks.length} visible</span>
|
|
115
118
|
${store.isMutating ? `<span class="${neutralChipClasses()}">Saving\u2026</span>` : ""}
|
|
116
119
|
</div>
|
|
120
|
+
<div class="board-filter-bar">
|
|
121
|
+
${STATUS_ORDER.map(status => {
|
|
122
|
+
const active = taskStatusFilter[status] !== false;
|
|
123
|
+
const count = selectedEpic.counts?.[status] ?? 0;
|
|
124
|
+
return `<button type="button" class="board-filter-pill ${active ? 'board-filter-pill--active' : 'board-filter-pill--inactive'} board-filter-pill--${status}" data-toggle-task-status-filter="${status}" aria-pressed="${active}" title="${active ? 'Hide' : 'Show'} ${STATUS_LABELS[status]} tasks">${STATUS_LABELS[status]} (${count})</button>`;
|
|
125
|
+
}).join('')}
|
|
126
|
+
${isTaskFilterNonDefault ? `<button type="button" class="board-filter-pill board-filter-pill--reset" data-reset-task-filter title="Reset filters to defaults">Reset</button>` : ''}
|
|
127
|
+
</div>
|
|
117
128
|
<div class="board-wh__actions">
|
|
118
129
|
<div class="board-wh__action-group">
|
|
119
130
|
<button type="button" class="board-copy-btn ${isCopied ? "board-copy-btn--active" : ""}" data-copy-epic-id="${escapeHtml(selectedEpic.id)}" aria-label="${escapeHtml(isCopied ? `Copied epic UUID for ${selectedEpic.title}` : copyLabel)}" title="${escapeHtml(isCopied ? "Epic UUID copied." : copyTooltip)}">
|
|
@@ -155,9 +166,12 @@ function renderWorkspaceHeader(props) {
|
|
|
155
166
|
// ---------------------------------------------------------------------------
|
|
156
167
|
|
|
157
168
|
function renderKanbanColumns(props) {
|
|
158
|
-
const { visibleTasks, selectedTaskId, isMutating } = props;
|
|
169
|
+
const { visibleTasks, selectedTaskId, isMutating, taskStatusFilter } = props;
|
|
170
|
+
const filter = taskStatusFilter || { ...DEFAULT_STATUS_FILTER };
|
|
159
171
|
|
|
160
|
-
const columnsMarkup = STATUS_ORDER
|
|
172
|
+
const columnsMarkup = STATUS_ORDER
|
|
173
|
+
.filter((status) => filter[status] !== false)
|
|
174
|
+
.map((status) => {
|
|
161
175
|
const columnTasks = visibleTasks.filter((t) => t.status === status);
|
|
162
176
|
const columnTitle = readStatusLabel(status);
|
|
163
177
|
const content = columnTasks.length === 0
|
|
@@ -265,19 +279,20 @@ function render(props) {
|
|
|
265
279
|
const headerMarkup = renderWorkspaceHeader({
|
|
266
280
|
selectedEpic,
|
|
267
281
|
snapshotEpics,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
282
|
+
store: {
|
|
283
|
+
copyFeedback: store.copyFeedback,
|
|
284
|
+
notesPanelOpen: store.notesPanelOpen,
|
|
285
|
+
isMutating: store.isMutating,
|
|
286
|
+
selectedEpicId: selectedEpic.id,
|
|
287
|
+
view: store.view,
|
|
274
288
|
viewModes,
|
|
289
|
+
taskStatusFilter: store.taskStatusFilter,
|
|
275
290
|
},
|
|
276
291
|
visibleTasks,
|
|
277
292
|
});
|
|
278
293
|
|
|
279
294
|
const contentMarkup = store.view === "kanban"
|
|
280
|
-
? renderKanbanColumns({ visibleTasks, selectedTaskId, isMutating: store.isMutating })
|
|
295
|
+
? renderKanbanColumns({ visibleTasks, selectedTaskId, isMutating: store.isMutating, taskStatusFilter: store.taskStatusFilter })
|
|
281
296
|
: renderListView({ visibleTasks, selectedTaskId });
|
|
282
297
|
|
|
283
298
|
return `
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeHtml, formatDate, normalizeStatus, STATUS_ORDER } from "../state/utils.js";
|
|
1
|
+
import { escapeHtml, formatDate, getSelectableStatuses, normalizeStatus, STATUS_ORDER } from "../state/utils.js";
|
|
2
2
|
|
|
3
3
|
export { escapeHtml, formatDate, normalizeStatus, STATUS_ORDER };
|
|
4
4
|
|
|
@@ -123,9 +123,10 @@ export function renderStatusBadge(rawStatus, label = readStatusLabel(rawStatus))
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
export function renderStatusSelect(name, selectedStatus, disabled = false) {
|
|
126
|
+
const statuses = getSelectableStatuses(selectedStatus);
|
|
126
127
|
return `
|
|
127
128
|
<select class="${fieldClasses()}" name="${escapeHtml(name)}" ${disabled ? "disabled" : ""}>
|
|
128
|
-
${
|
|
129
|
+
${statuses.map((status) => `
|
|
129
130
|
<option value="${escapeHtml(status)}" ${selectedStatus === status ? "selected" : ""}>${escapeHtml(STATUS_LABELS[status] ?? status)}</option>
|
|
130
131
|
`).join("")}
|
|
131
132
|
</select>
|