@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.
Files changed (117) hide show
  1. package/README.md +10 -7
  2. package/agents/developer.md +82 -2
  3. package/agents/scrum-master.md +215 -21
  4. package/agents/tester.md +90 -8
  5. package/hooks/on_session_end.sh +171 -20
  6. package/mcp/router/embedder.js +1 -1
  7. package/mcp/training_runner/index.js +239 -0
  8. package/mcp/training_runner/package-lock.json +1065 -0
  9. package/mcp/training_runner/package.json +15 -0
  10. package/package.json +14 -16
  11. package/scripts/check-permission-level.sh +107 -0
  12. package/scripts/check-publicignore-match.sh +122 -0
  13. package/scripts/check-worktree-liveness.sh +193 -0
  14. package/scripts/generate-rapport-manifest.sh +43 -0
  15. package/scripts/idea_manager.sh +47 -0
  16. package/scripts/install-worktree-commit-guard.sh +134 -0
  17. package/scripts/jenga-permission-level-switch.sh +109 -0
  18. package/scripts/smoke-harness.sh +139 -0
  19. package/scripts/validate-board.sh +62 -0
  20. package/scripts/with-lock.sh +158 -0
  21. package/scripts/worktree-remove-guard.sh +204 -0
  22. package/skills/clearify/SKILL.md +52 -0
  23. package/skills/close-story/SKILL.md +203 -0
  24. package/skills/close-story/scripts/check-story-closeable.sh +195 -0
  25. package/skills/close-story/scripts/compute-scope-divergence.sh +128 -0
  26. package/skills/close-story/scripts/extract-diff-stats.sh +48 -0
  27. package/skills/close-story/scripts/extract-task-diff-stats.sh +97 -0
  28. package/skills/close-story/scripts/update-task-frontmatter.sh +103 -0
  29. package/skills/commit/SKILL.md +30 -3
  30. package/skills/distribute/CONFIG_SCHEMA.md +148 -0
  31. package/skills/distribute/SKILL.md +173 -0
  32. package/skills/distribute/scripts/check-version.sh +74 -0
  33. package/skills/distribute/scripts/commit-version-bump.sh +108 -0
  34. package/skills/distribute/scripts/distribute-changes.sh +381 -0
  35. package/skills/do/SKILL.md +352 -1
  36. package/skills/do/assets/intent-vs-diff-prompt.md +69 -0
  37. package/skills/doc/assets/path-objectives.yaml +13 -0
  38. package/skills/doc-sync/SKILL.md +16 -0
  39. package/skills/doc-sync/assets/doc_targets.md +11 -0
  40. package/skills/idea/SKILL.md +56 -0
  41. package/skills/idea/assets/idea_handoff_template.md +26 -0
  42. package/skills/idea/assets/idea_template.md +3 -0
  43. package/skills/init/SKILL.md +101 -7
  44. package/skills/init/assets/directory_structure.txt +1 -0
  45. package/skills/init/assets/strategy_stub_template.md +38 -0
  46. package/skills/init/assets/workflow_template.json +1 -1
  47. package/skills/init/scripts/apply-project-visibility.sh +176 -0
  48. package/skills/init/scripts/detect-existing-codebase.sh +166 -0
  49. package/skills/init/scripts/init.sh +35 -1
  50. package/skills/jenga/SKILL.md +206 -14
  51. package/skills/jenga/scripts/board-scan.sh +238 -0
  52. package/skills/jenga/scripts/cascade-resolve.sh +297 -0
  53. package/skills/jenga/scripts/render-confirmation.sh +679 -0
  54. package/skills/jenga/scripts/render-picker.sh +439 -0
  55. package/skills/jenga/scripts/resolve-id.sh +367 -0
  56. package/skills/jenga-permission-level/SKILL.md +81 -0
  57. package/skills/proceed/SKILL.md +1 -1
  58. package/skills/publish/SKILL.md +8 -5
  59. package/skills/publish/assets/ci-contract.md +2 -2
  60. package/skills/publish/assets/ownership-matrix.md +1 -1
  61. package/skills/publish/scripts/finalize_changelog.sh +115 -0
  62. package/skills/publish/scripts/generate_release_notes.sh +475 -28
  63. package/skills/publish/scripts/npm_ci_pipeline.sh +44 -6
  64. package/skills/publish/scripts/publish_deploy.sh +38 -8
  65. package/skills/publish/scripts/run_gates.sh +2 -2
  66. package/skills/reconcile/SKILL.md +117 -5
  67. package/skills/reconcile/scripts/detect-unlinked-code.sh +741 -0
  68. package/skills/skillify/assets/init-new/assets/directory_structure.txt +5 -1
  69. package/skills/spinoff/SKILL.md +12 -7
  70. package/skills/todo/SKILL.md +2 -0
  71. package/skills/uncharted/SKILL.md +711 -0
  72. package/skills/uncharted/assets/SEGMENT_PROPOSAL_TEMPLATE.md +129 -0
  73. package/skills/uncharted/assets/UNDERSTANDING_DOC_TEMPLATE.md +160 -0
  74. package/skills/uncharted/scripts/apply-subsystem-cap.sh +573 -0
  75. package/skills/uncharted/scripts/detect-dependencies.sh +732 -0
  76. package/skills/uncharted/scripts/detect-tests.sh +553 -0
  77. package/skills/uncharted/scripts/discover-subsystems.sh +1029 -0
  78. package/skills/uncharted/scripts/enumerate-target.sh +470 -0
  79. package/skills/uncharted/scripts/import-source.sh +517 -0
  80. package/skills/uncharted/scripts/inspect-provenance.sh +573 -0
  81. package/skills/uncharted/scripts/resolve-segment-target.sh +640 -0
  82. package/skills/uncharted/scripts/run-engine.sh +655 -0
  83. package/skills/uncharted/scripts/validate-proposed-items.sh +125 -0
  84. package/skills/uncharted/scripts/write-backfilled-epics.sh +498 -0
  85. package/skills/wtf/SKILL.md +20 -0
  86. package/templates/CHANGELOG_TEMPLATE.md +13 -0
  87. package/templates/PROBLEM_RAPPORT_TEMPLATE.md +4 -1
  88. package/templates/SCRUM_BOARD_SCHEMA.md +206 -10
  89. package/templates/permission-levels/README.md +73 -0
  90. package/templates/permission-levels/level-1-locked.json +71 -0
  91. package/templates/permission-levels/level-2-guarded.json +64 -0
  92. package/templates/permission-levels/level-3-standard.json +62 -0
  93. package/templates/permission-levels/level-4-elevated.json +60 -0
  94. package/templates/permission-levels/level-5-unrestricted.json +58 -0
  95. package/skills/convert/SKILL.md +0 -124
  96. package/skills/convert/convert_cli.py +0 -235
  97. package/skills/convert/tests/sample.csv +0 -4
  98. package/skills/convert/tests/sample.json +0 -5
  99. package/skills/convert/tests/sample.jsonl +0 -3
  100. package/skills/convert/tests/sample.yaml +0 -18
  101. package/skills/convert/tests/sample_obj.csv +0 -2
  102. package/skills/convert/tests/sample_obj.json +0 -9
  103. package/skills/mirror-public/SKILL.md +0 -237
  104. package/skills/mirror-public/assets/config.json +0 -5
  105. package/skills/mirror-public/scripts/mirror.sh +0 -374
  106. package/skills/self-sync/SKILL.md +0 -73
  107. package/skills/self-sync/scripts/run.js +0 -136
  108. package/skills/train/SKILL.md +0 -116
  109. package/skills/train/assets/dashboard-templates/classifiers.html +0 -106
  110. package/skills/train/assets/dashboard-templates/nlp.html +0 -102
  111. package/skills/train/assets/dashboard-templates/transformers.html +0 -98
  112. package/skills/train/assets/results-parsers/__init__.py +0 -9
  113. package/skills/train/assets/results-parsers/classifiers.py +0 -84
  114. package/skills/train/assets/results-parsers/nlp.py +0 -88
  115. package/skills/train/assets/results-parsers/reporter.py +0 -154
  116. package/skills/train/assets/results-parsers/transformers.py +0 -120
  117. package/skills/train/train_cli.py +0 -786
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env bash
2
+ # ---------------------------------------------------------------------------
3
+ # skills/jenga/scripts/cascade-resolve.sh
4
+ #
5
+ # Cascade resolver for `/jenga`'s interactive scope-selection flow (E45).
6
+ # Given a resolved board-ID set (exact `E##` / `E##_S##` / `E##_S##_T##`
7
+ # IDs — already resolved by the picker in E45_S02_T01 or by
8
+ # `resolve-id.sh` in E45_S01_T02; this script does NOT do fuzzy-ID
9
+ # parsing), expands any epic or story selection into its full set of
10
+ # eligible descendants:
11
+ #
12
+ # - An epic selection expands to all of its stories and their tasks.
13
+ # - A story selection expands to all of its tasks.
14
+ # - Expansion only includes descendants in a Pending/eligible state —
15
+ # terminal-status descendants (Passed, Passed with remarks, Failed,
16
+ # Rejected, Blocked, Done) are excluded.
17
+ # - A descendant epic/story that has not yet been broken down on the
18
+ # board (zero stories under an epic, zero tasks under a story) is
19
+ # flagged under `undecomposed` so jenga's existing Phase 1/2
20
+ # decomposition logic can pick it up. This script never decomposes
21
+ # anything itself — it only flags the need.
22
+ #
23
+ # Per this repo's "Scripts Over Inline Logic" principle and the sibling
24
+ # board-scan.sh's own header contract, this script consumes
25
+ # board-scan.sh's JSON output as the SINGLE SOURCE OF TRUTH for board
26
+ # contents — it does not re-scan project/board/ independently.
27
+ #
28
+ # Downstream consumer: E45_S02_T02 (confirmation-tree renderer) reads
29
+ # this script's output to render the nested Epic > Story > Task
30
+ # confirmation tree with per-level counts.
31
+ #
32
+ # ---------------------------------------------------------------------------
33
+ # USAGE
34
+ # ---------------------------------------------------------------------------
35
+ # skills/jenga/scripts/cascade-resolve.sh "<comma-separated-ids>"
36
+ #
37
+ # One argument: a comma-separated list of exact, already-resolved board
38
+ # IDs (e.g. "E01,E02_S03,E04_S05_T06"). Whitespace around commas/ids is
39
+ # trimmed. IDs are matched case-sensitively against the board (board IDs
40
+ # are always uppercase per templates/SCRUM_BOARD_SCHEMA.md).
41
+ #
42
+ # ---------------------------------------------------------------------------
43
+ # OUTPUT SCHEMA (stable — downstream scripts depend on these exact names)
44
+ # ---------------------------------------------------------------------------
45
+ # stdout is a single JSON object. Nothing else is ever written to stdout.
46
+ #
47
+ # {
48
+ # "items": [ <board-scan.sh item objects — see that script's header
49
+ # for the per-item schema — for every epic/story/task
50
+ # node in the resolved tree, deduplicated> ],
51
+ # "resolved_ids": ["E01", "E01_S02", "E01_S02_T01"],
52
+ # "counts": {"epics": 1, "stories": 1, "tasks": 1},
53
+ # "undecomposed": [
54
+ # {"id": "E02", "type": "epic", "missing": "stories"},
55
+ # {"id": "E02_S01", "type": "story", "missing": "tasks"}
56
+ # ],
57
+ # "warnings": [
58
+ # {"input": "E99", "reason": "not found on board"},
59
+ # {"input": "E01_S02_T09", "reason": "excluded: terminal status (Passed)"},
60
+ # {"input": "foo", "reason": "malformed ID (does not match E##, E##_S##, or E##_S##_T## shape)"}
61
+ # ]
62
+ # }
63
+ #
64
+ # Field notes:
65
+ # - `items` includes every node actually present in the resolved tree —
66
+ # epics/stories that were directly selected or expanded into, plus
67
+ # eligible (non-terminal) descendant stories/tasks. It is NOT limited
68
+ # to leaf tasks, because the confirmation-tree renderer needs the
69
+ # full Epic > Story > Task shape, not just an execution list.
70
+ # - `resolved_ids` is the same set as `items`, as a flat list of IDs
71
+ # (convenience for callers that only need IDs).
72
+ # - `counts` reflects unique nodes of each type present in `items` —
73
+ # i.e. what the confirmation tree will actually render — not a count
74
+ # of input arguments.
75
+ # - `undecomposed` entries mean "this epic/story has zero children on
76
+ # the board at all", distinct from "this epic/story has children but
77
+ # none are eligible" (which is not flagged — there is nothing to
78
+ # decompose, the work is simply already done or blocked).
79
+ # - `warnings` covers three non-fatal per-input problems: an ID not
80
+ # found on the board, a directly-selected task/story/epic excluded
81
+ # for being in a terminal status, and a malformed ID shape. None of
82
+ # these abort the run — valid inputs are still resolved and stdout is
83
+ # still a complete, valid JSON object.
84
+ #
85
+ # ---------------------------------------------------------------------------
86
+ # ERROR HANDLING
87
+ # ---------------------------------------------------------------------------
88
+ # Exit codes:
89
+ # 0 resolution completed (stdout is always valid JSON on this path,
90
+ # even if some inputs produced warnings, and even if the result is
91
+ # an empty items/resolved_ids set because every input was invalid)
92
+ # 1 a structural failure: no argument given, board-scan.sh itself
93
+ # failed or produced unparseable output, or python3 is unavailable
94
+ # — these are real setup problems, not per-input noise
95
+ #
96
+ # ---------------------------------------------------------------------------
97
+
98
+ set -euo pipefail
99
+
100
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
101
+
102
+ if [ $# -lt 1 ] || [ -z "${1// /}" ]; then
103
+ echo "Usage: cascade-resolve.sh \"<comma-separated-ids>\"" >&2
104
+ echo "Example: cascade-resolve.sh \"E01,E02_S03,E04_S05_T06\"" >&2
105
+ exit 1
106
+ fi
107
+
108
+ RAW_IDS="$1"
109
+
110
+ # No project-dir resolution needed here: board-scan.sh (invoked below)
111
+ # already resolves JENGA_PROJECT_DIR itself (CLAUDE_PROJECT_DIR -> git
112
+ # toplevel -> cwd) and is the sole source of board contents for this
113
+ # script, per the "Scripts Over Inline Logic" / single-source-of-truth
114
+ # contract documented in its own header.
115
+ BOARD_SCAN="$SCRIPT_DIR/board-scan.sh"
116
+
117
+ if [ ! -x "$BOARD_SCAN" ]; then
118
+ echo "Error: board-scan.sh not found or not executable at $BOARD_SCAN" >&2
119
+ exit 1
120
+ fi
121
+
122
+ if ! command -v python3 >/dev/null 2>&1; then
123
+ echo "Error: python3 is required by cascade-resolve.sh" >&2
124
+ exit 1
125
+ fi
126
+
127
+ BOARD_JSON_FILE="$(mktemp)"
128
+ trap 'rm -f "$BOARD_JSON_FILE"' EXIT
129
+
130
+ if ! "$BOARD_SCAN" > "$BOARD_JSON_FILE"; then
131
+ echo "Error: board-scan.sh failed" >&2
132
+ exit 1
133
+ fi
134
+
135
+ python3 - "$RAW_IDS" "$BOARD_JSON_FILE" <<'PY'
136
+ import json
137
+ import re
138
+ import sys
139
+
140
+ raw_ids_arg = sys.argv[1]
141
+ board_json_file = sys.argv[2]
142
+
143
+ try:
144
+ with open(board_json_file, encoding="utf-8") as f:
145
+ board_items = json.load(f)
146
+ except Exception as e:
147
+ print(f"Error: could not parse board-scan.sh output as JSON: {e}", file=sys.stderr)
148
+ sys.exit(1)
149
+
150
+ # Terminal statuses, verbatim from templates/SCRUM_BOARD_SCHEMA.md's
151
+ # Status Values table. An item in one of these statuses is never
152
+ # included as an expanded/eligible descendant.
153
+ TERMINAL_STATUSES = {
154
+ "Passed",
155
+ "Passed with remarks",
156
+ "Failed",
157
+ "Rejected",
158
+ "Blocked",
159
+ "Done",
160
+ }
161
+
162
+ EPIC_RE = re.compile(r'^E\d{2}$')
163
+ STORY_RE = re.compile(r'^E\d{2}_S\d{2}$')
164
+ TASK_RE = re.compile(r'^E\d{2}_S\d{2}_T\d{2}$')
165
+
166
+ by_id = {}
167
+ stories_by_epic = {}
168
+ tasks_by_story = {}
169
+
170
+ for item in board_items:
171
+ item_id = item.get("id", "")
172
+ if not item_id:
173
+ continue
174
+ by_id[item_id] = item
175
+ if item.get("type") == "story":
176
+ epic_id = item.get("epic_id")
177
+ if epic_id:
178
+ stories_by_epic.setdefault(epic_id, []).append(item)
179
+ elif item.get("type") == "task":
180
+ story_id = item.get("story_id")
181
+ if story_id:
182
+ tasks_by_story.setdefault(story_id, []).append(item)
183
+
184
+
185
+ def classify(item_id):
186
+ if TASK_RE.match(item_id):
187
+ return "task"
188
+ if STORY_RE.match(item_id):
189
+ return "story"
190
+ if EPIC_RE.match(item_id):
191
+ return "epic"
192
+ return None
193
+
194
+
195
+ resolved = {} # id -> item, dedup-preserving insertion order
196
+ resolved_order = []
197
+ undecomposed = []
198
+ undecomposed_seen = set()
199
+ warnings = []
200
+
201
+
202
+ def add_item(item):
203
+ item_id = item["id"]
204
+ if item_id not in resolved:
205
+ resolved[item_id] = item
206
+ resolved_order.append(item_id)
207
+
208
+
209
+ def flag_undecomposed(entry_id, entry_type, missing):
210
+ key = (entry_id, missing)
211
+ if key in undecomposed_seen:
212
+ return
213
+ undecomposed_seen.add(key)
214
+ undecomposed.append({"id": entry_id, "type": entry_type, "missing": missing})
215
+
216
+
217
+ def expand_story(story_item, include_story_node=True):
218
+ """Add an eligible story node and its eligible (non-terminal) tasks.
219
+ Flags the story as undecomposed if it has zero tasks at all."""
220
+ if include_story_node:
221
+ add_item(story_item)
222
+ story_id = story_item["id"]
223
+ tasks = tasks_by_story.get(story_id, [])
224
+ if not tasks:
225
+ flag_undecomposed(story_id, "story", "tasks")
226
+ return
227
+ for task_item in tasks:
228
+ if task_item.get("status") in TERMINAL_STATUSES:
229
+ continue
230
+ add_item(task_item)
231
+
232
+
233
+ raw_inputs = [tok.strip() for tok in raw_ids_arg.split(",")]
234
+ raw_inputs = [tok for tok in raw_inputs if tok]
235
+
236
+ for input_id in raw_inputs:
237
+ item_type = classify(input_id)
238
+ if item_type is None:
239
+ warnings.append({
240
+ "input": input_id,
241
+ "reason": "malformed ID (does not match E##, E##_S##, or E##_S##_T## shape)",
242
+ })
243
+ continue
244
+
245
+ item = by_id.get(input_id)
246
+ if item is None:
247
+ warnings.append({"input": input_id, "reason": "not found on board"})
248
+ continue
249
+
250
+ if item_type == "epic":
251
+ # Always include the epic node itself for tree display, regardless
252
+ # of the epic's own status — cascading is about its descendants.
253
+ add_item(item)
254
+ stories = stories_by_epic.get(input_id, [])
255
+ if not stories:
256
+ flag_undecomposed(input_id, "epic", "stories")
257
+ continue
258
+ for story_item in stories:
259
+ if story_item.get("status") in TERMINAL_STATUSES:
260
+ continue
261
+ expand_story(story_item, include_story_node=True)
262
+
263
+ elif item_type == "story":
264
+ add_item(item)
265
+ expand_story(item, include_story_node=False)
266
+
267
+ else: # task
268
+ if item.get("status") in TERMINAL_STATUSES:
269
+ warnings.append({
270
+ "input": input_id,
271
+ "reason": f"excluded: terminal status ({item.get('status')})",
272
+ })
273
+ continue
274
+ add_item(item)
275
+
276
+ items_out = [resolved[i] for i in resolved_order]
277
+ counts = {"epics": 0, "stories": 0, "tasks": 0}
278
+ for it in items_out:
279
+ t = it.get("type")
280
+ if t == "epic":
281
+ counts["epics"] += 1
282
+ elif t == "story":
283
+ counts["stories"] += 1
284
+ elif t == "task":
285
+ counts["tasks"] += 1
286
+
287
+ output = {
288
+ "items": items_out,
289
+ "resolved_ids": resolved_order,
290
+ "counts": counts,
291
+ "undecomposed": undecomposed,
292
+ "warnings": warnings,
293
+ }
294
+
295
+ json.dump(output, sys.stdout, indent=2)
296
+ sys.stdout.write("\n")
297
+ PY