@jenga-ai/agent 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -78
- package/agents/developer.md +1 -1
- package/agents/scrum-master.md +20 -2
- package/agents/tester.md +3 -3
- package/hooks/on_session_end.sh +5 -5
- package/lib/generate-agent-context.js +2 -2
- package/lib/generate-copilot-hooks.js +1 -1
- package/lib/generate-skill-allow-list.js +37 -3
- package/lib/mirror.js +1 -1
- package/lib/postinstall-manifest.js +1 -1
- package/lib/skill-allow-list.json +2 -2
- package/mcp/help/index.js +8 -17
- package/mcp/help/scan.js +73 -0
- package/package.json +6 -1
- package/project/app/api/parsers/knowledge-graph.js +100 -9
- package/project/app/api/routes/health.js +36 -0
- package/project/app/api/scripts/capture-snapshot.js +9 -6
- package/project/app/ui/dist/assets/{index-CdK3Qrep.css → index-BVR_7Owg.css} +1 -1
- package/project/app/ui/dist/assets/index-CtU2xLQm.js +104 -0
- package/project/app/ui/dist/index.html +2 -2
- package/project/app/ui/package.json +4 -0
- package/project/app/ui/scripts/build-snapshot-html.cjs +63 -2
- package/scripts/acquire-concurrency-slot.sh +1 -1
- package/scripts/apply-j-prefix.sh +46 -5
- package/scripts/audit-twin-divergence.sh +73 -5
- package/scripts/build-pages-site.sh +1 -1
- package/scripts/check-public-playbook-steps.sh +158 -52
- package/scripts/check-publicignore-match.sh +2 -2
- package/scripts/compute-deploy-reconcile.sh +5 -5
- package/scripts/delete-bare-skill-dirs.sh +330 -0
- package/scripts/generate-legacy-shipped-paths.js +2 -2
- package/scripts/idea_manager.sh +258 -3
- package/scripts/mark-deployed.sh +2 -2
- package/scripts/populate-knowledge-graph.entity-resolution.test.js +254 -0
- package/scripts/populate-knowledge-graph.js +213 -5
- package/scripts/populate-knowledge-graph.staleness.test.js +130 -0
- package/scripts/postinstall.js +1 -1
- package/scripts/repoint-skill-refs.sh +539 -0
- package/scripts/todo_manager.sh +1 -1
- package/scripts/verify-legacy-seed-reconcile.sh +10 -10
- package/scripts/verify-postinstall-reconcile.sh +7 -7
- package/scripts/write-context-digest.sh +1 -1
- package/skills/j-clearify/SKILL.md +2 -2
- package/skills/j-close-story/scripts/check-privatized.sh +4 -4
- package/skills/j-distribute/CONFIG_SCHEMA.md +82 -5
- package/skills/j-do/SKILL.md +101 -17
- package/skills/j-doc-sync/SKILL.md +1 -0
- package/skills/j-gitignore/SKILL.md +157 -0
- package/skills/j-gitignore/assets/jenga-paths.txt +50 -0
- package/skills/j-gitignore/scripts/_catalog.sh +105 -0
- package/skills/j-gitignore/scripts/audit-gitignore.sh +194 -0
- package/skills/j-gitignore/scripts/repair-gitignore.sh +226 -0
- package/skills/j-gitignore/scripts/untrack-jenga-files.sh +210 -0
- package/skills/j-idea/SKILL.md +78 -6
- package/skills/j-idea/assets/idea_template.md +1 -1
- package/skills/j-improve/SKILL.md +1 -1
- package/skills/j-init/SKILL.md +53 -14
- package/skills/j-init/assets/.gitignore_template +1 -2
- package/skills/j-init/assets/scope-thresholds_template.json +5 -2
- package/skills/j-init/scripts/apply-scaffold-visibility.sh +192 -0
- package/skills/j-init/scripts/init.sh +22 -8
- package/skills/j-playbook/SKILL.md +1 -1
- package/skills/j-publish/SKILL.md +1 -1
- package/skills/j-publish/adapters/npm-ci.md +6 -1
- package/skills/j-publish/adapters/npm.md +1 -1
- package/skills/j-publish/scripts/generate_release_notes.sh +1 -1
- package/skills/j-publish/scripts/npm_stage_inspect.sh +61 -0
- package/skills/j-reconcile/SKILL.md +2 -2
- package/skills/j-reconcile/scripts/detect-unlinked-code.sh +11 -11
- package/skills/j-redo/SKILL.md +1 -1
- package/skills/j-skillify/assets/init-new/assets/.gitignore_template +1 -2
- package/skills/j-spinoff/SKILL.md +1 -1
- package/skills/j-status/SKILL.md +15 -0
- package/skills/j-todo/SKILL.md +3 -1
- package/skills/j-uncharted/SKILL.md +55 -8
- package/skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md +69 -0
- package/skills/j-uncharted/scripts/detect-dependencies.sh +1 -1
- package/skills/j-uncharted/scripts/detect-tests.sh +1 -1
- package/skills/j-uncharted/scripts/elicitation-state.sh +46 -8
- package/skills/j-uncharted/scripts/validate-proposed-items.sh +1 -1
- package/skills/j-wtf/SKILL.md +1 -1
- package/skills/jenga/SKILL.md +43 -9
- package/skills/jenga/playbooks/board-hygiene.json +32 -0
- package/skills/jenga/playbooks/schema.json +73 -6
- package/skills/jenga/playbooks/understand-then-commit.json +19 -0
- package/skills/jenga/scripts/load-nl-catalog.sh +1 -1
- package/skills/jenga/scripts/load-playbooks.sh +23 -11
- package/skills/jenga/scripts/match-playbook.sh +1 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +14 -1
- package/templates/SKILL_TEMPLATE.md +12 -0
- package/templates/permission-levels/level-4-elevated.json +1 -1
- package/templates/permission-levels/level-5-unrestricted.json +1 -1
- package/templates/playbook-types.json +34 -6
- package/project/app/ui/dist/assets/index-7fj-vllY.js +0 -104
- package/scripts/generate-j-alias.sh +0 -333
- package/skills/j-dev-done/SKILL.md +0 -53
- package/skills/j-dev-done/scripts/classify-commit-outcome.sh +0 -114
package/README.md
CHANGED
|
@@ -31,6 +31,34 @@ Jenga AI solves each of these with structure: persistent engineering context mai
|
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
34
|
+
## How Jenga AI's Agentic Workflow Works
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
j.init → j.pi-plan → j.todo → j.do
|
|
38
|
+
│
|
|
39
|
+
Developer agent
|
|
40
|
+
(isolated worktree, commits)
|
|
41
|
+
│
|
|
42
|
+
Tester agent
|
|
43
|
+
(validates, updates board status)
|
|
44
|
+
│
|
|
45
|
+
SessionEnd hook
|
|
46
|
+
(writes triggers to queue)
|
|
47
|
+
│
|
|
48
|
+
Scrum Master (next session)
|
|
49
|
+
(processes queue, rollups, unblocks)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
| Mechanism | Location | Purpose |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| Scrum board | `project/board/` | Epics, stories, tasks with structured frontmatter |
|
|
55
|
+
| Trigger queue | `project/queue/scrum_triggers.jsonl` | Async handoff to Scrum Master |
|
|
56
|
+
| Event log | `project/logs/events.json` | Append-only audit trail |
|
|
57
|
+
| Rapport system | `project/rapports/` | Problem/analysis reports by Developer and Tester |
|
|
58
|
+
| File locking | `<file>.lock` adjacent to board files | Concurrency control for parallel agents |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
34
62
|
## What You Get
|
|
35
63
|
|
|
36
64
|
- **Three specialised agents** — Scrum Master, Developer, Tester — each with a distinct role and no self-graded work
|
|
@@ -60,83 +88,6 @@ The Tester doesn't read your code and form an opinion about it. It executes the
|
|
|
60
88
|
|
|
61
89
|
---
|
|
62
90
|
|
|
63
|
-
## Examples
|
|
64
|
-
|
|
65
|
-
### A Real Session, On This Repo
|
|
66
|
-
|
|
67
|
-
Not a demo — this is what actually produced the section you're reading.
|
|
68
|
-
|
|
69
|
-
The conversation starts with a request to align this README's language and content with a positioning plan drafted for this epic (E41):
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
j.improve E41: I want to make some adjustments to the README in both
|
|
73
|
-
language as well as content to better match [the E41 positioning plan]
|
|
74
|
-
[...]
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
That plan already existed, so the copy gets edited directly — no need to re-run a fresh analysis pass just to re-derive it. A few edits later, ten more issues surface, one with an `j.brainstorm` request attached:
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
Then do a j.todo on this, as there are more places that should be
|
|
81
|
-
edited based on that scrutiny rapport:
|
|
82
|
-
* Remove warning banner about Copilot conflict
|
|
83
|
-
[...]
|
|
84
|
-
* Fewer, better and more relatable examples, we should
|
|
85
|
-
j.brainstorm this so I can send examples from real world usecases
|
|
86
|
-
[...]
|
|
87
|
-
* Should we really display the entire Skill list here?
|
|
88
|
-
[...]
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
`j.todo` classifies the ten items into two stories: nine mechanical README fixes (`E41_S10`), and the Examples section you're reading right now (`E41_S11`) — carved out and held back specifically because of that `j.brainstorm` request, since a real example had to come from the user, not be invented.
|
|
92
|
-
|
|
93
|
-
Once `E41_S10` is on the board, it's picked to run first. The Scrum Master reads the story and decomposes it into seven tasks, executed through `j.do`. Five are small, single-file text fixes deemed `--trivial` — they run inline, smoke-tested and committed directly, no worktree needed. Two need an editorial call — one of them being which skills are "foundational" enough to stay inline — those escalate to a real git worktree with a Developer and a Tester. The Tester independently re-verifies the diff, catches one file the Developer missed, fixes it, and queues the story for rollup. The Scrum Master processes that queue next: `E41_S10` → `Passed`. Epic `E41` stays `In Progress` — three other stories are still open.
|
|
94
|
-
|
|
95
|
-
Then, circling back to the deferred request:
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
Jenga AI: [...] Remaining open thread from this session: E41_S11
|
|
99
|
-
(Examples rework) is on the board but not started — it's gated on
|
|
100
|
-
a j.brainstorm session where you supply real-world use cases. Want
|
|
101
|
-
to do that now, or leave it queued?
|
|
102
|
-
|
|
103
|
-
You: do it now
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
`j.brainstorm` opens for the gated story — the session you're reading right now, working out what this example should even say.
|
|
107
|
-
|
|
108
|
-
Nobody hand-wrote this task breakdown. The board did it, the routing rules decided what needed a real worktree versus what could run inline, and the Tester — not the agent that made the edits — decided when each piece was actually done. The board entries are real: `E41_S10` and `E41_S11`.
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## How Jenga AI's Agentic Workflow Works
|
|
113
|
-
|
|
114
|
-
```
|
|
115
|
-
j.init → j.pi-plan → j.todo → j.do
|
|
116
|
-
│
|
|
117
|
-
Developer agent
|
|
118
|
-
(isolated worktree, commits)
|
|
119
|
-
│
|
|
120
|
-
Tester agent
|
|
121
|
-
(validates, updates board status)
|
|
122
|
-
│
|
|
123
|
-
SessionEnd hook
|
|
124
|
-
(writes triggers to queue)
|
|
125
|
-
│
|
|
126
|
-
Scrum Master (next session)
|
|
127
|
-
(processes queue, rollups, unblocks)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
| Mechanism | Location | Purpose |
|
|
131
|
-
|---|---|---|
|
|
132
|
-
| Scrum board | `project/board/` | Epics, stories, tasks with structured frontmatter |
|
|
133
|
-
| Trigger queue | `project/queue/scrum_triggers.jsonl` | Async handoff to Scrum Master |
|
|
134
|
-
| Event log | `project/logs/events.json` | Append-only audit trail |
|
|
135
|
-
| Rapport system | `project/rapports/` | Problem/analysis reports by Developer and Tester |
|
|
136
|
-
| File locking | `<file>.lock` adjacent to board files | Concurrency control for parallel agents |
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
91
|
## Agents
|
|
141
92
|
|
|
142
93
|
| Agent | Role | Owns |
|
|
@@ -161,6 +112,13 @@ Each agent is defined in `.agents/agents/`. They communicate exclusively through
|
|
|
161
112
|
npm install -g @jenga-ai/agent
|
|
162
113
|
```
|
|
163
114
|
|
|
115
|
+
> **pnpm or `--ignore-scripts`?** Jenga's install relies on a `postinstall` lifecycle script to
|
|
116
|
+
> mirror `skills/`/`agents/` into your project — if your package manager blocks lifecycle
|
|
117
|
+
> scripts by default (pnpm v8+) or you install with `--ignore-scripts`/`ignore-scripts=true`,
|
|
118
|
+
> the install will silently produce none of the framework files with no error shown. See
|
|
119
|
+
> [`docs/distribution.md`](docs/distribution.md#prerequisite--lifecycle-scripts-must-be-allowed-to-run-e26_s09)
|
|
120
|
+
> for the exact npm/pnpm opt-in step.
|
|
121
|
+
|
|
164
122
|
Or clone directly:
|
|
165
123
|
|
|
166
124
|
1. **Clone or copy this repo** into your project's root.
|
|
@@ -251,7 +209,7 @@ j.brainstorm → j.todo → j.do → j.commit
|
|
|
251
209
|
|
|
252
210
|
Calling `j.playbook` with no id prints a table of every available playbook (id, name, and steps) instead of resolving one.
|
|
253
211
|
|
|
254
|
-
Nothing executes until you confirm the chain, and any step can be unchecked first. `
|
|
212
|
+
Nothing executes until you confirm the chain, and any step can be unchecked first. `understand-then-commit` prepends `j.uncharted` investigation for unfamiliar code before running that same pipeline — useful when the change touches code you don't fully understand yet. No built-in playbook publishes or mirrors on your behalf: build chains terminate at `j.commit`, and publishing stays an explicit, separately invoked act.
|
|
255
213
|
|
|
256
214
|
When a step forwards its result into the next one, that value has a declared **output type** (a plain string, a list of board IDs, a list of files) so the chain can be validated before it runs. See [Getting Started](https://samwelmunga.github.io/jenga-npm/getting-started.html#how-playbooks-know-what-a-skill-produces) for how that works.
|
|
257
215
|
|
|
@@ -259,6 +217,55 @@ Want your own recurring chain? `j.playbook-new` walks you through authoring one
|
|
|
259
217
|
|
|
260
218
|
---
|
|
261
219
|
|
|
220
|
+
## Example
|
|
221
|
+
|
|
222
|
+
### A Real Session, On This Repo
|
|
223
|
+
|
|
224
|
+
Not a demo — this is what actually produced the section you're reading.
|
|
225
|
+
|
|
226
|
+
The conversation starts with a request to align this README's language and content with a positioning plan drafted for this epic (E41):
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
j.improve E41: I want to make some adjustments to the README in both
|
|
230
|
+
language as well as content to better match [the E41 positioning plan]
|
|
231
|
+
[...]
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
That plan already existed, so the copy gets edited directly — no need to re-run a fresh analysis pass just to re-derive it. A few edits later, ten more issues surface, one with an `j.brainstorm` request attached:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
Then do a j.todo on this, as there are more places that should be
|
|
238
|
+
edited based on that scrutiny rapport:
|
|
239
|
+
* Remove warning banner about Copilot conflict
|
|
240
|
+
[...]
|
|
241
|
+
* Fewer, better and more relatable examples, we should
|
|
242
|
+
j.brainstorm this so I can send examples from real world usecases
|
|
243
|
+
[...]
|
|
244
|
+
* Should we really display the entire Skill list here?
|
|
245
|
+
[...]
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`j.todo` classifies the ten items into two stories: nine mechanical README fixes (`E41_S10`), and the Examples section you're reading right now (`E41_S11`) — carved out and held back specifically because of that `j.brainstorm` request, since a real example had to come from the user, not be invented.
|
|
249
|
+
|
|
250
|
+
Once `E41_S10` is on the board, it's picked to run first. The Scrum Master reads the story and decomposes it into seven tasks, executed through `j.do`. Five are small, single-file text fixes deemed `--trivial` — they run inline, smoke-tested and committed directly, no worktree needed. Two need an editorial call — one of them being which skills are "foundational" enough to stay inline — those escalate to a real git worktree with a Developer and a Tester. The Tester independently re-verifies the diff, catches one file the Developer missed, fixes it, and queues the story for rollup. The Scrum Master processes that queue next: `E41_S10` → `Passed`. Epic `E41` stays `In Progress` — three other stories are still open.
|
|
251
|
+
|
|
252
|
+
Then, circling back to the deferred request:
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
Jenga AI: [...] Remaining open thread from this session: E41_S11
|
|
256
|
+
(Examples rework) is on the board but not started — it's gated on
|
|
257
|
+
a j.brainstorm session where you supply real-world use cases. Want
|
|
258
|
+
to do that now, or leave it queued?
|
|
259
|
+
|
|
260
|
+
You: do it now
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`j.brainstorm` opens for the gated story — the session you're reading right now, working out what this example should even say.
|
|
264
|
+
|
|
265
|
+
Nobody hand-wrote this task breakdown. The board did it, the routing rules decided what needed a real worktree versus what could run inline, and the Tester — not the agent that made the edits — decided when each piece was actually done. The board entries are real: `E41_S10` and `E41_S11`.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
262
269
|
## When to Use Jenga AI
|
|
263
270
|
|
|
264
271
|
**Use it when:**
|
package/agents/developer.md
CHANGED
|
@@ -215,7 +215,7 @@ This list is fixed and verbatim across both this file and `agents/tester.md` —
|
|
|
215
215
|
|
|
216
216
|
**Effect — forced inline scope.** `execution_scope` is force-set to `inline` for any `locked` task, overriding whatever scope `j.jenga`'s Execution Scope Assignment heuristics would otherwise assign — or auto-correcting a wrong value in place, with a logged `override_justification` note explaining the correction. The concrete mechanism is `skills/jenga/SKILL.md` Phase 0.5's **Rule 4 — `crucial_level: locked` forces `execution_scope: inline`** (added by E39_S03_T03).
|
|
217
217
|
|
|
218
|
-
**Effect — dispatch-time rejection of backgrounding.** A `locked` task can never be routed to a background subagent, a worktree-isolated session, or a bundled `j.jenga` story-batch execution, regardless of what its `execution_scope` value currently reads. This is enforced at two separate points, both added by E39_S03_T04: `skills/jenga/SKILL.md` Phase 3.5 step 5's **Guard: locked-task disqualifier (defense-in-depth)**, which disqualifies any story containing a `locked` task from the bundle path before dispatch, and `skills/do/SKILL.md` Section 4.2's **Locked-task dispatch guard (defense-in-depth)**, which forces the inline execution path (no worktree, no developer subagent) at the point of dispatch even if `execution_scope` somehow still reads something other than `inline`.
|
|
218
|
+
**Effect — dispatch-time rejection of backgrounding.** A `locked` task can never be routed to a background subagent, a worktree-isolated session, or a bundled `j.jenga` story-batch execution, regardless of what its `execution_scope` value currently reads. This is enforced at two separate points, both added by E39_S03_T04: `skills/jenga/SKILL.md` Phase 3.5 step 5's **Guard: locked-task disqualifier (defense-in-depth)**, which disqualifies any story containing a `locked` task from the bundle path before dispatch, and `skills/j-do/SKILL.md` Section 4.2's **Locked-task dispatch guard (defense-in-depth)**, which forces the inline execution path (no worktree, no developer subagent) at the point of dispatch even if `execution_scope` somehow still reads something other than `inline`.
|
|
219
219
|
|
|
220
220
|
**No agent-discretion obligation.** Unlike `advisory` (a reporting-cadence habit you must remember to keep up) and `gated` (a confirmation you must actively pause and perform), `locked` requires no judgment call from you at all. It is fully enforced by pre-flight validation (Rule 4) and dispatch-time guards (the Phase 3.5 and `j.do` guards above) before you ever begin work on the task — there is no step in this tier that depends on you noticing or remembering anything. Your only obligation is to recognize that a `locked` task will always run in the current foreground session, and to never manually route around that guarantee — for example, do not spin up your own background subagent or a separate worktree-isolated session to "help" with a `locked` task, even if it seems more efficient. If a locked task ever reaches you already running in a background or worktree-isolated context, treat that as a guard failure worth flagging (see Rapport System), not something to quietly work through.
|
|
221
221
|
|
package/agents/scrum-master.md
CHANGED
|
@@ -74,10 +74,28 @@ This is a self-contained procedure, not a session-start-only step. It may be inv
|
|
|
74
74
|
4. **Reject branch** — if the reason is generic or non-concrete, do not write any of `crucial_level` / `crucial_set_by` / `crucial_note` to the target item. Instead, decline the escalation using the same `.IGNORE.md` convention already documented in `agents/tester.md`'s "IGNORE.md — skipping resolved rapports" section: rename the rapport file to `<name>.IGNORE.md` and append an Ignore Log entry stating the escalation was declined for lacking a concrete reason. This keeps the declined rapport from being silently re-surfaced as a fresh `rapport_review` trigger on a future `on_session_end.sh` scan, since that scan's new-rapport detection skips `*.IGNORE.md` files.
|
|
75
75
|
5. **Report back** — in both branches, name the target item and the decision made (accepted at tier X with `crucial_set_by`/`crucial_note` set, or declined for lacking a concrete reason) as part of the existing "Report to the user" step below (Session Start — Queue Processing, item 3); no separate reporting step is needed.
|
|
76
76
|
6. **This is the only path** by which a mid-task agent request results in a `crucial_level` board write. Developer and tester never write `crucial_level`, `crucial_set_by`, or `crucial_note` directly to a board file themselves under any circumstance — they may only *request* the change via a `crucial_escalation` rapport, and the actual frontmatter write happens here, exclusively by scrum-master, closing the loop described in E39's Purpose section ("the actual frontmatter write still goes through scrum-master, never the subagent itself").
|
|
77
|
+
- **Rapport-sourced idea/topic detection (E35_S03_T02) — runs for every rapport processed under `rapport_review`, regardless of `Type`, in addition to (never instead of) the backlog-or-`Failed` handling above and the `crucial_escalation` special case above.** For each rapport, apply one further LLM judgment: does this rapport surface a distinct idea or topic not already captured in an existing epic/story/task or `project/ideas.md` entry? This mirrors the "Crucial Level Heuristic Proposal" propose-then-confirm shape (evaluation → proposal → same-session confirmation → write, or decline → record and suppress re-proposal) documented later in this file — read that section for the pattern this step reuses.
|
|
78
|
+
1. **Skip check — already-handled rapports.** Before evaluating a rapport for this step, check both of the following. If either is true, skip idea-detection for this rapport entirely and move on to the next rapport (this does not affect the rapport's normal backlog-or-`Failed` or `crucial_escalation` handling, which always runs regardless):
|
|
79
|
+
- `project/ideas.md` already contains a line tagged `<!-- rapport: <this rapport's file path> -->` — a rapport-sourced idea from this rapport was already confirmed and captured in a prior session, per the source-rapport link convention `skills/j-idea/SKILL.md` documents (E35_S03_T01).
|
|
80
|
+
- The rapport file itself already contains an `## Idea Detection Log` section (written by the decline branch below) — a proposal for this rapport was already made and declined in a prior session.
|
|
81
|
+
2. **Evaluate** — read the rapport's Summary, Context, and Problem Description sections and judge whether they surface a distinct idea or topic genuinely not already tracked anywhere on the board (`project/board/epics/`, `project/board/stories/`, `project/board/tasks/`) or in `project/ideas.md`. If nothing distinct surfaces, do nothing further for this rapport under this step.
|
|
82
|
+
3. **Propose** — if a distinct idea is found, state the proposed idea text to the user in-session and ask for confirmation. Do not add anything to `project/ideas.md` without it. Apply the same same-session-only discipline as the Confirm-Before-Write Gate below: if the session ends before the user confirms, the proposal is dropped — not persisted, not carried forward — and the rapport is simply re-evaluated fresh (subject to the skip check above) on a future `rapport_review` pass.
|
|
83
|
+
4. **On confirm** — append the idea to `project/ideas.md` using the exact same mechanism `j.idea` uses: `bash "$([ -f scripts/idea_manager.sh ] && echo scripts/idea_manager.sh || echo node_modules/@jenga-ai/agent/scripts/idea_manager.sh)" add '<idea text> <!-- rapport: <path to this rapport> -->'` — untagged (no `PROMOTED`/`REJECTED` at capture time), with the source-rapport link recorded via the `<!-- rapport: <path> -->` inline-comment convention already defined in `skills/j-idea/SKILL.md`'s "Source-Rapport Link Convention" subsection. Scrum-master does **not** author a board file directly from this step — the idea stays in `project/ideas.md` until separately promoted, exactly like any other `j.idea` entry (promotion is re-running `j.brainstorm` on it, per `skills/j-idea/SKILL.md`).
|
|
84
|
+
5. **On decline** — do not add anything to `project/ideas.md`. Instead, append the following section to the rapport file itself (a new, distinctly-named section — do **not** reuse the rapport template's existing `## Ignore Log` section, which is reserved for fully resolving/superseding the whole rapport via the `.IGNORE.md` rename convention; a declined idea proposal does not mean the rapport's underlying problem is resolved, so its normal backlog-or-`Failed`/`crucial_escalation` handling must be unaffected):
|
|
85
|
+
```
|
|
86
|
+
## Idea Detection Log
|
|
87
|
+
_Populated by scrum-master when a rapport-derived idea proposal is declined by the user._
|
|
88
|
+
|
|
89
|
+
**Declined by:** User
|
|
90
|
+
**Date:** YYYY-MM-DD (UTC)
|
|
91
|
+
**Proposed idea:** <one-line idea text that was proposed>
|
|
92
|
+
```
|
|
93
|
+
This is the same non-re-proposal guarantee as `crucial_declined` (see "Decline Handling" under "Crucial Level Heuristic Proposal" below), adapted for a rapport file, which carries no frontmatter of its own to hold a boolean flag: the presence of this section is exactly what the skip check in step 1 above looks for, so a declined proposal is never re-surfaced on a future `rapport_review` pass over the same rapport. A decline here is scoped to this one rapport only — it never suppresses idea-detection evaluation on any other rapport, including similar ones.
|
|
94
|
+
6. **Report back** — name any rapports where an idea was proposed and the outcome (captured to `project/ideas.md`, or declined) as part of the existing "Report to the user" step below (Session Start — Queue Processing, item 3); no separate reporting step is needed.
|
|
77
95
|
- `status_review`: Review the scrum board for any tasks or stories whose status should be updated based on recent activity.
|
|
78
96
|
- `story_rollup`: Check all tasks under the referenced story; if all are `Passed` or `Passed with remarks`, update the story status to `Passed` (or `Passed with remarks` if any remark exists). Then check epic rollup (see Rollup Logic).
|
|
79
|
-
- `elicitation_resume`: A `j.uncharted` conversational architecture elicitation session (`onboard`'s default flow, or `segment --mode investigate` — E20_S08_T03) ended mid-run without converging. Read `state_file` (`project/queue/elicitation-state/<elicitation_id>.json`, written by `skills/uncharted/scripts/elicitation-state.sh`) to see exactly where it left off — which nodes already converged, which are still pending or flagged, and any directory-triage/checkpoint data already confirmed — then resume the conversational flow documented in `skills/uncharted/SKILL.md`'s Multi-Session Persistence subsection from that point rather than restarting the elicitation from scratch. If the state file is missing or unreadable, report that to the user rather than silently starting a fresh elicitation under the same id.
|
|
80
|
-
- `capacity_starvation`: `skills/do/SKILL.md`'s per-session concurrency cap (E32_S15) blocked the same board item for 3 consecutive dispatch waves because its role (`developer` or `tester`) stayed at cap. Surface a plain warning naming the affected item and its consecutive-block wave count, suggesting the configured cap (`max_concurrent_developers` / `max_concurrent_testers` in `project/configs/scope-thresholds.json`) may be too low or the session may be starved. No automatic remediation — this trigger is informational only.
|
|
97
|
+
- `elicitation_resume`: A `j.uncharted` conversational architecture elicitation session (`onboard`'s default flow, or `segment --mode investigate` — E20_S08_T03) ended mid-run without converging. Read `state_file` (`project/queue/elicitation-state/<elicitation_id>.json`, written by `skills/j-uncharted/scripts/elicitation-state.sh`) to see exactly where it left off — which nodes already converged, which are still pending or flagged, and any directory-triage/checkpoint data already confirmed — then resume the conversational flow documented in `skills/j-uncharted/SKILL.md`'s Multi-Session Persistence subsection from that point rather than restarting the elicitation from scratch. If the state file is missing or unreadable, report that to the user rather than silently starting a fresh elicitation under the same id.
|
|
98
|
+
- `capacity_starvation`: `skills/j-do/SKILL.md`'s per-session concurrency cap (E32_S15) blocked the same board item for 3 consecutive dispatch waves because its role (`developer` or `tester`) stayed at cap. Surface a plain warning naming the affected item and its consecutive-block wave count, suggesting the configured cap (`max_concurrent_developers` / `max_concurrent_testers` in `project/configs/scope-thresholds.json`) may be too low or the session may be starved. No automatic remediation — this trigger is informational only.
|
|
81
99
|
- After processing all triggers, **clear the file** by writing an empty file — do not leave processed triggers.
|
|
82
100
|
|
|
83
101
|
2. **Check `project/queue/project_summary_updates.jsonl`** — If non-empty, review each proposed update and apply, revise, or reject it with a short note. Clear the file after processing.
|
package/agents/tester.md
CHANGED
|
@@ -167,7 +167,7 @@ Always include the sender object in the response.
|
|
|
167
167
|
|
|
168
168
|
**Trigger.** During steps 4-6 of "Invoked for test implementation and/or execution" above, you find a defect. Before defaulting to step 9's rapport path, classify it: is this **mechanical** (safe to fix in place) or does it **need a rapport** (a design decision or real risk is involved)? This classification is what determines which of the two remediation paths below you take — it is not optional bookkeeping, and it happens at the moment the defect is found, not retroactively.
|
|
169
169
|
|
|
170
|
-
**Motivating case.** `project/rapports/analysis/E42_S04-execution-overhead-postmortem.md` Finding 3: the tester found `skills/dev-done/scripts/classify-commit-outcome.sh` committed without its executable bit (`chmod +x`) plus a dead, unreachable error branch. Both were one-line-class fixes, but the only path available at the time was the full formal one — a `test_failure` rapport, a separate developer fix commit, a re-verification pass, and a second complete `j.self-sync` mirror run. That produced 3 of the task's 6 total commits for defects that needed no design judgment at all. This subsection exists so that pattern doesn't repeat.
|
|
170
|
+
**Motivating case.** `project/rapports/analysis/E42_S04-execution-overhead-postmortem.md` Finding 3: the tester found `skills/j-dev-done/scripts/classify-commit-outcome.sh` committed without its executable bit (`chmod +x`) plus a dead, unreachable error branch. Both were one-line-class fixes, but the only path available at the time was the full formal one — a `test_failure` rapport, a separate developer fix commit, a re-verification pass, and a second complete `j.self-sync` mirror run. That produced 3 of the task's 6 total commits for defects that needed no design judgment at all. This subsection exists so that pattern doesn't repeat.
|
|
171
171
|
|
|
172
172
|
**Qualifying examples (mechanical — fix in place).**
|
|
173
173
|
- A missing executable bit or other permission-bit error (e.g. a script committed without `chmod +x`) — the exact E42_S04 Finding 3 case.
|
|
@@ -260,7 +260,7 @@ Append this as a new array entry — never overwrite existing log content. This
|
|
|
260
260
|
|
|
261
261
|
**Effect — forced inline scope.** `execution_scope` is force-set to `inline` for any `locked` task, overriding whatever scope `j.jenga`'s Execution Scope Assignment heuristics would otherwise assign — or auto-correcting a wrong value in place, with a logged `override_justification` note. The concrete mechanism is `skills/jenga/SKILL.md` Phase 0.5's **Rule 4 — `crucial_level: locked` forces `execution_scope: inline`** (added by E39_S03_T03). As tester, verify this field is actually `inline` on any `locked` item you're validating — a value that slipped through would itself be a defect worth flagging.
|
|
262
262
|
|
|
263
|
-
**Effect — dispatch-time rejection of backgrounding.** A `locked` task can never be routed to a background subagent, a worktree-isolated session, or a bundled `j.jenga` story-batch execution, regardless of its `execution_scope` value. This is enforced at two points, both added by E39_S03_T04: `skills/jenga/SKILL.md` Phase 3.5 step 5's **Guard: locked-task disqualifier (defense-in-depth)** and `skills/do/SKILL.md` Section 4.2's **Locked-task dispatch guard (defense-in-depth)**. This matters directly to you as tester: you must never yourself dispatch, recommend, or improvise a background subagent, a separate worktree-isolated session, or a bundled batch run in order to verify a `locked` item faster or in parallel with other work — verification of a `locked` item happens in the same foreground session the guards already pinned it to, same as implementation.
|
|
263
|
+
**Effect — dispatch-time rejection of backgrounding.** A `locked` task can never be routed to a background subagent, a worktree-isolated session, or a bundled `j.jenga` story-batch execution, regardless of its `execution_scope` value. This is enforced at two points, both added by E39_S03_T04: `skills/jenga/SKILL.md` Phase 3.5 step 5's **Guard: locked-task disqualifier (defense-in-depth)** and `skills/j-do/SKILL.md` Section 4.2's **Locked-task dispatch guard (defense-in-depth)**. This matters directly to you as tester: you must never yourself dispatch, recommend, or improvise a background subagent, a separate worktree-isolated session, or a bundled batch run in order to verify a `locked` item faster or in parallel with other work — verification of a `locked` item happens in the same foreground session the guards already pinned it to, same as implementation.
|
|
264
264
|
|
|
265
265
|
**No agent-discretion obligation.** Unlike `advisory` (a reporting-cadence habit) and `gated` (a confirmation you must actively pause and perform), `locked` requires no judgment call from you. It is fully enforced by pre-flight validation (Rule 4) and dispatch-time guards (the Phase 3.5 and `j.do` guards above) before the developer ever begins work — none of this depends on you noticing or remembering anything mid-verification. Your only obligation is to recognize that a `locked` task always runs (and was always verified) in the current foreground session, and to never suggest or perform a workaround that would route around that guarantee. If you find evidence during verification that a `locked` item was actually run in a backgrounded or worktree-isolated context, treat that as a guard failure worth flagging (see Rapport System), not something to silently pass.
|
|
266
266
|
|
|
@@ -381,7 +381,7 @@ After every status update to a task or story, check whether a parent rollup is w
|
|
|
381
381
|
|
|
382
382
|
2. The scrum master processes rollup triggers from the queue at its next session start and updates story and epic statuses accordingly.
|
|
383
383
|
|
|
384
|
-
**Playbook step status is out of scope for this rollup (E53_S04_T03 audit).**
|
|
384
|
+
**Playbook step status is out of scope for this rollup (E53_S04_T03 audit).** `j.jenga` playbook
|
|
385
385
|
runs (`skills/jenga/scripts/run-playbook-step.sh`) track their own separate step-level status
|
|
386
386
|
vocabulary (`step_ready`/`complete`/`halted`, and per-step `passed`/`failed`/`skipped`) in an
|
|
387
387
|
ephemeral, session-local temp state file — never in `project/board/`. This rollup logic never
|
package/hooks/on_session_end.sh
CHANGED
|
@@ -128,8 +128,8 @@ if [ -d "$RAPPORT_DIR" ]; then
|
|
|
128
128
|
# Uses a portable `while read` loop rather than mapfile/readarray: macOS
|
|
129
129
|
# ships bash 3.2 (no mapfile support), and this hook must run there —
|
|
130
130
|
# matches the convention already established in scripts/smoke-harness.sh,
|
|
131
|
-
# skills/publish/scripts/generate_release_notes.sh, and
|
|
132
|
-
# skills/publish/scripts/finalize_changelog.sh. Fixed incidentally here
|
|
131
|
+
# skills/j-publish/scripts/generate_release_notes.sh, and
|
|
132
|
+
# skills/j-publish/scripts/finalize_changelog.sh. Fixed incidentally here
|
|
133
133
|
# because this task's acceptance criteria require the hook to actually
|
|
134
134
|
# execute end-to-end (mapfile silently failed on stock macOS bash,
|
|
135
135
|
# leaving CURRENT_FILES empty and masking real detection results).
|
|
@@ -312,9 +312,9 @@ for HANDOFF_FILE in "$HANDOFF_DIR"/*.json; do
|
|
|
312
312
|
# A conversational architecture elicitation session (/uncharted
|
|
313
313
|
# onboard's default flow, or segment --mode investigate — E20_S08_T03)
|
|
314
314
|
# ended mid-run without converging. The session driving it is
|
|
315
|
-
# responsible for calling skills/uncharted/scripts/elicitation-state.sh
|
|
315
|
+
# responsible for calling skills/j-uncharted/scripts/elicitation-state.sh
|
|
316
316
|
# pause and then writing this handoff with status "elicitation_paused"
|
|
317
|
-
# as its last action (see skills/uncharted/SKILL.md's Multi-Session
|
|
317
|
+
# as its last action (see skills/j-uncharted/SKILL.md's Multi-Session
|
|
318
318
|
# Persistence subsection). This routes that pause into a resume
|
|
319
319
|
# signal for the next scrum-master session, per the existing
|
|
320
320
|
# SessionEnd/queue pattern rather than a new persistence mechanism
|
|
@@ -425,4 +425,4 @@ bash "$PROJECT_DIR/scripts/todo_cleanup.sh"
|
|
|
425
425
|
# digest's consumer is a later session that may not have started yet when
|
|
426
426
|
# THIS session ends — see scripts/sweep-stale-context-digests.sh's header for
|
|
427
427
|
# the full rationale. Runs unconditionally, same as todo cleanup above.
|
|
428
|
-
bash "$PROJECT_DIR/scripts/sweep-stale-context-digests.sh"
|
|
428
|
+
bash "$PROJECT_DIR/scripts/sweep-stale-context-digests.sh"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Single source of truth for scaffolding the two root-level agent-context
|
|
6
6
|
* files from templates/agent-context.md.tpl (E41_S04_T02). Used by:
|
|
7
7
|
* - lib/commands/init.js (published `jenga init` CLI)
|
|
8
|
-
* - skills/init/scripts/init.sh (this repo's own board-scaffolding flow,
|
|
8
|
+
* - skills/j-init/scripts/init.sh (this repo's own board-scaffolding flow,
|
|
9
9
|
* invoked via `node` — see the CLI guard
|
|
10
10
|
* at the bottom of this file)
|
|
11
11
|
*
|
|
@@ -234,7 +234,7 @@ export function generateAgentContext(projectRoot = process.cwd(), packageRoot =
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
// CLI guard — allows `node lib/generate-agent-context.js [projectRoot]`,
|
|
237
|
-
// used by skills/init/scripts/init.sh (this repo's own board-scaffolding
|
|
237
|
+
// used by skills/j-init/scripts/init.sh (this repo's own board-scaffolding
|
|
238
238
|
// flow, where node is guaranteed available). The published npm CLI path
|
|
239
239
|
// (lib/commands/init.js) imports generateAgentContext() directly instead.
|
|
240
240
|
//
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* is no legitimate reason for a consumer to hand-edit it, so a full deterministic overwrite on
|
|
31
31
|
* every run is safe and simpler than a marker-merge.
|
|
32
32
|
*
|
|
33
|
-
* No `skills/self-sync/scripts/run.js` wiring is needed either: self-sync mirrors root-level
|
|
33
|
+
* No `skills/j-self-sync/scripts/run.js` wiring is needed either: self-sync mirrors root-level
|
|
34
34
|
* source directories, and this file has none to mirror — it is generated directly, exactly like
|
|
35
35
|
* `.github/copilot-instructions.md` is (also outside self-sync's `COPY_SET`/`GITHUB_COPY_SET`).
|
|
36
36
|
*
|
|
@@ -19,10 +19,26 @@
|
|
|
19
19
|
* Auto-regeneration at `/self-sync`/postinstall time is wired up separately by the follow-up
|
|
20
20
|
* task E50_S02_T02 — not in scope here.
|
|
21
21
|
*
|
|
22
|
+
* ## The three-way mapping (E50_S12_T01)
|
|
23
|
+
*
|
|
24
|
+
* This generator's correctness rests on a relationship that used to be left implicit in
|
|
25
|
+
* `extractName`'s regex. Stated explicitly, per `docs/skill-authoring.md`'s "The Canonical Naming
|
|
26
|
+
* Contract" table (settled 2026-09-09, transcribed from E50_S10):
|
|
27
|
+
*
|
|
28
|
+
* skills/j-<name>/ (directory) <-> name: j.<name> (frontmatter) <-> <name> (allow-listed
|
|
29
|
+
* identifier, after extractName's /^j[.:]/i strip)
|
|
30
|
+
*
|
|
31
|
+
* Three permanent exceptions skip the `j-` prefix on all three legs of that mapping and keep their
|
|
32
|
+
* bare form everywhere: `jenga`, `jenga-permission-level` (both deliberately excluded from twin
|
|
33
|
+
* generation, E50_S06), and `index` (not a skill — no SKILL.md, not part of routing). Every other
|
|
34
|
+
* skill under `skills/` is expected to satisfy the mapping above once E50_S15 lands (see that
|
|
35
|
+
* story's frontmatter-rewrite scope) — see the note below on the transitional state this generator
|
|
36
|
+
* currently has to tolerate.
|
|
37
|
+
*
|
|
22
38
|
* Consumers:
|
|
23
39
|
* - CLI guard at the bottom of this file (`node lib/generate-skill-allow-list.js`) — run once
|
|
24
40
|
* against this repo's own skills/ to produce the committed lib/skill-allow-list.json, and
|
|
25
|
-
* intended to be callable from skills/self-sync/scripts/run.js and scripts/postinstall.js in
|
|
41
|
+
* intended to be callable from skills/j-self-sync/scripts/run.js and scripts/postinstall.js in
|
|
26
42
|
* the follow-up task.
|
|
27
43
|
* - getSkillAllowListIdentifiers() — an in-memory-only helper for callers that want the
|
|
28
44
|
* identifier list without touching disk, e.g. the MCP router work in E50_S02_T03.
|
|
@@ -48,6 +64,24 @@ const DEFAULT_PACKAGE_ROOT = join(__dirname, "..");
|
|
|
48
64
|
* is needed here (unlike mcp/router/skill-index.js's fuller frontmatter parser, which also
|
|
49
65
|
* handles array fields like `keywords`/`examples`) — a direct regex against the frontmatter
|
|
50
66
|
* block is sufficient and avoids re-implementing that broader parser for a single field.
|
|
67
|
+
*
|
|
68
|
+
* This is the function that produces one leg of the three-way mapping described in this file's
|
|
69
|
+
* header (`directory <-> frontmatter name: <-> allow-listed identifier`): given a SKILL.md's raw
|
|
70
|
+
* frontmatter `name:` value, it returns the bare identifier that goes into the allow-list. For a
|
|
71
|
+
* normal skill (post-E50_S15) that value is `j.<name>` and this strips to `<name>`, matching the
|
|
72
|
+
* `skills/j-<name>/` directory it was read from with the `j-` prefix removed. For the three
|
|
73
|
+
* permanent exceptions (`jenga`, `jenga-permission-level`, `index`) the frontmatter carries no
|
|
74
|
+
* prefix at all and this is a no-op, matching their bare directory name directly.
|
|
75
|
+
*
|
|
76
|
+
* Transitional note (pre-E50_S15): as of E50_S12, most `skills/j-<name>/SKILL.md` twins still
|
|
77
|
+
* carry the doubled `name: j.j-<name>` form (E50_S15's frontmatter rewrite to `j.<name>` has not
|
|
78
|
+
* landed yet — that rewrite and the bare-directory deletion are scoped together and must land in
|
|
79
|
+
* the same change, per E50_S15's story file). Stripping a single leading `j[.:]` off a doubled
|
|
80
|
+
* value yields `j-<name>`, not `<name>` — a real, known, and separately-owned gap in the
|
|
81
|
+
* three-way mapping above, not a bug in this function's own contract. See
|
|
82
|
+
* `project/documentation/plans/E50_S12-plan.md` for why this task's regression coverage targets a
|
|
83
|
+
* synthetic twin-only tree (where the mapping already holds) rather than asserting a bijection
|
|
84
|
+
* against this repo's own transitional `skills/` tree.
|
|
51
85
|
*/
|
|
52
86
|
function extractName(content) {
|
|
53
87
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
@@ -116,7 +150,7 @@ export function getSkillAllowListIdentifiers(skillsDir = DEFAULT_SKILLS_DIR) {
|
|
|
116
150
|
|
|
117
151
|
/**
|
|
118
152
|
* Read the committed skill-allow-list.json artifact (produced by generateSkillAllowList /
|
|
119
|
-
* regenerated by postinstall.js and skills/self-sync/scripts/run.js) and return its `skills`
|
|
153
|
+
* regenerated by postinstall.js and skills/j-self-sync/scripts/run.js) and return its `skills`
|
|
120
154
|
* array. Resolution order mirrors resolveTemplatePath's packageRoot-then-projectRoot candidate
|
|
121
155
|
* order in lib/generate-agent-context.js and lib/generate-copilot-instructions.js: the artifact
|
|
122
156
|
* normally lives at `<packageRoot>/lib/skill-allow-list.json` (the installed jenga-agent
|
|
@@ -180,7 +214,7 @@ export function generateSkillAllowList(skillsDir = DEFAULT_SKILLS_DIR, outputPat
|
|
|
180
214
|
}
|
|
181
215
|
|
|
182
216
|
// CLI guard — allows `node lib/generate-skill-allow-list.js [skillsDir] [outputPath]`, intended
|
|
183
|
-
// to be callable from skills/self-sync/scripts/run.js and scripts/postinstall.js in the
|
|
217
|
+
// to be callable from skills/j-self-sync/scripts/run.js and scripts/postinstall.js in the
|
|
184
218
|
// follow-up auto-regeneration task (E50_S02_T02).
|
|
185
219
|
//
|
|
186
220
|
// process.argv[1] is compared via realpath, not as a raw string — see the identical comment
|
package/lib/mirror.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* (files or directories) from a source root into a destination root. Used
|
|
6
6
|
* by:
|
|
7
7
|
* - scripts/postinstall.js (consumer install; reconcileDeletes = false)
|
|
8
|
-
* - skills/self-sync/... (in-repo dev mirror; reconcileDeletes = true) [wired in T02]
|
|
8
|
+
* - skills/j-self-sync/... (in-repo dev mirror; reconcileDeletes = true) [wired in T02]
|
|
9
9
|
*
|
|
10
10
|
* Design constraints:
|
|
11
11
|
* - ESM, Node built-ins only (node:fs / node:fs/promises / node:path).
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
* "package_version": "3.0.1",
|
|
43
43
|
* "generated_at": "2026-09-07T00:00:00.000Z",
|
|
44
44
|
* "dest_root": ".agents",
|
|
45
|
-
* "paths": ["agents/developer.md", "skills/do/SKILL.md"]
|
|
45
|
+
* "paths": ["agents/developer.md", "skills/j-do/SKILL.md"]
|
|
46
46
|
* }
|
|
47
47
|
*
|
|
48
48
|
* - `paths` are relative to the destination root, POSIX-separated, deduped and
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated_at": "2026-09-
|
|
2
|
+
"generated_at": "2026-09-20T06:12:06.824Z",
|
|
3
3
|
"skill_count": 41,
|
|
4
4
|
"skills": [
|
|
5
5
|
"brainstorm",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"dashboard",
|
|
13
13
|
"dashboard-share",
|
|
14
14
|
"deep-dive",
|
|
15
|
-
"dev-done",
|
|
16
15
|
"distribute",
|
|
17
16
|
"do",
|
|
18
17
|
"doc",
|
|
@@ -21,6 +20,7 @@
|
|
|
21
20
|
"error",
|
|
22
21
|
"evaluate",
|
|
23
22
|
"examplify",
|
|
23
|
+
"gitignore",
|
|
24
24
|
"help",
|
|
25
25
|
"idea",
|
|
26
26
|
"improve",
|
package/mcp/help/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { resolve } from "path";
|
|
6
|
+
import { resolveSkillsDir, candidateSkillsDirs, listSkillFolders } from "./scan.js";
|
|
7
7
|
|
|
8
8
|
const server = new McpServer({
|
|
9
9
|
name: "help",
|
|
@@ -23,15 +23,13 @@ server.tool(
|
|
|
23
23
|
},
|
|
24
24
|
async ({ path: inputPath }) => {
|
|
25
25
|
const root = inputPath ? resolve(inputPath) : process.cwd();
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
join(root, ".agents", "skills"),
|
|
31
|
-
];
|
|
32
|
-
const skillsDir = candidates.find(existsSync);
|
|
26
|
+
// E50_S12_T06: the actual scan logic lives in ./scan.js (resolveSkillsDir /
|
|
27
|
+
// listSkillFolders) so it is importable and unit-testable without starting this
|
|
28
|
+
// MCP server. See that module's header for why. No behavior change here.
|
|
29
|
+
const skillsDir = resolveSkillsDir(root);
|
|
33
30
|
|
|
34
31
|
if (!skillsDir) {
|
|
32
|
+
const candidates = candidateSkillsDirs(root);
|
|
35
33
|
return {
|
|
36
34
|
content: [
|
|
37
35
|
{
|
|
@@ -42,14 +40,7 @@ server.tool(
|
|
|
42
40
|
};
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
const
|
|
46
|
-
const folders = entries.filter((entry) => {
|
|
47
|
-
try {
|
|
48
|
-
return statSync(join(skillsDir, entry)).isDirectory();
|
|
49
|
-
} catch {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
43
|
+
const folders = listSkillFolders(skillsDir);
|
|
53
44
|
|
|
54
45
|
if (folders.length === 0) {
|
|
55
46
|
return {
|
package/mcp/help/scan.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mcp/help/scan.js — pure directory-scan logic behind the `help` MCP tool (E50_S12_T06).
|
|
3
|
+
*
|
|
4
|
+
* Split out of index.js so it is importable and unit-testable without starting an MCP
|
|
5
|
+
* server. index.js instantiates an `McpServer` and ends with a top-level
|
|
6
|
+
* `await server.connect(transport)` that blocks on stdin — importing that file as a
|
|
7
|
+
* whole would hang any test that tried it, the exact untestability
|
|
8
|
+
* mcp/router/allow-list-guard.js's header describes for mcp/router/index.js, and the
|
|
9
|
+
* same reason mcp/router's Stage 1 decision was extracted into its own module by
|
|
10
|
+
* E50_S07_T02. This file mirrors that precedent: plain Node built-ins only, no MCP SDK
|
|
11
|
+
* import, no side effects at module load time.
|
|
12
|
+
*
|
|
13
|
+
* Confirmed by direct reading (E50_S12_T06): this is a pure `readdirSync`/`statSync`
|
|
14
|
+
* scan. It never parses a SKILL.md's frontmatter, never strips a `j.`/`j:` prefix, and
|
|
15
|
+
* never compares against the allow-list — it returns whatever directory names exist
|
|
16
|
+
* under `.claude/skills`/`.agents/skills` verbatim. A `j-<name>` twin directory and a
|
|
17
|
+
* bare `<name>` directory are both just directory names to this code; neither is
|
|
18
|
+
* treated differently in any way, so nothing here needs to change for a twin-only tree.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
22
|
+
import { join } from "path";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Resolves which of the two generated skill mirrors exists under `root`. The
|
|
26
|
+
* jenga-agent postinstall mirrors skills/ into both .claude/ (Claude Code) and
|
|
27
|
+
* .agents/ (Copilot / custom agents); this prefers .claude/, falling back to .agents/.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} root - project root to scan (already resolved to an absolute path)
|
|
30
|
+
* @returns {string|null} the first candidate skills directory that exists, or null
|
|
31
|
+
*/
|
|
32
|
+
export function resolveSkillsDir(root) {
|
|
33
|
+
const candidates = [
|
|
34
|
+
join(root, ".claude", "skills"),
|
|
35
|
+
join(root, ".agents", "skills"),
|
|
36
|
+
];
|
|
37
|
+
return candidates.find(existsSync) ?? null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The two candidate mirror paths under `root`, in preference order — exposed
|
|
42
|
+
* separately from resolveSkillsDir() so a caller can report "looked in: ..." even when
|
|
43
|
+
* neither candidate exists (mirrors index.js's `help` tool's own "No skills found"
|
|
44
|
+
* message).
|
|
45
|
+
*
|
|
46
|
+
* @param {string} root
|
|
47
|
+
* @returns {string[]}
|
|
48
|
+
*/
|
|
49
|
+
export function candidateSkillsDirs(root) {
|
|
50
|
+
return [
|
|
51
|
+
join(root, ".claude", "skills"),
|
|
52
|
+
join(root, ".agents", "skills"),
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Lists the immediate subdirectory names of `skillsDir` — the exact set the `help` tool
|
|
58
|
+
* reports. See this module's header for why this is safe to call against a twin-only
|
|
59
|
+
* tree with no code change: it is a verbatim directory listing, format-agnostic.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} skillsDir - an existing directory (typically resolveSkillsDir()'s result)
|
|
62
|
+
* @returns {string[]} immediate subdirectory names, in readdirSync's natural order
|
|
63
|
+
*/
|
|
64
|
+
export function listSkillFolders(skillsDir) {
|
|
65
|
+
const entries = readdirSync(skillsDir);
|
|
66
|
+
return entries.filter((entry) => {
|
|
67
|
+
try {
|
|
68
|
+
return statSync(join(skillsDir, entry)).isDirectory();
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jenga-ai/agent",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "An agentic development workflow for Claude Code, Copilot, and Codex — with a persistent Epic/Story/Task board, an isolated git worktree per task, and a separate tester agent that runs your test suite before anything is marked done.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -12,12 +12,17 @@
|
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.13.1"
|
|
14
14
|
},
|
|
15
|
+
"jenga": {
|
|
16
|
+
"requiresLifecycleScripts": true,
|
|
17
|
+
"note": "postinstall (scripts/postinstall.js) mirrors skills/agents/hooks into the consumer project and is load-bearing for the whole framework install. On package managers that block lifecycle scripts by default (pnpm v10+, npm 11.16+ via allowScripts), an explicit consumer-side opt-in is required — see docs/distribution.md."
|
|
18
|
+
},
|
|
15
19
|
"scripts": {
|
|
16
20
|
"postinstall": "node scripts/postinstall.js",
|
|
17
21
|
"prepack": "npm run ui:build --prefix project/app --",
|
|
18
22
|
"generate:legacy-paths": "node scripts/generate-legacy-shipped-paths.js",
|
|
19
23
|
"graph:populate": "node scripts/populate-knowledge-graph.js",
|
|
20
24
|
"test": "bats tests/*.bats",
|
|
25
|
+
"gate:twin-parity": "bash scripts/audit-twin-divergence.sh . --min-pairs 40",
|
|
21
26
|
"validate:npm-metadata": "bash scripts/validate_npm_metadata.sh",
|
|
22
27
|
"ui:dev": "npm run ui:dev --prefix project/app --",
|
|
23
28
|
"ui:build": "npm run ui:build --prefix project/app --",
|