@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.
Files changed (97) hide show
  1. package/README.md +85 -78
  2. package/agents/developer.md +1 -1
  3. package/agents/scrum-master.md +20 -2
  4. package/agents/tester.md +3 -3
  5. package/hooks/on_session_end.sh +5 -5
  6. package/lib/generate-agent-context.js +2 -2
  7. package/lib/generate-copilot-hooks.js +1 -1
  8. package/lib/generate-skill-allow-list.js +37 -3
  9. package/lib/mirror.js +1 -1
  10. package/lib/postinstall-manifest.js +1 -1
  11. package/lib/skill-allow-list.json +2 -2
  12. package/mcp/help/index.js +8 -17
  13. package/mcp/help/scan.js +73 -0
  14. package/package.json +6 -1
  15. package/project/app/api/parsers/knowledge-graph.js +100 -9
  16. package/project/app/api/routes/health.js +36 -0
  17. package/project/app/api/scripts/capture-snapshot.js +9 -6
  18. package/project/app/ui/dist/assets/{index-CdK3Qrep.css → index-BVR_7Owg.css} +1 -1
  19. package/project/app/ui/dist/assets/index-CtU2xLQm.js +104 -0
  20. package/project/app/ui/dist/index.html +2 -2
  21. package/project/app/ui/package.json +4 -0
  22. package/project/app/ui/scripts/build-snapshot-html.cjs +63 -2
  23. package/scripts/acquire-concurrency-slot.sh +1 -1
  24. package/scripts/apply-j-prefix.sh +46 -5
  25. package/scripts/audit-twin-divergence.sh +73 -5
  26. package/scripts/build-pages-site.sh +1 -1
  27. package/scripts/check-public-playbook-steps.sh +158 -52
  28. package/scripts/check-publicignore-match.sh +2 -2
  29. package/scripts/compute-deploy-reconcile.sh +5 -5
  30. package/scripts/delete-bare-skill-dirs.sh +330 -0
  31. package/scripts/generate-legacy-shipped-paths.js +2 -2
  32. package/scripts/idea_manager.sh +258 -3
  33. package/scripts/mark-deployed.sh +2 -2
  34. package/scripts/populate-knowledge-graph.entity-resolution.test.js +254 -0
  35. package/scripts/populate-knowledge-graph.js +213 -5
  36. package/scripts/populate-knowledge-graph.staleness.test.js +130 -0
  37. package/scripts/postinstall.js +1 -1
  38. package/scripts/repoint-skill-refs.sh +539 -0
  39. package/scripts/todo_manager.sh +1 -1
  40. package/scripts/verify-legacy-seed-reconcile.sh +10 -10
  41. package/scripts/verify-postinstall-reconcile.sh +7 -7
  42. package/scripts/write-context-digest.sh +1 -1
  43. package/skills/j-clearify/SKILL.md +2 -2
  44. package/skills/j-close-story/scripts/check-privatized.sh +4 -4
  45. package/skills/j-distribute/CONFIG_SCHEMA.md +82 -5
  46. package/skills/j-do/SKILL.md +101 -17
  47. package/skills/j-doc-sync/SKILL.md +1 -0
  48. package/skills/j-gitignore/SKILL.md +157 -0
  49. package/skills/j-gitignore/assets/jenga-paths.txt +50 -0
  50. package/skills/j-gitignore/scripts/_catalog.sh +105 -0
  51. package/skills/j-gitignore/scripts/audit-gitignore.sh +194 -0
  52. package/skills/j-gitignore/scripts/repair-gitignore.sh +226 -0
  53. package/skills/j-gitignore/scripts/untrack-jenga-files.sh +210 -0
  54. package/skills/j-idea/SKILL.md +78 -6
  55. package/skills/j-idea/assets/idea_template.md +1 -1
  56. package/skills/j-improve/SKILL.md +1 -1
  57. package/skills/j-init/SKILL.md +53 -14
  58. package/skills/j-init/assets/.gitignore_template +1 -2
  59. package/skills/j-init/assets/scope-thresholds_template.json +5 -2
  60. package/skills/j-init/scripts/apply-scaffold-visibility.sh +192 -0
  61. package/skills/j-init/scripts/init.sh +22 -8
  62. package/skills/j-playbook/SKILL.md +1 -1
  63. package/skills/j-publish/SKILL.md +1 -1
  64. package/skills/j-publish/adapters/npm-ci.md +6 -1
  65. package/skills/j-publish/adapters/npm.md +1 -1
  66. package/skills/j-publish/scripts/generate_release_notes.sh +1 -1
  67. package/skills/j-publish/scripts/npm_stage_inspect.sh +61 -0
  68. package/skills/j-reconcile/SKILL.md +2 -2
  69. package/skills/j-reconcile/scripts/detect-unlinked-code.sh +11 -11
  70. package/skills/j-redo/SKILL.md +1 -1
  71. package/skills/j-skillify/assets/init-new/assets/.gitignore_template +1 -2
  72. package/skills/j-spinoff/SKILL.md +1 -1
  73. package/skills/j-status/SKILL.md +15 -0
  74. package/skills/j-todo/SKILL.md +3 -1
  75. package/skills/j-uncharted/SKILL.md +55 -8
  76. package/skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md +69 -0
  77. package/skills/j-uncharted/scripts/detect-dependencies.sh +1 -1
  78. package/skills/j-uncharted/scripts/detect-tests.sh +1 -1
  79. package/skills/j-uncharted/scripts/elicitation-state.sh +46 -8
  80. package/skills/j-uncharted/scripts/validate-proposed-items.sh +1 -1
  81. package/skills/j-wtf/SKILL.md +1 -1
  82. package/skills/jenga/SKILL.md +43 -9
  83. package/skills/jenga/playbooks/board-hygiene.json +32 -0
  84. package/skills/jenga/playbooks/schema.json +73 -6
  85. package/skills/jenga/playbooks/understand-then-commit.json +19 -0
  86. package/skills/jenga/scripts/load-nl-catalog.sh +1 -1
  87. package/skills/jenga/scripts/load-playbooks.sh +23 -11
  88. package/skills/jenga/scripts/match-playbook.sh +1 -1
  89. package/templates/SCRUM_BOARD_SCHEMA.md +14 -1
  90. package/templates/SKILL_TEMPLATE.md +12 -0
  91. package/templates/permission-levels/level-4-elevated.json +1 -1
  92. package/templates/permission-levels/level-5-unrestricted.json +1 -1
  93. package/templates/playbook-types.json +34 -6
  94. package/project/app/ui/dist/assets/index-7fj-vllY.js +0 -104
  95. package/scripts/generate-j-alias.sh +0 -333
  96. package/skills/j-dev-done/SKILL.md +0 -53
  97. package/skills/j-dev-done/scripts/classify-commit-outcome.sh +0 -114
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env bash
2
+ # -----------------------------------------------------------------------------
3
+ # skills/j-gitignore/scripts/untrack-jenga-files.sh
4
+ #
5
+ # Removes Jenga-owned paths from git WITHOUT removing them from disk —
6
+ # `git rm -r --cached`, never a plain `git rm`. The files stay exactly where
7
+ # they are; git simply stops tracking them, and the .gitignore entries written
8
+ # by repair-gitignore.sh then keep them from coming back.
9
+ #
10
+ # Run repair-gitignore.sh FIRST. .gitignore has no effect on an already-tracked
11
+ # file, so untracking without the entries in place just means the next
12
+ # `git add -A` re-adds everything.
13
+ #
14
+ # ─── What "removed from origin" does and does not mean ──────────────────────
15
+ # With --commit --push, these paths disappear from the branch TIP on the
16
+ # remote: clone it fresh and they are gone. Earlier commits are UNTOUCHED —
17
+ # the blobs remain reachable in history, so this is not a secret-scrubbing
18
+ # tool. If something sensitive was committed, treat it as compromised, rotate
19
+ # it, and use a history-rewriting tool (git-filter-repo) separately.
20
+ #
21
+ # Invoked via `bash`, not executed directly.
22
+ #
23
+ # Usage:
24
+ # untrack-jenga-files.sh [project_root]
25
+ # [--tiers scaffold,hybrid,board,optional]
26
+ # [--dry-run] [--commit] [--push]
27
+ # [--allow-dirty-index]
28
+ #
29
+ # Options:
30
+ # --dry-run List what would be untracked; touch nothing.
31
+ # --commit Commit the removals. Without it, changes are left
32
+ # staged for you to inspect and commit yourself.
33
+ # --push Push to the branch's upstream. Requires --commit.
34
+ # Never implied — this is the only outward-facing step.
35
+ # --allow-dirty-index Proceed even if unrelated changes are already staged.
36
+ # Off by default so this can never sweep your staged
37
+ # work into its commit.
38
+ #
39
+ # Exit codes:
40
+ # 0 Success (including "nothing tracked — nothing to do")
41
+ # 1 Bad usage or missing prerequisite
42
+ # 2 Invalid tier
43
+ # 3 git operation failed
44
+ # 4 Refused: index already dirty, or --push without --commit
45
+ # -----------------------------------------------------------------------------
46
+
47
+ set -uo pipefail
48
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_catalog.sh"
49
+
50
+ info() { echo "[untrack] $*"; }
51
+ warn() { echo "[untrack] WARNING: $*"; }
52
+ err() { echo "[untrack] ERROR: $*" >&2; }
53
+
54
+ PROJECT_ROOT=""
55
+ TIERS="scaffold"
56
+ DRY_RUN=0
57
+ DO_COMMIT=0
58
+ DO_PUSH=0
59
+ ALLOW_DIRTY=0
60
+
61
+ while [ $# -gt 0 ]; do
62
+ case "$1" in
63
+ --tiers) TIERS="${2:-}"; shift 2 ;;
64
+ --tiers=*) TIERS="${1#*=}"; shift ;;
65
+ --dry-run) DRY_RUN=1; shift ;;
66
+ --commit) DO_COMMIT=1; shift ;;
67
+ --push) DO_PUSH=1; shift ;;
68
+ --allow-dirty-index) ALLOW_DIRTY=1; shift ;;
69
+ -h|--help)
70
+ echo "Usage: $(basename "$0") [project_root] [--tiers <csv>] [--dry-run] [--commit] [--push] [--allow-dirty-index]"
71
+ exit 0 ;;
72
+ -*) err "Unknown option: $1"; exit 1 ;;
73
+ *)
74
+ if [ -n "$PROJECT_ROOT" ]; then err "More than one project root given."; exit 1; fi
75
+ PROJECT_ROOT="$1"; shift ;;
76
+ esac
77
+ done
78
+
79
+ if [ "$DO_PUSH" -eq 1 ] && [ "$DO_COMMIT" -eq 0 ]; then
80
+ err "--push requires --commit. There would be nothing to push."
81
+ exit 4
82
+ fi
83
+
84
+ PROJECT_ROOT="${PROJECT_ROOT:-$PWD}"
85
+ validate_tiers "$TIERS" || exit 2
86
+ resolve_root "$PROJECT_ROOT" || exit 1
87
+ load_catalog "$TIERS" || exit 1
88
+
89
+ # ─── Refuse to run on a dirty index ─────────────────────────────────────────
90
+ # .gitignore is the one staged path that is never "unrelated": it is the
91
+ # companion change repair-gitignore.sh just made, and untracking without it is
92
+ # pointless. Staged alone, it is allowed through and folded into this commit.
93
+ # Anything else staged is refused.
94
+ if [ "$ALLOW_DIRTY" -eq 0 ] && [ "$DRY_RUN" -eq 0 ]; then
95
+ if ! git diff --cached --quiet 2>/dev/null; then
96
+ staged="$(git diff --cached --name-only)"
97
+ if [ "$staged" = ".gitignore" ]; then
98
+ info "Staged .gitignore detected — folding the ignore-rule change into this commit."
99
+ else
100
+ err "The index already has staged changes:"
101
+ printf '%s\n' "$staged" | sed 's/^/ /' >&2
102
+ err "Refusing to run — a --commit here would sweep them into this commit."
103
+ err "Commit or unstage them first, or pass --allow-dirty-index if you"
104
+ err "genuinely want them included."
105
+ exit 4
106
+ fi
107
+ fi
108
+ fi
109
+
110
+ # ─── Find what is actually tracked ──────────────────────────────────────────
111
+ TRACKED_PATHS=()
112
+ TOTAL_FILES=0
113
+ for i in "${!CATALOG_PATHS[@]}"; do
114
+ path="${CATALOG_PATHS[$i]}"
115
+ count="$(git ls-files -- "$path" 2>/dev/null | wc -l | tr -d ' ')"
116
+ if [ "${count:-0}" -gt 0 ]; then
117
+ TRACKED_PATHS+=("$path")
118
+ TOTAL_FILES=$((TOTAL_FILES + count))
119
+ printf ' %-36s %s tracked file(s)\n' "$path" "$count"
120
+ fi
121
+ done
122
+
123
+ if [ "${#TRACKED_PATHS[@]}" -eq 0 ]; then
124
+ info "No catalog path (tiers: ${TIERS}) is tracked — nothing to untrack."
125
+ exit 0
126
+ fi
127
+
128
+ echo
129
+ info "${#TRACKED_PATHS[@]} path(s), ${TOTAL_FILES} file(s) would stop being tracked."
130
+ info "All of them remain on disk — this uses 'git rm -r --cached'."
131
+
132
+ if [ "$DRY_RUN" -eq 1 ]; then
133
+ info "--dry-run — nothing changed."
134
+ exit 0
135
+ fi
136
+
137
+ # ─── Untrack ────────────────────────────────────────────────────────────────
138
+ for path in "${TRACKED_PATHS[@]}"; do
139
+ if ! git rm -r --cached --quiet -- "$path"; then
140
+ err "git rm --cached failed for '${path}'. The index may be partially staged;"
141
+ err "inspect with 'git status' and 'git reset' to back out."
142
+ exit 3
143
+ fi
144
+ info "Untracked: ${path}"
145
+ done
146
+
147
+ # Verify nothing was deleted from disk before reporting success.
148
+ MISSING=0
149
+ for path in "${TRACKED_PATHS[@]}"; do
150
+ probe="${path%/}"
151
+ if [ ! -e "$probe" ]; then
152
+ warn "'${probe}' is not on disk. It was already absent before this ran"
153
+ warn "(git tracked it, the working tree did not), so nothing was lost here."
154
+ MISSING=$((MISSING + 1))
155
+ fi
156
+ done
157
+ [ "$MISSING" -eq 0 ] && info "Verified: every untracked path is still present on disk."
158
+
159
+ if [ "$DO_COMMIT" -eq 0 ]; then
160
+ echo
161
+ info "Changes are STAGED but not committed. Review with:"
162
+ info " git status"
163
+ info " git diff --cached --stat"
164
+ info "Then commit yourself, or re-run with --commit."
165
+ exit 0
166
+ fi
167
+
168
+ # ─── Commit ─────────────────────────────────────────────────────────────────
169
+ COMMIT_MSG="chore(jenga): untrack Jenga-owned files, keep them on disk
170
+
171
+ Removes the Jenga scaffold from version control via 'git rm --cached' and
172
+ relies on the j.gitignore managed block to keep it out. Files are untouched
173
+ on disk. Tiers: ${TIERS}.
174
+
175
+ Note: this changes the branch tip only — earlier commits still contain these
176
+ paths."
177
+
178
+ if ! git commit -q -m "$COMMIT_MSG"; then
179
+ err "git commit failed. Your removals are still staged."
180
+ exit 3
181
+ fi
182
+ info "Committed: $(git rev-parse --short HEAD)"
183
+
184
+ if [ "$DO_PUSH" -eq 0 ]; then
185
+ echo
186
+ info "Not pushed. The remote still has these files until you push."
187
+ exit 0
188
+ fi
189
+
190
+ # ─── Push ───────────────────────────────────────────────────────────────────
191
+ BRANCH="$(git rev-parse --abbrev-ref HEAD)"
192
+ UPSTREAM="$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null)"
193
+
194
+ if [ -z "$UPSTREAM" ]; then
195
+ err "Branch '${BRANCH}' has no upstream — refusing to guess a remote."
196
+ err "Set one, then push yourself: git push -u origin ${BRANCH}"
197
+ err "The commit itself succeeded and is safe on your local branch."
198
+ exit 3
199
+ fi
200
+
201
+ info "Pushing ${BRANCH} → ${UPSTREAM} ..."
202
+ if ! git push; then
203
+ err "Push failed — most likely the branch is behind its upstream."
204
+ err "Reconcile (git pull --rebase) and push yourself. The commit is safe locally."
205
+ exit 3
206
+ fi
207
+
208
+ info "Pushed. These paths are gone from ${UPSTREAM}'s tip."
209
+ info "They REMAIN in earlier commits — this did not rewrite history."
210
+ exit 0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: j.idea
3
- description: Polyfill alias of the idea skill under a collision-safe directory name. Identical behavior to /idea — Capture a loosely-defined idea to project/ideas.md — a lightweight, "maybe someday" log with no board or promotion overhead. Use when the bare /idea form is shadowed by another tool's own built-in command of the same name.
3
+ description: Polyfill alias of the idea skill under a collision-safe directory name. Identical behavior to /idea — DEPRECATED - use the prefixed 'j-idea' skill instead. Capture a loosely-defined idea to project/ideas.md — a lightweight, "maybe someday" log with no board or promotion overhead. Use when the bare /idea form is shadowed by another tool's own built-in command of the same name.
4
4
  keywords:
5
5
  - idea
6
6
  - capture idea
@@ -52,12 +52,84 @@ This file is generated/synced by `scripts/generate-j-alias.sh idea` from `skills
52
52
  - **Promoting an idea** means re-running `/brainstorm` on it, which routes onward to `/btw` or `/todo` as normal. That routing behavior already exists and is out of scope for `/idea`.
53
53
  - **Terminal idea states** are marked directly in `project/ideas.md` using the same HTML-comment tag convention `project/todo.md` uses for `RECONCILED`:
54
54
  - `PROMOTED` — the idea was picked up via `/brainstorm` and turned into board work.
55
- - `DISCARDED` — the idea was reviewed and dropped.
55
+ - `REJECTED` — the idea was reviewed and dropped.
56
56
 
57
- Example (mirroring `project/todo.md`'s tagging style):
57
+ ### Embedded Board ID
58
+
59
+ `PROMOTED`/`REJECTED` tags support an embedded board ID, so the idea→board-item link is
60
+ machine-readable directly from the tag itself, without needing a separate lookup:
61
+
62
+ - `<!-- PROMOTED: E##_S##[_T##] -->` — the `_T##` task suffix is optional; use whichever level
63
+ the idea was actually promoted into (an epic, a story, or a specific task).
64
+ - `<!-- REJECTED -->` — stays bare, with no embedded ID. A rejection has no resulting board
65
+ item to point at.
66
+
67
+ Example (mirroring `project/todo.md`'s tagging style):
68
+ ```
69
+ Add dark mode toggle to settings page <!-- PROMOTED: E12_S03 -->
70
+ Add a settings-page keyboard shortcut cheat sheet <!-- PROMOTED: E12_S03_T02 -->
71
+ Rewrite onboarding copy in a more casual tone <!-- REJECTED -->
72
+ ```
73
+
74
+ ### Source-Rapport Link Convention
75
+
76
+ Some ideas originate from a rapport rather than being manually captured via `/idea` — e.g. when
77
+ scrum-master's `rapport_review` step (E35_S03) detects a distinct idea surfaced inside a
78
+ problem rapport and, on user confirmation, appends it to `project/ideas.md` the same way
79
+ `/idea` does. When that happens, the entry's source rapport is recorded with a second inline
80
+ HTML comment on the same line:
81
+
82
+ ```
83
+ <!-- rapport: <path/to/rapport.md> -->
84
+ ```
85
+
86
+ Rules:
87
+ - Written at capture time, alongside the idea text, by whichever flow is adding the entry
88
+ (scrum-master's rapport-derived capture path; `/idea`'s own manual capture never has a source
89
+ rapport and omits this comment entirely).
90
+ - Left in place through promotion/rejection — the `rapport:` comment and the terminal
91
+ `PROMOTED`/`REJECTED` comment coexist on the same line, e.g.:
58
92
  ```
59
- Add dark mode toggle to settings page <!-- PROMOTED -->
60
- Rewrite onboarding copy in a more casual tone <!-- DISCARDED -->
93
+ Extract shared retry logic into a utility <!-- rapport: project/rapports/problems/E40_S02_T01-segment-remarks.md --> <!-- PROMOTED: E61_S01 -->
61
94
  ```
95
+ - Consumed by the outcome-record mechanism at promotion/rejection time: if a `rapport:` comment
96
+ is present on the line, that rapport is updated with the decision (and `[SPIKE]`-prefixed in
97
+ its title/header if not already, per `templates/SCRUM_BOARD_SCHEMA.md`'s `[SPIKE]` section);
98
+ if absent (a manually-captured `/idea` entry with no source rapport), a
99
+ `project/documentation/plans/<slug>_PROMOTED.md` or `_REJECTED.md` file is created instead.
100
+ - Verified compatible with `scripts/idea_manager.sh`'s `real_entries()` filtering: that function
101
+ only strips lines whose first non-whitespace characters are `<!--`; a trailing inline comment
102
+ on an otherwise real entry line is untouched and the line still counts as a real entry.
103
+
104
+ No agent invoked by `/idea` applies terminal (`PROMOTED`/`REJECTED`) tags or acts on them — that
105
+ is reserved for the promotion step (e.g. within `/brainstorm`) and, for rapport-sourced ideas,
106
+ scrum-master's rapport-review flow.
107
+
108
+ ### Outcome-Record Mechanism (`idea_manager.sh`) — E35_S03_T03
109
+
110
+ `scripts/idea_manager.sh` implements the tagging and outcome-record behavior described above, via
111
+ four additional subcommands beyond `add`/`list`:
112
+
113
+ - `tag "<idea-text>" <PROMOTED:E##_S##[_T##]|REJECTED>` — writes the terminal tag onto the
114
+ `project/ideas.md` line matching `<idea-text>` (an exact/literal match; errors if zero or more
115
+ than one line matches, and errors if the line is already tagged).
116
+ - `get-id "<idea-text>"` — prints the embedded board ID from that line's `PROMOTED` tag (errors
117
+ if the line has no `PROMOTED` tag).
118
+ - `get-rapport "<idea-text>"` — prints the `rapport:` path recorded on that line, or nothing
119
+ (exit 0) if the line has no source-rapport comment.
120
+ - `resolve "<idea-text>" <promoted|rejected> [board_id]` — the full outcome-record mechanism in
121
+ one call: tags the line (as `tag` does), then either updates the idea's source rapport or, if
122
+ none is recorded, creates the `project/documentation/plans/` fallback file:
123
+ - **Source rapport present:** the rapport's `# Rapport: ...` title line is prefixed
124
+ `[SPIKE] ` if not already, and an `## Idea Outcome Record` section (decision, date, resulting
125
+ board ID if promoted, and the idea text) is appended to the rapport file.
126
+ - **No source rapport:** `project/documentation/plans/<slug>_PROMOTED.md` or `_REJECTED.md` is
127
+ created (idea text slugified, lowercased, capped at 60 characters), containing the same
128
+ decision/date/board-ID/idea-text content.
129
+
130
+ `board_id` is required for `promoted` (validated to start with `E<digits>`) and must be omitted
131
+ for `rejected` (a rejection has no resulting board item).
62
132
 
63
- No agent invoked by `/idea` applies these tags or acts on them — that is reserved for a future promotion step (e.g. within `/brainstorm`).
133
+ This is the mechanism itself, not the trigger — nothing currently calls `resolve` automatically.
134
+ A future promotion step (e.g. within `/brainstorm`'s routing, or scrum-master acting on a
135
+ rapport-sourced idea) is expected to invoke it once that trigger exists.
@@ -1,3 +1,3 @@
1
1
  # Ideas
2
2
 
3
- <!-- Format: <idea text> (one line per idea; terminal tags PROMOTED / DISCARDED appended by /brainstorm, not by /idea) -->
3
+ <!-- Format: <idea text> (one line per idea; terminal tags PROMOTED / REJECTED appended by /brainstorm, not by /idea) -->
@@ -50,7 +50,7 @@ Assess whether you have enough context to evaluate current behavior against the
50
50
  Invoke the `/examplify` skill. Specify clearly what you want it to find out — e.g. which code paths are exercised, what inputs/outputs look like, or what edge cases exist.
51
51
 
52
52
  ### 5. Run `/evaluate`
53
- Copy `.agents/skills/evaluate/assets/evaluation_invokation_template.yml` and fill in:
53
+ Copy `.agents/skills/j-evaluate/assets/evaluation_invokation_template.yml` and fill in:
54
54
  - `paths`: path(s) to the relevant example files
55
55
  - `goal`: the confirmed target goal
56
56
 
@@ -101,13 +101,44 @@ run can never silently relocate directories or edit `.gitignore`.
101
101
  > `.agents/`/`.claude/` convention), was built and then withdrawn before release —
102
102
  > testing found it left board resolution and session-end hooks writing to two
103
103
  > different trees. It is not offered here. See
104
- > `skills/distribute/CONFIG_SCHEMA.md` for the root-cause note and the tracked
104
+ > `skills/j-distribute/CONFIG_SCHEMA.md` for the root-cause note and the tracked
105
105
  > follow-up to reintroduce it once fixed.
106
106
 
107
- Carry the chosen value into step 3. Do not apply it yourself — the script owns all
107
+ Carry the chosen value into step 4. Do not apply it yourself — the script owns all
108
108
  of the mechanical work.
109
109
 
110
- ### 3. Run the scaffold script
110
+ ### 3. Ask whether the distributed scaffold should be committed
111
+
112
+ This is a **distinct** question from step 2 — `project_files_visibility` (step 2)
113
+ covers only the `project/` working tree (the scrum board, `todo.md`, `queue/`,
114
+ `rapports/`, `logs/`). It has no effect on `.claude/`/`.agents/`, the distributed
115
+ framework scaffold (skill and agent definitions), which is a separate tree with a
116
+ separate lifecycle — it gets overwritten wholesale by every `/distribute` run or npm
117
+ upgrade, unlike `project/`. Kept as its own flag (`scaffold_visibility`) rather than
118
+ folded into `project_files_visibility`'s enum; see
119
+ `skills/j-distribute/CONFIG_SCHEMA.md`'s "Scaffold visibility" section for the full
120
+ rationale.
121
+
122
+ Ask the user this question, verbatim, before running any script:
123
+
124
+ Should the distributed .claude/.agents Jenga AI framework scaffold (skills, agent
125
+ definitions — implementation detail, not your own code) be committed into this
126
+ project's git history?
127
+ 1. Yes, commit it — keep it tracked and visible, exactly like today
128
+ 2. No, keep it on disk but add it to .gitignore so it's never committed
129
+ 3. Not sure — explain the trade-offs and ask me again
130
+
131
+ If the user picks option 3, explain the trade-offs and re-ask. Do not proceed until
132
+ the answer maps to one of `visible` (option 1) or `ignored` (option 2).
133
+
134
+ If the run is non-interactive (no user available to answer), use the default:
135
+ **`visible`**. It is the only choice that changes nothing on disk, so an unattended
136
+ run reproduces exactly today's behavior — the scaffold is still committed by default.
137
+
138
+ Carry the chosen value into step 4 as well. Do not apply it yourself — the script
139
+ owns all of the mechanical work.
140
+
141
+ ### 4. Run the scaffold script
111
142
 
112
143
  `init.sh` is not guaranteed to live at a single fixed path: in a project that
113
144
  installed Jenga via npm, it was mirrored to `.claude/skills/j-init/scripts/`
@@ -117,7 +148,7 @@ source checkout, where it lives at the bare `skills/j-init/scripts/` path
117
148
  instead. This step runs before `CLAUDE.md`/`AGENTS.md` exist, so it cannot
118
149
  rely on either file's routing instructions to resolve the path — it must
119
150
  locate its own script directly. Execute the init script from the project
120
- root, passing the choice from step 2:
151
+ root, passing the choices from steps 2 and 3:
121
152
 
122
153
  ```bash
123
154
  INIT_SCRIPT=""
@@ -128,11 +159,14 @@ if [[ -z "$INIT_SCRIPT" ]]; then
128
159
  echo "Error: could not locate init.sh under .claude/skills/, .agents/skills/, or skills/" >&2
129
160
  exit 1
130
161
  fi
131
- chmod +x "$INIT_SCRIPT" && "$INIT_SCRIPT" --visibility <visible|ignored>
162
+ chmod +x "$INIT_SCRIPT" && "$INIT_SCRIPT" --visibility <visible|ignored> --scaffold-visibility <visible|ignored>
132
163
  ```
133
164
 
134
165
  Omitting `--visibility` falls back to the `JENGA_PROJECT_FILES_VISIBILITY`
135
- environment variable, then to `visible`.
166
+ environment variable, then to `visible`. Omitting `--scaffold-visibility` falls
167
+ back to the `JENGA_SCAFFOLD_VISIBILITY` environment variable, then to `visible`
168
+ — the same default-preserving fallback chain, applied independently to the
169
+ distributed-scaffold question from step 3.
136
170
 
137
171
  This script handles all scaffolding in one step:
138
172
  1. Initializes the git repository
@@ -148,22 +182,27 @@ This script handles all scaffolding in one step:
148
182
  11. Creates `docs/STRATEGY.md` — a strategic brief stub intended for investors, partners, and the product team
149
183
  12. Creates `CHANGELOG.md` from the shared template — a running log of notable changes, seeded with an `[Unreleased]` section
150
184
  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`
185
+ 14. Applies the chosen scaffold visibility mode via `scripts/apply-scaffold-visibility.sh`, which records it as `scaffold_visibility` in `jenga.config.json` and, when `ignored`, adds `.claude/` and `.agents/` to `.gitignore`
186
+ 15. Stages and commits all files with the message `init: scaffold project structure and workflow config`
152
187
 
153
- The visibility mode is validated before any scaffolding happens, so an invalid
154
- value fails fast and leaves nothing behind. It is applied before the commit, so
155
- the `.gitignore` entry is captured in the initial commit.
188
+ Both visibility modes are validated before any scaffolding happens, so an invalid
189
+ value fails fast and leaves nothing behind. Both are applied before the commit, so
190
+ any resulting `.gitignore` entries are captured in the initial commit — this is what
191
+ lets `scaffold_visibility: ignored` keep `.claude/`/`.agents/` out of the commit even
192
+ though they may already exist on disk (from npm postinstall or a prior `/distribute`
193
+ run) by the time `/init` runs `git add -A`.
156
194
 
157
195
  If the script fails, check that you are in the project root and that git and `jq`
158
196
  are available.
159
197
 
160
- See `skills/distribute/CONFIG_SCHEMA.md` for the full `project_files_visibility`
161
- field reference.
198
+ See `skills/j-distribute/CONFIG_SCHEMA.md` for the full `project_files_visibility` and
199
+ `scaffold_visibility` field reference.
162
200
 
163
- ### 4. Prompt next step
201
+ ### 5. Prompt next step
164
202
 
165
203
  Inform the user that setup is complete, and state which visibility mode was applied
166
- and where the working files now live. Mention that `docs/STRATEGY.md` was created as
204
+ for both `project_files_visibility` and `scaffold_visibility`, and where the working
205
+ files and scaffold now live. Mention that `docs/STRATEGY.md` was created as
167
206
  a strategic brief stub for investors, partners, and the product team — they can fill
168
207
  it in now or return to it later. Suggest running `/pi-plan` to define project goals
169
208
  and epics.
@@ -11,5 +11,4 @@ Desktop.ini
11
11
  # Dependency directories
12
12
  node_modules/
13
13
  vendor/
14
- .venv/
15
- EOF
14
+ .venv/
@@ -1,7 +1,10 @@
1
1
  {
2
- "threshold_version": 2,
2
+ "threshold_version": 3,
3
3
  "inline_max_files": 3,
4
4
  "inline_max_lines": 75,
5
5
  "story_max_files": 5,
6
- "bundle_lock_ttl_minutes": 30
6
+ "bundle_lock_ttl_minutes": 30,
7
+ "max_concurrent_developers": 3,
8
+ "max_concurrent_testers": 3,
9
+ "slot_ttl_minutes": 45
7
10
  }
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # apply-scaffold-visibility.sh — apply the `scaffold_visibility` mode to a project.
4
+ #
5
+ # This is a DISTINCT flag from `project_files_visibility` (see
6
+ # apply-project-visibility.sh). `project_files_visibility` covers the
7
+ # project/ working tree (scrum board, todo.md, queue/, rapports/, logs/) —
8
+ # E31_S05's own Background section scopes it to that tree only. This script
9
+ # instead covers the *distributed framework scaffold* — `.claude/` and
10
+ # `.agents/` — which is a different tree with a different lifecycle (it is
11
+ # overwritten wholesale by every `/distribute` run or npm upgrade; project/
12
+ # is not). Filed as new, adjacent scope under E31_S07 rather than folded
13
+ # silently into project_files_visibility's existing enum. See
14
+ # skills/j-distribute/CONFIG_SCHEMA.md's "Scaffold visibility" section for the
15
+ # full rationale.
16
+ #
17
+ # This is skills/j-init/'s own copy of skills/init/scripts/apply-scaffold-visibility.sh
18
+ # (E31_S07_T03) — skills/j-init/ is a separate, hand-maintained directory, not
19
+ # auto-synced with skills/init/ (scripts/generate-j-alias.sh explicitly excludes
20
+ # this pair), and is the copy that actually ships to public-GitHub-mirror and
21
+ # npm-package consumers. Keep this file's logic in lockstep with skills/init/'s
22
+ # copy — any behavioral change made there should be mirrored here.
23
+ #
24
+ # Usage:
25
+ # apply-scaffold-visibility.sh <mode> [project_root]
26
+ # apply-scaffold-visibility.sh --check-only <mode>
27
+ #
28
+ # Modes:
29
+ # visible Scaffold directories stay where they are. No-op on disk beyond
30
+ # the config write. This is the default — matches the behavior
31
+ # of every /init run before this flag existed.
32
+ # ignored .claude/ and .agents/ are added to the project's .gitignore —
33
+ # present on disk (whenever npm postinstall or /distribute
34
+ # places them, whether that happens before or after this runs),
35
+ # never committed. Entries are written unconditionally, the same
36
+ # way apply-project-visibility.sh gitignores project/ whether or
37
+ # not it exists yet, so a scaffold created by a later
38
+ # /distribute run is covered too, not just one already on disk.
39
+ #
40
+ # Exit codes:
41
+ # 0 Success
42
+ # 1 Bad usage or missing prerequisite
43
+ # 2 Invalid mode (outside the two-value enum)
44
+ # 3 Filesystem apply failure
45
+ # 4 jenga.config.json write failure
46
+
47
+ # Do NOT use set -e globally — each step handles its own errors.
48
+ set -uo pipefail
49
+
50
+ info() { echo "[scaffold-visibility] $*"; }
51
+ warn() { echo "[scaffold-visibility] WARNING: $*"; }
52
+ err() { echo "[scaffold-visibility] ERROR: $*" >&2; }
53
+
54
+ usage() {
55
+ echo "Usage: $(basename "$0") <visible|ignored> [project_root]" >&2
56
+ echo " $(basename "$0") --check-only <visible|ignored>" >&2
57
+ exit 1
58
+ }
59
+
60
+ # The distributed framework scaffold — mirrored copies of skills/agents for
61
+ # Claude Code and Copilot/other agents. Distinct from JENGA_WORKING_PATHS in
62
+ # apply-project-visibility.sh, which covers project/ only.
63
+ JENGA_SCAFFOLD_PATHS=(".claude" ".agents")
64
+
65
+ VALID_MODES="visible ignored"
66
+
67
+ validate_mode() {
68
+ local mode="$1"
69
+ for valid in $VALID_MODES; do
70
+ [ "$mode" = "$valid" ] && return 0
71
+ done
72
+ err "Invalid scaffold_visibility value: '${mode}'"
73
+ err "Allowed values are: ${VALID_MODES// /, }"
74
+ exit 2
75
+ }
76
+
77
+ # ---------------------------------------------------------------------------
78
+ # Argument parsing
79
+ # ---------------------------------------------------------------------------
80
+
81
+ CHECK_ONLY=0
82
+ if [ "${1:-}" = "--check-only" ]; then
83
+ CHECK_ONLY=1
84
+ shift
85
+ fi
86
+
87
+ MODE="${1:-}"
88
+ [ -n "$MODE" ] || usage
89
+
90
+ validate_mode "$MODE"
91
+
92
+ if [ "$CHECK_ONLY" -eq 1 ]; then
93
+ info "Mode '$MODE' is valid."
94
+ exit 0
95
+ fi
96
+
97
+ PROJECT_ROOT="${2:-$PWD}"
98
+ if [ ! -d "$PROJECT_ROOT" ]; then
99
+ err "Project root does not exist: $PROJECT_ROOT"
100
+ exit 1
101
+ fi
102
+ cd "$PROJECT_ROOT" || { err "Cannot enter project root: $PROJECT_ROOT"; exit 1; }
103
+
104
+ if ! command -v jq >/dev/null 2>&1; then
105
+ err "jq is required to write jenga.config.json but was not found on PATH."
106
+ exit 1
107
+ fi
108
+
109
+ # ---------------------------------------------------------------------------
110
+ # ignored — append to .gitignore, without ever duplicating an entry
111
+ # ---------------------------------------------------------------------------
112
+
113
+ gitignore_append() {
114
+ local entry="$1"
115
+ local gitignore=".gitignore"
116
+
117
+ if [ -f "$gitignore" ] && grep -qxF -- "$entry" "$gitignore"; then
118
+ info "'$entry' already present in .gitignore — skipping."
119
+ return 0
120
+ fi
121
+
122
+ # Don't glue our entry onto a final line that lacks a newline.
123
+ if [ -s "$gitignore" ] && [ -n "$(tail -c 1 "$gitignore")" ]; then
124
+ printf '\n' >> "$gitignore"
125
+ fi
126
+
127
+ if ! printf '%s\n' "$entry" >> "$gitignore"; then
128
+ err "Failed to append '$entry' to .gitignore"
129
+ exit 3
130
+ fi
131
+ info "Added '$entry' to .gitignore"
132
+ }
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # jenga.config.json — merge the field in, written atomically (temp + mv)
136
+ # ---------------------------------------------------------------------------
137
+
138
+ write_scaffold_config() {
139
+ local mode="$1"
140
+ local config="jenga.config.json"
141
+ local tmp="${config}.tmp"
142
+ local existing='{}'
143
+
144
+ # /init normally runs before any /distribute, so the file usually does not
145
+ # exist yet. Merge rather than overwrite so other fields survive.
146
+ if [ -f "$config" ]; then
147
+ if ! jq empty "$config" 2>/dev/null; then
148
+ err "$config exists but contains malformed JSON — refusing to overwrite it."
149
+ exit 4
150
+ fi
151
+ existing="$(cat "$config")"
152
+ fi
153
+
154
+ local content
155
+ content="$(jq --arg v "$mode" '.scaffold_visibility = $v' <<< "$existing")"
156
+ if [ -z "$content" ]; then
157
+ err "Failed to construct $config content."
158
+ exit 4
159
+ fi
160
+
161
+ if ! printf '%s\n' "$content" > "$tmp"; then
162
+ err "Failed to write temporary config file: $tmp"
163
+ exit 4
164
+ fi
165
+
166
+ if ! mv "$tmp" "$config"; then
167
+ err "Failed to atomically move $tmp to $config"
168
+ rm -f "$tmp"
169
+ exit 4
170
+ fi
171
+
172
+ info "Wrote scaffold_visibility = \"$mode\" to $config"
173
+ }
174
+
175
+ # ---------------------------------------------------------------------------
176
+ # Apply
177
+ # ---------------------------------------------------------------------------
178
+
179
+ case "$MODE" in
180
+ visible)
181
+ info "Mode 'visible' — scaffold directories stay in place; nothing to change on disk."
182
+ ;;
183
+ ignored)
184
+ for path in "${JENGA_SCAFFOLD_PATHS[@]}"; do
185
+ gitignore_append "${path}/"
186
+ done
187
+ ;;
188
+ esac
189
+
190
+ write_scaffold_config "$MODE"
191
+
192
+ info "Applied scaffold_visibility '$MODE' to $PROJECT_ROOT"