@jenga-ai/agent 3.4.0 → 3.6.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 +85 -78
- package/agents/developer.md +1 -1
- package/agents/scrum-master.md +20 -2
- package/agents/tester.md +3 -3
- package/hooks/on_session_end.sh +5 -5
- package/lib/generate-agent-context.js +2 -2
- package/lib/generate-copilot-hooks.js +1 -1
- package/lib/generate-skill-allow-list.js +37 -3
- package/lib/mirror.js +1 -1
- package/lib/postinstall-manifest.js +1 -1
- package/lib/skill-allow-list.json +2 -2
- package/mcp/help/index.js +8 -17
- package/mcp/help/scan.js +73 -0
- package/package.json +6 -1
- package/project/app/api/parsers/knowledge-graph.js +100 -9
- package/project/app/api/routes/health.js +36 -0
- package/project/app/api/scripts/capture-snapshot.js +9 -6
- package/project/app/ui/dist/assets/{index-CdK3Qrep.css → index-BVR_7Owg.css} +1 -1
- package/project/app/ui/dist/assets/index-CtU2xLQm.js +104 -0
- package/project/app/ui/dist/index.html +2 -2
- package/project/app/ui/package.json +4 -0
- package/project/app/ui/scripts/build-snapshot-html.cjs +63 -2
- package/scripts/acquire-concurrency-slot.sh +1 -1
- package/scripts/apply-j-prefix.sh +46 -5
- package/scripts/audit-twin-divergence.sh +73 -5
- package/scripts/build-pages-site.sh +1 -1
- package/scripts/check-public-playbook-steps.sh +158 -52
- package/scripts/check-publicignore-match.sh +2 -2
- package/scripts/compute-deploy-reconcile.sh +5 -5
- package/scripts/delete-bare-skill-dirs.sh +330 -0
- package/scripts/generate-legacy-shipped-paths.js +2 -2
- package/scripts/idea_manager.sh +258 -3
- package/scripts/mark-deployed.sh +2 -2
- package/scripts/populate-knowledge-graph.entity-resolution.test.js +254 -0
- package/scripts/populate-knowledge-graph.js +213 -5
- package/scripts/populate-knowledge-graph.staleness.test.js +130 -0
- package/scripts/postinstall.js +1 -1
- package/scripts/repoint-skill-refs.sh +539 -0
- package/scripts/todo_manager.sh +1 -1
- package/scripts/verify-legacy-seed-reconcile.sh +10 -10
- package/scripts/verify-postinstall-reconcile.sh +7 -7
- package/scripts/write-context-digest.sh +1 -1
- package/skills/j-clearify/SKILL.md +2 -2
- package/skills/j-close-story/scripts/check-privatized.sh +4 -4
- package/skills/j-distribute/CONFIG_SCHEMA.md +82 -5
- package/skills/j-do/SKILL.md +101 -17
- package/skills/j-doc-sync/SKILL.md +1 -0
- package/skills/j-gitignore/SKILL.md +157 -0
- package/skills/j-gitignore/assets/jenga-paths.txt +50 -0
- package/skills/j-gitignore/scripts/_catalog.sh +105 -0
- package/skills/j-gitignore/scripts/audit-gitignore.sh +194 -0
- package/skills/j-gitignore/scripts/repair-gitignore.sh +226 -0
- package/skills/j-gitignore/scripts/untrack-jenga-files.sh +210 -0
- package/skills/j-idea/SKILL.md +78 -6
- package/skills/j-idea/assets/idea_template.md +1 -1
- package/skills/j-improve/SKILL.md +1 -1
- package/skills/j-init/SKILL.md +53 -14
- package/skills/j-init/assets/.gitignore_template +1 -2
- package/skills/j-init/assets/scope-thresholds_template.json +5 -2
- package/skills/j-init/scripts/apply-scaffold-visibility.sh +192 -0
- package/skills/j-init/scripts/init.sh +22 -8
- package/skills/j-playbook/SKILL.md +1 -1
- package/skills/j-publish/SKILL.md +1 -1
- package/skills/j-publish/adapters/npm-ci.md +6 -1
- package/skills/j-publish/adapters/npm.md +1 -1
- package/skills/j-publish/scripts/generate_release_notes.sh +1 -1
- package/skills/j-publish/scripts/npm_stage_inspect.sh +61 -0
- package/skills/j-reconcile/SKILL.md +2 -2
- package/skills/j-reconcile/scripts/detect-unlinked-code.sh +11 -11
- package/skills/j-redo/SKILL.md +1 -1
- package/skills/j-skillify/assets/init-new/assets/.gitignore_template +1 -2
- package/skills/j-spinoff/SKILL.md +1 -1
- package/skills/j-status/SKILL.md +15 -0
- package/skills/j-todo/SKILL.md +3 -1
- package/skills/j-uncharted/SKILL.md +55 -8
- package/skills/j-uncharted/assets/NODE_QUESTION_TEMPLATE.md +69 -0
- package/skills/j-uncharted/scripts/detect-dependencies.sh +1 -1
- package/skills/j-uncharted/scripts/detect-tests.sh +1 -1
- package/skills/j-uncharted/scripts/elicitation-state.sh +46 -8
- package/skills/j-uncharted/scripts/validate-proposed-items.sh +1 -1
- package/skills/j-wtf/SKILL.md +1 -1
- package/skills/jenga/SKILL.md +43 -9
- package/skills/jenga/playbooks/board-hygiene.json +32 -0
- package/skills/jenga/playbooks/schema.json +73 -6
- package/skills/jenga/playbooks/understand-then-commit.json +19 -0
- package/skills/jenga/scripts/load-nl-catalog.sh +1 -1
- package/skills/jenga/scripts/load-playbooks.sh +23 -11
- package/skills/jenga/scripts/match-playbook.sh +1 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +14 -1
- package/templates/SKILL_TEMPLATE.md +12 -0
- package/templates/permission-levels/level-4-elevated.json +1 -1
- package/templates/permission-levels/level-5-unrestricted.json +1 -1
- package/templates/playbook-types.json +34 -6
- package/project/app/ui/dist/assets/index-7fj-vllY.js +0 -104
- package/scripts/generate-j-alias.sh +0 -333
- package/skills/j-dev-done/SKILL.md +0 -53
- package/skills/j-dev-done/scripts/classify-commit-outcome.sh +0 -114
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/delete-bare-skill-dirs.sh — delete the bare-name skill directories and
|
|
3
|
+
# rewrite their surviving j-<name> twins' frontmatter (E50_S15).
|
|
4
|
+
#
|
|
5
|
+
# Purpose
|
|
6
|
+
# -------
|
|
7
|
+
# Performs E50_S15's own irreversible step: for every skills/<name>/ that still has
|
|
8
|
+
# its original, pre-`j-` "bare" directory form alongside its canonical
|
|
9
|
+
# skills/j-<name>/ twin, (1) delete the bare directory outright and (2) rewrite the
|
|
10
|
+
# twin's SKILL.md frontmatter `name:` field from the transitional `j.j-<name>` form
|
|
11
|
+
# to the settled `j.<name>` form (E50_S10's contract). Both steps land together for
|
|
12
|
+
# every entry in one script run: renaming the twin first (while the bare directory
|
|
13
|
+
# still exists) would leave two directories both claiming `j.<name>`; deleting the
|
|
14
|
+
# bare directory first would leave no directory claiming it until the rename
|
|
15
|
+
# catches up. Neither intermediate state is ever produced by --apply, because this
|
|
16
|
+
# script performs every rewrite before it performs any delete (see step 4 below),
|
|
17
|
+
# and only after that first pass completes clean does it touch a filesystem entry.
|
|
18
|
+
#
|
|
19
|
+
# This script does NOT invoke `git commit`. E50_S15_T04 (the sole task allowed to
|
|
20
|
+
# run --apply) is responsible for staging and committing the resulting working-tree
|
|
21
|
+
# state as a single atomic commit immediately afterward — mirroring
|
|
22
|
+
# scripts/repoint-skill-refs.sh and scripts/apply-j-prefix.sh, neither of which
|
|
23
|
+
# commits on the caller's behalf either. Keeping VCS operations at the call site
|
|
24
|
+
# (not buried inside this script) keeps the script's own contract to "leave the
|
|
25
|
+
# working tree in the correct final state or abort with zero writes" — it does not
|
|
26
|
+
# also have to reason about repo-wide staging concerns (what else might already be
|
|
27
|
+
# staged, whether a lock file needs to be excluded, etc).
|
|
28
|
+
#
|
|
29
|
+
# Modes
|
|
30
|
+
# -----
|
|
31
|
+
# --dry-run Report the full computed delete set and, for each entry, the exact
|
|
32
|
+
# frontmatter line change planned. Nothing is written.
|
|
33
|
+
# --apply Perform the deletion + rename in place. Requires the live count to
|
|
34
|
+
# still match EXPECTED_DELETE_COUNT (see below) or the run aborts
|
|
35
|
+
# with zero filesystem writes.
|
|
36
|
+
#
|
|
37
|
+
# Exactly one of --dry-run/--apply is required. Either mode always runs the delta
|
|
38
|
+
# check and the count cross-check FIRST (see below) before touching or reporting on
|
|
39
|
+
# any file.
|
|
40
|
+
#
|
|
41
|
+
# Usage
|
|
42
|
+
# -----
|
|
43
|
+
# scripts/delete-bare-skill-dirs.sh --dry-run
|
|
44
|
+
# scripts/delete-bare-skill-dirs.sh --apply
|
|
45
|
+
#
|
|
46
|
+
# Delete-set computation — never hardcoded
|
|
47
|
+
# ------------------------------------------
|
|
48
|
+
# Computed fresh from disk on every invocation: every directory directly under
|
|
49
|
+
# skills/ that (a) is not itself "j-"-prefixed, (b) is not one of the three
|
|
50
|
+
# permanent exceptions (jenga, jenga-permission-level, index — E50_S06/E50), and
|
|
51
|
+
# (c) has a matching skills/j-<name>/ twin on disk. E50_S15_T01 already showed the
|
|
52
|
+
# live tree can drift from the story's authoring-time snapshot (6 orphan
|
|
53
|
+
# j-<name>-only directories were added after the story was written) — a hardcoded
|
|
54
|
+
# name list would silently miss a future drift the exact same way, so the set is
|
|
55
|
+
# always recomputed, never literal.
|
|
56
|
+
#
|
|
57
|
+
# Count cross-check
|
|
58
|
+
# ------------------
|
|
59
|
+
# EXPECTED_DELETE_COUNT below is the one hardcoded number in this script — not a
|
|
60
|
+
# name list, a single integer recorded by E50_S15_T01's live pre-flight recompute
|
|
61
|
+
# (2026-09-19: 40). If the live computed delete-set size does not equal this
|
|
62
|
+
# number, the script aborts with a non-zero exit and performs zero filesystem
|
|
63
|
+
# writes, in either mode. This catches the case where skills/ drifted again
|
|
64
|
+
# between T01's recompute and this script actually running --apply.
|
|
65
|
+
#
|
|
66
|
+
# Bare-vs-twin delta check
|
|
67
|
+
# --------------------------
|
|
68
|
+
# Before computing the delete set, every bare (non-"j-"-prefixed, non-exception)
|
|
69
|
+
# directory under skills/ MUST have a skills/j-<name>/ twin on disk. Any unpaired
|
|
70
|
+
# bare directory aborts the run (naming it) before any write — mirrors
|
|
71
|
+
# scripts/repoint-skill-refs.sh's identical guard.
|
|
72
|
+
#
|
|
73
|
+
# Frontmatter rewrite
|
|
74
|
+
# ---------------------
|
|
75
|
+
# For every delete-set entry <name>, skills/j-<name>/SKILL.md's frontmatter `name:`
|
|
76
|
+
# field is rewritten from `j.j-<name>` to `j.<name>`. A twin whose frontmatter does
|
|
77
|
+
# not already read exactly `j.j-<name>` is treated as an error (aborts before any
|
|
78
|
+
# write) rather than silently skipped or forced — the whole point of this
|
|
79
|
+
# cross-check is that any twin already in an unexpected state must be looked at by
|
|
80
|
+
# a human, not silently overwritten or silently left behind.
|
|
81
|
+
#
|
|
82
|
+
# Never touches the generated mirrors
|
|
83
|
+
# --------------------------------------
|
|
84
|
+
# This script only ever reads/writes under skills/. It has no code path that
|
|
85
|
+
# touches .claude/skills/ or .agents/skills/ — those are regenerated by
|
|
86
|
+
# /self-sync (E50_S15_T05), never hand-edited here, per CLAUDE.md's standing
|
|
87
|
+
# read-only-mirror rule.
|
|
88
|
+
|
|
89
|
+
set -euo pipefail
|
|
90
|
+
|
|
91
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
92
|
+
# This task's AC requires a clean default-level shellcheck run; SC1091 is an
|
|
93
|
+
# info-level "did not follow sourced file" finding that would otherwise fail that
|
|
94
|
+
# bar even though the sourced file does exist (see -x). Same fix already applied to
|
|
95
|
+
# scripts/apply-j-prefix.sh, scripts/audit-twin-divergence.sh, and
|
|
96
|
+
# scripts/build-pages-site.sh.
|
|
97
|
+
# shellcheck source=lib/resolve-project-dir.sh disable=SC1091
|
|
98
|
+
source "$SCRIPT_DIR/../lib/resolve-project-dir.sh"
|
|
99
|
+
|
|
100
|
+
PROJECT_DIR="$JENGA_PROJECT_DIR"
|
|
101
|
+
SKILLS_DIR="$PROJECT_DIR/skills"
|
|
102
|
+
|
|
103
|
+
# ---- Single named source: directory exception list (mirrors repoint-skill-refs.sh) ----
|
|
104
|
+
DELETE_BARE_SKILL_DIRS_EXCEPTIONS=(jenga jenga-permission-level index)
|
|
105
|
+
|
|
106
|
+
is_exception() {
|
|
107
|
+
local name="$1" ex
|
|
108
|
+
for ex in "${DELETE_BARE_SKILL_DIRS_EXCEPTIONS[@]}"; do
|
|
109
|
+
if [ "$name" = "$ex" ]; then
|
|
110
|
+
return 0
|
|
111
|
+
fi
|
|
112
|
+
done
|
|
113
|
+
return 1
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# ---- Single named source: expected delete-set count (E50_S15_T01, 2026-09-19) ----
|
|
117
|
+
EXPECTED_DELETE_COUNT=40
|
|
118
|
+
|
|
119
|
+
usage() {
|
|
120
|
+
cat >&2 <<'EOF'
|
|
121
|
+
Usage: delete-bare-skill-dirs.sh (--dry-run|--apply)
|
|
122
|
+
|
|
123
|
+
--dry-run Report the computed delete set and planned frontmatter rewrites.
|
|
124
|
+
Writes nothing.
|
|
125
|
+
--apply Delete every bare directory in the delete set and rewrite its
|
|
126
|
+
twin's frontmatter name: line, in one pass. Aborts with zero
|
|
127
|
+
writes if the live delete-set count does not match the recorded
|
|
128
|
+
expected count.
|
|
129
|
+
EOF
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
MODE=""
|
|
133
|
+
|
|
134
|
+
while [ "$#" -gt 0 ]; do
|
|
135
|
+
case "$1" in
|
|
136
|
+
--dry-run)
|
|
137
|
+
MODE="dry-run"
|
|
138
|
+
shift
|
|
139
|
+
;;
|
|
140
|
+
--apply)
|
|
141
|
+
MODE="apply"
|
|
142
|
+
shift
|
|
143
|
+
;;
|
|
144
|
+
-h|--help)
|
|
145
|
+
usage
|
|
146
|
+
exit 0
|
|
147
|
+
;;
|
|
148
|
+
*)
|
|
149
|
+
echo "delete-bare-skill-dirs.sh: error: unknown argument '$1'" >&2
|
|
150
|
+
usage
|
|
151
|
+
exit 1
|
|
152
|
+
;;
|
|
153
|
+
esac
|
|
154
|
+
done
|
|
155
|
+
|
|
156
|
+
if [ -z "$MODE" ]; then
|
|
157
|
+
echo "delete-bare-skill-dirs.sh: error: exactly one of --dry-run or --apply is required" >&2
|
|
158
|
+
usage
|
|
159
|
+
exit 1
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# ============================================================================
|
|
163
|
+
# 1. Live bare-vs-twin delta check — ALWAYS runs first, before any file I/O.
|
|
164
|
+
# ============================================================================
|
|
165
|
+
BARE_DIRS=()
|
|
166
|
+
TWIN_NAMES=()
|
|
167
|
+
UNPAIRED=()
|
|
168
|
+
|
|
169
|
+
if [ -d "$SKILLS_DIR" ]; then
|
|
170
|
+
for d in "$SKILLS_DIR"/*/; do
|
|
171
|
+
[ -d "$d" ] || continue
|
|
172
|
+
name="$(basename "$d")"
|
|
173
|
+
case "$name" in
|
|
174
|
+
j-*)
|
|
175
|
+
TWIN_NAMES+=("${name#j-}")
|
|
176
|
+
continue
|
|
177
|
+
;;
|
|
178
|
+
esac
|
|
179
|
+
BARE_DIRS+=("$name")
|
|
180
|
+
done
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
is_twin_name() {
|
|
184
|
+
local target="$1" n
|
|
185
|
+
for n in "${TWIN_NAMES[@]-}"; do
|
|
186
|
+
[ -n "${n:-}" ] || continue
|
|
187
|
+
if [ "$n" = "$target" ]; then
|
|
188
|
+
return 0
|
|
189
|
+
fi
|
|
190
|
+
done
|
|
191
|
+
return 1
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for name in "${BARE_DIRS[@]-}"; do
|
|
195
|
+
[ -n "${name:-}" ] || continue
|
|
196
|
+
if is_exception "$name"; then
|
|
197
|
+
continue
|
|
198
|
+
fi
|
|
199
|
+
if ! is_twin_name "$name"; then
|
|
200
|
+
UNPAIRED+=("$name")
|
|
201
|
+
fi
|
|
202
|
+
done
|
|
203
|
+
|
|
204
|
+
echo "== delete-bare-skill-dirs.sh: bare-vs-twin delta =="
|
|
205
|
+
echo " bare directories under skills/ (incl. exceptions): ${#BARE_DIRS[@]}"
|
|
206
|
+
echo " twin directories (skills/j-<name>/): $(printf '%s\n' "${TWIN_NAMES[@]-}" | grep -c . || true)"
|
|
207
|
+
if [ "${#UNPAIRED[@]}" -gt 0 ]; then
|
|
208
|
+
echo " UNPAIRED bare directories (no twin, not on the exception list):"
|
|
209
|
+
for u in "${UNPAIRED[@]}"; do
|
|
210
|
+
echo " - $u"
|
|
211
|
+
done
|
|
212
|
+
echo "delete-bare-skill-dirs.sh: error: the directory(ies) named above have no skills/j-<name>/ twin and are not on the exception list. Nothing was written. Disposition each one before running this script." >&2
|
|
213
|
+
exit 1
|
|
214
|
+
fi
|
|
215
|
+
echo " delta check clean — every bare directory is either excepted or paired with a twin."
|
|
216
|
+
echo
|
|
217
|
+
|
|
218
|
+
# ============================================================================
|
|
219
|
+
# 2. Compute the delete set: every bare, non-excepted directory with a twin.
|
|
220
|
+
# ============================================================================
|
|
221
|
+
DELETE_SET=()
|
|
222
|
+
for name in "${BARE_DIRS[@]-}"; do
|
|
223
|
+
[ -n "${name:-}" ] || continue
|
|
224
|
+
if is_exception "$name"; then
|
|
225
|
+
continue
|
|
226
|
+
fi
|
|
227
|
+
DELETE_SET+=("$name")
|
|
228
|
+
done
|
|
229
|
+
|
|
230
|
+
# Sort for deterministic, reviewable output.
|
|
231
|
+
if [ "${#DELETE_SET[@]}" -gt 0 ]; then
|
|
232
|
+
SORTED_DELETE_SET=()
|
|
233
|
+
while IFS= read -r n; do
|
|
234
|
+
[ -n "$n" ] || continue
|
|
235
|
+
SORTED_DELETE_SET+=("$n")
|
|
236
|
+
done < <(printf '%s\n' "${DELETE_SET[@]}" | sort)
|
|
237
|
+
DELETE_SET=("${SORTED_DELETE_SET[@]}")
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
echo "== delete-bare-skill-dirs.sh: computed delete set =="
|
|
241
|
+
echo " count: ${#DELETE_SET[@]} (expected: $EXPECTED_DELETE_COUNT)"
|
|
242
|
+
if [ "${#DELETE_SET[@]}" -ne "$EXPECTED_DELETE_COUNT" ]; then
|
|
243
|
+
echo "delete-bare-skill-dirs.sh: error: live delete-set count (${#DELETE_SET[@]}) does not match the recorded expected count ($EXPECTED_DELETE_COUNT, from E50_S15_T01's 2026-09-19 pre-flight recompute). Nothing was written. skills/ has drifted since that recompute -- re-run E50_S15_T01's checks before proceeding." >&2
|
|
244
|
+
exit 1
|
|
245
|
+
fi
|
|
246
|
+
for n in "${DELETE_SET[@]}"; do
|
|
247
|
+
echo " - $n"
|
|
248
|
+
done
|
|
249
|
+
echo " count check clean."
|
|
250
|
+
echo
|
|
251
|
+
|
|
252
|
+
# ============================================================================
|
|
253
|
+
# 3. Pre-validate every twin's current frontmatter name: line before writing
|
|
254
|
+
# anything, in either mode -- an unexpected frontmatter state aborts the
|
|
255
|
+
# whole run rather than being silently skipped or forced.
|
|
256
|
+
# ============================================================================
|
|
257
|
+
PLANNED=() # "name|old_line|new_line" per entry, in DELETE_SET order
|
|
258
|
+
|
|
259
|
+
for name in "${DELETE_SET[@]}"; do
|
|
260
|
+
twin_skill_md="$SKILLS_DIR/j-$name/SKILL.md"
|
|
261
|
+
if [ ! -f "$twin_skill_md" ]; then
|
|
262
|
+
echo "delete-bare-skill-dirs.sh: error: expected twin file not found: $twin_skill_md. Nothing was written." >&2
|
|
263
|
+
exit 1
|
|
264
|
+
fi
|
|
265
|
+
current_line="$(grep -m1 -E '^name:[[:space:]]' "$twin_skill_md" || true)"
|
|
266
|
+
expected_old="name: j.j-${name}"
|
|
267
|
+
new_line="name: j.${name}"
|
|
268
|
+
if [ "$current_line" != "$expected_old" ]; then
|
|
269
|
+
echo "delete-bare-skill-dirs.sh: error: $twin_skill_md's frontmatter name: line reads '$current_line', expected exactly '$expected_old'. Nothing was written. This twin is in an unexpected state -- resolve it manually before re-running." >&2
|
|
270
|
+
exit 1
|
|
271
|
+
fi
|
|
272
|
+
PLANNED+=("${name}|${expected_old}|${new_line}")
|
|
273
|
+
done
|
|
274
|
+
|
|
275
|
+
echo "== delete-bare-skill-dirs.sh: planned frontmatter rewrites (${#PLANNED[@]}) =="
|
|
276
|
+
for entry in "${PLANNED[@]}"; do
|
|
277
|
+
IFS='|' read -r name old_line new_line <<< "$entry"
|
|
278
|
+
echo " skills/j-${name}/SKILL.md: '${old_line}' -> '${new_line}'"
|
|
279
|
+
done
|
|
280
|
+
echo
|
|
281
|
+
|
|
282
|
+
if [ "$MODE" = "dry-run" ]; then
|
|
283
|
+
echo "== delete-bare-skill-dirs.sh: dry-run complete — ${#DELETE_SET[@]} director(ies) would be deleted, ${#PLANNED[@]} frontmatter line(s) would be rewritten =="
|
|
284
|
+
echo " Bare directories that would be deleted:"
|
|
285
|
+
for name in "${DELETE_SET[@]}"; do
|
|
286
|
+
echo " - skills/${name}/"
|
|
287
|
+
done
|
|
288
|
+
echo " No files were written. Run with --apply to perform this (E50_S15_T04 only)."
|
|
289
|
+
exit 0
|
|
290
|
+
fi
|
|
291
|
+
|
|
292
|
+
# ============================================================================
|
|
293
|
+
# 4. --apply: rewrite every twin's frontmatter FIRST (full pass), then delete
|
|
294
|
+
# every bare directory (full pass). Doing all rewrites before any delete
|
|
295
|
+
# means a mid-run failure during the rewrite pass leaves every bare
|
|
296
|
+
# directory still present (recoverable: nothing deleted yet), and a
|
|
297
|
+
# mid-run failure during the delete pass leaves every twin already
|
|
298
|
+
# correctly renamed (recoverable: re-run recomputes a smaller, still-valid
|
|
299
|
+
# delete set next time, since a twin with no more `j.j-` prefix simply
|
|
300
|
+
# won't re-match the pre-validation above for whichever entries already
|
|
301
|
+
# completed). Neither pass order can leave a "two directories claim
|
|
302
|
+
# j.<name>" state or a "no directory claims it" state uncommitted, because
|
|
303
|
+
# a bare directory is never deleted before its own twin's rename has
|
|
304
|
+
# already succeeded.
|
|
305
|
+
# ============================================================================
|
|
306
|
+
echo "== delete-bare-skill-dirs.sh: applying frontmatter rewrites =="
|
|
307
|
+
for entry in "${PLANNED[@]}"; do
|
|
308
|
+
IFS='|' read -r name old_line new_line <<< "$entry"
|
|
309
|
+
twin_skill_md="$SKILLS_DIR/j-${name}/SKILL.md"
|
|
310
|
+
tmp_file="$(mktemp "${twin_skill_md}.rename.XXXXXX")"
|
|
311
|
+
cp -p "$twin_skill_md" "$tmp_file"
|
|
312
|
+
awk -v old="$old_line" -v new="$new_line" '
|
|
313
|
+
$0 == old && !done { print new; done=1; next }
|
|
314
|
+
{ print }
|
|
315
|
+
' "$twin_skill_md" > "$tmp_file"
|
|
316
|
+
mv "$tmp_file" "$twin_skill_md"
|
|
317
|
+
echo " rewrote skills/j-${name}/SKILL.md"
|
|
318
|
+
done
|
|
319
|
+
echo
|
|
320
|
+
|
|
321
|
+
echo "== delete-bare-skill-dirs.sh: deleting bare directories =="
|
|
322
|
+
for name in "${DELETE_SET[@]}"; do
|
|
323
|
+
rm -rf "${SKILLS_DIR:?}/${name:?}"
|
|
324
|
+
echo " deleted skills/${name}/"
|
|
325
|
+
done
|
|
326
|
+
echo
|
|
327
|
+
|
|
328
|
+
echo "== delete-bare-skill-dirs.sh: apply complete — ${#DELETE_SET[@]} director(ies) deleted, ${#PLANNED[@]} frontmatter line(s) rewritten =="
|
|
329
|
+
echo " Working tree now needs a single commit covering both changes (this script does not commit)."
|
|
330
|
+
exit 0
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* and unions it with the paths CURRENTLY on disk under this repo's own `skills/`
|
|
36
36
|
* and `agents/` directories — i.e. "what this release is about to ship" folds into
|
|
37
37
|
* the running cumulative record. This is the mode wired into the publish pipeline
|
|
38
|
-
* (`skills/publish/scripts/npm_pipeline.sh` / `npm_ci_pipeline.sh`, via the
|
|
38
|
+
* (`skills/j-publish/scripts/npm_pipeline.sh` / `npm_ci_pipeline.sh`, via the
|
|
39
39
|
* `generate:legacy-paths` npm script) so every future publish keeps the list
|
|
40
40
|
* current with zero network dependency and zero publish-time registry flakiness.
|
|
41
41
|
*
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
* "generated_at": "<ISO 8601>",
|
|
58
58
|
* "package": "@jenga-ai/agent",
|
|
59
59
|
* "source": "bootstrap-from-registry+incremental" | "incremental",
|
|
60
|
-
* "paths": ["agents/developer.md", "skills/do/SKILL.md", ...]
|
|
60
|
+
* "paths": ["agents/developer.md", "skills/j-do/SKILL.md", ...]
|
|
61
61
|
* }
|
|
62
62
|
*
|
|
63
63
|
* `paths` are relative to a mirror root, POSIX-separated, deduped and sorted — matching the same
|
package/scripts/idea_manager.sh
CHANGED
|
@@ -3,15 +3,35 @@
|
|
|
3
3
|
# Run from the repository root.
|
|
4
4
|
|
|
5
5
|
IDEA_FILE="project/ideas.md"
|
|
6
|
-
TEMPLATE="skills/idea/assets/idea_template.md"
|
|
6
|
+
TEMPLATE="skills/j-idea/assets/idea_template.md"
|
|
7
|
+
PLANS_DIR="project/documentation/plans"
|
|
7
8
|
|
|
8
9
|
usage() {
|
|
9
10
|
cat >&2 <<EOF
|
|
10
11
|
Usage: $0 <subcommand> [args]
|
|
11
12
|
|
|
12
13
|
Subcommands:
|
|
13
|
-
add "<entry>"
|
|
14
|
-
|
|
14
|
+
add "<entry>" Append entry to project/ideas.md (auto-creates from
|
|
15
|
+
template if missing)
|
|
16
|
+
list Print all non-comment, non-blank entries (silent if
|
|
17
|
+
file missing/empty)
|
|
18
|
+
tag "<idea-text>" <TAG> Tag an existing idea line. <TAG> is either
|
|
19
|
+
"PROMOTED:E##_S##[_T##]" or "REJECTED". Errors if the
|
|
20
|
+
line is not found, is ambiguous, or is already tagged.
|
|
21
|
+
get-id "<idea-text>" Print the embedded board ID from a PROMOTED line
|
|
22
|
+
(errors if the line has no PROMOTED tag).
|
|
23
|
+
get-rapport "<idea-text>" Print the source-rapport path recorded on the line via
|
|
24
|
+
a "<!-- rapport: <path> -->" comment. Prints nothing
|
|
25
|
+
(exit 0) if the line has no such comment.
|
|
26
|
+
resolve "<idea-text>" <promoted|rejected> [board_id]
|
|
27
|
+
The full outcome-record mechanism: tags the line, then
|
|
28
|
+
either updates the idea's source rapport (decision,
|
|
29
|
+
date, resulting board ID, [SPIKE]-prefixed title) or,
|
|
30
|
+
if the idea has no source rapport, creates
|
|
31
|
+
project/documentation/plans/<slug>_PROMOTED.md or
|
|
32
|
+
_REJECTED.md with the same content. board_id is
|
|
33
|
+
required for "promoted" and must be omitted for
|
|
34
|
+
"rejected".
|
|
15
35
|
EOF
|
|
16
36
|
exit 1
|
|
17
37
|
}
|
|
@@ -23,6 +43,129 @@ real_entries() {
|
|
|
23
43
|
| grep -v '^\s*<!--'
|
|
24
44
|
}
|
|
25
45
|
|
|
46
|
+
# find_idea_line <idea-text>
|
|
47
|
+
# Prints "LINE_NUM:CONTENT" for the single line in $IDEA_FILE matching <idea-text> literally.
|
|
48
|
+
# Errors (exit 1) if the file is missing, no line matches, or more than one line matches.
|
|
49
|
+
find_idea_line() {
|
|
50
|
+
local needle="$1"
|
|
51
|
+
local matches
|
|
52
|
+
if [ ! -f "$IDEA_FILE" ]; then
|
|
53
|
+
echo "Error: $IDEA_FILE does not exist" >&2
|
|
54
|
+
return 1
|
|
55
|
+
fi
|
|
56
|
+
matches=$(grep -Fn -- "$needle" "$IDEA_FILE")
|
|
57
|
+
if [ -z "$matches" ]; then
|
|
58
|
+
echo "Error: no line in $IDEA_FILE matches: $needle" >&2
|
|
59
|
+
return 1
|
|
60
|
+
fi
|
|
61
|
+
if [ "$(printf '%s\n' "$matches" | wc -l | tr -d ' ')" -gt 1 ]; then
|
|
62
|
+
echo "Error: idea text matches multiple lines (ambiguous), refine it: $needle" >&2
|
|
63
|
+
return 1
|
|
64
|
+
fi
|
|
65
|
+
printf '%s\n' "$matches"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# tag_line <line-num> <tag-text>
|
|
69
|
+
# Appends " <!-- <tag-text> -->" to the given line number of $IDEA_FILE, in place.
|
|
70
|
+
# Uses awk string concatenation (not sed substitution) so arbitrary idea text/paths
|
|
71
|
+
# containing &, /, or other sed-special characters are never re-interpreted.
|
|
72
|
+
tag_line() {
|
|
73
|
+
local linenum="$1"
|
|
74
|
+
local tagtext="$2"
|
|
75
|
+
local tmpfile
|
|
76
|
+
tmpfile="${IDEA_FILE}.tmp.$$"
|
|
77
|
+
awk -v n="$linenum" -v suffix=" <!-- ${tagtext} -->" \
|
|
78
|
+
'NR==n { $0 = $0 suffix } { print }' "$IDEA_FILE" > "$tmpfile" && mv "$tmpfile" "$IDEA_FILE"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# slugify <text> — lowercase, non-alphanumeric runs collapsed to a single '-', trimmed, capped
|
|
82
|
+
slugify() {
|
|
83
|
+
printf '%s' "$1" \
|
|
84
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
85
|
+
| sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' \
|
|
86
|
+
| cut -c1-60
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# extract_rapport_link <line-content> — prints the rapport: path, or nothing if absent
|
|
90
|
+
extract_rapport_link() {
|
|
91
|
+
printf '%s' "$1" | grep -oE '<!-- rapport: [^>]*-->' | sed -E 's/<!-- rapport: *([^ ]+) *-->/\1/'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# extract_promoted_id <line-content> — prints the embedded board ID, or nothing if absent
|
|
95
|
+
extract_promoted_id() {
|
|
96
|
+
printf '%s' "$1" | grep -oE '<!-- PROMOTED: [^>]*-->' | sed -E 's/<!-- PROMOTED: *([^ ]+) *-->/\1/'
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# already_tagged <line-content> — returns 0 (true) if the line already has a terminal tag
|
|
100
|
+
already_tagged() {
|
|
101
|
+
case "$1" in
|
|
102
|
+
*"<!-- PROMOTED"*|*"<!-- REJECTED"*) return 0 ;;
|
|
103
|
+
*) return 1 ;;
|
|
104
|
+
esac
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# validate_board_id <id> — loose shape check: E<digits> optionally _S<digits> optionally _T<digits>
|
|
108
|
+
validate_board_id() {
|
|
109
|
+
case "$1" in
|
|
110
|
+
E[0-9]*) return 0 ;;
|
|
111
|
+
*) return 1 ;;
|
|
112
|
+
esac
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# update_rapport_outcome <rapport-path> <decision-label> <date> <board-id> <idea-text>
|
|
116
|
+
# Prefixes the rapport's "# Rapport: ..." title with "[SPIKE] " if not already present, then
|
|
117
|
+
# appends an "## Idea Outcome Record" section with the decision.
|
|
118
|
+
update_rapport_outcome() {
|
|
119
|
+
local rapport_path="$1" decision_label="$2" today="$3" board_id="$4" idea_text="$5"
|
|
120
|
+
local tmpfile
|
|
121
|
+
tmpfile="${rapport_path}.tmp.$$"
|
|
122
|
+
|
|
123
|
+
awk '
|
|
124
|
+
/^# Rapport:/ && $0 !~ /\[SPIKE\]/ {
|
|
125
|
+
sub(/^# Rapport: /, "# Rapport: [SPIKE] ")
|
|
126
|
+
}
|
|
127
|
+
{ print }
|
|
128
|
+
' "$rapport_path" > "$tmpfile" && mv "$tmpfile" "$rapport_path"
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
echo ""
|
|
132
|
+
echo "---"
|
|
133
|
+
echo ""
|
|
134
|
+
echo "## Idea Outcome Record"
|
|
135
|
+
echo ""
|
|
136
|
+
echo "**Decision:** ${decision_label}"
|
|
137
|
+
echo "**Date:** ${today} (UTC)"
|
|
138
|
+
if [ "$decision_label" = "Promoted" ]; then
|
|
139
|
+
echo "**Resulting Board ID:** ${board_id}"
|
|
140
|
+
fi
|
|
141
|
+
echo "**Idea:** ${idea_text}"
|
|
142
|
+
} >> "$rapport_path"
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# write_fallback_outcome <idea-text> <decision-label> <date> <board-id> <file-suffix>
|
|
146
|
+
# Creates project/documentation/plans/<slug><file-suffix> and prints its path.
|
|
147
|
+
write_fallback_outcome() {
|
|
148
|
+
local idea_text="$1" decision_label="$2" today="$3" board_id="$4" suffix="$5"
|
|
149
|
+
local slug outfile
|
|
150
|
+
slug=$(slugify "$idea_text")
|
|
151
|
+
mkdir -p "$PLANS_DIR"
|
|
152
|
+
outfile="${PLANS_DIR}/${slug}${suffix}"
|
|
153
|
+
{
|
|
154
|
+
echo "# Idea Outcome: ${idea_text}"
|
|
155
|
+
echo ""
|
|
156
|
+
echo "**Decision:** ${decision_label}"
|
|
157
|
+
echo "**Date:** ${today} (UTC)"
|
|
158
|
+
if [ "$decision_label" = "Promoted" ]; then
|
|
159
|
+
echo "**Resulting Board ID:** ${board_id}"
|
|
160
|
+
fi
|
|
161
|
+
echo ""
|
|
162
|
+
echo "## Idea"
|
|
163
|
+
echo ""
|
|
164
|
+
echo "${idea_text}"
|
|
165
|
+
} > "$outfile"
|
|
166
|
+
printf '%s' "$outfile"
|
|
167
|
+
}
|
|
168
|
+
|
|
26
169
|
case "${1:-}" in
|
|
27
170
|
add)
|
|
28
171
|
[ -z "${2:-}" ] && { echo "Error: add requires an entry argument" >&2; exit 1; }
|
|
@@ -41,6 +184,118 @@ case "${1:-}" in
|
|
|
41
184
|
exit 0
|
|
42
185
|
;;
|
|
43
186
|
|
|
187
|
+
tag)
|
|
188
|
+
[ -z "${2:-}" ] && { echo "Error: tag requires an idea-text argument" >&2; exit 1; }
|
|
189
|
+
[ -z "${3:-}" ] && { echo "Error: tag requires a TAG argument (PROMOTED:E##_S##[_T##] or REJECTED)" >&2; exit 1; }
|
|
190
|
+
MATCH=$(find_idea_line "$2") || exit 1
|
|
191
|
+
LINENUM=$(printf '%s' "$MATCH" | cut -d: -f1)
|
|
192
|
+
CONTENT=$(printf '%s' "$MATCH" | cut -d: -f2-)
|
|
193
|
+
if already_tagged "$CONTENT"; then
|
|
194
|
+
echo "Error: idea line is already tagged; refusing to double-tag: $CONTENT" >&2
|
|
195
|
+
exit 1
|
|
196
|
+
fi
|
|
197
|
+
RAW_TAG="$3"
|
|
198
|
+
case "$RAW_TAG" in
|
|
199
|
+
PROMOTED:*)
|
|
200
|
+
BOARD_ID="${RAW_TAG#PROMOTED:}"
|
|
201
|
+
if ! validate_board_id "$BOARD_ID"; then
|
|
202
|
+
echo "Error: invalid board ID format in PROMOTED tag: $BOARD_ID (expected E##_S##[_T##] or E##)" >&2
|
|
203
|
+
exit 1
|
|
204
|
+
fi
|
|
205
|
+
TAG_TEXT="PROMOTED: $BOARD_ID"
|
|
206
|
+
;;
|
|
207
|
+
REJECTED)
|
|
208
|
+
TAG_TEXT="REJECTED"
|
|
209
|
+
;;
|
|
210
|
+
*)
|
|
211
|
+
echo "Error: TAG must be PROMOTED:E##_S##[_T##] or REJECTED, got: $RAW_TAG" >&2
|
|
212
|
+
exit 1
|
|
213
|
+
;;
|
|
214
|
+
esac
|
|
215
|
+
tag_line "$LINENUM" "$TAG_TEXT"
|
|
216
|
+
echo "Tagged line $LINENUM: <!-- $TAG_TEXT -->"
|
|
217
|
+
;;
|
|
218
|
+
|
|
219
|
+
get-id)
|
|
220
|
+
[ -z "${2:-}" ] && { echo "Error: get-id requires an idea-text argument" >&2; exit 1; }
|
|
221
|
+
MATCH=$(find_idea_line "$2") || exit 1
|
|
222
|
+
CONTENT=$(printf '%s' "$MATCH" | cut -d: -f2-)
|
|
223
|
+
ID=$(extract_promoted_id "$CONTENT")
|
|
224
|
+
if [ -z "$ID" ]; then
|
|
225
|
+
echo "Error: no PROMOTED tag found on matching line" >&2
|
|
226
|
+
exit 1
|
|
227
|
+
fi
|
|
228
|
+
echo "$ID"
|
|
229
|
+
;;
|
|
230
|
+
|
|
231
|
+
get-rapport)
|
|
232
|
+
[ -z "${2:-}" ] && { echo "Error: get-rapport requires an idea-text argument" >&2; exit 1; }
|
|
233
|
+
MATCH=$(find_idea_line "$2") || exit 1
|
|
234
|
+
CONTENT=$(printf '%s' "$MATCH" | cut -d: -f2-)
|
|
235
|
+
extract_rapport_link "$CONTENT"
|
|
236
|
+
exit 0
|
|
237
|
+
;;
|
|
238
|
+
|
|
239
|
+
resolve)
|
|
240
|
+
[ -z "${2:-}" ] && { echo "Error: resolve requires an idea-text argument" >&2; exit 1; }
|
|
241
|
+
[ -z "${3:-}" ] && { echo "Error: resolve requires a decision argument: promoted|rejected" >&2; exit 1; }
|
|
242
|
+
IDEA_TEXT="$2"
|
|
243
|
+
DECISION="$3"
|
|
244
|
+
BOARD_ID="${4:-}"
|
|
245
|
+
|
|
246
|
+
case "$DECISION" in
|
|
247
|
+
promoted|rejected) : ;;
|
|
248
|
+
*) echo "Error: decision must be 'promoted' or 'rejected', got: $DECISION" >&2; exit 1 ;;
|
|
249
|
+
esac
|
|
250
|
+
|
|
251
|
+
if [ "$DECISION" = "promoted" ]; then
|
|
252
|
+
[ -z "$BOARD_ID" ] && { echo "Error: promoted decision requires a board_id argument (E##_S##[_T##] or E##)" >&2; exit 1; }
|
|
253
|
+
if ! validate_board_id "$BOARD_ID"; then
|
|
254
|
+
echo "Error: invalid board ID format: $BOARD_ID" >&2
|
|
255
|
+
exit 1
|
|
256
|
+
fi
|
|
257
|
+
else
|
|
258
|
+
[ -n "$BOARD_ID" ] && { echo "Error: rejected decision does not take a board_id argument" >&2; exit 1; }
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
MATCH=$(find_idea_line "$IDEA_TEXT") || exit 1
|
|
262
|
+
LINENUM=$(printf '%s' "$MATCH" | cut -d: -f1)
|
|
263
|
+
CONTENT=$(printf '%s' "$MATCH" | cut -d: -f2-)
|
|
264
|
+
|
|
265
|
+
if already_tagged "$CONTENT"; then
|
|
266
|
+
echo "Error: idea line is already tagged; refusing to re-resolve: $CONTENT" >&2
|
|
267
|
+
exit 1
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
RAPPORT_PATH=$(extract_rapport_link "$CONTENT")
|
|
271
|
+
|
|
272
|
+
if [ "$DECISION" = "promoted" ]; then
|
|
273
|
+
TAG_TEXT="PROMOTED: $BOARD_ID"
|
|
274
|
+
DECISION_LABEL="Promoted"
|
|
275
|
+
FILE_SUFFIX="_PROMOTED.md"
|
|
276
|
+
else
|
|
277
|
+
TAG_TEXT="REJECTED"
|
|
278
|
+
DECISION_LABEL="Rejected"
|
|
279
|
+
FILE_SUFFIX="_REJECTED.md"
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
TODAY=$(date -u +%Y-%m-%d)
|
|
283
|
+
|
|
284
|
+
tag_line "$LINENUM" "$TAG_TEXT"
|
|
285
|
+
|
|
286
|
+
if [ -n "$RAPPORT_PATH" ]; then
|
|
287
|
+
if [ ! -f "$RAPPORT_PATH" ]; then
|
|
288
|
+
echo "Error: source rapport not found at $RAPPORT_PATH; idea line was tagged but outcome record was not written" >&2
|
|
289
|
+
exit 1
|
|
290
|
+
fi
|
|
291
|
+
update_rapport_outcome "$RAPPORT_PATH" "$DECISION_LABEL" "$TODAY" "$BOARD_ID" "$IDEA_TEXT"
|
|
292
|
+
echo "Idea line $LINENUM tagged <!-- $TAG_TEXT -->; rapport updated: $RAPPORT_PATH"
|
|
293
|
+
else
|
|
294
|
+
OUTFILE=$(write_fallback_outcome "$IDEA_TEXT" "$DECISION_LABEL" "$TODAY" "$BOARD_ID" "$FILE_SUFFIX")
|
|
295
|
+
echo "Idea line $LINENUM tagged <!-- $TAG_TEXT -->; outcome record created: $OUTFILE"
|
|
296
|
+
fi
|
|
297
|
+
;;
|
|
298
|
+
|
|
44
299
|
*)
|
|
45
300
|
usage
|
|
46
301
|
;;
|
package/scripts/mark-deployed.sh
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# 2. Derives each eligible ticket's matched EST-tagged commit SHAs (and,
|
|
14
14
|
# for parity/logging, their unioned touched-file list) using the
|
|
15
15
|
# identical anchored-grep + union technique
|
|
16
|
-
# skills/self-sync/scripts/mark-merged.sh already implements.
|
|
16
|
+
# skills/j-self-sync/scripts/mark-merged.sh already implements.
|
|
17
17
|
# 3. Checks ANCESTRY, not diff-membership: a ticket is "covered" by a tag
|
|
18
18
|
# only when EVERY one of its matched commits is an ancestor of that
|
|
19
19
|
# tag's resolved_private_sha (git merge-base --is-ancestor). A tag
|
|
@@ -183,7 +183,7 @@ REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || true)
|
|
|
183
183
|
|
|
184
184
|
COMPUTE_SCRIPT="$SCRIPT_DIR/compute-deploy-reconcile.sh"
|
|
185
185
|
WITH_LOCK_SCRIPT="$REPO_ROOT/scripts/with-lock.sh"
|
|
186
|
-
UPDATE_FRONTMATTER_SCRIPT="$REPO_ROOT/skills/close-story/scripts/update-task-frontmatter.sh"
|
|
186
|
+
UPDATE_FRONTMATTER_SCRIPT="$REPO_ROOT/skills/j-close-story/scripts/update-task-frontmatter.sh"
|
|
187
187
|
MARKER_FILE="$REPO_ROOT/project/data/deploy-reconcile-marker.json"
|
|
188
188
|
|
|
189
189
|
[ -f "$WITH_LOCK_SCRIPT" ] || die "expected script not found: $WITH_LOCK_SCRIPT"
|