@jenga-ai/agent 3.5.0 → 4.0.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 (136) 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 +79 -8
  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 +5 -1
  15. package/project/app/api/lib/resolve-project-root.js +1 -1
  16. package/project/app/api/parsers/knowledge-graph.js +100 -9
  17. package/project/app/api/routes/health.js +36 -0
  18. package/project/app/api/scripts/capture-snapshot.js +9 -6
  19. package/project/app/package.json +4 -0
  20. package/project/app/ui/dist/assets/index-BADc5mmH.css +1 -0
  21. package/project/app/ui/dist/assets/index-C3oiuli_.js +104 -0
  22. package/project/app/ui/dist/index.html +2 -2
  23. package/project/app/ui/package.json +4 -0
  24. package/project/app/ui/scripts/build-snapshot-html.cjs +63 -2
  25. package/scripts/acquire-concurrency-slot.sh +35 -5
  26. package/scripts/apply-j-prefix.sh +46 -5
  27. package/scripts/build-pages-site.sh +1 -1
  28. package/scripts/check-public-playbook-steps.sh +158 -52
  29. package/scripts/check-publicignore-match.sh +2 -2
  30. package/scripts/compute-deploy-reconcile.sh +5 -5
  31. package/scripts/delete-bare-skill-dirs.sh +329 -0
  32. package/scripts/generate-legacy-shipped-paths.js +2 -2
  33. package/scripts/idea_manager.sh +273 -3
  34. package/scripts/mark-deployed.sh +2 -2
  35. package/scripts/populate-knowledge-graph.entity-resolution.test.js +254 -0
  36. package/scripts/populate-knowledge-graph.js +213 -5
  37. package/scripts/populate-knowledge-graph.staleness.test.js +130 -0
  38. package/scripts/postinstall.js +1 -1
  39. package/scripts/release-concurrency-slot.sh +34 -4
  40. package/scripts/render-ranked-list.sh +270 -0
  41. package/scripts/repoint-dead-bare-path-prose.py +81 -0
  42. package/scripts/repoint-skill-refs.sh +539 -0
  43. package/scripts/rewrite-stale-skill-preambles.py +188 -0
  44. package/scripts/strip-polyfill-frontmatter.py +166 -0
  45. package/scripts/todo_manager.sh +16 -1
  46. package/scripts/validate-typed-object.sh +750 -0
  47. package/scripts/verify-legacy-seed-reconcile.sh +10 -10
  48. package/scripts/verify-postinstall-reconcile.sh +7 -7
  49. package/scripts/write-context-digest.sh +1 -1
  50. package/skills/j-brainstorm/SKILL.md +3 -4
  51. package/skills/j-btw/SKILL.md +3 -4
  52. package/skills/j-clearify/SKILL.md +5 -6
  53. package/skills/j-close-story/SKILL.md +11 -12
  54. package/skills/j-close-story/scripts/check-privatized.sh +4 -4
  55. package/skills/j-close-story/scripts/check-story-closeable.sh +11 -4
  56. package/skills/j-commit/SKILL.md +3 -4
  57. package/skills/j-continue/SKILL.md +5 -6
  58. package/skills/j-deep-dive/SKILL.md +3 -4
  59. package/skills/j-distribute/CONFIG_SCHEMA.md +82 -5
  60. package/skills/j-distribute/SKILL.md +3 -4
  61. package/skills/j-do/SKILL.md +100 -18
  62. package/skills/j-doc/SKILL.md +3 -4
  63. package/skills/j-doc-sync/SKILL.md +4 -4
  64. package/skills/j-dooo/SKILL.md +6 -15
  65. package/skills/j-error/SKILL.md +3 -4
  66. package/skills/j-evaluate/SKILL.md +3 -4
  67. package/skills/j-examplify/SKILL.md +3 -4
  68. package/skills/j-gitignore/SKILL.md +157 -0
  69. package/skills/j-gitignore/assets/jenga-paths.txt +50 -0
  70. package/skills/j-gitignore/scripts/_catalog.sh +105 -0
  71. package/skills/j-gitignore/scripts/audit-gitignore.sh +194 -0
  72. package/skills/j-gitignore/scripts/repair-gitignore.sh +226 -0
  73. package/skills/j-gitignore/scripts/untrack-jenga-files.sh +210 -0
  74. package/skills/j-help/SKILL.md +3 -4
  75. package/skills/j-idea/SKILL.md +80 -9
  76. package/skills/j-idea/assets/idea_template.md +1 -1
  77. package/skills/j-improve/SKILL.md +4 -5
  78. package/skills/j-init/SKILL.md +23 -14
  79. package/skills/j-init/assets/scope-thresholds_template.json +5 -2
  80. package/skills/j-init/scripts/apply-scaffold-visibility.sh +9 -7
  81. package/skills/j-init/scripts/init.sh +4 -4
  82. package/skills/j-jbp/SKILL.md +3 -4
  83. package/skills/j-lgtm/SKILL.md +3 -4
  84. package/skills/j-pi-plan/SKILL.md +3 -4
  85. package/skills/j-playbook/SKILL.md +1 -1
  86. package/skills/j-proceed/SKILL.md +3 -4
  87. package/skills/j-publish/SKILL.md +4 -5
  88. package/skills/j-publish/adapters/npm-ci.md +6 -1
  89. package/skills/j-publish/adapters/npm.md +1 -1
  90. package/skills/j-publish/scripts/generate_release_notes.sh +1 -1
  91. package/skills/j-publish/scripts/npm_stage_inspect.sh +61 -0
  92. package/skills/j-publish/scripts/run_gates.sh +1 -1
  93. package/skills/j-reconcile/SKILL.md +40 -7
  94. package/skills/j-reconcile/assets/report_format.md +11 -0
  95. package/skills/j-reconcile/scripts/detect-unlinked-code.sh +13 -13
  96. package/skills/j-reconcile-origin/SKILL.md +3 -4
  97. package/skills/j-redo/SKILL.md +4 -5
  98. package/skills/j-skillify/SKILL.md +3 -4
  99. package/skills/j-spinoff/SKILL.md +4 -5
  100. package/skills/j-status/SKILL.md +18 -4
  101. package/skills/j-todo/SKILL.md +44 -5
  102. package/skills/j-todo/scripts/argument-is-not-ranked-list.sh +92 -0
  103. package/skills/j-todo/scripts/argument-is-ranked-list.sh +78 -0
  104. package/skills/j-uncharted/SKILL.md +252 -13
  105. package/skills/j-uncharted/scripts/detect-dependencies.sh +80 -22
  106. package/skills/j-uncharted/scripts/detect-tests.sh +1 -1
  107. package/skills/j-uncharted/scripts/diff-since-baseline.sh +600 -0
  108. package/skills/j-uncharted/scripts/elicitation-state.sh +1 -1
  109. package/skills/j-uncharted/scripts/find-scan-baseline.sh +545 -0
  110. package/skills/j-uncharted/scripts/run-engine.sh +36 -2
  111. package/skills/j-uncharted/scripts/validate-proposed-items.sh +1 -1
  112. package/skills/j-uncharted/scripts/write-scan-record.sh +361 -0
  113. package/skills/j-wtf/SKILL.md +4 -5
  114. package/skills/jenga/SKILL.md +106 -11
  115. package/skills/jenga/playbooks/board-hygiene.json +32 -0
  116. package/skills/jenga/playbooks/schema.json +73 -6
  117. package/skills/jenga/playbooks/understand-then-commit.json +19 -0
  118. package/skills/jenga/scripts/load-nl-catalog.js +5 -2
  119. package/skills/jenga/scripts/load-nl-catalog.sh +1 -1
  120. package/skills/jenga/scripts/load-playbooks.sh +290 -5
  121. package/skills/jenga/scripts/match-playbook.sh +4 -4
  122. package/skills/jenga/scripts/run-playbook-step.sh +267 -1
  123. package/templates/SCRUM_BOARD_SCHEMA.md +14 -1
  124. package/templates/SKILL_TEMPLATE.md +12 -0
  125. package/templates/permission-levels/level-1-locked.json +1 -1
  126. package/templates/permission-levels/level-2-guarded.json +1 -1
  127. package/templates/permission-levels/level-3-standard.json +1 -1
  128. package/templates/permission-levels/level-4-elevated.json +2 -2
  129. package/templates/permission-levels/level-5-unrestricted.json +2 -2
  130. package/templates/playbook-types.json +40 -6
  131. package/project/app/ui/dist/assets/index-7fj-vllY.js +0 -104
  132. package/project/app/ui/dist/assets/index-CdK3Qrep.css +0 -1
  133. package/scripts/audit-twin-divergence.sh +0 -625
  134. package/scripts/generate-j-alias.sh +0 -333
  135. package/skills/j-dev-done/SKILL.md +0 -53
  136. package/skills/j-dev-done/scripts/classify-commit-outcome.sh +0 -114
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env bash
2
+ # -----------------------------------------------------------------------------
3
+ # skills/j-gitignore/scripts/repair-gitignore.sh
4
+ #
5
+ # Repairs an already-scaffolded project's .gitignore. Two jobs, in order:
6
+ #
7
+ # 1. Strips stray heredoc terminator lines (a bare `EOF`) left behind by the
8
+ # botched `cat > .gitignore <<EOF` in pre-fix /init scaffolds. The upstream
9
+ # fix (E31_S07_T02/T03, E42_S06_T01) repaired the TEMPLATE, which only helps
10
+ # projects scaffolded afterwards; this repairs one already on disk.
11
+ #
12
+ # 2. Adds (mode `ignored`) or removes (mode `visible`) the Jenga path entries,
13
+ # inside a managed block so the operation is exactly reversible.
14
+ #
15
+ # The managed block also ABSORBS loose duplicates: /init's
16
+ # apply-project-visibility.sh and apply-scaffold-visibility.sh append bare lines
17
+ # with no markers, so a previously-initialised project has loose `project/`,
18
+ # `.claude/` and `.agents/` entries. Any loose line exactly matching a selected
19
+ # catalog path is pulled out of the file and re-emitted inside the block, rather
20
+ # than left to sit alongside it as a duplicate.
21
+ #
22
+ # Everything outside the managed block is preserved byte for byte. The file is
23
+ # written atomically (temp + mv), so an interrupted run cannot truncate it.
24
+ #
25
+ # This does NOT untrack anything. .gitignore has no effect on a file git already
26
+ # tracks — that is untrack-jenga-files.sh's job, and it must run after this.
27
+ #
28
+ # Invoked via `bash`, not executed directly.
29
+ #
30
+ # Usage:
31
+ # repair-gitignore.sh <ignored|visible> [project_root]
32
+ # [--tiers scaffold,hybrid,board,optional]
33
+ # [--dry-run] [--keep-eof]
34
+ #
35
+ # Options:
36
+ # --tiers <csv> Which catalog tiers to act on. Default: scaffold
37
+ # --dry-run Print a unified diff of the intended change; write nothing.
38
+ # --keep-eof Leave stray `EOF` lines alone. Use only if this project
39
+ # genuinely means to ignore a file literally named EOF.
40
+ #
41
+ # Exit codes:
42
+ # 0 Success (including "already correct — nothing to do")
43
+ # 1 Bad usage or missing prerequisite
44
+ # 2 Invalid mode or tier
45
+ # 3 Filesystem write failure
46
+ # -----------------------------------------------------------------------------
47
+
48
+ set -uo pipefail
49
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_catalog.sh"
50
+
51
+ info() { echo "[gitignore-repair] $*"; }
52
+ err() { echo "[gitignore-repair] ERROR: $*" >&2; }
53
+
54
+ MODE=""
55
+ PROJECT_ROOT=""
56
+ TIERS="scaffold"
57
+ DRY_RUN=0
58
+ KEEP_EOF=0
59
+
60
+ while [ $# -gt 0 ]; do
61
+ case "$1" in
62
+ --tiers) TIERS="${2:-}"; shift 2 ;;
63
+ --tiers=*) TIERS="${1#*=}"; shift ;;
64
+ --dry-run) DRY_RUN=1; shift ;;
65
+ --keep-eof) KEEP_EOF=1; shift ;;
66
+ -h|--help)
67
+ echo "Usage: $(basename "$0") <ignored|visible> [project_root] [--tiers <csv>] [--dry-run] [--keep-eof]"
68
+ exit 0 ;;
69
+ -*) err "Unknown option: $1"; exit 1 ;;
70
+ *)
71
+ if [ -z "$MODE" ]; then MODE="$1"
72
+ elif [ -z "$PROJECT_ROOT" ]; then PROJECT_ROOT="$1"
73
+ else err "Unexpected argument: $1"; exit 1
74
+ fi
75
+ shift ;;
76
+ esac
77
+ done
78
+
79
+ case "$MODE" in
80
+ ignored|visible) ;;
81
+ "") err "Missing mode. Usage: $(basename "$0") <ignored|visible> [project_root]"; exit 1 ;;
82
+ *) err "Invalid mode '${MODE}'. Allowed: ignored, visible"; exit 2 ;;
83
+ esac
84
+
85
+ PROJECT_ROOT="${PROJECT_ROOT:-$PWD}"
86
+ validate_tiers "$TIERS" || exit 2
87
+ resolve_root "$PROJECT_ROOT" || exit 1
88
+ load_catalog "$TIERS" || exit 1
89
+
90
+ GITIGNORE=".gitignore"
91
+ TMP="${GITIGNORE}.jenga-repair.$$"
92
+ cleanup() { rm -f "$TMP"; }
93
+ trap cleanup EXIT
94
+
95
+ # Fast membership test for "is this line one of our selected catalog paths".
96
+ is_catalog_path() {
97
+ local line="$1" p
98
+ for p in "${CATALOG_PATHS[@]}"; do
99
+ [ "$line" = "$p" ] && return 0
100
+ done
101
+ return 1
102
+ }
103
+
104
+ # ─── Read the existing file ─────────────────────────────────────────────────
105
+ ORIGINAL=()
106
+ if [ -f "$GITIGNORE" ]; then
107
+ while IFS= read -r line || [ -n "$line" ]; do
108
+ ORIGINAL+=("$line")
109
+ done < "$GITIGNORE"
110
+ elif [ "$MODE" = "visible" ]; then
111
+ info "No .gitignore at this root and mode is 'visible' — nothing to do."
112
+ exit 0
113
+ else
114
+ info "No .gitignore at this root — creating one."
115
+ fi
116
+
117
+ # ─── Filter: drop stray terminators, the old managed block, loose dupes ─────
118
+ FILTERED=()
119
+ in_block=0
120
+ removed_eof=0
121
+ removed_loose=0
122
+ had_block=0
123
+
124
+ for line in "${ORIGINAL[@]+"${ORIGINAL[@]}"}"; do
125
+ if [ "$in_block" -eq 1 ]; then
126
+ [ "$line" = "$MANAGED_END" ] && in_block=0
127
+ continue
128
+ fi
129
+ if [ "$line" = "$MANAGED_BEGIN" ]; then
130
+ in_block=1; had_block=1; continue
131
+ fi
132
+ if [ "$KEEP_EOF" -eq 0 ] && [[ "$line" =~ ^[[:space:]]*EOF[[:space:]]*$ ]]; then
133
+ removed_eof=$((removed_eof + 1)); continue
134
+ fi
135
+ if is_catalog_path "$line"; then
136
+ removed_loose=$((removed_loose + 1)); continue
137
+ fi
138
+ FILTERED+=("$line")
139
+ done
140
+
141
+ if [ "$in_block" -eq 1 ]; then
142
+ err "Unterminated managed block — found '${MANAGED_BEGIN}' with no matching end marker."
143
+ err "Refusing to write. Fix ${GITIGNORE} by hand, then re-run."
144
+ exit 3
145
+ fi
146
+
147
+ # Trim trailing blank lines so repeated runs cannot accumulate whitespace.
148
+ # Indexed from the end by arithmetic, NOT via ${arr[-1]}: macOS still ships
149
+ # bash 3.2, which has no negative array subscripts — there the subscript
150
+ # silently evaluates empty, the loop condition stays true, and nothing is ever
151
+ # unset, so the script spins forever.
152
+ _last=$(( ${#FILTERED[@]} - 1 ))
153
+ while [ "$_last" -ge 0 ] && [ -z "${FILTERED[$_last]// /}" ]; do
154
+ unset "FILTERED[$_last]"
155
+ _last=$(( _last - 1 ))
156
+ done
157
+
158
+ # ─── Build the new file ─────────────────────────────────────────────────────
159
+ : > "$TMP" || { err "Cannot write temp file: $TMP"; exit 3; }
160
+
161
+ for line in "${FILTERED[@]+"${FILTERED[@]}"}"; do
162
+ printf '%s\n' "$line" >> "$TMP" || { err "Write failed: $TMP"; exit 3; }
163
+ done
164
+
165
+ if [ "$MODE" = "ignored" ]; then
166
+ [ "${#FILTERED[@]}" -gt 0 ] && printf '\n' >> "$TMP"
167
+ {
168
+ printf '%s\n' "$MANAGED_BEGIN"
169
+ printf '%s\n' "# Managed by j.gitignore — regenerate with:"
170
+ printf '%s\n' "# bash <skill>/scripts/repair-gitignore.sh ignored . --tiers ${TIERS}"
171
+ printf '%s\n' "# Remove with the same script in 'visible' mode. Edits inside this block"
172
+ printf '%s\n' "# are overwritten; put your own patterns outside it."
173
+ for i in "${!CATALOG_PATHS[@]}"; do
174
+ printf '%s\n' "${CATALOG_PATHS[$i]}"
175
+ done
176
+ printf '%s\n' "$MANAGED_END"
177
+ } >> "$TMP" || { err "Write failed: $TMP"; exit 3; }
178
+ fi
179
+
180
+ # ─── Report / apply ─────────────────────────────────────────────────────────
181
+ if [ -f "$GITIGNORE" ] && cmp -s "$GITIGNORE" "$TMP"; then
182
+ info "Already correct — ${GITIGNORE} needs no change."
183
+ exit 0
184
+ fi
185
+
186
+ echo
187
+ echo "── Proposed change to ${GITIGNORE} ────────────────────────────────────"
188
+ if [ -f "$GITIGNORE" ]; then
189
+ diff -u "$GITIGNORE" "$TMP" | sed 's/^/ /' || true
190
+ else
191
+ sed 's/^/ + /' "$TMP"
192
+ fi
193
+ echo
194
+
195
+ info "Stray 'EOF' terminators removed : ${removed_eof}"
196
+ info "Loose duplicate entries folded in: ${removed_loose}"
197
+ info "Pre-existing managed block : $([ "$had_block" -eq 1 ] && echo replaced || echo none)"
198
+ info "Mode : ${MODE} (tiers: ${TIERS})"
199
+
200
+ if [ "$DRY_RUN" -eq 1 ]; then
201
+ info "--dry-run — nothing written."
202
+ exit 0
203
+ fi
204
+
205
+ if ! mv "$TMP" "$GITIGNORE"; then
206
+ err "Failed to move ${TMP} into place."
207
+ exit 3
208
+ fi
209
+ trap - EXIT
210
+
211
+ # Never report success without re-verifying the result on disk.
212
+ if [ "$MODE" = "ignored" ]; then
213
+ for p in "${CATALOG_PATHS[@]}"; do
214
+ if ! grep -qxF -- "$p" "$GITIGNORE"; then
215
+ err "Post-write verification failed: '${p}' is not in ${GITIGNORE}."
216
+ exit 3
217
+ fi
218
+ done
219
+ fi
220
+ if [ "$KEEP_EOF" -eq 0 ] && grep -qE '^[[:space:]]*EOF[[:space:]]*$' "$GITIGNORE"; then
221
+ err "Post-write verification failed: a stray EOF line survived."
222
+ exit 3
223
+ fi
224
+
225
+ info "Wrote ${GITIGNORE} (verified)."
226
+ exit 0
@@ -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.help
3
- description: Polyfill alias of the help skill under a collision-safe directory name. Identical behavior to /help — List all available skills with a short description of what each one does. Also use this skill when the user wants to know what commands or skills are available in the project. Use when the bare /help form is shadowed by another tool's own built-in command of the same name.
3
+ description: List all available skills with a short description of what each one does. Also use this skill when the user wants to know what commands or skills are available in the project.
4
4
  keywords:
5
5
  - help
6
6
  - skills
@@ -8,7 +8,6 @@ keywords:
8
8
  - what can you do
9
9
  - list skills
10
10
  - j-help
11
- - polyfill
12
11
  examples:
13
12
  - "what skills are available?"
14
13
  - "show me all commands"
@@ -17,9 +16,9 @@ examples:
17
16
 
18
17
  # Help — List Available Skills
19
18
 
20
- This skill is a literal-directory-name duplicate of `skills/help/`. It exists so that `/j-help` (and `j.j-help`) give a guaranteed-unshadowed way to reach the same flow as `/help`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/help` alias (Claude Code's native skill resolution is a literal-string, directory-name-based match — see `docs/skill-authoring.md`'s "Invocation Convention").
19
+ `skills/j-help/` is the **canonical, hand-edited** directory for this skill, per CLAUDE.md's "The Canonical Naming Contract" (the `E50` reopening of 2026-09-09, which promoted `skills/j-help/` from generated twin to sole canonical form). The `j-` prefix is there for collision safety — a real directory under a distinct name, so a host tool shipping its own same-named built-in command cannot shadow it (Claude Code's native skill resolution is a literal-string, directory-name-based match; see `docs/skill-authoring.md`'s "Invocation Convention").
21
20
 
22
- This file is generated/synced by `scripts/generate-j-alias.sh help` from `skills/help/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
21
+ > ⚠️ **`scripts/generate-j-alias.sh` was retired by `E50_S14` and no longer exists — there is nothing to run.** This file was previously generated from a bare `skills/help/SKILL.md` source; `E50_S15` deleted that directory. This file is now the sole canonical, hand-edited source for this skill — edit it directly.
23
22
 
24
23
  ## Instructions
25
24
 
@@ -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: Capture a loosely-defined idea to project/ideas.md — a lightweight, "maybe someday" log with no board or promotion overhead.
4
4
  keywords:
5
5
  - idea
6
6
  - capture idea
@@ -8,7 +8,6 @@ keywords:
8
8
  - brain dump
9
9
  - maybe someday
10
10
  - j-idea
11
- - polyfill
12
11
  examples:
13
12
  - "capture this as an idea"
14
13
  - "log this idea for later"
@@ -20,9 +19,9 @@ metadata:
20
19
 
21
20
  # Idea — Lightweight Idea Capture
22
21
 
23
- This skill is a literal-directory-name duplicate of `skills/idea/`. It exists so that `/j-idea` (and `j.j-idea`) give a guaranteed-unshadowed way to reach the same flow as `/idea`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/idea` alias (Claude Code's native skill resolution is a literal-string, directory-name-based match — see `docs/skill-authoring.md`'s "Invocation Convention").
22
+ `skills/j-idea/` is the **canonical, hand-edited** directory for this skill, per CLAUDE.md's "The Canonical Naming Contract" (the `E50` reopening of 2026-09-09, which promoted `skills/j-idea/` from generated twin to sole canonical form). The `j-` prefix is there for collision safety — a real directory under a distinct name, so a host tool shipping its own same-named built-in command cannot shadow it (Claude Code's native skill resolution is a literal-string, directory-name-based match; see `docs/skill-authoring.md`'s "Invocation Convention").
24
23
 
25
- This file is generated/synced by `scripts/generate-j-alias.sh idea` from `skills/idea/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
24
+ > ⚠️ **`scripts/generate-j-alias.sh` was retired by `E50_S14` and no longer exists — there is nothing to run.** This file was previously generated from a bare `skills/idea/SKILL.md` source; `E50_S15` deleted that directory. This file is now the sole canonical, hand-edited source for this skill — edit it directly.
26
25
 
27
26
  ## Instructions
28
27
 
@@ -52,12 +51,84 @@ This file is generated/synced by `scripts/generate-j-alias.sh idea` from `skills
52
51
  - **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
52
  - **Terminal idea states** are marked directly in `project/ideas.md` using the same HTML-comment tag convention `project/todo.md` uses for `RECONCILED`:
54
53
  - `PROMOTED` — the idea was picked up via `/brainstorm` and turned into board work.
55
- - `DISCARDED` — the idea was reviewed and dropped.
54
+ - `REJECTED` — the idea was reviewed and dropped.
56
55
 
57
- Example (mirroring `project/todo.md`'s tagging style):
56
+ ### Embedded Board ID
57
+
58
+ `PROMOTED`/`REJECTED` tags support an embedded board ID, so the idea→board-item link is
59
+ machine-readable directly from the tag itself, without needing a separate lookup:
60
+
61
+ - `<!-- PROMOTED: E##_S##[_T##] -->` — the `_T##` task suffix is optional; use whichever level
62
+ the idea was actually promoted into (an epic, a story, or a specific task).
63
+ - `<!-- REJECTED -->` — stays bare, with no embedded ID. A rejection has no resulting board
64
+ item to point at.
65
+
66
+ Example (mirroring `project/todo.md`'s tagging style):
67
+ ```
68
+ Add dark mode toggle to settings page <!-- PROMOTED: E12_S03 -->
69
+ Add a settings-page keyboard shortcut cheat sheet <!-- PROMOTED: E12_S03_T02 -->
70
+ Rewrite onboarding copy in a more casual tone <!-- REJECTED -->
71
+ ```
72
+
73
+ ### Source-Rapport Link Convention
74
+
75
+ Some ideas originate from a rapport rather than being manually captured via `/idea` — e.g. when
76
+ scrum-master's `rapport_review` step (E35_S03) detects a distinct idea surfaced inside a
77
+ problem rapport and, on user confirmation, appends it to `project/ideas.md` the same way
78
+ `/idea` does. When that happens, the entry's source rapport is recorded with a second inline
79
+ HTML comment on the same line:
80
+
81
+ ```
82
+ <!-- rapport: <path/to/rapport.md> -->
83
+ ```
84
+
85
+ Rules:
86
+ - Written at capture time, alongside the idea text, by whichever flow is adding the entry
87
+ (scrum-master's rapport-derived capture path; `/idea`'s own manual capture never has a source
88
+ rapport and omits this comment entirely).
89
+ - Left in place through promotion/rejection — the `rapport:` comment and the terminal
90
+ `PROMOTED`/`REJECTED` comment coexist on the same line, e.g.:
58
91
  ```
59
- Add dark mode toggle to settings page <!-- PROMOTED -->
60
- Rewrite onboarding copy in a more casual tone <!-- DISCARDED -->
92
+ Extract shared retry logic into a utility <!-- rapport: project/rapports/problems/E40_S02_T01-segment-remarks.md --> <!-- PROMOTED: E61_S01 -->
61
93
  ```
94
+ - Consumed by the outcome-record mechanism at promotion/rejection time: if a `rapport:` comment
95
+ is present on the line, that rapport is updated with the decision (and `[SPIKE]`-prefixed in
96
+ its title/header if not already, per `templates/SCRUM_BOARD_SCHEMA.md`'s `[SPIKE]` section);
97
+ if absent (a manually-captured `/idea` entry with no source rapport), a
98
+ `project/documentation/plans/<slug>_PROMOTED.md` or `_REJECTED.md` file is created instead.
99
+ - Verified compatible with `scripts/idea_manager.sh`'s `real_entries()` filtering: that function
100
+ only strips lines whose first non-whitespace characters are `<!--`; a trailing inline comment
101
+ on an otherwise real entry line is untouched and the line still counts as a real entry.
102
+
103
+ No agent invoked by `/idea` applies terminal (`PROMOTED`/`REJECTED`) tags or acts on them — that
104
+ is reserved for the promotion step (e.g. within `/brainstorm`) and, for rapport-sourced ideas,
105
+ scrum-master's rapport-review flow.
106
+
107
+ ### Outcome-Record Mechanism (`idea_manager.sh`) — E35_S03_T03
108
+
109
+ `scripts/idea_manager.sh` implements the tagging and outcome-record behavior described above, via
110
+ four additional subcommands beyond `add`/`list`:
111
+
112
+ - `tag "<idea-text>" <PROMOTED:E##_S##[_T##]|REJECTED>` — writes the terminal tag onto the
113
+ `project/ideas.md` line matching `<idea-text>` (an exact/literal match; errors if zero or more
114
+ than one line matches, and errors if the line is already tagged).
115
+ - `get-id "<idea-text>"` — prints the embedded board ID from that line's `PROMOTED` tag (errors
116
+ if the line has no `PROMOTED` tag).
117
+ - `get-rapport "<idea-text>"` — prints the `rapport:` path recorded on that line, or nothing
118
+ (exit 0) if the line has no source-rapport comment.
119
+ - `resolve "<idea-text>" <promoted|rejected> [board_id]` — the full outcome-record mechanism in
120
+ one call: tags the line (as `tag` does), then either updates the idea's source rapport or, if
121
+ none is recorded, creates the `project/documentation/plans/` fallback file:
122
+ - **Source rapport present:** the rapport's `# Rapport: ...` title line is prefixed
123
+ `[SPIKE] ` if not already, and an `## Idea Outcome Record` section (decision, date, resulting
124
+ board ID if promoted, and the idea text) is appended to the rapport file.
125
+ - **No source rapport:** `project/documentation/plans/<slug>_PROMOTED.md` or `_REJECTED.md` is
126
+ created (idea text slugified, lowercased, capped at 60 characters), containing the same
127
+ decision/date/board-ID/idea-text content.
128
+
129
+ `board_id` is required for `promoted` (validated to start with `E<digits>`) and must be omitted
130
+ for `rejected` (a rejection has no resulting board item).
62
131
 
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`).
132
+ This is the mechanism itself, not the trigger — nothing currently calls `resolve` automatically.
133
+ A future promotion step (e.g. within `/brainstorm`'s routing, or scrum-master acting on a
134
+ 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) -->
@@ -1,16 +1,15 @@
1
1
  ---
2
2
  name: j.improve
3
- description: Polyfill alias of the improve skill under a collision-safe directory name. Identical behavior to /improve — A skill for analyzing a codebase and producing a structured improvement plan toward a defined goal. Use when the bare /improve form is shadowed by another tool's own built-in command of the same name.
3
+ description: A skill for analyzing a codebase and producing a structured improvement plan toward a defined goal.
4
4
  keywords:
5
5
  - j-improve
6
- - polyfill
7
6
  ---
8
7
 
9
8
  # Improve
10
9
 
11
- This skill is a literal-directory-name duplicate of `skills/improve/`. It exists so that `/j-improve` (and `j.j-improve`) give a guaranteed-unshadowed way to reach the same flow as `/improve`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/improve` alias (Claude Code's native skill resolution is a literal-string, directory-name-based match — see `docs/skill-authoring.md`'s "Invocation Convention").
10
+ `skills/j-improve/` is the **canonical, hand-edited** directory for this skill, per CLAUDE.md's "The Canonical Naming Contract" (the `E50` reopening of 2026-09-09, which promoted `skills/j-improve/` from generated twin to sole canonical form). The `j-` prefix is there for collision safety — a real directory under a distinct name, so a host tool shipping its own same-named built-in command cannot shadow it (Claude Code's native skill resolution is a literal-string, directory-name-based match; see `docs/skill-authoring.md`'s "Invocation Convention").
12
11
 
13
- This file is generated/synced by `scripts/generate-j-alias.sh improve` from `skills/improve/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
12
+ > ⚠️ **`scripts/generate-j-alias.sh` was retired by `E50_S14` and no longer exists — there is nothing to run.** This file was previously generated from a bare `skills/improve/SKILL.md` source; `E50_S15` deleted that directory. This file is now the sole canonical, hand-edited source for this skill — edit it directly.
14
13
 
15
14
  ## Instructions
16
15
 
@@ -50,7 +49,7 @@ Assess whether you have enough context to evaluate current behavior against the
50
49
  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
50
 
52
51
  ### 5. Run `/evaluate`
53
- Copy `.agents/skills/evaluate/assets/evaluation_invokation_template.yml` and fill in:
52
+ Copy `.agents/skills/j-evaluate/assets/evaluation_invokation_template.yml` and fill in:
54
53
  - `paths`: path(s) to the relevant example files
55
54
  - `goal`: the confirmed target goal
56
55