@jenga-ai/agent 1.3.0 → 2.0.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.
- package/README.md +97 -92
- package/agents/developer.md +9 -8
- package/agents/scrum-master.md +57 -23
- package/agents/tester.md +51 -5
- package/hooks/on_session_end.sh +13 -1
- package/lib/generate-agent-context.js +18 -1
- package/lib/generate-copilot-instructions.js +18 -1
- package/lib/generate-skill-allow-list.js +191 -0
- package/lib/skill-allow-list.json +43 -0
- package/package.json +18 -4
- package/scripts/apply-j-prefix.sh +230 -0
- package/scripts/consume-context-digest.sh +103 -0
- package/scripts/postinstall.js +25 -0
- package/scripts/sweep-stale-context-digests.sh +132 -0
- package/scripts/validate-board.sh +5 -0
- package/scripts/write-context-digest.sh +230 -0
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/btw/SKILL.md +1 -1
- package/skills/clearify/SKILL.md +1 -1
- package/skills/close-story/SKILL.md +78 -6
- package/skills/close-story/scripts/check-privatized.sh +345 -0
- package/skills/commit/SKILL.md +1 -1
- package/skills/continue/SKILL.md +1 -1
- package/skills/deep-dive/SKILL.md +1 -1
- package/skills/dev-done/SKILL.md +1 -1
- package/skills/distribute/SKILL.md +1 -1
- package/skills/do/SKILL.md +100 -10
- package/skills/doc/README.md +155 -0
- package/skills/doc/SKILL.md +43 -13
- package/skills/doc/authoring-notes.md +72 -0
- package/skills/doc/scripts/resolve_last_update.py +149 -0
- package/skills/doc-sync/SKILL.md +1 -1
- package/skills/dooo/SKILL.md +1 -1
- package/skills/error/SKILL.md +1 -1
- package/skills/evaluate/SKILL.md +1 -1
- package/skills/examplify/SKILL.md +1 -1
- package/skills/help/SKILL.md +1 -1
- package/skills/idea/SKILL.md +1 -1
- package/skills/improve/SKILL.md +1 -1
- package/skills/init/SKILL.md +1 -1
- package/skills/init/assets/scope-thresholds_template.json +3 -3
- package/skills/j-init/SKILL.md +168 -0
- package/skills/j-init/assets/.gitignore_template +15 -0
- package/skills/j-init/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/j-init/assets/directory_structure.txt +14 -0
- package/skills/j-init/assets/scope-thresholds_template.json +7 -0
- package/skills/j-init/assets/strategy_stub_template.md +38 -0
- package/skills/j-init/assets/test-config_template.json +4 -0
- package/skills/j-init/assets/workflow_template.json +30 -0
- package/skills/j-init/scripts/apply-project-visibility.sh +176 -0
- package/skills/j-init/scripts/detect-existing-codebase.sh +166 -0
- package/skills/j-init/scripts/init.sh +116 -0
- package/skills/jbp/SKILL.md +1 -1
- package/skills/jenga/SKILL.md +1 -1
- package/skills/jenga/scripts/render-confirmation.sh +55 -18
- package/skills/jenga-permission-level/SKILL.md +1 -1
- package/skills/lgtm/SKILL.md +1 -1
- package/skills/pi-plan/SKILL.md +1 -1
- package/skills/proceed/SKILL.md +1 -1
- package/skills/publish/SKILL.md +1 -1
- package/skills/publish/adapters/npm-ci.md +26 -4
- package/skills/publish/scripts/npm_ci_pipeline.sh +21 -1
- package/skills/reconcile/SKILL.md +1 -1
- package/skills/reconcile-origin/SKILL.md +1 -1
- package/skills/redo/SKILL.md +1 -1
- package/skills/skillify/SKILL.md +1 -1
- package/skills/spinoff/SKILL.md +1 -1
- package/skills/status/SKILL.md +1 -1
- package/skills/todo/SKILL.md +40 -3
- package/skills/todo/scripts/add_trivial_task.sh +216 -0
- package/skills/todo/scripts/update_story_tasks.py +87 -0
- package/skills/uncharted/SKILL.md +1 -1
- package/skills/wtf/SKILL.md +1 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +33 -2
- package/templates/agent-context.md.tpl +32 -9
- package/templates/copilot-instructions.md.tpl +25 -8
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ---------------------------------------------------------------------------
|
|
3
|
+
# skills/close-story/scripts/check-privatized.sh
|
|
4
|
+
#
|
|
5
|
+
# Static (run-independent) `.publicignore` membership check for a single
|
|
6
|
+
# board ticket, invoked by /close-story at both task granularity (Step 2's
|
|
7
|
+
# per-task loop) and story granularity (Step 4's finalization), per
|
|
8
|
+
# E51_S03_T03. Unlike `Merged` (skills/self-sync/scripts/mark-merged.sh) and
|
|
9
|
+
# `Publicized` (skills/mirror-public/scripts/mark-publicized.sh, E51_S03_T02),
|
|
10
|
+
# which both react to a specific run's file diff, `Privatized` has NO
|
|
11
|
+
# dependency on any run ever having occurred -- it is a pure blocklist
|
|
12
|
+
# membership test against the ticket's own derived touched-file list, per
|
|
13
|
+
# templates/SCRUM_BOARD_SCHEMA.md's "Static vs. Reactive Status Setting"
|
|
14
|
+
# section.
|
|
15
|
+
#
|
|
16
|
+
# Usage: check-privatized.sh <id> <board-file-path>
|
|
17
|
+
#
|
|
18
|
+
# <id> EST ticket id -- a task id (E##_S##_T##) or a story id
|
|
19
|
+
# (E##_S##). The anchored-grep commit matching (below)
|
|
20
|
+
# is what keeps a story id from over-matching its own
|
|
21
|
+
# children's commits, so the same script handles both
|
|
22
|
+
# granularities with no separate code path.
|
|
23
|
+
# <board-file-path> Path to the ticket's own board markdown file (task or
|
|
24
|
+
# story), used to read its current `status:` and as the
|
|
25
|
+
# write target.
|
|
26
|
+
#
|
|
27
|
+
# Touched-file derivation -- IDENTICAL technique to
|
|
28
|
+
# skills/self-sync/scripts/mark-merged.sh (reused, not reinvented, per this
|
|
29
|
+
# task's own description):
|
|
30
|
+
# 1. git log --all --no-merges --extended-regexp \
|
|
31
|
+
# --grep="<id>([^0-9_]|$)" --pretty=format:'%H'
|
|
32
|
+
# The anchored pattern (not a plain substring grep) is required so a
|
|
33
|
+
# story id like "E51_S03" does not over-match every one of its own
|
|
34
|
+
# child tasks' commits -- the character immediately following a story
|
|
35
|
+
# id in a child task's commit message is "_", which the anchor
|
|
36
|
+
# excludes.
|
|
37
|
+
# 2. Per matched commit: `git diff --name-only <sha>~1..<sha>`, falling
|
|
38
|
+
# back to `git diff-tree --no-commit-id --name-only -r <sha>` for a
|
|
39
|
+
# root commit with no parent.
|
|
40
|
+
# 3. Union (dedup) the file paths across all matched commits.
|
|
41
|
+
# 4. Zero matched commits -> nothing to check -> UNCHANGED (not an error).
|
|
42
|
+
#
|
|
43
|
+
# .publicignore matching -- ported (not sourced) from
|
|
44
|
+
# skills/mirror-public/scripts/mirror.sh's `_publicignore_rule_for` helper:
|
|
45
|
+
# directory-prefix match for trailing-"/" lines, glob match against the full
|
|
46
|
+
# relative path and the basename for everything else, skipping comments,
|
|
47
|
+
# blank lines, and "+"-prefixed include lines (which never block anything).
|
|
48
|
+
# This is a deliberate, flagged duplication of matching logic -- not a
|
|
49
|
+
# `source` of mirror.sh, which unconditionally loads config.json and
|
|
50
|
+
# requires rsync near the top of its execution, neither of which this
|
|
51
|
+
# lighter, dependency-free check needs. Same tradeoff mark-merged.sh's own
|
|
52
|
+
# header comment already documents and accepts for its COPY_SET extraction.
|
|
53
|
+
#
|
|
54
|
+
# A ticket is "fully blocklisted" iff EVERY file in its derived touched-file
|
|
55
|
+
# list matches SOME .publicignore pattern. Partial coverage leaves the
|
|
56
|
+
# ticket unchanged, no error (mirrors AC5's "no files fully covered"
|
|
57
|
+
# language, applied to the blocklist side).
|
|
58
|
+
#
|
|
59
|
+
# Precedence / mismatch logging (AC4, this script's half of it): a ticket
|
|
60
|
+
# already at `status: Publicized` that is found fully blocklisted still gets
|
|
61
|
+
# overwritten to `Privatized` (Privatized wins -- the static, always-knowable
|
|
62
|
+
# check takes precedence per the story's AC4 and
|
|
63
|
+
# templates/SCRUM_BOARD_SCHEMA.md's "Publicized / Privatized / Deployed
|
|
64
|
+
# Lifecycle Relationship" section), but the contradiction is logged to
|
|
65
|
+
# project/logs/events.json as `event: publicized_privatized_mismatch` (same
|
|
66
|
+
# array-of-objects convention already used by session_start/etc., and the
|
|
67
|
+
# same event name E51_S03_T02's own task description documents for the
|
|
68
|
+
# reverse-direction mismatch) so it's flagged for investigation rather than
|
|
69
|
+
# silently resolved. The read-modify-write against events.json is wrapped in
|
|
70
|
+
# scripts/with-lock.sh since multiple sessions may be writing to it
|
|
71
|
+
# concurrently.
|
|
72
|
+
#
|
|
73
|
+
# A ticket already at `status: Privatized` is skipped without error
|
|
74
|
+
# (idempotency) -- same defensive-skip convention mark-merged.sh uses for
|
|
75
|
+
# its own terminal status.
|
|
76
|
+
#
|
|
77
|
+
# Output contract (stdout, single line) -- consumed by /close-story's prose
|
|
78
|
+
# steps to decide whether to report a Privatized ticket:
|
|
79
|
+
# PRIVATIZED status was written (or already Privatized -- treated the
|
|
80
|
+
# same by the caller: either way the ticket ends this call at
|
|
81
|
+
# status Privatized)
|
|
82
|
+
# UNCHANGED ticket left as-is (zero matched commits, partial coverage,
|
|
83
|
+
# or already Privatized with nothing further to do)
|
|
84
|
+
#
|
|
85
|
+
# Exit codes:
|
|
86
|
+
# 0 success (covers both PRIVATIZED and UNCHANGED outcomes)
|
|
87
|
+
# 1 error (bad usage, missing file, or a status-write failure)
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
set -euo pipefail
|
|
91
|
+
IFS=$'\n\t'
|
|
92
|
+
|
|
93
|
+
die() {
|
|
94
|
+
printf 'check-privatized.sh: error: %s\n' "$*" >&2
|
|
95
|
+
exit 1
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
log() {
|
|
99
|
+
printf 'check-privatized.sh: %s\n' "$*" >&2
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
usage() {
|
|
103
|
+
cat <<'EOF'
|
|
104
|
+
Usage: check-privatized.sh <id> <board-file-path>
|
|
105
|
+
|
|
106
|
+
Static .publicignore membership check for a single ticket (task or story).
|
|
107
|
+
Prints PRIVATIZED or UNCHANGED to stdout. See script header for full
|
|
108
|
+
semantics.
|
|
109
|
+
EOF
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if [ $# -ne 2 ]; then
|
|
113
|
+
usage >&2
|
|
114
|
+
die "expected exactly 2 arguments, got $#"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
ID="$1"
|
|
118
|
+
BOARD_FILE="$2"
|
|
119
|
+
|
|
120
|
+
[ -f "$BOARD_FILE" ] || die "board file not found: $BOARD_FILE"
|
|
121
|
+
|
|
122
|
+
# -----------------------------------------------------------------------------
|
|
123
|
+
# Locate script + repo root (same symlink-resolution + repo-root derivation
|
|
124
|
+
# pattern as mark-merged.sh, so this script behaves identically whether
|
|
125
|
+
# invoked directly or via a symlink).
|
|
126
|
+
# -----------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
SCRIPT_PATH="${BASH_SOURCE[0]}"
|
|
129
|
+
while [ -h "$SCRIPT_PATH" ]; do
|
|
130
|
+
LINK_TARGET="$(readlink "$SCRIPT_PATH")"
|
|
131
|
+
case "$LINK_TARGET" in
|
|
132
|
+
/*) SCRIPT_PATH="$LINK_TARGET" ;;
|
|
133
|
+
*) SCRIPT_PATH="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)/$LINK_TARGET" ;;
|
|
134
|
+
esac
|
|
135
|
+
done
|
|
136
|
+
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
|
|
137
|
+
|
|
138
|
+
REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || true)"
|
|
139
|
+
[ -n "$REPO_ROOT" ] || die "could not locate repo root (git rev-parse failed from $SCRIPT_DIR)"
|
|
140
|
+
|
|
141
|
+
PUBLICIGNORE="$REPO_ROOT/.publicignore"
|
|
142
|
+
[ -f "$PUBLICIGNORE" ] || die ".publicignore not found at $PUBLICIGNORE"
|
|
143
|
+
|
|
144
|
+
WITH_LOCK_SCRIPT="$REPO_ROOT/scripts/with-lock.sh"
|
|
145
|
+
UPDATE_FRONTMATTER_SCRIPT="$REPO_ROOT/skills/close-story/scripts/update-task-frontmatter.sh"
|
|
146
|
+
EVENTS_FILE="$REPO_ROOT/project/logs/events.json"
|
|
147
|
+
|
|
148
|
+
[ -f "$WITH_LOCK_SCRIPT" ] || die "expected script not found: $WITH_LOCK_SCRIPT"
|
|
149
|
+
[ -f "$UPDATE_FRONTMATTER_SCRIPT" ] || die "expected script not found: $UPDATE_FRONTMATTER_SCRIPT"
|
|
150
|
+
|
|
151
|
+
# -----------------------------------------------------------------------------
|
|
152
|
+
# Frontmatter field reader -- same "line == ---" boundary-detection style
|
|
153
|
+
# already used by update-task-frontmatter.sh / mark-merged.sh.
|
|
154
|
+
# -----------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
read_frontmatter_field() {
|
|
157
|
+
local file="$1" key="$2"
|
|
158
|
+
awk -v key="$key" '
|
|
159
|
+
NR==1 && $0=="---" { infm=1; next }
|
|
160
|
+
infm && $0=="---" { exit }
|
|
161
|
+
infm && $0 ~ ("^" key ":") {
|
|
162
|
+
sub("^" key ":[ \t]*", "", $0)
|
|
163
|
+
print $0
|
|
164
|
+
exit
|
|
165
|
+
}
|
|
166
|
+
' "$file"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
trim() {
|
|
170
|
+
local s="$1"
|
|
171
|
+
s="${s#"${s%%[![:space:]]*}"}"
|
|
172
|
+
s="${s%"${s##*[![:space:]]}"}"
|
|
173
|
+
s="${s%\"}"; s="${s#\"}"
|
|
174
|
+
s="${s%\'}"; s="${s#\'}"
|
|
175
|
+
printf '%s' "$s"
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
CURRENT_STATUS="$(trim "$(read_frontmatter_field "$BOARD_FILE" "status")")"
|
|
179
|
+
|
|
180
|
+
if [ "$CURRENT_STATUS" = "Privatized" ]; then
|
|
181
|
+
log "$ID: already Privatized -- skip (idempotent no-op)"
|
|
182
|
+
echo "UNCHANGED"
|
|
183
|
+
exit 0
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# -----------------------------------------------------------------------------
|
|
187
|
+
# Touched-file derivation -- identical technique to mark-merged.sh.
|
|
188
|
+
# -----------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
commits_for_ticket() {
|
|
191
|
+
local id="$1"
|
|
192
|
+
git -C "$REPO_ROOT" log --all --no-merges --extended-regexp \
|
|
193
|
+
--grep="${id}([^0-9_]|\$)" --pretty=format:'%H' 2>/dev/null || true
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
commit_files() {
|
|
197
|
+
local sha="$1"
|
|
198
|
+
if git -C "$REPO_ROOT" rev-parse --verify -q "${sha}~1" >/dev/null 2>&1; then
|
|
199
|
+
git -C "$REPO_ROOT" diff --name-only "${sha}~1..${sha}" 2>/dev/null || true
|
|
200
|
+
else
|
|
201
|
+
git -C "$REPO_ROOT" diff-tree --no-commit-id --name-only -r "$sha" 2>/dev/null || true
|
|
202
|
+
fi
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
collect_touched_files() {
|
|
206
|
+
local id="$1"
|
|
207
|
+
local shas sha
|
|
208
|
+
shas="$(commits_for_ticket "$id")"
|
|
209
|
+
[ -n "$shas" ] || return 0
|
|
210
|
+
while IFS= read -r sha; do
|
|
211
|
+
[ -n "$sha" ] || continue
|
|
212
|
+
commit_files "$sha"
|
|
213
|
+
done <<< "$shas" | sort -u
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
TOUCHED="$(collect_touched_files "$ID")"
|
|
217
|
+
|
|
218
|
+
if [ -z "$TOUCHED" ]; then
|
|
219
|
+
log "$ID: zero matched EST-tagged commits -- left unchanged"
|
|
220
|
+
echo "UNCHANGED"
|
|
221
|
+
exit 0
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# -----------------------------------------------------------------------------
|
|
225
|
+
# .publicignore matching -- ported from mirror.sh's _publicignore_rule_for.
|
|
226
|
+
#
|
|
227
|
+
# Prints the matching .publicignore line and returns 0 on a match; returns 1
|
|
228
|
+
# with no output if nothing matched. Directory-prefix match for trailing-"/"
|
|
229
|
+
# lines, glob match against the full relative path and the basename for
|
|
230
|
+
# everything else, skipping comments/blank lines/"+"-prefixed include lines.
|
|
231
|
+
# -----------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
_publicignore_rule_for() {
|
|
234
|
+
local rel_path="$1" line pattern base dirpat
|
|
235
|
+
base="$(basename "$rel_path")"
|
|
236
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
237
|
+
case "$line" in
|
|
238
|
+
''|'#'*|'+'*) continue ;;
|
|
239
|
+
esac
|
|
240
|
+
pattern="$line"
|
|
241
|
+
case "$pattern" in
|
|
242
|
+
'- '*) pattern="${pattern#- }" ;;
|
|
243
|
+
esac
|
|
244
|
+
[ -n "$pattern" ] || continue
|
|
245
|
+
case "$pattern" in
|
|
246
|
+
*/)
|
|
247
|
+
dirpat="${pattern%/}"
|
|
248
|
+
case "$rel_path" in
|
|
249
|
+
"$dirpat"/*)
|
|
250
|
+
printf '%s\n' "$line"
|
|
251
|
+
return 0
|
|
252
|
+
;;
|
|
253
|
+
esac
|
|
254
|
+
;;
|
|
255
|
+
*)
|
|
256
|
+
case "$rel_path" in
|
|
257
|
+
$pattern)
|
|
258
|
+
printf '%s\n' "$line"
|
|
259
|
+
return 0
|
|
260
|
+
;;
|
|
261
|
+
esac
|
|
262
|
+
case "$base" in
|
|
263
|
+
$pattern)
|
|
264
|
+
printf '%s\n' "$line"
|
|
265
|
+
return 0
|
|
266
|
+
;;
|
|
267
|
+
esac
|
|
268
|
+
;;
|
|
269
|
+
esac
|
|
270
|
+
done < "$PUBLICIGNORE"
|
|
271
|
+
return 1
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
# True (0) iff every line of $1 (touched files, newline-separated) matches
|
|
275
|
+
# some .publicignore pattern.
|
|
276
|
+
is_fully_blocklisted() {
|
|
277
|
+
local touched="$1"
|
|
278
|
+
local line
|
|
279
|
+
while IFS= read -r line; do
|
|
280
|
+
[ -n "$line" ] || continue
|
|
281
|
+
_publicignore_rule_for "$line" >/dev/null || return 1
|
|
282
|
+
done <<< "$touched"
|
|
283
|
+
return 0
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if ! is_fully_blocklisted "$TOUCHED"; then
|
|
287
|
+
log "$ID: not fully covered by .publicignore -- left unchanged"
|
|
288
|
+
echo "UNCHANGED"
|
|
289
|
+
exit 0
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
# -----------------------------------------------------------------------------
|
|
293
|
+
# Fully blocklisted. Precedence check against a contradictory Publicized
|
|
294
|
+
# status (AC4) before writing.
|
|
295
|
+
# -----------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
if [ "$CURRENT_STATUS" = "Publicized" ]; then
|
|
298
|
+
log "$ID: WARNING -- ticket is status: Publicized but every touched file matches .publicignore; Privatized takes precedence (overwriting), logging mismatch"
|
|
299
|
+
|
|
300
|
+
TOUCHED_JSON_LIST="$(printf '%s\n' "$TOUCHED" | awk '
|
|
301
|
+
BEGIN { printf "[" ; first=1 }
|
|
302
|
+
{ if (!first) printf ","; printf "\"%s\"", $0; first=0 }
|
|
303
|
+
END { printf "]" }
|
|
304
|
+
')"
|
|
305
|
+
[ -n "$TOUCHED_JSON_LIST" ] || TOUCHED_JSON_LIST="[]"
|
|
306
|
+
|
|
307
|
+
MISMATCH_EVENT=$(printf '{"event": "publicized_privatized_mismatch", "agent": "developer", "id": "%s", "file": "%s", "touched_files": %s, "date": "%s"}' \
|
|
308
|
+
"$ID" "$BOARD_FILE" "$TOUCHED_JSON_LIST" "$(date -u +%Y-%m-%dT%H:%M:%SZ)")
|
|
309
|
+
|
|
310
|
+
if [ -f "$EVENTS_FILE" ]; then
|
|
311
|
+
"$WITH_LOCK_SCRIPT" "$EVENTS_FILE" -- bash -c '
|
|
312
|
+
set -euo pipefail
|
|
313
|
+
events_file="$1"
|
|
314
|
+
new_event="$2"
|
|
315
|
+
tmp="$(mktemp)"
|
|
316
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
317
|
+
python3 -c "
|
|
318
|
+
import json, sys
|
|
319
|
+
path = sys.argv[1]
|
|
320
|
+
new_event = json.loads(sys.argv[2])
|
|
321
|
+
with open(path) as f:
|
|
322
|
+
data = json.load(f)
|
|
323
|
+
data.append(new_event)
|
|
324
|
+
with open(path, \"w\") as f:
|
|
325
|
+
json.dump(data, f, indent=2)
|
|
326
|
+
f.write(\"\n\")
|
|
327
|
+
" "$events_file" "$new_event"
|
|
328
|
+
else
|
|
329
|
+
echo "check-privatized.sh: python3 not available -- could not append mismatch event" >&2
|
|
330
|
+
rm -f "$tmp"
|
|
331
|
+
exit 1
|
|
332
|
+
fi
|
|
333
|
+
' _ "$EVENTS_FILE" "$MISMATCH_EVENT" || log "WARNING: failed to log publicized_privatized_mismatch event for $ID (continuing with status overwrite)"
|
|
334
|
+
else
|
|
335
|
+
log "WARNING: $EVENTS_FILE not found -- could not log publicized_privatized_mismatch event"
|
|
336
|
+
fi
|
|
337
|
+
fi
|
|
338
|
+
|
|
339
|
+
log "$ID: fully covered by .publicignore -- writing status: Privatized ($BOARD_FILE)"
|
|
340
|
+
if ! "$WITH_LOCK_SCRIPT" "$BOARD_FILE" -- bash "$UPDATE_FRONTMATTER_SCRIPT" "$BOARD_FILE" status Privatized; then
|
|
341
|
+
die "failed to write status: Privatized for $ID ($BOARD_FILE)"
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
echo "PRIVATIZED"
|
|
345
|
+
exit 0
|
package/skills/commit/SKILL.md
CHANGED
package/skills/continue/SKILL.md
CHANGED
package/skills/dev-done/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: distribute
|
|
2
|
+
name: j:distribute
|
|
3
3
|
description: Distribute Jenga AI framework files from this private monorepo to one or more consuming projects via the local filesystem. Manages release type selection, version bumping, dry-run preview, per-target file copy, and a post-distribution git commit.
|
|
4
4
|
keywords:
|
|
5
5
|
- distribute
|
package/skills/do/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: do
|
|
2
|
+
name: j:do
|
|
3
3
|
description: Execute tasks from the scrum board. Reads from project/todo.md, resolves each entry to its full scrum board context, and drives the developer agent through implementation with the correct sender object and communication contract. Loops until all selected tasks are done or the user exits.
|
|
4
4
|
keywords:
|
|
5
5
|
- do
|
|
@@ -16,6 +16,14 @@ metadata:
|
|
|
16
16
|
|
|
17
17
|
# Do — Execute Scrum Board Tasks
|
|
18
18
|
|
|
19
|
+
## `--trivial` Flag
|
|
20
|
+
|
|
21
|
+
**Syntax:** `/do <id> --trivial` — a dispatch-time override, distinct from `/todo --trivial` (a creation-time flag documented in `skills/todo/SKILL.md`). Where `/todo --trivial` writes a brand-new task straight to `execution_scope: inline`, `/do <id> --trivial` overrides an **already-existing** task's `execution_scope` — whatever it currently is, including absent (legacy tasks with no execution-scope fields at all) — to `inline` at the moment it's dispatched. See `### 4.1.5. \`--trivial\` Dispatch-Time Override` below for the full mechanics.
|
|
22
|
+
|
|
23
|
+
**No softer fallback tier — hard fallback to the full pipeline instead.** Unlike `light` scope (which sits between `inline` and `task`), `--trivial` always forces `inline` directly, with no intermediate tier to fall back to first. To compensate, a `--trivial`-forced run that fails `scripts/smoke-harness.sh` or shows detected scope creep mid-run automatically re-routes to the full `task` pipeline (worktree + developer + tester) via the same `#### Fallback to Full Task-Scope Pipeline` procedure the `light`-tier fallback uses — see `### 4.2`'s failure-handling branches and the shared Fallback subsection under `### 4.3`.
|
|
24
|
+
|
|
25
|
+
**Human-only, same as `/todo --trivial`.** `--trivial` is invoked by a human typing `/do <id> --trivial` — it is never applied autonomously by `/jenga` or the scrum-master's own breakdown/dispatch passes.
|
|
26
|
+
|
|
19
27
|
## Instructions
|
|
20
28
|
|
|
21
29
|
### 0. Load threshold config
|
|
@@ -318,9 +326,40 @@ Before invoking the developer, check whether this task was manually scoped by a
|
|
|
318
326
|
```
|
|
319
327
|
Then proceed to step 4.2.
|
|
320
328
|
|
|
329
|
+
### 4.1.5. `--trivial` Dispatch-Time Override
|
|
330
|
+
|
|
331
|
+
After override validation (step 4.1) and before branching on `execution_scope` in step 4.2, check whether this invocation was `/do <id> --trivial`.
|
|
332
|
+
|
|
333
|
+
1. **Detect the flag.** If the task was invoked as `/do <id>` with no `--trivial` flag, skip this entire section and proceed directly to `### 4.2`.
|
|
334
|
+
|
|
335
|
+
2. **If `--trivial` is present**, read the task's current `execution_scope` from frontmatter. Treat an absent value as `task`, per the epic's backward-compatibility rule (a task that omits `execution_scope` is treated as `execution_scope: task`). Call this the **prior tier** — this covers both tasks nobody flagged as trivial at creation (an already-assigned `story`/`task`/`light` tier) and legacy tasks with no `execution_scope` set at all.
|
|
336
|
+
|
|
337
|
+
3. **Overwrite `execution_scope` to `inline`** in the task's frontmatter, unconditionally — `--trivial` always forces `inline`, never a softer "lightest safe tier."
|
|
338
|
+
|
|
339
|
+
4. **Record the override for audit**, reusing the existing `jenga_assigned` / `override_justification` pairing already defined in `templates/SCRUM_BOARD_SCHEMA.md` for exactly this situation ("scope overridden by a human"), rather than inventing a new field:
|
|
340
|
+
- Set `jenga_assigned: false` (if not already `false`).
|
|
341
|
+
- Set (or append to, if already present) `override_justification`:
|
|
342
|
+
```
|
|
343
|
+
override_justification: "/do --trivial dispatch-time override on <date>: execution_scope forced from '<prior_tier>' to 'inline' by human operator."
|
|
344
|
+
```
|
|
345
|
+
- Also set (or append to) `scope_rationale`, mirroring the phrasing convention `skills/todo/scripts/add_trivial_task.sh` already uses for the creation-time flag, so both audit fields agree on the prior tier:
|
|
346
|
+
```
|
|
347
|
+
scope_rationale: "forced inline via /do --trivial (dispatch-time override); prior execution_scope was '<prior_tier>'"
|
|
348
|
+
```
|
|
349
|
+
- Emit a non-fatal log line (styled like 4.2's `AUTO-CORRECTION` message):
|
|
350
|
+
```
|
|
351
|
+
TRIVIAL OVERRIDE [<task_id>]: execution_scope forced from "<prior_tier>" to "inline" via --trivial dispatch-time override.
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
5. **Set an in-session marker** (this dispatch is a `--trivial`-forced run) — this does not need to be persisted to frontmatter; it only needs to survive for the remainder of this `/do` invocation. This marker is distinct from an organically-assigned `execution_scope: inline` task (one the scrum-master or `/jenga` assigned `inline` to directly, with no `--trivial` involved) because its failure handling differs — see `### 4.2`'s failure-handling steps below. Do not confuse a `--trivial`-forced run with an organic `inline` task when applying those steps.
|
|
355
|
+
|
|
356
|
+
6. **Proceed to `### 4.2. Inline Execution Path`** with `execution_scope` now `inline`. Everything else about inline execution (implementation, smoke test invocation, commit convention) is identical between an organic `inline` task and a `--trivial`-forced one — only the two failure-handling branches in `### 4.2` differ, per the marker set in step 5 above.
|
|
357
|
+
|
|
358
|
+
**Precedence with the locked-task dispatch guard.** If `crucial_level: locked` (checked by `### 4.2`'s locked-task dispatch guard, which always runs and always wins), `--trivial` is redundant but harmless — the task was already going to be forced `inline`. The locked-task guard's own audit fields take precedence for that correction; do not double-write conflicting `override_justification` text. A `locked` task's fallback behavior remains "halt and report," never the `--trivial` fallback below, regardless of whether `--trivial` was also passed.
|
|
359
|
+
|
|
321
360
|
### 4.2. Inline Execution Path (execution_scope: inline)
|
|
322
361
|
|
|
323
|
-
After resolving the task context (step 4)
|
|
362
|
+
After resolving the task context (step 4), passing override validation (step 4.1), and applying the `--trivial` dispatch-time override if present (step 4.1.5), read `execution_scope` from the task frontmatter.
|
|
324
363
|
|
|
325
364
|
**Locked-task dispatch guard (defense-in-depth).** Before branching on `execution_scope` below, read `crucial_level` from the task frontmatter (per `templates/SCRUM_BOARD_SCHEMA.md`'s Crucial Flag Fields). If `crucial_level: locked`:
|
|
326
365
|
|
|
@@ -349,12 +388,14 @@ After resolving the task context (step 4) and passing override validation (step
|
|
|
349
388
|
WARNING [<task_id>]: scripts/smoke-harness.sh not found. Smoke test skipped (stub pass).
|
|
350
389
|
```
|
|
351
390
|
4. **If the smoke test exits non-zero**:
|
|
352
|
-
-
|
|
353
|
-
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
391
|
+
- **If this is a `--trivial`-forced run** (marker set in step 4.1.5 — and `crucial_level` is not `locked`, which never falls back, per 4.1.5's precedence note): do NOT write `status: Failed`. `--trivial` always forces `inline` with no softer "lightest safe tier" to fall back to first, so a smoke-harness failure here goes straight to the shared `#### Fallback to Full Task-Scope Pipeline` procedure below (origin: `trivial`). Do not proceed with the remaining inline steps below — the Fallback procedure takes over from here.
|
|
392
|
+
- **Otherwise** (an organically-assigned `inline` task, `--trivial` not involved): behavior is unchanged from before —
|
|
393
|
+
- Write `status: Failed` to the task's frontmatter.
|
|
394
|
+
- Emit:
|
|
395
|
+
```
|
|
396
|
+
INLINE TASK FAILED [<task_id>]: smoke test returned non-zero exit code. Task marked Failed. Halting.
|
|
397
|
+
```
|
|
398
|
+
- Do not commit. Do not proceed to the next task.
|
|
358
399
|
5. **If the smoke test passes**:
|
|
359
400
|
- Commit the changes using the standard commit convention (`task(<task_id>): <short description>`) via `/commit` in inline mode (E32_S04_T03).
|
|
360
401
|
- Run the **Intent-vs-Diff Check** (see `### 5.1. Intent-vs-Diff Check` below) for this task.
|
|
@@ -365,9 +406,58 @@ After resolving the task context (step 4) and passing override validation (step
|
|
|
365
406
|
7. `inline` tasks always have `needs_docs: false` — skip plan and summary documentation for the implemented task.
|
|
366
407
|
8. Continue to `### 6. Verify documentation`, then `### 7. After successful completion`.
|
|
367
408
|
|
|
368
|
-
If the implementation cannot be completed inline (scope is larger than anticipated
|
|
409
|
+
If the implementation cannot be completed inline (scope is larger than anticipated — detected scope creep mid-run):
|
|
410
|
+
- **If this is a `--trivial`-forced run** (and `crucial_level` is not `locked`): invoke the shared `#### Fallback to Full Task-Scope Pipeline` procedure below (origin: `trivial`) — the same procedure the smoke-harness-failure branch above uses, not a second bespoke re-route.
|
|
411
|
+
- **Otherwise** (an organically-assigned `inline` task): abort and re-route to the normal developer path (step 5), unchanged from before.
|
|
412
|
+
- **Regardless of `--trivial`**, if `crucial_level: locked`, do not re-route via either path above; re-attempt inline or halt and report, per the locked-task dispatch guard above.
|
|
369
413
|
|
|
370
|
-
**If `execution_scope` is
|
|
414
|
+
**If `execution_scope` is `light`** (and `crucial_level` is not `locked` — the locked-task dispatch guard above already ran and takes precedence over any scope check), proceed to `### 4.3. Light Execution Path` below instead of step 5.
|
|
415
|
+
|
|
416
|
+
**If `execution_scope` is not `inline` and not `light`** (or is absent / `task` / `story` / `epic`) **and `crucial_level` is not `locked`**, proceed to step 5 (invoke the developer agent) as normal.
|
|
417
|
+
|
|
418
|
+
### 4.3. Light Execution Path (execution_scope: light)
|
|
419
|
+
|
|
420
|
+
After resolving the task context (step 4), passing override validation (step 4.1), and applying the `--trivial` dispatch-time override if present (step 4.1.5 — note `--trivial` always forces `inline`, so a `light`-scoped task only reaches this section if `--trivial` was *not* passed), if `execution_scope: light` and `crucial_level` is not `locked` (per the locked-task dispatch guard in 4.2, which runs first and always wins), route the task through this path instead of the full `task`-scope pipeline in step 5.
|
|
421
|
+
|
|
422
|
+
`light` sits between `inline` and `task`: unlike `inline`, it spawns a real developer subagent (so it can handle small branching logic that inline's main-session execution isn't suited for); unlike `task`, it does not create a dedicated worktree and does not invoke the tester as a separate step.
|
|
423
|
+
|
|
424
|
+
1. **Spawn a developer subagent** (Agent tool, `subagent_type: "developer"`) with the same sender object and context payload as step 5 would use, but with an explicit instruction added to the dispatch prompt: **do not create a worktree** — implement directly against the current checkout (the session's existing working tree), not an isolated `.claude/worktrees/<slug>` copy. This is the one concrete difference from the step-5 `task` path: everything else about how the subagent implements the task (reading the task file, following acceptance criteria, following repo conventions) is unchanged.
|
|
425
|
+
|
|
426
|
+
2. **After the developer subagent reports implementation complete**, run the smoke test harness using the same invocation convention as `### 4.2. Inline Execution Path`:
|
|
427
|
+
- Run `bash scripts/smoke-harness.sh <changed_file>...`, passing the paths the subagent changed. With no arguments the harness infers them from `git diff --name-only HEAD`. It exits `0` on pass and `1` on failure.
|
|
428
|
+
- If `scripts/smoke-harness.sh` does not exist, log a warning and treat the result as a pass:
|
|
429
|
+
```
|
|
430
|
+
WARNING [<task_id>]: scripts/smoke-harness.sh not found. Smoke test skipped (stub pass).
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
3. **If the smoke test passes**:
|
|
434
|
+
- The developer subagent self-verifies the implementation against the task's acceptance criteria. No tester subagent is invoked for a `light`-scoped task — this is a deliberate, documented exception to "the tester is the sole status-writer" (`agents/tester.md`), mirroring the same exception already established for `inline` scope in step 5 of `### 4.2`. Since no tester runs, the developer/orchestrator is the one who writes the terminal status for a `light`-scoped task.
|
|
435
|
+
- Commit the changes using the standard commit convention (`task(<task_id>): <short description>`) via `/commit`.
|
|
436
|
+
- Run the **Intent-vs-Diff Check** (see `### 5.1. Intent-vs-Diff Check` below) for this task.
|
|
437
|
+
- Write `status: Passed` and `date_completed: <today>` to the task's frontmatter if self-verification passes.
|
|
438
|
+
- Remove the task from `project/todo.md`.
|
|
439
|
+
- Continue to `### 6. Verify documentation`, then `### 7. After successful completion`.
|
|
440
|
+
|
|
441
|
+
4. **If the smoke test fails (non-zero exit)**: do NOT write `status: Failed` and do NOT halt. Instead, invoke `#### Fallback to Full Task-Scope Pipeline` below (origin: `light`).
|
|
442
|
+
|
|
443
|
+
#### Fallback to Full Task-Scope Pipeline
|
|
444
|
+
|
|
445
|
+
This is a self-contained, reusable procedure with two current callers — `### 4.2`'s `--trivial`-forced inline failure branches (origin: `trivial`) and `### 4.3`'s `light`-scope smoke-harness failure (origin: `light`) — given a task that was attempted under a reduced-overhead execution scope and failed its smoke-harness check (or, for `trivial`, showed detected scope creep mid-run), do the following. The only thing that varies by caller is the notice text in step 5; steps 1–4 and 6 are identical regardless of origin.
|
|
446
|
+
|
|
447
|
+
1. **Do not mark the task `Failed`.** A smoke-harness failure (or detected scope creep) under a reduced-overhead scope means the scope was too small for the task, not that the task itself is unworkable — the correct response is to retry under full isolation, not to reject the work.
|
|
448
|
+
2. **Create a worktree** for the task, named `<E##_S##_T##-short-slug>` per standard Worktree Management conventions, if one does not already exist for this task. (A task dispatched under `light` scope, or forced `inline` via `--trivial`, never had one — both premises skip worktree creation — so this step always creates a fresh worktree in that case.)
|
|
449
|
+
3. **Spawn a developer subagent** in that worktree and have it pick up from the current state of the code (the changes already made by the reduced-overhead attempt are still present in the working tree / already committed, if any commit occurred — the subagent continues from there rather than starting over).
|
|
450
|
+
4. **Invoke the tester agent** per the normal `### 5. Invoke the developer agent` flow's contract — full sender object, commit SHAs, worktree path. The tester is responsible for the terminal status write, exactly as in the standard `task`-scope pipeline.
|
|
451
|
+
5. **Emit a clear, non-fatal fallback notice** to the user/orchestrator, using the message matching the caller's origin:
|
|
452
|
+
- origin `light`:
|
|
453
|
+
```
|
|
454
|
+
LIGHT SCOPE FALLBACK [<task_id>]: smoke test failed; re-routing to full task-scope pipeline (worktree + developer + tester).
|
|
455
|
+
```
|
|
456
|
+
- origin `trivial`:
|
|
457
|
+
```
|
|
458
|
+
TRIVIAL OVERRIDE FALLBACK [<task_id>]: smoke test failed (or scope creep detected); re-routing to full task-scope pipeline (worktree + developer + tester).
|
|
459
|
+
```
|
|
460
|
+
6. Resume normal `task`-scope processing (steps 6–8 below) once the tester returns a verdict.
|
|
371
461
|
|
|
372
462
|
### 5. Invoke the developer agent
|
|
373
463
|
Pass the following to the developer agent:
|