@jenga-ai/agent 1.0.0 → 1.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 (38) hide show
  1. package/README.md +28 -10
  2. package/agents/scrum-master.md +75 -0
  3. package/mcp/router/embedder.js +1 -1
  4. package/mcp/training_runner/index.js +239 -0
  5. package/mcp/training_runner/package-lock.json +1065 -0
  6. package/mcp/training_runner/package.json +15 -0
  7. package/package.json +13 -11
  8. package/skills/close-story/SKILL.md +203 -0
  9. package/skills/close-story/scripts/check-story-closeable.sh +195 -0
  10. package/skills/close-story/scripts/compute-scope-divergence.sh +128 -0
  11. package/skills/close-story/scripts/extract-diff-stats.sh +48 -0
  12. package/skills/close-story/scripts/extract-task-diff-stats.sh +97 -0
  13. package/skills/close-story/scripts/update-task-frontmatter.sh +103 -0
  14. package/skills/commit/SKILL.md +18 -0
  15. package/skills/distribute/CONFIG_SCHEMA.md +90 -0
  16. package/skills/distribute/SKILL.md +173 -0
  17. package/skills/distribute/scripts/check-version.sh +74 -0
  18. package/skills/distribute/scripts/commit-version-bump.sh +108 -0
  19. package/skills/distribute/scripts/distribute-changes.sh +381 -0
  20. package/skills/do/SKILL.md +314 -0
  21. package/skills/do/assets/intent-vs-diff-prompt.md +69 -0
  22. package/skills/doc/assets/path-objectives.yaml +13 -0
  23. package/skills/init/SKILL.md +4 -3
  24. package/skills/init/assets/strategy_stub_template.md +38 -0
  25. package/skills/init/scripts/init.sh +6 -1
  26. package/skills/jenga/SKILL.md +51 -2
  27. package/skills/strategy/SKILL.md +312 -0
  28. package/templates/SCRUM_BOARD_SCHEMA.md +49 -0
  29. package/skills/train/SKILL.md +0 -116
  30. package/skills/train/assets/dashboard-templates/classifiers.html +0 -106
  31. package/skills/train/assets/dashboard-templates/nlp.html +0 -102
  32. package/skills/train/assets/dashboard-templates/transformers.html +0 -98
  33. package/skills/train/assets/results-parsers/__init__.py +0 -9
  34. package/skills/train/assets/results-parsers/classifiers.py +0 -84
  35. package/skills/train/assets/results-parsers/nlp.py +0 -88
  36. package/skills/train/assets/results-parsers/reporter.py +0 -154
  37. package/skills/train/assets/results-parsers/transformers.py +0 -120
  38. 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