@jenga-ai/agent 2.0.0 → 3.1.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 (177) hide show
  1. package/README.md +82 -243
  2. package/agents/developer.md +5 -5
  3. package/agents/scrum-master.md +23 -23
  4. package/agents/tester.md +5 -5
  5. package/bin/jenga.js +10 -0
  6. package/hooks/copilot_session_end.sh +7 -3
  7. package/hooks/prompt_router_helper.js +17 -5
  8. package/lib/commands/doctor.js +351 -0
  9. package/lib/commands/init.js +16 -0
  10. package/lib/generate-copilot-hooks.js +116 -0
  11. package/lib/generate-skill-allow-list.js +9 -3
  12. package/lib/legacy-shipped-paths.json +336 -0
  13. package/lib/postinstall-manifest.js +469 -0
  14. package/lib/skill-allow-list.json +2 -3
  15. package/package.json +16 -25
  16. package/scripts/apply-j-prefix.sh +25 -12
  17. package/scripts/generate-j-alias.sh +333 -0
  18. package/scripts/generate-legacy-shipped-paths.js +248 -0
  19. package/scripts/postinstall.js +205 -2
  20. package/scripts/verify-legacy-seed-reconcile.sh +254 -0
  21. package/scripts/verify-postinstall-reconcile.sh +392 -0
  22. package/skills/{brainstorm → j-brainstorm}/SKILL.md +9 -2
  23. package/skills/{btw → j-btw}/SKILL.md +9 -2
  24. package/skills/{clearify → j-clearify}/SKILL.md +9 -2
  25. package/skills/{close-story → j-close-story}/SKILL.md +17 -10
  26. package/skills/{close-story → j-close-story}/scripts/check-privatized.sh +2 -2
  27. package/skills/{close-story → j-close-story}/scripts/check-story-closeable.sh +1 -1
  28. package/skills/{close-story → j-close-story}/scripts/extract-task-diff-stats.sh +1 -1
  29. package/skills/{commit → j-commit}/SKILL.md +9 -2
  30. package/skills/j-continue/SKILL.md +36 -0
  31. package/skills/{deep-dive → j-deep-dive}/SKILL.md +9 -8
  32. package/skills/{dev-done → j-dev-done}/SKILL.md +11 -4
  33. package/skills/{dev-done → j-dev-done}/scripts/classify-commit-outcome.sh +4 -4
  34. package/skills/{distribute → j-distribute}/SKILL.md +17 -10
  35. package/skills/{distribute → j-distribute}/scripts/distribute-changes.sh +1 -1
  36. package/skills/{do → j-do}/SKILL.md +12 -5
  37. package/skills/{doc → j-doc}/README.md +5 -5
  38. package/skills/{doc → j-doc}/SKILL.md +15 -8
  39. package/skills/{doc → j-doc}/authoring-notes.md +1 -1
  40. package/skills/{doc-sync → j-doc-sync}/SKILL.md +9 -2
  41. package/skills/{dooo → j-dooo}/SKILL.md +9 -2
  42. package/skills/j-error/SKILL.md +36 -0
  43. package/skills/{evaluate → j-evaluate}/SKILL.md +9 -2
  44. package/skills/j-examplify/SKILL.md +49 -0
  45. package/skills/{help → j-help}/SKILL.md +9 -2
  46. package/skills/{idea → j-idea}/SKILL.md +10 -3
  47. package/skills/{idea → j-idea}/assets/idea_handoff_template.md +1 -1
  48. package/skills/{improve → j-improve}/SKILL.md +9 -2
  49. package/skills/j-init/SKILL.md +2 -2
  50. package/skills/j-jbp/SKILL.md +32 -0
  51. package/skills/j-lgtm/SKILL.md +28 -0
  52. package/skills/{pi-plan → j-pi-plan}/SKILL.md +10 -3
  53. package/skills/{proceed → j-proceed}/SKILL.md +9 -2
  54. package/skills/{publish → j-publish}/SKILL.md +47 -40
  55. package/skills/{publish → j-publish}/adapters/droplet.md +1 -1
  56. package/skills/{publish → j-publish}/adapters/mobile-ios.md +3 -3
  57. package/skills/{publish → j-publish}/adapters/npm-ci.md +3 -3
  58. package/skills/{publish → j-publish}/adapters/npm.md +8 -8
  59. package/skills/{publish → j-publish}/assets/ci-contract.md +2 -2
  60. package/skills/{publish → j-publish}/schemas/publish.schema.json +1 -1
  61. package/skills/{publish → j-publish}/scripts/npm_stage_inspect.sh +34 -1
  62. package/skills/{publish → j-publish}/scripts/npm_stage_pipeline.sh +9 -4
  63. package/skills/{publish → j-publish}/scripts/publish_deploy.sh +4 -4
  64. package/skills/{publish → j-publish}/scripts/validate_npm_stage_env.sh +1 -1
  65. package/skills/{publish → j-publish}/wizards/droplet.md +1 -1
  66. package/skills/{publish → j-publish}/wizards/mobile-ios.md +1 -1
  67. package/skills/{publish → j-publish}/wizards/npm-ci.md +1 -1
  68. package/skills/{publish → j-publish}/wizards/npm.md +1 -1
  69. package/skills/{reconcile → j-reconcile}/SKILL.md +12 -5
  70. package/skills/{reconcile → j-reconcile}/scripts/detect-unlinked-code.sh +2 -2
  71. package/skills/{reconcile → j-reconcile}/scripts/resolve-reconcile-scope.sh +3 -3
  72. package/skills/{reconcile-origin → j-reconcile-origin}/SKILL.md +13 -6
  73. package/skills/{redo → j-redo}/SKILL.md +9 -2
  74. package/skills/{skillify → j-skillify}/SKILL.md +10 -3
  75. package/skills/{spinoff → j-spinoff}/SKILL.md +9 -2
  76. package/skills/{status → j-status}/SKILL.md +9 -2
  77. package/skills/{todo → j-todo}/SKILL.md +10 -3
  78. package/skills/{todo → j-todo}/assets/todo_handoff_template.md +1 -1
  79. package/skills/{todo → j-todo}/scripts/add_trivial_task.sh +3 -3
  80. package/skills/{todo → j-todo}/scripts/update_story_tasks.py +2 -2
  81. package/skills/{uncharted → j-uncharted}/SKILL.md +35 -28
  82. package/skills/{uncharted → j-uncharted}/assets/UNDERSTANDING_DOC_TEMPLATE.md +2 -2
  83. package/skills/{uncharted → j-uncharted}/scripts/detect-dependencies.sh +1 -1
  84. package/skills/{uncharted → j-uncharted}/scripts/detect-tests.sh +1 -1
  85. package/skills/{uncharted → j-uncharted}/scripts/directory-triage.sh +3 -3
  86. package/skills/{uncharted → j-uncharted}/scripts/elicitation-state.sh +3 -3
  87. package/skills/{uncharted → j-uncharted}/scripts/enumerate-target.sh +1 -1
  88. package/skills/{uncharted → j-uncharted}/scripts/import-source.sh +1 -1
  89. package/skills/{uncharted → j-uncharted}/scripts/inspect-provenance.sh +1 -1
  90. package/skills/{uncharted → j-uncharted}/scripts/resolve-segment-target.sh +5 -5
  91. package/skills/{uncharted → j-uncharted}/scripts/run-engine.sh +1 -1
  92. package/skills/{uncharted → j-uncharted}/scripts/validate-proposed-items.sh +2 -2
  93. package/skills/{uncharted → j-uncharted}/scripts/write-backfilled-epics.sh +1 -1
  94. package/skills/j-wtf/SKILL.md +27 -0
  95. package/skills/jenga/SKILL.md +1 -1
  96. package/skills/jenga-permission-level/SKILL.md +1 -1
  97. package/templates/SCRUM_BOARD_SCHEMA.md +1 -1
  98. package/templates/agent-context.md.tpl +10 -10
  99. package/templates/copilot-instructions.md.tpl +89 -19
  100. package/skills/continue/SKILL.md +0 -29
  101. package/skills/error/SKILL.md +0 -29
  102. package/skills/examplify/SKILL.md +0 -42
  103. package/skills/init/SKILL.md +0 -155
  104. package/skills/init/assets/scope-thresholds_template.json +0 -7
  105. package/skills/init/assets/strategy_stub_template.md +0 -38
  106. package/skills/init/assets/workflow_template.json +0 -30
  107. package/skills/init/scripts/apply-project-visibility.sh +0 -176
  108. package/skills/init/scripts/detect-existing-codebase.sh +0 -166
  109. package/skills/init/scripts/init.sh +0 -116
  110. package/skills/jbp/SKILL.md +0 -25
  111. package/skills/lgtm/SKILL.md +0 -21
  112. package/skills/skillify/assets/init-new/assets/.gitignore_template +0 -15
  113. package/skills/skillify/assets/init-new/assets/PROJECT_SUMMARY_template.md +0 -13
  114. package/skills/skillify/assets/init-new/assets/directory_structure.txt +0 -14
  115. package/skills/skillify/assets/init-new/assets/test-config_template.json +0 -4
  116. package/skills/wtf/SKILL.md +0 -20
  117. /package/skills/{close-story → j-close-story}/scripts/compute-scope-divergence.sh +0 -0
  118. /package/skills/{close-story → j-close-story}/scripts/extract-diff-stats.sh +0 -0
  119. /package/skills/{close-story → j-close-story}/scripts/update-task-frontmatter.sh +0 -0
  120. /package/skills/{commit → j-commit}/assets/user_instructions_template.md +0 -0
  121. /package/skills/{distribute → j-distribute}/CONFIG_SCHEMA.md +0 -0
  122. /package/skills/{distribute → j-distribute}/scripts/check-version.sh +0 -0
  123. /package/skills/{distribute → j-distribute}/scripts/commit-version-bump.sh +0 -0
  124. /package/skills/{do → j-do}/assets/intent-vs-diff-prompt.md +0 -0
  125. /package/skills/{do → j-do}/assets/sender_template.json +0 -0
  126. /package/skills/{doc → j-doc}/assets/path-objectives.yaml +0 -0
  127. /package/skills/{doc → j-doc}/scripts/resolve_last_update.py +0 -0
  128. /package/skills/{doc-sync → j-doc-sync}/assets/default_excludes.txt +0 -0
  129. /package/skills/{doc-sync → j-doc-sync}/assets/doc_targets.md +0 -0
  130. /package/skills/{evaluate → j-evaluate}/assets/evaluation_invokation_template.yml +0 -0
  131. /package/skills/{evaluate → j-evaluate}/assets/evaluation_rapport_template.md +0 -0
  132. /package/skills/{idea → j-idea}/assets/idea_template.md +0 -0
  133. /package/skills/{pi-plan → j-pi-plan}/assets/epic.json +0 -0
  134. /package/skills/{pi-plan → j-pi-plan}/assets/story_template.md +0 -0
  135. /package/skills/{publish → j-publish}/assets/ExportOptions.plist.template +0 -0
  136. /package/skills/{publish → j-publish}/assets/ownership-matrix.md +0 -0
  137. /package/skills/{publish → j-publish}/assets/publish.example.json +0 -0
  138. /package/skills/{publish → j-publish}/assets/publish.example.npm-ci.json +0 -0
  139. /package/skills/{publish → j-publish}/assets/publish.example.npm.json +0 -0
  140. /package/skills/{publish → j-publish}/assets/secrets-guide.md +0 -0
  141. /package/skills/{publish → j-publish}/schemas/fixtures/npm-ci-minimal.json +0 -0
  142. /package/skills/{publish → j-publish}/schemas/fixtures/npm-ci-with-empty-secrets.json +0 -0
  143. /package/skills/{publish → j-publish}/schemas/fixtures/npm-ci-with-workflow-path.json +0 -0
  144. /package/skills/{publish → j-publish}/scripts/check_target_config.sh +0 -0
  145. /package/skills/{publish → j-publish}/scripts/droplet_pipeline.sh +0 -0
  146. /package/skills/{publish → j-publish}/scripts/finalize_changelog.sh +0 -0
  147. /package/skills/{publish → j-publish}/scripts/generate_release_notes.sh +0 -0
  148. /package/skills/{publish → j-publish}/scripts/ios_pipeline.sh +0 -0
  149. /package/skills/{publish → j-publish}/scripts/npm_ci_pipeline.sh +0 -0
  150. /package/skills/{publish → j-publish}/scripts/npm_pipeline.sh +0 -0
  151. /package/skills/{publish → j-publish}/scripts/publish_common.sh +0 -0
  152. /package/skills/{publish → j-publish}/scripts/reconcile_tags.sh +0 -0
  153. /package/skills/{publish → j-publish}/scripts/run_gates.sh +0 -0
  154. /package/skills/{publish → j-publish}/scripts/setup_wizard.sh +0 -0
  155. /package/skills/{publish → j-publish}/scripts/show_history.sh +0 -0
  156. /package/skills/{publish → j-publish}/scripts/suggest_semver_bump.sh +0 -0
  157. /package/skills/{publish → j-publish}/scripts/validate_config.sh +0 -0
  158. /package/skills/{publish → j-publish}/scripts/validate_droplet_env.sh +0 -0
  159. /package/skills/{publish → j-publish}/scripts/validate_ios_env.sh +0 -0
  160. /package/skills/{publish → j-publish}/scripts/validate_npm_ci_env.sh +0 -0
  161. /package/skills/{publish → j-publish}/scripts/validate_npm_env.sh +0 -0
  162. /package/skills/{publish → j-publish}/scripts/write_ledger_entry.sh +0 -0
  163. /package/skills/{reconcile → j-reconcile}/assets/report_format.md +0 -0
  164. /package/skills/{reconcile-origin → j-reconcile-origin}/scripts/reconcile-origin.sh +0 -0
  165. /package/skills/{skillify → j-skillify}/assets/init-new/SKILL.md +0 -0
  166. /package/skills/{init → j-skillify/assets/init-new}/assets/.gitignore_template +0 -0
  167. /package/skills/{init → j-skillify/assets/init-new}/assets/PROJECT_SUMMARY_template.md +0 -0
  168. /package/skills/{init → j-skillify/assets/init-new}/assets/directory_structure.txt +0 -0
  169. /package/skills/{init → j-skillify/assets/init-new}/assets/test-config_template.json +0 -0
  170. /package/skills/{skillify → j-skillify}/assets/init-new/assets/workflow_template.json +0 -0
  171. /package/skills/{skillify → j-skillify}/assets/init-new/scripts/init.sh +0 -0
  172. /package/skills/{skillify → j-skillify}/assets/init-old/SKILL.md +0 -0
  173. /package/skills/{status → j-status}/assets/output_format.md +0 -0
  174. /package/skills/{todo → j-todo}/assets/todo_template.md +0 -0
  175. /package/skills/{uncharted → j-uncharted}/assets/SEGMENT_PROPOSAL_TEMPLATE.md +0 -0
  176. /package/skills/{uncharted → j-uncharted}/scripts/apply-subsystem-cap.sh +0 -0
  177. /package/skills/{uncharted → j-uncharted}/scripts/discover-subsystems.sh +0 -0
@@ -0,0 +1,392 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # verify-postinstall-reconcile.sh — fixture rehearsal for E26_S08_T01
4
+ #
5
+ # Rehearses the manifest-based delete reconciliation in scripts/postinstall.js
6
+ # against a THROWAWAY fixture consumer project, as required by E26_S08's
7
+ # `crucial_level: gated` note ("must be rehearsed against a throwaway fixture
8
+ # consumer directory before being considered done, not just unit-reasoned about").
9
+ #
10
+ # SAFETY: this script creates its own fixture root via `mktemp -d` and writes and
11
+ # deletes ONLY inside it. It takes no path argument and will never operate on an
12
+ # existing directory, so it cannot touch this repo or a real consumer project.
13
+ # Set KEEP_FIXTURE=1 to leave the fixture on disk for inspection.
14
+ #
15
+ # Scenarios covered
16
+ # A. First install with no prior manifest -> additive only, pre-existing consumer
17
+ # files survive, NO delete pass runs.
18
+ # B. Upgrade across a release that renames / removes / excludes skills -> stale
19
+ # package files removed from both mirror roots, consumer-authored files survive
20
+ # (including one planted INSIDE a package directory that is otherwise emptied),
21
+ # and files common to both versions are left byte- and mtime-identical.
22
+ #
23
+ # Usage: bash scripts/verify-postinstall-reconcile.sh
24
+ # Exit: 0 = all assertions passed, 1 = at least one failed.
25
+
26
+ set -uo pipefail
27
+
28
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
29
+ FIXTURE="$(mktemp -d "${TMPDIR:-/tmp}/jenga-postinstall-fixture.XXXXXX")"
30
+
31
+ PASS=0
32
+ FAIL=0
33
+
34
+ # When RESULTS_FILE is set, every verdict is also appended in a machine-readable
35
+ # `PASS<TAB><name>` / `FAIL<TAB><name>` form, so tests/postinstall-delete-reconciliation.bats
36
+ # can assert on individual named checks instead of on one opaque exit status.
37
+ record() { if [ -n "${RESULTS_FILE:-}" ]; then printf '%s\t%s\n' "$1" "$2" >> "$RESULTS_FILE"; fi; }
38
+
39
+ pass() { PASS=$((PASS + 1)); record PASS "$1"; printf ' \033[32mPASS\033[0m %s\n' "$1"; }
40
+ fail() { FAIL=$((FAIL + 1)); record FAIL "$1"; printf ' \033[31mFAIL\033[0m %s\n' "$1"; }
41
+
42
+ exists() { [ -e "$1" ]; }
43
+ assert_file() { if [ -f "$1" ]; then pass "$2"; else fail "$2 (missing: $1)"; fi; }
44
+ assert_absent() { if [ ! -e "$1" ]; then pass "$2"; else fail "$2 (still present: $1)"; fi; }
45
+ assert_grep() { if grep -q "$1" "$2" 2>/dev/null; then pass "$3"; else fail "$3"; fi; }
46
+ assert_nogrep() { if grep -q "$1" "$2" 2>/dev/null; then fail "$3"; else pass "$3"; fi; }
47
+ # ERE variant — needed where the pattern is a real regex rather than a literal.
48
+ assert_nogrep_re() { if grep -qE "$1" "$2" 2>/dev/null; then fail "$3"; else pass "$3"; fi; }
49
+
50
+ cleanup() {
51
+ if [ "${KEEP_FIXTURE:-0}" = "1" ]; then
52
+ printf '\n Fixture retained at: %s\n' "$FIXTURE"
53
+ else
54
+ # Confined to the mktemp -d created by this script itself.
55
+ rm -rf "$FIXTURE"
56
+ fi
57
+ }
58
+ trap cleanup EXIT
59
+
60
+ # ── build a fake package version ─────────────────────────────────────────────
61
+ # Copies the real lib/, scripts/ and templates/ from this repo (so the code under
62
+ # test is the real code), then lays down a small synthetic skills/ + agents/ tree.
63
+ make_pkg() {
64
+ local dir="$1" version="$2"
65
+ mkdir -p "$dir"
66
+ cp -R "$REPO_ROOT/lib" "$dir/lib"
67
+ cp -R "$REPO_ROOT/scripts" "$dir/scripts"
68
+ cp -R "$REPO_ROOT/templates" "$dir/templates" 2>/dev/null || true
69
+ cat > "$dir/package.json" <<JSON
70
+ { "name": "@jenga-ai/agent", "version": "$version", "type": "module" }
71
+ JSON
72
+ mkdir -p "$dir/skills" "$dir/agents"
73
+ }
74
+
75
+ add_skill() {
76
+ local dir="$1" name="$2" body="$3"
77
+ mkdir -p "$dir/skills/$name"
78
+ printf '%s\n' "$body" > "$dir/skills/$name/SKILL.md"
79
+ }
80
+
81
+ run_install() {
82
+ local pkg="$1" consumer="$2" log="$3"
83
+ ( cd "$pkg" && INIT_CWD="$consumer" node scripts/postinstall.js ) > "$log" 2>&1
84
+ }
85
+
86
+ echo
87
+ echo "══ Jenga postinstall delete-reconciliation rehearsal ══"
88
+ echo " fixture: $FIXTURE"
89
+
90
+ # ── package v1.0.0 ───────────────────────────────────────────────────────────
91
+ PKG1="$FIXTURE/pkg-v1"
92
+ make_pkg "$PKG1" "1.0.0"
93
+ add_skill "$PKG1" "do" "# do — stable across both versions"
94
+ add_skill "$PKG1" "renamed-old" "# renamed-old — becomes renamed-new in v2"
95
+ add_skill "$PKG1" "j-legacy-twin" "# j-legacy-twin — excluded in v2 (E50_S06 style)"
96
+ printf '# developer agent v1\n' > "$PKG1/agents/developer.md"
97
+
98
+ # ── package v2.0.0 ───────────────────────────────────────────────────────────
99
+ PKG2="$FIXTURE/pkg-v2"
100
+ make_pkg "$PKG2" "2.0.0"
101
+ add_skill "$PKG2" "do" "# do — stable across both versions" # byte-identical
102
+ add_skill "$PKG2" "renamed-new" "# renamed-new — replaces renamed-old"
103
+ # j-legacy-twin intentionally absent (excluded), renamed-old intentionally absent
104
+ printf '# developer agent v2 (changed)\n' > "$PKG2/agents/developer.md"
105
+
106
+ CONSUMER="$FIXTURE/consumer"
107
+ mkdir -p "$CONSUMER"
108
+
109
+ # ═══ Scenario A — first install, no prior manifest ═══════════════════════════
110
+ echo
111
+ echo "── Scenario A: first install (no prior manifest) ──"
112
+
113
+ # Plant a consumer file BEFORE the very first install. A first install must never
114
+ # delete it, because there is no manifest telling us what we wrote before.
115
+ mkdir -p "$CONSUMER/.agents/skills/preexisting" "$CONSUMER/.claude/skills/preexisting"
116
+ printf '# consumer file that predates any jenga install\n' > "$CONSUMER/.agents/skills/preexisting/JUNK.md"
117
+ printf '# consumer file that predates any jenga install\n' > "$CONSUMER/.claude/skills/preexisting/JUNK.md"
118
+
119
+ run_install "$PKG1" "$CONSUMER" "$FIXTURE/install-v1.log"
120
+
121
+ assert_grep "no previous install manifest" "$FIXTURE/install-v1.log" \
122
+ "first install reports no delete pass"
123
+ assert_nogrep_re "[1-9][0-9]* stale file" "$FIXTURE/install-v1.log" \
124
+ "first install deletes nothing (zero stale files reported)"
125
+ assert_file "$CONSUMER/.agents/skills/preexisting/JUNK.md" \
126
+ "pre-existing consumer file survives first install (.agents)"
127
+ assert_file "$CONSUMER/.claude/skills/preexisting/JUNK.md" \
128
+ "pre-existing consumer file survives first install (.claude)"
129
+ assert_file "$CONSUMER/.agents/skills/renamed-old/SKILL.md" \
130
+ "v1 skill mirrored to .agents"
131
+ assert_file "$CONSUMER/.claude/skills/renamed-old/SKILL.md" \
132
+ "v1 skill mirrored to .claude"
133
+ assert_file "$CONSUMER/.agents/.jenga-postinstall-manifest.json" \
134
+ "manifest written to .agents on first install"
135
+ assert_file "$CONSUMER/.claude/.jenga-postinstall-manifest.json" \
136
+ "manifest written to .claude on first install"
137
+ assert_nogrep "preexisting/JUNK.md" "$CONSUMER/.agents/.jenga-postinstall-manifest.json" \
138
+ "consumer file is NOT recorded in the manifest"
139
+ assert_grep "skills/do/SKILL.md" "$CONSUMER/.agents/.jenga-postinstall-manifest.json" \
140
+ "manifest records mirrored package paths"
141
+
142
+ # ═══ Scenario B — upgrade with renames / removals / exclusions ════════════════
143
+ echo
144
+ echo "── Scenario B: upgrade 1.0.0 -> 2.0.0 ──"
145
+
146
+ # Consumer authors their own custom skill alongside package-owned ones.
147
+ for root in .agents .claude; do
148
+ mkdir -p "$CONSUMER/$root/skills/my-custom-skill"
149
+ printf '# my hand-authored skill\n' > "$CONSUMER/$root/skills/my-custom-skill/SKILL.md"
150
+ # ...and drops a note INSIDE a package directory that v2 removes entirely.
151
+ printf '# my notes inside a package-owned dir\n' > "$CONSUMER/$root/skills/j-legacy-twin/MY-NOTES.md"
152
+ done
153
+
154
+ # Record identity of a file common to both versions, to prove it is not needlessly
155
+ # deleted and recopied (mirror() should classify it as `skipped`).
156
+ DO_BEFORE="$(ls -li "$CONSUMER/.agents/skills/do/SKILL.md" | awk '{print $1, $6, $7, $8}')"
157
+
158
+ run_install "$PKG2" "$CONSUMER" "$FIXTURE/install-v2.log"
159
+
160
+ echo
161
+ echo " [upgrade cleanup lines]"
162
+ grep -E "stale file|left in place|no previous" "$FIXTURE/install-v2.log" | sed 's/^/ /'
163
+ echo
164
+
165
+ # 1. Stale, fully-removed package skill is gone from BOTH roots, dir pruned.
166
+ assert_absent "$CONSUMER/.agents/skills/renamed-old" \
167
+ "renamed-away skill dir removed from .agents"
168
+ assert_absent "$CONSUMER/.claude/skills/renamed-old" \
169
+ "renamed-away skill dir removed from .claude"
170
+
171
+ # 2. Excluded twin's package file is gone, but the consumer's note in the same dir
172
+ # survives — so the directory itself must NOT be pruned.
173
+ assert_absent "$CONSUMER/.agents/skills/j-legacy-twin/SKILL.md" \
174
+ "excluded twin's package file removed (.agents)"
175
+ assert_absent "$CONSUMER/.claude/skills/j-legacy-twin/SKILL.md" \
176
+ "excluded twin's package file removed (.claude)"
177
+ assert_file "$CONSUMER/.agents/skills/j-legacy-twin/MY-NOTES.md" \
178
+ "consumer note INSIDE the emptied package dir survives (.agents)"
179
+ assert_file "$CONSUMER/.claude/skills/j-legacy-twin/MY-NOTES.md" \
180
+ "consumer note INSIDE the emptied package dir survives (.claude)"
181
+
182
+ # 3. Consumer's own custom skill is untouched.
183
+ assert_file "$CONSUMER/.agents/skills/my-custom-skill/SKILL.md" \
184
+ "consumer custom skill survives upgrade (.agents)"
185
+ assert_file "$CONSUMER/.claude/skills/my-custom-skill/SKILL.md" \
186
+ "consumer custom skill survives upgrade (.claude)"
187
+ assert_file "$CONSUMER/.agents/skills/preexisting/JUNK.md" \
188
+ "pre-manifest consumer file still survives upgrade (.agents)"
189
+ assert_file "$CONSUMER/.claude/skills/preexisting/JUNK.md" \
190
+ "pre-manifest consumer file still survives upgrade (.claude)"
191
+
192
+ # 4. New skill arrived.
193
+ assert_file "$CONSUMER/.agents/skills/renamed-new/SKILL.md" \
194
+ "renamed-to skill installed (.agents)"
195
+ assert_file "$CONSUMER/.claude/skills/renamed-new/SKILL.md" \
196
+ "renamed-to skill installed (.claude)"
197
+
198
+ # 5. Unchanged common file was neither deleted nor recopied.
199
+ DO_AFTER="$(ls -li "$CONSUMER/.agents/skills/do/SKILL.md" | awk '{print $1, $6, $7, $8}')"
200
+ if [ "$DO_BEFORE" = "$DO_AFTER" ]; then
201
+ pass "file common to both versions untouched (same inode+mtime)"
202
+ else
203
+ fail "file common to both versions was rewritten ($DO_BEFORE -> $DO_AFTER)"
204
+ fi
205
+
206
+ # 6. Changed common file was overwritten.
207
+ assert_grep "v2 (changed)" "$CONSUMER/.agents/agents/developer.md" \
208
+ "changed package file overwritten with v2 content"
209
+
210
+ # 7. Manifest refreshed.
211
+ assert_nogrep "renamed-old" "$CONSUMER/.agents/.jenga-postinstall-manifest.json" \
212
+ "refreshed manifest no longer lists the removed skill"
213
+ assert_grep "renamed-new" "$CONSUMER/.agents/.jenga-postinstall-manifest.json" \
214
+ "refreshed manifest lists the new skill"
215
+ assert_nogrep "my-custom-skill" "$CONSUMER/.agents/.jenga-postinstall-manifest.json" \
216
+ "refreshed manifest never records consumer-authored files"
217
+
218
+ # ═══ Scenario C — idempotent re-run of the same version ══════════════════════
219
+ echo
220
+ echo "── Scenario C: re-run same version (version gate) ──"
221
+ run_install "$PKG2" "$CONSUMER" "$FIXTURE/install-v2-again.log"
222
+ assert_grep "Nothing to do" "$FIXTURE/install-v2-again.log" \
223
+ "same-version re-run short-circuits before any delete pass"
224
+ assert_file "$CONSUMER/.agents/skills/my-custom-skill/SKILL.md" \
225
+ "consumer custom skill still present after re-run"
226
+ assert_file "$CONSUMER/.agents/skills/renamed-new/SKILL.md" \
227
+ "package skill still present after re-run"
228
+
229
+ # ═══ Scenario D — adversarial / corrupt manifests ════════════════════════════
230
+ # Exercises the guards directly: a manifest is data on the consumer's disk, so a
231
+ # tampered or corrupted one must never turn into an out-of-bounds or wrong-type
232
+ # delete. Uses a fresh consumer so it cannot disturb the scenarios above.
233
+ echo
234
+ echo "── Scenario D: adversarial + corrupt manifests ──"
235
+
236
+ CONSUMER_D="$FIXTURE/consumer-d"
237
+ mkdir -p "$CONSUMER_D"
238
+ run_install "$PKG1" "$CONSUMER_D" "$FIXTURE/d-install-v1.log"
239
+
240
+ # A file OUTSIDE the mirror roots that a traversal entry would try to reach.
241
+ printf 'precious\n' > "$CONSUMER_D/OUTSIDE-THE-MIRROR.txt"
242
+ # A symlink planted at a path the manifest will claim as ours.
243
+ mkdir -p "$CONSUMER_D/.agents/skills/trap"
244
+ ln -sf "$CONSUMER_D/OUTSIDE-THE-MIRROR.txt" "$CONSUMER_D/.agents/skills/trap/LINK.md"
245
+ # A directory planted where the manifest claims a file.
246
+ mkdir -p "$CONSUMER_D/.agents/skills/trap/DIR.md"
247
+
248
+ # Hand-craft a hostile manifest: traversal escape, symlink, directory, plus a real
249
+ # stale file that SHOULD still be cleaned up (proves the guards are selective, not
250
+ # a blanket bail-out).
251
+ python3 - "$CONSUMER_D" <<'PYEOF'
252
+ import json, sys, os
253
+ root = sys.argv[1]
254
+ m = os.path.join(root, ".agents", ".jenga-postinstall-manifest.json")
255
+ d = json.load(open(m))
256
+ d["paths"] = [
257
+ "../OUTSIDE-THE-MIRROR.txt", # traversal escape -> must be refused
258
+ "skills/trap/LINK.md", # symlink -> must be refused
259
+ "skills/trap/DIR.md", # directory -> must be refused
260
+ "skills/renamed-old/SKILL.md", # genuinely stale -> must be deleted
261
+ ]
262
+ json.dump(d, open(m, "w"), indent=2)
263
+ PYEOF
264
+
265
+ run_install "$PKG2" "$CONSUMER_D" "$FIXTURE/d-install-v2.log"
266
+
267
+ echo
268
+ echo " [guard lines]"
269
+ grep -E "left in place|stale file" "$FIXTURE/d-install-v2.log" | sed 's/^/ /'
270
+ echo
271
+
272
+ assert_file "$CONSUMER_D/OUTSIDE-THE-MIRROR.txt" \
273
+ "traversal entry cannot delete a file outside the mirror root"
274
+ assert_grep "outside-dest-root" "$FIXTURE/d-install-v2.log" \
275
+ "traversal entry explicitly refused"
276
+ if [ -L "$CONSUMER_D/.agents/skills/trap/LINK.md" ]; then
277
+ pass "symlink entry refused, not followed or unlinked"
278
+ else
279
+ fail "symlink entry was removed"
280
+ fi
281
+ assert_grep "not-a-regular-file" "$FIXTURE/d-install-v2.log" \
282
+ "non-regular-file entries explicitly refused"
283
+ if [ -d "$CONSUMER_D/.agents/skills/trap/DIR.md" ]; then
284
+ pass "directory entry refused (manifests record files only)"
285
+ else
286
+ fail "directory entry was removed"
287
+ fi
288
+ assert_absent "$CONSUMER_D/.agents/skills/renamed-old/SKILL.md" \
289
+ "genuinely stale entry still cleaned up alongside refusals"
290
+
291
+ # Corrupt manifest must disable the delete pass entirely, not guess.
292
+ CONSUMER_E="$FIXTURE/consumer-e"
293
+ mkdir -p "$CONSUMER_E"
294
+ run_install "$PKG1" "$CONSUMER_E" "$FIXTURE/e-install-v1.log"
295
+ printf '{ this is not valid json' > "$CONSUMER_E/.agents/.jenga-postinstall-manifest.json"
296
+ printf '{"manifest_version": 99, "paths": ["skills/renamed-old/SKILL.md"]}' \
297
+ > "$CONSUMER_E/.claude/.jenga-postinstall-manifest.json"
298
+ run_install "$PKG2" "$CONSUMER_E" "$FIXTURE/e-install-v2.log"
299
+
300
+ assert_file "$CONSUMER_E/.agents/skills/renamed-old/SKILL.md" \
301
+ "corrupt manifest disables the delete pass (.agents)"
302
+ assert_file "$CONSUMER_E/.claude/skills/renamed-old/SKILL.md" \
303
+ "unknown manifest_version disables the delete pass (.claude)"
304
+ assert_grep "no previous install manifest" "$FIXTURE/e-install-v2.log" \
305
+ "unreadable manifest degrades to additive-only"
306
+ assert_grep "manifest_version" "$CONSUMER_E/.agents/.jenga-postinstall-manifest.json" \
307
+ "a fresh valid manifest is rewritten over the corrupt one"
308
+
309
+ # ═══ Scenario E — case-only rename on a case-insensitive filesystem ══════════
310
+ # Regression cover for tester finding 1 (E26_S08_T01 rapport). Staleness is decided
311
+ # by a case-SENSITIVE string set, but unlinks happen on a filesystem that may be
312
+ # case-INSENSITIVE (macOS APFS, Windows NTFS). After skill.md -> SKILL.md the old
313
+ # manifest string looks stale, yet it resolves to the file this run just wrote --
314
+ # so a string-only diff deletes the new file and the skill vanishes entirely.
315
+ echo
316
+ echo "── Scenario E: case-only rename (finding 1) ──"
317
+
318
+ PKG1C="$FIXTURE/pkg-v1-case"
319
+ PKG2C="$FIXTURE/pkg-v2-case"
320
+ make_pkg "$PKG1C" "1.0.0"
321
+ make_pkg "$PKG2C" "2.0.0"
322
+ mkdir -p "$PKG1C/skills/alpha" "$PKG2C/skills/alpha"
323
+ printf '# alpha\n' > "$PKG1C/skills/alpha/skill.md" # lowercase in v1
324
+ printf '# alpha\n' > "$PKG2C/skills/alpha/SKILL.md" # UPPERCASE in v2
325
+
326
+ CONSUMER_C="$FIXTURE/consumer-case"
327
+ mkdir -p "$CONSUMER_C"
328
+ run_install "$PKG1C" "$CONSUMER_C" "$FIXTURE/case-v1.log"
329
+ run_install "$PKG2C" "$CONSUMER_C" "$FIXTURE/case-v2.log"
330
+
331
+ # Detect whether this filesystem is even case-insensitive; on a case-SENSITIVE fs
332
+ # both names legitimately coexist and the old one is genuinely stale.
333
+ if [ -f "$CONSUMER_C/.agents/skills/alpha/skill.md" ] && \
334
+ [ -f "$CONSUMER_C/.agents/skills/alpha/SKILL.md" ] && \
335
+ [ "$(cat "$CONSUMER_C/.agents/skills/alpha/skill.md" 2>/dev/null)" != "$(cat "$CONSUMER_C/.agents/skills/alpha/SKILL.md" 2>/dev/null)" ]; then
336
+ CASE_INSENSITIVE=0
337
+ else
338
+ CASE_INSENSITIVE=1
339
+ fi
340
+
341
+ if [ "$CASE_INSENSITIVE" -eq 1 ]; then
342
+ # The whole point: the skill must still exist under SOME name after the upgrade.
343
+ if [ -f "$CONSUMER_C/.agents/skills/alpha/SKILL.md" ] || [ -f "$CONSUMER_C/.agents/skills/alpha/skill.md" ]; then
344
+ pass "case-only rename does not delete the just-written file (.agents)"
345
+ else
346
+ fail "case-only rename deleted the skill entirely (.agents)"
347
+ fi
348
+ if [ -f "$CONSUMER_C/.claude/skills/alpha/SKILL.md" ] || [ -f "$CONSUMER_C/.claude/skills/alpha/skill.md" ]; then
349
+ pass "case-only rename does not delete the just-written file (.claude)"
350
+ else
351
+ fail "case-only rename deleted the skill entirely (.claude)"
352
+ fi
353
+ assert_grep "written-this-run" "$FIXTURE/case-v2.log" \
354
+ "identity guard reports the spared entry as written-this-run"
355
+ else
356
+ pass "case-only rename (skipped: filesystem is case-sensitive)"
357
+ pass "case-only rename (skipped: filesystem is case-sensitive) (.claude)"
358
+ pass "identity guard reports the spared entry as written-this-run (n/a on case-sensitive fs)"
359
+ fi
360
+
361
+ # ═══ Scenario F — copySet entry missing from the package ═════════════════════
362
+ # Regression cover for tester finding 2. mirror() silently skips a missing source,
363
+ # so currentPaths under-reports and the delete pass would read the whole mirrored
364
+ # subtree as stale -- converting a bad publish into mass deletion on every consumer.
365
+ echo
366
+ echo "── Scenario F: packaging regression (finding 2) ──"
367
+
368
+ PKG2M="$FIXTURE/pkg-v2-missing"
369
+ make_pkg "$PKG2M" "2.0.0"
370
+ printf '# developer agent v2\n' > "$PKG2M/agents/developer.md"
371
+ rm -rf "$PKG2M/skills" # simulate skills/ omitted from the published package
372
+
373
+ CONSUMER_M="$FIXTURE/consumer-missing"
374
+ mkdir -p "$CONSUMER_M"
375
+ run_install "$PKG1" "$CONSUMER_M" "$FIXTURE/missing-v1.log"
376
+ run_install "$PKG2M" "$CONSUMER_M" "$FIXTURE/missing-v2.log"
377
+
378
+ assert_file "$CONSUMER_M/.agents/skills/do/SKILL.md" \
379
+ "missing copySet entry does not wipe the mirrored subtree (.agents)"
380
+ assert_file "$CONSUMER_M/.claude/skills/do/SKILL.md" \
381
+ "missing copySet entry does not wipe the mirrored subtree (.claude)"
382
+ assert_grep "Upgrade cleanup skipped" "$FIXTURE/missing-v2.log" \
383
+ "packaging regression reported, cleanup skipped"
384
+ assert_grep "skills/renamed-old/SKILL.md" "$CONSUMER_M/.agents/.jenga-postinstall-manifest.json" \
385
+ "previous manifest left intact (still describes what is on disk)"
386
+
387
+ echo
388
+ echo "══════════════════════════════════════════════════════"
389
+ printf ' Result: %d passed, %d failed\n' "$PASS" "$FAIL"
390
+ echo "══════════════════════════════════════════════════════"
391
+ echo
392
+ [ "$FAIL" -eq 0 ]
@@ -1,21 +1,28 @@
1
1
  ---
2
- name: j:brainstorm
3
- description: Engage the scrum-master agent in a focused planning session to define, refine, or challenge features, improvements, tasks, stories, and epics. The agent asks probing questions, challenges assumptions, and helps shape ideas into actionable backlog items.
2
+ name: j.brainstorm
3
+ description: Polyfill alias of the brainstorm skill under a collision-safe directory name. Identical behavior to /brainstorm — Engage the scrum-master agent in a focused planning session to define, refine, or challenge features, improvements, tasks, stories, and epics. The agent asks probing questions, challenges assumptions, and helps shape ideas into actionable backlog items. Use when the bare /brainstorm form is shadowed by another tool's own built-in command of the same name.
4
4
  keywords:
5
5
  - brainstorm
6
6
  - plan
7
7
  - ideate
8
8
  - feature planning
9
9
  - requirements
10
+ - j-brainstorm
11
+ - polyfill
10
12
  examples:
11
13
  - "let's brainstorm ideas for X"
12
14
  - "I need to plan a new feature"
15
+ - "j-brainstorm"
13
16
  metadata:
14
17
  prefered_agent: scrum-master
15
18
  ---
16
19
 
17
20
  # Brainstorm — Collaborative Planning with the Scrum Master
18
21
 
22
+ This skill is a literal-directory-name duplicate of `skills/brainstorm/`. It exists so that `/j-brainstorm` (and `j.j-brainstorm`) give a guaranteed-unshadowed way to reach the same flow as `/brainstorm`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/brainstorm` alias (Claude Code's native skill resolution is a literal-string, directory-name-based match — see `docs/skill-authoring.md`'s "Invocation Convention").
23
+
24
+ This file is generated/synced by `scripts/generate-j-alias.sh brainstorm` from `skills/brainstorm/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
25
+
19
26
  ## Instructions
20
27
 
21
28
  1. **Invoke the `scrum-master` agent** — Pass the user's input directly to it as the opening prompt for the brainstorm session.
@@ -1,21 +1,28 @@
1
1
  ---
2
- name: j:btw
3
- description: Capture a new mission (feature, change, or addition) and fit it into the project's Epic/Story structure, then choose to implement now or defer.
2
+ name: j.btw
3
+ description: Polyfill alias of the btw skill under a collision-safe directory name. Identical behavior to /btw — Capture a new mission (feature, change, or addition) and fit it into the project's Epic/Story structure, then choose to implement now or defer. Use when the bare /btw form is shadowed by another tool's own built-in command of the same name.
4
4
  keywords:
5
5
  - btw
6
6
  - capture
7
7
  - mission
8
8
  - new idea
9
9
  - side task
10
+ - j-btw
11
+ - polyfill
10
12
  examples:
11
13
  - "btw I also need to add X"
12
14
  - "capture this as a new task"
15
+ - "j-btw"
13
16
  metadata:
14
17
  prefered_agent: scrum-master
15
18
  ---
16
19
 
17
20
  # BTW — Capture a Mission
18
21
 
22
+ This skill is a literal-directory-name duplicate of `skills/btw/`. It exists so that `/j-btw` (and `j.j-btw`) give a guaranteed-unshadowed way to reach the same flow as `/btw`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/btw` alias (Claude Code's native skill resolution is a literal-string, directory-name-based match — see `docs/skill-authoring.md`'s "Invocation Convention").
23
+
24
+ This file is generated/synced by `scripts/generate-j-alias.sh btw` from `skills/btw/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
25
+
19
26
  ## Instructions
20
27
 
21
28
  1. **Gather mission details** — Ask the user:
@@ -1,6 +1,6 @@
1
1
  ---
2
- name: j:clearify
3
- description: Clarifies ambiguous, dense, or under-specified prompts and conversation on request — inspects an attached prompt or falls back to the current conversation and surfaces plain-language clarifications with examples.
2
+ name: j.clearify
3
+ description: Polyfill alias of the clearify skill under a collision-safe directory name. Identical behavior to /clearify — Clarifies ambiguous, dense, or under-specified prompts and conversation on request — inspects an attached prompt or falls back to the current conversation and surfaces plain-language clarifications with examples. Use when the bare /clearify form is shadowed by another tool's own built-in command of the same name.
4
4
  keywords:
5
5
  - clarify this
6
6
  - clarify
@@ -9,16 +9,23 @@ keywords:
9
9
  - wtf
10
10
  - explain that again
11
11
  - can you simplify
12
+ - j-clearify
13
+ - polyfill
12
14
  examples:
13
15
  - "clarify this"
14
16
  - "clearify the last message"
15
17
  - "wtf does this mean"
16
18
  - "I don't understand what you're asking me to do here"
19
+ - "j-clearify"
17
20
  alias: wtf
18
21
  ---
19
22
 
20
23
  # Clearify — Ambiguity Clarification
21
24
 
25
+ This skill is a literal-directory-name duplicate of `skills/clearify/`. It exists so that `/j-clearify` (and `j.j-clearify`) give a guaranteed-unshadowed way to reach the same flow as `/clearify`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/clearify` alias (Claude Code's native skill resolution is a literal-string, directory-name-based match — see `docs/skill-authoring.md`'s "Invocation Convention").
26
+
27
+ This file is generated/synced by `scripts/generate-j-alias.sh clearify` from `skills/clearify/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
28
+
22
29
  > **Note on the `alias: wtf` frontmatter field:** this repo has no runtime mechanism that reads an `alias` key to route slash commands — no existing `SKILL.md` implements one, and `project/configs/workflow.json` has no alias registry. The field here documents the intended relationship only. `/wtf` is made invocable as a working alias by the companion skill folder at `skills/wtf/SKILL.md`, which delegates to these same instructions.
23
30
 
24
31
  ## Instructions
@@ -1,20 +1,27 @@
1
1
  ---
2
- name: j:close-story
3
- description: Close a story by verifying all tasks are in terminal state, extracting actual diff stats per task, computing scope divergence flags, and writing closure metadata to task frontmatter.
2
+ name: j.close-story
3
+ description: Polyfill alias of the close-story skill under a collision-safe directory name. Identical behavior to /close-story — Close a story by verifying all tasks are in terminal state, extracting actual diff stats per task, computing scope divergence flags, and writing closure metadata to task frontmatter. Use when the bare /close-story form is shadowed by another tool's own built-in command of the same name.
4
4
  keywords:
5
5
  - close story
6
6
  - close
7
7
  - finish story
8
8
  - complete story
9
9
  - story done
10
+ - j-close-story
11
+ - polyfill
10
12
  examples:
11
13
  - "close story E17_S06"
12
14
  - "/close-story E32_S06"
13
15
  - "mark story E12_S03 as done"
16
+ - "j-close-story"
14
17
  ---
15
18
 
16
19
  # close-story — Close a Story
17
20
 
21
+ This skill is a literal-directory-name duplicate of `skills/close-story/`. It exists so that `/j-close-story` (and `j.j-close-story`) give a guaranteed-unshadowed way to reach the same flow as `/close-story`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/close-story` alias (Claude Code's native skill resolution is a literal-string, directory-name-based match — see `docs/skill-authoring.md`'s "Invocation Convention").
22
+
23
+ This file is generated/synced by `scripts/generate-j-alias.sh close-story` from `skills/close-story/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
24
+
18
25
  ## Purpose
19
26
 
20
27
  `/close-story <story-id>` performs the final closure steps for a story:
@@ -38,7 +45,7 @@ examples:
38
45
  Run the guard script:
39
46
 
40
47
  ```bash
41
- bash skills/close-story/scripts/check-story-closeable.sh <story-id>
48
+ bash skills/j-close-story/scripts/check-story-closeable.sh <story-id>
42
49
  ```
43
50
 
44
51
  - If it exits with code 0 and prints `CLOSEABLE`, continue.
@@ -65,7 +72,7 @@ For **each task ID** listed in the story's `tasks:` frontmatter array:
65
72
 
66
73
  3. **Run per-task diff stat extraction:**
67
74
  ```bash
68
- bash skills/close-story/scripts/extract-task-diff-stats.sh <task-id>
75
+ bash skills/j-close-story/scripts/extract-task-diff-stats.sh <task-id>
69
76
  ```
70
77
  The script outputs:
71
78
  ```
@@ -76,11 +83,11 @@ For **each task ID** listed in the story's `tasks:` frontmatter array:
76
83
 
77
84
  4. **Write stats to task frontmatter:**
78
85
  ```bash
79
- bash skills/close-story/scripts/update-task-frontmatter.sh \
86
+ bash skills/j-close-story/scripts/update-task-frontmatter.sh \
80
87
  "project/board/tasks/<task-id>_*.md" \
81
88
  actual_files_changed <N>
82
89
 
83
- bash skills/close-story/scripts/update-task-frontmatter.sh \
90
+ bash skills/j-close-story/scripts/update-task-frontmatter.sh \
84
91
  "project/board/tasks/<task-id>_*.md" \
85
92
  actual_lines_delta <N>
86
93
  ```
@@ -97,13 +104,13 @@ For **each task ID** listed in the story's `tasks:` frontmatter array:
97
104
 
98
105
  Run the divergence computation:
99
106
  ```bash
100
- DIVERGENCE_FLAG=$(bash skills/close-story/scripts/compute-scope-divergence.sh \
107
+ DIVERGENCE_FLAG=$(bash skills/j-close-story/scripts/compute-scope-divergence.sh \
101
108
  "<execution_scope>" <actual_files_changed> <actual_lines_delta>)
102
109
  ```
103
110
 
104
111
  Write the result to task frontmatter:
105
112
  ```bash
106
- bash skills/close-story/scripts/update-task-frontmatter.sh \
113
+ bash skills/j-close-story/scripts/update-task-frontmatter.sh \
107
114
  "project/board/tasks/<task-id>_*.md" \
108
115
  scope_divergence_flag "$DIVERGENCE_FLAG"
109
116
  ```
@@ -120,7 +127,7 @@ For **each task ID** listed in the story's `tasks:` frontmatter array:
120
127
  frontmatter writes and divergence computation above, check whether this
121
128
  task's own work is permanently excluded from the public mirror:
122
129
  ```bash
123
- bash skills/close-story/scripts/check-privatized.sh <task-id> "project/board/tasks/<task-id>_*.md"
130
+ bash skills/j-close-story/scripts/check-privatized.sh <task-id> "project/board/tasks/<task-id>_*.md"
124
131
  ```
125
132
  This is a **static** check (per `templates/SCRUM_BOARD_SCHEMA.md`'s "Static
126
133
  vs. Reactive Status Setting" section) — it derives the task's touched-file
@@ -170,7 +177,7 @@ against the story's own direct commits (E51_S03_T03) **before** deciding the
170
177
  story's terminal status:
171
178
 
172
179
  ```bash
173
- bash skills/close-story/scripts/check-privatized.sh <story-id> "project/board/stories/<story-id>_*.md"
180
+ bash skills/j-close-story/scripts/check-privatized.sh <story-id> "project/board/stories/<story-id>_*.md"
174
181
  ```
175
182
 
176
183
  Passing the story id (`E##_S##`) rather than a task id makes the script's
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # ---------------------------------------------------------------------------
3
- # skills/close-story/scripts/check-privatized.sh
3
+ # skills/j-close-story/scripts/check-privatized.sh
4
4
  #
5
5
  # Static (run-independent) `.publicignore` membership check for a single
6
6
  # board ticket, invoked by /close-story at both task granularity (Step 2's
@@ -142,7 +142,7 @@ PUBLICIGNORE="$REPO_ROOT/.publicignore"
142
142
  [ -f "$PUBLICIGNORE" ] || die ".publicignore not found at $PUBLICIGNORE"
143
143
 
144
144
  WITH_LOCK_SCRIPT="$REPO_ROOT/scripts/with-lock.sh"
145
- UPDATE_FRONTMATTER_SCRIPT="$REPO_ROOT/skills/close-story/scripts/update-task-frontmatter.sh"
145
+ UPDATE_FRONTMATTER_SCRIPT="$REPO_ROOT/skills/j-close-story/scripts/update-task-frontmatter.sh"
146
146
  EVENTS_FILE="$REPO_ROOT/project/logs/events.json"
147
147
 
148
148
  [ -f "$WITH_LOCK_SCRIPT" ] || die "expected script not found: $WITH_LOCK_SCRIPT"
@@ -22,7 +22,7 @@ usage() {
22
22
  }
23
23
 
24
24
  # Locate the project root relative to this script's location.
25
- # Script lives at: skills/close-story/scripts/check-story-closeable.sh
25
+ # Script lives at: skills/j-close-story/scripts/check-story-closeable.sh
26
26
  # Project root is three levels up (skills/ → repo root).
27
27
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
28
  PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
@@ -12,7 +12,7 @@
12
12
  #
13
13
  # For bundle tasks (a single commit covers multiple task IDs), each task that
14
14
  # matches the commit receives the full bundle stats — this is the "full credit"
15
- # attribution model. See skills/close-story/SKILL.md for the rationale.
15
+ # attribution model. See skills/j-close-story/SKILL.md for the rationale.
16
16
  #
17
17
  # Exit codes:
18
18
  # 0 — success (even when no commits are found; outputs 0 values)
@@ -1,19 +1,26 @@
1
1
  ---
2
- name: j:commit
3
- description: Commit implemented epic, story, or task work using the EST naming convention. Also handles user-action prerequisites and new-epic boundaries. Use after completing any EST work item.
2
+ name: j.commit
3
+ description: Polyfill alias of the commit skill under a collision-safe directory name. Identical behavior to /commit — Commit implemented epic, story, or task work using the EST naming convention. Also handles user-action prerequisites and new-epic boundaries. Use after completing any EST work item. Use when the bare /commit form is shadowed by another tool's own built-in command of the same name.
4
4
  keywords:
5
5
  - commit
6
6
  - save
7
7
  - git commit
8
8
  - done
9
9
  - push
10
+ - j-commit
11
+ - polyfill
10
12
  examples:
11
13
  - "commit this work"
12
14
  - "save my changes"
15
+ - "j-commit"
13
16
  ---
14
17
 
15
18
  # Commit — Commit Completed Work
16
19
 
20
+ This skill is a literal-directory-name duplicate of `skills/commit/`. It exists so that `/j-commit` (and `j.j-commit`) give a guaranteed-unshadowed way to reach the same flow as `/commit`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/commit` alias (Claude Code's native skill resolution is a literal-string, directory-name-based match — see `docs/skill-authoring.md`'s "Invocation Convention").
21
+
22
+ This file is generated/synced by `scripts/generate-j-alias.sh commit` from `skills/commit/SKILL.md` — do not hand-edit it; re-run the generator instead to pick up source changes.
23
+
17
24
  ## Inline Mode (called by /do for inline-scoped tasks)
18
25
 
19
26
  When invoked with the `--inline` flag OR when the environment variable `JENGA_COMMIT_INLINE=1` is set, execute inline mode: