@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
|
@@ -67,13 +67,15 @@ ISSUE_ID=""
|
|
|
67
67
|
SESSION_ID=""
|
|
68
68
|
TIMEOUT="${FIREBASE_TIMEOUT_SECONDS:-20}"
|
|
69
69
|
|
|
70
|
+
need_value() { [ $# -ge 2 ] || { echo "ERR: $1 needs a value" >&2; exit 4; }; }
|
|
71
|
+
|
|
70
72
|
while [ $# -gt 0 ]; do
|
|
71
73
|
case "$1" in
|
|
72
|
-
--project) PROJECT_ID="$2"; shift 2 ;;
|
|
73
|
-
--platform) PLATFORM="$2"; shift 2 ;;
|
|
74
|
-
--bundle) BUNDLE="$2"; shift 2 ;;
|
|
75
|
-
--issue-id) ISSUE_ID="$2"; shift 2 ;;
|
|
76
|
-
--session-id) SESSION_ID="$2"; shift 2 ;;
|
|
74
|
+
--project) need_value "$@"; PROJECT_ID="$2"; shift 2 ;;
|
|
75
|
+
--platform) need_value "$@"; PLATFORM="$2"; shift 2 ;;
|
|
76
|
+
--bundle) need_value "$@"; BUNDLE="$2"; shift 2 ;;
|
|
77
|
+
--issue-id) need_value "$@"; ISSUE_ID="$2"; shift 2 ;;
|
|
78
|
+
--session-id) need_value "$@"; SESSION_ID="$2"; shift 2 ;;
|
|
77
79
|
-h|--help)
|
|
78
80
|
echo "usage: $0 <issue-url> | $0 --project ... --platform ... --bundle ... --issue-id ... [--session-id ...]" >&2
|
|
79
81
|
exit 4 ;;
|
|
@@ -234,6 +236,7 @@ fi
|
|
|
234
236
|
# 1. Build header + claim, base64url
|
|
235
237
|
# 2. Sign with the SA private key
|
|
236
238
|
# 3. POST to oauth2.googleapis.com/token
|
|
239
|
+
TOKEN_RC=0
|
|
237
240
|
ACCESS_TOKEN=$(SA_JSON_IN="$SA_JSON" python3 - <<'PY'
|
|
238
241
|
import base64, json, os, sys, time, urllib.parse, urllib.request
|
|
239
242
|
|
|
@@ -302,10 +305,10 @@ except Exception as e:
|
|
|
302
305
|
print("", file=sys.stderr)
|
|
303
306
|
sys.exit(2)
|
|
304
307
|
PY
|
|
305
|
-
)
|
|
308
|
+
) || TOKEN_RC=$?
|
|
306
309
|
|
|
307
|
-
if [ -z "$ACCESS_TOKEN" ]; then
|
|
308
|
-
echo "ERR: Firebase token exchange failed" >&2
|
|
310
|
+
if [ "$TOKEN_RC" -ne 0 ] || [ -z "$ACCESS_TOKEN" ]; then
|
|
311
|
+
echo "ERR: Firebase token exchange failed (exchange rc=$TOKEN_RC)" >&2
|
|
309
312
|
exit 3
|
|
310
313
|
fi
|
|
311
314
|
|
|
@@ -89,13 +89,15 @@ BATCH=4
|
|
|
89
89
|
log() { printf '%s\n' "$*" >&2; }
|
|
90
90
|
die() { local code="$1"; shift; log "fetch-figma-annotations: $*"; exit "$code"; }
|
|
91
91
|
|
|
92
|
+
need_value() { [ $# -ge 2 ] || die 1 "$1 requires a value"; }
|
|
93
|
+
|
|
92
94
|
while [ $# -gt 0 ]; do
|
|
93
95
|
case "$1" in
|
|
94
|
-
--file-key) FILE_KEY="$
|
|
95
|
-
--node-id) NODE_IDS="$
|
|
96
|
-
--url) URL="$
|
|
97
|
-
--lang-prefixes) LANG_PREFIXES="${2:-TR,EN}"; shift 2 ;;
|
|
98
|
-
--batch) BATCH="${2:-4}"; shift 2 ;;
|
|
96
|
+
--file-key) need_value "$@"; FILE_KEY="$2"; shift 2 ;;
|
|
97
|
+
--node-id) need_value "$@"; NODE_IDS="$2"; shift 2 ;;
|
|
98
|
+
--url) need_value "$@"; URL="$2"; shift 2 ;;
|
|
99
|
+
--lang-prefixes) need_value "$@"; LANG_PREFIXES="${2:-TR,EN}"; shift 2 ;;
|
|
100
|
+
--batch) need_value "$@"; BATCH="${2:-4}"; shift 2 ;;
|
|
99
101
|
-h|--help) grep -E '^#( |$)' "$0" | sed -E 's/^# ?//'; exit 0 ;;
|
|
100
102
|
*) die 1 "unknown argument: $1" ;;
|
|
101
103
|
esac
|
|
@@ -62,11 +62,13 @@ ISSUE_ID=""
|
|
|
62
62
|
TIMEOUT="${FORTIFY_TIMEOUT_SECONDS:-25}"
|
|
63
63
|
AUTH_SCHEME="${FORTIFY_AUTH_SCHEME:-auto}"
|
|
64
64
|
|
|
65
|
+
need_value() { [ $# -ge 2 ] || { echo "ERR: $1 needs a value" >&2; exit 4; }; }
|
|
66
|
+
|
|
65
67
|
while [ $# -gt 0 ]; do
|
|
66
68
|
case "$1" in
|
|
67
|
-
--version-id) VERSION_ID="$2"; shift 2 ;;
|
|
68
|
-
--issue) ISSUE_ID="$2"; shift 2 ;;
|
|
69
|
-
--instance-id) ISSUE_ID="$2"; shift 2 ;;
|
|
69
|
+
--version-id) need_value "$@"; VERSION_ID="$2"; shift 2 ;;
|
|
70
|
+
--issue) need_value "$@"; ISSUE_ID="$2"; shift 2 ;;
|
|
71
|
+
--instance-id) need_value "$@"; ISSUE_ID="$2"; shift 2 ;;
|
|
70
72
|
-h|--help)
|
|
71
73
|
echo "usage: $0 <ssc-url> | $0 --version-id <id> [--issue <id>] | $0 --instance-id <id>" >&2
|
|
72
74
|
exit 4 ;;
|
|
@@ -73,11 +73,13 @@ TIMEOUT="${GRAYLOG_TIMEOUT_SECONDS:-25}"
|
|
|
73
73
|
RANGE="${GRAYLOG_RANGE_SECONDS:-86400}"
|
|
74
74
|
LIMIT="${GRAYLOG_LIMIT:-200}"
|
|
75
75
|
|
|
76
|
+
need_value() { [ $# -ge 2 ] || { echo "ERR: $1 needs a value" >&2; exit 4; }; }
|
|
77
|
+
|
|
76
78
|
while [ $# -gt 0 ]; do
|
|
77
79
|
case "$1" in
|
|
78
|
-
--trx) TRX_ID="$2"; shift 2 ;;
|
|
79
|
-
--conv) CONV_ID="$2"; shift 2 ;;
|
|
80
|
-
--env) ENVIRONMENT="$2"; shift 2 ;;
|
|
80
|
+
--trx) need_value "$@"; TRX_ID="$2"; shift 2 ;;
|
|
81
|
+
--conv) need_value "$@"; CONV_ID="$2"; shift 2 ;;
|
|
82
|
+
--env) need_value "$@"; ENVIRONMENT="$2"; shift 2 ;;
|
|
81
83
|
-h|--help)
|
|
82
84
|
echo "usage: $0 --trx <transaction-id> | --conv <conversation-id> [--env auto|prod|test]" >&2
|
|
83
85
|
exit 4 ;;
|
|
@@ -85,7 +85,7 @@ refresh_grant_cfg() {
|
|
|
85
85
|
|
|
86
86
|
RESPONSE=$(curl -s --max-time 20 -X POST "$TOKEN_ENDPOINT" \
|
|
87
87
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
88
|
-
-K <(refresh_grant_cfg))
|
|
88
|
+
-K <(refresh_grant_cfg)) || RESPONSE=""
|
|
89
89
|
|
|
90
90
|
NEW_ACCESS=$(printf '%s' "$RESPONSE" | jq -r '.access_token // empty' 2>/dev/null || true)
|
|
91
91
|
NEW_REFRESH=$(printf '%s' "$RESPONSE" | jq -r '.refresh_token // empty' 2>/dev/null || true)
|
|
@@ -81,6 +81,8 @@ if ! command -v resolve_figma_token >/dev/null 2>&1; then
|
|
|
81
81
|
fi
|
|
82
82
|
|
|
83
83
|
FIGMA_API="https://api.figma.com/v1"
|
|
84
|
+
WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/figma-screenshot.XXXXXX")
|
|
85
|
+
trap 'rm -rf "$WORK_DIR"' EXIT INT TERM
|
|
84
86
|
PARALLEL_DL=4
|
|
85
87
|
HTTP_TIMEOUT=60
|
|
86
88
|
MAX_IDS_PER_CALL=50
|
|
@@ -126,7 +128,8 @@ Options:
|
|
|
126
128
|
hold the other channel's design" before a run may write that a
|
|
127
129
|
channel has none. Emits JSON, downloads nothing.
|
|
128
130
|
--feature-slug Feature name to match against, for --discover-sections.
|
|
129
|
-
--output-dir Output directory. Default:
|
|
131
|
+
--output-dir Output directory. Default: a fresh mktemp dir
|
|
132
|
+
($TMPDIR/figma-screenshots.XXXXXX).
|
|
130
133
|
--scale Render scale (1-4). Default: 2.
|
|
131
134
|
--format Image format. Default: png.
|
|
132
135
|
--help Show this help text and exit.
|
|
@@ -256,7 +259,7 @@ fetch_nodes_metadata() {
|
|
|
256
259
|
fetch_image_urls() {
|
|
257
260
|
local ids="$1"
|
|
258
261
|
local tmp
|
|
259
|
-
tmp=$(mktemp)
|
|
262
|
+
tmp=$(mktemp "$WORK_DIR/images.XXXXXX")
|
|
260
263
|
local url="$FIGMA_API/images/$FILE_KEY?ids=$ids&format=$FORMAT&scale=$SCALE"
|
|
261
264
|
local code
|
|
262
265
|
code=$(curl_figma "$tmp" "$url")
|
|
@@ -281,7 +284,7 @@ render_and_download() {
|
|
|
281
284
|
|
|
282
285
|
# Emit a job list (nodeId<TAB>url<TAB>outPath) for parallel download.
|
|
283
286
|
local jobs
|
|
284
|
-
jobs=$(mktemp)
|
|
287
|
+
jobs=$(mktemp "$WORK_DIR/jobs.XXXXXX")
|
|
285
288
|
local key_prefix="${FILE_KEY:0:4}"
|
|
286
289
|
printf '%s' "$urls_json" | jq -r 'to_entries[] | "\(.key)\t\(.value)"' \
|
|
287
290
|
| while IFS=$'\t' read -r nid url; do
|
|
@@ -292,7 +295,7 @@ render_and_download() {
|
|
|
292
295
|
|
|
293
296
|
# Parallel download. xargs -P keeps the channel count bounded.
|
|
294
297
|
local results
|
|
295
|
-
results=$(mktemp)
|
|
298
|
+
results=$(mktemp "$WORK_DIR/results.XXXXXX")
|
|
296
299
|
# shellcheck disable=SC2016
|
|
297
300
|
awk -F'\t' '{printf "%s\037%s\037%s\n", $1,$2,$3}' "$jobs" \
|
|
298
301
|
| xargs -P "$PARALLEL_DL" -I{} bash -c '
|
|
@@ -389,17 +392,19 @@ if [ "$#" -eq 0 ]; then
|
|
|
389
392
|
exit 1
|
|
390
393
|
fi
|
|
391
394
|
|
|
395
|
+
need_value() { [ "$#" -ge 2 ] || die 1 "$1 requires a value"; }
|
|
396
|
+
|
|
392
397
|
while [ "$#" -gt 0 ]; do
|
|
393
398
|
case "$1" in
|
|
394
399
|
--help|-h) print_help; exit 0 ;;
|
|
395
|
-
--file-key) FILE_KEY="$
|
|
396
|
-
--node-id) NODE_ID="$
|
|
397
|
-
--section) SECTION_URL="$
|
|
400
|
+
--file-key) need_value "$@"; FILE_KEY="$2"; shift 2 ;;
|
|
401
|
+
--node-id) need_value "$@"; NODE_ID="$2"; shift 2 ;;
|
|
402
|
+
--section) need_value "$@"; SECTION_URL="$2"; shift 2 ;;
|
|
398
403
|
--discover-sections) DISCOVER=1; shift ;;
|
|
399
|
-
--feature-slug) FEATURE_SLUG="$
|
|
400
|
-
--output-dir) OUTPUT_DIR="$
|
|
401
|
-
--scale) SCALE="${2:-2}"; shift 2 ;;
|
|
402
|
-
--format) FORMAT="${2:-png}"; shift 2 ;;
|
|
404
|
+
--feature-slug) need_value "$@"; FEATURE_SLUG="$2"; shift 2 ;;
|
|
405
|
+
--output-dir) need_value "$@"; OUTPUT_DIR="$2"; shift 2 ;;
|
|
406
|
+
--scale) need_value "$@"; SCALE="${2:-2}"; shift 2 ;;
|
|
407
|
+
--format) need_value "$@"; FORMAT="${2:-png}"; shift 2 ;;
|
|
403
408
|
*) die 1 "unknown argument: $1" ;;
|
|
404
409
|
esac
|
|
405
410
|
done
|
|
@@ -431,7 +436,7 @@ if [ -z "${FIGMA_TOKEN:-}" ]; then
|
|
|
431
436
|
fi
|
|
432
437
|
|
|
433
438
|
if [ -z "$OUTPUT_DIR" ]; then
|
|
434
|
-
OUTPUT_DIR
|
|
439
|
+
OUTPUT_DIR=$(mktemp -d "${TMPDIR:-/tmp}/figma-screenshots.XXXXXX")
|
|
435
440
|
fi
|
|
436
441
|
mkdir -p "$OUTPUT_DIR"
|
|
437
442
|
|
|
@@ -444,14 +449,12 @@ mkdir -p "$OUTPUT_DIR"
|
|
|
444
449
|
# "Elite Kart" on one section and "Elit Kart" on its sibling.
|
|
445
450
|
|
|
446
451
|
run_discover_mode() {
|
|
447
|
-
local
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
printf '%s' "$body" | FEATURE_SLUG="$FEATURE_SLUG" FILE_KEY="$FILE_KEY" python3 -c '
|
|
452
|
+
local tree code
|
|
453
|
+
tree=$(mktemp "$WORK_DIR/tree.XXXXXX")
|
|
454
|
+
code=$(curl_figma "$tree" "$FIGMA_API/files/${FILE_KEY}?depth=3")
|
|
455
|
+
handle_http_code "$code" "file tree for $FILE_KEY"
|
|
456
|
+
|
|
457
|
+
FEATURE_SLUG="$FEATURE_SLUG" FILE_KEY="$FILE_KEY" python3 -c '
|
|
455
458
|
import json, os, re, sys, unicodedata
|
|
456
459
|
|
|
457
460
|
def fold(t):
|
|
@@ -475,11 +478,11 @@ def channel_of(name):
|
|
|
475
478
|
return label
|
|
476
479
|
return None
|
|
477
480
|
|
|
478
|
-
raw = sys.
|
|
481
|
+
raw = open(sys.argv[1]).read()
|
|
479
482
|
try:
|
|
480
483
|
doc = json.loads(raw).get("document", {})
|
|
481
484
|
except Exception:
|
|
482
|
-
print(json.dumps({"error": "unparseable file response"})); sys.exit(
|
|
485
|
+
print(json.dumps({"error": "unparseable file response"})); sys.exit(6)
|
|
483
486
|
|
|
484
487
|
slug_words = [w for w in fold(os.environ["FEATURE_SLUG"]).split() if len(w) > 2]
|
|
485
488
|
out, weak, pages = [], [], 0
|
|
@@ -516,7 +519,7 @@ print(json.dumps({
|
|
|
516
519
|
"candidates": sorted(out, key=lambda c: (c["channelGuess"] or "zz", c["id"])),
|
|
517
520
|
"weakMatches": sorted(weak, key=lambda c: c["id"]),
|
|
518
521
|
}, ensure_ascii=False, indent=2))
|
|
519
|
-
'
|
|
522
|
+
' "$tree"
|
|
520
523
|
}
|
|
521
524
|
|
|
522
525
|
# --- Section mode -----------------------------------------------------------
|
|
@@ -570,7 +573,7 @@ PY
|
|
|
570
573
|
|
|
571
574
|
# Collect ids and chunk to MAX_IDS_PER_CALL.
|
|
572
575
|
local ids_file
|
|
573
|
-
ids_file=$(mktemp)
|
|
576
|
+
ids_file=$(mktemp "$WORK_DIR/ids.XXXXXX")
|
|
574
577
|
awk -F'\t' '{print $1}' "$lookup" > "$ids_file"
|
|
575
578
|
|
|
576
579
|
local rows
|
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
# An unset or non-executable CRED_STORE is tolerated: resolution falls through to
|
|
32
32
|
# the env fallback so a keychain-less CI box still works.
|
|
33
33
|
|
|
34
|
-
# The canonical key first, the legacy key second. Space-separated
|
|
35
|
-
# below
|
|
34
|
+
# The canonical key first, the legacy key second. Space-separated; the loop
|
|
35
|
+
# below splits explicitly because zsh (the pipeline's Bash tool) does not
|
|
36
|
+
# word-split an unquoted parameter, so `for key in $KEYS` saw one key there.
|
|
36
37
|
FIGMA_TOKEN_LOGICAL_KEYS="${FIGMA_TOKEN_LOGICAL_KEYS:-figma figma_pat}"
|
|
37
38
|
|
|
38
39
|
# resolve_figma_token
|
|
@@ -40,13 +41,16 @@ FIGMA_TOKEN_LOGICAL_KEYS="${FIGMA_TOKEN_LOGICAL_KEYS:-figma figma_pat}"
|
|
|
40
41
|
resolve_figma_token() {
|
|
41
42
|
local key tok
|
|
42
43
|
if [ -n "${CRED_STORE:-}" ] && [ -x "${CRED_STORE:-}" ]; then
|
|
43
|
-
|
|
44
|
+
while IFS= read -r key; do
|
|
45
|
+
[ -n "$key" ] || continue
|
|
44
46
|
tok=$("$CRED_STORE" get "$key" 2>/dev/null || true)
|
|
45
47
|
if [ -n "$tok" ]; then
|
|
46
48
|
printf '%s' "$tok"
|
|
47
49
|
return 0
|
|
48
50
|
fi
|
|
49
|
-
done
|
|
51
|
+
done <<EOF_KEYS
|
|
52
|
+
$(printf '%s\n' "$FIGMA_TOKEN_LOGICAL_KEYS" | tr ' ' '\n')
|
|
53
|
+
EOF_KEYS
|
|
50
54
|
fi
|
|
51
55
|
if [ -n "${FIGMA_PAT:-}" ]; then
|
|
52
56
|
printf '%s' "$FIGMA_PAT"
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
# Required env:
|
|
44
44
|
# ACCOUNT_JIRA_TOKEN_KEY keychain service name (optional)
|
|
45
45
|
# ACCOUNT_JIRA_HOST e.g. jira.example.com (optional)
|
|
46
|
-
# ACCOUNT_GH_TOKEN_KEY keychain service name (optional)
|
|
47
46
|
# ACCOUNT_DEFAULT_OWNER fallback GitHub owner (optional)
|
|
48
47
|
# ACCOUNT_DEFAULT_REPO fallback GitHub repo (optional)
|
|
49
48
|
# PROMPT_LANG tr|en - controls maturity.summary language (default: tr)
|
|
@@ -54,13 +54,15 @@ BACKUP_DIR="${JIRA_BACKUP_DIR:-$HOME/.claude/logs/multi-agent/jira-backups}"
|
|
|
54
54
|
|
|
55
55
|
die() { echo "$1" >&2; exit "${2:-64}"; }
|
|
56
56
|
|
|
57
|
+
need_value() { [ "$#" -ge 2 ] || die "$1 requires a value"; }
|
|
58
|
+
|
|
57
59
|
while [ "$#" -gt 0 ]; do
|
|
58
60
|
case "$1" in
|
|
59
|
-
--issue) ISSUE="$
|
|
60
|
-
--body-file) BODY_FILE="$
|
|
61
|
-
--target) TARGET="$
|
|
62
|
-
--mode) MODE="$
|
|
63
|
-
--backup-dir) BACKUP_DIR="$
|
|
61
|
+
--issue) need_value "$@"; ISSUE="$2"; shift 2 ;;
|
|
62
|
+
--body-file) need_value "$@"; BODY_FILE="$2"; shift 2 ;;
|
|
63
|
+
--target) need_value "$@"; TARGET="$2"; shift 2 ;;
|
|
64
|
+
--mode) need_value "$@"; MODE="$2"; shift 2 ;;
|
|
65
|
+
--backup-dir) need_value "$@"; BACKUP_DIR="$2"; shift 2 ;;
|
|
64
66
|
--confirm-overwrite) CONFIRM=1; shift ;;
|
|
65
67
|
--dry-run) DRY_RUN=1; shift ;;
|
|
66
68
|
-h|--help) sed -n '2,40p' "$0"; exit 0 ;;
|
|
@@ -69,6 +69,11 @@ PUNCT_MAP_DEFAULT_TR = {
|
|
|
69
69
|
PUNCT_MAP_EN = dict(PUNCT_MAP_DEFAULT_TR)
|
|
70
70
|
PUNCT_MAP_EN["\u00A7"] = "section"
|
|
71
71
|
|
|
72
|
+
# Typographic dashes (U+2010 to U+2015), quotes (U+2018 to U+201F), the
|
|
73
|
+
# ellipsis and the section sign, including the variants the maps above do
|
|
74
|
+
# not rewrite, so a residual can actually be reported.
|
|
75
|
+
FORBIDDEN_PUNCT_RE = re.compile("[\u2010-\u2015\u2018-\u201F\u2026\u00A7]")
|
|
76
|
+
|
|
72
77
|
|
|
73
78
|
def detect_language(markdown: str, front_matter_lang: str | None) -> str:
|
|
74
79
|
if front_matter_lang:
|
|
@@ -76,12 +81,11 @@ def detect_language(markdown: str, front_matter_lang: str | None) -> str:
|
|
|
76
81
|
if lang.startswith("en"):
|
|
77
82
|
return "en"
|
|
78
83
|
return "tr"
|
|
79
|
-
# Heuristic:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return "tr"
|
|
84
|
+
# Heuristic: a Turkish document carries Turkish-only letters somewhere in
|
|
85
|
+
# its body; a text without a single one is English.
|
|
86
|
+
if re.search(r"[ğışçöüĞİŞÇÖÜ]", markdown):
|
|
87
|
+
return "tr"
|
|
88
|
+
return "en"
|
|
85
89
|
|
|
86
90
|
|
|
87
91
|
def apply_punctuation_gate(text: str, language: str) -> tuple[str, list[str]]:
|
|
@@ -91,7 +95,9 @@ def apply_punctuation_gate(text: str, language: str) -> tuple[str, list[str]]:
|
|
|
91
95
|
out = out.replace(src, dst)
|
|
92
96
|
|
|
93
97
|
warnings: list[str] = []
|
|
94
|
-
|
|
98
|
+
# The residual check must look wider than the mapping: every mapped
|
|
99
|
+
# character was just replaced, so testing those again can never fire.
|
|
100
|
+
leftover = sorted(set(FORBIDDEN_PUNCT_RE.findall(out)))
|
|
95
101
|
if leftover:
|
|
96
102
|
warnings.append(
|
|
97
103
|
"punctuation gate: residual forbidden characters: "
|
|
@@ -33,7 +33,7 @@ set -euo pipefail
|
|
|
33
33
|
CMD="${1:-}"
|
|
34
34
|
STATE="${2:-}"
|
|
35
35
|
[ -z "$CMD" ] || [ -z "$STATE" ] && {
|
|
36
|
-
echo "usage: $0 <prepare|commit|push|pr|teardown> <state.json>" >&2
|
|
36
|
+
echo "usage: $0 <prepare|bridge|commit|push|pr|teardown> <state.json>" >&2
|
|
37
37
|
exit 1
|
|
38
38
|
}
|
|
39
39
|
[ ! -f "$STATE" ] && { echo "ERR: state file not found: $STATE" >&2; exit 2; }
|
|
@@ -54,6 +54,8 @@ TASK_ID=$(python3 -c "import json; d=json.load(open('$STATE')); print(d['taskId'
|
|
|
54
54
|
COMMIT_TYPE=$(python3 -c "import json; d=json.load(open('$STATE')); print(d.get('commitType','feat'))")
|
|
55
55
|
ISSUE_KEY=$(python3 -c "import json; d=json.load(open('$STATE')); print(d['issueRef'].get('key') or '')")
|
|
56
56
|
ISSUE_TITLE=$(python3 -c "import json; d=json.load(open('$STATE')); print(d['issueRef'].get('title') or '')")
|
|
57
|
+
IDENTITY_NAME=$(python3 -c "import json; d=json.load(open('$STATE')); print((d.get('identity') or {}).get('name') or '')")
|
|
58
|
+
IDENTITY_EMAIL=$(python3 -c "import json; d=json.load(open('$STATE')); print((d.get('identity') or {}).get('email') or '')")
|
|
57
59
|
|
|
58
60
|
cmd_prepare() {
|
|
59
61
|
mkdir -p "$WORKTREE_ROOT"
|
|
@@ -90,24 +92,32 @@ cmd_commit() {
|
|
|
90
92
|
# loop - a piped `while` runs in a subshell and the summary exit would
|
|
91
93
|
# always be 0 regardless of per-repo failures.
|
|
92
94
|
local failed=0
|
|
95
|
+
# state.identity is the commit author/committer, as documented in the schema
|
|
96
|
+
# above. Git's own config is the fallback when the picker recorded none.
|
|
97
|
+
local identity_args=()
|
|
98
|
+
if [ -n "$IDENTITY_NAME" ] && [ -n "$IDENTITY_EMAIL" ]; then
|
|
99
|
+
identity_args=(-c "user.name=$IDENTITY_NAME" -c "user.email=$IDENTITY_EMAIL")
|
|
100
|
+
fi
|
|
93
101
|
while IFS=$'\t' read -r name url provider; do
|
|
94
102
|
local target="$WORKTREE_ROOT/$name"
|
|
95
103
|
[ ! -d "$target" ] && continue
|
|
96
104
|
cd "$target"
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
105
|
+
# Only what the dev session staged is committed. A blanket `git add -A`
|
|
106
|
+
# here swept build output and nested-worktree gitlinks into the commit.
|
|
107
|
+
if git diff --cached --quiet 2>/dev/null; then
|
|
108
|
+
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
|
|
109
|
+
echo "[$name] nothing staged (unstaged or untracked changes present) - stage what belongs in the commit, skipping"
|
|
110
|
+
else
|
|
111
|
+
echo "[$name] no changes, skipping commit"
|
|
112
|
+
fi
|
|
102
113
|
cd - >/dev/null
|
|
103
114
|
continue
|
|
104
115
|
fi
|
|
105
|
-
git add -A
|
|
106
116
|
local subject="$COMMIT_TYPE($name): $ISSUE_TITLE"
|
|
107
117
|
[ -n "$ISSUE_KEY" ] && subject="$subject [$ISSUE_KEY]"
|
|
108
118
|
# Trim subject to 72 chars
|
|
109
119
|
subject=$(printf '%s' "$subject" | cut -c1-72)
|
|
110
|
-
if git commit --quiet -m "$subject"; then
|
|
120
|
+
if git ${identity_args[@]+"${identity_args[@]}"} commit --quiet -m "$subject"; then
|
|
111
121
|
echo "[$name] committed: $subject"
|
|
112
122
|
else
|
|
113
123
|
echo "ERR: [$name] commit failed" >&2
|
|
@@ -155,10 +155,19 @@ do_start() {
|
|
|
155
155
|
write_state "$state_path" "$new"
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
require_todo() {
|
|
159
|
+
# $1 state path, $2 todo_id - exits 1 when the id is not in the plan.
|
|
160
|
+
if ! jq -e --arg id "$2" '(.plan.todos // []) | map(select(.id == $id)) | length > 0' "$1" >/dev/null 2>&1; then
|
|
161
|
+
echo "plan-todos: todo '$2' not found" >&2
|
|
162
|
+
exit 1
|
|
163
|
+
fi
|
|
164
|
+
}
|
|
165
|
+
|
|
158
166
|
do_complete() {
|
|
159
167
|
local task_id="${1:-}"; local todo_id="${2:-}"; local notes="${3:-}"
|
|
160
168
|
[ -z "$task_id" ] || [ -z "$todo_id" ] && { echo "usage: complete <task-id> <todo-id> [notes]" >&2; exit 64; }
|
|
161
169
|
local state_path; state_path=$(resolve_state "$task_id")
|
|
170
|
+
require_todo "$state_path" "$todo_id"
|
|
162
171
|
local now; now=$(iso_now)
|
|
163
172
|
local new
|
|
164
173
|
new=$(jq --arg id "$todo_id" --arg t "$now" --arg n "$notes" '
|
|
@@ -176,6 +185,7 @@ do_fail() {
|
|
|
176
185
|
local task_id="${1:-}"; local todo_id="${2:-}"; local reason="${3:-}"
|
|
177
186
|
[ -z "$task_id" ] || [ -z "$todo_id" ] || [ -z "$reason" ] && { echo "usage: fail <task-id> <todo-id> <reason>" >&2; exit 64; }
|
|
178
187
|
local state_path; state_path=$(resolve_state "$task_id")
|
|
188
|
+
require_todo "$state_path" "$todo_id"
|
|
179
189
|
local now; now=$(iso_now)
|
|
180
190
|
local new
|
|
181
191
|
new=$(jq --arg id "$todo_id" --arg t "$now" --arg r "$reason" '
|
|
@@ -190,6 +200,7 @@ do_skip() {
|
|
|
190
200
|
local task_id="${1:-}"; local todo_id="${2:-}"; local reason="${3:-}"
|
|
191
201
|
[ -z "$task_id" ] || [ -z "$todo_id" ] || [ -z "$reason" ] && { echo "usage: skip <task-id> <todo-id> <reason>" >&2; exit 64; }
|
|
192
202
|
local state_path; state_path=$(resolve_state "$task_id")
|
|
203
|
+
require_todo "$state_path" "$todo_id"
|
|
193
204
|
local now; now=$(iso_now)
|
|
194
205
|
local new
|
|
195
206
|
new=$(jq --arg id "$todo_id" --arg t "$now" --arg r "$reason" '
|
|
@@ -408,9 +408,16 @@ post_bitbucket_server() {
|
|
|
408
408
|
needs_work) status_payload='{"status":"NEEDS_WORK"}' ;;
|
|
409
409
|
esac
|
|
410
410
|
|
|
411
|
-
|
|
411
|
+
local http
|
|
412
|
+
http=$(curl -sS -m 30 -K <(bb_auth_cfg "$auth_user" "$auth_token") -X PUT \
|
|
412
413
|
-H "Content-Type: application/json" "$base/participants/$user_slug" \
|
|
413
|
-
-d "$status_payload"
|
|
414
|
+
-d "$status_payload" -o /dev/null -w '%{http_code}' || echo 000)
|
|
415
|
+
case "$http" in
|
|
416
|
+
200|201) ;;
|
|
417
|
+
*)
|
|
418
|
+
echo "post-pr-review: bitbucket verdict PUT failed (HTTP $http) for PR #$PR_NUM - $DECISION was NOT recorded" >&2
|
|
419
|
+
return 4 ;;
|
|
420
|
+
esac
|
|
414
421
|
}
|
|
415
422
|
|
|
416
423
|
# --- Main switch ------------------------------------------------------------
|
|
@@ -18,12 +18,13 @@
|
|
|
18
18
|
# ./repo-cache.sh bitbucket <workspace> [--refresh]
|
|
19
19
|
#
|
|
20
20
|
# Required env per-provider:
|
|
21
|
-
# github ->
|
|
21
|
+
# github -> (none) - uses the gh CLI's own auth; GH_USER selects the account
|
|
22
22
|
# bitbucket -> BB_TOKEN_KEY, BB_USER_KEY, BB_HOST (e.g. bitbucket.example.com)
|
|
23
23
|
# local -> (none) - scopes are treated as filesystem roots; "scope=$HOME"
|
|
24
24
|
# walks $HOME with maxdepth, scope=<path> walks just that path.
|
|
25
|
-
# LOCAL_SCAN_ROOTS env
|
|
26
|
-
# LOCAL_SCAN_MAXDEPTH env (default 4) bounds
|
|
25
|
+
# LOCAL_SCAN_ROOTS env (colon-separated, like PATH) overrides the
|
|
26
|
+
# default root list. LOCAL_SCAN_MAXDEPTH env (default 4) bounds
|
|
27
|
+
# traversal cost.
|
|
27
28
|
|
|
28
29
|
set -euo pipefail
|
|
29
30
|
|
|
@@ -211,16 +212,23 @@ print(json.dumps(out))
|
|
|
211
212
|
local)
|
|
212
213
|
# Scan filesystem roots for git repos. Default roots cover the common
|
|
213
214
|
# places personal/work checkouts live; override via LOCAL_SCAN_ROOTS.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
# When SCOPE is an absolute path use it directly; otherwise treat SCOPE
|
|
218
|
-
# as a label and walk the configured roots.
|
|
215
|
+
# Roots are held in an array, never a whitespace-split string: a HOME with
|
|
216
|
+
# a space in it split into two nonexistent roots and the scan found nothing.
|
|
217
|
+
ROOTS=()
|
|
219
218
|
if [ -d "$SCOPE" ]; then
|
|
220
|
-
ROOTS="$SCOPE"
|
|
219
|
+
ROOTS=("$SCOPE")
|
|
220
|
+
elif [ -n "${LOCAL_SCAN_ROOTS:-}" ]; then
|
|
221
|
+
while IFS= read -r root; do
|
|
222
|
+
[ -n "$root" ] && ROOTS+=("$root")
|
|
223
|
+
done <<EOF_ROOTS
|
|
224
|
+
$(printf '%s\n' "$LOCAL_SCAN_ROOTS" | tr ':' '\n')
|
|
225
|
+
EOF_ROOTS
|
|
226
|
+
else
|
|
227
|
+
ROOTS=("$HOME" "$HOME/Documents" "$HOME/Developer" "$HOME/projects" "$HOME/code" "$HOME/work")
|
|
221
228
|
fi
|
|
229
|
+
MAXDEPTH="${LOCAL_SCAN_MAXDEPTH:-4}"
|
|
222
230
|
: > "$CACHE_TMP.list"
|
|
223
|
-
for root in $ROOTS; do
|
|
231
|
+
for root in ${ROOTS[@]+"${ROOTS[@]}"}; do
|
|
224
232
|
[ -d "$root" ] || continue
|
|
225
233
|
# -prune avoids descending into already-found .git trees (nested submodules
|
|
226
234
|
# show up via submodule-detector, not the top-level picker).
|
|
@@ -10,11 +10,16 @@
|
|
|
10
10
|
# - Reads watched repos from prefs.global.reviewWatch.repos[] (or --repos).
|
|
11
11
|
# - For each repo, lists open PRs since the last seen cursor (state in
|
|
12
12
|
# ~/.claude/state/review-watch/<owner-repo>.json).
|
|
13
|
-
# - For each PR not yet reviewed (or updated since last review),
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
13
|
+
# - For each PR not yet reviewed (or updated since last review), writes the
|
|
14
|
+
# PR input to a per-PR agent-state.json (see Storage) and prints the
|
|
15
|
+
# dispatch line. The review itself is /multi-agent:review's LLM loop,
|
|
16
|
+
# which fills `.review.findings` for that task id; review-watch never
|
|
17
|
+
# spends tokens.
|
|
18
|
+
# - On a later scan, a PR whose agent-state carries findings for the
|
|
19
|
+
# current head SHA is posted through post-pr-review.sh, and only a
|
|
20
|
+
# successful post stamps reviewed_prs[<n>] = <sha>. A PR is never marked
|
|
21
|
+
# reviewed without a review having been posted.
|
|
22
|
+
# REVIEW_WATCH_DRY_RUN=1 reports what would be posted and stamps nothing.
|
|
18
23
|
# - Honors labelFilter (only review PRs carrying the label, default: all).
|
|
19
24
|
# - Skips PRs the user authored (we don't review our own PRs through this
|
|
20
25
|
# surface - that's what the in-pipeline Phase 4 / standalone /review is
|
|
@@ -36,13 +41,26 @@
|
|
|
36
41
|
# ~/.claude/state/review-watch/<owner__repo>.json
|
|
37
42
|
# { last_seen_iso: "2026-05-11T12:34:56Z",
|
|
38
43
|
# reviewed_prs: { "1234": "head-sha-abc" } }
|
|
44
|
+
# ~/.claude/logs/multi-agent/review-watch/<owner__repo>-pr<n>/agent-state.json
|
|
45
|
+
# { taskId, review: { input: {...}, headCommitSha, findings? } }
|
|
46
|
+
# (task id <owner__repo>-pr<n>; post-pr-review.sh reads it by that id)
|
|
39
47
|
|
|
40
48
|
set -euo pipefail
|
|
41
49
|
|
|
42
50
|
PREFS="$HOME/.claude/multi-agent-preferences.json"
|
|
43
51
|
STATE_DIR="$HOME/.claude/state/review-watch"
|
|
52
|
+
REVIEW_LOGS="$HOME/.claude/logs/multi-agent/review-watch"
|
|
44
53
|
mkdir -p "$STATE_DIR"
|
|
45
54
|
|
|
55
|
+
POSTER=""
|
|
56
|
+
for _cand in "$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/post-pr-review.sh" \
|
|
57
|
+
"$HOME/.claude/lib/post-pr-review.sh" \
|
|
58
|
+
"$HOME/.copilot/lib/post-pr-review.sh" \
|
|
59
|
+
"$HOME/.codex/lib/post-pr-review.sh"; do
|
|
60
|
+
[ -f "$_cand" ] && { POSTER="$_cand"; break; }
|
|
61
|
+
done
|
|
62
|
+
unset _cand
|
|
63
|
+
|
|
46
64
|
CMD="${1:-}"
|
|
47
65
|
shift || true
|
|
48
66
|
|
|
@@ -192,16 +210,44 @@ review_one_repo() {
|
|
|
192
210
|
continue
|
|
193
211
|
fi
|
|
194
212
|
|
|
213
|
+
local task_id state_file
|
|
214
|
+
task_id="${repo//\//__}-pr${pr_num}"
|
|
215
|
+
state_file="$REVIEW_LOGS/$task_id/agent-state.json"
|
|
216
|
+
|
|
217
|
+
# Findings already written for THIS head by /multi-agent:review: post them.
|
|
218
|
+
# The stamp below is the only place reviewed_prs is written, and it is
|
|
219
|
+
# written only after post-pr-review.sh returned 0.
|
|
220
|
+
if [ -f "$state_file" ] \
|
|
221
|
+
&& [ "$(jq -r '.review.headCommitSha // ""' "$state_file" 2>/dev/null)" = "$head_sha" ] \
|
|
222
|
+
&& jq -e '.review.findings | type == "array"' "$state_file" >/dev/null 2>&1; then
|
|
223
|
+
if [ "${REVIEW_WATCH_DRY_RUN:-0}" = "1" ]; then
|
|
224
|
+
printf ' dry-run: would post the review for PR #%s (task %s) via post-pr-review.sh\n' "$pr_num" "$task_id"
|
|
225
|
+
elif [ -z "$POSTER" ]; then
|
|
226
|
+
err "post-pr-review.sh not found - PR #$pr_num stays pending"
|
|
227
|
+
elif bash "$POSTER" "$task_id"; then
|
|
228
|
+
printf ' posted review for PR #%s at %s\n' "$pr_num" "$head_sha"
|
|
229
|
+
state=$(jq --arg n "$pr_num" --arg s "$head_sha" \
|
|
230
|
+
'.reviewed_prs[$n] = $s' <<<"$state")
|
|
231
|
+
else
|
|
232
|
+
err "post-pr-review.sh failed for $repo PR #$pr_num - stays pending, not stamped"
|
|
233
|
+
fi
|
|
234
|
+
continue
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
# No review for this head yet: write the input /multi-agent:review reads
|
|
238
|
+
# and hand off. review-watch does not run the LLM itself, because token
|
|
239
|
+
# cost decisions stay with the user; the PR stays un-stamped until a
|
|
240
|
+
# review is actually posted.
|
|
195
241
|
printf ' dispatch review: %s PR #%s - %s\n' "$repo" "$pr_num" "$title"
|
|
196
|
-
printf ' url=%s head=%s\n' "$url" "$head_sha"
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
242
|
+
printf ' url=%s head=%s task=%s\n' "$url" "$head_sha" "$task_id"
|
|
243
|
+
mkdir -p "$(dirname "$state_file")"
|
|
244
|
+
jq -n --arg repo "$repo" --arg n "$pr_num" --arg sha "$head_sha" \
|
|
245
|
+
--arg url "$url" --arg title "$title" --arg t "$task_id" '
|
|
246
|
+
{ schemaVersion: "2.1.0", taskId: $t,
|
|
247
|
+
review: { input: { kind: "pr", provider: "github", host: "github.com",
|
|
248
|
+
orgRepo: $repo, prNumber: ($n | tonumber),
|
|
249
|
+
url: $url, title: $title },
|
|
250
|
+
headCommitSha: $sha, iterationNumber: 1 } }' > "$state_file"
|
|
205
251
|
done < <(jq -c '.[]' <<<"$prs_json")
|
|
206
252
|
|
|
207
253
|
# Persist the NEWEST updatedAt seen this scan - the previous per-PR
|