@jenga-ai/agent 3.1.0 → 3.1.1

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 CHANGED
@@ -40,7 +40,7 @@ Jenga AI solves each of these with structure: persistent engineering context mai
40
40
  - **Isolated git worktrees per task** — the Developer never works directly on your main branch
41
41
  - **Works with any AI coding agent or AI-native IDE** — Claude Code, GitHub Copilot, and Codex CLI are all supported today
42
42
 
43
- > 📖 **Full reference:** [project/.wiki/documentation.md](project/.wiki/documentation.md) | [Intro Guide](project/.wiki/intro-guide.md)
43
+ > 📖 **Full reference:** [Docs site](https://samwelmunga.github.io/jenga-npm/reference.html) · **Intro Guide:** [Docs site](https://samwelmunga.github.io/jenga-npm/getting-started.html) — mirrored at [project/.wiki/documentation.md](project/.wiki/documentation.md) / [intro-guide.md](project/.wiki/intro-guide.md)
44
44
 
45
45
  ### "Isn't this just an LLM grading another LLM?"
46
46
 
@@ -215,7 +215,7 @@ Skills live in `.agents/skills/<name>/SKILL.md`. Invoke with `j.<name>` in your
215
215
  | `j.do` | Execute tasks from the scrum board, drives the Developer agent through the full loop |
216
216
  | `j.status` | Print a full scrum board overview — epics, stories, tasks, rapports, queue depth |
217
217
 
218
- > 📖 **Full skill list** (planning, review, committing & maintenance commands): [project/.wiki/documentation.md](project/.wiki/documentation.md#skills)
218
+ > 📖 **Full skill list** (planning, review, committing & maintenance commands): [Docs site](https://samwelmunga.github.io/jenga-npm/skills.html) — mirrored at [project/.wiki/documentation.md#skills](project/.wiki/documentation.md#skills)
219
219
 
220
220
  ---
221
221
 
@@ -232,4 +232,4 @@ Skills live in `.agents/skills/<name>/SKILL.md`. Invoke with `j.<name>` in your
232
232
  - You're doing a quick one-off script or single-session experiment
233
233
  - Your project has no meaningful test surface
234
234
 
235
- 📖 **Full reference:** [project/.wiki/documentation.md](project/.wiki/documentation.md)
235
+ 📖 **Full reference:** [Docs site](https://samwelmunga.github.io/jenga-npm/reference.html) — mirrored at [project/.wiki/documentation.md](project/.wiki/documentation.md)
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated_at": "2026-09-08T02:00:42.924Z",
2
+ "generated_at": "2026-09-08T21:53:54.204Z",
3
3
  "skill_count": 36,
4
4
  "skills": [
5
5
  "brainstorm",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jenga-ai/agent",
3
- "version": "3.1.0",
3
+ "version": "3.1.1",
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": {
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/build-pages-site.sh
3
+ #
4
+ # Deterministically (re)generates the wiki-derived pages of the GitHub Pages
5
+ # documentation site (docs/*.md) from the existing wiki mirror under
6
+ # project/.wiki/. This is the sync mechanism behind E41_S13_T01's
7
+ # source-of-truth decision ("restructure from the wiki, keep both in sync"):
8
+ # project/.wiki/ stays the canonical, doc-sync-maintained content; this
9
+ # script is the deterministic transform from that content into the
10
+ # multi-page, navigable Pages site.
11
+ #
12
+ # WHEN TO RE-RUN: after `j.doc-sync` (or any manual edit) updates
13
+ # project/.wiki/documentation.md, project/.wiki/intro-guide.md, or
14
+ # project/.wiki/concepts/*.md, re-run this script to refresh the Pages site
15
+ # so it doesn't silently drift from the wiki the way README.md and the wiki
16
+ # itself have drifted from each other before (see skills/doc-sync/SKILL.md
17
+ # for the doc-sync side of this convention).
18
+ #
19
+ # WHAT THIS SCRIPT DOES NOT TOUCH:
20
+ # - docs/_config.yml, docs/index.md — hand-authored site structure/config,
21
+ # not derived from wiki content. Edit these directly if the page set or
22
+ # nav changes.
23
+ # - docs/README.md — maintainer-facing decision record, not a rendered
24
+ # Jekyll page and not wiki-derived content.
25
+ #
26
+ # WHAT THIS SCRIPT REGENERATES (always overwritten, never hand-edit these):
27
+ # - docs/getting-started.md <- project/.wiki/intro-guide.md
28
+ # - docs/concepts.md <- project/.wiki/concepts/*.md (concatenated)
29
+ # - docs/skills.md <- project/.wiki/documentation.md "## Skills"
30
+ # - docs/agents.md <- project/.wiki/documentation.md "## Agents"
31
+ # - docs/hooks.md <- project/.wiki/documentation.md "## Hooks"
32
+ # - docs/mcp-tools.md <- project/.wiki/documentation.md "## MCP Tools"
33
+ # - docs/reference.md <- project/.wiki/documentation.md
34
+ # "## Directory Structure" +
35
+ # "## Agent Communication Contract"
36
+ # (plus a short hand-authored index blurb
37
+ # linking to skills/agents/hooks/mcp-tools)
38
+ #
39
+ # KNOWN LIMITATION: this is a structural transform, not a fact-checker. It
40
+ # ports project/.wiki/documentation.md's content as-is, including whatever
41
+ # is currently stale in that file (e.g. it documents ~30 skills under the
42
+ # old bare `/<name>` invocation form and does not yet list every skill in
43
+ # skills/, including the `j-<name>` twins — a pre-existing wiki staleness
44
+ # gap, not something introduced by this script). Fixing the wiki's own
45
+ # staleness is doc-sync's job, not this script's — this script only keeps
46
+ # the Pages site faithful to whatever the wiki currently says.
47
+ #
48
+ # HEADING-STABILITY ASSUMPTION: section extraction below is keyed to exact
49
+ # top-level ("## ") heading text in documentation.md (Agents, Skills, MCP
50
+ # Tools, Hooks, Directory Structure, Agent Communication Contract). If any
51
+ # of those headings are renamed, update the `case` statement in
52
+ # extract_sections() to match.
53
+ #
54
+ # Usage: scripts/build-pages-site.sh
55
+ # Safe to re-run any number of times — every run fully overwrites its
56
+ # output files from the current wiki content (no partial/incremental state).
57
+
58
+ set -euo pipefail
59
+
60
+ # shellcheck source=lib/resolve-project-dir.sh disable=SC1091
61
+ source "$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh"
62
+
63
+ REPO_ROOT="$JENGA_PROJECT_DIR"
64
+ WIKI_DIR="$REPO_ROOT/project/.wiki"
65
+ DOCS_DIR="$REPO_ROOT/docs"
66
+ DOC_MD="$WIKI_DIR/documentation.md"
67
+ INTRO_MD="$WIKI_DIR/intro-guide.md"
68
+ CONCEPTS_DIR="$WIKI_DIR/concepts"
69
+
70
+ for f in "$DOC_MD" "$INTRO_MD"; do
71
+ if [ ! -f "$f" ]; then
72
+ echo "build-pages-site.sh: required source file not found: $f" >&2
73
+ exit 1
74
+ fi
75
+ done
76
+ if [ ! -d "$CONCEPTS_DIR" ]; then
77
+ echo "build-pages-site.sh: required source directory not found: $CONCEPTS_DIR" >&2
78
+ exit 1
79
+ fi
80
+
81
+ WORK="$(mktemp -d)"
82
+ trap 'rm -rf "$WORK"' EXIT
83
+
84
+ # --- 1. Split documentation.md into per-section scratch files -------------
85
+ # A line can only belong to one target section at a time; the generic
86
+ # "any other ## heading" rule resets `section` to "" so unrelated top-level
87
+ # sections (e.g. "## Table of Contents") are dropped rather than bleeding
88
+ # into whichever named section preceded them.
89
+ awk -v work="$WORK" '
90
+ /^## Agents$/ { section = "agents"; next }
91
+ /^## Skills$/ { section = "skills"; next }
92
+ /^## MCP Tools$/ { section = "mcp-tools"; next }
93
+ /^## Hooks$/ { section = "hooks"; next }
94
+ /^## Directory Structure$/ { section = "directory-structure"; next }
95
+ /^## Agent Communication Contract$/ { section = "contract"; next }
96
+ /^## / { section = "" }
97
+ {
98
+ if (section != "") {
99
+ print >> (work "/section-" section ".md")
100
+ }
101
+ }
102
+ ' "$DOC_MD"
103
+
104
+ for s in agents skills mcp-tools hooks directory-structure contract; do
105
+ [ -f "$WORK/section-$s.md" ] || touch "$WORK/section-$s.md"
106
+ done
107
+
108
+ write_page() {
109
+ # write_page <output-path> <title> <permalink> <body-file>
110
+ local out="$1" title="$2" permalink="$3" body="$4"
111
+ {
112
+ printf -- '---\n'
113
+ printf 'layout: page\n'
114
+ printf 'title: %s\n' "$title"
115
+ printf 'permalink: %s\n' "$permalink"
116
+ printf -- '---\n\n'
117
+ cat "$body"
118
+ } > "$out"
119
+ }
120
+
121
+ # --- 2. docs/agents.md, docs/hooks.md, docs/mcp-tools.md -------------------
122
+ # Straight ports — no internal markdown links were found in these sections
123
+ # of documentation.md, so no link rewriting is needed beyond the front
124
+ # matter wrapper.
125
+ write_page "$DOCS_DIR/agents.md" "Agents" "/agents.html" "$WORK/section-agents.md"
126
+ write_page "$DOCS_DIR/hooks.md" "Hooks" "/hooks.html" "$WORK/section-hooks.md"
127
+ write_page "$DOCS_DIR/mcp-tools.md" "MCP Tools" "/mcp-tools.html" "$WORK/section-mcp-tools.md"
128
+
129
+ # --- 3. docs/skills.md ------------------------------------------------------
130
+ # Single reference page (matches the story's own suggested top-level
131
+ # category list: Getting Started / Concepts / Skills reference / Agents /
132
+ # Hooks / MCP Tools — "Skills reference" is one page, not one page per
133
+ # skill). Category (### ) and per-skill (#### ) structure is preserved
134
+ # as-is from the source.
135
+ write_page "$DOCS_DIR/skills.md" "Skills Reference" "/skills.html" "$WORK/section-skills.md"
136
+
137
+ # --- 4. docs/reference.md ---------------------------------------------------
138
+ # Combines the two remaining documentation.md sections (Directory Structure,
139
+ # Agent Communication Contract) behind a short hand-authored index blurb
140
+ # that links out to the pages built above — this replaces documentation.md's
141
+ # original role as the single "full reference" entry point, now that its
142
+ # content is split across multiple pages.
143
+ {
144
+ cat <<'EOF'
145
+ ## Full Reference Index
146
+
147
+ This page is the entry point into the full reference material, split across
148
+ several pages so nothing requires scrolling through one giant file:
149
+
150
+ - **[Skills Reference](./skills.md)** — every skill, grouped by Setup &
151
+ Planning, Execution, Status & Review, and Committing & Maintenance.
152
+ - **[Agents](./agents.md)** — Scrum Master, Developer, Tester: roles,
153
+ ownership, and responsibilities.
154
+ - **[Hooks](./hooks.md)** — session lifecycle hooks and what they run.
155
+ - **[MCP Tools](./mcp-tools.md)** — Model Context Protocol tools available
156
+ in a Claude Code session.
157
+
158
+ The rest of this page covers the repo's directory structure and the
159
+ inter-agent communication contract (the typed "sender object" every agent
160
+ call carries).
161
+
162
+ ---
163
+
164
+ EOF
165
+ cat "$WORK/section-directory-structure.md"
166
+ printf '\n---\n\n'
167
+ cat "$WORK/section-contract.md"
168
+ } > "$WORK/reference-body.md"
169
+ write_page "$DOCS_DIR/reference.md" "Reference" "/reference.html" "$WORK/reference-body.md"
170
+
171
+ # --- 5. docs/getting-started.md ---------------------------------------------
172
+ # Port of intro-guide.md verbatim (drop the leading H1 — front matter
173
+ # supplies the page title instead), with its two links into the
174
+ # now-restructured reference/concepts pages rewritten.
175
+ tail -n +2 "$INTRO_MD" > "$WORK/intro-body.md"
176
+ # NOTE on delimiter choice: sed's `s<delim>pattern<delim>replacement<delim>`
177
+ # breaks if the replacement text itself contains the delimiter character.
178
+ # Several replacements below contain a literal "#" (anchor fragments), so
179
+ # "#" cannot be used as the delimiter here — "|" is used instead, since
180
+ # none of these paths/anchors contain a literal "|".
181
+ sed -i.bak \
182
+ -e 's|\[documentation\.md\](\./documentation\.md)|[reference.md](./reference.md)|g' \
183
+ -e 's|(\./concepts/role-separation\.md)|(./concepts.md#role-separation)|g' \
184
+ -e 's|(\./concepts/board-hierarchy\.md)|(./concepts.md#board-hierarchy)|g' \
185
+ -e 's|(\./concepts/session-continuity\.md)|(./concepts.md#session-continuity)|g' \
186
+ -e 's|(\./concepts/first-feature\.md)|(./concepts.md#your-first-feature)|g' \
187
+ -e 's|(\./concepts/multi-session-work\.md)|(./concepts.md#working-across-sessions)|g' \
188
+ -e 's|(\./concepts/mid-flow-capture\.md)|(./concepts.md#capturing-mid-flow-ideas)|g' \
189
+ -e 's|(\./concepts/parallel-tasks\.md)|(./concepts.md#parallel-tasks)|g' \
190
+ "$WORK/intro-body.md"
191
+ rm -f "$WORK/intro-body.md.bak"
192
+ write_page "$DOCS_DIR/getting-started.md" "Getting Started" "/getting-started.html" "$WORK/intro-body.md"
193
+
194
+ # --- 6. docs/concepts.md -----------------------------------------------------
195
+ # Concatenates all 7 project/.wiki/concepts/*.md files into one page:
196
+ # - each file's H1 becomes an H2 section heading (slug listed below must
197
+ # stay in sync with each file's actual title text — GitHub Pages/kramdown
198
+ # slugifies headings to lowercase-hyphenated automatically)
199
+ # - all other headings are demoted one level (## -> ###, ### -> ####)
200
+ # - sibling/parent links are rewritten to point within the merged page
201
+ # and at the sibling getting-started.md / reference.md pages
202
+ #
203
+ {
204
+ cat <<'EOF'
205
+ ## Concepts
206
+
207
+ The ideas behind Jenga AI's structure, and the how-tos for using it day to
208
+ day. Jump to any section:
209
+
210
+ - [Role Separation](#role-separation)
211
+ - [Board Hierarchy](#board-hierarchy)
212
+ - [Session Continuity](#session-continuity)
213
+ - [Your First Feature](#your-first-feature)
214
+ - [Working Across Sessions](#working-across-sessions)
215
+ - [Capturing Mid-Flow Ideas](#capturing-mid-flow-ideas)
216
+ - [Parallel Tasks](#parallel-tasks)
217
+
218
+ ---
219
+
220
+ EOF
221
+ } > "$WORK/concepts-body.md"
222
+
223
+ append_concept() {
224
+ # append_concept <filename-stem> <section-title>
225
+ local stem="$1"
226
+ local title="$2"
227
+ local src="$CONCEPTS_DIR/$stem.md"
228
+ {
229
+ printf '## %s\n\n' "$title"
230
+ # NOTE on delimiter choice: same reasoning as the getting-started block
231
+ # above — replacements here contain a literal "#" anchor character, so
232
+ # "|" is used as the sed delimiter instead of "#".
233
+ #
234
+ # NOTE on heading demotion: a naive two-pass "## -> ###" then
235
+ # "### -> ####" sed would double-demote lines that were originally
236
+ # "## " (they'd match the first rule, becoming "### ", and then ALSO
237
+ # match the second rule on the same pass, becoming "#### " — wrong).
238
+ # The single extended-regex rule below captures the existing run of
239
+ # 2-3 "#" characters and prepends exactly one more, so each line is
240
+ # demoted exactly once regardless of its original level.
241
+ tail -n +2 "$src" \
242
+ | sed \
243
+ -e 's|\[documentation\.md\](\.\./documentation\.md)|[reference.md](./reference.md)|g' \
244
+ -e 's|(\.\./intro-guide\.md)|(./getting-started.md)|g' \
245
+ -e 's|(\.\./documentation\.md)|(./reference.md)|g' \
246
+ -e 's|(\./role-separation\.md)|(#role-separation)|g' \
247
+ -e 's|(\./board-hierarchy\.md)|(#board-hierarchy)|g' \
248
+ -e 's|(\./session-continuity\.md)|(#session-continuity)|g' \
249
+ -e 's|(\./first-feature\.md)|(#your-first-feature)|g' \
250
+ -e 's|(\./multi-session-work\.md)|(#working-across-sessions)|g' \
251
+ -e 's|(\./mid-flow-capture\.md)|(#capturing-mid-flow-ideas)|g' \
252
+ -e 's|(\./parallel-tasks\.md)|(#parallel-tasks)|g' \
253
+ | sed -E 's/^(#{2,3}) /#\1 /'
254
+ printf '\n---\n\n'
255
+ } >> "$WORK/concepts-body.md"
256
+ }
257
+
258
+ append_concept "role-separation" "Role Separation"
259
+ append_concept "board-hierarchy" "Board Hierarchy"
260
+ append_concept "session-continuity" "Session Continuity"
261
+ append_concept "first-feature" "Your First Feature"
262
+ append_concept "multi-session-work" "Working Across Sessions"
263
+ append_concept "mid-flow-capture" "Capturing Mid-Flow Ideas"
264
+ append_concept "parallel-tasks" "Parallel Tasks"
265
+
266
+ write_page "$DOCS_DIR/concepts.md" "Concepts" "/concepts.html" "$WORK/concepts-body.md"
267
+
268
+ echo "build-pages-site.sh: regenerated docs/{getting-started,concepts,skills,agents,hooks,mcp-tools,reference}.md from project/.wiki/*"
@@ -151,7 +151,21 @@ if [ -z "$REPO_ROOT" ]; then
151
151
  exit 2
152
152
  fi
153
153
 
154
- WITH_LOCK="$REPO_ROOT/scripts/with-lock.sh"
154
+ # ─── Resolve with-lock.sh's package root ──────────────────────────────────
155
+ # postinstall.js mirrors only skills/ and agents/ into a consumer's .claude/
156
+ # and .agents/ — scripts/ (which owns with-lock.sh) is never copied there, so
157
+ # this script — itself shipped under skills/j-uncharted/scripts/ and mirrored
158
+ # alongside it — cannot assume "$REPO_ROOT/scripts/with-lock.sh" exists.
159
+ # Mirrors skills/init/scripts/init.sh's PKG_ROOT fallback: prefer a monorepo
160
+ # checkout's sibling scripts/ dir, else fall back to the installed npm
161
+ # package under node_modules/@jenga-ai/agent.
162
+ if [ -f "$SCRIPT_DIR/../../../scripts/with-lock.sh" ]; then
163
+ WITH_LOCK="$SCRIPT_DIR/../../../scripts/with-lock.sh"
164
+ elif [ -f "$REPO_ROOT/node_modules/@jenga-ai/agent/scripts/with-lock.sh" ]; then
165
+ WITH_LOCK="$REPO_ROOT/node_modules/@jenga-ai/agent/scripts/with-lock.sh"
166
+ else
167
+ WITH_LOCK="$REPO_ROOT/scripts/with-lock.sh"
168
+ fi
155
169
  STATE_DIR="$REPO_ROOT/project/queue/elicitation-state"
156
170
  DEFAULT_CAP=5
157
171
 
@@ -132,13 +132,15 @@ If all applicable rules pass (or the task is a legacy task), proceed to the next
132
132
 
133
133
  ### Phase 0.75 — Entry Mode Resolution
134
134
 
135
- This phase determines **how `/jenga` was invoked** and, for two of the three entry modes, produces a **scoped set** — a confirmed list of board IDs (epics/stories/tasks) that Phases 1-4 must restrict themselves to. All board scanning, ID parsing, cascade expansion, and rendering used by this phase already live in `skills/jenga/scripts/` per this repo's "Scripts Over Inline Logic" principle — this phase never re-implements any of that logic inline. The executing agent's job here is limited to: invoking the right script with the right arguments, relaying its STDOUT verbatim to the user when the contract calls for that, capturing the `STATE_FILE:` line from STDERR for the next turn, and forwarding the user's raw reply back into the next invocation unmodified.
135
+ This phase determines **how `/jenga` was invoked** and, for two of the four entry modes, produces a **scoped set** — a confirmed list of board IDs (epics/stories/tasks) that Phases 1-4 must restrict themselves to. All board scanning, ID parsing, cascade expansion, and rendering used by this phase already live in `skills/jenga/scripts/` per this repo's "Scripts Over Inline Logic" principle — this phase never re-implements any of that logic inline. The executing agent's job here is limited to: invoking the right script with the right arguments, relaying its STDOUT verbatim to the user when the contract calls for that, capturing the `STATE_FILE:` line from STDERR for the next turn, and forwarding the user's raw reply back into the next invocation unmodified.
136
136
 
137
137
  **Determine the invocation form** from the raw argument (if any) passed to `/jenga`:
138
138
 
139
139
  - No argument at all → **bare branch**.
140
140
  - The argument is the literal string `*` → **wildcard branch**.
141
- - Any other non-empty argument → **scoped branch** (treat the whole argument as the comma-separated raw ID list).
141
+ - Any other non-empty argument → invoke `skills/jenga/scripts/detect-nl-intent.sh "<raw argument>"` (E53_S01_T01) and branch on its `classification` field:
142
+ - `all_resolved` or `mixed` → **scoped branch** (below) — this is the same branch as before; only its internal mechanics changed (see below).
143
+ - `nl_intent` → **natural-language branch** (below) — new for E53_S01, no new sigil or entry point, purely a new outcome of this same argument-shape detection.
142
144
 
143
145
  #### Wildcard branch (`/jenga *`)
144
146
 
@@ -154,10 +156,35 @@ Skip both the picker and the confirmation step entirely. There is no scoped set
154
156
 
155
157
  #### Scoped branch (`/jenga <ids>`)
156
158
 
157
- 1. Invoke `skills/jenga/scripts/resolve-id.sh "<raw argument>"` directly — the picker is skipped entirely in this branch.
158
- 2. Parse the JSON array response, one object per comma-delimited input segment.
159
- - If **every** segment has `status: "resolved"`, collect their `resolved_id` values into a comma-separated list and continue to the shared confirmation step below.
160
- - If **any** segment has `status: "rejected"`, halt this phase (do not proceed to confirmation or Phase 1) and report each rejected segment's `input` and `reason` to the user verbatim, per `resolve-id.sh`'s own contract — a partial or ambiguous ID is never guessed. The user must re-invoke `/jenga <ids>` with corrected input.
159
+ This branch is entered when `detect-nl-intent.sh` (invoked above) classifies the argument as `all_resolved` or `mixed` — the picker is skipped entirely in this branch. `detect-nl-intent.sh` has already invoked `resolve-id.sh` internally and reduced its per-segment output to one of these two shapes; `skills/jenga/SKILL.md` never parses `resolve-id.sh`'s raw array itself (see `detect-nl-intent.sh`'s own header comment for the full classification contract, E53_S01_T01).
160
+
161
+ 1. On `all_resolved`, take the `resolved_ids` (or `resolved_ids_csv`) field directly from `detect-nl-intent.sh`'s output and continue to the shared confirmation step below.
162
+ 2. On `mixed`, halt this phase (do not proceed to confirmation or Phase 1) and report each entry in `detect-nl-intent.sh`'s `rejected` array — its `input` and `reason` — to the user verbatim; a partial or ambiguous ID is never guessed. The user must re-invoke `/jenga <ids>` with corrected input.
163
+
164
+ #### Natural-language branch (`/jenga <free-form text>`)
165
+
166
+ This branch is entered when `detect-nl-intent.sh` classifies the argument as `nl_intent` — every comma-delimited segment failed the ID grammar, so the raw argument is treated as natural-language intent rather than a malformed ID list. This is purely a new *outcome* of the same argument-shape detection above — no new sigil, trigger prefix, or separate entry point is introduced.
167
+
168
+ 1. **Load the catalog** — invoke `skills/jenga/scripts/load-nl-catalog.sh` with no arguments (E53_S01_T02). Its stdout is the full skill catalog (`name`/`description`/`keywords`/`examples`/`prefered_agent` per skill), sourced exclusively from `lib/generate-skill-allow-list.js`'s generated inventory — see the script's own header for the full contract. Never re-derive this catalog by re-scanning `skills/` inline.
169
+ 2. **Match** — run `skills/route/SKILL.md`'s **Step 2 — Match the Prompt to a Skill** (the three-pass keyword → example-similarity → description match, including its tie-break and no-match handling) against this catalog, treating `detect-nl-intent.sh`'s `raw_argument` field as the prompt. Reuse that section's matching logic by reference — do not re-author its prose here.
170
+ 3. **Confident single match** — report the routing decision using `skills/route/SKILL.md`'s **Step 7 — Report Routing Decision** format (substitute `/jenga` for `/route` as the invoking command named in the report), then invoke the matched skill exactly as `skills/route/SKILL.md`'s **Step 6 — Invoke the Matched Skill** already does: load `agents/<prefered_agent>.md` when the matched skill specifies `metadata.prefered_agent`, otherwise execute the skill instructions directly. The matched skill's own execution takes over from here — do not continue into this `/jenga` invocation's Phase 1.
171
+ 4. **No match, or an ambiguous multi-way tie (single-skill match)** — before surfacing `/route`'s generic disambiguation options, attempt a **playbook fallback** (E53_S02): invoke `skills/jenga/scripts/match-playbook.sh "<raw_argument>"`. This step only ever runs when step 3 above did NOT already commit to a confident single-skill match — a confident single-skill match always wins outright and this playbook fallback is never even invoked in that case. Branch on `match-playbook.sh`'s `classification` field:
172
+ - `playbook_match` → continue to **step 5 (Playbook proposal and execution)** below.
173
+ - `ambiguous` or `no_match` → continue to **step 6 (Fall through to `/route`'s disambiguation)** below — the exact behavior this branch already had before E53_S02, unchanged.
174
+ 5. **Playbook proposal and execution** — entered only on a `playbook_match` result from step 4. A proposed playbook is an ordered chain of skills (e.g. the canonical `brainstorm -> j.todo -> j.do -> j.dev-done -> j.mirror-public` chain defined in `skills/jenga/playbooks/brainstorm-to-mirror.json`) that must be confirmed, editable, and confirmable per `CLAUDE.md`'s Interaction Pattern before any step executes — the same confirm-before-execute posture `/jenga` already applies to the bare/scoped branches via `render-confirmation.sh`.
175
+ a. **Render and confirm the chain** — invoke `skills/jenga/scripts/render-playbook-confirmation.sh "<playbook_id>" "<name>" "<comma-separated steps>"` (start mode, using `match-playbook.sh`'s `playbook_id`/`name`/`steps` fields verbatim). Relay STDOUT (the numbered chain + instructions) to the user verbatim. Capture the `STATE_FILE:` path from STDERR.
176
+ b. Wait for the user's chat reply, then invoke `skills/jenga/scripts/render-playbook-confirmation.sh <state_file> "<raw_reply>"` (continue mode).
177
+ - **Toggle or error turn** (plain text on STDOUT, state file retained) — relay verbatim and return to step 5b for another reply. This loops exactly as the existing bare/scoped confirmation flow's own toggle/error turns already do.
178
+ - **Cancellation** — relay the cancellation acknowledgement and halt the entire `/jenga` invocation immediately, with no step executed — identical posture to the existing picker/confirmation cancellation edge cases already documented for the bare/scoped branches (see "## Edge Cases" below).
179
+ - **Confirmed** (JSON object on STDOUT, state file removed) — take the confirmed `steps` array (checked-only, in original playbook order) and continue to step 5c.
180
+ c. **Initialize the sequential runner** — invoke `skills/jenga/scripts/run-playbook-step.sh init "<playbook_id>" "<name>" "<comma-separated confirmed steps>"`. Its `step_ready` result names the first step to invoke.
181
+ d. **Execute steps in a loop** — for the step named by the runner's most recent `step_ready` result:
182
+ i. Invoke that step exactly as `skills/route/SKILL.md`'s **Step 6 — Invoke the Matched Skill** already does for a single matched skill: load `agents/<prefered_agent>.md` when that step's own `SKILL.md` specifies `metadata.prefered_agent`, otherwise execute its instructions directly.
183
+ ii. After that step's execution concludes, call `skills/jenga/scripts/run-playbook-step.sh advance <state_file> passed` (the step completed successfully) or `... advance <state_file> failed "<short failure note>"` (the step failed).
184
+ iii. On a `step_ready` result, repeat step 5d for the newly-named step.
185
+ iv. On a `complete` result, report the full list of completed steps to the user and stop — the playbook run is finished; do not continue into this `/jenga` invocation's Phase 1.
186
+ v. On a `halted` result, **immediately stop executing any further steps** — no silent skip-ahead. Report `failed_step`, `failed_note`, `completed` (steps that already finished), and `never_run` (steps that never got a chance to run) to the user verbatim from the halt report. Do not continue into this `/jenga` invocation's Phase 1.
187
+ 6. **Fall through to `/route`'s disambiguation** — entered when step 4 found no playbook match (`ambiguous` or `no_match`). Surface the same disambiguation options `skills/route/SKILL.md`'s **Step 2** already defines for these cases (browse `/help`, create a new skill via `/btw`, or proceed with the raw prompt) by reference to that section — do not re-copy its prose. Halt this `/jenga` invocation once the user picks an option; none of Phase 0.75's remaining steps or Phases 1-4 run for this branch.
161
188
 
162
189
  #### Shared confirmation step (bare and scoped branches only)
163
190
 
@@ -255,6 +282,11 @@ When no eligible candidates remain in Phase 4, exit and output:
255
282
  - **Bundle `/do` call failure** — treated as a skip for the entire bundle; mark all bundled tasks' status back to `Pending` and continue Phase 4 with remaining non-bundled candidates.
256
283
  - **Picker cancelled (bare branch)** — the entire `/jenga` run halts immediately after relaying the cancellation acknowledgement; no phase past 0.75 runs, and nothing on the board is modified.
257
284
  - **Confirmation cancelled (bare or scoped branch)** — same as picker cancellation: the entire `/jenga` run halts immediately; no scoped set is produced and no later phase runs.
258
- - **`resolve-id.sh` rejects one or more segments (scoped branch)** — the whole invocation halts at Phase 0.75 with the rejected segments' reasons reported verbatim; no partial scope is assembled from the segments that did resolve, and no fallback guess is made for the rejected ones. The user must re-invoke `/jenga <ids>` with corrected input.
285
+ - **`detect-nl-intent.sh` classifies the argument as `mixed` (scoped branch)** — the whole invocation halts at Phase 0.75 with each rejected segment's `input`/`reason` reported verbatim, per `detect-nl-intent.sh`'s own classification contract (E53_S01_T01); no partial scope is assembled from the segments that did resolve, and no fallback guess is made for the rejected ones. The user must re-invoke `/jenga <ids>` with corrected input.
286
+ - **`detect-nl-intent.sh` classifies the argument as `nl_intent`, no confident single-skill match, and `match-playbook.sh` (E53_S02) also finds no playbook match** — the natural-language branch's step 4 attempts the playbook fallback first (see the Natural-language branch's step 4/6), and only THEN surfaces `skills/route/SKILL.md`'s Step 2 no-match disambiguation options (browse `/help`, create a new skill via `/btw`, proceed with the raw prompt) instead of guessing; no phase past 0.75 runs until the user picks one.
287
+ - **`detect-nl-intent.sh` classifies the argument as `nl_intent`, no confident single-skill match, and `match-playbook.sh` returns an ambiguous multi-way tie between playbooks** — treated the same as the no-playbook-match case above: falls through to `skills/route/SKILL.md`'s Step 2 tie-break prompt (top candidates + a "neither, describe what you need" option) instead of guessing; no phase past 0.75 runs until the user picks one. (`match-playbook.sh`'s own `ambiguous` result — a tie between playbooks — is intentionally not given its own separate disambiguation UI; it is treated identically to `no_match` and routed to the same `/route` Step 2 fallback prose, which already has its own tie-break handling.)
288
+ - **`match-playbook.sh` returns `playbook_match` and the user confirms the full chain, and every step succeeds** — the Natural-language branch's step 5d reports the full `completed` steps list to the user and stops; `/jenga`'s own Phase 1 never runs for this invocation (execution was already fully handled by the playbook's own steps, e.g. `j.do`/`j.dev-done`).
289
+ - **`match-playbook.sh` returns `playbook_match` but the user cancels at the chain confirmation step (step 5b)** — identical posture to the existing picker/confirmation cancellation cases above: the entire `/jenga` run halts immediately after relaying the cancellation acknowledgement, with NO step of the chain executed; nothing on the board is modified by this invocation.
290
+ - **`match-playbook.sh` returns `playbook_match`, the user confirms, and a step mid-chain fails** — the Natural-language branch's step 5d(v) halts immediately on `run-playbook-step.sh`'s `halted` result: no step after the failed one runs (no silent skip-ahead), and the user is shown exactly which steps already completed, which step failed (with its note), and which steps never ran.
259
291
  - **`/jenga *` (wildcard branch)** — never produces a scoped set; Phases 1-4 run fully unrestricted over the entire board, identical to `/jenga`'s behavior before Phase 0.75 existed.
260
292
  - **Stale out-of-scope story queued in `todo.md` from an earlier run (scoped run only)** — Phase 3.5's scoped-set guard skips it entirely (not considered for bundling), so it cannot be dispatched via a bundle `/do <E##_S##>` call that would otherwise bypass Phase 4's own scoped-set exclusion; it remains untouched in `todo.md` until a future run's scope includes it.
@@ -0,0 +1,22 @@
1
+ {
2
+ "id": "brainstorm-to-mirror",
3
+ "name": "Idea to Public Release",
4
+ "description": "Takes a rough idea all the way from planning through implementation, committing, and a public mirror release -- the canonical end-to-end Jenga workflow chain.",
5
+ "keywords": [
6
+ "idea to release",
7
+ "plan and ship",
8
+ "idea to done",
9
+ "full workflow",
10
+ "end to end",
11
+ "plan build ship",
12
+ "idea to production"
13
+ ],
14
+ "examples": [
15
+ "I have an idea, help me plan it, build it, and ship it",
16
+ "take this feature from idea to committed and published",
17
+ "let's go from a rough idea all the way to a public release",
18
+ "plan this out, implement it, commit it, and push it to the public mirror",
19
+ "walk this through the whole pipeline from brainstorm to release"
20
+ ],
21
+ "steps": ["brainstorm", "todo", "do", "dev-done", "mirror-public"]
22
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://jenga.local/schemas/jenga-playbook.schema.json",
4
+ "title": "Jenga Multi-Skill Playbook",
5
+ "description": "Schema for a single multi-skill playbook definition consumed by skills/jenga/scripts/load-playbooks.sh (E53_S02_T01). A playbook is a dedicated, versionable data file describing an ORDERED chain of skills that /jenga's natural-language branch may propose (as an editable, confirmable numbered list -- see skills/jenga/scripts/render-playbook-confirmation.sh, E53_S02_T03) when free-text intent spans more than one skill and does not cleanly resolve to a single one via skills/route/SKILL.md's Step 2 matching. This file itself (schema.json) is never treated as a playbook -- load-playbooks.sh explicitly excludes it by filename when scanning skills/jenga/playbooks/*.json.",
6
+ "type": "object",
7
+ "required": ["id", "name", "description", "keywords", "examples", "steps"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "id": {
11
+ "type": "string",
12
+ "description": "Stable, unique, kebab-case identifier for this playbook (e.g. \"brainstorm-to-mirror\"). Must equal the filename's basename without the .json extension -- load-playbooks.sh validates this so a playbook's id can never silently drift from its file location.",
13
+ "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
14
+ },
15
+ "name": {
16
+ "type": "string",
17
+ "description": "Short human-readable display name shown to the user in the confirmation prompt and in routing/report output, e.g. \"Idea to Public Release\"."
18
+ },
19
+ "description": {
20
+ "type": "string",
21
+ "description": "One-sentence explanation of what this playbook accomplishes end-to-end, used as the lowest-priority match signal (description match, same as skills/route/SKILL.md's Step 2 Pass 3) when keywords/examples don't produce a confident match."
22
+ },
23
+ "keywords": {
24
+ "type": "array",
25
+ "description": "Short phrases (1-3 words) for verbatim, case-insensitive keyword matching against the raw natural-language prompt -- the highest-priority match signal (Pass 1), mirroring skills/route/SKILL.md's Step 2 Pass 1 semantics exactly, but scoped to this playbook's catalog rather than the single-skill catalog.",
26
+ "items": { "type": "string" },
27
+ "minItems": 1
28
+ },
29
+ "examples": {
30
+ "type": "array",
31
+ "description": "Natural-language example prompts a user might type that should resolve to this playbook. Used for the semantic similarity match (Pass 2), mirroring skills/route/SKILL.md's Step 2 Pass 2 semantics. At least one example must plausibly span the full breadth of this playbook's steps (not just its first step) so it is distinguishable from a plain single-skill match.",
32
+ "items": { "type": "string" },
33
+ "minItems": 1
34
+ },
35
+ "steps": {
36
+ "type": "array",
37
+ "description": "Ordered list of bare skill names (the directory name under skills/<name>/SKILL.md, e.g. \"brainstorm\", not \"j.brainstorm\" or \"/brainstorm\") that make up this playbook's chain, in the exact execution order. Each entry MUST resolve to an existing skills/<name>/SKILL.md at load time -- load-playbooks.sh skips (with a stderr warning) any playbook referencing a nonexistent skill rather than silently including a broken chain in the catalog.",
38
+ "items": { "type": "string" },
39
+ "minItems": 2
40
+ }
41
+ }
42
+ }