@mmerterden/multi-agent-pipeline 16.18.0 → 16.20.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/CHANGELOG.md +68 -0
- package/README.md +5 -5
- package/README.tr.md +2 -2
- package/docs/FIGMA_PIPELINE.md +1 -1
- package/docs/adr/0001-three-model-triage.md +4 -2
- package/docs/architecture.md +2 -2
- package/docs/ecosystem.md +13 -11
- package/docs/features.md +2 -2
- package/index.js +1 -1
- package/install/_codex-agents.mjs +2 -2
- package/install/_common.mjs +25 -1
- package/install/_dev-only-files.mjs +3 -2
- package/install/_mcp-register.mjs +4 -3
- package/install/_plugin-skills.mjs +1 -3
- package/install/copilot.mjs +18 -9
- package/install/index.mjs +2 -4
- package/install/templates/copilot-instructions.md +7 -7
- package/package.json +4 -3
- package/pipeline/agents/android-architect.md +1 -0
- package/pipeline/agents/backend-architect.md +1 -0
- package/pipeline/agents/code-reviewer.md +1 -0
- package/pipeline/agents/dev-critic.md +2 -1
- package/pipeline/agents/explorer.md +1 -0
- package/pipeline/agents/ios-architect.md +1 -0
- package/pipeline/agents/security-auditor.md +1 -0
- package/pipeline/agents/task-clarifier.md +1 -0
- package/pipeline/claude-md-template.md +2 -2
- package/pipeline/commands/multi-agent/SKILL.md +5 -5
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/design-check/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/resume/SKILL.md +1 -0
- package/pipeline/commands/multi-agent/review/SKILL.md +27 -14
- package/pipeline/commands/multi-agent/review-analysis/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +24 -2
- package/pipeline/commands/multi-agent/sync/SKILL.md +2 -2
- package/pipeline/commands/sim-test.md +64 -20
- package/pipeline/lib/credential-inventory.sh +15 -2
- package/pipeline/lib/credential-store-resolver.sh +14 -4
- package/pipeline/lib/credential-store.sh +8 -2
- package/pipeline/lib/extract-conventions.sh +1 -14
- package/pipeline/lib/fetch-confluence.sh +12 -4
- package/pipeline/lib/fetch-crashlytics.sh +11 -8
- package/pipeline/lib/fetch-document.sh +1 -1
- package/pipeline/lib/fetch-figma-annotations.sh +7 -5
- package/pipeline/lib/fetch-fortify.sh +5 -3
- package/pipeline/lib/fetch-graylog.sh +5 -3
- package/pipeline/lib/figma-mcp-refresh.sh +1 -1
- package/pipeline/lib/figma-screenshot.sh +27 -24
- package/pipeline/lib/figma-token.sh +8 -4
- package/pipeline/lib/issue-fetcher.sh +0 -1
- package/pipeline/lib/jira-publish.sh +7 -5
- package/pipeline/lib/md2confluence-v3.py +13 -7
- package/pipeline/lib/multi-repo-pipeline.sh +18 -8
- package/pipeline/lib/plan-todos.sh +11 -0
- package/pipeline/lib/post-pr-review.sh +9 -2
- package/pipeline/lib/repo-cache.sh +18 -10
- package/pipeline/lib/review-watch.sh +60 -14
- package/pipeline/lib/shadow-git.sh +8 -4
- package/pipeline/lib/vercel-deploy.sh +2 -2
- package/pipeline/multi-agent-refs/_dev-context.md +5 -2
- package/pipeline/multi-agent-refs/analysis/locked.md +4 -4
- package/pipeline/multi-agent-refs/analysis/render.md +1 -1
- package/pipeline/multi-agent-refs/channels/pr.md +22 -4
- package/pipeline/multi-agent-refs/cross-cli-contract.md +1 -1
- package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +15 -2
- package/pipeline/multi-agent-refs/features/review-delta.md +89 -0
- package/pipeline/multi-agent-refs/features/scope-check.md +41 -0
- package/pipeline/multi-agent-refs/features/verify-by-test.md +6 -5
- package/pipeline/multi-agent-refs/features/worktree-finalize.md +1 -1
- package/pipeline/multi-agent-refs/outside-the-pipeline.md +6 -6
- package/pipeline/multi-agent-refs/payload-contracts.md +1 -1
- package/pipeline/multi-agent-refs/phases/log-format.md +1 -1
- package/pipeline/multi-agent-refs/phases/modes.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +4 -2
- package/pipeline/multi-agent-refs/phases/phase-1-analysis.md +3 -3
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +5 -5
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +17 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +67 -24
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +10 -0
- package/pipeline/multi-agent-refs/phases/phase-6-commit.md +2 -0
- package/pipeline/multi-agent-refs/phases/phase-7-report.md +4 -2
- package/pipeline/multi-agent-refs/rules.md +2 -2
- package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
- package/pipeline/rules/figma-pipeline.md +18 -72
- package/pipeline/rules/outside-the-pipeline.md +4 -3
- package/pipeline/schemas/agent-state.schema.json +130 -1
- package/pipeline/schemas/dev-critic-output.schema.json +5 -0
- package/pipeline/schemas/prefs.schema.json +47 -0
- package/pipeline/schemas/reviewer-output.schema.json +7 -2
- package/pipeline/schemas/scope-check.schema.json +55 -0
- package/pipeline/schemas/token-budget.json +3 -3
- package/pipeline/schemas/triage-output.schema.json +12 -2
- package/pipeline/scripts/README.md +3 -2
- package/pipeline/scripts/_fingerprint.mjs +173 -0
- package/pipeline/scripts/_stack-routing.mjs +1 -1
- package/pipeline/scripts/agent-guard.py +102 -21
- package/pipeline/scripts/anonymize-findings.mjs +7 -6
- package/pipeline/scripts/build-skills-index.mjs +14 -3
- package/pipeline/scripts/cost-budget-check.mjs +5 -3
- package/pipeline/scripts/cost-lib.sh +0 -15
- package/pipeline/scripts/diff-explain.mjs +22 -12
- package/pipeline/scripts/evidence-gate.mjs +73 -5
- package/pipeline/scripts/finding-fingerprint.mjs +101 -0
- package/pipeline/scripts/gc-refs.sh +6 -2
- package/pipeline/scripts/gc-tmp.sh +1 -1
- package/pipeline/scripts/gc-worktrees.sh +1 -1
- package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
- package/pipeline/scripts/github-ssh-setup.sh +7 -2
- package/pipeline/scripts/graph-build.mjs +2 -2
- package/pipeline/scripts/jira-wiki-escape.mjs +2 -1
- package/pipeline/scripts/keychain.py +12 -11
- package/pipeline/scripts/learning-curve.mjs +1 -1
- package/pipeline/scripts/migrate-prefs.mjs +1 -1
- package/pipeline/scripts/output-quality-check.sh +3 -1
- package/pipeline/scripts/phase-tracker.sh +1 -1
- package/pipeline/scripts/phase0-exit-gate.mjs +2 -1
- package/pipeline/scripts/plan-coverage-gate.mjs +2 -1
- package/pipeline/scripts/pre-commit-check.sh +23 -13
- package/pipeline/scripts/prune-logs.sh +1 -1
- package/pipeline/scripts/render-agent-log-cost.sh +3 -1
- package/pipeline/scripts/render-cost-summary.sh +4 -2
- package/pipeline/scripts/render-work-summary.sh +5 -3
- package/pipeline/scripts/repo-map.mjs +3 -2
- package/pipeline/scripts/review-delta.mjs +217 -0
- package/pipeline/scripts/run-metrics.mjs +20 -0
- package/pipeline/scripts/scan-skills.sh +6 -2
- package/pipeline/scripts/scope-check-gate.mjs +90 -0
- package/pipeline/scripts/search-logs.sh +8 -6
- package/pipeline/scripts/sign-skills.sh +3 -1
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +12 -5
- package/pipeline/scripts/triage-memory.mjs +25 -4
- package/pipeline/scripts/uninstall.mjs +20 -12
- package/pipeline/scripts/update-check.sh +2 -2
- package/pipeline/scripts/update-issue-progress.sh +6 -5
- package/pipeline/scripts/validate-analysis-doc.mjs +6 -6
- package/pipeline/scripts/validate-reviewer.mjs +6 -0
- package/pipeline/scripts/validate-triage.mjs +20 -0
- package/pipeline/scripts/verify-skills.sh +3 -1
- package/pipeline/scripts/worktree-finalize.sh +22 -10
- package/pipeline/skills/.skill-manifest.json +81 -57
- package/pipeline/skills/.skills-index.json +19 -19
- package/pipeline/skills/shared/README.md +8 -8
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +7 -7
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +23 -14
- package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +5 -0
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +2 -2
- package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +56 -0
- package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +0 -4
- package/pipeline/skills/shared/external/api-patterns/SKILL.md +12 -24
- package/pipeline/skills/shared/external/app-store-changelog/references/release-notes-guidelines.md +34 -0
- package/pipeline/skills/shared/external/app-store-changelog/scripts/collect_release_changes.sh +33 -0
- package/pipeline/skills/shared/external/architecture/SKILL.md +7 -9
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +0 -4
- package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +0 -1
- package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +0 -14
- package/pipeline/skills/shared/external/hig-components-content/SKILL.md +13 -13
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +16 -16
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +6 -6
- package/pipeline/skills/shared/external/hig-components-system/SKILL.md +13 -13
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +23 -23
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +18 -18
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +30 -30
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +11 -11
- package/pipeline/skills/shared/external/hig-technologies/SKILL.md +33 -33
- package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +52 -52
- package/pipeline/skills/shared/external/ios-coding-standard/references/lint-local.sh +1 -1
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +11 -11
- package/pipeline/skills/shared/external/ios-developer/SKILL.md +0 -1
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +7 -3
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -15
- package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +0 -5
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/bootstrap/Package.swift +17 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/Resources/.keep +0 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/bootstrap/Sources/MyApp/main.swift +11 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/bootstrap/version.env +2 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/build_icon.sh +49 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/compile_and_run.sh +63 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/launch.sh +28 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/make_appcast.sh +82 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/package_app.sh +206 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/setup_dev_signing.sh +52 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/sign-and-notarize.sh +52 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/assets/templates/version.env +2 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/references/packaging.md +17 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/references/release.md +32 -0
- package/pipeline/skills/shared/external/macos-spm-app-packaging/references/scaffold.md +79 -0
- package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +0 -1
- package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +0 -4
- package/pipeline/skills/shared/external/swift-concurrency-expert/references/approachable-concurrency.md +63 -0
- package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +272 -0
- package/pipeline/skills/shared/external/swift-concurrency-expert/references/swiftui-concurrency-tour-wwdc.md +33 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +150 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/report-template.md +47 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/pipeline/skills/shared/external/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/async-state.md +96 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/components-index.md +46 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/controls.md +57 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/focus.md +90 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/form.md +97 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/grids.md +71 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/haptics.md +71 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/list.md +86 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/media.md +73 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/overlay.md +45 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/performance.md +62 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/previews.md +48 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/searchable.md +71 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +155 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/split-views.md +72 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +114 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/theming.md +71 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/pipeline/skills/shared/external/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/pipeline/skills/skills-index.md +8 -8
- package/pipeline/skills/shared/external/help-skills/SKILL.md +0 -166
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Pre-commit secret detection for multi-agent pipeline
|
|
3
3
|
# Scans staged git changes for accidentally committed secrets
|
|
4
|
-
# Exit 0 = clean, Exit
|
|
4
|
+
# Exit 0 = clean, Exit 2 = secrets found. The PreToolUse hook contract
|
|
5
|
+
# (install/templates/claude-hooks.json, agent-guard.sh) blocks the tool call
|
|
6
|
+
# only on exit 2, with the reason on stderr.
|
|
5
7
|
|
|
6
8
|
set -uo pipefail
|
|
7
9
|
|
|
@@ -56,6 +58,14 @@ process.stdin.on("end", () => {
|
|
|
56
58
|
fi
|
|
57
59
|
fi
|
|
58
60
|
|
|
61
|
+
# Staged paths from `git diff --cached --name-only` are repo-root-relative, so
|
|
62
|
+
# the `-f` tests below only see them from the repo root. Hooks fire with the
|
|
63
|
+
# caller's cwd, which is often a subdirectory.
|
|
64
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
65
|
+
if [ -n "$REPO_ROOT" ]; then
|
|
66
|
+
cd "$REPO_ROOT" || exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
59
69
|
FOUND=0
|
|
60
70
|
|
|
61
71
|
# `file "$f" | grep -q "binary"` never matches real `file` output (it names a
|
|
@@ -74,14 +84,14 @@ scan_file() {
|
|
|
74
84
|
|
|
75
85
|
# .env files
|
|
76
86
|
if echo "$file" | grep -qE '\.env($|\.)'; then
|
|
77
|
-
echo "BLOCKED: .env file staged: $file"
|
|
87
|
+
echo "BLOCKED: .env file staged: $file" >&2
|
|
78
88
|
FOUND=1
|
|
79
89
|
return 0
|
|
80
90
|
fi
|
|
81
91
|
|
|
82
92
|
# Credentials files
|
|
83
93
|
if echo "$file" | grep -qiE '(credentials|secrets|tokens)\.(json|yaml|yml|plist)$'; then
|
|
84
|
-
echo "BLOCKED: Credentials file staged: $file"
|
|
94
|
+
echo "BLOCKED: Credentials file staged: $file" >&2
|
|
85
95
|
FOUND=1
|
|
86
96
|
return 0
|
|
87
97
|
fi
|
|
@@ -90,37 +100,37 @@ scan_file() {
|
|
|
90
100
|
|
|
91
101
|
# API keys / tokens (key = "long_string" pattern)
|
|
92
102
|
if echo "$content" | grep -qiE '(api[_-]?key|api[_-]?secret|access[_-]?token|auth[_-]?token|secret[_-]?key)\s*[:=]\s*["'"'"'][A-Za-z0-9+/=_-]{20,}'; then
|
|
93
|
-
echo "BLOCKED: Possible API key/token in $file"
|
|
103
|
+
echo "BLOCKED: Possible API key/token in $file" >&2
|
|
94
104
|
FOUND=1
|
|
95
105
|
fi
|
|
96
106
|
|
|
97
107
|
# AWS access keys
|
|
98
108
|
if echo "$content" | grep -qE 'AKIA[0-9A-Z]{16}'; then
|
|
99
|
-
echo "BLOCKED: AWS access key in $file"
|
|
109
|
+
echo "BLOCKED: AWS access key in $file" >&2
|
|
100
110
|
FOUND=1
|
|
101
111
|
fi
|
|
102
112
|
|
|
103
113
|
# Private keys
|
|
104
114
|
if echo "$content" | grep -qE 'BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY'; then
|
|
105
|
-
echo "BLOCKED: Private key in $file"
|
|
115
|
+
echo "BLOCKED: Private key in $file" >&2
|
|
106
116
|
FOUND=1
|
|
107
117
|
fi
|
|
108
118
|
|
|
109
119
|
# Firebase/GCP service account JSON
|
|
110
120
|
if echo "$content" | grep -qE '"type"\s*:\s*"service_account"'; then
|
|
111
|
-
echo "BLOCKED: Service account JSON in $file"
|
|
121
|
+
echo "BLOCKED: Service account JSON in $file" >&2
|
|
112
122
|
FOUND=1
|
|
113
123
|
fi
|
|
114
124
|
|
|
115
125
|
# High-signal provider token prefixes (low false-positive rate)
|
|
116
126
|
if echo "$content" | grep -qE '(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{60,}|xox[baprs]-[A-Za-z0-9-]{12,}|sk_live_[A-Za-z0-9]{20,}|rk_live_[A-Za-z0-9]{20,}|AIza[0-9A-Za-z_-]{35}|npm_[A-Za-z0-9]{36}|glpat-[A-Za-z0-9_-]{20,}'; then
|
|
117
|
-
echo "BLOCKED: Provider access token in $file"
|
|
127
|
+
echo "BLOCKED: Provider access token in $file" >&2
|
|
118
128
|
FOUND=1
|
|
119
129
|
fi
|
|
120
130
|
|
|
121
131
|
# JWT (three base64url segments - header.payload.signature)
|
|
122
132
|
if echo "$content" | grep -qE 'eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}'; then
|
|
123
|
-
echo "BLOCKED: JWT in $file"
|
|
133
|
+
echo "BLOCKED: JWT in $file" >&2
|
|
124
134
|
FOUND=1
|
|
125
135
|
fi
|
|
126
136
|
|
|
@@ -191,7 +201,7 @@ scan_file() {
|
|
|
191
201
|
if (H >= 4.2) { print s; exit }
|
|
192
202
|
}')
|
|
193
203
|
if [ -n "$entropy_hit" ]; then
|
|
194
|
-
echo "BLOCKED: High-entropy string (possible secret) in $file"
|
|
204
|
+
echo "BLOCKED: High-entropy string (possible secret) in $file" >&2
|
|
195
205
|
FOUND=1
|
|
196
206
|
fi
|
|
197
207
|
fi
|
|
@@ -234,9 +244,9 @@ if [ "$SCAN_UNSTAGED" = "1" ]; then
|
|
|
234
244
|
fi
|
|
235
245
|
|
|
236
246
|
if [ $FOUND -eq 1 ]; then
|
|
237
|
-
echo ""
|
|
238
|
-
echo "Secret check failed. Remove secrets from staged files before committing."
|
|
239
|
-
exit
|
|
247
|
+
echo "" >&2
|
|
248
|
+
echo "Secret check failed. Remove secrets from staged files before committing." >&2
|
|
249
|
+
exit 2
|
|
240
250
|
fi
|
|
241
251
|
|
|
242
252
|
exit 0
|
|
@@ -151,7 +151,7 @@ while IFS= read -r -d '' d; do
|
|
|
151
151
|
# so a concurrent run keeps a resumable log dir. --task is an explicit,
|
|
152
152
|
# deliberate target and bypasses the grace.
|
|
153
153
|
if [ "$GRACE_MIN" -gt 0 ] && [ -z "$TASK" ] &&
|
|
154
|
-
find "$d" -mmin "-$GRACE_MIN" -print 2>/dev/null
|
|
154
|
+
[ -n "$(find "$d" -mmin "-$GRACE_MIN" -print -quit 2>/dev/null)" ]; then
|
|
155
155
|
skipped_active=$((skipped_active + 1))
|
|
156
156
|
continue
|
|
157
157
|
fi
|
|
@@ -25,7 +25,9 @@ shift || true
|
|
|
25
25
|
SPANS_FILE=""
|
|
26
26
|
while [ $# -gt 0 ]; do
|
|
27
27
|
case "$1" in
|
|
28
|
-
--otel-spans)
|
|
28
|
+
--otel-spans)
|
|
29
|
+
[ $# -ge 2 ] || { echo "render-agent-log-cost: --otel-spans needs a path" >&2; exit 1; }
|
|
30
|
+
SPANS_FILE="$2"; shift 2 ;;
|
|
29
31
|
*) shift ;;
|
|
30
32
|
esac
|
|
31
33
|
done
|
|
@@ -19,7 +19,9 @@ shift || true
|
|
|
19
19
|
SPANS_FILE=""
|
|
20
20
|
while [ $# -gt 0 ]; do
|
|
21
21
|
case "$1" in
|
|
22
|
-
--otel-spans)
|
|
22
|
+
--otel-spans)
|
|
23
|
+
[ $# -ge 2 ] || { echo "render-cost-summary: --otel-spans needs a path" >&2; exit 1; }
|
|
24
|
+
SPANS_FILE="$2"; shift 2 ;;
|
|
23
25
|
*) shift ;;
|
|
24
26
|
esac
|
|
25
27
|
done
|
|
@@ -71,7 +73,7 @@ if [ -n "$tracker_file" ]; then
|
|
|
71
73
|
(.phases // {} | to_entries)
|
|
72
74
|
end)
|
|
73
75
|
| [ .[] |
|
|
74
|
-
select(.value.tokens_in // 0 > 0 or .value.tokens_out // 0 > 0) |
|
|
76
|
+
select(((.value.tokens_in // 0) > 0) or ((.value.tokens_out // 0) > 0)) |
|
|
75
77
|
{
|
|
76
78
|
phase: .key,
|
|
77
79
|
phase_name: (.value.name // .key),
|
|
@@ -44,11 +44,13 @@ TASK_ID="${1:?usage: render-work-summary.sh <task-id>}"
|
|
|
44
44
|
shift || true
|
|
45
45
|
|
|
46
46
|
WORKTREE=""; BRANCH=""; BASE=""
|
|
47
|
+
need_value() { [ $# -ge 2 ] || { echo "render-work-summary: $1 needs a value" >&2; exit 1; }; }
|
|
48
|
+
|
|
47
49
|
while [ $# -gt 0 ]; do
|
|
48
50
|
case "$1" in
|
|
49
|
-
--worktree) WORKTREE="$2"; shift 2 ;;
|
|
50
|
-
--branch) BRANCH="$2"; shift 2 ;;
|
|
51
|
-
--base-branch) BASE="$2"; shift 2 ;;
|
|
51
|
+
--worktree) need_value "$@"; WORKTREE="$2"; shift 2 ;;
|
|
52
|
+
--branch) need_value "$@"; BRANCH="$2"; shift 2 ;;
|
|
53
|
+
--base-branch) need_value "$@"; BASE="$2"; shift 2 ;;
|
|
52
54
|
*) shift ;;
|
|
53
55
|
esac
|
|
54
56
|
done
|
|
@@ -30,6 +30,7 @@ import { promises as fs } from "node:fs";
|
|
|
30
30
|
import { existsSync, statSync } from "node:fs";
|
|
31
31
|
import path from "node:path";
|
|
32
32
|
import process from "node:process";
|
|
33
|
+
import { matchesGlob } from "./_path-match.mjs";
|
|
33
34
|
|
|
34
35
|
// ---- CLI args --------------------------------------------------------------
|
|
35
36
|
|
|
@@ -142,8 +143,8 @@ async function walk(root, args) {
|
|
|
142
143
|
continue;
|
|
143
144
|
}
|
|
144
145
|
const rel = path.relative(root, full);
|
|
145
|
-
if (args.include && !args.include.some((g) => rel
|
|
146
|
-
if (args.exclude && args.exclude.some((g) => rel
|
|
146
|
+
if (args.include && !args.include.some((g) => matchesGlob(rel, g))) continue;
|
|
147
|
+
if (args.exclude && args.exclude.some((g) => matchesGlob(rel, g))) continue;
|
|
147
148
|
out.push({ path: rel, lang: LANG_EXT[ext], abs: full });
|
|
148
149
|
}
|
|
149
150
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* review-delta.mjs - classify one Phase 4 round against the round before it.
|
|
4
|
+
*
|
|
5
|
+
* Reviewers re-read the whole diff every round and return findings with no
|
|
6
|
+
* memory of the previous round. This script gives the loop that memory: it
|
|
7
|
+
* fingerprints both rounds (see _fingerprint.mjs) and reports which accepted
|
|
8
|
+
* findings are still present after the fix, which were resolved, which triage
|
|
9
|
+
* downgraded, and which are new. A finding that survives `--trip-cycles`
|
|
10
|
+
* consecutive rework cycles is the autopilot circuit-breaker's trigger 2.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* review-delta.mjs --rounds-dir <dir> --iteration N [options]
|
|
14
|
+
* review-delta.mjs --previous <round-N-1.json> --current <round-N.json> [--prior <round-N-2.json> ...] [options]
|
|
15
|
+
*
|
|
16
|
+
* Options:
|
|
17
|
+
* --kind triage|reviewer|dev-critic document shape (default triage)
|
|
18
|
+
* --trip-cycles N recurrence that trips (default 2)
|
|
19
|
+
* --severities a,b accepted severities that count as the
|
|
20
|
+
* rework mandate (default blocking,important)
|
|
21
|
+
*
|
|
22
|
+
* Output (stdout, JSON): {iteration, previousIteration, counts, new[], stillPresent[],
|
|
23
|
+
* resolved[], downgraded[], stillPresentBlocking[], recurrence{}, tripped, plateau}
|
|
24
|
+
* Each list entry is {fingerprint, severity, file, issue} so a log line reads
|
|
25
|
+
* without a lookup. With no previous round: {"skipped":"no-previous-round"}.
|
|
26
|
+
*
|
|
27
|
+
* Exit codes:
|
|
28
|
+
* 0 progress, or nothing to compare (fail-open like evidence-gate)
|
|
29
|
+
* 1 unparseable input or wrong shape
|
|
30
|
+
* 3 trigger-2 signal: a mandate finding recurred >= --trip-cycles times
|
|
31
|
+
* 64 usage
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
35
|
+
import { join } from "node:path";
|
|
36
|
+
|
|
37
|
+
import { annotate, collectFingerprints, KINDS } from "./_fingerprint.mjs";
|
|
38
|
+
|
|
39
|
+
const USAGE = `usage:
|
|
40
|
+
review-delta.mjs --rounds-dir <dir> --iteration N [--kind ${KINDS.join("|")}] [--trip-cycles N] [--severities a,b]
|
|
41
|
+
review-delta.mjs --previous <json> --current <json> [--prior <json> ...] [same options]`;
|
|
42
|
+
|
|
43
|
+
const ROUND_FILE = (n) => `triage-round-${n}.json`;
|
|
44
|
+
|
|
45
|
+
function parseArgs(argv) {
|
|
46
|
+
const opts = { prior: [], kind: "triage", tripCycles: 2, severities: ["blocking", "important"] };
|
|
47
|
+
for (let i = 0; i < argv.length; i++) {
|
|
48
|
+
const a = argv[i];
|
|
49
|
+
const next = () => argv[++i];
|
|
50
|
+
if (a === "--rounds-dir") opts.roundsDir = next();
|
|
51
|
+
else if (a === "--iteration") opts.iteration = Number(next());
|
|
52
|
+
else if (a === "--previous") opts.previous = next();
|
|
53
|
+
else if (a === "--current") opts.current = next();
|
|
54
|
+
else if (a === "--prior") opts.prior.push(next());
|
|
55
|
+
else if (a === "--kind") opts.kind = next();
|
|
56
|
+
else if (a === "--trip-cycles") opts.tripCycles = Number(next());
|
|
57
|
+
else if (a === "--severities")
|
|
58
|
+
opts.severities = next()
|
|
59
|
+
.split(",")
|
|
60
|
+
.map((s) => s.trim());
|
|
61
|
+
else return null;
|
|
62
|
+
}
|
|
63
|
+
return opts;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function loadDoc(path, kind) {
|
|
67
|
+
const doc = JSON.parse(readFileSync(path, "utf-8"));
|
|
68
|
+
return annotate(doc, kind).doc;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolve the ordered list of round files, newest first: [current, previous, prior...].
|
|
73
|
+
* Returns null when there is no previous round to compare against.
|
|
74
|
+
*/
|
|
75
|
+
function resolveRounds(opts) {
|
|
76
|
+
if (opts.roundsDir) {
|
|
77
|
+
if (!Number.isInteger(opts.iteration) || opts.iteration < 1) {
|
|
78
|
+
throw new Error("--iteration must be a positive integer");
|
|
79
|
+
}
|
|
80
|
+
const current = join(opts.roundsDir, ROUND_FILE(opts.iteration));
|
|
81
|
+
if (!existsSync(current)) throw new Error(`current round file missing: ${current}`);
|
|
82
|
+
const chain = [current];
|
|
83
|
+
for (let n = opts.iteration - 1; n >= 1; n--) {
|
|
84
|
+
const p = join(opts.roundsDir, ROUND_FILE(n));
|
|
85
|
+
if (!existsSync(p)) break;
|
|
86
|
+
chain.push(p);
|
|
87
|
+
}
|
|
88
|
+
return { chain, iteration: opts.iteration };
|
|
89
|
+
}
|
|
90
|
+
if (!opts.current) throw new Error("--current is required without --rounds-dir");
|
|
91
|
+
const chain = [opts.current];
|
|
92
|
+
if (opts.previous) chain.push(opts.previous, ...opts.prior);
|
|
93
|
+
return { chain, iteration: chain.length };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function entry(fp, finding) {
|
|
97
|
+
return {
|
|
98
|
+
fingerprint: fp,
|
|
99
|
+
severity: finding.severity ?? null,
|
|
100
|
+
file: finding.file ?? null,
|
|
101
|
+
issue: finding.issue ?? null,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function buckets(kind) {
|
|
106
|
+
return kind === "triage"
|
|
107
|
+
? { accepted: ["accepted"], elsewhere: ["deferred", "rejected"] }
|
|
108
|
+
: { accepted: ["findings"], elsewhere: [] };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function mandate(doc, kind, severities) {
|
|
112
|
+
return collectFingerprints(doc, { kind, buckets: buckets(kind).accepted, severities });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function setEqual(a, b) {
|
|
116
|
+
if (a.size !== b.size) return false;
|
|
117
|
+
for (const x of a) if (!b.has(x)) return false;
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function computeDelta(docs, { kind, severities, tripCycles }) {
|
|
122
|
+
const [current, previous, ...priors] = docs;
|
|
123
|
+
const b = buckets(kind);
|
|
124
|
+
const prevMandate = mandate(previous, kind, severities);
|
|
125
|
+
const prevAll = collectFingerprints(previous, { kind, buckets: b.accepted });
|
|
126
|
+
const curAccepted = collectFingerprints(current, { kind, buckets: b.accepted });
|
|
127
|
+
const curElsewhere = collectFingerprints(current, { kind, buckets: b.elsewhere });
|
|
128
|
+
|
|
129
|
+
const stillPresent = [];
|
|
130
|
+
const resolved = [];
|
|
131
|
+
const downgraded = [];
|
|
132
|
+
for (const [fp, f] of prevMandate) {
|
|
133
|
+
if (curAccepted.has(fp)) stillPresent.push(entry(fp, curAccepted.get(fp)));
|
|
134
|
+
else if (curElsewhere.has(fp)) downgraded.push(entry(fp, f));
|
|
135
|
+
else resolved.push(entry(fp, f));
|
|
136
|
+
}
|
|
137
|
+
const fresh = [];
|
|
138
|
+
for (const [fp, f] of curAccepted) if (!prevAll.has(fp)) fresh.push(entry(fp, f));
|
|
139
|
+
|
|
140
|
+
const priorMandates = priors.map((d) => mandate(d, kind, severities));
|
|
141
|
+
const recurrence = {};
|
|
142
|
+
for (const { fingerprint } of stillPresent) {
|
|
143
|
+
let n = 1;
|
|
144
|
+
for (const m of priorMandates) {
|
|
145
|
+
if (!m.has(fingerprint)) break;
|
|
146
|
+
n += 1;
|
|
147
|
+
}
|
|
148
|
+
recurrence[fingerprint] = n;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let plateau = false;
|
|
152
|
+
if (priors.length > 0 && stillPresent.length > 0) {
|
|
153
|
+
const prevStill = new Set();
|
|
154
|
+
for (const fp of priorMandates[0].keys()) if (prevMandate.has(fp)) prevStill.add(fp);
|
|
155
|
+
plateau = setEqual(prevStill, new Set(stillPresent.map((e) => e.fingerprint)));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const tripped = stillPresent.some((e) => recurrence[e.fingerprint] >= tripCycles);
|
|
159
|
+
return {
|
|
160
|
+
counts: {
|
|
161
|
+
new: fresh.length,
|
|
162
|
+
stillPresent: stillPresent.length,
|
|
163
|
+
resolved: resolved.length,
|
|
164
|
+
downgraded: downgraded.length,
|
|
165
|
+
},
|
|
166
|
+
new: fresh,
|
|
167
|
+
stillPresent,
|
|
168
|
+
resolved,
|
|
169
|
+
downgraded,
|
|
170
|
+
stillPresentBlocking: stillPresent.filter((e) => e.severity === "blocking"),
|
|
171
|
+
recurrence,
|
|
172
|
+
tripped,
|
|
173
|
+
plateau,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function main() {
|
|
178
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
179
|
+
if (!opts || !KINDS.includes(opts.kind) || !(opts.tripCycles >= 1)) {
|
|
180
|
+
console.error(USAGE);
|
|
181
|
+
return 64;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let rounds;
|
|
185
|
+
try {
|
|
186
|
+
rounds = resolveRounds(opts);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
console.error(`error: ${err.message}`);
|
|
189
|
+
return opts.roundsDir && /missing/.test(err.message) ? 1 : 64;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (rounds.chain.length < 2) {
|
|
193
|
+
process.stdout.write(JSON.stringify({ skipped: "no-previous-round" }) + "\n");
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let docs;
|
|
198
|
+
try {
|
|
199
|
+
docs = rounds.chain.map((p) => loadDoc(p, opts.kind));
|
|
200
|
+
} catch (err) {
|
|
201
|
+
console.error(`error: ${err.message}`);
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const delta = computeDelta(docs, opts);
|
|
206
|
+
const out = {
|
|
207
|
+
iteration: rounds.iteration,
|
|
208
|
+
previousIteration: rounds.iteration - 1,
|
|
209
|
+
...delta,
|
|
210
|
+
};
|
|
211
|
+
process.stdout.write(JSON.stringify(out, null, 2) + "\n");
|
|
212
|
+
return delta.tripped ? 3 : 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (process.argv[1] && /review-delta\.mjs$/.test(process.argv[1])) {
|
|
216
|
+
process.exit(main());
|
|
217
|
+
}
|
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
* attribution reports "unavailable".
|
|
24
24
|
* consensusVerdict - unanimous-* / split / unverified
|
|
25
25
|
* buildPassed - per-repo build outcome
|
|
26
|
+
* diff.filesTouched / locAdded / - size of the change, from state.diffRisk
|
|
27
|
+
* locRemoved / maxScore (Phase 4 Step 1.75); null when the
|
|
28
|
+
* run predates the persisted totals
|
|
29
|
+
* reviewDelta.stillPresentFinal / - cross-round classification of the
|
|
30
|
+
* resolvedTotal / tripped last iteration (state.reviewIterations[].delta)
|
|
26
31
|
*
|
|
27
32
|
* Usage:
|
|
28
33
|
* node pipeline/scripts/run-metrics.mjs --state path/to/agent-state.json
|
|
@@ -147,6 +152,10 @@ if (projects.length) {
|
|
|
147
152
|
buildPassed = typeof b === "string" ? b === "passed" : b && b.ok === true;
|
|
148
153
|
}
|
|
149
154
|
|
|
155
|
+
const diffRisk = state.diffRisk && typeof state.diffRisk === "object" ? state.diffRisk : {};
|
|
156
|
+
const deltas = iterations.map((it) => it.delta).filter((d) => d && typeof d === "object");
|
|
157
|
+
const lastDelta = deltas.length ? deltas[deltas.length - 1] : null;
|
|
158
|
+
|
|
150
159
|
const metrics = {
|
|
151
160
|
taskId: state.taskId ?? null,
|
|
152
161
|
status: state.status ?? null,
|
|
@@ -193,6 +202,17 @@ const metrics = {
|
|
|
193
202
|
},
|
|
194
203
|
consensusVerdict: lastTriage.consensus ? lastTriage.consensus.verdict : null,
|
|
195
204
|
buildPassed,
|
|
205
|
+
diff: {
|
|
206
|
+
filesTouched: Number.isInteger(diffRisk.files) ? diffRisk.files : null,
|
|
207
|
+
locAdded: Number.isInteger(diffRisk.loc_added) ? diffRisk.loc_added : null,
|
|
208
|
+
locRemoved: Number.isInteger(diffRisk.loc_removed) ? diffRisk.loc_removed : null,
|
|
209
|
+
maxScore: typeof diffRisk.max_score === "number" ? diffRisk.max_score : null,
|
|
210
|
+
},
|
|
211
|
+
reviewDelta: {
|
|
212
|
+
stillPresentFinal: lastDelta ? (lastDelta.stillPresent || []).length : null,
|
|
213
|
+
resolvedTotal: deltas.length ? deltas.reduce((n, d) => n + (d.resolved || []).length, 0) : null,
|
|
214
|
+
tripped: deltas.some((d) => d.tripped === true),
|
|
215
|
+
},
|
|
196
216
|
};
|
|
197
217
|
|
|
198
218
|
console.log(JSON.stringify(metrics, null, 2));
|
|
@@ -34,10 +34,14 @@ THRESHOLD="medium"
|
|
|
34
34
|
|
|
35
35
|
while [ $# -gt 0 ]; do
|
|
36
36
|
case "$1" in
|
|
37
|
-
--root)
|
|
37
|
+
--root)
|
|
38
|
+
[ $# -ge 2 ] || { echo "scan-skills: --root needs a value" >&2; exit 64; }
|
|
39
|
+
ROOT="$2"; shift 2 ;;
|
|
38
40
|
--strict) STRICT=1; shift ;;
|
|
39
41
|
--json) JSON=1; shift ;;
|
|
40
|
-
--threshold)
|
|
42
|
+
--threshold)
|
|
43
|
+
[ $# -ge 2 ] || { echo "scan-skills: --threshold needs a value" >&2; exit 64; }
|
|
44
|
+
THRESHOLD="$2"; shift 2 ;;
|
|
41
45
|
--help|-h)
|
|
42
46
|
sed -n '1,30p' "$0" | sed 's/^# \{0,1\}//'
|
|
43
47
|
exit 0
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scope-check-gate.mjs - every file in the diff needs a stated reason.
|
|
4
|
+
*
|
|
5
|
+
* Phase 3 writes `.pipeline/scope-check.json` before the handoff: one reason
|
|
6
|
+
* per touched file, the things it deliberately did not do, and the simplifier
|
|
7
|
+
* rationales that used to be discarded. This gate compares that record with
|
|
8
|
+
* the real diff so an unjustified file cannot ride into review unnoticed.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* git diff --name-only <base>...HEAD | node scope-check-gate.mjs --check .pipeline/scope-check.json --diff-files -
|
|
12
|
+
* node scope-check-gate.mjs --check scope-check.json --diff-files files.txt [--advisory]
|
|
13
|
+
*
|
|
14
|
+
* Output (stdout, JSON): {ok, justified, unjustified[], unlisted[], notDone}
|
|
15
|
+
* unjustified = in the diff, missing from files[] (or with an empty reason)
|
|
16
|
+
* unlisted = in files[], not in the diff (stale entry; reported, never fatal)
|
|
17
|
+
*
|
|
18
|
+
* Exit codes: 0 every diff file justified (or --advisory), 1 otherwise or on
|
|
19
|
+
* unreadable input, 64 usage.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readFileSync } from "node:fs";
|
|
23
|
+
|
|
24
|
+
import { normalizeFile } from "./_fingerprint.mjs";
|
|
25
|
+
|
|
26
|
+
const USAGE =
|
|
27
|
+
"usage: scope-check-gate.mjs --check <scope-check.json> --diff-files <path|-> [--advisory]";
|
|
28
|
+
|
|
29
|
+
function parseArgs(argv) {
|
|
30
|
+
const opts = {};
|
|
31
|
+
for (let i = 0; i < argv.length; i++) {
|
|
32
|
+
const a = argv[i];
|
|
33
|
+
if (a === "--check") opts.check = argv[++i];
|
|
34
|
+
else if (a === "--diff-files") opts.diffFiles = argv[++i];
|
|
35
|
+
else if (a === "--advisory") opts.advisory = true;
|
|
36
|
+
else return null;
|
|
37
|
+
}
|
|
38
|
+
return opts.check && opts.diffFiles ? opts : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readText(path) {
|
|
42
|
+
return path === "-" ? readFileSync(0, "utf-8") : readFileSync(path, "utf-8");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function evaluate(check, diffFiles) {
|
|
46
|
+
const files = Array.isArray(check?.files) ? check.files : [];
|
|
47
|
+
const reasons = new Map();
|
|
48
|
+
for (const f of files) {
|
|
49
|
+
if (!f || typeof f.path !== "string") continue;
|
|
50
|
+
const reason = typeof f.reason === "string" ? f.reason.trim() : "";
|
|
51
|
+
reasons.set(normalizeFile(f.path), reason);
|
|
52
|
+
}
|
|
53
|
+
const diff = [...new Set(diffFiles.map(normalizeFile).filter(Boolean))];
|
|
54
|
+
const unjustified = diff.filter((p) => !(reasons.get(p) ?? "").length);
|
|
55
|
+
const diffSet = new Set(diff);
|
|
56
|
+
const unlisted = [...reasons.keys()].filter((p) => !diffSet.has(p));
|
|
57
|
+
return {
|
|
58
|
+
ok: unjustified.length === 0,
|
|
59
|
+
justified: diff.length - unjustified.length,
|
|
60
|
+
unjustified,
|
|
61
|
+
unlisted,
|
|
62
|
+
notDone: Array.isArray(check?.notDone) ? check.notDone.length : 0,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function main() {
|
|
67
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
68
|
+
if (!opts) {
|
|
69
|
+
console.error(USAGE);
|
|
70
|
+
return 64;
|
|
71
|
+
}
|
|
72
|
+
const fail = opts.advisory ? 0 : 1;
|
|
73
|
+
let check;
|
|
74
|
+
let diffFiles;
|
|
75
|
+
try {
|
|
76
|
+
check = JSON.parse(readText(opts.check));
|
|
77
|
+
diffFiles = readText(opts.diffFiles).split("\n");
|
|
78
|
+
} catch (err) {
|
|
79
|
+
console.error(`scope-check-gate: ${err.message}`);
|
|
80
|
+
process.stdout.write(JSON.stringify({ ok: false, error: err.message }) + "\n");
|
|
81
|
+
return fail;
|
|
82
|
+
}
|
|
83
|
+
const result = evaluate(check, diffFiles);
|
|
84
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
85
|
+
return result.ok ? 0 : fail;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (process.argv[1] && /scope-check-gate\.mjs$/.test(process.argv[1])) {
|
|
89
|
+
process.exit(main());
|
|
90
|
+
}
|
|
@@ -42,16 +42,18 @@ LIMIT=20
|
|
|
42
42
|
QUERY=""
|
|
43
43
|
SEMANTIC=0
|
|
44
44
|
|
|
45
|
+
need_value() { [ $# -ge 2 ] || { echo "search-logs: $1 needs a value" >&2; exit 64; }; }
|
|
46
|
+
|
|
45
47
|
while [ $# -gt 0 ]; do
|
|
46
48
|
case "$1" in
|
|
47
|
-
--root) ROOT="$2"; shift 2 ;;
|
|
48
|
-
--project) PROJECT="$2"; shift 2 ;;
|
|
49
|
-
--since) SINCE="$2"; shift 2 ;;
|
|
50
|
-
--phase) PHASE="$2"; shift 2 ;;
|
|
49
|
+
--root) need_value "$@"; ROOT="$2"; shift 2 ;;
|
|
50
|
+
--project) need_value "$@"; PROJECT="$2"; shift 2 ;;
|
|
51
|
+
--since) need_value "$@"; SINCE="$2"; shift 2 ;;
|
|
52
|
+
--phase) need_value "$@"; PHASE="$2"; shift 2 ;;
|
|
51
53
|
--json) JSON=1; shift ;;
|
|
52
54
|
--tsv) TSV=1; shift ;;
|
|
53
55
|
--semantic) SEMANTIC=1; shift ;;
|
|
54
|
-
--limit) LIMIT="$2"; shift 2 ;;
|
|
56
|
+
--limit) need_value "$@"; LIMIT="$2"; shift 2 ;;
|
|
55
57
|
--help|-h)
|
|
56
58
|
awk 'NR==1{next} /^[^#]/{exit} {sub(/^# ?/,""); print}' "$0"
|
|
57
59
|
exit 0
|
|
@@ -78,7 +80,7 @@ fi
|
|
|
78
80
|
# --limit (passed as --top N). --since/--phase have no meaning here.
|
|
79
81
|
if [ "$SEMANTIC" -eq 1 ]; then
|
|
80
82
|
TM_SCRIPT="$SCRIPT_DIR/triage-memory.mjs"
|
|
81
|
-
if [ ! -
|
|
83
|
+
if [ ! -f "$TM_SCRIPT" ] || ! command -v node >/dev/null 2>&1; then
|
|
82
84
|
echo "search-logs: --semantic requires node + triage-memory.mjs" >&2
|
|
83
85
|
exit 1
|
|
84
86
|
fi
|
|
@@ -18,7 +18,9 @@ set -euo pipefail
|
|
|
18
18
|
ROOT_OVERRIDE=""
|
|
19
19
|
while [ $# -gt 0 ]; do
|
|
20
20
|
case "$1" in
|
|
21
|
-
--root)
|
|
21
|
+
--root)
|
|
22
|
+
[ $# -ge 2 ] || { echo "sign-skills: --root needs a value" >&2; exit 1; }
|
|
23
|
+
ROOT_OVERRIDE="$2"; shift 2 ;;
|
|
22
24
|
*) shift ;;
|
|
23
25
|
esac
|
|
24
26
|
done
|
|
@@ -95,7 +95,7 @@ echo ""
|
|
|
95
95
|
echo "→ 3. No personal-data hardcodes in generic docs"
|
|
96
96
|
# These are patterns that must be placeholders, never literals
|
|
97
97
|
PERSONAL_HARDCODES=(
|
|
98
|
-
"${USER}_tkgithub"
|
|
98
|
+
"${USER:-$(id -un)}_tkgithub"
|
|
99
99
|
)
|
|
100
100
|
for p in "${PERSONAL_HARDCODES[@]}"; do
|
|
101
101
|
FOUND=$(find "$CLAUDE_CMDS" -name SKILL.md -exec grep -l -F -- "$p" {} + 2>/dev/null || true)
|
|
@@ -159,15 +159,22 @@ echo "→ 7. Agent personas deployed to both CLIs"
|
|
|
159
159
|
|
|
160
160
|
CLAUDE_AGENTS="$HOME/.claude/agents"
|
|
161
161
|
COPILOT_AGENTS="$HOME/.copilot/agents"
|
|
162
|
-
AGENT_FILES=(
|
|
162
|
+
AGENT_FILES=()
|
|
163
|
+
if [ -d "$CLAUDE_AGENTS" ]; then
|
|
164
|
+
for f in "$CLAUDE_AGENTS"/*.md; do
|
|
165
|
+
[ -f "$f" ] && AGENT_FILES+=("$(basename "$f")")
|
|
166
|
+
done
|
|
167
|
+
fi
|
|
163
168
|
|
|
164
169
|
if [ -d "$CLAUDE_AGENTS" ] && [ -d "$COPILOT_AGENTS" ]; then
|
|
165
170
|
missing_on_copilot=0
|
|
166
|
-
for a in "${AGENT_FILES[@]}"; do
|
|
171
|
+
for a in ${AGENT_FILES[@]+"${AGENT_FILES[@]}"}; do
|
|
167
172
|
[ -f "$COPILOT_AGENTS/$a" ] || missing_on_copilot=$((missing_on_copilot+1))
|
|
168
173
|
done
|
|
169
|
-
if [ "$missing_on_copilot" -eq 0 ]; then
|
|
170
|
-
pass "all
|
|
174
|
+
if [ "$missing_on_copilot" -eq 0 ] && [ "${#AGENT_FILES[@]}" -gt 0 ]; then
|
|
175
|
+
pass "all ${#AGENT_FILES[@]} personas present on both CLIs"
|
|
176
|
+
elif [ "${#AGENT_FILES[@]}" -eq 0 ]; then
|
|
177
|
+
fail "no persona files under $CLAUDE_AGENTS"
|
|
171
178
|
else
|
|
172
179
|
fail "$missing_on_copilot persona file(s) missing from Copilot side"
|
|
173
180
|
fi
|