@jenga-ai/agent 1.1.0 → 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.
Files changed (91) hide show
  1. package/README.md +7 -3
  2. package/agents/developer.md +82 -2
  3. package/agents/scrum-master.md +140 -21
  4. package/agents/tester.md +90 -8
  5. package/hooks/on_session_end.sh +171 -20
  6. package/package.json +1 -1
  7. package/scripts/check-permission-level.sh +107 -0
  8. package/scripts/check-publicignore-match.sh +122 -0
  9. package/scripts/check-worktree-liveness.sh +193 -0
  10. package/scripts/generate-rapport-manifest.sh +43 -0
  11. package/scripts/idea_manager.sh +47 -0
  12. package/scripts/install-worktree-commit-guard.sh +134 -0
  13. package/scripts/jenga-permission-level-switch.sh +109 -0
  14. package/scripts/smoke-harness.sh +139 -0
  15. package/scripts/validate-board.sh +62 -0
  16. package/scripts/with-lock.sh +158 -0
  17. package/scripts/worktree-remove-guard.sh +204 -0
  18. package/skills/clearify/SKILL.md +52 -0
  19. package/skills/commit/SKILL.md +13 -4
  20. package/skills/distribute/CONFIG_SCHEMA.md +60 -2
  21. package/skills/do/SKILL.md +48 -11
  22. package/skills/doc-sync/SKILL.md +16 -0
  23. package/skills/doc-sync/assets/doc_targets.md +11 -0
  24. package/skills/idea/SKILL.md +56 -0
  25. package/skills/idea/assets/idea_handoff_template.md +26 -0
  26. package/skills/idea/assets/idea_template.md +3 -0
  27. package/skills/init/SKILL.md +100 -7
  28. package/skills/init/assets/directory_structure.txt +1 -0
  29. package/skills/init/assets/workflow_template.json +1 -1
  30. package/skills/init/scripts/apply-project-visibility.sh +176 -0
  31. package/skills/init/scripts/detect-existing-codebase.sh +166 -0
  32. package/skills/init/scripts/init.sh +30 -1
  33. package/skills/jenga/SKILL.md +160 -17
  34. package/skills/jenga/scripts/board-scan.sh +238 -0
  35. package/skills/jenga/scripts/cascade-resolve.sh +297 -0
  36. package/skills/jenga/scripts/render-confirmation.sh +679 -0
  37. package/skills/jenga/scripts/render-picker.sh +439 -0
  38. package/skills/jenga/scripts/resolve-id.sh +367 -0
  39. package/skills/jenga-permission-level/SKILL.md +81 -0
  40. package/skills/proceed/SKILL.md +1 -1
  41. package/skills/publish/SKILL.md +8 -5
  42. package/skills/publish/assets/ci-contract.md +2 -2
  43. package/skills/publish/assets/ownership-matrix.md +1 -1
  44. package/skills/publish/scripts/finalize_changelog.sh +115 -0
  45. package/skills/publish/scripts/generate_release_notes.sh +475 -28
  46. package/skills/publish/scripts/npm_ci_pipeline.sh +44 -6
  47. package/skills/publish/scripts/publish_deploy.sh +38 -8
  48. package/skills/publish/scripts/run_gates.sh +2 -2
  49. package/skills/reconcile/SKILL.md +117 -5
  50. package/skills/reconcile/scripts/detect-unlinked-code.sh +741 -0
  51. package/skills/skillify/assets/init-new/assets/directory_structure.txt +5 -1
  52. package/skills/spinoff/SKILL.md +12 -7
  53. package/skills/todo/SKILL.md +2 -0
  54. package/skills/uncharted/SKILL.md +711 -0
  55. package/skills/uncharted/assets/SEGMENT_PROPOSAL_TEMPLATE.md +129 -0
  56. package/skills/uncharted/assets/UNDERSTANDING_DOC_TEMPLATE.md +160 -0
  57. package/skills/uncharted/scripts/apply-subsystem-cap.sh +573 -0
  58. package/skills/uncharted/scripts/detect-dependencies.sh +732 -0
  59. package/skills/uncharted/scripts/detect-tests.sh +553 -0
  60. package/skills/uncharted/scripts/discover-subsystems.sh +1029 -0
  61. package/skills/uncharted/scripts/enumerate-target.sh +470 -0
  62. package/skills/uncharted/scripts/import-source.sh +517 -0
  63. package/skills/uncharted/scripts/inspect-provenance.sh +573 -0
  64. package/skills/uncharted/scripts/resolve-segment-target.sh +640 -0
  65. package/skills/uncharted/scripts/run-engine.sh +655 -0
  66. package/skills/uncharted/scripts/validate-proposed-items.sh +125 -0
  67. package/skills/uncharted/scripts/write-backfilled-epics.sh +498 -0
  68. package/skills/wtf/SKILL.md +20 -0
  69. package/templates/CHANGELOG_TEMPLATE.md +13 -0
  70. package/templates/PROBLEM_RAPPORT_TEMPLATE.md +4 -1
  71. package/templates/SCRUM_BOARD_SCHEMA.md +157 -10
  72. package/templates/permission-levels/README.md +73 -0
  73. package/templates/permission-levels/level-1-locked.json +71 -0
  74. package/templates/permission-levels/level-2-guarded.json +64 -0
  75. package/templates/permission-levels/level-3-standard.json +62 -0
  76. package/templates/permission-levels/level-4-elevated.json +60 -0
  77. package/templates/permission-levels/level-5-unrestricted.json +58 -0
  78. package/skills/convert/SKILL.md +0 -124
  79. package/skills/convert/convert_cli.py +0 -235
  80. package/skills/convert/tests/sample.csv +0 -4
  81. package/skills/convert/tests/sample.json +0 -5
  82. package/skills/convert/tests/sample.jsonl +0 -3
  83. package/skills/convert/tests/sample.yaml +0 -18
  84. package/skills/convert/tests/sample_obj.csv +0 -2
  85. package/skills/convert/tests/sample_obj.json +0 -9
  86. package/skills/mirror-public/SKILL.md +0 -237
  87. package/skills/mirror-public/assets/config.json +0 -5
  88. package/skills/mirror-public/scripts/mirror.sh +0 -374
  89. package/skills/self-sync/SKILL.md +0 -73
  90. package/skills/self-sync/scripts/run.js +0 -136
  91. package/skills/strategy/SKILL.md +0 -312
@@ -15,6 +15,7 @@ DROPLET_PIPELINE_SCRIPT="$SCRIPT_DIR/droplet_pipeline.sh"
15
15
  NPM_CI_PIPELINE_SCRIPT="$SCRIPT_DIR/npm_ci_pipeline.sh"
16
16
  RUN_GATES_SCRIPT="$SCRIPT_DIR/run_gates.sh"
17
17
  GENERATE_RELEASE_NOTES_SCRIPT="$SCRIPT_DIR/generate_release_notes.sh"
18
+ FINALIZE_CHANGELOG_SCRIPT="$SCRIPT_DIR/finalize_changelog.sh"
18
19
  SUGGEST_SEMVER_SCRIPT="$SCRIPT_DIR/suggest_semver_bump.sh"
19
20
  WRITE_LEDGER_ENTRY_SCRIPT="$SCRIPT_DIR/write_ledger_entry.sh"
20
21
  IOS_PIPELINE_SCRIPT="$SCRIPT_DIR/ios_pipeline.sh"
@@ -31,6 +32,7 @@ AUTO_YES=0
31
32
  DRY_RUN=0
32
33
  BUMP_OVERRIDE=""
33
34
  RELEASE_NOTES_PATH=""
35
+ RELEASE_NOTES_BYPASSED=0
34
36
  PROMPT_INPUT_FD=0
35
37
  NON_INTERACTIVE=0
36
38
  TARGET_TYPE=""
@@ -41,7 +43,7 @@ SELECTED_VERSION=""
41
43
  usage() {
42
44
  cat <<'USAGE'
43
45
  Usage: publish_deploy.sh [--target <name>] [--config <path>] [--yes] [--dry-run]
44
- [--minor | --major] [--release-notes <path>]
46
+ [--minor | --major] [--notes-file <path>]
45
47
  USAGE
46
48
  }
47
49
 
@@ -290,8 +292,7 @@ generate_release_notes() {
290
292
  return 0
291
293
  fi
292
294
 
293
- RELEASE_NOTES_PATH="$(publish_temp_release_notes_path)"
294
- local cmd=(bash "$GENERATE_RELEASE_NOTES_SCRIPT" --target "$TARGET_NAME" --output "$RELEASE_NOTES_PATH")
295
+ local cmd=(bash "$GENERATE_RELEASE_NOTES_SCRIPT" --target "$TARGET_NAME")
295
296
  if [[ -n "$LAST_TAG" ]]; then
296
297
  cmd+=(--from-tag "$LAST_TAG")
297
298
  fi
@@ -299,6 +300,8 @@ generate_release_notes() {
299
300
  if ! "${cmd[@]}"; then
300
301
  fail_with "$EXIT_ADAPTER_FAILURE" "Failed to generate release notes."
301
302
  fi
303
+
304
+ RELEASE_NOTES_PATH="$PUBLISH_REPO_ROOT/CHANGELOG.md"
302
305
  }
303
306
 
304
307
  review_release_notes() {
@@ -317,9 +320,18 @@ review_release_notes() {
317
320
 
318
321
  resolve_selected_version() {
319
322
  local current_version suggested_version
320
- current_version="$(publish_latest_ledger_version "$HISTORY_FILE" 2>/dev/null || true)"
321
- [[ -n "$current_version" ]] || current_version='v0.0.0'
322
- current_version="$(publish_normalize_version "$current_version")" || current_version='v0.0.0'
323
+ # For npm/npm-ci targets, package.json is the authoritative version source.
324
+ # The ledger may lag behind if a previous publish didn't bump package.json.
325
+ if [[ "$TARGET_TYPE" == "npm" || "$TARGET_TYPE" == "npm-ci" ]]; then
326
+ local pkg_version
327
+ pkg_version="$(jq -r '.version // empty' "$PUBLISH_REPO_ROOT/package.json" 2>/dev/null || true)"
328
+ current_version="$(publish_normalize_version "$pkg_version" 2>/dev/null)" || current_version=''
329
+ fi
330
+ if [[ -z "$current_version" ]]; then
331
+ current_version="$(publish_latest_ledger_version "$HISTORY_FILE" 2>/dev/null || true)"
332
+ [[ -n "$current_version" ]] || current_version='v0.0.0'
333
+ current_version="$(publish_normalize_version "$current_version")" || current_version='v0.0.0'
334
+ fi
323
335
 
324
336
  suggested_version="$(bash "$SUGGEST_SEMVER_SCRIPT" --yes "$CONFIG_PATH" | tail -n 1 | tr -d '\r')"
325
337
  suggested_version="$(publish_normalize_version "$suggested_version")" || fail_with "$EXIT_ADAPTER_FAILURE" "Unable to determine a suggested version."
@@ -380,7 +392,7 @@ run_adapter_pipeline() {
380
392
  cmd=(bash "$NPM_PIPELINE_SCRIPT" "$TARGET_NAME" "$CONFIG_PATH")
381
393
  ;;
382
394
  npm-ci)
383
- cmd=(bash "$NPM_CI_PIPELINE_SCRIPT" --target "$TARGET_NAME" --config "$CONFIG_PATH")
395
+ cmd=(bash "$NPM_CI_PIPELINE_SCRIPT" --target "$TARGET_NAME" --config "$CONFIG_PATH" --version "$SELECTED_VERSION")
384
396
  ;;
385
397
  droplet)
386
398
  cmd=(bash "$DROPLET_PIPELINE_SCRIPT" --target "$TARGET_NAME" --config "$CONFIG_PATH")
@@ -423,6 +435,22 @@ run_post_gates() {
423
435
  return 1
424
436
  }
425
437
 
438
+ finalize_changelog() {
439
+ # Skipped on a dry run — the real tag and ledger entry are not created
440
+ # either (see write_ledger_entry's own --dry-run handling), and
441
+ # [Unreleased] content from generate_release_notes was still updated
442
+ # earlier in the flow, just left unstamped.
443
+ (( DRY_RUN )) && return 0
444
+ # Skipped when release notes were supplied via the --notes-file bypass
445
+ # flag: RELEASE_NOTES_PATH then points at an arbitrary user-supplied file
446
+ # rather than the standing CHANGELOG.md, so there is no [Unreleased]
447
+ # section of the standing file to finalize.
448
+ (( RELEASE_NOTES_BYPASSED )) && return 0
449
+
450
+ bash "$FINALIZE_CHANGELOG_SCRIPT" "$SELECTED_VERSION" "$RELEASE_NOTES_PATH" \
451
+ || fail_with "$EXIT_ADAPTER_FAILURE" "Failed to finalize CHANGELOG.md."
452
+ }
453
+
426
454
  write_ledger_entry() {
427
455
  local platform_state="$1"
428
456
  local cmd=(bash "$WRITE_LEDGER_ENTRY_SCRIPT" "$TARGET_NAME" "$TARGET_TYPE" "$platform_state" "$RELEASE_NOTES_PATH" --version "$SELECTED_VERSION" --config "$CONFIG_PATH")
@@ -483,9 +511,10 @@ parse_args() {
483
511
  BUMP_OVERRIDE="major"
484
512
  shift
485
513
  ;;
486
- --release-notes)
514
+ --notes-file)
487
515
  [[ $# -ge 2 ]] || { usage >&2; exit 1; }
488
516
  RELEASE_NOTES_PATH="$2"
517
+ RELEASE_NOTES_BYPASSED=1
489
518
  shift 2
490
519
  ;;
491
520
  --help|-h)
@@ -531,6 +560,7 @@ main() {
531
560
  platform_state="dry-run"
532
561
  fi
533
562
 
563
+ finalize_changelog
534
564
  write_ledger_entry "$platform_state"
535
565
  print_manual_steps
536
566
  }
@@ -245,7 +245,7 @@ resolve_default_command() {
245
245
  # fall through to iOS tooling. Missing scripts are announced with a clear
246
246
  # informational note (on stderr so it does not pollute the captured
247
247
  # command) before the gate is reported as skipped upstream.
248
- if [[ "$TARGET_TYPE" == "npm" ]]; then
248
+ if [[ "$TARGET_TYPE" == "npm" || "$TARGET_TYPE" == "npm-ci" ]]; then
249
249
  case "$gate_name" in
250
250
  build|test|lint)
251
251
  if find_npm_script_in_root "$gate_name"; then
@@ -563,7 +563,7 @@ build_gate_list() {
563
563
  local -a mandatory_pre_gates=()
564
564
 
565
565
  if [[ "$PHASE" == "pre" ]]; then
566
- if [[ "$TARGET_TYPE" == "npm" ]]; then
566
+ if [[ "$TARGET_TYPE" == "npm" || "$TARGET_TYPE" == "npm-ci" ]]; then
567
567
  # npm-specific mandatory gates: test, build, lint (per E26_S06_T03).
568
568
  mandatory_pre_gates=(test build lint)
569
569
  else
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: reconcile
3
- description: Reconcile the scrum board with actual implementation state. Cross-checks every task's board status against git history and worktrees, merges orphaned worktree branches, demotes unimplemented "Done" items, promotes secretly-implemented items, and cleans stale entries from todo.md. Use when the board feels out of sync, after a big merge session, when tasks were completed outside the normal workflow, or when todo.md has grown stale. Trigger on phrases like "sync the board", "clean up the board", "reconcile", "board is out of date", "todo is stale", or "check what's really done".
3
+ description: Reconcile the scrum board with actual implementation state. Cross-checks every task's board status against git history and worktrees, merges orphaned worktree branches, demotes unimplemented "Done" items, promotes secretly-implemented items, flags code with no board provenance and offers /uncharted segment for it, and cleans stale entries from todo.md. Use when the board feels out of sync, after a big merge session, when tasks were completed outside the normal workflow, or when todo.md has grown stale. Trigger on phrases like "sync the board", "clean up the board", "reconcile", "board is out of date", "todo is stale", or "check what's really done".
4
4
  metadata:
5
5
  prefered_agent: scrum-master
6
6
  ---
7
7
 
8
8
  # Reconcile — Board ↔ Code Synchronisation
9
9
 
10
- Walks the full board (epics → stories → tasks), verifies each item's status against what actually exists in git, and fixes any drift. Also cleans `project/todo.md` of entries that are already done.
10
+ Walks the full board (epics → stories → tasks), verifies each item's status against what actually exists in git, and fixes any drift. Also cleans `project/todo.md` of entries that are already done. Then runs the same check in reverse — code that exists with no board item and no EST-tagged commit behind it — and offers `/uncharted segment` for what it finds.
11
11
 
12
12
  ## Instructions
13
13
 
@@ -80,14 +80,126 @@ At the end of Phase 4, if any DoD gaps were found across any stories, include a
80
80
 
81
81
  **Important:** Gap detection is **report-only**. Do not automatically change the status of any story or epic based on unchecked DoD boxes — surface the gaps so a human can review and decide.
82
82
 
83
- ### 5. Clean `project/todo.md`
83
+ ### 5. Detect unlinked code (no board provenance)
84
+ Phases 2 and 3 ask "does this board item exist in the code?". This phase asks the inverse —
85
+ **"does this code exist on the board?"** — and finds paths with no Jenga provenance at all.
86
+
87
+ Run:
88
+
89
+ ```bash
90
+ skills/reconcile/scripts/detect-unlinked-code.sh
91
+ ```
92
+
93
+ It emits a single JSON object and exits `0` whether or not it finds anything; read
94
+ `summary.uncharted_paths` rather than branching on the exit status. All the deterministic work —
95
+ exclusions, the board check, the commit check, grouping — is in the script. Everything below is
96
+ judgement and presentation.
97
+
98
+ A path is reported as unlinked only when **both** provenance signals are absent: no board file
99
+ references it, **and** no commit naming a board item added it. Either signal alone is provenance,
100
+ so a file created by a `task(...)` commit is not reported even if no board file names it by path.
101
+ Signal B accepts the current EST tags (`epic(...)`, `story(...)`, `task(E##_S##_T##)`) and the two
102
+ older conventions still present in this repo's history (`E04_S01: ...` and
103
+ `feat(train): implement E01_S05 - ...`).
104
+
105
+ The script reuses the board-linkage check from
106
+ `skills/uncharted/scripts/resolve-segment-target.sh` through its batch interface. Do not
107
+ re-derive linkage yourself, and do not substitute a `grep` over `project/board/` if the script
108
+ fails — a second answer to "is this path on the board" is what that reuse exists to prevent.
109
+ If the script exits non-zero, report the failure in the reconcile report and continue to phase 6.
110
+
111
+ #### Reading the result
112
+
113
+ - **`groups[]`** — the actionable findings, sorted by `unlinked_count` descending. Each entry is
114
+ a directory, not a file, because the useful unit for `/uncharted segment` is a directory or
115
+ feature. Every group directory has itself been checked against the board, so these are
116
+ directories with no provenance of their own either. `fully_unlinked: true` means *every*
117
+ candidate file beneath the directory is unlinked — those are the strongest segment candidates
118
+ and should be offered first. When `false`, the directory holds a mix and only the listed files
119
+ are unlinked.
120
+ - **`covered_groups[]`** — files with no provenance of their own that nonetheless sit under a
121
+ directory the board *does* reference (`directory_linkage.status: "linked"`, with the owning
122
+ board items in `directory_linkage.items` and the spelling that matched in `matched_as`).
123
+ **Do not offer `/uncharted segment` for these** — the board already owns that directory and a
124
+ segment run would duplicate existing items. Mention them in the report as a one-line count so
125
+ the information is not lost, and move on.
126
+ - **`files`** is capped by `--limit` (default 10); `files_truncated` gives the remainder. The
127
+ counts are always true totals, so never re-count `files` to report a number.
128
+ - **`not_checked[]`** — **this is a third state, not a quiet synonym for unlinked.** It means the
129
+ linkage question could not be answered for that path (it is outside the repository, it is the
130
+ repo root, there is no board directory, or it is a stale index entry that no longer exists on
131
+ disk). Report these under their own heading with the `reason` shown, and:
132
+ - **never** describe them as unlinked or add them to the unlinked totals;
133
+ - **never** include them in the `/uncharted segment` offer.
134
+
135
+ A group whose `directory_linkage.status` is `not_checked` is a different case: its *files* were
136
+ checked and are genuinely unlinked, only the directory verdict is missing. Keep offering it, but
137
+ say the directory itself could not be verified.
138
+
139
+ Saying "no board item references this" about a path we never managed to check is a false
140
+ claim of a verified absence. If a `not_checked` entry looks like it matters, the follow-up is
141
+ to investigate that path directly, not to assume the worst about it.
142
+ - **`notices[]`** — surface verbatim in the report if non-empty.
143
+
144
+ #### The `/uncharted segment` offer
145
+
146
+ Drive this off **`summary.uncharted_paths`** and `groups[]`, not `unlinked_paths` —
147
+ `unlinked_paths` includes the covered files, which must never be offered.
148
+ (`unlinked_paths` == `uncharted_paths` + `covered_paths`.)
149
+
150
+ If `summary.uncharted_paths` is `0`, note "no unlinked code found" in the report and continue to
151
+ phase 6. Otherwise present the top groups and offer `/uncharted segment`, using the numbered
152
+ form from `CLAUDE.md`'s Interaction Pattern with the free-text option last:
153
+
154
+ ```
155
+ Found <N> path(s) with no board provenance, in <M> director(y|ies):
156
+
157
+ 1. <directory> — <unlinked_count> file(s)<, entire directory if fully_unlinked>
158
+ 2. <directory> — <unlinked_count> file(s)
159
+ ...
160
+
161
+ Neither these files nor their directories are referenced by any board item, and no commit
162
+ naming a board item introduced them. How would you like to handle it?
163
+ 1. Run `/uncharted segment <directory>` on <top group> now
164
+ 2. Choose a different group to run `/uncharted segment` on
165
+ 3. Skip for now — carry on with the rest of the reconcile pass
166
+ 4. Other (describe below)
167
+ ```
168
+
169
+ List at most the top 10 groups; if there are more, say how many were not shown.
170
+
171
+ **This offer is non-blocking.** Declining it — option 3, an unrecognised answer, or no answer at
172
+ all — changes nothing about the rest of the reconcile run. Record the finding in the report and
173
+ **continue to phase 6 regardless**. Detection is report-only in the same way DoD gap detection
174
+ is: never change a board status, create a board item, or modify any file on the strength of this
175
+ phase. If the user picks option 1 or 2, finish the reconcile pass first, then hand off to
176
+ `/uncharted segment` at the end so the board is consistent before new items are written.
177
+
178
+ ### 6. Clean `project/todo.md`
84
179
  Walk the todo entries parsed in phase 1:
85
180
 
86
181
  - **Already-done entries** — if an entry references a task/story/epic whose pre-reconcile status (from the snapshot in phase 1) was already a completed status **and** whose implementation has been confirmed (phase 2), **remove the line entirely** from `project/todo.md`.
87
182
  - **Newly-reconciled entries** — entries that were commented out in phase 3 stay as `<!-- RECONCILED: ... -->`.
88
183
  - If `project/todo.md` is left with only the header, the format comment, and blank lines, delete the file.
89
184
 
90
- ### 6. Print a summary
91
- Output a reconciliation report using the format in `assets/report_format.md`.
185
+ ### 7. Print a summary
186
+ Output a reconciliation report using the format in `assets/report_format.md`, with one
187
+ additional section from phase 5 placed just before `TODO CLEANUP`:
188
+
189
+ ```
190
+ 🗺️ UNLINKED CODE (no board item for the files or their directory)
191
+ 📁 <directory> — <N> file(s) [entire directory]
192
+ 📁 <directory> — <N> file(s)
193
+ Offered `/uncharted segment`: <accepted for <directory> | declined>
194
+ Covered: <N> file(s) in <M> director(y|ies) already owned by a board item — not offered
195
+
196
+ ❓ NOT CHECKED (board linkage could not be determined — not a finding of unlinked)
197
+ <path> — <reason>
198
+ ```
199
+
200
+ Omit `UNLINKED CODE` when `summary.uncharted_paths` is 0, omit its `Covered:` line when
201
+ `summary.covered_paths` is 0, and omit `NOT CHECKED` when
202
+ `not_checked[]` is empty. Keep the two separate: merging them would report paths as unlinked
203
+ that were never actually checked.
92
204
 
93
205
  If no changes were made, print: `Board and todo.md are in sync — nothing to reconcile. ✅`