@jenga-ai/agent 1.0.1 → 1.1.1
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.
- package/README.md +10 -7
- package/agents/developer.md +82 -2
- package/agents/scrum-master.md +215 -21
- package/agents/tester.md +90 -8
- package/hooks/on_session_end.sh +171 -20
- package/mcp/router/embedder.js +1 -1
- package/mcp/training_runner/index.js +239 -0
- package/mcp/training_runner/package-lock.json +1065 -0
- package/mcp/training_runner/package.json +15 -0
- package/package.json +14 -16
- package/scripts/check-permission-level.sh +107 -0
- package/scripts/check-publicignore-match.sh +122 -0
- package/scripts/check-worktree-liveness.sh +193 -0
- package/scripts/generate-rapport-manifest.sh +43 -0
- package/scripts/idea_manager.sh +47 -0
- package/scripts/install-worktree-commit-guard.sh +134 -0
- package/scripts/jenga-permission-level-switch.sh +109 -0
- package/scripts/smoke-harness.sh +139 -0
- package/scripts/validate-board.sh +62 -0
- package/scripts/with-lock.sh +158 -0
- package/scripts/worktree-remove-guard.sh +204 -0
- package/skills/clearify/SKILL.md +52 -0
- package/skills/close-story/SKILL.md +203 -0
- package/skills/close-story/scripts/check-story-closeable.sh +195 -0
- package/skills/close-story/scripts/compute-scope-divergence.sh +128 -0
- package/skills/close-story/scripts/extract-diff-stats.sh +48 -0
- package/skills/close-story/scripts/extract-task-diff-stats.sh +97 -0
- package/skills/close-story/scripts/update-task-frontmatter.sh +103 -0
- package/skills/commit/SKILL.md +30 -3
- package/skills/distribute/CONFIG_SCHEMA.md +148 -0
- package/skills/distribute/SKILL.md +173 -0
- package/skills/distribute/scripts/check-version.sh +74 -0
- package/skills/distribute/scripts/commit-version-bump.sh +108 -0
- package/skills/distribute/scripts/distribute-changes.sh +381 -0
- package/skills/do/SKILL.md +352 -1
- package/skills/do/assets/intent-vs-diff-prompt.md +69 -0
- package/skills/doc/assets/path-objectives.yaml +13 -0
- package/skills/doc-sync/SKILL.md +16 -0
- package/skills/doc-sync/assets/doc_targets.md +11 -0
- package/skills/idea/SKILL.md +56 -0
- package/skills/idea/assets/idea_handoff_template.md +26 -0
- package/skills/idea/assets/idea_template.md +3 -0
- package/skills/init/SKILL.md +101 -7
- package/skills/init/assets/directory_structure.txt +1 -0
- package/skills/init/assets/strategy_stub_template.md +38 -0
- package/skills/init/assets/workflow_template.json +1 -1
- package/skills/init/scripts/apply-project-visibility.sh +176 -0
- package/skills/init/scripts/detect-existing-codebase.sh +166 -0
- package/skills/init/scripts/init.sh +35 -1
- package/skills/jenga/SKILL.md +206 -14
- package/skills/jenga/scripts/board-scan.sh +238 -0
- package/skills/jenga/scripts/cascade-resolve.sh +297 -0
- package/skills/jenga/scripts/render-confirmation.sh +679 -0
- package/skills/jenga/scripts/render-picker.sh +439 -0
- package/skills/jenga/scripts/resolve-id.sh +367 -0
- package/skills/jenga-permission-level/SKILL.md +81 -0
- package/skills/proceed/SKILL.md +1 -1
- package/skills/publish/SKILL.md +8 -5
- package/skills/publish/assets/ci-contract.md +2 -2
- package/skills/publish/assets/ownership-matrix.md +1 -1
- package/skills/publish/scripts/finalize_changelog.sh +115 -0
- package/skills/publish/scripts/generate_release_notes.sh +475 -28
- package/skills/publish/scripts/npm_ci_pipeline.sh +44 -6
- package/skills/publish/scripts/publish_deploy.sh +38 -8
- package/skills/publish/scripts/run_gates.sh +2 -2
- package/skills/reconcile/SKILL.md +117 -5
- package/skills/reconcile/scripts/detect-unlinked-code.sh +741 -0
- package/skills/skillify/assets/init-new/assets/directory_structure.txt +5 -1
- package/skills/spinoff/SKILL.md +12 -7
- package/skills/todo/SKILL.md +2 -0
- package/skills/uncharted/SKILL.md +711 -0
- package/skills/uncharted/assets/SEGMENT_PROPOSAL_TEMPLATE.md +129 -0
- package/skills/uncharted/assets/UNDERSTANDING_DOC_TEMPLATE.md +160 -0
- package/skills/uncharted/scripts/apply-subsystem-cap.sh +573 -0
- package/skills/uncharted/scripts/detect-dependencies.sh +732 -0
- package/skills/uncharted/scripts/detect-tests.sh +553 -0
- package/skills/uncharted/scripts/discover-subsystems.sh +1029 -0
- package/skills/uncharted/scripts/enumerate-target.sh +470 -0
- package/skills/uncharted/scripts/import-source.sh +517 -0
- package/skills/uncharted/scripts/inspect-provenance.sh +573 -0
- package/skills/uncharted/scripts/resolve-segment-target.sh +640 -0
- package/skills/uncharted/scripts/run-engine.sh +655 -0
- package/skills/uncharted/scripts/validate-proposed-items.sh +125 -0
- package/skills/uncharted/scripts/write-backfilled-epics.sh +498 -0
- package/skills/wtf/SKILL.md +20 -0
- package/templates/CHANGELOG_TEMPLATE.md +13 -0
- package/templates/PROBLEM_RAPPORT_TEMPLATE.md +4 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +206 -10
- package/templates/permission-levels/README.md +73 -0
- package/templates/permission-levels/level-1-locked.json +71 -0
- package/templates/permission-levels/level-2-guarded.json +64 -0
- package/templates/permission-levels/level-3-standard.json +62 -0
- package/templates/permission-levels/level-4-elevated.json +60 -0
- package/templates/permission-levels/level-5-unrestricted.json +58 -0
- package/skills/convert/SKILL.md +0 -124
- package/skills/convert/convert_cli.py +0 -235
- package/skills/convert/tests/sample.csv +0 -4
- package/skills/convert/tests/sample.json +0 -5
- package/skills/convert/tests/sample.jsonl +0 -3
- package/skills/convert/tests/sample.yaml +0 -18
- package/skills/convert/tests/sample_obj.csv +0 -2
- package/skills/convert/tests/sample_obj.json +0 -9
- package/skills/mirror-public/SKILL.md +0 -237
- package/skills/mirror-public/assets/config.json +0 -5
- package/skills/mirror-public/scripts/mirror.sh +0 -374
- package/skills/self-sync/SKILL.md +0 -73
- package/skills/self-sync/scripts/run.js +0 -136
- package/skills/train/SKILL.md +0 -116
- package/skills/train/assets/dashboard-templates/classifiers.html +0 -106
- package/skills/train/assets/dashboard-templates/nlp.html +0 -102
- package/skills/train/assets/dashboard-templates/transformers.html +0 -98
- package/skills/train/assets/results-parsers/__init__.py +0 -9
- package/skills/train/assets/results-parsers/classifiers.py +0 -84
- package/skills/train/assets/results-parsers/nlp.py +0 -88
- package/skills/train/assets/results-parsers/reporter.py +0 -154
- package/skills/train/assets/results-parsers/transformers.py +0 -120
- package/skills/train/train_cli.py +0 -786
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# distribute-changes.sh — Copy JengaAgent framework files into a consuming project
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# distribute-changes.sh <project_path> [--dry-run]
|
|
6
|
+
#
|
|
7
|
+
# Arguments:
|
|
8
|
+
# project_path Absolute or relative path to the consuming project root.
|
|
9
|
+
# --dry-run Print each planned action without writing any files.
|
|
10
|
+
#
|
|
11
|
+
# Behaviour:
|
|
12
|
+
# 1. Reads distribute.config.json from the monorepo root for the include list.
|
|
13
|
+
# 2. Reads <project_path>/jenga.config.json for target_dir and project_name.
|
|
14
|
+
# 3. Loads per-project exclusions from <project_path>/.jenga_ignore (warn, not error).
|
|
15
|
+
# 4. Rsyncs each included item to both <target_dir>/ and .claude/ in the project.
|
|
16
|
+
# 5. Copies AGENT.md, CLAUDE.md, WARP.md to the project root.
|
|
17
|
+
# 6. Writes jenga.config.json atomically (temp + mv).
|
|
18
|
+
#
|
|
19
|
+
# Exit codes:
|
|
20
|
+
# 0 Success
|
|
21
|
+
# 1 Bad argument / missing prerequisite
|
|
22
|
+
# 2 Missing or unreadable config file
|
|
23
|
+
# 3 rsync or copy failure
|
|
24
|
+
# 4 jenga.config.json write failure
|
|
25
|
+
|
|
26
|
+
# Do NOT use set -e globally — each step handles its own errors.
|
|
27
|
+
set -uo pipefail
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# Helpers
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
usage() {
|
|
34
|
+
echo "Usage: $(basename "$0") <project_path> [--dry-run]" >&2
|
|
35
|
+
echo "" >&2
|
|
36
|
+
echo " project_path Path to the consuming project root." >&2
|
|
37
|
+
echo " --dry-run Print planned actions without writing files." >&2
|
|
38
|
+
exit 1
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
info() { echo "[distribute] $*"; }
|
|
42
|
+
warn() { echo "[distribute] WARNING: $*"; }
|
|
43
|
+
err() { echo "[distribute] ERROR: $*" >&2; }
|
|
44
|
+
dry_run() { echo "[DRY RUN] $*"; }
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Resolve monorepo root from this script's location.
|
|
48
|
+
# Script lives at: skills/distribute/scripts/distribute-changes.sh
|
|
49
|
+
# Repo root is three levels up.
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
53
|
+
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# Check runtime dependencies
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
60
|
+
err "jq is required but not found on PATH. Install jq and retry."
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if ! command -v rsync >/dev/null 2>&1; then
|
|
65
|
+
err "rsync is required but not found on PATH. Install rsync and retry."
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# Parse arguments
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
PROJECT_PATH=""
|
|
74
|
+
DRY_RUN=0
|
|
75
|
+
|
|
76
|
+
for arg in "$@"; do
|
|
77
|
+
case "$arg" in
|
|
78
|
+
--dry-run)
|
|
79
|
+
DRY_RUN=1
|
|
80
|
+
;;
|
|
81
|
+
-*)
|
|
82
|
+
err "Unknown option: $arg"
|
|
83
|
+
usage
|
|
84
|
+
;;
|
|
85
|
+
*)
|
|
86
|
+
if [ -z "$PROJECT_PATH" ]; then
|
|
87
|
+
PROJECT_PATH="$arg"
|
|
88
|
+
else
|
|
89
|
+
err "Unexpected extra argument: $arg"
|
|
90
|
+
usage
|
|
91
|
+
fi
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
if [ -z "$PROJECT_PATH" ]; then
|
|
97
|
+
err "Missing required argument: project_path"
|
|
98
|
+
usage
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Resolve to absolute path — must already exist
|
|
102
|
+
if ! _resolved="$(cd "$PROJECT_PATH" 2>/dev/null && pwd)"; then
|
|
103
|
+
err "Target project path does not exist or is not a directory: $PROJECT_PATH"
|
|
104
|
+
exit 1
|
|
105
|
+
fi
|
|
106
|
+
PROJECT_PATH="$_resolved"
|
|
107
|
+
|
|
108
|
+
if [ ! -d "$PROJECT_PATH" ]; then
|
|
109
|
+
err "Target project path does not exist or is not a directory: $PROJECT_PATH"
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
info "Monorepo root : $REPO_ROOT"
|
|
114
|
+
info "Target project: $PROJECT_PATH"
|
|
115
|
+
[ "$DRY_RUN" -eq 1 ] && info "Dry-run mode enabled — no files will be written."
|
|
116
|
+
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
# Step 1: Read distribute.config.json for the include list
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
DIST_CONFIG="$REPO_ROOT/distribute.config.json"
|
|
122
|
+
|
|
123
|
+
if [ ! -f "$DIST_CONFIG" ]; then
|
|
124
|
+
err "distribute.config.json not found at $DIST_CONFIG"
|
|
125
|
+
exit 2
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Read include list into a newline-delimited variable (Bash 3 compatible)
|
|
129
|
+
INCLUDE_RAW="$(jq -r '.distribute.include[]' "$DIST_CONFIG" 2>/dev/null)"
|
|
130
|
+
|
|
131
|
+
if [ -z "$INCLUDE_RAW" ]; then
|
|
132
|
+
err "distribute.config.json has an empty or missing distribute.include array: $DIST_CONFIG"
|
|
133
|
+
exit 2
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Build an array from the newline-delimited output
|
|
137
|
+
INCLUDE_LIST=()
|
|
138
|
+
while IFS= read -r item; do
|
|
139
|
+
[ -n "$item" ] && INCLUDE_LIST+=("$item")
|
|
140
|
+
done <<< "$INCLUDE_RAW"
|
|
141
|
+
|
|
142
|
+
info "Include list (${#INCLUDE_LIST[@]} items): ${INCLUDE_LIST[*]}"
|
|
143
|
+
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
# Step 2: Read <project_path>/jenga.config.json for target_dir and project_name
|
|
146
|
+
# ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
JENGA_CONFIG="$PROJECT_PATH/jenga.config.json"
|
|
149
|
+
TARGET_DIR=".agents"
|
|
150
|
+
PROJECT_NAME=""
|
|
151
|
+
FIRST_DISTRIBUTION=0
|
|
152
|
+
|
|
153
|
+
if [ -f "$JENGA_CONFIG" ]; then
|
|
154
|
+
_target_dir="$(jq -r '.target_dir // empty' "$JENGA_CONFIG" 2>/dev/null)"
|
|
155
|
+
if [ -n "$_target_dir" ]; then
|
|
156
|
+
TARGET_DIR="$_target_dir"
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
_project_name="$(jq -r '.project_name // empty' "$JENGA_CONFIG" 2>/dev/null)"
|
|
160
|
+
if [ -n "$_project_name" ]; then
|
|
161
|
+
PROJECT_NAME="$_project_name"
|
|
162
|
+
fi
|
|
163
|
+
else
|
|
164
|
+
FIRST_DISTRIBUTION=1
|
|
165
|
+
info "No jenga.config.json found — treating as first distribution."
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Derive project_name from directory basename if not set
|
|
169
|
+
if [ -z "$PROJECT_NAME" ]; then
|
|
170
|
+
PROJECT_NAME="$(basename "$PROJECT_PATH")"
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
info "Target directory : $TARGET_DIR"
|
|
174
|
+
info "Project name : $PROJECT_NAME"
|
|
175
|
+
[ "$FIRST_DISTRIBUTION" -eq 1 ] && info "First distribution — no version comparison performed."
|
|
176
|
+
|
|
177
|
+
# ---------------------------------------------------------------------------
|
|
178
|
+
# Step 3: Load per-project exclusions from .jenga_ignore
|
|
179
|
+
# ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
JENGA_IGNORE="$PROJECT_PATH/.jenga_ignore"
|
|
182
|
+
|
|
183
|
+
# Store exclusions as a newline-delimited string for Bash 3 compatibility
|
|
184
|
+
IGNORE_LIST=""
|
|
185
|
+
IGNORE_COUNT=0
|
|
186
|
+
|
|
187
|
+
if [ -f "$JENGA_IGNORE" ]; then
|
|
188
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
189
|
+
# Skip blank lines and comment lines
|
|
190
|
+
[ -z "$line" ] && continue
|
|
191
|
+
case "$line" in \#*) continue ;; esac
|
|
192
|
+
IGNORE_LIST="${IGNORE_LIST}${line}"$'\n'
|
|
193
|
+
IGNORE_COUNT=$((IGNORE_COUNT + 1))
|
|
194
|
+
done < "$JENGA_IGNORE"
|
|
195
|
+
info "Loaded $IGNORE_COUNT exclusion(s) from .jenga_ignore."
|
|
196
|
+
else
|
|
197
|
+
warn ".jenga_ignore not found at $JENGA_IGNORE — no per-project exclusions applied."
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
# Helper: returns 0 if the given item is in the ignore list, 1 otherwise.
|
|
201
|
+
is_ignored() {
|
|
202
|
+
local needle="$1"
|
|
203
|
+
local entry
|
|
204
|
+
while IFS= read -r entry; do
|
|
205
|
+
[ "$entry" = "$needle" ] && return 0
|
|
206
|
+
done <<< "$IGNORE_LIST"
|
|
207
|
+
return 1
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# ---------------------------------------------------------------------------
|
|
211
|
+
# Step 4: Compute effective include list (exclude items in .jenga_ignore)
|
|
212
|
+
# ---------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
EFFECTIVE_INCLUDE=()
|
|
215
|
+
for item in "${INCLUDE_LIST[@]}"; do
|
|
216
|
+
if is_ignored "$item"; then
|
|
217
|
+
info "Skipping '$item' (excluded by .jenga_ignore)."
|
|
218
|
+
else
|
|
219
|
+
EFFECTIVE_INCLUDE+=("$item")
|
|
220
|
+
fi
|
|
221
|
+
done
|
|
222
|
+
|
|
223
|
+
info "Effective include list (${#EFFECTIVE_INCLUDE[@]} items): ${EFFECTIVE_INCLUDE[*]}"
|
|
224
|
+
|
|
225
|
+
# ---------------------------------------------------------------------------
|
|
226
|
+
# Step 5: Copy items to both <target_dir>/ and .claude/ via rsync
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
_rsync_item() {
|
|
230
|
+
local src="$1"
|
|
231
|
+
local dst_parent="$2"
|
|
232
|
+
|
|
233
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
234
|
+
dry_run "rsync -a --delete \"$src\" \"$dst_parent/\""
|
|
235
|
+
return 0
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
if ! mkdir -p "$dst_parent"; then
|
|
239
|
+
err "Failed to create destination directory: $dst_parent"
|
|
240
|
+
return 3
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
if ! rsync -a --delete "$src" "$dst_parent/"; then
|
|
244
|
+
err "rsync failed: $src -> $dst_parent/"
|
|
245
|
+
return 3
|
|
246
|
+
fi
|
|
247
|
+
return 0
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
for item in "${EFFECTIVE_INCLUDE[@]}"; do
|
|
251
|
+
src="$REPO_ROOT/$item"
|
|
252
|
+
|
|
253
|
+
if [ ! -e "$src" ]; then
|
|
254
|
+
warn "Source item does not exist, skipping: $src"
|
|
255
|
+
continue
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
# Determine destination parent directories
|
|
259
|
+
# rsync trailing slash copies the item itself into the parent,
|
|
260
|
+
# so we pass the containing dir as the destination.
|
|
261
|
+
dst_agents_parent="$PROJECT_PATH/$TARGET_DIR/$(dirname "$item")"
|
|
262
|
+
dst_claude_parent="$PROJECT_PATH/.claude/$(dirname "$item")"
|
|
263
|
+
|
|
264
|
+
# Normalise: dirname returns "." for a top-level item
|
|
265
|
+
if [ "$(dirname "$item")" = "." ]; then
|
|
266
|
+
dst_agents_parent="$PROJECT_PATH/$TARGET_DIR"
|
|
267
|
+
dst_claude_parent="$PROJECT_PATH/.claude"
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
if ! _rsync_item "$src" "$dst_agents_parent"; then
|
|
271
|
+
exit 3
|
|
272
|
+
fi
|
|
273
|
+
[ "$DRY_RUN" -eq 0 ] && info "Copied $item -> $TARGET_DIR/$item"
|
|
274
|
+
|
|
275
|
+
if ! _rsync_item "$src" "$dst_claude_parent"; then
|
|
276
|
+
exit 3
|
|
277
|
+
fi
|
|
278
|
+
[ "$DRY_RUN" -eq 0 ] && info "Copied $item -> .claude/$item"
|
|
279
|
+
done
|
|
280
|
+
|
|
281
|
+
# ---------------------------------------------------------------------------
|
|
282
|
+
# Step 6: Copy root-level docs to the consuming project root
|
|
283
|
+
# ---------------------------------------------------------------------------
|
|
284
|
+
|
|
285
|
+
ROOT_DOCS="AGENT.md CLAUDE.md WARP.md"
|
|
286
|
+
|
|
287
|
+
for doc in $ROOT_DOCS; do
|
|
288
|
+
src="$REPO_ROOT/$doc"
|
|
289
|
+
dst="$PROJECT_PATH/$doc"
|
|
290
|
+
|
|
291
|
+
if [ ! -f "$src" ]; then
|
|
292
|
+
warn "Root doc not found in monorepo, skipping: $src"
|
|
293
|
+
continue
|
|
294
|
+
fi
|
|
295
|
+
|
|
296
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
297
|
+
dry_run "cp \"$src\" \"$dst\""
|
|
298
|
+
else
|
|
299
|
+
if ! cp "$src" "$dst"; then
|
|
300
|
+
err "Failed to copy $doc to project root: $dst"
|
|
301
|
+
exit 3
|
|
302
|
+
fi
|
|
303
|
+
info "Copied $doc -> project root"
|
|
304
|
+
fi
|
|
305
|
+
done
|
|
306
|
+
|
|
307
|
+
# ---------------------------------------------------------------------------
|
|
308
|
+
# Step 7: Write jenga.config.json atomically
|
|
309
|
+
# ---------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
PKG_JSON="$REPO_ROOT/package.json"
|
|
312
|
+
if [ ! -f "$PKG_JSON" ]; then
|
|
313
|
+
err "package.json not found at $PKG_JSON — cannot determine version."
|
|
314
|
+
exit 2
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
MONOREPO_VERSION="$(jq -r '.version // empty' "$PKG_JSON" 2>/dev/null)"
|
|
318
|
+
if [ -z "$MONOREPO_VERSION" ]; then
|
|
319
|
+
err "Could not read version from $PKG_JSON"
|
|
320
|
+
exit 2
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
UPDATED_AT="$(date -u +"%Y-%m-%d")"
|
|
324
|
+
LAST_DISTRIBUTED="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
325
|
+
|
|
326
|
+
# Build JSON using jq to ensure correct escaping of all values
|
|
327
|
+
JENGA_CONFIG_CONTENT="$(jq -n \
|
|
328
|
+
--arg project_name "$PROJECT_NAME" \
|
|
329
|
+
--arg target_dir "$TARGET_DIR" \
|
|
330
|
+
--arg version "$MONOREPO_VERSION" \
|
|
331
|
+
--arg updated_at "$UPDATED_AT" \
|
|
332
|
+
--arg last_distributed "$LAST_DISTRIBUTED" \
|
|
333
|
+
--arg source "private" \
|
|
334
|
+
'{
|
|
335
|
+
project_name: $project_name,
|
|
336
|
+
target_dir: $target_dir,
|
|
337
|
+
version: $version,
|
|
338
|
+
updated_at: $updated_at,
|
|
339
|
+
last_distributed: $last_distributed,
|
|
340
|
+
source: $source
|
|
341
|
+
}')"
|
|
342
|
+
|
|
343
|
+
if [ -z "$JENGA_CONFIG_CONTENT" ]; then
|
|
344
|
+
err "Failed to construct jenga.config.json content."
|
|
345
|
+
exit 4
|
|
346
|
+
fi
|
|
347
|
+
|
|
348
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
349
|
+
dry_run "Write jenga.config.json to $JENGA_CONFIG"
|
|
350
|
+
dry_run "Content:"
|
|
351
|
+
while IFS= read -r line; do
|
|
352
|
+
dry_run " $line"
|
|
353
|
+
done <<< "$JENGA_CONFIG_CONTENT"
|
|
354
|
+
else
|
|
355
|
+
TEMP_CONFIG="${JENGA_CONFIG}.tmp"
|
|
356
|
+
|
|
357
|
+
if ! printf '%s\n' "$JENGA_CONFIG_CONTENT" > "$TEMP_CONFIG"; then
|
|
358
|
+
err "Failed to write temporary config file: $TEMP_CONFIG"
|
|
359
|
+
exit 4
|
|
360
|
+
fi
|
|
361
|
+
|
|
362
|
+
if ! mv "$TEMP_CONFIG" "$JENGA_CONFIG"; then
|
|
363
|
+
err "Failed to atomically move $TEMP_CONFIG to $JENGA_CONFIG"
|
|
364
|
+
rm -f "$TEMP_CONFIG"
|
|
365
|
+
exit 4
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
info "Wrote jenga.config.json (version $MONOREPO_VERSION)"
|
|
369
|
+
fi
|
|
370
|
+
|
|
371
|
+
# ---------------------------------------------------------------------------
|
|
372
|
+
# Done
|
|
373
|
+
# ---------------------------------------------------------------------------
|
|
374
|
+
|
|
375
|
+
if [ "$DRY_RUN" -eq 1 ]; then
|
|
376
|
+
info "Dry run complete — no files were written."
|
|
377
|
+
else
|
|
378
|
+
info "Distribution complete for: $PROJECT_NAME ($PROJECT_PATH)"
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
exit 0
|