@jenga-ai/agent 3.1.1 → 3.2.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.
Files changed (39) hide show
  1. package/agents/developer.md +15 -15
  2. package/agents/scrum-master.md +17 -17
  3. package/agents/tester.md +25 -15
  4. package/lib/skill-allow-list.json +3 -2
  5. package/package.json +1 -1
  6. package/scripts/audit-twin-divergence.sh +625 -0
  7. package/scripts/check-public-playbook-steps.sh +136 -0
  8. package/skills/j-close-story/SKILL.md +1 -1
  9. package/skills/j-do/SKILL.md +19 -19
  10. package/skills/j-doc-sync/SKILL.md +12 -1
  11. package/skills/j-idea/SKILL.md +1 -1
  12. package/skills/j-init/SKILL.md +5 -4
  13. package/skills/j-init/assets/directory_structure.txt +1 -0
  14. package/skills/j-init/scripts/detect-existing-codebase.sh +2 -2
  15. package/skills/j-init/scripts/init.sh +13 -2
  16. package/skills/j-playbook/SKILL.md +81 -0
  17. package/skills/j-proceed/SKILL.md +1 -1
  18. package/skills/j-publish/SKILL.md +1 -1
  19. package/skills/j-publish/adapters/npm-ci.md +29 -0
  20. package/skills/j-publish/scripts/npm_ci_pipeline.sh +3 -0
  21. package/skills/j-publish/scripts/npm_pipeline.sh +18 -0
  22. package/skills/j-publish/scripts/npm_stage_pipeline.sh +81 -41
  23. package/skills/j-reconcile/SKILL.md +1 -0
  24. package/skills/j-redo/SKILL.md +1 -1
  25. package/skills/j-status/SKILL.md +12 -0
  26. package/skills/j-todo/SKILL.md +2 -2
  27. package/skills/j-uncharted/SKILL.md +8 -7
  28. package/skills/j-uncharted/scripts/validate-proposed-items.sh +18 -2
  29. package/skills/jenga/SKILL.md +55 -16
  30. package/skills/jenga/playbooks/idea-to-committed.json +20 -0
  31. package/skills/jenga/playbooks/schema.json +1 -1
  32. package/skills/jenga/scripts/load-playbooks.sh +855 -27
  33. package/skills/jenga/scripts/match-playbook.sh +1 -1
  34. package/skills/jenga/scripts/render-playbook-confirmation.sh +162 -8
  35. package/skills/jenga/scripts/run-playbook-step.sh +535 -42
  36. package/skills/jenga-permission-level/SKILL.md +4 -4
  37. package/templates/KNOWLEDGE_GRAPH_STUB_SCHEMA_TEMPLATE.md +128 -0
  38. package/templates/playbook-types.json +8 -0
  39. package/skills/jenga/playbooks/brainstorm-to-mirror.json +0 -22
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # scripts/check-public-playbook-steps.sh — every public playbook's steps must ship publicly
4
+ #
5
+ # E50_S20_T01. Guards the invariant that a playbook shipped to the public mirror can actually
6
+ # run there: for each playbook JSON that `.publicignore` does NOT block, every step must resolve
7
+ # to a skill directory that `.publicignore` also does not block.
8
+ #
9
+ # Why this exists: `skills/jenga/scripts/load-playbooks.sh` requires every step's
10
+ # `skills/<dir>/SKILL.md` to exist, and silently SKIPS the whole playbook (stderr warning only)
11
+ # when one is missing. In the private repo every skill is present, so a playbook referencing a
12
+ # private-only skill looks perfectly healthy — the breakage appears only in the public mirror,
13
+ # where it surfaced as a stage-deploy gate failure rather than as a test failure (E50_S13).
14
+ # This check reproduces that condition statically, on the private side, before anything ships.
15
+ #
16
+ # Blocklist semantics are NOT re-derived here. Classification is delegated to
17
+ # scripts/check-publicignore-match.sh, which itself reuses /mirror-public's own
18
+ # `rsync --exclude-from=.publicignore` matching — a second, hand-rolled answer to "is this path
19
+ # blocked" is exactly the drift this repo has been bitten by before.
20
+ #
21
+ # Composed steps: a StepObject of the form {"playbook": "<id>"} composes another playbook. Its
22
+ # own steps are checked when that playbook is itself public; a public playbook composing a
23
+ # BLOCKED playbook is a violation in its own right, since the composed id will not resolve in
24
+ # the mirror (see tests/load-playbooks-composition.bats's nonexistent-playbook-id behaviour).
25
+ #
26
+ # Usage:
27
+ # scripts/check-public-playbook-steps.sh [<repo-root>]
28
+ #
29
+ # Exit codes:
30
+ # 0 every public playbook's every step (and composed playbook) is public
31
+ # 1 at least one violation — each is named on stdout
32
+ # 2 usage / environment error (bad root, missing helper, unparseable playbook)
33
+
34
+ set -euo pipefail
35
+
36
+ REPO_ROOT="${1:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
37
+
38
+ if [ ! -d "$REPO_ROOT" ]; then
39
+ echo "check-public-playbook-steps.sh: error: not a directory: $REPO_ROOT" >&2
40
+ exit 2
41
+ fi
42
+
43
+ PLAYBOOKS_DIR="$REPO_ROOT/skills/jenga/playbooks"
44
+ MATCHER="$REPO_ROOT/scripts/check-publicignore-match.sh"
45
+
46
+ if [ ! -f "$REPO_ROOT/.publicignore" ]; then
47
+ echo "check-public-playbook-steps.sh: no .publicignore at $REPO_ROOT — nothing to enforce, skipping"
48
+ exit 0
49
+ fi
50
+
51
+ if [ ! -f "$MATCHER" ]; then
52
+ echo "check-public-playbook-steps.sh: error: missing $MATCHER (the single source of blocklist semantics)" >&2
53
+ exit 2
54
+ fi
55
+
56
+ if [ ! -d "$PLAYBOOKS_DIR" ]; then
57
+ echo "check-public-playbook-steps.sh: no playbooks directory at $PLAYBOOKS_DIR — nothing to check"
58
+ exit 0
59
+ fi
60
+
61
+ # classify <path-relative-to-repo-root> -> echoes PUBLIC or BLOCKED
62
+ classify() {
63
+ bash "$MATCHER" "$1" 2>/dev/null | head -1 | cut -f1
64
+ }
65
+
66
+ violations=0
67
+ checked=0
68
+ skipped_private=0
69
+
70
+ for pb in "$PLAYBOOKS_DIR"/*.json; do
71
+ [ -f "$pb" ] || continue
72
+ base="$(basename "$pb")"
73
+ # schema.json describes playbooks, it is not one.
74
+ [ "$base" = "schema.json" ] && continue
75
+
76
+ rel="skills/jenga/playbooks/$base"
77
+ if [ "$(classify "$rel")" = "BLOCKED" ]; then
78
+ skipped_private=$((skipped_private + 1))
79
+ continue
80
+ fi
81
+
82
+ checked=$((checked + 1))
83
+
84
+ # Emit one "<kind>\t<value>" line per step. A bare string and {"skill": ...} are both skills;
85
+ # {"playbook": ...} is a composition.
86
+ steps="$(python3 -c '
87
+ import json, sys
88
+ with open(sys.argv[1]) as fh:
89
+ pb = json.load(fh)
90
+ for s in pb.get("steps", []):
91
+ if isinstance(s, str):
92
+ print("skill\t" + s)
93
+ elif isinstance(s, dict):
94
+ if "skill" in s:
95
+ print("skill\t" + str(s["skill"]))
96
+ elif "playbook" in s:
97
+ print("playbook\t" + str(s["playbook"]))
98
+ ' "$pb")" || {
99
+ echo "check-public-playbook-steps.sh: error: could not parse $rel" >&2
100
+ exit 2
101
+ }
102
+
103
+ while IFS=$'\t' read -r kind value; do
104
+ [ -n "${kind:-}" ] || continue
105
+ case "$kind" in
106
+ skill)
107
+ target="skills/$value/SKILL.md"
108
+ if [ ! -f "$REPO_ROOT/$target" ]; then
109
+ echo "VIOLATION $rel -> step '$value': no $target on disk"
110
+ violations=$((violations + 1))
111
+ elif [ "$(classify "$target")" = "BLOCKED" ]; then
112
+ echo "VIOLATION $rel -> step '$value': $target is blocklisted, so this public playbook cannot load in the mirror"
113
+ violations=$((violations + 1))
114
+ fi
115
+ ;;
116
+ playbook)
117
+ target="skills/jenga/playbooks/$value.json"
118
+ if [ ! -f "$REPO_ROOT/$target" ]; then
119
+ echo "VIOLATION $rel -> composes '$value': no $target on disk"
120
+ violations=$((violations + 1))
121
+ elif [ "$(classify "$target")" = "BLOCKED" ]; then
122
+ echo "VIOLATION $rel -> composes '$value': $target is blocklisted, so the composed id will not resolve in the mirror"
123
+ violations=$((violations + 1))
124
+ fi
125
+ ;;
126
+ esac
127
+ done <<< "$steps"
128
+ done
129
+
130
+ if [ "$violations" -gt 0 ]; then
131
+ echo "check-public-playbook-steps.sh: $violations violation(s) across $checked public playbook(s) ($skipped_private private playbook(s) not checked)"
132
+ exit 1
133
+ fi
134
+
135
+ echo "check-public-playbook-steps.sh: OK — $checked public playbook(s) checked, every step ships publicly ($skipped_private private playbook(s) skipped)"
136
+ exit 0
@@ -129,7 +129,7 @@ For **each task ID** listed in the story's `tasks:` frontmatter array:
129
129
  ```bash
130
130
  bash skills/j-close-story/scripts/check-privatized.sh <task-id> "project/board/tasks/<task-id>_*.md"
131
131
  ```
132
- This is a **static** check (per `templates/SCRUM_BOARD_SCHEMA.md`'s "Static
132
+ This is a **static** check (per `$([ -f templates/SCRUM_BOARD_SCHEMA.md ] && echo templates/SCRUM_BOARD_SCHEMA.md || echo node_modules/@jenga-ai/agent/templates/SCRUM_BOARD_SCHEMA.md)`'s "Static
133
133
  vs. Reactive Status Setting" section) — it derives the task's touched-file
134
134
  list from its own EST-tagged commit history (same technique as Step 2) and
135
135
  tests every file against `.publicignore`, with **no dependency on any
@@ -58,7 +58,7 @@ Extract the following named values for use throughout this skill:
58
58
  These values must be read fresh on each invocation. Never use hardcoded fallbacks.
59
59
 
60
60
  ### 1. Check for `project/todo.md`
61
- Run `bash scripts/todo_manager.sh exists`. If it exits non-zero, inform the user there are no queued tasks and exit.
61
+ Run `bash "$([ -f scripts/todo_manager.sh ] && echo scripts/todo_manager.sh || echo node_modules/@jenga-ai/agent/scripts/todo_manager.sh)" exists`. If it exits non-zero, inform the user there are no queued tasks and exit.
62
62
 
63
63
  ### 1.5. Story-Bundle Execution Mode
64
64
 
@@ -284,7 +284,7 @@ No patch file is written on success.
284
284
  If `/do` is invoked with a task ID (`E##_S##_T##`) or a plain-text title, skip this section and use the normal task execution path (steps 2–8 below).
285
285
 
286
286
  ### 2. List tasks and let the user choose
287
- Run `bash scripts/todo_manager.sh list` to display the queued tasks. Ask the user:
287
+ Run `bash "$([ -f scripts/todo_manager.sh ] && echo scripts/todo_manager.sh || echo node_modules/@jenga-ai/agent/scripts/todo_manager.sh)" list` to display the queued tasks. Ask the user:
288
288
  - Execute a specific task (by number or title)
289
289
  - Execute the next task from the top of the list
290
290
  - Exit
@@ -294,14 +294,14 @@ Run `bash scripts/todo_manager.sh list` to display the queued tasks. Ask the use
294
294
  Before resolving a task for execution, inspect the selected entry's ID:
295
295
 
296
296
  - **Epic (`E##`)** — the entry refers to a whole epic that has not yet been broken into stories.
297
- Use the scrum-master agent to read the epic's board file (`$(bash scripts/board_resolver.sh)epics/`) and decompose it into stories. For each story produced:
298
- 1. Write a story file to `$(bash scripts/board_resolver.sh)stories/`.
299
- 2. Run `bash scripts/todo_manager.sh add '<story title>: <E##_S##>'`
300
- After breakdown, Run `bash scripts/todo_manager.sh remove '<epic entry title>'` and go back to step 2 so the new stories are visible.
297
+ Use the scrum-master agent to read the epic's board file (`$(bash "$([ -f scripts/board_resolver.sh ] && echo scripts/board_resolver.sh || echo node_modules/@jenga-ai/agent/scripts/board_resolver.sh)")epics/`) and decompose it into stories. For each story produced:
298
+ 1. Write a story file to `$(bash "$([ -f scripts/board_resolver.sh ] && echo scripts/board_resolver.sh || echo node_modules/@jenga-ai/agent/scripts/board_resolver.sh)")stories/`.
299
+ 2. Run `bash "$([ -f scripts/todo_manager.sh ] && echo scripts/todo_manager.sh || echo node_modules/@jenga-ai/agent/scripts/todo_manager.sh)" add '<story title>: <E##_S##>'`
300
+ After breakdown, Run `bash "$([ -f scripts/todo_manager.sh ] && echo scripts/todo_manager.sh || echo node_modules/@jenga-ai/agent/scripts/todo_manager.sh)" remove '<epic entry title>'` and go back to step 2 so the new stories are visible.
301
301
 
302
302
  - **Story (`E##_S##`) with no tasks** — the entry refers to a story that has not yet been broken into tasks.
303
- Check `$(bash scripts/board_resolver.sh)tasks/` for any task files whose front-matter `story_id` matches this story. If none exist, use the scrum-master agent to read the story's board file and decompose it into tasks. For each task produced:
304
- 1. Write a task file to `$(bash scripts/board_resolver.sh)tasks/`.
303
+ Check `$(bash "$([ -f scripts/board_resolver.sh ] && echo scripts/board_resolver.sh || echo node_modules/@jenga-ai/agent/scripts/board_resolver.sh)")tasks/` for any task files whose front-matter `story_id` matches this story. If none exist, use the scrum-master agent to read the story's board file and decompose it into tasks. For each task produced:
304
+ 1. Write a task file to `$(bash "$([ -f scripts/board_resolver.sh ] && echo scripts/board_resolver.sh || echo node_modules/@jenga-ai/agent/scripts/board_resolver.sh)")tasks/`.
305
305
  After breakdown, keep the story entry in `project/todo.md` (tasks are discovered from it automatically). Go back to step 2.
306
306
 
307
307
  - **Story (`E##_S##`) with existing tasks**, or **Task (`E##_S##_T##`)** — no breakdown needed; proceed to step 4.
@@ -310,7 +310,7 @@ Before resolving a task for execution, inspect the selected entry's ID:
310
310
  Each todo entry uses the format: `<mission title>: <E##_S##_T##>` (or `E##_S##` if no task ID).
311
311
 
312
312
  Before starting:
313
- 1. Locate and read the matching file from `$(bash scripts/board_resolver.sh)tasks/` (or `$(bash scripts/board_resolver.sh)stories/` if story-level)
313
+ 1. Locate and read the matching file from `$(bash "$([ -f scripts/board_resolver.sh ] && echo scripts/board_resolver.sh || echo node_modules/@jenga-ai/agent/scripts/board_resolver.sh)")tasks/` (or `$(bash "$([ -f scripts/board_resolver.sh ] && echo scripts/board_resolver.sh || echo node_modules/@jenga-ai/agent/scripts/board_resolver.sh)")stories/` if story-level)
314
314
  2. If the file does not exist, warn the user and skip — do not proceed with a task that has no scrum board definition
315
315
  3. Present a brief summary of the task: title, acceptance criteria, parent story, parent epic
316
316
 
@@ -343,7 +343,7 @@ After override validation (step 4.1) and before branching on `execution_scope` i
343
343
 
344
344
  3. **Overwrite `execution_scope` to `inline`** in the task's frontmatter, unconditionally — `--trivial` always forces `inline`, never a softer "lightest safe tier."
345
345
 
346
- 4. **Record the override for audit**, reusing the existing `jenga_assigned` / `override_justification` pairing already defined in `templates/SCRUM_BOARD_SCHEMA.md` for exactly this situation ("scope overridden by a human"), rather than inventing a new field:
346
+ 4. **Record the override for audit**, reusing the existing `jenga_assigned` / `override_justification` pairing already defined in `$([ -f templates/SCRUM_BOARD_SCHEMA.md ] && echo templates/SCRUM_BOARD_SCHEMA.md || echo node_modules/@jenga-ai/agent/templates/SCRUM_BOARD_SCHEMA.md)` for exactly this situation ("scope overridden by a human"), rather than inventing a new field:
347
347
  - Set `jenga_assigned: false` (if not already `false`).
348
348
  - Set (or append to, if already present) `override_justification`:
349
349
  ```
@@ -368,7 +368,7 @@ After override validation (step 4.1) and before branching on `execution_scope` i
368
368
 
369
369
  After resolving the task context (step 4), passing override validation (step 4.1), and applying the `--trivial` dispatch-time override if present (step 4.1.5), read `execution_scope` from the task frontmatter.
370
370
 
371
- **Locked-task dispatch guard (defense-in-depth).** Before branching on `execution_scope` below, read `crucial_level` from the task frontmatter (per `templates/SCRUM_BOARD_SCHEMA.md`'s Crucial Flag Fields). If `crucial_level: locked`:
371
+ **Locked-task dispatch guard (defense-in-depth).** Before branching on `execution_scope` below, read `crucial_level` from the task frontmatter (per `$([ -f templates/SCRUM_BOARD_SCHEMA.md ] && echo templates/SCRUM_BOARD_SCHEMA.md || echo node_modules/@jenga-ai/agent/templates/SCRUM_BOARD_SCHEMA.md)`'s Crucial Flag Fields). If `crucial_level: locked`:
372
372
 
373
373
  - This task MUST be routed through the inline execution path below — no worktree, no developer subagent — regardless of what `execution_scope` currently reads. This guards against a locked task reaching dispatch with a non-`inline` `execution_scope` (a race, a manually edited file, or a task added to a story's `tasks:` list after `skills/jenga/SKILL.md` Phase 0.5's Rule 4 last ran).
374
374
  - If `execution_scope` is already `inline`, proceed directly to the inline steps below — no correction needed.
@@ -389,10 +389,10 @@ After resolving the task context (step 4), passing override validation (step 4.1
389
389
  1. Read the task file and load its full content (description, acceptance criteria). Do NOT create a worktree. Do NOT spawn a developer subagent.
390
390
  2. Implement the task inline — make the required changes to files directly in the current session.
391
391
  3. Run the smoke test harness before committing anything:
392
- - Run `bash scripts/smoke-harness.sh <changed_file>...`, passing the paths changed in step 2. With no arguments the harness infers them from `git diff --name-only HEAD`. It exits `0` on pass and `1` on failure.
393
- - If `scripts/smoke-harness.sh` does not exist, log a warning and treat the result as a pass:
392
+ - Run `bash "$([ -f scripts/smoke-harness.sh ] && echo scripts/smoke-harness.sh || echo node_modules/@jenga-ai/agent/scripts/smoke-harness.sh)" <changed_file>...`, passing the paths changed in step 2. With no arguments the harness infers them from `git diff --name-only HEAD`. It exits `0` on pass and `1` on failure.
393
+ - If neither `scripts/smoke-harness.sh` nor `node_modules/@jenga-ai/agent/scripts/smoke-harness.sh` exists, log a warning and treat the result as a pass:
394
394
  ```
395
- WARNING [<task_id>]: scripts/smoke-harness.sh not found. Smoke test skipped (stub pass).
395
+ WARNING [<task_id>]: smoke-harness.sh not found. Smoke test skipped (stub pass).
396
396
  ```
397
397
  4. **If the smoke test exits non-zero**:
398
398
  - **If this is a `--trivial`-forced run** (marker set in step 4.1.5 — and `crucial_level` is not `locked`, which never falls back, per 4.1.5's precedence note): do NOT write `status: Failed`. `--trivial` always forces `inline` with no softer "lightest safe tier" to fall back to first, so a smoke-harness failure here goes straight to the shared `#### Fallback to Full Task-Scope Pipeline` procedure below (origin: `trivial`). Do not proceed with the remaining inline steps below — the Fallback procedure takes over from here.
@@ -431,10 +431,10 @@ After resolving the task context (step 4), passing override validation (step 4.1
431
431
  1. **Spawn a developer subagent** (Agent tool, `subagent_type: "developer"`) with the same sender object and context payload as step 5 would use, but with an explicit instruction added to the dispatch prompt: **do not create a worktree** — implement directly against the current checkout (the session's existing working tree), not an isolated `.claude/worktrees/<slug>` copy. This is the one concrete difference from the step-5 `task` path: everything else about how the subagent implements the task (reading the task file, following acceptance criteria, following repo conventions) is unchanged.
432
432
 
433
433
  2. **After the developer subagent reports implementation complete**, run the smoke test harness using the same invocation convention as `### 4.2. Inline Execution Path`:
434
- - Run `bash scripts/smoke-harness.sh <changed_file>...`, passing the paths the subagent changed. With no arguments the harness infers them from `git diff --name-only HEAD`. It exits `0` on pass and `1` on failure.
435
- - If `scripts/smoke-harness.sh` does not exist, log a warning and treat the result as a pass:
434
+ - Run `bash "$([ -f scripts/smoke-harness.sh ] && echo scripts/smoke-harness.sh || echo node_modules/@jenga-ai/agent/scripts/smoke-harness.sh)" <changed_file>...`, passing the paths the subagent changed. With no arguments the harness infers them from `git diff --name-only HEAD`. It exits `0` on pass and `1` on failure.
435
+ - If neither `scripts/smoke-harness.sh` nor `node_modules/@jenga-ai/agent/scripts/smoke-harness.sh` exists, log a warning and treat the result as a pass:
436
436
  ```
437
- WARNING [<task_id>]: scripts/smoke-harness.sh not found. Smoke test skipped (stub pass).
437
+ WARNING [<task_id>]: smoke-harness.sh not found. Smoke test skipped (stub pass).
438
438
  ```
439
439
 
440
440
  3. **If the smoke test passes**:
@@ -528,8 +528,8 @@ Additionally, if the completed work introduces user-facing changes, update `READ
528
528
  ### 7. After successful completion
529
529
  - Check for any `_INSTRUCTIONS.md` files in `project/instructions/` whose ID matches the completed task. If found, present them to the user and explain that these actions must be completed before the feature will work correctly.
530
530
  - Invoke the `/commit` skill to commit the work (if not already committed by the developer)
531
- - Run `bash scripts/todo_manager.sh remove '<task title>'` to remove the completed task from `project/todo.md`
532
- - Run `bash scripts/todo_manager.sh teardown` to delete `project/todo.md` if it is now effectively empty
531
+ - Run `bash "$([ -f scripts/todo_manager.sh ] && echo scripts/todo_manager.sh || echo node_modules/@jenga-ai/agent/scripts/todo_manager.sh)" remove '<task title>'` to remove the completed task from `project/todo.md`
532
+ - Run `bash "$([ -f scripts/todo_manager.sh ] && echo scripts/todo_manager.sh || echo node_modules/@jenga-ai/agent/scripts/todo_manager.sh)" teardown` to delete `project/todo.md` if it is now effectively empty
533
533
 
534
534
  ### 8. Loop
535
535
  Go back to step 1.
@@ -101,7 +101,7 @@ Every candidate identified above as "missing documentation for new things added
101
101
  1. **Check for `.publicignore` at the repo root.** Most projects using this framework will not have one (they haven't adopted `/mirror-public`) — if it's absent, **skip this entire sub-step**. Fall through to flagging every "missing documentation" candidate exactly as `3.` above already would, with no further filtering.
102
102
  2. **If `.publicignore` exists**, for each "missing documentation" candidate, determine its source path (relative to the repo root) and classify it by running:
103
103
  ```
104
- scripts/check-publicignore-match.sh <path> [<path> ...]
104
+ bash "$([ -f scripts/check-publicignore-match.sh ] && echo scripts/check-publicignore-match.sh || echo node_modules/@jenga-ai/agent/scripts/check-publicignore-match.sh)" <path> [<path> ...]
105
105
  ```
106
106
  This reuses `/mirror-public`'s own `mirror.sh` matching logic (`rsync --exclude-from=.publicignore`) — a file this script reports `BLOCKED` is guaranteed to be a file `/mirror-public --dry-run` would also report as "would be blocked", and vice versa for `PUBLIC`. It needs no network access and does not require `/mirror-public` to be configured.
107
107
  3. **`BLOCKED`** → do not flag this candidate as missing documentation. It's private-only and will never ship to the public mirror, so public docs coverage is not applicable.
@@ -181,6 +181,17 @@ After all writes are done, print a summary:
181
181
  If nothing needed updating, print: `✅ Documentation is already in sync with the source.`
182
182
  If minification could not reach the target, note: `⚠️ docs/API.md reduced to X% (target was Y% — essential content limit reached).`
183
183
 
184
+ ### 8. GitHub Pages site follow-up (E41_S13)
185
+
186
+ If this run updated `project/.wiki/documentation.md`, `project/.wiki/intro-guide.md`, or any
187
+ `project/.wiki/concepts/*.md` file, also re-run `bash "$([ -f scripts/build-pages-site.sh ] && echo scripts/build-pages-site.sh || echo node_modules/@jenga-ai/agent/scripts/build-pages-site.sh)"` afterward. That
188
+ script deterministically regenerates the GitHub Pages documentation site's wiki-derived pages
189
+ (`docs/getting-started.md`, `docs/concepts.md`, `docs/skills.md`, `docs/agents.md`,
190
+ `docs/hooks.md`, `docs/mcp-tools.md`, `docs/reference.md`) from the wiki content this step just
191
+ updated — without re-running it, the Pages site silently drifts from the wiki the same way
192
+ `README.md` and the wiki itself have drifted from each other before. Mention this in the
193
+ summary output (e.g. `🔄 Re-ran build-pages-site.sh to refresh the Pages site`).
194
+
184
195
  ---
185
196
 
186
197
  ## Reference files
@@ -36,7 +36,7 @@ This file is generated/synced by `scripts/generate-j-alias.sh idea` from `skills
36
36
 
37
37
  3. **Add to `project/ideas.md`** by running:
38
38
  ```
39
- bash scripts/idea_manager.sh add '<idea>'
39
+ bash "$([ -f scripts/idea_manager.sh ] && echo scripts/idea_manager.sh || echo node_modules/@jenga-ai/agent/scripts/idea_manager.sh)" add '<idea>'
40
40
  ```
41
41
 
42
42
  4. **Ask the user**: "Capture another idea, or done?"
@@ -144,10 +144,11 @@ This script handles all scaffolding in one step:
144
144
  7. Creates `project/configs/scope-thresholds.json` with default execution-scope thresholds (consumed by `/jenga` and `/do`, which halt if it's missing)
145
145
  8. Creates `project/data/baselines.json`
146
146
  9. Creates `project/logs/events.json`
147
- 10. Creates `docs/STRATEGY.md` — a strategic brief stub intended for investors, partners, and the product team
148
- 11. Creates `CHANGELOG.md` from the shared template — a running log of notable changes, seeded with an `[Unreleased]` section
149
- 12. Applies the chosen visibility mode via `scripts/apply-project-visibility.sh`, which records it as `project_files_visibility` in `jenga.config.json` and performs any `.gitignore` change
150
- 13. Stages and commits all files with the message `init: scaffold project structure and workflow config`
147
+ 10. Creates `project/knowledge-graph/STUB_SCHEMA.md` from the shared template and `project/knowledge-graph/graph.json` seeded with empty `nodes`/`edges` arrays — consumed by `/uncharted`'s conversational elicitation flow
148
+ 11. Creates `docs/STRATEGY.md` — a strategic brief stub intended for investors, partners, and the product team
149
+ 12. Creates `CHANGELOG.md` from the shared template — a running log of notable changes, seeded with an `[Unreleased]` section
150
+ 13. Applies the chosen visibility mode via `scripts/apply-project-visibility.sh`, which records it as `project_files_visibility` in `jenga.config.json` and performs any `.gitignore` change
151
+ 14. Stages and commits all files with the message `init: scaffold project structure and workflow config`
151
152
 
152
153
  The visibility mode is validated before any scaffolding happens, so an invalid
153
154
  value fails fast and leaves nothing behind. It is applied before the commit, so
@@ -3,6 +3,7 @@ project/board/stories
3
3
  project/board/tasks
4
4
  project/instructions
5
5
  project/configs
6
+ project/knowledge-graph
6
7
  project/data
7
8
  project/queue
8
9
  project/rapports/problems
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # ---------------------------------------------------------------------------
3
- # skills/init/scripts/detect-existing-codebase.sh
3
+ # skills/j-init/scripts/detect-existing-codebase.sh
4
4
  #
5
5
  # Deterministic front half of the /init "detect existing project state" step.
6
6
  # /init currently always scaffolds as though the target directory were empty.
@@ -12,7 +12,7 @@
12
12
  # This script answers exactly one question -- "what kind of directory is
13
13
  # this?" -- and nothing else. It never scaffolds, never prompts, never runs
14
14
  # /uncharted, and never modifies anything on disk. Deciding what to DO with
15
- # the verdict is agent judgement and lives in skills/init/SKILL.md.
15
+ # the verdict is agent judgement and lives in skills/j-init/SKILL.md.
16
16
  #
17
17
  # ---------------------------------------------------------------------------
18
18
  # VERDICTS -- exactly one is printed on stdout, nothing else
@@ -8,9 +8,9 @@ VISIBILITY_SCRIPT="$SCRIPT_DIR/apply-project-visibility.sh"
8
8
  # ─── Resolve the package root that owns templates/ and lib/ ──────────────────
9
9
  # postinstall.js mirrors only skills/ and agents/ into .claude/ and .agents/ —
10
10
  # templates/ and lib/ are never copied there, so a script running from a
11
- # mirrored copy (.claude/skills/init/scripts/ or .agents/skills/init/scripts/)
11
+ # mirrored copy (.claude/skills/j-init/scripts/ or .agents/skills/j-init/scripts/)
12
12
  # cannot reach its siblings via a fixed ../../../ climb the way it can in this
13
- # monorepo checkout, where init.sh actually lives at skills/init/scripts/ with
13
+ # monorepo checkout, where init.sh actually lives at skills/j-init/scripts/ with
14
14
  # templates/ and lib/ three levels up. Consumers instead have them inside the
15
15
  # installed npm package.
16
16
  if [[ -d "$SCRIPT_DIR/../../../templates" ]]; then
@@ -81,6 +81,17 @@ echo '{}' > project/data/baselines.json
81
81
  echo "→ Creating events.json..."
82
82
  echo '[]' > project/logs/events.json
83
83
 
84
+ # ─── 8.5. Create project/knowledge-graph/{STUB_SCHEMA.md,graph.json} ─────────
85
+ # Consumed by skills/uncharted's conversational elicitation flow (`onboard`,
86
+ # `segment --mode investigate`), which writes coarse graph nodes/edges to
87
+ # graph.json per the stub schema — both must exist before that flow's first
88
+ # write, per templates/KNOWLEDGE_GRAPH_STUB_SCHEMA_TEMPLATE.md's own header
89
+ # comment (E20_S08_T01; replaced wholesale once E20_S01's real schema lands).
90
+ echo "→ Copying knowledge-graph STUB_SCHEMA.md from template..."
91
+ cp "$PKG_ROOT/templates/KNOWLEDGE_GRAPH_STUB_SCHEMA_TEMPLATE.md" project/knowledge-graph/STUB_SCHEMA.md
92
+ echo "→ Creating project/knowledge-graph/graph.json..."
93
+ echo '{"nodes": [], "edges": []}' > project/knowledge-graph/graph.json
94
+
84
95
  # ─── 9. Create docs/STRATEGY.md ──────────────────────────────────────────────
85
96
  echo "→ Creating docs/STRATEGY.md (strategic brief for investors, partners, and the product team)..."
86
97
  mkdir -p docs
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: j.playbook
3
+ description: Invoke a specific /jenga playbook directly by ID, skipping natural-language matching entirely and going straight to chain confirmation and execution.
4
+ keywords:
5
+ - run playbook
6
+ - invoke playbook by id
7
+ - playbook direct
8
+ - execute playbook
9
+ examples:
10
+ - "j.playbook brainstorm-to-mirror"
11
+ - "run the understand-then-ship playbook"
12
+ - "invoke playbook by id"
13
+ ---
14
+
15
+ # Playbook — Direct Playbook Invocation by ID
16
+
17
+ ## Purpose
18
+
19
+ `/jenga`'s natural-language branch (`skills/jenga/SKILL.md`) proposes a playbook only when
20
+ free-text intent doesn't cleanly resolve to a single skill and `skills/jenga/scripts/match-playbook.sh`
21
+ finds a confident match. That's the right default when the user doesn't know a playbook exists or
22
+ doesn't know its exact id. `j.playbook <id>` is the other case: the user already knows exactly
23
+ which playbook they want and names it directly — this skill skips `detect-nl-intent.sh` and
24
+ `match-playbook.sh` entirely and goes straight to the same confirmation-and-execution machinery
25
+ `/jenga`'s natural-language branch already uses (`E53_S06_T03`, per story `E53_S06`'s fourth and
26
+ fifth Acceptance Criteria).
27
+
28
+ This skill never re-implements chain confirmation, sequential execution, `forward_from`/`resolve`
29
+ resolution, or composition/conditional handling — all of that is `skills/jenga/SKILL.md`'s
30
+ Natural-language branch steps 5a-5e, reused here by reference. The only genuinely new logic in
31
+ this skill is id resolution (step 1 below).
32
+
33
+ ## Instructions
34
+
35
+ 1. **Resolve the id** — invoke `skills/jenga/scripts/load-playbooks.sh lookup "<id>"`
36
+ (`E53_S06_T02`), where `<id>` is this skill's argument. Branch on the returned `status` field:
37
+
38
+ - **`"not_found"`** — no playbook with that id exists. Tell the user plainly that no such
39
+ playbook was found. As a "did you mean" nudge, you may additionally invoke
40
+ `skills/jenga/scripts/load-playbooks.sh` (no arguments, full-catalog mode) and list the
41
+ available `id`s from its output — use your judgment on whether this is helpful given the
42
+ specific id the user typed. Halt this invocation; do not proceed to step 2.
43
+ - **`"invalid"`** — a file for that id exists but failed load-time validation. Report the
44
+ `reason` field to the user **verbatim** — never a generic "not found" message, since this is
45
+ a materially different situation from `not_found` (the story's explicit distinguishing
46
+ requirement, `E53_S06_T02`). Halt this invocation; do not proceed to step 2.
47
+ - **`"valid"`** — continue to step 2, using the returned `playbook` object's `id`, `name`, and
48
+ `steps` fields. This object has the exact same field shape as one entry from
49
+ `load-playbooks.sh`'s full-catalog output — the same shape `match-playbook.sh`'s
50
+ `playbook_match` result carries `id`/`name`/`steps` in, for `/jenga`'s Natural-language
51
+ branch step 5.
52
+
53
+ 2. **Confirm and execute the chain** — follow `skills/jenga/SKILL.md`'s Natural-language branch
54
+ **step 5, sub-steps a through e, verbatim** (conditional/origin metadata resolution; render and
55
+ confirm the chain via `render-playbook-confirmation.sh`; the reply loop; sequential-runner
56
+ `init`; the execute-in-a-loop procedure covering skip evaluation, `forward_from`/`resolve`
57
+ resolution (`E53_S06_T01`), invocation, `advance`, and halt handling) exactly as written there
58
+ — do not duplicate that prose here. Use the `id`/`name`/`steps` resolved in step 1 above
59
+ wherever that section refers to `match-playbook.sh`'s `playbook_id`/`name`/`steps` output; every
60
+ other detail (including how `forward_from`, `resolve`, conditionals, and composed/nested steps
61
+ are handled) is identical, with no special-casing for this direct-invocation entry point.
62
+
63
+ The one framing difference: step 5b's confirmation prompt need not (and should not) present
64
+ this as a *proposal* the user might not have expected — they named this playbook explicitly by
65
+ id. Otherwise render, confirm, and execute exactly as that section already does.
66
+
67
+ 3. **On a `complete` or `halted` result** (per step 5e-v/5e-vi), report exactly as that section
68
+ already specifies. This skill does not continue into any further phase — there is no `/jenga`
69
+ Phase 1-4 to fall into here, since this is a direct entry point, not `/jenga` itself.
70
+
71
+ ## Edge Cases
72
+
73
+ - **The looked-up playbook contains a `resolve` step or composed/nested steps** — handled
74
+ identically to the natural-language path; no special-casing exists or is ever needed here (per
75
+ story `E53_S06`'s fifth Acceptance Criterion).
76
+ - **The user cancels at the chain confirmation step (step 5c)** — identical posture to
77
+ `/jenga`'s own playbook-confirmation cancellation: halt immediately after relaying the
78
+ cancellation acknowledgement, with no step of the chain executed.
79
+ - **`load-playbooks.sh lookup` itself fails unexpectedly (exit code 2, usage/setup error)** — this
80
+ is an environment/setup problem, not a normal `not_found`/`invalid` result; surface the script's
81
+ stderr output to the user rather than treating it as either playbook-lookup outcome.
@@ -30,7 +30,7 @@ This file is generated/synced by `scripts/generate-j-alias.sh proceed` from `ski
30
30
 
31
31
  3. **Determine the next action**:
32
32
  - If there are tasks in `Pending` or `In Progress` status that have not yet been assigned to the developer, identify them.
33
- - If outstanding tasks are ready for implementation, write a session handoff to `project/queue/handoffs/scrum-master-<session_id>-<task_id>.json` (per-session path — see `templates/SCRUM_BOARD_SCHEMA.md`'s `handoffs/` section; use the first task ID, or `batch` if several) with `"status": "planning_complete"` so that `on_session_end.sh` routes them to the developer queue.
33
+ - If outstanding tasks are ready for implementation, write a session handoff to `project/queue/handoffs/scrum-master-<session_id>-<task_id>.json` (per-session path — see `$([ -f templates/SCRUM_BOARD_SCHEMA.md ] && echo templates/SCRUM_BOARD_SCHEMA.md || echo node_modules/@jenga-ai/agent/templates/SCRUM_BOARD_SCHEMA.md)`'s `handoffs/` section; use the first task ID, or `batch` if several) with `"status": "planning_complete"` so that `on_session_end.sh` routes them to the developer queue.
34
34
  - If all tasks are complete, check for epic/story rollup and update board statuses accordingly.
35
35
 
36
36
  4. **Report** a clear summary to the user: what is done, what is in progress, what is next — and which agent will handle it.
@@ -261,7 +261,7 @@ Implementation: `bash skills/j-publish/scripts/generate_release_notes.sh [--targ
261
261
 
262
262
  Release-note rules:
263
263
  - The last publish tag is the highest semver tag on the current branch that also has a matching ledger entry in `project/logs/publish-history.json`.
264
- - **Default target (no `--output`):** the repo-root `CHANGELOG.md` — created from `templates/CHANGELOG_TEMPLATE.md` first if it doesn't exist yet (backward-compat for projects scaffolded before this convention). New Features/Bug Fixes/Other/Completed-task entries since the last publish tag are appended under the existing `## [Unreleased]` heading's subsections; everything else in the file (prior versioned entries, manual edits) is preserved. Entries already present are deduped (matched by commit short-sha or task id), so re-running with no new commits produces zero diff.
264
+ - **Default target (no `--output`):** the repo-root `CHANGELOG.md` — created from `$([ -f templates/CHANGELOG_TEMPLATE.md ] && echo templates/CHANGELOG_TEMPLATE.md || echo node_modules/@jenga-ai/agent/templates/CHANGELOG_TEMPLATE.md)` first if it doesn't exist yet (backward-compat for projects scaffolded before this convention). New Features/Bug Fixes/Other/Completed-task entries since the last publish tag are appended under the existing `## [Unreleased]` heading's subsections; everything else in the file (prior versioned entries, manual edits) is preserved. Entries already present are deduped (matched by commit short-sha or task id), so re-running with no new commits produces zero diff.
265
265
  - **`--output <path>`:** writes a standalone, disposable draft to that path instead (the pre-E36 behavior) — `CHANGELOG.md` is not touched in this mode.
266
266
  - If no prior ledger-backed tag exists, a `--output` draft includes `> First release — full history included`; the standing `CHANGELOG.md` merge mode has no equivalent banner (it just merges full history into `[Unreleased]` like any other run).
267
267
  - Scrum-board enrichment is best-effort only; missing or unreadable board data never fails the command.
@@ -240,6 +240,35 @@ automated and human halves of the flow:
240
240
  `bash skills/j-publish/scripts/npm_stage_inspect.sh approve <stage-id> --otp <otp>`
241
241
  from their own machine. `reject` (same script) is available to either
242
242
  side to discard a staged candidate.
243
+ - **Stage-id capture reads the CI run's own log, not a local `npm stage
244
+ list` call.** Because the actual `npm stage publish --provenance` call
245
+ runs inside the dispatched Actions run, the local `npm_stage_pipeline.sh`
246
+ process never sees npm's real stage-publish output — `STAGE_OUTPUT` for
247
+ an `npm-ci` target is only ever the fixed dispatch-summary string ("staged
248
+ via GitHub Actions workflow run: `<url>`"). Once `gh run watch` reports
249
+ the run finished successfully, phase 5 instead fetches the full run log
250
+ via `gh run view <run-id> --repo <github_repo> --log` and parses that
251
+ text for a `stage id: ...` line (or a JSON payload), using the same
252
+ parsing logic the `npm` target applies to its local output. **`npm stage
253
+ list ... --json` (the `npm` target's third fallback attempt) is never
254
+ invoked for `npm-ci`** — there is no local npm credential for this target
255
+ type, so a local `npm stage list` call would only ever fail with an
256
+ unrelated auth error.
257
+ - **Manual recovery when the log can't be parsed.** If no stage id can be
258
+ parsed from the run log, `npm_stage_pipeline.sh` exits `3`
259
+ (`EXIT_STAGE_FAILURE`) with a message stating that staging on the
260
+ registry likely **succeeded** — the workflow run itself exited 0, so the
261
+ `npm stage publish --provenance` step almost certainly ran — pointing at
262
+ `gh run view <run-id> --repo <github_repo> --log` (or the printed run
263
+ URL) to find the id by hand in the `npm stage publish` step output, and
264
+ printing an already-filled-in recovery command:
265
+ ```
266
+ bash skills/j-publish/scripts/write_ledger_entry.sh <target> npm-ci staged "" \
267
+ --version <version> --config <config> --stage-id <recovered-id> --dist-tag <tag>
268
+ ```
269
+ `<target>`, `<version>`, `<config>`, and `<tag>` are the real, already-known
270
+ values for the run that just happened — only `--stage-id` is left for the
271
+ operator to fill in by hand after reading it out of the run log.
243
272
 
244
273
  Same registry-existence precondition as a normal `npm-ci` deploy: staged
245
274
  publishing only applies to a package that has already had at least one
@@ -191,6 +191,9 @@ jobs:
191
191
  - name: Install dependencies
192
192
  run: npm ci
193
193
 
194
+ - name: Regenerate lib/legacy-shipped-paths.json (E26_S08_T03)
195
+ run: node scripts/generate-legacy-shipped-paths.js || echo "::warning::legacy-shipped-paths generation failed; publishing without an updated list"
196
+
194
197
  - name: Publish to npm
195
198
  run: npm publish --provenance --access ${NPM_ACCESS} --tag ${DIST_TAG}
196
199
 
@@ -207,6 +207,24 @@ if (( DRY_RUN )); then
207
207
  MODE_LABEL="dry-run"
208
208
  fi
209
209
 
210
+ # Regenerate lib/legacy-shipped-paths.json (E26_S08_T03) — incremental, no-network mode:
211
+ # folds this release's own skills/+agents/ tree into the running cumulative record so the
212
+ # artifact this version SHIPS already reflects what it is about to publish, ready to seed
213
+ # a pre-manifest consumer's first manifest on their NEXT upgrade. Runs on both dry-run and
214
+ # live publish (it only writes a local file, never touches the registry) so a dry-run
215
+ # rehearsal surfaces a generation failure too. Best-effort: a failure here must not block
216
+ # a publish, matching this repo's existing fail-toward-doing-nothing posture for generated
217
+ # artifacts (see lib/generate-skill-allow-list.js's equivalent best-effort call sites).
218
+ GENERATE_LEGACY_PATHS_SCRIPT="$REPO_ROOT/scripts/generate-legacy-shipped-paths.js"
219
+ if [[ -f "$GENERATE_LEGACY_PATHS_SCRIPT" ]]; then
220
+ log_info "Regenerating lib/legacy-shipped-paths.json (incremental, no network)…"
221
+ if ! node "$GENERATE_LEGACY_PATHS_SCRIPT"; then
222
+ log_warn "legacy-shipped-paths generation failed; publishing without an updated list"
223
+ fi
224
+ else
225
+ log_warn "scripts/generate-legacy-shipped-paths.js not found; skipping legacy-paths regeneration"
226
+ fi
227
+
210
228
  echo "========== NPM PUBLISH PIPELINE =========="
211
229
  printf 'Package: %s\n' "$PACKAGE_NAME"
212
230
  printf 'Version: %s\n' "$PACKAGE_VERSION"