@jenga-ai/agent 3.2.0 → 3.5.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 (68) hide show
  1. package/README.md +52 -12
  2. package/agents/developer.md +16 -1
  3. package/agents/scrum-master.md +1 -0
  4. package/bin/jenga.js +10 -0
  5. package/lib/commands/dashboard.js +92 -0
  6. package/lib/skill-allow-list.json +6 -2
  7. package/package.json +21 -2
  8. package/project/app/api/lib/resolve-project-root.js +120 -0
  9. package/project/app/api/package.json +16 -0
  10. package/project/app/api/parsers/architecture.js +72 -0
  11. package/project/app/api/parsers/board.js +141 -0
  12. package/project/app/api/parsers/documentation.js +125 -0
  13. package/project/app/api/parsers/git-log.js +52 -0
  14. package/project/app/api/parsers/ideas.js +62 -0
  15. package/project/app/api/parsers/knowledge-graph.js +73 -0
  16. package/project/app/api/parsers/lib/markdown-dir-reader.js +163 -0
  17. package/project/app/api/parsers/rapports.js +148 -0
  18. package/project/app/api/parsers/todo.js +179 -0
  19. package/project/app/api/response.js +47 -0
  20. package/project/app/api/routes/architecture.js +23 -0
  21. package/project/app/api/routes/board.js +46 -0
  22. package/project/app/api/routes/documentation.js +24 -0
  23. package/project/app/api/routes/health.js +25 -0
  24. package/project/app/api/routes/history.js +55 -0
  25. package/project/app/api/routes/rapports.js +24 -0
  26. package/project/app/api/scripts/capture-snapshot.js +294 -0
  27. package/project/app/api/server.js +112 -0
  28. package/project/app/api/types.js +40 -0
  29. package/project/app/package.json +21 -0
  30. package/project/app/ui/dist/assets/index-7fj-vllY.js +104 -0
  31. package/project/app/ui/dist/assets/index-CdK3Qrep.css +1 -0
  32. package/project/app/ui/dist/index.html +13 -0
  33. package/project/app/ui/package.json +23 -0
  34. package/project/app/ui/scripts/build-snapshot-html.cjs +214 -0
  35. package/project/app/ui/scripts/dashboard-open.cjs +88 -0
  36. package/project/app/ui/scripts/dashboard-start.cjs +87 -0
  37. package/scripts/acquire-concurrency-slot.sh +220 -0
  38. package/scripts/compute-deploy-reconcile.sh +439 -0
  39. package/scripts/jenga-permission-level-switch.sh +19 -3
  40. package/scripts/mark-deployed.sh +532 -0
  41. package/scripts/populate-knowledge-graph.js +429 -0
  42. package/scripts/release-concurrency-slot.sh +129 -0
  43. package/scripts/validate-board.sh +60 -2
  44. package/scripts/verify-consumer-install.sh +470 -0
  45. package/skills/j-cloud-connect/SKILL.md +95 -0
  46. package/skills/j-cloud-connect/scripts/configure-backend.sh +267 -0
  47. package/skills/j-cloud-connect/scripts/install-rclone.sh +153 -0
  48. package/skills/j-dashboard/SKILL.md +144 -0
  49. package/skills/j-dashboard/scripts/launch.sh +121 -0
  50. package/skills/j-dashboard/scripts/resolve-app-dir.sh +164 -0
  51. package/skills/j-dashboard/scripts/snapshot.sh +267 -0
  52. package/skills/j-dashboard-share/SKILL.md +96 -0
  53. package/skills/j-dashboard-share/scripts/upload-snapshot.sh +173 -0
  54. package/skills/j-init/SKILL.md +52 -13
  55. package/skills/j-init/assets/.gitignore_template +1 -2
  56. package/skills/j-init/scripts/apply-scaffold-visibility.sh +192 -0
  57. package/skills/j-init/scripts/init.sh +19 -5
  58. package/skills/j-playbook/SKILL.md +12 -0
  59. package/skills/j-playbook-new/SKILL.md +155 -0
  60. package/skills/j-playbook-new/scripts/playbook-new.sh +332 -0
  61. package/skills/j-publish/scripts/npm_ci_pipeline.sh +6 -0
  62. package/skills/j-skillify/assets/init-new/assets/.gitignore_template +1 -2
  63. package/skills/j-uncharted/SKILL.md +54 -7
  64. package/skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md +69 -0
  65. package/skills/j-uncharted/scripts/elicitation-state.sh +45 -7
  66. package/skills/jenga/scripts/load-nl-catalog.js +22 -6
  67. package/skills/jenga/scripts/load-playbooks.sh +146 -35
  68. package/templates/SCRUM_BOARD_SCHEMA.md +18 -6
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env bash
2
+ # -----------------------------------------------------------------------------
3
+ # skills/j-dashboard-share/scripts/upload-snapshot.sh
4
+ #
5
+ # Uploads a local dashboard snapshot HTML file (as produced by j-dashboard's
6
+ # `scripts/snapshot.sh`) to a configured rclone remote via `rclone copyto`, at
7
+ # a templated, Drive-side (remote-side) destination path:
8
+ #
9
+ # JengaAI/<repo-directory-name>/<datetime>-board-snapshot.html
10
+ #
11
+ # - <repo-directory-name> is the basename of the repository root, derived
12
+ # via `basename "$(git rev-parse --show-toplevel)"` — never hardcoded to
13
+ # any specific project.
14
+ # - <datetime> is the actual UTC run time, formatted `%Y%m%dT%H%M%SZ`
15
+ # (compact ISO 8601-ish, sortable, filesystem/URL-safe).
16
+ #
17
+ # This destination path is entirely independent of the LOCAL snapshot
18
+ # filename (whatever `snapshot.sh --out` wrote it as) — this script only
19
+ # reads the local file's bytes and uploads them under its own remote-side
20
+ # name.
21
+ #
22
+ # Before attempting any upload, this script verifies the target rclone
23
+ # remote is actually configured (present in `rclone listremotes`). If it is
24
+ # not, this script does NOT attempt the upload and does NOT let rclone
25
+ # surface its own raw error — it dies with an actionable message pointing
26
+ # the user at `j.cloud-connect` (E60_S01) to configure a remote first.
27
+ #
28
+ # HARD SCOPE BOUNDARY: this script uploads ONLY. It never invokes
29
+ # `rclone link` or any other link-creating/sharing subcommand, anywhere in
30
+ # its logic — that is a deliberate, separate, manual action reserved for the
31
+ # user (see E47_S05's story decision). Do not add one.
32
+ #
33
+ # This script does not create or wire up the `j-dashboard-share` skill
34
+ # itself (SKILL.md) — that is a separate task (E47_S05_T02). It assumes a
35
+ # remote has already been configured via `j.cloud-connect`
36
+ # (skills/j-cloud-connect/scripts/{install-rclone.sh,configure-backend.sh},
37
+ # E60_S01) and that `rclone` is already on PATH.
38
+ #
39
+ # Invoked via `bash`, not executed directly: this script intentionally ships
40
+ # without the executable bit, matching the convention already documented in
41
+ # skills/j-cloud-connect/scripts/install-rclone.sh and
42
+ # skills/j-dashboard/scripts/resolve-app-dir.sh. Whatever wires this into the
43
+ # j-dashboard-share skill (E47_S05_T02) should invoke it as
44
+ # `bash skills/j-dashboard-share/scripts/upload-snapshot.sh`.
45
+ #
46
+ # Usage:
47
+ # upload-snapshot.sh --file <local-snapshot-path> --remote <remote-name>
48
+ #
49
+ # --file <path> Path to the local snapshot HTML file to upload (e.g.
50
+ # the output of `j.dashboard --snapshot`). Must exist
51
+ # and be non-empty.
52
+ # --remote <name> Name of an already-configured rclone remote (without a
53
+ # trailing colon), e.g. `gdrive`. Checked against
54
+ # `rclone listremotes` before any upload is attempted.
55
+ #
56
+ # Exit codes:
57
+ # 0 Upload succeeded. The full remote destination path is printed on
58
+ # stdout as the last line.
59
+ # 1 Any failure: bad arguments, missing/empty local file, `rclone` not on
60
+ # PATH, not inside a git repository, the target remote is not
61
+ # configured (points the user at `j.cloud-connect`), or `rclone copyto`
62
+ # itself failed. A human-readable reason is always printed to stderr.
63
+ # -----------------------------------------------------------------------------
64
+
65
+ set -euo pipefail
66
+
67
+ usage() {
68
+ cat <<'EOF'
69
+ Usage: upload-snapshot.sh --file <local-snapshot-path> --remote <remote-name>
70
+
71
+ Uploads a local dashboard snapshot HTML file to a configured rclone remote
72
+ via `rclone copyto`, at the templated destination path:
73
+
74
+ JengaAI/<repo-directory-name>/<datetime>-board-snapshot.html
75
+
76
+ --file <path> Path to the local snapshot HTML file to upload. Must
77
+ exist and be non-empty.
78
+ --remote <name> Name of an already-configured rclone remote (no
79
+ trailing colon). If not configured, this script tells
80
+ you to run j.cloud-connect instead of attempting the
81
+ upload.
82
+
83
+ Upload only — never runs `rclone link` or any other sharing/link-creating
84
+ command.
85
+ EOF
86
+ }
87
+
88
+ die() {
89
+ echo "Error: $*" >&2
90
+ exit 1
91
+ }
92
+
93
+ # -----------------------------------------------------------------------------
94
+ # Argument parsing.
95
+ # -----------------------------------------------------------------------------
96
+
97
+ LOCAL_FILE=""
98
+ REMOTE_NAME=""
99
+
100
+ while [ "$#" -gt 0 ]; do
101
+ case "$1" in
102
+ -h|--help)
103
+ usage
104
+ exit 0
105
+ ;;
106
+ --file)
107
+ [ "$#" -ge 2 ] || die "--file requires a value"
108
+ LOCAL_FILE="$2"
109
+ shift 2
110
+ ;;
111
+ --remote)
112
+ [ "$#" -ge 2 ] || die "--remote requires a value"
113
+ REMOTE_NAME="$2"
114
+ shift 2
115
+ ;;
116
+ *)
117
+ usage >&2
118
+ die "unknown argument: $1"
119
+ ;;
120
+ esac
121
+ done
122
+
123
+ [ -n "$LOCAL_FILE" ] || { usage >&2; die "--file is required"; }
124
+ [ -n "$REMOTE_NAME" ] || { usage >&2; die "--remote is required"; }
125
+
126
+ # -----------------------------------------------------------------------------
127
+ # Preflight — local file, rclone availability, git repository.
128
+ # -----------------------------------------------------------------------------
129
+
130
+ [ -f "$LOCAL_FILE" ] || die "local snapshot file not found: $LOCAL_FILE"
131
+ [ -s "$LOCAL_FILE" ] || die "local snapshot file is empty: $LOCAL_FILE"
132
+
133
+ command -v rclone >/dev/null 2>&1 || die "rclone is not on PATH. Install it first (see skills/j-cloud-connect/scripts/install-rclone.sh) and re-run."
134
+
135
+ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || die "not inside a git repository — cannot derive <repo-directory-name> for the destination path. Run this script from within the project's repository."
136
+ REPO_DIR_NAME="$(basename "$REPO_ROOT")"
137
+
138
+ # -----------------------------------------------------------------------------
139
+ # Step 1 — Verify the target remote is actually configured BEFORE attempting
140
+ # any upload. Never let a raw rclone error surface for "remote not
141
+ # configured" — point the user at j.cloud-connect instead.
142
+ # -----------------------------------------------------------------------------
143
+
144
+ REMOTES_OUTPUT="$(rclone listremotes 2>&1)" || die "'rclone listremotes' failed: $REMOTES_OUTPUT"
145
+
146
+ if ! printf '%s\n' "$REMOTES_OUTPUT" | grep -Fxq "${REMOTE_NAME}:"; then
147
+ die "remote '$REMOTE_NAME' is not configured in rclone. Run j.cloud-connect first to set it up, then re-run this upload."
148
+ fi
149
+
150
+ # -----------------------------------------------------------------------------
151
+ # Step 2 — Build the templated destination path.
152
+ # -----------------------------------------------------------------------------
153
+
154
+ DATETIME="$(date -u +%Y%m%dT%H%M%SZ)"
155
+ DEST_RELATIVE_PATH="JengaAI/${REPO_DIR_NAME}/${DATETIME}-board-snapshot.html"
156
+ DEST="${REMOTE_NAME}:${DEST_RELATIVE_PATH}"
157
+
158
+ # -----------------------------------------------------------------------------
159
+ # Step 3 — Upload. stdio inherited (no capture/reformatting), matching this
160
+ # repo's j-cloud-connect script conventions. Upload only: this script never
161
+ # calls `rclone link` or any other sharing/link-creating command.
162
+ # -----------------------------------------------------------------------------
163
+
164
+ echo "Uploading '$LOCAL_FILE' to '$DEST'..."
165
+
166
+ COPY_EXIT=0
167
+ rclone copyto "$LOCAL_FILE" "$DEST" || COPY_EXIT=$?
168
+ if [ "$COPY_EXIT" -ne 0 ]; then
169
+ die "'rclone copyto $LOCAL_FILE $DEST' exited with status $COPY_EXIT. Upload did not succeed — see rclone's output above for the reason."
170
+ fi
171
+
172
+ echo "Upload succeeded."
173
+ echo "$DEST"
@@ -104,10 +104,41 @@ run can never silently relocate directories or edit `.gitignore`.
104
104
  > `skills/distribute/CONFIG_SCHEMA.md` for the root-cause note and the tracked
105
105
  > follow-up to reintroduce it once fixed.
106
106
 
107
- Carry the chosen value into step 3. Do not apply it yourself — the script owns all
107
+ Carry the chosen value into step 4. Do not apply it yourself — the script owns all
108
108
  of the mechanical work.
109
109
 
110
- ### 3. Run the scaffold script
110
+ ### 3. Ask whether the distributed scaffold should be committed
111
+
112
+ This is a **distinct** question from step 2 — `project_files_visibility` (step 2)
113
+ covers only the `project/` working tree (the scrum board, `todo.md`, `queue/`,
114
+ `rapports/`, `logs/`). It has no effect on `.claude/`/`.agents/`, the distributed
115
+ framework scaffold (skill and agent definitions), which is a separate tree with a
116
+ separate lifecycle — it gets overwritten wholesale by every `/distribute` run or npm
117
+ upgrade, unlike `project/`. Kept as its own flag (`scaffold_visibility`) rather than
118
+ folded into `project_files_visibility`'s enum; see
119
+ `skills/distribute/CONFIG_SCHEMA.md`'s "Scaffold visibility" section for the full
120
+ rationale.
121
+
122
+ Ask the user this question, verbatim, before running any script:
123
+
124
+ Should the distributed .claude/.agents Jenga AI framework scaffold (skills, agent
125
+ definitions — implementation detail, not your own code) be committed into this
126
+ project's git history?
127
+ 1. Yes, commit it — keep it tracked and visible, exactly like today
128
+ 2. No, keep it on disk but add it to .gitignore so it's never committed
129
+ 3. Not sure — explain the trade-offs and ask me again
130
+
131
+ If the user picks option 3, explain the trade-offs and re-ask. Do not proceed until
132
+ the answer maps to one of `visible` (option 1) or `ignored` (option 2).
133
+
134
+ If the run is non-interactive (no user available to answer), use the default:
135
+ **`visible`**. It is the only choice that changes nothing on disk, so an unattended
136
+ run reproduces exactly today's behavior — the scaffold is still committed by default.
137
+
138
+ Carry the chosen value into step 4 as well. Do not apply it yourself — the script
139
+ owns all of the mechanical work.
140
+
141
+ ### 4. Run the scaffold script
111
142
 
112
143
  `init.sh` is not guaranteed to live at a single fixed path: in a project that
113
144
  installed Jenga via npm, it was mirrored to `.claude/skills/j-init/scripts/`
@@ -117,7 +148,7 @@ source checkout, where it lives at the bare `skills/j-init/scripts/` path
117
148
  instead. This step runs before `CLAUDE.md`/`AGENTS.md` exist, so it cannot
118
149
  rely on either file's routing instructions to resolve the path — it must
119
150
  locate its own script directly. Execute the init script from the project
120
- root, passing the choice from step 2:
151
+ root, passing the choices from steps 2 and 3:
121
152
 
122
153
  ```bash
123
154
  INIT_SCRIPT=""
@@ -128,11 +159,14 @@ if [[ -z "$INIT_SCRIPT" ]]; then
128
159
  echo "Error: could not locate init.sh under .claude/skills/, .agents/skills/, or skills/" >&2
129
160
  exit 1
130
161
  fi
131
- chmod +x "$INIT_SCRIPT" && "$INIT_SCRIPT" --visibility <visible|ignored>
162
+ chmod +x "$INIT_SCRIPT" && "$INIT_SCRIPT" --visibility <visible|ignored> --scaffold-visibility <visible|ignored>
132
163
  ```
133
164
 
134
165
  Omitting `--visibility` falls back to the `JENGA_PROJECT_FILES_VISIBILITY`
135
- environment variable, then to `visible`.
166
+ environment variable, then to `visible`. Omitting `--scaffold-visibility` falls
167
+ back to the `JENGA_SCAFFOLD_VISIBILITY` environment variable, then to `visible`
168
+ — the same default-preserving fallback chain, applied independently to the
169
+ distributed-scaffold question from step 3.
136
170
 
137
171
  This script handles all scaffolding in one step:
138
172
  1. Initializes the git repository
@@ -148,22 +182,27 @@ This script handles all scaffolding in one step:
148
182
  11. Creates `docs/STRATEGY.md` — a strategic brief stub intended for investors, partners, and the product team
149
183
  12. Creates `CHANGELOG.md` from the shared template — a running log of notable changes, seeded with an `[Unreleased]` section
150
184
  13. Applies the chosen visibility mode via `scripts/apply-project-visibility.sh`, which records it as `project_files_visibility` in `jenga.config.json` and performs any `.gitignore` change
151
- 14. Stages and commits all files with the message `init: scaffold project structure and workflow config`
185
+ 14. Applies the chosen scaffold visibility mode via `scripts/apply-scaffold-visibility.sh`, which records it as `scaffold_visibility` in `jenga.config.json` and, when `ignored`, adds `.claude/` and `.agents/` to `.gitignore`
186
+ 15. Stages and commits all files with the message `init: scaffold project structure and workflow config`
152
187
 
153
- The visibility mode is validated before any scaffolding happens, so an invalid
154
- value fails fast and leaves nothing behind. It is applied before the commit, so
155
- the `.gitignore` entry is captured in the initial commit.
188
+ Both visibility modes are validated before any scaffolding happens, so an invalid
189
+ value fails fast and leaves nothing behind. Both are applied before the commit, so
190
+ any resulting `.gitignore` entries are captured in the initial commit — this is what
191
+ lets `scaffold_visibility: ignored` keep `.claude/`/`.agents/` out of the commit even
192
+ though they may already exist on disk (from npm postinstall or a prior `/distribute`
193
+ run) by the time `/init` runs `git add -A`.
156
194
 
157
195
  If the script fails, check that you are in the project root and that git and `jq`
158
196
  are available.
159
197
 
160
- See `skills/distribute/CONFIG_SCHEMA.md` for the full `project_files_visibility`
161
- field reference.
198
+ See `skills/distribute/CONFIG_SCHEMA.md` for the full `project_files_visibility` and
199
+ `scaffold_visibility` field reference.
162
200
 
163
- ### 4. Prompt next step
201
+ ### 5. Prompt next step
164
202
 
165
203
  Inform the user that setup is complete, and state which visibility mode was applied
166
- and where the working files now live. Mention that `docs/STRATEGY.md` was created as
204
+ for both `project_files_visibility` and `scaffold_visibility`, and where the working
205
+ files and scaffold now live. Mention that `docs/STRATEGY.md` was created as
167
206
  a strategic brief stub for investors, partners, and the product team — they can fill
168
207
  it in now or return to it later. Suggest running `/pi-plan` to define project goals
169
208
  and epics.
@@ -11,5 +11,4 @@ Desktop.ini
11
11
  # Dependency directories
12
12
  node_modules/
13
13
  vendor/
14
- .venv/
15
- EOF
14
+ .venv/
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # apply-scaffold-visibility.sh — apply the `scaffold_visibility` mode to a project.
4
+ #
5
+ # This is a DISTINCT flag from `project_files_visibility` (see
6
+ # apply-project-visibility.sh). `project_files_visibility` covers the
7
+ # project/ working tree (scrum board, todo.md, queue/, rapports/, logs/) —
8
+ # E31_S05's own Background section scopes it to that tree only. This script
9
+ # instead covers the *distributed framework scaffold* — `.claude/` and
10
+ # `.agents/` — which is a different tree with a different lifecycle (it is
11
+ # overwritten wholesale by every `/distribute` run or npm upgrade; project/
12
+ # is not). Filed as new, adjacent scope under E31_S07 rather than folded
13
+ # silently into project_files_visibility's existing enum. See
14
+ # skills/distribute/CONFIG_SCHEMA.md's "Scaffold visibility" section for the
15
+ # full rationale.
16
+ #
17
+ # This is skills/j-init/'s own copy of skills/init/scripts/apply-scaffold-visibility.sh
18
+ # (E31_S07_T03) — skills/j-init/ is a separate, hand-maintained directory, not
19
+ # auto-synced with skills/init/ (scripts/generate-j-alias.sh explicitly excludes
20
+ # this pair), and is the copy that actually ships to public-GitHub-mirror and
21
+ # npm-package consumers. Keep this file's logic in lockstep with skills/init/'s
22
+ # copy — any behavioral change made there should be mirrored here.
23
+ #
24
+ # Usage:
25
+ # apply-scaffold-visibility.sh <mode> [project_root]
26
+ # apply-scaffold-visibility.sh --check-only <mode>
27
+ #
28
+ # Modes:
29
+ # visible Scaffold directories stay where they are. No-op on disk beyond
30
+ # the config write. This is the default — matches the behavior
31
+ # of every /init run before this flag existed.
32
+ # ignored .claude/ and .agents/ are added to the project's .gitignore —
33
+ # present on disk (whenever npm postinstall or /distribute
34
+ # places them, whether that happens before or after this runs),
35
+ # never committed. Entries are written unconditionally, the same
36
+ # way apply-project-visibility.sh gitignores project/ whether or
37
+ # not it exists yet, so a scaffold created by a later
38
+ # /distribute run is covered too, not just one already on disk.
39
+ #
40
+ # Exit codes:
41
+ # 0 Success
42
+ # 1 Bad usage or missing prerequisite
43
+ # 2 Invalid mode (outside the two-value enum)
44
+ # 3 Filesystem apply failure
45
+ # 4 jenga.config.json write failure
46
+
47
+ # Do NOT use set -e globally — each step handles its own errors.
48
+ set -uo pipefail
49
+
50
+ info() { echo "[scaffold-visibility] $*"; }
51
+ warn() { echo "[scaffold-visibility] WARNING: $*"; }
52
+ err() { echo "[scaffold-visibility] ERROR: $*" >&2; }
53
+
54
+ usage() {
55
+ echo "Usage: $(basename "$0") <visible|ignored> [project_root]" >&2
56
+ echo " $(basename "$0") --check-only <visible|ignored>" >&2
57
+ exit 1
58
+ }
59
+
60
+ # The distributed framework scaffold — mirrored copies of skills/agents for
61
+ # Claude Code and Copilot/other agents. Distinct from JENGA_WORKING_PATHS in
62
+ # apply-project-visibility.sh, which covers project/ only.
63
+ JENGA_SCAFFOLD_PATHS=(".claude" ".agents")
64
+
65
+ VALID_MODES="visible ignored"
66
+
67
+ validate_mode() {
68
+ local mode="$1"
69
+ for valid in $VALID_MODES; do
70
+ [ "$mode" = "$valid" ] && return 0
71
+ done
72
+ err "Invalid scaffold_visibility value: '${mode}'"
73
+ err "Allowed values are: ${VALID_MODES// /, }"
74
+ exit 2
75
+ }
76
+
77
+ # ---------------------------------------------------------------------------
78
+ # Argument parsing
79
+ # ---------------------------------------------------------------------------
80
+
81
+ CHECK_ONLY=0
82
+ if [ "${1:-}" = "--check-only" ]; then
83
+ CHECK_ONLY=1
84
+ shift
85
+ fi
86
+
87
+ MODE="${1:-}"
88
+ [ -n "$MODE" ] || usage
89
+
90
+ validate_mode "$MODE"
91
+
92
+ if [ "$CHECK_ONLY" -eq 1 ]; then
93
+ info "Mode '$MODE' is valid."
94
+ exit 0
95
+ fi
96
+
97
+ PROJECT_ROOT="${2:-$PWD}"
98
+ if [ ! -d "$PROJECT_ROOT" ]; then
99
+ err "Project root does not exist: $PROJECT_ROOT"
100
+ exit 1
101
+ fi
102
+ cd "$PROJECT_ROOT" || { err "Cannot enter project root: $PROJECT_ROOT"; exit 1; }
103
+
104
+ if ! command -v jq >/dev/null 2>&1; then
105
+ err "jq is required to write jenga.config.json but was not found on PATH."
106
+ exit 1
107
+ fi
108
+
109
+ # ---------------------------------------------------------------------------
110
+ # ignored — append to .gitignore, without ever duplicating an entry
111
+ # ---------------------------------------------------------------------------
112
+
113
+ gitignore_append() {
114
+ local entry="$1"
115
+ local gitignore=".gitignore"
116
+
117
+ if [ -f "$gitignore" ] && grep -qxF -- "$entry" "$gitignore"; then
118
+ info "'$entry' already present in .gitignore — skipping."
119
+ return 0
120
+ fi
121
+
122
+ # Don't glue our entry onto a final line that lacks a newline.
123
+ if [ -s "$gitignore" ] && [ -n "$(tail -c 1 "$gitignore")" ]; then
124
+ printf '\n' >> "$gitignore"
125
+ fi
126
+
127
+ if ! printf '%s\n' "$entry" >> "$gitignore"; then
128
+ err "Failed to append '$entry' to .gitignore"
129
+ exit 3
130
+ fi
131
+ info "Added '$entry' to .gitignore"
132
+ }
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # jenga.config.json — merge the field in, written atomically (temp + mv)
136
+ # ---------------------------------------------------------------------------
137
+
138
+ write_scaffold_config() {
139
+ local mode="$1"
140
+ local config="jenga.config.json"
141
+ local tmp="${config}.tmp"
142
+ local existing='{}'
143
+
144
+ # /init normally runs before any /distribute, so the file usually does not
145
+ # exist yet. Merge rather than overwrite so other fields survive.
146
+ if [ -f "$config" ]; then
147
+ if ! jq empty "$config" 2>/dev/null; then
148
+ err "$config exists but contains malformed JSON — refusing to overwrite it."
149
+ exit 4
150
+ fi
151
+ existing="$(cat "$config")"
152
+ fi
153
+
154
+ local content
155
+ content="$(jq --arg v "$mode" '.scaffold_visibility = $v' <<< "$existing")"
156
+ if [ -z "$content" ]; then
157
+ err "Failed to construct $config content."
158
+ exit 4
159
+ fi
160
+
161
+ if ! printf '%s\n' "$content" > "$tmp"; then
162
+ err "Failed to write temporary config file: $tmp"
163
+ exit 4
164
+ fi
165
+
166
+ if ! mv "$tmp" "$config"; then
167
+ err "Failed to atomically move $tmp to $config"
168
+ rm -f "$tmp"
169
+ exit 4
170
+ fi
171
+
172
+ info "Wrote scaffold_visibility = \"$mode\" to $config"
173
+ }
174
+
175
+ # ---------------------------------------------------------------------------
176
+ # Apply
177
+ # ---------------------------------------------------------------------------
178
+
179
+ case "$MODE" in
180
+ visible)
181
+ info "Mode 'visible' — scaffold directories stay in place; nothing to change on disk."
182
+ ;;
183
+ ignored)
184
+ for path in "${JENGA_SCAFFOLD_PATHS[@]}"; do
185
+ gitignore_append "${path}/"
186
+ done
187
+ ;;
188
+ esac
189
+
190
+ write_scaffold_config "$MODE"
191
+
192
+ info "Applied scaffold_visibility '$MODE' to $PROJECT_ROOT"
@@ -4,6 +4,7 @@ set -euo pipefail
4
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
5
  ASSETS_DIR="$SCRIPT_DIR/../assets"
6
6
  VISIBILITY_SCRIPT="$SCRIPT_DIR/apply-project-visibility.sh"
7
+ SCAFFOLD_VISIBILITY_SCRIPT="$SCRIPT_DIR/apply-scaffold-visibility.sh"
7
8
 
8
9
  # ─── Resolve the package root that owns templates/ and lib/ ──────────────────
9
10
  # postinstall.js mirrors only skills/ and agents/ into .claude/ and .agents/ —
@@ -22,23 +23,33 @@ else
22
23
  exit 1
23
24
  fi
24
25
 
25
- # ─── 0. Resolve project_files_visibility ─────────────────────────────────────
26
+ # ─── 0. Resolve project_files_visibility and scaffold_visibility ─────────────
26
27
  # Defaults to `visible` — the only value that touches nothing on disk — so an
27
28
  # unattended run can never silently relocate directories or edit .gitignore.
29
+ # project_files_visibility covers the project/ working tree (board, todo.md,
30
+ # queue/, rapports/, logs/). scaffold_visibility is a distinct, independent
31
+ # flag (E31_S07_T01, ported here in E31_S07_T03) covering the distributed
32
+ # .claude/.agents framework scaffold — kept separate per
33
+ # skills/distribute/CONFIG_SCHEMA.md's "Scaffold visibility" section, rather
34
+ # than folded into project_files_visibility's existing enum.
28
35
  VISIBILITY="${JENGA_PROJECT_FILES_VISIBILITY:-visible}"
36
+ SCAFFOLD_VISIBILITY="${JENGA_SCAFFOLD_VISIBILITY:-visible}"
29
37
 
30
38
  while [[ $# -gt 0 ]]; do
31
39
  case "$1" in
32
40
  --visibility) VISIBILITY="${2:-}"; shift 2 ;;
33
41
  --visibility=*) VISIBILITY="${1#*=}"; shift ;;
42
+ --scaffold-visibility) SCAFFOLD_VISIBILITY="${2:-}"; shift 2 ;;
43
+ --scaffold-visibility=*) SCAFFOLD_VISIBILITY="${1#*=}"; shift ;;
34
44
  *) echo "Unknown argument: $1" >&2
35
- echo "Usage: $(basename "$0") [--visibility <visible|ignored>]" >&2
45
+ echo "Usage: $(basename "$0") [--visibility <visible|ignored>] [--scaffold-visibility <visible|ignored>]" >&2
36
46
  exit 1 ;;
37
47
  esac
38
48
  done
39
49
 
40
50
  # Validate before scaffolding so a typo cannot leave a half-initialised project.
41
51
  bash "$VISIBILITY_SCRIPT" --check-only "$VISIBILITY"
52
+ bash "$SCAFFOLD_VISIBILITY_SCRIPT" --check-only "$SCAFFOLD_VISIBILITY"
42
53
 
43
54
  # ─── 1. Initialize git repository ────────────────────────────────────────────
44
55
  echo "→ Initializing git repository..."
@@ -101,11 +112,14 @@ cp "$ASSETS_DIR/strategy_stub_template.md" docs/STRATEGY.md
101
112
  echo "→ Creating CHANGELOG.md from template..."
102
113
  cp "$PKG_ROOT/templates/CHANGELOG_TEMPLATE.md" CHANGELOG.md
103
114
 
104
- # ─── 11. Apply project_files_visibility ──────────────────────────────────────
105
- # Runs before the commit so the .gitignore entry (ignored) is captured in the
106
- # initial commit.
115
+ # ─── 11. Apply project_files_visibility and scaffold_visibility ─────────────
116
+ # Both run before the commit so any resulting .gitignore entries (ignored)
117
+ # are captured in the initial commit rather than left for the user to notice
118
+ # after the fact.
107
119
  echo "→ Applying project files visibility ($VISIBILITY)..."
108
120
  bash "$VISIBILITY_SCRIPT" "$VISIBILITY" "$PWD"
121
+ echo "→ Applying scaffold visibility ($SCAFFOLD_VISIBILITY)..."
122
+ bash "$SCAFFOLD_VISIBILITY_SCRIPT" "$SCAFFOLD_VISIBILITY" "$PWD"
109
123
 
110
124
  # ─── 12. Generate CLAUDE.md / AGENTS.md ──────────────────────────────────────
111
125
  # Unconditional — never gated on agentTarget (E41_S04). Applies the J-
@@ -32,6 +32,18 @@ this skill is id resolution (step 1 below).
32
32
 
33
33
  ## Instructions
34
34
 
35
+ 0. **Bare invocation — no id given.** If this skill was invoked with no argument at all, do not
36
+ proceed to step 1. Instead:
37
+ a. Invoke `skills/jenga/scripts/load-playbooks.sh` with no arguments (its existing full-catalog
38
+ mode — the same call step 1's `"not_found"` branch already uses for its "did you mean"
39
+ nudge; no new script is introduced for this).
40
+ b. Render the returned JSON array as a Markdown table with columns `Id`, `Name`, `Source`, and
41
+ `Steps`. For the `Source` column, render `Built-in` for a `source` field of `"builtin"` and
42
+ `Project` for `"project"`. For the `Steps` column, render each entry in that playbook's
43
+ `steps` array joined by `->`: a bare string step renders as itself; a StepObject step
44
+ renders its `skill` or `playbook` field value (whichever is present).
45
+ c. Halt this invocation after rendering the table — do not proceed to step 1.
46
+
35
47
  1. **Resolve the id** — invoke `skills/jenga/scripts/load-playbooks.sh lookup "<id>"`
36
48
  (`E53_S06_T02`), where `<id>` is this skill's argument. Branch on the returned `status` field:
37
49