@jenga-ai/agent 1.2.4 → 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 -91
- package/agents/developer.md +26 -7
- package/agents/scrum-master.md +57 -22
- package/agents/tester.md +68 -4
- package/hooks/on_session_end.sh +40 -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 +35 -20
- 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 +12 -2
- package/skills/continue/SKILL.md +1 -1
- package/skills/deep-dive/SKILL.md +1 -1
- package/skills/dev-done/SKILL.md +46 -0
- package/skills/dev-done/scripts/classify-commit-outcome.sh +114 -0
- 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 +8 -7
- package/skills/init/assets/scope-thresholds_template.json +7 -0
- package/skills/init/scripts/init.sh +6 -0
- 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 +67 -1
- package/skills/publish/adapters/npm-ci.md +60 -4
- package/skills/publish/adapters/npm.md +18 -0
- package/skills/publish/assets/ci-contract.md +27 -0
- package/skills/publish/assets/publish.example.json +27 -0
- package/skills/publish/schemas/publish.schema.json +20 -0
- package/skills/publish/scripts/npm_ci_pipeline.sh +50 -1
- package/skills/publish/scripts/npm_stage_inspect.sh +829 -0
- package/skills/publish/scripts/npm_stage_pipeline.sh +427 -0
- package/skills/publish/scripts/publish_common.sh +16 -0
- package/skills/publish/scripts/show_history.sh +12 -5
- package/skills/publish/scripts/validate_npm_stage_env.sh +184 -0
- package/skills/publish/scripts/write_ledger_entry.sh +92 -2
- package/skills/reconcile/SKILL.md +122 -12
- package/skills/reconcile/assets/report_format.md +17 -0
- package/skills/reconcile/scripts/resolve-reconcile-scope.sh +489 -0
- 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 +201 -22
- package/skills/uncharted/scripts/directory-triage.sh +342 -0
- package/skills/uncharted/scripts/elicitation-state.sh +457 -0
- package/skills/wtf/SKILL.md +1 -1
- package/templates/SCRUM_BOARD_SCHEMA.md +90 -2
- package/templates/agent-context.md.tpl +47 -12
- package/templates/copilot-instructions.md.tpl +36 -9
- package/mcp/router/README.md +0 -19
- package/mcp/router/embedder.js +0 -23
- package/mcp/router/index.js +0 -204
- package/mcp/router/matcher.js +0 -87
- package/mcp/router/package-lock.json +0 -1048
- package/mcp/router/package.json +0 -11
- package/mcp/router/skill-index.js +0 -104
- package/skills/route/SKILL.md +0 -180
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ---------------------------------------------------------------------------
|
|
3
|
+
# skills/uncharted/scripts/directory-triage.sh
|
|
4
|
+
#
|
|
5
|
+
# Deterministic front half of the directory-triage step (E20_S08_T03) that
|
|
6
|
+
# sits between discovery and any developer/tester Investigative Mode
|
|
7
|
+
# dispatch, for both `/uncharted onboard`'s conversational default and
|
|
8
|
+
# `/uncharted segment --mode investigate`.
|
|
9
|
+
#
|
|
10
|
+
# The problem: an investigative pass that walks every directory in a
|
|
11
|
+
# candidate set wastes turns (and, worse, a user's attention at the
|
|
12
|
+
# confirmation gate) on vendor/generated noise nobody wants a coarse graph
|
|
13
|
+
# node for. This script answers the MECHANICAL half of that question —
|
|
14
|
+
# "is this directory already excluded by .gitignore, or does it match a
|
|
15
|
+
# well-known vendor/generated pattern?" — so the agent only has to spend
|
|
16
|
+
# judgement on what's left: the content-based "generalize" cases a fixed
|
|
17
|
+
# pattern list can never catch (e.g. "this directory is SOAP request
|
|
18
|
+
# mock-ups for a test suite").
|
|
19
|
+
#
|
|
20
|
+
# This script performs NO judgement of its own and writes nothing. It
|
|
21
|
+
# classifies; the agent decides what to do with `remaining`.
|
|
22
|
+
#
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# INPUT
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# A root (the repository, or the target `onboard`/`segment` is investigating)
|
|
27
|
+
# and a set of candidate directory paths to triage, relative to that root.
|
|
28
|
+
# Candidates come from positional arguments, or newline-delimited from stdin
|
|
29
|
+
# when none are given — composable with discover-subsystems.sh the same way
|
|
30
|
+
# apply-subsystem-cap.sh is:
|
|
31
|
+
#
|
|
32
|
+
# discover-subsystems.sh <root> | jq -r '.candidates[].path' \
|
|
33
|
+
# | directory-triage.sh <root>
|
|
34
|
+
#
|
|
35
|
+
# A candidate that does not exist under <root>, or is not a directory, is
|
|
36
|
+
# reported in `notices` and otherwise skipped — it is an input problem, not a
|
|
37
|
+
# triage verdict.
|
|
38
|
+
#
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# THE DETERMINISTIC IGNORE-LIST
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Path-COMPONENT match against a fixed, known-noise pattern list — never a
|
|
43
|
+
# substring match, so a real subsystem directory named e.g. "targets/" is not
|
|
44
|
+
# caught by the "target" pattern. The list is intentionally short and named
|
|
45
|
+
# in the task's own acceptance criteria; it is expected to need occasional
|
|
46
|
+
# extension as new ecosystems are triaged, and is kept as a single array
|
|
47
|
+
# below for exactly that reason — this is a revisitable list, not a closed
|
|
48
|
+
# one:
|
|
49
|
+
#
|
|
50
|
+
# node_modules vendor dist build .venv
|
|
51
|
+
# venv __pycache__ .next coverage target
|
|
52
|
+
# .git .tox .mypy_cache .pytest_cache .cache
|
|
53
|
+
# *.egg-info (suffix match on the last path component)
|
|
54
|
+
#
|
|
55
|
+
# A candidate already excluded by .gitignore (via `git check-ignore`) is
|
|
56
|
+
# reported separately (`reason: "gitignore"`) even if it ALSO matches a
|
|
57
|
+
# pattern — gitignore is checked first and wins, since "why is this
|
|
58
|
+
# ignored" should point at the actual mechanism in effect.
|
|
59
|
+
#
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# OPTIONS
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
# --root DIR Root the candidates are resolved against. Required as
|
|
64
|
+
# the first positional argument OR via this flag.
|
|
65
|
+
# --json-out FILE Also write the JSON report to a file. stdout gets it
|
|
66
|
+
# regardless.
|
|
67
|
+
# -h, --help Show this help and exit 0.
|
|
68
|
+
#
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# OUTPUT (stdout, JSON)
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
# {
|
|
73
|
+
# "script": "directory-triage.sh",
|
|
74
|
+
# "version": 1,
|
|
75
|
+
# "root": "<as given>",
|
|
76
|
+
# "root_absolute": "<canonicalised>",
|
|
77
|
+
# "candidate_count": <int>,
|
|
78
|
+
# "ignored_count": <int>,
|
|
79
|
+
# "remaining_count": <int>,
|
|
80
|
+
# "ignored": [ { "path", "absolute_path", "reason" }, ... ],
|
|
81
|
+
# "remaining": [ { "path", "absolute_path" }, ... ],
|
|
82
|
+
# "notices": [ "<non-fatal diagnostic>", ... ]
|
|
83
|
+
# }
|
|
84
|
+
#
|
|
85
|
+
# `reason` on an ignored entry is either "gitignore" or "pattern:<name>"
|
|
86
|
+
# (e.g. "pattern:node_modules") — never a bare boolean, so a consumer never
|
|
87
|
+
# has to re-derive why something was excluded.
|
|
88
|
+
#
|
|
89
|
+
# `remaining` is the generalize-list INPUT, not its output — this script
|
|
90
|
+
# does not attempt content-based judgement (reading file contents to guess
|
|
91
|
+
# "this looks like SOAP mock-ups") at all. That pass is agent judgement, per
|
|
92
|
+
# the Skill Implementation Principle in CLAUDE.md, and lives in
|
|
93
|
+
# skills/uncharted/SKILL.md's Directory Triage subsection, not here.
|
|
94
|
+
#
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# EXIT CODES
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# 0 — success (including zero candidates, or everything remaining)
|
|
99
|
+
# 1 — usage error: unknown flag, missing root
|
|
100
|
+
# 2 — input error: root does not exist or is not a directory
|
|
101
|
+
#
|
|
102
|
+
# Examples:
|
|
103
|
+
# directory-triage.sh . src lib vendor node_modules
|
|
104
|
+
# discover-subsystems.sh . | jq -r '.candidates[].path' | directory-triage.sh .
|
|
105
|
+
#
|
|
106
|
+
# Requires: bash, python3, git (optional — gitignore check is skipped with a
|
|
107
|
+
# notice when the root is not inside a git working tree).
|
|
108
|
+
|
|
109
|
+
set -euo pipefail
|
|
110
|
+
|
|
111
|
+
SCRIPT_NAME="directory-triage.sh"
|
|
112
|
+
ROOT=""
|
|
113
|
+
JSON_OUT=""
|
|
114
|
+
CANDIDATES=()
|
|
115
|
+
|
|
116
|
+
print_help() {
|
|
117
|
+
cat <<'EOF'
|
|
118
|
+
Usage: directory-triage.sh [--root DIR] [--json-out FILE] [<root>] [candidate...]
|
|
119
|
+
discover-subsystems.sh <root> | jq -r '.candidates[].path' | directory-triage.sh <root>
|
|
120
|
+
|
|
121
|
+
Classifies candidate directories under <root> into:
|
|
122
|
+
- ignored: already excluded by .gitignore, or matching a known
|
|
123
|
+
vendor/generated pattern (node_modules, vendor, dist, build,
|
|
124
|
+
.venv, venv, __pycache__, .next, coverage, target, .git,
|
|
125
|
+
.tox, .mypy_cache, .pytest_cache, .cache, *.egg-info)
|
|
126
|
+
- remaining: everything else — the input to the agent-judgement
|
|
127
|
+
generalize-list pass documented in skills/uncharted/SKILL.md
|
|
128
|
+
|
|
129
|
+
Options:
|
|
130
|
+
--root DIR Root the candidates are resolved against (or give it as
|
|
131
|
+
the first positional argument).
|
|
132
|
+
--json-out FILE Also write the JSON report to a file. stdout gets it
|
|
133
|
+
regardless.
|
|
134
|
+
-h, --help Show this help and exit 0.
|
|
135
|
+
|
|
136
|
+
Exit codes: 0 success, 1 usage error, 2 root missing/not a directory.
|
|
137
|
+
EOF
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
usage_error() {
|
|
141
|
+
echo "Usage: $SCRIPT_NAME [--root DIR] [--json-out FILE] [<root>] [candidate...]" >&2
|
|
142
|
+
echo " (candidates may also be piped newline-delimited on stdin)" >&2
|
|
143
|
+
exit 1
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
while [ "$#" -gt 0 ]; do
|
|
147
|
+
case "$1" in
|
|
148
|
+
-h|--help)
|
|
149
|
+
print_help
|
|
150
|
+
exit 0
|
|
151
|
+
;;
|
|
152
|
+
--root)
|
|
153
|
+
[ "$#" -ge 2 ] || usage_error
|
|
154
|
+
ROOT="$2"
|
|
155
|
+
shift 2
|
|
156
|
+
;;
|
|
157
|
+
--json-out)
|
|
158
|
+
[ "$#" -ge 2 ] || usage_error
|
|
159
|
+
JSON_OUT="$2"
|
|
160
|
+
shift 2
|
|
161
|
+
;;
|
|
162
|
+
--)
|
|
163
|
+
shift
|
|
164
|
+
break
|
|
165
|
+
;;
|
|
166
|
+
-*)
|
|
167
|
+
usage_error
|
|
168
|
+
;;
|
|
169
|
+
*)
|
|
170
|
+
if [ -z "$ROOT" ]; then
|
|
171
|
+
ROOT="$1"
|
|
172
|
+
else
|
|
173
|
+
CANDIDATES+=("$1")
|
|
174
|
+
fi
|
|
175
|
+
shift
|
|
176
|
+
;;
|
|
177
|
+
esac
|
|
178
|
+
done
|
|
179
|
+
|
|
180
|
+
# Remaining positionals after `--` are also candidates.
|
|
181
|
+
for arg in "$@"; do
|
|
182
|
+
CANDIDATES+=("$arg")
|
|
183
|
+
done
|
|
184
|
+
|
|
185
|
+
[ -n "$ROOT" ] || usage_error
|
|
186
|
+
|
|
187
|
+
if [ ! -d "$ROOT" ]; then
|
|
188
|
+
echo "$SCRIPT_NAME: root '$ROOT' does not exist or is not a directory" >&2
|
|
189
|
+
exit 2
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
ROOT_ABS=$(cd -- "$ROOT" && pwd -P)
|
|
193
|
+
|
|
194
|
+
# If no candidates were given on the command line, read newline-delimited
|
|
195
|
+
# candidates from stdin (only when stdin is not a terminal, so an
|
|
196
|
+
# interactive invocation with no candidates doesn't hang waiting for input).
|
|
197
|
+
if [ "${#CANDIDATES[@]}" -eq 0 ] && [ ! -t 0 ]; then
|
|
198
|
+
while IFS= read -r line; do
|
|
199
|
+
[ -n "$line" ] && CANDIDATES+=("$line")
|
|
200
|
+
done
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# Determine gitignore availability once, up front.
|
|
204
|
+
GITIGNORE_AVAILABLE=0
|
|
205
|
+
if git -C "$ROOT_ABS" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
206
|
+
GITIGNORE_AVAILABLE=1
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
# Fixed, path-component ignore-list. Kept as a single array so it stays easy
|
|
210
|
+
# to extend — see the header note above.
|
|
211
|
+
IGNORE_PATTERNS=(
|
|
212
|
+
node_modules vendor dist build .venv venv __pycache__ .next coverage
|
|
213
|
+
target .git .tox .mypy_cache .pytest_cache .cache
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# ---------------------------------------------------------------------------
|
|
217
|
+
# Build the JSON input for the python3 classifier: one record per candidate,
|
|
218
|
+
# already resolved to an absolute path plus its git-ignore verdict (computed
|
|
219
|
+
# in bash, since `git check-ignore` is a subprocess call best kept out of the
|
|
220
|
+
# python3 half). Pattern matching itself happens in python3 for portable,
|
|
221
|
+
# component-wise path matching (os.path based) rather than a second,
|
|
222
|
+
# divergent bash implementation.
|
|
223
|
+
# ---------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
NOTICES=()
|
|
226
|
+
RECORDS_JSON="[]"
|
|
227
|
+
|
|
228
|
+
if [ "${#CANDIDATES[@]}" -gt 0 ]; then
|
|
229
|
+
RECORDS_TMP=$(mktemp)
|
|
230
|
+
trap 'rm -f "$RECORDS_TMP"' EXIT
|
|
231
|
+
|
|
232
|
+
{
|
|
233
|
+
for candidate in "${CANDIDATES[@]}"; do
|
|
234
|
+
CAND_PATH="$ROOT_ABS/$candidate"
|
|
235
|
+
if [ ! -e "$CAND_PATH" ]; then
|
|
236
|
+
NOTICES+=("candidate '$candidate' does not exist under '$ROOT_ABS' — skipped")
|
|
237
|
+
continue
|
|
238
|
+
fi
|
|
239
|
+
if [ ! -d "$CAND_PATH" ]; then
|
|
240
|
+
NOTICES+=("candidate '$candidate' is not a directory — skipped")
|
|
241
|
+
continue
|
|
242
|
+
fi
|
|
243
|
+
CAND_ABS=$(cd -- "$CAND_PATH" && pwd -P)
|
|
244
|
+
|
|
245
|
+
GITIGNORED="false"
|
|
246
|
+
if [ "$GITIGNORE_AVAILABLE" -eq 1 ]; then
|
|
247
|
+
if git -C "$ROOT_ABS" check-ignore -q -- "$CAND_ABS" 2>/dev/null; then
|
|
248
|
+
GITIGNORED="true"
|
|
249
|
+
fi
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
printf '%s\t%s\t%s\n' "$candidate" "$CAND_ABS" "$GITIGNORED"
|
|
253
|
+
done
|
|
254
|
+
} > "$RECORDS_TMP"
|
|
255
|
+
|
|
256
|
+
if [ "$GITIGNORE_AVAILABLE" -eq 0 ]; then
|
|
257
|
+
NOTICES+=("root is not inside a git working tree — gitignore check skipped for all candidates")
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
RECORDS_JSON=$(python3 -c '
|
|
261
|
+
import json, sys
|
|
262
|
+
records = []
|
|
263
|
+
with open(sys.argv[1], "r") as f:
|
|
264
|
+
for line in f:
|
|
265
|
+
line = line.rstrip("\n")
|
|
266
|
+
if not line:
|
|
267
|
+
continue
|
|
268
|
+
parts = line.split("\t")
|
|
269
|
+
if len(parts) != 3:
|
|
270
|
+
continue
|
|
271
|
+
path, abs_path, gitignored = parts
|
|
272
|
+
records.append({"path": path, "absolute_path": abs_path, "gitignored": gitignored == "true"})
|
|
273
|
+
print(json.dumps(records))
|
|
274
|
+
' "$RECORDS_TMP")
|
|
275
|
+
fi
|
|
276
|
+
|
|
277
|
+
NOTICES_JSON=$(python3 -c '
|
|
278
|
+
import json, sys
|
|
279
|
+
print(json.dumps(sys.argv[1:]))
|
|
280
|
+
' "${NOTICES[@]:-}")
|
|
281
|
+
# printf with an empty array above can leave one empty-string arg; strip it.
|
|
282
|
+
if [ "${#NOTICES[@]}" -eq 0 ]; then
|
|
283
|
+
NOTICES_JSON="[]"
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
python3 -c '
|
|
287
|
+
import json, sys, os
|
|
288
|
+
|
|
289
|
+
root = sys.argv[1]
|
|
290
|
+
root_absolute = sys.argv[2]
|
|
291
|
+
records = json.loads(sys.argv[3])
|
|
292
|
+
notices = json.loads(sys.argv[4])
|
|
293
|
+
patterns = sys.argv[5:]
|
|
294
|
+
|
|
295
|
+
def pattern_reason(path):
|
|
296
|
+
# Path-COMPONENT match, never substring: split on os.sep and compare
|
|
297
|
+
# each component (and a suffix-glob case for *.egg-info) against the
|
|
298
|
+
# fixed pattern list.
|
|
299
|
+
components = [c for c in path.split(os.sep) if c]
|
|
300
|
+
for comp in components:
|
|
301
|
+
for pat in patterns:
|
|
302
|
+
if pat.startswith("*."):
|
|
303
|
+
suffix = pat[1:]
|
|
304
|
+
if comp.endswith(suffix):
|
|
305
|
+
return "pattern:" + pat
|
|
306
|
+
elif comp == pat:
|
|
307
|
+
return "pattern:" + pat
|
|
308
|
+
return None
|
|
309
|
+
|
|
310
|
+
ignored = []
|
|
311
|
+
remaining = []
|
|
312
|
+
|
|
313
|
+
for rec in records:
|
|
314
|
+
path = rec["path"]
|
|
315
|
+
abs_path = rec["absolute_path"]
|
|
316
|
+
if rec.get("gitignored"):
|
|
317
|
+
ignored.append({"path": path, "absolute_path": abs_path, "reason": "gitignore"})
|
|
318
|
+
continue
|
|
319
|
+
reason = pattern_reason(path)
|
|
320
|
+
if reason:
|
|
321
|
+
ignored.append({"path": path, "absolute_path": abs_path, "reason": reason})
|
|
322
|
+
else:
|
|
323
|
+
remaining.append({"path": path, "absolute_path": abs_path})
|
|
324
|
+
|
|
325
|
+
report = {
|
|
326
|
+
"script": "directory-triage.sh",
|
|
327
|
+
"version": 1,
|
|
328
|
+
"root": root,
|
|
329
|
+
"root_absolute": root_absolute,
|
|
330
|
+
"candidate_count": len(records),
|
|
331
|
+
"ignored_count": len(ignored),
|
|
332
|
+
"remaining_count": len(remaining),
|
|
333
|
+
"ignored": ignored,
|
|
334
|
+
"remaining": remaining,
|
|
335
|
+
"notices": notices,
|
|
336
|
+
}
|
|
337
|
+
print(json.dumps(report, indent=2))
|
|
338
|
+
' "$ROOT" "$ROOT_ABS" "$RECORDS_JSON" "$NOTICES_JSON" "${IGNORE_PATTERNS[@]}" > "${JSON_OUT:-/dev/stdout}"
|
|
339
|
+
|
|
340
|
+
if [ -n "$JSON_OUT" ]; then
|
|
341
|
+
cat "$JSON_OUT"
|
|
342
|
+
fi
|