@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
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: j.examplify
3
- description: Polyfill alias of the examplify skill under a collision-safe directory name. Identical behavior to /examplify — Explains concepts, features, use cases, and patterns based on provided context — a description, scenario, code snippet, or file. Use when the user wants to understand what something is, how it works, when to use it, or wants a concrete example. Use when the bare /examplify form is shadowed by another tool's own built-in command of the same name.
3
+ description: Explains concepts, features, use cases, and patterns based on provided context — a description, scenario, code snippet, or file. Use when the user wants to understand what something is, how it works, when to use it, or wants a concrete example.
4
4
  keywords:
5
5
  - examplify
6
6
  - explain
@@ -8,7 +8,6 @@ keywords:
8
8
  - how does
9
9
  - understand
10
10
  - j-examplify
11
- - polyfill
12
11
  examples:
13
12
  - "explain how this works"
14
13
  - "give me an example of X"
@@ -17,9 +16,9 @@ examples:
17
16
 
18
17
  # Concept Explainer
19
18
 
20
- This skill is a literal-directory-name duplicate of `skills/examplify/`. It exists so that `/j-examplify` (and `j.j-examplify`) give a guaranteed-unshadowed way to reach the same flow as `/examplify`, even if a host tool's own built-in command of the same name would otherwise shadow or override the bare `/examplify` 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-examplify/` 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-examplify/` 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 examplify` from `skills/examplify/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/examplify/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
 
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: j.gitignore
3
+ description: Retroactively repair an already-scaffolded project's Jenga gitignore state — strips the stray heredoc `EOF` line left by pre-fix /init scaffolds, adds or removes the Jenga-owned path entries via a managed block, and untracks those paths from git (and from origin) while leaving every file on disk.
4
+ keywords:
5
+ - gitignore
6
+ - fix gitignore
7
+ - untrack jenga files
8
+ - remove from origin
9
+ - stray EOF
10
+ - jenga files committed
11
+ - stop tracking scaffold
12
+ examples:
13
+ - "jenga files keep getting committed"
14
+ - "remove .claude and .agents from the repo but keep them locally"
15
+ - "my .gitignore has a stray EOF line"
16
+ - "untrack the jenga scaffold from origin"
17
+ - "j.gitignore"
18
+ ---
19
+
20
+ # j.gitignore
21
+
22
+ Repairs the Jenga-related git state of a project that was **already scaffolded**. `/init` does this
23
+ correctly for new projects; this skill is the retroactive path for projects created before the fix,
24
+ or projects whose ignore rules have drifted.
25
+
26
+ ## When to use this
27
+
28
+ - `.gitignore` contains a stray `EOF` line (the signature of the botched `cat > .gitignore <<EOF` in
29
+ pre-fix `/init` scaffolds).
30
+ - `.claude/`, `.agents/`, `jenga.config.json`, `.jenga-version` etc. are being committed even though
31
+ you expected Jenga to ignore them.
32
+ - You want the Jenga scaffold gone from the remote, but still working locally.
33
+ - You want to **reverse** any of the above and commit the scaffold deliberately.
34
+
35
+ ## Why /init cannot do this
36
+
37
+ `skills/j-init/SKILL.md` hard-stops on `already-scaffolded`, and for good reason: `init.sh` does an
38
+ unconditional `cp "$ASSETS_DIR/.gitignore_template" .gitignore`, which would clobber the project's
39
+ existing `.gitignore`, and later steps overwrite `PROJECT_SUMMARY.md`, `workflow.json` and
40
+ `test-config.json` with fresh stubs. The upstream fixes (`E31_S07_T02`/`T03`, `E42_S06_T01`) repaired
41
+ the **template**, which is forward-only. Nothing repairs a project already on disk. That is this
42
+ skill's entire reason to exist.
43
+
44
+ `apply-project-visibility.sh` and `apply-scaffold-visibility.sh` remain the **init-time** path and are
45
+ not replaced. They only ever append `project/`, `.claude/` and `.agents/` as loose lines — they do not
46
+ remove a stray `EOF`, do not cover the file-level paths, and cannot untrack anything.
47
+
48
+ ## Scripts
49
+
50
+ All three ship without the executable bit — invoke via `bash`. All are bash 3.2 compatible (macOS).
51
+
52
+ | Script | Role |
53
+ |---|---|
54
+ | `scripts/audit-gitignore.sh` | Read-only. Reports stray terminators, per-path entry/ignored/tracked state, and what is present on origin. Exit `10` means findings, `0` means clean. |
55
+ | `scripts/repair-gitignore.sh` | Strips stray `EOF` lines, then adds (`ignored`) or removes (`visible`) the entries inside a managed block. Atomic write, verified after. |
56
+ | `scripts/untrack-jenga-files.sh` | `git rm -r --cached` — stops tracking, never deletes from disk. Optional `--commit`, opt-in `--push`. |
57
+ | `assets/jenga-paths.txt` | The single catalog of Jenga-owned paths, tiered. All three scripts read it. |
58
+
59
+ ## Path tiers
60
+
61
+ `--tiers` selects what to act on. Default is `scaffold` — the safe set.
62
+
63
+ | Tier | Contents | Default |
64
+ |---|---|---|
65
+ | `scaffold` | `.claude/`, `.agents/`, `.jenga/`, `.jenga-version`, `jenga.config.json`, `.github/hooks/jenga.json`, `J-CLAUDE.md`, `J-AGENTS.md` — entirely Jenga-owned, regenerated by postinstall/`/distribute`. | ✅ on |
66
+ | `hybrid` | `CLAUDE.md`, `AGENTS.md`, `.github/copilot-instructions.md` — Jenga writes a *managed block* and preserves your content around it. Ignoring these hides **your** prose too. | opt-in |
67
+ | `board` | `project/` — governed by `project_files_visibility`. Teams sharing a board want it tracked. | opt-in |
68
+ | `optional` | `.copilot/` — not generated by any current Jenga version. Defensive only. | opt-in |
69
+
70
+ ## Procedure
71
+
72
+ ### 1. Audit
73
+
74
+ ```bash
75
+ bash <skill>/scripts/audit-gitignore.sh . --tiers scaffold
76
+ ```
77
+
78
+ Present the three summary counts to the user. If `AUDIT_RESULT=clean`, say so and stop — there is
79
+ nothing to repair.
80
+
81
+ ### 2. Confirm scope
82
+
83
+ The audit's per-path table shows what is tracked. Before changing anything, ask:
84
+
85
+ Which Jenga paths should this project ignore?
86
+ 1. Scaffold only — .claude/, .agents/, jenga.config.json, .jenga-version, hook + J- files (recommended)
87
+ 2. Scaffold + the board (project/) — adds your scrum board, todo.md, queue, rapports, logs
88
+ 3. Scaffold + CLAUDE.md / AGENTS.md / copilot-instructions.md — note these may contain your own prose
89
+ 4. Everything, including the defensive .copilot/ entry
90
+ 5. Other (describe below)
91
+
92
+ Two warnings to surface if they are in scope:
93
+
94
+ - **`jenga.config.json`** holds `project_files_visibility` and `scaffold_visibility`. Ignoring it
95
+ means a fresh clone loses those settings and the next `/init`-style run re-prompts for them.
96
+ - **`hybrid` tier** files contain user-authored content outside Jenga's managed block. Ignoring them
97
+ removes that content from the repo too.
98
+
99
+ ### 3. Repair the .gitignore
100
+
101
+ Always dry-run first and show the diff:
102
+
103
+ ```bash
104
+ bash <skill>/scripts/repair-gitignore.sh ignored . --tiers <tiers> --dry-run
105
+ ```
106
+
107
+ Then apply without `--dry-run`. This step is purely local and reversible (`visible` mode removes the
108
+ block again), so it does not need separate confirmation beyond the scope choice in step 2.
109
+
110
+ If the user genuinely wants to ignore a file literally named `EOF`, pass `--keep-eof`.
111
+
112
+ ### 4. Untrack
113
+
114
+ `.gitignore` has no effect on files git already tracks, so step 3 alone changes nothing for anything
115
+ already committed. Dry-run first:
116
+
117
+ ```bash
118
+ bash <skill>/scripts/untrack-jenga-files.sh . --tiers <tiers> --dry-run
119
+ ```
120
+
121
+ Then choose how far to go:
122
+
123
+ The .gitignore is fixed. How far should I take the untracking?
124
+ 1. Stage the removals only — I review `git status` and commit myself
125
+ 2. Stage and commit locally — nothing reaches the remote until I push
126
+ 3. Stage, commit, and push — removes them from origin's branch tip
127
+ 4. Stop here — just the .gitignore change
128
+ 5. Other (describe below)
129
+
130
+ Map to `--commit` / `--commit --push` accordingly. **Never pass `--push` without the user explicitly
131
+ choosing option 3** — it is the only outward-facing step and cannot be undone by re-running the skill.
132
+
133
+ ### 5. Report
134
+
135
+ Re-run the audit to confirm `AUDIT_RESULT=clean`, then state plainly what changed, and always
136
+ include the history caveat below.
137
+
138
+ ## What this does NOT do
139
+
140
+ - **It does not rewrite history.** `--push` removes the paths from the branch *tip*. Every earlier
141
+ commit still contains them, and the blobs stay reachable. A fresh clone is clean; `git log` is not.
142
+ - **It is not a secret scrubber.** If a `.env` or credential was committed, treat it as compromised,
143
+ rotate it, and use `git-filter-repo` separately. Say this out loud if the audit shows anything
144
+ credential-shaped.
145
+ - **It never deletes from disk.** Every removal is `git rm --cached`, and the script re-verifies each
146
+ path is still present before reporting success.
147
+
148
+ ## Edge cases
149
+
150
+ | Situation | Behavior |
151
+ |---|---|
152
+ | No `.gitignore` at the root | `ignored` creates one; `visible` exits 0 as a no-op. |
153
+ | No git repo | All three exit 1 with a clear message — this skill needs git. |
154
+ | No upstream branch | Audit says nothing was ever pushed. `--push` refuses rather than guessing a remote; the commit is already safe locally. |
155
+ | Unrelated staged changes | `untrack-jenga-files.sh` refuses (exit 4) so it cannot sweep your work into its commit. A staged `.gitignore` alone is allowed through and folded in, since it is the companion change. |
156
+ | Unterminated managed block | `repair-gitignore.sh` refuses to write (exit 3) rather than risk eating the rest of the file. |
157
+ | Path tracked but absent from disk | Reported as a warning; nothing was lost, git simply tracked something the working tree did not have. |
@@ -0,0 +1,50 @@
1
+ # skills/j-gitignore/assets/jenga-paths.txt
2
+ #
3
+ # The canonical catalog of Jenga-owned paths a consuming project may end up
4
+ # with on disk. Consumed by audit-gitignore.sh, repair-gitignore.sh and
5
+ # untrack-jenga-files.sh — all three read this one file, so the path surface
6
+ # is defined in exactly one place.
7
+ #
8
+ # Format: <tier>TAB<path>TAB<description>
9
+ # Blank lines and lines starting with '#' are ignored.
10
+ #
11
+ # Trailing '/' marks a directory entry and is preserved verbatim into
12
+ # .gitignore (git treats 'foo/' as directory-only, which is what we want).
13
+ #
14
+ # ─── Tiers ───────────────────────────────────────────────────────────────────
15
+ #
16
+ # scaffold Entirely Jenga-owned. Regenerated wholesale by `npm postinstall`
17
+ # or `/distribute`, so nothing of yours is ever lost by ignoring
18
+ # them. Safe default.
19
+ #
20
+ # hybrid A Jenga-managed block inside a file that ALSO holds your own
21
+ # content. lib/generate-agent-context.js and
22
+ # lib/generate-copilot-instructions.js both replace only their
23
+ # managed block and preserve everything outside it. Ignoring these
24
+ # therefore also hides YOUR prose from the repo. Opt-in, never
25
+ # applied without the user saying so.
26
+ #
27
+ # board The project/ working tree (scrum board, todo.md, queue/,
28
+ # rapports/, logs/). Governed by jenga.config.json's
29
+ # `project_files_visibility`. Teams that share a board want this
30
+ # tracked; solo users usually do not. Opt-in.
31
+ #
32
+ # optional Defensive entries for paths no current Jenga version generates.
33
+ # Costs nothing to ignore, guards against a future or third-party
34
+ # agent surface appearing. Opt-in.
35
+ #
36
+ # ─────────────────────────────────────────────────────────────────────────────
37
+
38
+ scaffold .claude/ Claude Code skill/agent mirror — rewritten by every postinstall and /distribute run (scripts/postinstall.js targetRoots). Contains .jenga-postinstall-manifest.json.
39
+ scaffold .agents/ Copilot/Codex skill/agent mirror — the second of postinstall.js's two targetRoots. Contains .jenga-postinstall-manifest.json.
40
+ scaffold .jenga/ `jenga` CLI state directory. lib/commands/init.js already appends this entry itself; listed here so the audit reports it consistently.
41
+ scaffold .jenga-version Installed-version stamp written at consumer root by scripts/postinstall.js.
42
+ scaffold jenga.config.json Jenga project config (project_files_visibility, scaffold_visibility). SEE THE WARNING IN SKILL.md — ignoring this means a fresh clone loses your visibility settings.
43
+ scaffold .github/hooks/jenga.json Copilot CLI hook wiring. "Entirely Jenga-owned output" per scripts/postinstall.js — no user content, unlike copilot-instructions.md.
44
+ scaffold J-CLAUDE.md Collision-case copy written only when you already had your own CLAUDE.md. A J- file can only ever have been created by Jenga.
45
+ scaffold J-AGENTS.md Collision-case copy written only when you already had your own AGENTS.md. Jenga-created by definition.
46
+ hybrid CLAUDE.md Jenga writes a managed block here and preserves content outside it. May contain your own instructions.
47
+ hybrid AGENTS.md Same managed-block treatment as CLAUDE.md, for Codex and generic AGENTS.md consumers.
48
+ hybrid .github/copilot-instructions.md Managed-block bootstrap. postinstall.js explicitly notes this one is NOT entirely Jenga-owned.
49
+ board project/ The whole Jenga working tree. Mirrors what apply-project-visibility.sh's JENGA_WORKING_PATHS appends at init time.
50
+ optional .copilot/ Not generated by any current Jenga version (grepped across lib/, scripts/, skills/). Defensive only.
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env bash
2
+ # -----------------------------------------------------------------------------
3
+ # skills/j-gitignore/scripts/_catalog.sh
4
+ #
5
+ # Shared helpers for the j-gitignore scripts. Sourced, never run directly:
6
+ #
7
+ # source "$(dirname "${BASH_SOURCE[0]}")/_catalog.sh"
8
+ #
9
+ # Provides:
10
+ # CATALOG_FILE absolute path to assets/jenga-paths.txt
11
+ # MANAGED_BEGIN/_END the managed-block markers
12
+ # load_catalog <tiers> populates CATALOG_PATHS/_TIERS/_DESCS arrays
13
+ # validate_tiers <csv> rejects an unknown tier name
14
+ # resolve_root <dir> cd's to a project root, verifying it is a git repo
15
+ #
16
+ # The leading underscore marks this as internal to the skill — it is not an
17
+ # entry point and takes no arguments of its own.
18
+ # -----------------------------------------------------------------------------
19
+
20
+ # Deliberately no `set -e` — callers handle their own errors, matching the
21
+ # convention in skills/j-init/scripts/apply-scaffold-visibility.sh.
22
+ set -uo pipefail
23
+
24
+ _CATALOG_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+ CATALOG_FILE="${_CATALOG_LIB_DIR}/../assets/jenga-paths.txt"
26
+
27
+ MANAGED_BEGIN="# >>> jenga:gitignore >>>"
28
+ MANAGED_END="# <<< jenga:gitignore <<<"
29
+
30
+ VALID_TIERS="scaffold hybrid board optional"
31
+
32
+ # Populated by load_catalog.
33
+ CATALOG_PATHS=()
34
+ CATALOG_TIERS=()
35
+ CATALOG_DESCS=()
36
+
37
+ validate_tiers() {
38
+ local csv="$1" tier valid found
39
+ [ -n "$csv" ] || { echo "ERROR: --tiers was given an empty value." >&2; return 2; }
40
+ # Split on commas into an array FIRST, then restore IFS. Setting IFS=',' for
41
+ # the duration of both loops would stop the space-separated $VALID_TIERS from
42
+ # splitting, making every tier look invalid.
43
+ local -a requested=()
44
+ local old_ifs="$IFS"
45
+ IFS=','; read -r -a requested <<< "$csv"; IFS="$old_ifs"
46
+ for tier in "${requested[@]}"; do
47
+ found=0
48
+ for valid in $VALID_TIERS; do
49
+ [ "$tier" = "$valid" ] && found=1 && break
50
+ done
51
+ if [ "$found" -eq 0 ]; then
52
+ echo "ERROR: Unknown tier '${tier}'. Valid tiers: ${VALID_TIERS// /, }" >&2
53
+ return 2
54
+ fi
55
+ done
56
+ return 0
57
+ }
58
+
59
+ # load_catalog <comma-separated-tiers>
60
+ # Reads CATALOG_FILE, keeping only rows whose tier is in the requested set.
61
+ load_catalog() {
62
+ local want="$1"
63
+ CATALOG_PATHS=(); CATALOG_TIERS=(); CATALOG_DESCS=()
64
+
65
+ if [ ! -f "$CATALOG_FILE" ]; then
66
+ echo "ERROR: Path catalog not found: $CATALOG_FILE" >&2
67
+ return 1
68
+ fi
69
+
70
+ local tier path desc
71
+ while IFS=$'\t' read -r tier path desc || [ -n "${tier:-}" ]; do
72
+ [ -z "${tier// /}" ] && continue
73
+ case "$tier" in \#*) continue ;; esac
74
+ [ -n "${path:-}" ] || continue
75
+ case ",${want}," in
76
+ *",${tier},"*)
77
+ CATALOG_PATHS+=("$path")
78
+ CATALOG_TIERS+=("$tier")
79
+ CATALOG_DESCS+=("${desc:-}")
80
+ ;;
81
+ esac
82
+ done < "$CATALOG_FILE"
83
+
84
+ if [ "${#CATALOG_PATHS[@]}" -eq 0 ]; then
85
+ echo "ERROR: No catalog entries matched tiers '${want}'." >&2
86
+ return 1
87
+ fi
88
+ return 0
89
+ }
90
+
91
+ # resolve_root <dir> — cd into it and confirm it is the root of a git work tree.
92
+ resolve_root() {
93
+ local root="$1"
94
+ if [ ! -d "$root" ]; then
95
+ echo "ERROR: Project root does not exist: $root" >&2
96
+ return 1
97
+ fi
98
+ cd "$root" || { echo "ERROR: Cannot enter project root: $root" >&2; return 1; }
99
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
100
+ echo "ERROR: Not a git repository: $root" >&2
101
+ echo " This skill repairs a project's tracked/ignored state, so it needs git." >&2
102
+ return 1
103
+ fi
104
+ return 0
105
+ }
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env bash
2
+ # -----------------------------------------------------------------------------
3
+ # skills/j-gitignore/scripts/audit-gitignore.sh
4
+ #
5
+ # Read-only. Reports the Jenga-related git state of an already-scaffolded
6
+ # project. Changes nothing — this is the "check" half of the skill, safe to run
7
+ # anywhere, any number of times.
8
+ #
9
+ # Reports three independent things per catalog path:
10
+ #
11
+ # entry Is the exact entry literally present in .gitignore?
12
+ # ignored Does git actually ignore the path (`git check-ignore`)? This can
13
+ # be true via a broader rule even when `entry` is no — and is
14
+ # ALWAYS reported as "no" for a tracked path, because .gitignore
15
+ # has no effect on a file git already tracks. That divergence is
16
+ # the single most common cause of "I ignored it and it still keeps
17
+ # getting committed".
18
+ # tracked Does git currently track anything under the path?
19
+ #
20
+ # Plus the stray-heredoc-terminator check: a bare `EOF` line left behind by the
21
+ # botched `cat > .gitignore <<EOF` in pre-fix /init scaffolds (fixed for NEW
22
+ # projects by E31_S07_T02/T03 and E42_S06_T01, which replaced the heredoc with
23
+ # a `cp` from assets/.gitignore_template — forward-only, so already-scaffolded
24
+ # projects still carry the damage).
25
+ #
26
+ # Invoked via `bash`, not executed directly — ships without the executable bit,
27
+ # same convention as skills/j-cloud-connect/scripts/install-rclone.sh.
28
+ #
29
+ # Usage:
30
+ # audit-gitignore.sh [project_root] [--tiers scaffold,hybrid,board,optional]
31
+ #
32
+ # Exit codes:
33
+ # 0 Audit ran; nothing to fix
34
+ # 1 Bad usage or missing prerequisite (not a git repo, catalog missing)
35
+ # 2 Invalid tier name
36
+ # 10 Audit ran; findings exist (stray terminators, missing entries, or
37
+ # tracked paths). Not an error — this is the signal to run the repair.
38
+ # -----------------------------------------------------------------------------
39
+
40
+ set -uo pipefail
41
+ source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_catalog.sh"
42
+
43
+ PROJECT_ROOT=""
44
+ TIERS="scaffold"
45
+
46
+ while [ $# -gt 0 ]; do
47
+ case "$1" in
48
+ --tiers) TIERS="${2:-}"; shift 2 ;;
49
+ --tiers=*) TIERS="${1#*=}"; shift ;;
50
+ -h|--help)
51
+ echo "Usage: $(basename "$0") [project_root] [--tiers scaffold,hybrid,board,optional]"
52
+ exit 0 ;;
53
+ -*)
54
+ echo "ERROR: Unknown option: $1" >&2
55
+ echo "Usage: $(basename "$0") [project_root] [--tiers <csv>]" >&2
56
+ exit 1 ;;
57
+ *)
58
+ if [ -n "$PROJECT_ROOT" ]; then
59
+ echo "ERROR: More than one project root given: '$PROJECT_ROOT' and '$1'" >&2
60
+ exit 1
61
+ fi
62
+ PROJECT_ROOT="$1"; shift ;;
63
+ esac
64
+ done
65
+
66
+ PROJECT_ROOT="${PROJECT_ROOT:-$PWD}"
67
+ validate_tiers "$TIERS" || exit 2
68
+ resolve_root "$PROJECT_ROOT" || exit 1
69
+ load_catalog "$TIERS" || exit 1
70
+
71
+ GITIGNORE=".gitignore"
72
+ FINDINGS=0
73
+
74
+ echo "═══════════════════════════════════════════════════════════════════════"
75
+ echo " Jenga gitignore audit — $(pwd)"
76
+ echo " Tiers: ${TIERS}"
77
+ echo "═══════════════════════════════════════════════════════════════════════"
78
+ echo
79
+
80
+ # ─── 1. Stray heredoc terminators ───────────────────────────────────────────
81
+ echo "── 1. Stray heredoc terminators ───────────────────────────────────────"
82
+ STRAY_COUNT=0
83
+ if [ ! -f "$GITIGNORE" ]; then
84
+ echo " No .gitignore at this root — nothing to check."
85
+ else
86
+ # A line that is exactly EOF (tolerating trailing whitespace). Reported with
87
+ # line numbers so a human can eyeball it before anything is removed.
88
+ while IFS= read -r hit; do
89
+ [ -z "$hit" ] && continue
90
+ echo " ✗ line ${hit%%:*}: stray '${hit#*:}' terminator"
91
+ STRAY_COUNT=$((STRAY_COUNT + 1))
92
+ done < <(grep -nE '^[[:space:]]*EOF[[:space:]]*$' "$GITIGNORE" 2>/dev/null)
93
+
94
+ if [ "$STRAY_COUNT" -eq 0 ]; then
95
+ echo " ✓ Clean — no stray terminator lines."
96
+ else
97
+ echo
98
+ echo " → ${STRAY_COUNT} stray line(s). repair-gitignore.sh removes these."
99
+ FINDINGS=$((FINDINGS + STRAY_COUNT))
100
+ fi
101
+ fi
102
+ echo
103
+
104
+ # ─── 2. Managed block ────────────────────────────────────────────────────────
105
+ echo "── 2. Managed block ───────────────────────────────────────────────────"
106
+ if [ -f "$GITIGNORE" ] && grep -qxF -- "$MANAGED_BEGIN" "$GITIGNORE"; then
107
+ echo " ✓ Present — this project has been through repair-gitignore.sh before."
108
+ else
109
+ echo " · Absent — entries (if any) are loose, e.g. appended by /init's"
110
+ echo " apply-project-visibility.sh / apply-scaffold-visibility.sh."
111
+ fi
112
+ echo
113
+
114
+ # ─── 3. Per-path state ───────────────────────────────────────────────────────
115
+ echo "── 3. Per-path state ──────────────────────────────────────────────────"
116
+ printf " %-34s %-7s %-8s %-8s %s\n" "PATH" "ENTRY" "IGNORED" "TRACKED" "TIER"
117
+ printf " %-34s %-7s %-8s %-8s %s\n" "----" "-----" "-------" "-------" "----"
118
+
119
+ MISSING_ENTRIES=0
120
+ TRACKED_PATHS=()
121
+
122
+ for i in "${!CATALOG_PATHS[@]}"; do
123
+ path="${CATALOG_PATHS[$i]}"
124
+ tier="${CATALOG_TIERS[$i]}"
125
+
126
+ entry="no"
127
+ if [ -f "$GITIGNORE" ] && grep -qxF -- "$path" "$GITIGNORE"; then
128
+ entry="yes"
129
+ fi
130
+
131
+ # check-ignore works on paths that do not exist on disk, so a not-yet-created
132
+ # scaffold dir is still evaluated against the rules.
133
+ ignored="no"
134
+ if git check-ignore -q -- "$path" 2>/dev/null; then
135
+ ignored="yes"
136
+ fi
137
+
138
+ tracked="no"
139
+ if [ -n "$(git ls-files -- "$path" 2>/dev/null | head -n 1)" ]; then
140
+ tracked="yes"
141
+ TRACKED_PATHS+=("$path")
142
+ fi
143
+
144
+ flag=""
145
+ if [ "$entry" = "no" ]; then flag=" ← no entry"; MISSING_ENTRIES=$((MISSING_ENTRIES + 1)); fi
146
+ if [ "$tracked" = "yes" ]; then flag="${flag} ← TRACKED"; fi
147
+
148
+ printf " %-34s %-7s %-8s %-8s %s%s\n" "$path" "$entry" "$ignored" "$tracked" "$tier" "$flag"
149
+ done
150
+ echo
151
+
152
+ # ─── 4. Origin state ─────────────────────────────────────────────────────────
153
+ echo "── 4. Origin ──────────────────────────────────────────────────────────"
154
+ BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)"
155
+ UPSTREAM="$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null)"
156
+
157
+ if [ -z "$UPSTREAM" ]; then
158
+ echo " · Branch '${BRANCH}' has no upstream — nothing has been pushed from it."
159
+ echo " Untracking here only ever affects local history."
160
+ else
161
+ echo " Upstream: ${UPSTREAM}"
162
+ ON_ORIGIN=0
163
+ for path in "${TRACKED_PATHS[@]:-}"; do
164
+ [ -z "$path" ] && continue
165
+ if [ -n "$(git ls-tree -r --name-only "$UPSTREAM" -- "$path" 2>/dev/null | head -n 1)" ]; then
166
+ echo " ✗ ${path} — present on ${UPSTREAM}"
167
+ ON_ORIGIN=$((ON_ORIGIN + 1))
168
+ fi
169
+ done
170
+ if [ "$ON_ORIGIN" -eq 0 ]; then
171
+ echo " ✓ No catalog path is present on ${UPSTREAM}."
172
+ else
173
+ echo
174
+ echo " → ${ON_ORIGIN} path(s) live on the remote. untrack-jenga-files.sh"
175
+ echo " --commit --push removes them from the branch TIP. It does NOT"
176
+ echo " rewrite history — earlier commits still contain them."
177
+ fi
178
+ fi
179
+ echo
180
+
181
+ # ─── Summary ─────────────────────────────────────────────────────────────────
182
+ FINDINGS=$((FINDINGS + MISSING_ENTRIES + ${#TRACKED_PATHS[@]}))
183
+ echo "═══════════════════════════════════════════════════════════════════════"
184
+ echo " Stray terminators : ${STRAY_COUNT}"
185
+ echo " Missing entries : ${MISSING_ENTRIES}"
186
+ echo " Tracked paths : ${#TRACKED_PATHS[@]}"
187
+ echo "═══════════════════════════════════════════════════════════════════════"
188
+
189
+ if [ "$FINDINGS" -gt 0 ]; then
190
+ echo "AUDIT_RESULT=findings"
191
+ exit 10
192
+ fi
193
+ echo "AUDIT_RESULT=clean"
194
+ exit 0