@mmerterden/multi-agent-pipeline 20.8.0 → 20.8.2
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/CHANGELOG.md +48 -0
- package/docs/facts.json +3 -3
- package/index.js +1 -0
- package/install/_codex-agents.mjs +1 -1
- package/install/_common.mjs +486 -53
- package/install/_mcp-register.mjs +173 -117
- package/install/claude.mjs +281 -220
- package/install/codex.mjs +7 -7
- package/install/copilot.mjs +13 -11
- package/install/index.mjs +92 -27
- package/install/templates/claude-hooks.json +9 -9
- package/manifest.json +112 -114
- package/package.json +5 -2
- package/pipeline/commands/multi-agent/update/SKILL.md +28 -17
- package/pipeline/lib/confusables.json +79 -33
- package/pipeline/lib/extract-conventions.sh +3 -3
- package/pipeline/lib/json-file-lock.mjs +27 -7
- package/pipeline/lib/normalize-text.mjs +86 -17
- package/pipeline/lib/outbound-gate.mjs +13 -4
- package/pipeline/lib/redact.mjs +87 -13
- package/pipeline/multi-agent-refs/analysis/evidence.md +1 -1
- package/pipeline/multi-agent-refs/analysis/synthesis.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
- package/pipeline/multi-agent-refs/conventions-defaults.md +1 -1
- package/pipeline/multi-agent-refs/features/unattended-security.md +2 -2
- package/pipeline/scripts/agent-guard.py +150 -25
- package/pipeline/scripts/audit-log.sh +3 -4
- package/pipeline/scripts/autopilot-runner.mjs +14 -5
- package/pipeline/scripts/doctor.mjs +8 -2
- package/pipeline/scripts/gen-skills-index.mjs +13 -1
- package/pipeline/scripts/log-metric.sh +9 -3
- package/pipeline/scripts/migrate-prefs.mjs +18 -4
- package/pipeline/scripts/pre-commit-check.sh +119 -27
- package/pipeline/scripts/scan-agent-config.sh +9 -9
- package/pipeline/scripts/unattended_policy.py +12 -3
- package/pipeline/scripts/uninstall.mjs +88 -1
- package/pipeline/scripts/usage-identity.mjs +1 -1
- package/pipeline/scripts/usage-register.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +30 -30
- package/pipeline/skills/shared/README.md +64 -64
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +2 -2
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +6 -0
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +3 -0
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +3 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +3 -4
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +5 -3
- package/pipeline/skills/shared/external/authentication/SKILL.md +29 -17
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +3 -1
- package/pipeline/skills/shared/external/background-processing/SKILL.md +10 -8
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +7 -7
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +6 -3
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +43 -0
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +4 -2
- package/pipeline/skills/shared/external/core-data/SKILL.md +12 -2
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +31 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +1 -1
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +11 -5
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
- package/pipeline/skills/shared/external/ios-module-structure/modules/_TEMPLATE.yml +1 -1
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +6 -6
- package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +3 -2
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +20 -18
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +4 -4
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -9
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +201 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +64 -30
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +20 -12
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +7 -7
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +26 -12
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +4 -3
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +51 -24
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +19 -9
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +26 -19
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +39 -46
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +37 -63
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +4 -2
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +5 -4
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +3 -2
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +2 -2
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +4 -4
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +15 -6
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +2 -1
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -4
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +1 -1
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +25 -6
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +6 -1
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +3 -2
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +1 -1
- package/pipeline/skills/shared/external/swift-security/SKILL.md +10 -8
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +8 -5
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +13 -9
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +7 -6
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +8 -5
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +3 -2
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +5 -3
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +17 -9
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +4 -2
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +13 -6
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +3 -1
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +8 -5
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +15 -7
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +8 -6
- package/pipeline/scripts/gen-ref-toc.mjs +0 -279
- package/pipeline/scripts/make-manifest.mjs +0 -199
- package/pipeline/scripts/scorecard-snapshot.mjs +0 -178
|
@@ -85,6 +85,13 @@ process.stdin.on("end", () => {
|
|
|
85
85
|
});
|
|
86
86
|
' 2>/dev/null || true)"
|
|
87
87
|
fi
|
|
88
|
+
# A command that never spells git, even with its quotes and backslashes
|
|
89
|
+
# removed (g'i't, "git", \git), cannot run it through any path the helper
|
|
90
|
+
# below would recognise, so it returns before paying for the parse.
|
|
91
|
+
if [ -n "$HOOK_COMMAND" ] \
|
|
92
|
+
&& ! printf '%s' "$HOOK_COMMAND" | tr -d "\"'\\\\" | grep -qi 'git'; then
|
|
93
|
+
exit 0
|
|
94
|
+
fi
|
|
88
95
|
if [ -n "$HOOK_COMMAND" ]; then
|
|
89
96
|
# Commit detection is a tokenizer, not a regex: agent-guard.py shares its
|
|
90
97
|
# shell parser through `--git-subcommand`, so an absolute git path, a
|
|
@@ -156,7 +163,7 @@ is_binary() {
|
|
|
156
163
|
# a diff, or the whole file for something not yet tracked at all - every line
|
|
157
164
|
# of a brand-new file is an addition).
|
|
158
165
|
scan_file() {
|
|
159
|
-
local file="$1" content="$2"
|
|
166
|
+
local file="$1" content="$2" folded="${3:-}"
|
|
160
167
|
|
|
161
168
|
# .env files
|
|
162
169
|
if echo "$file" | grep -qE '\.env($|\.)'; then
|
|
@@ -174,17 +181,11 @@ scan_file() {
|
|
|
174
181
|
|
|
175
182
|
[ -z "$content" ] && return 0
|
|
176
183
|
|
|
177
|
-
# A token written with an invisible character or a look-alike letter:
|
|
178
|
-
# folded copy
|
|
179
|
-
if
|
|
180
|
-
|
|
181
|
-
&& [ -f "$PRE_COMMIT_DIR/../lib/normalize-text.mjs" ]; then
|
|
182
|
-
local folded
|
|
183
|
-
folded=$(printf '%s' "$content" | node "$PRE_COMMIT_DIR/../lib/normalize-text.mjs" 2>/dev/null) || folded=""
|
|
184
|
-
if [ -n "$folded" ] && [ "$folded" != "$content" ]; then
|
|
185
|
-
content="$content
|
|
184
|
+
# A token written with an invisible character or a look-alike letter: the
|
|
185
|
+
# folded copy (see fold_and_scan) is scanned together with the raw text.
|
|
186
|
+
if [ -n "$folded" ] && [ "$folded" != "$content" ]; then
|
|
187
|
+
content="$content
|
|
186
188
|
$folded"
|
|
187
|
-
fi
|
|
188
189
|
fi
|
|
189
190
|
|
|
190
191
|
# API keys / tokens (key = "long_string" pattern)
|
|
@@ -281,15 +282,50 @@ $folded"
|
|
|
281
282
|
if [ -n "$candidates" ]; then
|
|
282
283
|
entropy_hit=$(printf '%s\n' "$candidates" | awk '
|
|
283
284
|
# Word-shaped identifier: letters and _ or - only, split into segments at
|
|
284
|
-
# separators and case boundaries (UIApplication -> UI, Application),
|
|
285
|
-
# the segments
|
|
286
|
-
#
|
|
287
|
-
#
|
|
288
|
-
#
|
|
289
|
-
#
|
|
290
|
-
|
|
285
|
+
# separators and case boundaries (UIApplication -> UI, Application),
|
|
286
|
+
# where the segments average 4+ letters, none is longer than 24, and the
|
|
287
|
+
# letter pairs inside them read like English. PAIR scores every pair as
|
|
288
|
+
# log2 of how often it occurs across a 235k-word English word list
|
|
289
|
+
# relative to a uniform draw, rounded and clipped to -4..4 (stored +4,
|
|
290
|
+
# one row per first letter). Identifiers made of words average above
|
|
291
|
+
# +0.5 per pair even with an abbreviation or two (Qr, Ffp, Otp, Kg);
|
|
292
|
+
# uniformly random letters average below +0.1, so the mean must reach
|
|
293
|
+
# +0.25. Clipping keeps one unusual pair from outweighing the rest. An
|
|
294
|
+
# all-caps segment of up to five letters inside a token that also has
|
|
295
|
+
# lowercase letters is an acronym (URL, HTTP, JSON) and is not scored.
|
|
296
|
+
BEGIN {
|
|
297
|
+
rows = "06655352403757150767433232 52015000500600400421400010 " \
|
|
298
|
+
"60306006504400600515500040 50026021600313500420301030 " \
|
|
299
|
+
"63564442401657452876433420 40004300400400400302400020 " \
|
|
300
|
+
"50005033500423400510400030 60006000600322600403301050 " \
|
|
301
|
+
"64764551103657651577350105 20002000000000200000200000 " \
|
|
302
|
+
"30005001400201200010100010 71237220701622630024520060 " \
|
|
303
|
+
"64006000600032650010400040 62667462613324631357422031 " \
|
|
304
|
+
"44553352403667560766644321 60006006500501630644400030 " \
|
|
305
|
+
"00000000000000000000400000 73447233703354740445432050 " \
|
|
306
|
+
"51506105602453552067502040 61307106700421600624502050 " \
|
|
307
|
+
"44443230401657240665000000 40006000400000300000100000 " \
|
|
308
|
+
"40004002400102400100000000 20102000300000220002000020 " \
|
|
309
|
+
"31332020200433340343000000 30004000200000300000000011";
|
|
310
|
+
split(rows, row, " ");
|
|
311
|
+
alpha = "abcdefghijklmnopqrstuvwxyz";
|
|
312
|
+
for (r = 1; r <= 26; r++)
|
|
313
|
+
for (k = 1; k <= 26; k++)
|
|
314
|
+
PAIR[substr(alpha, r, 1) substr(alpha, k, 1)] = substr(row[r], k, 1) - 4;
|
|
315
|
+
}
|
|
316
|
+
function score_segment(seg, has_lower, l, j) {
|
|
317
|
+
if (has_lower && seg ~ /^[A-Z]+$/ && length(seg) <= 5) return;
|
|
318
|
+
l = tolower(seg);
|
|
319
|
+
for (j = 2; j <= length(l); j++) {
|
|
320
|
+
pairs++;
|
|
321
|
+
pair_score += PAIR[substr(l, j - 1, 2)];
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function wordlike(t, n, i, c, p, nx, seg, nseg, total, maxlen, split_here, has_lower) {
|
|
291
325
|
if (t !~ /^[A-Za-z_-]+$/) return 0;
|
|
292
|
-
|
|
326
|
+
has_lower = (t ~ /[a-z]/);
|
|
327
|
+
pairs = 0; pair_score = 0;
|
|
328
|
+
n = length(t); seg = ""; nseg = 0; total = 0; maxlen = 0;
|
|
293
329
|
for (i = 1; i <= n + 1; i++) {
|
|
294
330
|
c = (i <= n) ? substr(t, i, 1) : "_";
|
|
295
331
|
split_here = 0;
|
|
@@ -302,13 +338,13 @@ $folded"
|
|
|
302
338
|
if (split_here && seg != "") {
|
|
303
339
|
nseg++; total += length(seg);
|
|
304
340
|
if (length(seg) > maxlen) maxlen = length(seg);
|
|
305
|
-
|
|
341
|
+
score_segment(seg, has_lower);
|
|
306
342
|
seg = "";
|
|
307
343
|
}
|
|
308
344
|
if (c != "_" && c != "-") seg = seg c;
|
|
309
345
|
}
|
|
310
|
-
if (nseg == 0) return 0;
|
|
311
|
-
return (total / nseg >= 4 && maxlen <= 24 &&
|
|
346
|
+
if (nseg == 0 || pairs == 0) return 0;
|
|
347
|
+
return (total / nseg >= 4 && maxlen <= 24 && pair_score * 4 >= pairs);
|
|
312
348
|
}
|
|
313
349
|
{
|
|
314
350
|
s=$0;
|
|
@@ -357,9 +393,63 @@ $folded"
|
|
|
357
393
|
fi
|
|
358
394
|
}
|
|
359
395
|
|
|
396
|
+
# Files are queued and scanned together so that the look-alike fold runs in
|
|
397
|
+
# one node process for every non-ASCII file of the run rather than one per
|
|
398
|
+
# file. ASCII content folds to itself and is not sent.
|
|
399
|
+
Q_FILES=()
|
|
400
|
+
Q_CONTENTS=()
|
|
401
|
+
queue_file() {
|
|
402
|
+
Q_FILES+=("$1")
|
|
403
|
+
Q_CONTENTS+=("$2")
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
FOLD_WARNED=0
|
|
407
|
+
fold_warn() {
|
|
408
|
+
[ "$FOLD_WARNED" = "1" ] && return 0
|
|
409
|
+
FOLD_WARNED=1
|
|
410
|
+
echo "pre-commit-check: $1 - non-ASCII content scanned without look-alike folding" >&2
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
fold_and_scan() {
|
|
414
|
+
local i n=${#Q_FILES[@]} lib="$PRE_COMMIT_DIR/../lib/normalize-text.mjs" out rec
|
|
415
|
+
local -a idx=() folded=()
|
|
416
|
+
for ((i = 0; i < n; i++)); do
|
|
417
|
+
folded[i]=""
|
|
418
|
+
if printf '%s' "${Q_CONTENTS[i]}" | LC_ALL=C grep -q '[^ -~[:space:]]' 2>/dev/null; then
|
|
419
|
+
idx+=("$i")
|
|
420
|
+
fi
|
|
421
|
+
done
|
|
422
|
+
if [ "${#idx[@]}" -gt 0 ]; then
|
|
423
|
+
if ! command -v node >/dev/null 2>&1 || [ ! -f "$lib" ]; then
|
|
424
|
+
fold_warn "node or lib/normalize-text.mjs unavailable"
|
|
425
|
+
else
|
|
426
|
+
out=$(mktemp "${TMPDIR:-/tmp}/pre-commit-fold.XXXXXX") || out=""
|
|
427
|
+
if [ -n "$out" ] && for i in "${idx[@]}"; do printf '%s\0' "${Q_CONTENTS[i]}"; done \
|
|
428
|
+
| node "$lib" --nul >"$out" 2>/dev/null; then
|
|
429
|
+
local -a recs=()
|
|
430
|
+
while IFS= read -r -d '' rec; do recs+=("$rec"); done <"$out"
|
|
431
|
+
if [ "${#recs[@]}" -eq "${#idx[@]}" ]; then
|
|
432
|
+
for ((i = 0; i < ${#idx[@]}; i++)); do folded[idx[i]]="${recs[i]}"; done
|
|
433
|
+
else
|
|
434
|
+
fold_warn "the look-alike fold returned ${#recs[@]} of ${#idx[@]} records"
|
|
435
|
+
fi
|
|
436
|
+
else
|
|
437
|
+
fold_warn "the look-alike fold (node lib/normalize-text.mjs) failed"
|
|
438
|
+
fi
|
|
439
|
+
[ -n "$out" ] && rm -f "$out"
|
|
440
|
+
fi
|
|
441
|
+
fi
|
|
442
|
+
for ((i = 0; i < n; i++)); do
|
|
443
|
+
scan_file "${Q_FILES[i]}" "${Q_CONTENTS[i]}" "${folded[i]}"
|
|
444
|
+
done
|
|
445
|
+
Q_FILES=()
|
|
446
|
+
Q_CONTENTS=()
|
|
447
|
+
}
|
|
448
|
+
|
|
360
449
|
if [ -n "$SCAN_FILE_TARGET" ]; then
|
|
361
450
|
is_binary "$SCAN_FILE_TARGET" && exit 0
|
|
362
|
-
|
|
451
|
+
queue_file "$SCAN_FILE_TARGET" "$(cat "$SCAN_FILE_TARGET" 2>/dev/null)"
|
|
452
|
+
fold_and_scan
|
|
363
453
|
if [ "$FOUND" -eq 1 ]; then
|
|
364
454
|
echo "" >&2
|
|
365
455
|
echo "Secret check failed on $SCAN_FILE_TARGET." >&2
|
|
@@ -402,10 +492,11 @@ if [ -n "$SCAN_RANGE" ]; then
|
|
|
402
492
|
# blob at this commit so a NUL-laden file cannot hide a key.
|
|
403
493
|
content=$(gitscan show --text --no-textconv "${commit}:${file}" 2>/dev/null || true)
|
|
404
494
|
fi
|
|
405
|
-
|
|
495
|
+
queue_file "$file" "$content"
|
|
406
496
|
done < <(gitscan diff-tree --no-commit-id -r --root -m --first-parent \
|
|
407
497
|
--name-only -z --diff-filter=d "$commit" 2>/dev/null)
|
|
408
498
|
done
|
|
499
|
+
fold_and_scan
|
|
409
500
|
if [ "$FOUND" -eq 1 ]; then
|
|
410
501
|
echo "" >&2
|
|
411
502
|
echo "Secret check failed on $SCAN_RANGE." >&2
|
|
@@ -424,7 +515,7 @@ while IFS= read -r -d '' file; do
|
|
|
424
515
|
[ ! -f "$file" ] && continue
|
|
425
516
|
is_binary "$file" && continue
|
|
426
517
|
content=$(git diff --cached -- "$file" 2>/dev/null | grep "^+" | grep -v "^+++" || true)
|
|
427
|
-
|
|
518
|
+
queue_file "$file" "$content"
|
|
428
519
|
done < <(git diff --cached --name-only -z 2>/dev/null)
|
|
429
520
|
|
|
430
521
|
if [ "$SCAN_UNSTAGED" = "1" ]; then
|
|
@@ -435,7 +526,7 @@ if [ "$SCAN_UNSTAGED" = "1" ]; then
|
|
|
435
526
|
[ ! -f "$file" ] && continue
|
|
436
527
|
is_binary "$file" && continue
|
|
437
528
|
content=$(git diff HEAD -- "$file" 2>/dev/null | grep "^+" | grep -v "^+++" || true)
|
|
438
|
-
|
|
529
|
+
queue_file "$file" "$content"
|
|
439
530
|
done < <(git diff HEAD --name-only -z 2>/dev/null)
|
|
440
531
|
|
|
441
532
|
# Untracked (brand-new) files - there is no diff to take yet, so every line
|
|
@@ -445,9 +536,10 @@ if [ "$SCAN_UNSTAGED" = "1" ]; then
|
|
|
445
536
|
[ ! -f "$file" ] && continue
|
|
446
537
|
is_binary "$file" && continue
|
|
447
538
|
content=$(sed 's/^/+/' "$file" 2>/dev/null || true)
|
|
448
|
-
|
|
539
|
+
queue_file "$file" "$content"
|
|
449
540
|
done < <(git ls-files --others --exclude-standard -z 2>/dev/null)
|
|
450
541
|
fi
|
|
542
|
+
fold_and_scan
|
|
451
543
|
|
|
452
544
|
if [ $FOUND -eq 1 ]; then
|
|
453
545
|
echo "" >&2
|
|
@@ -123,16 +123,16 @@ for _event, entries in (t.get("hooks") or {}).items():
|
|
|
123
123
|
for e in entries:
|
|
124
124
|
for h in e.get("hooks", []):
|
|
125
125
|
c = h.get("command", "")
|
|
126
|
-
# Allow only `bash $HOME/.claude/scripts/<name
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
# still runs on the whole string.
|
|
126
|
+
# Allow only `bash "$HOME/.claude/scripts/<name>"` (the path quoted
|
|
127
|
+
# so a HOME with a space still runs it, or unquoted) plus plain long
|
|
128
|
+
# flags. SessionEnd needs one: capture-flush takes --if-stale so a
|
|
129
|
+
# session end does not re-flush a run that already finished. What
|
|
130
|
+
# stays banned is everything that could turn a flag into a second
|
|
131
|
+
# command - no spaces inside a token, no quotes beyond the pair
|
|
132
|
+
# around the path, no metacharacters, and the eval/substitution
|
|
133
|
+
# check below still runs on the whole string.
|
|
134
134
|
if not re.fullmatch(
|
|
135
|
-
r'bash \$HOME/\.claude/scripts/[A-Za-z0-9._-]+(?: --[a-z0-9][a-z0-9-]*)*',
|
|
135
|
+
r'bash (?:"\$HOME/\.claude/scripts/[A-Za-z0-9._-]+"|\$HOME/\.claude/scripts/[A-Za-z0-9._-]+)(?: --[a-z0-9][a-z0-9-]*)*',
|
|
136
136
|
c.strip()):
|
|
137
137
|
bad.append(c)
|
|
138
138
|
if re.search(r'\beval\b|\$\(|`|[;&|><]', c):
|
|
@@ -451,7 +451,14 @@ def structural_refusal(cmd):
|
|
|
451
451
|
return None
|
|
452
452
|
|
|
453
453
|
|
|
454
|
+
# shlex is quadratic in a token's length; a longer segment is split on
|
|
455
|
+
# whitespace with quotes and backslashes removed (agent-guard.py coarse_tokens).
|
|
456
|
+
SHLEX_MAX_CHARS = 16384
|
|
457
|
+
|
|
458
|
+
|
|
454
459
|
def tokenize(text):
|
|
460
|
+
if len(text) > SHLEX_MAX_CHARS:
|
|
461
|
+
return [re.sub(r"[\"'\\]", "", t) for t in text.split()]
|
|
455
462
|
try:
|
|
456
463
|
return shlex.split(text)
|
|
457
464
|
except ValueError:
|
|
@@ -2603,9 +2610,11 @@ TOOLKIT_PREFIX = "mcp__multi-agent-toolkit__"
|
|
|
2603
2610
|
# Keys a toolkit tool uses for a filesystem destination it writes. Checked with
|
|
2604
2611
|
# check_path so a toolkit call cannot write a screenshot, recording, build
|
|
2605
2612
|
# output or export over a protected path. MCP tools run outside the OS sandbox,
|
|
2606
|
-
# so this is their only path boundary.
|
|
2607
|
-
|
|
2608
|
-
|
|
2613
|
+
# so this is their only path boundary. The toolkit's write parameters are
|
|
2614
|
+
# listed in test/fixtures/toolkit-path-params.json, generated from its tool
|
|
2615
|
+
# schemas, and the unattended-guard test checks each one against this list.
|
|
2616
|
+
TOOLKIT_PATH_KEYS = ("path", "filename", "out_dir", "output", "output_path", "output_dir",
|
|
2617
|
+
"output_graph", "export_path", "derived_data_path", "result_bundle_path",
|
|
2609
2618
|
"figma_path", "screenshot_path", "video_path", "file_path")
|
|
2610
2619
|
|
|
2611
2620
|
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
writeFileSync,
|
|
50
50
|
} from "fs";
|
|
51
51
|
import { execFileSync } from "child_process";
|
|
52
|
+
import { createHash } from "crypto";
|
|
52
53
|
import { join, dirname } from "path";
|
|
53
54
|
import { homedir } from "os";
|
|
54
55
|
import { pathToFileURL } from "url";
|
|
@@ -82,6 +83,22 @@ export const PIPELINE_AGENT_FILES = [
|
|
|
82
83
|
*/
|
|
83
84
|
export const PIPELINE_CORE_SKILL_DIRS = ["apple-archive-compliance", "google-play-compliance"];
|
|
84
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Files the installer ships into ~/.claude/templates (install/templates/).
|
|
88
|
+
* Used only for an install that predates the install manifest; a unit test
|
|
89
|
+
* asserts it matches the source tree.
|
|
90
|
+
*/
|
|
91
|
+
export const PIPELINE_TEMPLATE_FILES = [
|
|
92
|
+
"claude-hooks.json",
|
|
93
|
+
"codex-instructions.md",
|
|
94
|
+
"copilot-instructions.md",
|
|
95
|
+
"multi-agent-autopilot-awake.plist.template",
|
|
96
|
+
"multi-agent-autopilot.plist.template",
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
/** Written by install/_common.mjs writeInstallManifest at the host dir root. */
|
|
100
|
+
export const INSTALL_MANIFEST = ".pipeline-manifest.json";
|
|
101
|
+
|
|
85
102
|
const flags = process.argv.slice(2).filter((a) => a !== "uninstall");
|
|
86
103
|
|
|
87
104
|
const TOOL_FLAGS = [
|
|
@@ -238,6 +255,65 @@ function preservePrefsCredentialMapping(prefsPath) {
|
|
|
238
255
|
);
|
|
239
256
|
}
|
|
240
257
|
|
|
258
|
+
function sha256Of(path) {
|
|
259
|
+
try {
|
|
260
|
+
return createHash("sha256").update(readFileSync(path)).digest("hex");
|
|
261
|
+
} catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function rmdirIfEmpty(dir) {
|
|
267
|
+
try {
|
|
268
|
+
if (readdirSync(dir).length > 0) return;
|
|
269
|
+
} catch {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
rmIfExists(dir);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Remove the co-owned files the install manifest records (top-level commands,
|
|
277
|
+
* lib/, templates/), each only while its hash is still the one installed. An
|
|
278
|
+
* edited file is the user's now and stays. Returns false when there is no
|
|
279
|
+
* manifest, so the caller falls back to the pre-manifest cleanup.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} hostDir e.g. ~/.claude
|
|
282
|
+
* @param {string[]} keepInLib lib/ files kept on purpose
|
|
283
|
+
* @returns {boolean}
|
|
284
|
+
*/
|
|
285
|
+
function rmManifestFiles(hostDir, keepInLib) {
|
|
286
|
+
const manifestPath = join(hostDir, INSTALL_MANIFEST);
|
|
287
|
+
let files;
|
|
288
|
+
try {
|
|
289
|
+
files = JSON.parse(readFileSync(manifestPath, "utf-8"))?.files;
|
|
290
|
+
} catch {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
if (!files || typeof files !== "object") return false;
|
|
294
|
+
const kept = [];
|
|
295
|
+
const dirs = new Set();
|
|
296
|
+
for (const [rel, hash] of Object.entries(files)) {
|
|
297
|
+
if (typeof rel !== "string" || rel.split("/").some((part) => part === ".." || part === "")) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (rel.startsWith("lib/") && keepInLib.includes(rel.slice(4))) continue;
|
|
301
|
+
const path = join(hostDir, rel);
|
|
302
|
+
if (!existsSync(path)) continue;
|
|
303
|
+
dirs.add(dirname(path));
|
|
304
|
+
if (sha256Of(path) === hash) rmIfExists(path);
|
|
305
|
+
else kept.push(rel);
|
|
306
|
+
}
|
|
307
|
+
for (const dir of [...dirs].sort((a, b) => b.length - a.length)) {
|
|
308
|
+
if (dir !== join(hostDir, "commands")) rmdirIfEmpty(dir);
|
|
309
|
+
}
|
|
310
|
+
if (kept.length > 0) {
|
|
311
|
+
console.log(` kept ${kept.length} file(s) you changed since install: ${kept.join(", ")}`);
|
|
312
|
+
}
|
|
313
|
+
rmIfExists(manifestPath);
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
|
|
241
317
|
/**
|
|
242
318
|
* Conditional rm - respects --dry-run.
|
|
243
319
|
* @param {string} path
|
|
@@ -766,8 +842,19 @@ export async function main() {
|
|
|
766
842
|
rmIfExists(join(CLAUDE, "scripts"));
|
|
767
843
|
// Pipeline-managed trees the installer lays down alongside scripts/.
|
|
768
844
|
rmIfExists(join(CLAUDE, "multi-agent-refs"));
|
|
845
|
+
rmIfExists(join(CLAUDE, ".pipeline-swap"));
|
|
769
846
|
rmIfExists(join(CLAUDE, "schemas"));
|
|
770
|
-
|
|
847
|
+
if (!rmManifestFiles(CLAUDE, KEEP_IN_LIB)) {
|
|
848
|
+
// An install that predates the manifest wiped lib/ and templates/ on
|
|
849
|
+
// every run, so their shipped names are the pipeline's. The top-level
|
|
850
|
+
// commands cannot be told from the user's own and stay.
|
|
851
|
+
rmDirExcept(join(CLAUDE, "lib"), KEEP_IN_LIB);
|
|
852
|
+
rmMatchingFiles(join(CLAUDE, "templates"), (name) => PIPELINE_TEMPLATE_FILES.includes(name));
|
|
853
|
+
rmdirIfEmpty(join(CLAUDE, "templates"));
|
|
854
|
+
console.log(
|
|
855
|
+
` note: no ${INSTALL_MANIFEST}; top-level files in ${join(CLAUDE, "commands")} were left in place`,
|
|
856
|
+
);
|
|
857
|
+
}
|
|
771
858
|
rmIfExists(join(CLAUDE, ".pipeline-version"));
|
|
772
859
|
// NOTE: ~/.claude/rules/ is USER-OWNED. install lays baseline rules down
|
|
773
860
|
// write-if-missing and NEVER overwrites them (install/claude.mjs
|
|
@@ -200,7 +200,7 @@ function persist(prefsPath, patch, env) {
|
|
|
200
200
|
j.global.usageLog = { ...(j.global.usageLog || {}), ...patch };
|
|
201
201
|
return j;
|
|
202
202
|
},
|
|
203
|
-
{ mode: 0o600, timeoutMs: lockTimeoutFrom(env) },
|
|
203
|
+
{ mode: 0o600, followSymlink: true, timeoutMs: lockTimeoutFrom(env) },
|
|
204
204
|
);
|
|
205
205
|
return written !== undefined;
|
|
206
206
|
} catch {
|
|
@@ -230,7 +230,7 @@ export function persistPrefs(
|
|
|
230
230
|
if (registeredAs) j.global.usageLog.registeredAs = registeredAs;
|
|
231
231
|
return j;
|
|
232
232
|
},
|
|
233
|
-
{ mode: 0o600, timeoutMs: lockTimeoutFrom(process.env) },
|
|
233
|
+
{ mode: 0o600, followSymlink: true, timeoutMs: lockTimeoutFrom(process.env) },
|
|
234
234
|
);
|
|
235
235
|
return written !== undefined;
|
|
236
236
|
} catch {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-27T07:55:06Z",
|
|
4
4
|
"skillCount": 218,
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
@@ -269,7 +269,7 @@
|
|
|
269
269
|
},
|
|
270
270
|
{
|
|
271
271
|
"path": "shared/external/alarmkit/SKILL.md",
|
|
272
|
-
"sha256": "
|
|
272
|
+
"sha256": "ce285a6e95d03e3bc2f5c3150822e6ee2d07a4b629b81e8c0df81e6840161cb9"
|
|
273
273
|
},
|
|
274
274
|
{
|
|
275
275
|
"path": "shared/external/android-architecture/SKILL.md",
|
|
@@ -325,15 +325,15 @@
|
|
|
325
325
|
},
|
|
326
326
|
{
|
|
327
327
|
"path": "shared/external/app-store-optimization/SKILL.md",
|
|
328
|
-
"sha256": "
|
|
328
|
+
"sha256": "6015dd7b4c8661e23b677c53c391a3e39116fee2ad6b2b031b3d310eef4a3e2b"
|
|
329
329
|
},
|
|
330
330
|
{
|
|
331
331
|
"path": "shared/external/app-store-review/SKILL.md",
|
|
332
|
-
"sha256": "
|
|
332
|
+
"sha256": "0b77f648fd2d54e991f1c25540629f0a7d4fd2a1b30de7f8aaf07331b93963ea"
|
|
333
333
|
},
|
|
334
334
|
{
|
|
335
335
|
"path": "shared/external/apple-on-device-ai/SKILL.md",
|
|
336
|
-
"sha256": "
|
|
336
|
+
"sha256": "a86a4032c158cdf5523009e332618c0e460a70fe695be99fed8c8767a69afff8"
|
|
337
337
|
},
|
|
338
338
|
{
|
|
339
339
|
"path": "shared/external/architecture/SKILL.md",
|
|
@@ -341,7 +341,7 @@
|
|
|
341
341
|
},
|
|
342
342
|
{
|
|
343
343
|
"path": "shared/external/authentication/SKILL.md",
|
|
344
|
-
"sha256": "
|
|
344
|
+
"sha256": "4d3da49257432d30196b9970721f801524a7cdce4e6ddcaefc4fe31832a49157"
|
|
345
345
|
},
|
|
346
346
|
{
|
|
347
347
|
"path": "shared/external/avkit/SKILL.md",
|
|
@@ -349,7 +349,7 @@
|
|
|
349
349
|
},
|
|
350
350
|
{
|
|
351
351
|
"path": "shared/external/background-processing/SKILL.md",
|
|
352
|
-
"sha256": "
|
|
352
|
+
"sha256": "876516478a6019297b886f754e2ec59e96bc73419cfff753dc7bdc7a4336b6e9"
|
|
353
353
|
},
|
|
354
354
|
{
|
|
355
355
|
"path": "shared/external/backlog/SKILL.md",
|
|
@@ -357,7 +357,7 @@
|
|
|
357
357
|
},
|
|
358
358
|
{
|
|
359
359
|
"path": "shared/external/callkit-voip/SKILL.md",
|
|
360
|
-
"sha256": "
|
|
360
|
+
"sha256": "44646c2f192170a5468c18a0c78fb77ffcf135bb8d8cf2a7581ac2d1c0909eeb"
|
|
361
361
|
},
|
|
362
362
|
{
|
|
363
363
|
"path": "shared/external/ci-cd-pipelines/SKILL.md",
|
|
@@ -369,7 +369,7 @@
|
|
|
369
369
|
},
|
|
370
370
|
{
|
|
371
371
|
"path": "shared/external/cloudkit-sync/SKILL.md",
|
|
372
|
-
"sha256": "
|
|
372
|
+
"sha256": "9464e93c0435628f3628a6f1775b7478dea75514b0fb3aa3cc09c7cec83be48d"
|
|
373
373
|
},
|
|
374
374
|
{
|
|
375
375
|
"path": "shared/external/compose-components/SKILL.md",
|
|
@@ -393,11 +393,11 @@
|
|
|
393
393
|
},
|
|
394
394
|
{
|
|
395
395
|
"path": "shared/external/core-bluetooth/SKILL.md",
|
|
396
|
-
"sha256": "
|
|
396
|
+
"sha256": "3b7b41c7287cb86ee3c5bc477952c659fddbf050ceefbad95ab8a95ff15750be"
|
|
397
397
|
},
|
|
398
398
|
{
|
|
399
399
|
"path": "shared/external/core-data/SKILL.md",
|
|
400
|
-
"sha256": "
|
|
400
|
+
"sha256": "c232488bc3de3542b25a8b40ba7da648a1c16b8fd467ade21dbde28620ab2c26"
|
|
401
401
|
},
|
|
402
402
|
{
|
|
403
403
|
"path": "shared/external/core-motion/SKILL.md",
|
|
@@ -405,11 +405,11 @@
|
|
|
405
405
|
},
|
|
406
406
|
{
|
|
407
407
|
"path": "shared/external/core-nfc/SKILL.md",
|
|
408
|
-
"sha256": "
|
|
408
|
+
"sha256": "c838b7e38bb0c65f409674220ef9f6b668839ce2c0546609b75c69c1d7c18567"
|
|
409
409
|
},
|
|
410
410
|
{
|
|
411
411
|
"path": "shared/external/coreml/SKILL.md",
|
|
412
|
-
"sha256": "
|
|
412
|
+
"sha256": "b5f3fdf161a2bd2ab839a52093f0cb99c2c3026211e48a7d13dbab712e11b255"
|
|
413
413
|
},
|
|
414
414
|
{
|
|
415
415
|
"path": "shared/external/council/SKILL.md",
|
|
@@ -417,7 +417,7 @@
|
|
|
417
417
|
},
|
|
418
418
|
{
|
|
419
419
|
"path": "shared/external/cryptokit/SKILL.md",
|
|
420
|
-
"sha256": "
|
|
420
|
+
"sha256": "419f46a3058145f48e1f38e56741dc8074441132a1834812a6d9ad020e1c2c7e"
|
|
421
421
|
},
|
|
422
422
|
{
|
|
423
423
|
"path": "shared/external/css-modern/SKILL.md",
|
|
@@ -437,7 +437,7 @@
|
|
|
437
437
|
},
|
|
438
438
|
{
|
|
439
439
|
"path": "shared/external/device-integrity/SKILL.md",
|
|
440
|
-
"sha256": "
|
|
440
|
+
"sha256": "9d8c39c65f7b5918c25c7ef3dd438fe62901e61ecc6fa2d7ecbd7c9a0fcfa4c0"
|
|
441
441
|
},
|
|
442
442
|
{
|
|
443
443
|
"path": "shared/external/docker-expert/SKILL.md",
|
|
@@ -569,7 +569,7 @@
|
|
|
569
569
|
},
|
|
570
570
|
{
|
|
571
571
|
"path": "shared/external/localization-reuse-map/SKILL.md",
|
|
572
|
-
"sha256": "
|
|
572
|
+
"sha256": "0a7429a2bbc2454b12ae4fb9af1eb09cc5a5d1ab96c05c693fc4827747bd8d15"
|
|
573
573
|
},
|
|
574
574
|
{
|
|
575
575
|
"path": "shared/external/macos-menubar-tuist-app/SKILL.md",
|
|
@@ -581,7 +581,7 @@
|
|
|
581
581
|
},
|
|
582
582
|
{
|
|
583
583
|
"path": "shared/external/mapkit-location/SKILL.md",
|
|
584
|
-
"sha256": "
|
|
584
|
+
"sha256": "d6be293b79f2e0e3312db441c7c315779b7f345cea3c264508bfbf76f49e79ef"
|
|
585
585
|
},
|
|
586
586
|
{
|
|
587
587
|
"path": "shared/external/metrickit-diagnostics/SKILL.md",
|
|
@@ -613,7 +613,7 @@
|
|
|
613
613
|
},
|
|
614
614
|
{
|
|
615
615
|
"path": "shared/external/passkit-wallet/SKILL.md",
|
|
616
|
-
"sha256": "
|
|
616
|
+
"sha256": "972a26763ef557d6534e970c6a444526c0fc3eb0ac005f01ac1fee103e052e58"
|
|
617
617
|
},
|
|
618
618
|
{
|
|
619
619
|
"path": "shared/external/pdfkit/SKILL.md",
|
|
@@ -621,11 +621,11 @@
|
|
|
621
621
|
},
|
|
622
622
|
{
|
|
623
623
|
"path": "shared/external/pencilkit-drawing/SKILL.md",
|
|
624
|
-
"sha256": "
|
|
624
|
+
"sha256": "f18ccc4d5d985492758003aba555ccbfbede2e9c18c70932dc7c1198c4c2f624"
|
|
625
625
|
},
|
|
626
626
|
{
|
|
627
627
|
"path": "shared/external/permissionkit/SKILL.md",
|
|
628
|
-
"sha256": "
|
|
628
|
+
"sha256": "ea30c7a3bb9128a5f43064d5d2e46360d1fe08c732eb3ecedd63490d7d4420a5"
|
|
629
629
|
},
|
|
630
630
|
{
|
|
631
631
|
"path": "shared/external/photos-camera-media/SKILL.md",
|
|
@@ -637,7 +637,7 @@
|
|
|
637
637
|
},
|
|
638
638
|
{
|
|
639
639
|
"path": "shared/external/push-notifications/SKILL.md",
|
|
640
|
-
"sha256": "
|
|
640
|
+
"sha256": "9f73964265e99c21aa0bab61b153a77ad3ac953f45766c852b1c8cfbef8498c3"
|
|
641
641
|
},
|
|
642
642
|
{
|
|
643
643
|
"path": "shared/external/python-patterns/SKILL.md",
|
|
@@ -649,7 +649,7 @@
|
|
|
649
649
|
},
|
|
650
650
|
{
|
|
651
651
|
"path": "shared/external/realitykit-ar/SKILL.md",
|
|
652
|
-
"sha256": "
|
|
652
|
+
"sha256": "66726df1164bb6ca96878cb941b4395ff3cff10ebc6743f91aa22b8ec795c246"
|
|
653
653
|
},
|
|
654
654
|
{
|
|
655
655
|
"path": "shared/external/rest-api-design/SKILL.md",
|
|
@@ -721,7 +721,7 @@
|
|
|
721
721
|
},
|
|
722
722
|
{
|
|
723
723
|
"path": "shared/external/swift-concurrency/SKILL.md",
|
|
724
|
-
"sha256": "
|
|
724
|
+
"sha256": "428a7e31167e98ef300d0c000afda438bfdd7853793571ab4d1d05623e6d4383"
|
|
725
725
|
},
|
|
726
726
|
{
|
|
727
727
|
"path": "shared/external/swift-formatstyle/SKILL.md",
|
|
@@ -733,7 +733,7 @@
|
|
|
733
733
|
},
|
|
734
734
|
{
|
|
735
735
|
"path": "shared/external/swift-security/SKILL.md",
|
|
736
|
-
"sha256": "
|
|
736
|
+
"sha256": "0358e6b23ec268c51bf34d9341a80150746d2cbe7f9e4159de5133442a1b0d2e"
|
|
737
737
|
},
|
|
738
738
|
{
|
|
739
739
|
"path": "shared/external/swift-testing-pro/SKILL.md",
|
|
@@ -741,7 +741,7 @@
|
|
|
741
741
|
},
|
|
742
742
|
{
|
|
743
743
|
"path": "shared/external/swift-testing/SKILL.md",
|
|
744
|
-
"sha256": "
|
|
744
|
+
"sha256": "bed2911b41450d88b77cc913648ea421d99d7c167fb49f4c8bcf54b580952c29"
|
|
745
745
|
},
|
|
746
746
|
{
|
|
747
747
|
"path": "shared/external/swiftdata-pro/SKILL.md",
|
|
@@ -749,7 +749,7 @@
|
|
|
749
749
|
},
|
|
750
750
|
{
|
|
751
751
|
"path": "shared/external/swiftdata/SKILL.md",
|
|
752
|
-
"sha256": "
|
|
752
|
+
"sha256": "5fa27f7d9c0997acc5959ad811aa4b49ccbd34d62b5237556e1ffaa650447163"
|
|
753
753
|
},
|
|
754
754
|
{
|
|
755
755
|
"path": "shared/external/swiftlint/SKILL.md",
|
|
@@ -777,7 +777,7 @@
|
|
|
777
777
|
},
|
|
778
778
|
{
|
|
779
779
|
"path": "shared/external/swiftui-navigation/SKILL.md",
|
|
780
|
-
"sha256": "
|
|
780
|
+
"sha256": "422a6c0b680d422af731474a9b03d141ef04e1f16359ce25a3ba22780f2bf1c2"
|
|
781
781
|
},
|
|
782
782
|
{
|
|
783
783
|
"path": "shared/external/swiftui-patterns/SKILL.md",
|
|
@@ -829,7 +829,7 @@
|
|
|
829
829
|
},
|
|
830
830
|
{
|
|
831
831
|
"path": "shared/external/vision-framework/SKILL.md",
|
|
832
|
-
"sha256": "
|
|
832
|
+
"sha256": "370fdf6eaf71833dff0e8f2d3989a849b5c42ff3198605aa818c2fd0c12c7ef7"
|
|
833
833
|
},
|
|
834
834
|
{
|
|
835
835
|
"path": "shared/external/vue-composition/SKILL.md",
|
|
@@ -837,7 +837,7 @@
|
|
|
837
837
|
},
|
|
838
838
|
{
|
|
839
839
|
"path": "shared/external/weatherkit/SKILL.md",
|
|
840
|
-
"sha256": "
|
|
840
|
+
"sha256": "db8a11fc9c572c6d9754c031ad97a868c5d0e6748855d90b72a2aeb44e2d4255"
|
|
841
841
|
},
|
|
842
842
|
{
|
|
843
843
|
"path": "shared/external/web-accessibility/SKILL.md",
|
|
@@ -853,7 +853,7 @@
|
|
|
853
853
|
},
|
|
854
854
|
{
|
|
855
855
|
"path": "shared/external/widgetkit/SKILL.md",
|
|
856
|
-
"sha256": "
|
|
856
|
+
"sha256": "d4bce1c362e57616b252304414cfd01354586c7ed0be6c22f61efcc0980e26b0"
|
|
857
857
|
},
|
|
858
858
|
{
|
|
859
859
|
"path": "shared/external/xcode-build-benchmark/SKILL.md",
|