@mmerterden/multi-agent-pipeline 20.2.0 → 20.3.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 +923 -1
- package/README.md +104 -81
- package/README.tr.md +103 -62
- package/docs/FIGMA_PIPELINE.md +35 -35
- package/docs/adr/0006-skills-core-external-split.md +1 -1
- package/docs/adr/0007-multi-tool-adapter-framework.md +6 -0
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +7 -0
- package/docs/architecture.md +50 -14
- package/docs/best-practices.md +1 -1
- package/docs/ecosystem.md +56 -32
- package/docs/facts.json +10 -10
- package/docs/features.md +97 -5
- package/docs/recovery-guide.md +7 -14
- package/docs/server-readiness.md +31 -24
- package/index.js +1 -1
- package/install/_common.mjs +3 -5
- package/install/_platform-filter.mjs +23 -1
- package/install/_unattended-profile.mjs +321 -75
- package/install/claude.mjs +51 -10
- package/install/codex.mjs +2 -0
- package/install/copilot.mjs +2 -0
- package/install/index.mjs +30 -17
- package/install/templates/claude-hooks.json +16 -5
- package/install/templates/copilot-instructions.md +1 -1
- package/install/templates/multi-agent-autopilot-awake.plist.template +48 -0
- package/install/templates/multi-agent-autopilot.plist.template +12 -5
- package/install/unattended-profile-legacy.json +80 -0
- package/manifest.json +616 -483
- package/package.json +8 -3
- package/pipeline/agents/code-reviewer.md +10 -0
- package/pipeline/agents/plan-critic.md +98 -0
- package/pipeline/agents/security-auditor.md +10 -0
- package/pipeline/agents/task-clarifier.md +10 -0
- package/pipeline/commands/multi-agent/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/analysis/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +12 -1
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/autopilot-off/SKILL.md +36 -6
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +77 -12
- package/pipeline/commands/multi-agent/autopilot-status/SKILL.md +29 -8
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/channels/SKILL.md +9 -9
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/create-jira/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/design-check/SKILL.md +16 -11
- package/pipeline/commands/multi-agent/diff-explain/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/doctor/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/feedback/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/forget/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/graph/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/help/SKILL.md +37 -39
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/issue/SKILL.md +7 -1
- package/pipeline/commands/multi-agent/jira/SKILL.md +7 -1
- package/pipeline/commands/multi-agent/kill/SKILL.md +9 -3
- package/pipeline/commands/multi-agent/language/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/log/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +5 -0
- package/pipeline/commands/multi-agent/model/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/prune-logs/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/prune-prompts/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/purge/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/refactor/SKILL.md +5 -0
- package/pipeline/commands/multi-agent/research/SKILL.md +49 -0
- package/pipeline/commands/multi-agent/resume/SKILL.md +29 -6
- package/pipeline/commands/multi-agent/review/SKILL.md +28 -9
- package/pipeline/commands/multi-agent/review-analysis/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/review-issue/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/review-jira/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/route-off/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/route-on/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/route-status/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/routines/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/save/SKILL.md +6 -2
- package/pipeline/commands/multi-agent/scaffold/SKILL.md +47 -0
- package/pipeline/commands/multi-agent/scan/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/search/SKILL.md +12 -8
- package/pipeline/commands/multi-agent/security-review/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/serve/SKILL.md +62 -0
- package/pipeline/commands/multi-agent/setup/SKILL.md +27 -36
- package/pipeline/commands/multi-agent/stack/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/status/SKILL.md +10 -9
- package/pipeline/commands/multi-agent/steer/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/sync/SKILL.md +13 -13
- package/pipeline/commands/multi-agent/test/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/test-accessibility/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/test-dark-mode/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/test-dynamic-type/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/test-screenshots/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +4 -0
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +5 -1
- package/pipeline/commands/multi-agent/update/SKILL.md +4 -0
- package/pipeline/contract/CHANGELOG.md +74 -0
- package/pipeline/contract/README.md +126 -0
- package/pipeline/contract/build.mjs +427 -0
- package/pipeline/contract/fixtures/answer-result.json +11 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +8 -0
- package/pipeline/contract/fixtures/error-unauthorized.json +5 -0
- package/pipeline/contract/fixtures/error-unsigned.json +5 -0
- package/pipeline/contract/fixtures/issues-empty.json +18 -0
- package/pipeline/contract/fixtures/launch-plan.json +31 -0
- package/pipeline/contract/fixtures/runs-awaiting-question.json +70 -0
- package/pipeline/contract/fixtures/runs-empty.json +6 -0
- package/pipeline/contract/fixtures/runs-failed.json +84 -0
- package/pipeline/contract/fixtures/runs-old-schema.json +70 -0
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +101 -0
- package/pipeline/contract/fixtures/runs-pr-opened.json +106 -0
- package/pipeline/contract/fixtures/runs-running.json +84 -0
- package/pipeline/contract/fixtures/worktrees-empty.json +4 -0
- package/pipeline/contract/frozen/toolbox.json +107 -0
- package/pipeline/contract/manifest.json +263 -0
- package/pipeline/contract/types/index.d.ts +343 -0
- package/pipeline/lib/_jira-auth.sh +6 -2
- package/pipeline/lib/account-resolver.sh +1 -1
- package/pipeline/lib/autopilot-state.sh +19 -0
- package/pipeline/lib/context-link-extractor.sh +12 -5
- package/pipeline/lib/credential-inventory.sh +12 -5
- package/pipeline/lib/credential-store.sh +116 -185
- package/pipeline/lib/fetch-confluence.sh +44 -3
- package/pipeline/lib/fetch-document.sh +3 -4
- package/pipeline/lib/fetch-fortify.sh +1 -1
- package/pipeline/lib/figma-mcp-refresh.sh +2 -2
- package/pipeline/lib/figma-token.sh +5 -1
- package/pipeline/lib/issue-fetcher.sh +233 -16
- package/pipeline/lib/json-file-lock.mjs +172 -0
- package/pipeline/lib/model-dispatch.sh +21 -12
- package/pipeline/lib/model-rung.sh +6 -1
- package/pipeline/lib/multi-repo-pipeline.sh +1 -1
- package/pipeline/lib/outbound-gate.mjs +46 -16
- package/pipeline/lib/parse-complaints.sh +14 -7
- package/pipeline/lib/plan-todos.sh +3 -3
- package/pipeline/lib/post-pr-review.sh +9 -9
- package/pipeline/lib/pr-request-location.mjs +85 -0
- package/pipeline/lib/regular-file.mjs +153 -0
- package/pipeline/lib/repo-hygiene.sh +17 -0
- package/pipeline/lib/route-state.sh +5 -1
- package/pipeline/lib/run-paths.sh +3 -2
- package/pipeline/lib/stack-detect.sh +19 -1
- package/pipeline/lib/unattended-profile-check.mjs +178 -0
- package/pipeline/lib/unattended-settings-location.mjs +28 -0
- package/pipeline/lib/unattended.mjs +76 -0
- package/pipeline/lib/unattended.sh +32 -0
- package/pipeline/lib/untrusted.mjs +76 -0
- package/pipeline/lib/user-facing.mjs +82 -0
- package/pipeline/lib/user-facing.sh +58 -0
- package/pipeline/multi-agent-refs/_dev-context.md +1 -1
- package/pipeline/multi-agent-refs/analysis/evidence.md +4 -0
- package/pipeline/multi-agent-refs/analysis/locked.md +2 -2
- package/pipeline/multi-agent-refs/analysis/render.md +4 -3
- package/pipeline/multi-agent-refs/analysis/resolve.md +3 -1
- package/pipeline/multi-agent-refs/analysis/synthesis.md +4 -5
- package/pipeline/multi-agent-refs/analysis-template.md +10 -17
- package/pipeline/multi-agent-refs/channels/jira.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +3 -3
- package/pipeline/multi-agent-refs/conventions-defaults.md +32 -32
- package/pipeline/multi-agent-refs/cross-cli-contract.md +10 -17
- package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +37 -9
- package/pipeline/multi-agent-refs/features/autopilot-operations.md +277 -0
- package/pipeline/multi-agent-refs/features/constitution.md +196 -0
- package/pipeline/multi-agent-refs/features/doctor.md +6 -3
- package/pipeline/multi-agent-refs/features/external-context-injection.md +6 -1
- package/pipeline/multi-agent-refs/features/jira-context.md +1 -1
- package/pipeline/multi-agent-refs/features/maturity-followup.md +43 -23
- package/pipeline/multi-agent-refs/features/model-fallback.md +9 -0
- package/pipeline/multi-agent-refs/features/phone-api.md +306 -0
- package/pipeline/multi-agent-refs/features/plan-critic.md +159 -0
- package/pipeline/multi-agent-refs/features/research.md +150 -0
- package/pipeline/multi-agent-refs/features/review-decision.md +185 -0
- package/pipeline/multi-agent-refs/features/scaffold.md +160 -0
- package/pipeline/multi-agent-refs/features/security-audit.md +6 -2
- package/pipeline/multi-agent-refs/features/skill-conformance.md +4 -1
- package/pipeline/multi-agent-refs/features/stack-adapters.md +116 -0
- package/pipeline/multi-agent-refs/features/unattended-gates.md +316 -0
- package/pipeline/multi-agent-refs/features/unattended-security.md +731 -0
- package/pipeline/multi-agent-refs/features/url-enrichment.md +2 -2
- package/pipeline/multi-agent-refs/features/usage-reporting.md +127 -31
- package/pipeline/multi-agent-refs/features/verify-by-test.md +1 -1
- package/pipeline/multi-agent-refs/features/visual-evidence.md +7 -7
- package/pipeline/multi-agent-refs/keychain.md +6 -11
- package/pipeline/multi-agent-refs/payload-contracts.md +2 -2
- package/pipeline/multi-agent-refs/phases/log-format.md +2 -3
- package/pipeline/multi-agent-refs/phases/modes.md +10 -12
- package/pipeline/multi-agent-refs/phases/operations.md +11 -5
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +73 -81
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +22 -22
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +42 -66
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +45 -79
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +28 -38
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +27 -25
- package/pipeline/multi-agent-refs/phases.md +1 -1
- package/pipeline/multi-agent-refs/picker-contract.md +1 -1
- package/pipeline/multi-agent-refs/progress-contract.md +13 -16
- package/pipeline/multi-agent-refs/readiness-review.md +1 -1
- package/pipeline/multi-agent-refs/research/engine.md +91 -0
- package/pipeline/multi-agent-refs/rules.md +6 -4
- package/pipeline/multi-agent-refs/setup/repo-discovery.md +2 -2
- package/pipeline/multi-agent-refs/tracker-contract.md +2 -2
- package/pipeline/multi-agent-refs/unattended-contract.md +68 -16
- package/pipeline/rules/figma-pipeline.md +12 -12
- package/pipeline/schemas/agent-state.schema.json +480 -18
- package/pipeline/schemas/analysis-spec.schema.json +4 -4
- package/pipeline/schemas/answer-request.schema.json +24 -0
- package/pipeline/schemas/answer-result.schema.json +28 -0
- package/pipeline/schemas/autopilot-config.schema.json +111 -13
- package/pipeline/schemas/command-parameters.schema.json +99 -0
- package/pipeline/schemas/constitution.schema.json +56 -0
- package/pipeline/schemas/contract-error.schema.json +52 -0
- package/pipeline/schemas/design-check-config.schema.json +5 -1
- package/pipeline/schemas/issues.schema.json +61 -0
- package/pipeline/schemas/launch-plan.schema.json +46 -0
- package/pipeline/schemas/launch-request.schema.json +45 -0
- package/pipeline/schemas/launch.json +61 -0
- package/pipeline/schemas/launch.schema.json +84 -0
- package/pipeline/schemas/phases.json +2 -2
- package/pipeline/schemas/phases.schema.json +68 -0
- package/pipeline/schemas/phone-devices.schema.json +61 -0
- package/pipeline/schemas/phone-signed-request.schema.json +67 -0
- package/pipeline/schemas/plan-critique.schema.json +99 -0
- package/pipeline/schemas/plan-todos.schema.json +7 -7
- package/pipeline/schemas/planning-output.schema.json +5 -0
- package/pipeline/schemas/pr-request.schema.json +46 -0
- package/pipeline/schemas/prefs.schema.json +82 -7
- package/pipeline/schemas/research-output.schema.json +118 -0
- package/pipeline/schemas/review-file-exclusions.schema.json +25 -0
- package/pipeline/schemas/reviewer-output.schema.json +40 -4
- package/pipeline/schemas/run-questions.json +392 -0
- package/pipeline/schemas/run-questions.schema.json +118 -0
- package/pipeline/schemas/runs-index.schema.json +189 -0
- package/pipeline/schemas/scaffold-manifest.schema.json +43 -0
- package/pipeline/schemas/secret-patterns.schema.json +28 -0
- package/pipeline/schemas/stack-adapters.json +527 -0
- package/pipeline/schemas/stack-adapters.schema.json +184 -0
- package/pipeline/schemas/token-budget.json +1 -1
- package/pipeline/schemas/token-budget.schema.json +26 -0
- package/pipeline/schemas/triage-output.schema.json +64 -3
- package/pipeline/schemas/unattended-policy.json +139 -0
- package/pipeline/schemas/unattended-policy.schema.json +73 -0
- package/pipeline/schemas/unattended-profile.json +248 -0
- package/pipeline/schemas/unattended-profile.schema.json +198 -0
- package/pipeline/schemas/worktrees.schema.json +51 -0
- package/pipeline/scripts/README.md +1 -0
- package/pipeline/scripts/_autopilot-config.mjs +130 -0
- package/pipeline/scripts/_autopilot-ops.mjs +567 -0
- package/pipeline/scripts/_autopilot-outcomes.mjs +174 -0
- package/pipeline/scripts/_command-contract.mjs +384 -0
- package/pipeline/scripts/_cost.mjs +40 -0
- package/pipeline/scripts/_notices.mjs +160 -0
- package/pipeline/scripts/_phone-auth.mjs +485 -0
- package/pipeline/scripts/_pre-existing.mjs +294 -0
- package/pipeline/scripts/_redact.mjs +77 -0
- package/pipeline/scripts/_run-paths.mjs +4 -2
- package/pipeline/scripts/_stack-adapter.mjs +678 -0
- package/pipeline/scripts/_stack-routing.mjs +1 -1
- package/pipeline/scripts/agent-guard.py +348 -37
- package/pipeline/scripts/agent-guard.sh +41 -13
- package/pipeline/scripts/analysis-story-tree.mjs +79 -3
- package/pipeline/scripts/answer-question.mjs +181 -0
- package/pipeline/scripts/audit-log-rotate.sh +1 -4
- package/pipeline/scripts/audit-log.sh +4 -4
- package/pipeline/scripts/autopilot-arming.mjs +389 -21
- package/pipeline/scripts/autopilot-awake.mjs +255 -0
- package/pipeline/scripts/autopilot-intake.mjs +137 -36
- package/pipeline/scripts/autopilot-menubar.swift +156 -44
- package/pipeline/scripts/autopilot-publish.mjs +1625 -0
- package/pipeline/scripts/autopilot-runner.mjs +1678 -222
- package/pipeline/scripts/autopilot-status.sh +198 -33
- package/pipeline/scripts/build-lock.sh +120 -0
- package/pipeline/scripts/build-references.mjs +4 -1
- package/pipeline/scripts/build-stack-plugins.mjs +59 -22
- package/pipeline/scripts/capture-flush.sh +1 -1
- package/pipeline/scripts/capture-resume.sh +13 -9
- package/pipeline/scripts/check-derived-drift.mjs +52 -11
- package/pipeline/scripts/commands.mjs +88 -0
- package/pipeline/scripts/constitution.mjs +362 -0
- package/pipeline/scripts/contract-server.mjs +776 -0
- package/pipeline/scripts/cost-analyze.mjs +89 -39
- package/pipeline/scripts/diff-explain.mjs +12 -1
- package/pipeline/scripts/doctor.mjs +77 -28
- package/pipeline/scripts/evidence-gate.mjs +192 -12
- package/pipeline/scripts/feedback-send.mjs +4 -2
- package/pipeline/scripts/gate-ledger.mjs +449 -0
- package/pipeline/scripts/gc-abandoned.sh +132 -13
- package/pipeline/scripts/gen-facts.mjs +31 -15
- package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
- package/pipeline/scripts/github-ssh-setup.sh +140 -29
- package/pipeline/scripts/graph-mermaid.mjs +4 -1
- package/pipeline/scripts/issues.mjs +236 -0
- package/pipeline/scripts/jira-attach.sh +6 -2
- package/pipeline/scripts/jira-search.sh +4 -3
- package/pipeline/scripts/keychain-save.sh +125 -24
- package/pipeline/scripts/keychain.py +63 -93
- package/pipeline/scripts/launch-request.mjs +747 -0
- package/pipeline/scripts/localize-commands.mjs +4 -10
- package/pipeline/scripts/log-metric.sh +6 -5
- package/pipeline/scripts/maturity-followup.mjs +13 -4
- package/pipeline/scripts/memory-save.sh +25 -0
- package/pipeline/scripts/migrate-prefs.mjs +4 -3
- package/pipeline/scripts/open-questions-gate.mjs +276 -0
- package/pipeline/scripts/phase-tracker.sh +41 -27
- package/pipeline/scripts/phase0-exit-gate.mjs +22 -4
- package/pipeline/scripts/phone-devices.mjs +224 -0
- package/pipeline/scripts/plan-coverage-gate.mjs +200 -66
- package/pipeline/scripts/plan-critique-gate.mjs +591 -0
- package/pipeline/scripts/pr-request.mjs +188 -0
- package/pipeline/scripts/pre-commit-check.sh +115 -4
- package/pipeline/scripts/probe-evidence-capability.sh +44 -5
- package/pipeline/scripts/record-phase.mjs +71 -0
- package/pipeline/scripts/render-agent-log-cost.sh +17 -2
- package/pipeline/scripts/render-cost-summary.sh +1 -1
- package/pipeline/scripts/render-work-summary.sh +1 -1
- package/pipeline/scripts/require-supported-version.sh +4 -1
- package/pipeline/scripts/research-gate.mjs +704 -0
- package/pipeline/scripts/review-decision-gate.mjs +403 -0
- package/pipeline/scripts/routine-registry.mjs +5 -2
- package/pipeline/scripts/runs-index.mjs +135 -27
- package/pipeline/scripts/scaffold-gate.mjs +393 -0
- package/pipeline/scripts/skill-conformance.mjs +25 -8
- package/pipeline/scripts/skill-siblings.mjs +2 -1
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +32 -6
- package/pipeline/scripts/smoke-schema-validation.sh +6 -2
- package/pipeline/scripts/spec-consistency-gate.mjs +469 -0
- package/pipeline/scripts/symbol-existence-gate.mjs +450 -0
- package/pipeline/scripts/test-gap-scan.mjs +40 -2
- package/pipeline/scripts/test-integrity-gate.mjs +20 -4
- package/pipeline/scripts/test-strength.mjs +484 -0
- package/pipeline/scripts/test-summary.mjs +651 -0
- package/pipeline/scripts/triage-memory.mjs +49 -9
- package/pipeline/scripts/unattended_policy.py +2786 -0
- package/pipeline/scripts/uninstall.mjs +10 -10
- package/pipeline/scripts/update-issue-progress.sh +1 -1
- package/pipeline/scripts/usage-identity.mjs +288 -0
- package/pipeline/scripts/usage-register.mjs +185 -63
- package/pipeline/scripts/usage-report.mjs +230 -66
- package/pipeline/scripts/validate-complaint-doc.mjs +28 -10
- package/pipeline/scripts/validate-planning.mjs +6 -0
- package/pipeline/scripts/verify-citations.mjs +151 -38
- package/pipeline/scripts/verify.mjs +58 -18
- package/pipeline/scripts/worktree-prepare.sh +126 -0
- package/pipeline/scripts/worktrees.mjs +124 -0
- package/pipeline/scripts/write-state.mjs +48 -17
- package/pipeline/skills/.skill-manifest.json +222 -226
- package/pipeline/skills/.skills-index.json +77 -88
- package/pipeline/skills/shared/README.md +44 -45
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +3 -2
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +10 -9
- package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +6 -5
- package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +11 -3
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +4 -3
- package/pipeline/skills/shared/core/multi-agent-autopilot-off/SKILL.md +32 -6
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +66 -8
- package/pipeline/skills/shared/core/multi-agent-autopilot-status/SKILL.md +27 -9
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +7 -4
- package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +7 -5
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-doctor/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-feedback/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +7 -4
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-research/SKILL.md +35 -0
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +7 -3
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +7 -5
- package/pipeline/skills/shared/core/multi-agent-review-analysis/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-scaffold/SKILL.md +30 -0
- package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +4 -3
- package/pipeline/skills/shared/core/multi-agent-security-review/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +60 -0
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +8 -8
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +7 -8
- package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +10 -10
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-test-accessibility/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-test-dark-mode/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-test-dynamic-type/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-test-screenshots/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +2 -1
- package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +3 -2
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +2 -1
- package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +46 -0
- package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +9 -3
- package/pipeline/skills/shared/external/NOTICE-paul-hudson-skills.md +54 -0
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +9 -11
- package/pipeline/skills/shared/external/NOTICE-vibeship-spawner-skills.md +203 -0
- package/pipeline/skills/shared/external/NOTICE-xcode-build-skills.md +10 -3
- package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +131 -25
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -3
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +2 -1
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +85 -76
- package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +51 -3
- package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +9 -0
- package/pipeline/skills/shared/external/android-datastore/SKILL.md +4 -3
- package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +4 -3
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +106 -181
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +147 -0
- package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +45 -4
- package/pipeline/skills/shared/external/android-performance/SKILL.md +27 -2
- package/pipeline/skills/shared/external/android-performance/references/patterns.md +37 -37
- package/pipeline/skills/shared/external/api-patterns/SKILL.md +111 -67
- package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +128 -0
- package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +136 -186
- package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +140 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +207 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/operations.md +66 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +175 -0
- package/pipeline/skills/shared/external/app-clips/SKILL.md +2 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +2 -0
- package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +4 -3
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +2 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +2 -0
- package/pipeline/skills/shared/external/architecture/SKILL.md +106 -39
- package/pipeline/skills/shared/external/architecture/references/adr-and-review.md +76 -0
- package/pipeline/skills/shared/external/authentication/SKILL.md +2 -0
- package/pipeline/skills/shared/external/avkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/background-processing/SKILL.md +2 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +3 -2
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +2 -0
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +3 -2
- package/pipeline/skills/shared/external/clean-code/SKILL.md +192 -90
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +2 -1
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/compose-components/SKILL.md +4 -4
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +22 -22
- package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +10 -10
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +13 -6
- package/pipeline/skills/shared/external/compose-testing/references/patterns.md +59 -59
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +2 -0
- package/pipeline/skills/shared/external/context-compression/SKILL.md +118 -250
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +2 -0
- package/pipeline/skills/shared/external/core-data/SKILL.md +2 -0
- package/pipeline/skills/shared/external/core-motion/SKILL.md +2 -0
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +2 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +2 -1
- package/pipeline/skills/shared/external/council/SKILL.md +2 -1
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +3 -2
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +3 -2
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +2 -0
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +130 -20
- package/pipeline/skills/shared/external/debugging-strategies/references/hard-cases.md +54 -0
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +2 -0
- package/pipeline/skills/shared/external/docker-expert/SKILL.md +137 -381
- package/pipeline/skills/shared/external/docker-expert/references/patterns.md +98 -0
- package/pipeline/skills/shared/external/energykit/SKILL.md +2 -1
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +3 -1
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +116 -185
- package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +235 -0
- package/pipeline/skills/shared/external/fastapi-pro/references/testing-and-deployment.md +69 -0
- package/pipeline/skills/shared/external/firebase/SKILL.md +4 -3
- package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +151 -293
- package/pipeline/skills/shared/external/github-actions-templates/references/patterns.md +96 -0
- package/pipeline/skills/shared/external/healthkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/hig-components-content/SKILL.md +69 -72
- package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +111 -0
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +77 -86
- package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +98 -0
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +150 -78
- package/pipeline/skills/shared/external/hig-components-system/SKILL.md +76 -97
- package/pipeline/skills/shared/external/hig-components-system/references/surfaces.md +98 -0
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +48 -76
- package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +132 -0
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +147 -106
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +61 -77
- package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +92 -0
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +147 -77
- package/pipeline/skills/shared/external/hig-technologies/SKILL.md +61 -121
- package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +108 -0
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +2 -0
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +3 -2
- package/pipeline/skills/shared/external/humanizer/SKILL.md +2 -1
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +2 -0
- package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +2 -1
- package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +14 -2
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
- package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +4 -3
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +8 -6
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +11 -11
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +2 -1
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +2 -0
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +2 -0
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +102 -193
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +103 -0
- package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -2
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +7 -9
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +3 -3
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +2 -2
- package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +4 -3
- package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +5 -3
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +2 -0
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +2 -0
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +149 -46
- package/pipeline/skills/shared/external/monorepo-architect/references/patterns.md +70 -0
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +2 -0
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/natural-language/SKILL.md +2 -0
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +3 -2
- package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +91 -21
- package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +247 -0
- package/pipeline/skills/shared/external/nodejs-backend-patterns/references/testing-and-frameworks.md +57 -0
- package/pipeline/skills/shared/external/observability-engineer/SKILL.md +155 -232
- package/pipeline/skills/shared/external/observability-engineer/references/patterns.md +75 -0
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +2 -0
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +18 -18
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +2 -1
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +2 -1
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +2 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +3 -2
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +3 -2
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +2 -2
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +3 -2
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +7 -7
- package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +69 -69
- package/pipeline/skills/shared/external/room-database/references/patterns.md +126 -126
- package/pipeline/skills/shared/external/search-first/SKILL.md +2 -1
- package/pipeline/skills/shared/external/security-review/SKILL.md +4 -3
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +2 -2
- package/pipeline/skills/shared/external/signal-community/SKILL.md +1 -1
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +2 -1
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +2 -1
- package/pipeline/skills/shared/external/spm-build-analysis/SKILL.md +2 -0
- package/pipeline/skills/shared/external/storekit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +5 -4
- package/pipeline/skills/shared/external/swift-concurrency-pro/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swift-security/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +10 -0
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftdata-pro/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +3 -1
- package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +5 -4
- package/pipeline/skills/shared/external/swiftui-pro/SKILL.md +2 -1
- package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +5 -4
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +2 -0
- package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +4 -3
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +3 -2
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +3 -2
- package/pipeline/skills/shared/external/tipkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +3 -2
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +3 -2
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +3 -2
- package/pipeline/skills/shared/external/web-performance/SKILL.md +3 -2
- package/pipeline/skills/shared/external/web-testing/SKILL.md +3 -2
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +2 -0
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +1 -1
- package/pipeline/skills/shared/external/xcode-build-benchmark/SKILL.md +2 -0
- package/pipeline/skills/shared/external/xcode-build-fixer/SKILL.md +2 -0
- package/pipeline/skills/shared/external/xcode-build-orchestrator/SKILL.md +2 -0
- package/pipeline/skills/shared/external/xcode-compilation-analyzer/SKILL.md +2 -0
- package/pipeline/skills/shared/external/xcode-project-analyzer/SKILL.md +2 -0
- package/pipeline/skills/skills-index.md +40 -41
- package/docs/token-budget-history.md +0 -24
- package/pipeline/skills/shared/external/agentflow/SKILL.md +0 -199
- package/pipeline/skills/shared/external/android-ui-verification/SKILL.md +0 -66
- package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +0 -299
- package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +0 -255
- package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +0 -167
- package/pipeline/skills/shared/external/closed-loop-delivery/SKILL.md +0 -116
- package/pipeline/skills/shared/external/ios-developer/SKILL.md +0 -216
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
#
|
|
3
3
|
# fetch-fortify.sh
|
|
4
4
|
# Query a Fortify SSC instance for a project version's open findings and
|
|
5
|
-
# emit a normalized JSON view that Phase
|
|
5
|
+
# emit a normalized JSON view that Phase 3 (Review) consumes as a
|
|
6
6
|
# deterministic security gate.
|
|
7
7
|
#
|
|
8
8
|
# Three input forms:
|
|
@@ -125,8 +125,8 @@ fi
|
|
|
125
125
|
|
|
126
126
|
# Keychain write through credential-store.sh with the `-` form, which reads the secret
|
|
127
127
|
# from stdin: the value never lands on any argv (visible to ps), which is the property
|
|
128
|
-
#
|
|
129
|
-
#
|
|
128
|
+
# a direct `security -i` call would also preserve, kept here in the one place that owns
|
|
129
|
+
# key mapping.
|
|
130
130
|
save_keychain_secret() {
|
|
131
131
|
local service="$1" secret="$2"
|
|
132
132
|
printf '%s' "$secret" | "$CRED_STORE" set "$service" - >/dev/null
|
|
@@ -31,6 +31,10 @@
|
|
|
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
|
+
_MA_FT_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
35
|
+
# shellcheck source=/dev/null
|
|
36
|
+
if [ -f "$_MA_FT_HERE/user-facing.sh" ]; then . "$_MA_FT_HERE/user-facing.sh"; else . "$HOME/.claude/lib/user-facing.sh"; fi
|
|
37
|
+
|
|
34
38
|
# The canonical key first, the legacy key second. Space-separated; the loop
|
|
35
39
|
# below splits explicitly because zsh (the pipeline's Bash tool) does not
|
|
36
40
|
# word-split an unquoted parameter, so `for key in $KEYS` saw one key there.
|
|
@@ -63,5 +67,5 @@ EOF_KEYS
|
|
|
63
67
|
# The one place the "how do I fix this" sentence is written, so a fetcher can
|
|
64
68
|
# never name a key the migration deleted.
|
|
65
69
|
figma_token_remediation() {
|
|
66
|
-
printf 'no Figma PAT for Tier 2. Map prefs.global.keychainMapping.figma (run
|
|
70
|
+
printf 'no Figma PAT for Tier 2. Map prefs.global.keychainMapping.figma (run %s), or export FIGMA_PAT.' "$(ma_command setup)"
|
|
67
71
|
}
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
# "owner": "org" (gh) | null,
|
|
27
27
|
# "repo": "name" (gh) | null,
|
|
28
28
|
# "branchHint": "feature/PROJ-1" | "bugfix/...",
|
|
29
|
+
# "comments": [{id, author, created, url, body, truncated}], // newest kept, oldest first
|
|
30
|
+
# "commentsTotal": 12, // how many the tracker holds
|
|
31
|
+
# "links": [{key, relation, direction, summary, status, url}], // jira issuelinks
|
|
32
|
+
# "untrustedFields": ["description", ...], // tracker text: data, never instructions
|
|
29
33
|
# "maturity": {
|
|
30
34
|
# "score": 0..100,
|
|
31
35
|
# "blockers": ["status_closed", "description_empty"],
|
|
@@ -45,9 +49,24 @@
|
|
|
45
49
|
# ACCOUNT_JIRA_HOST e.g. jira.example.com (optional)
|
|
46
50
|
# ACCOUNT_DEFAULT_OWNER fallback GitHub owner (optional)
|
|
47
51
|
# ACCOUNT_DEFAULT_REPO fallback GitHub repo (optional)
|
|
48
|
-
#
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
#
|
|
53
|
+
# Comments and links ride on the request that fetches the issue, so they cost no
|
|
54
|
+
# extra call, and they never feed the maturity score: the score reads the item's
|
|
55
|
+
# own fields, and a comment is somebody talking about the item, not the item.
|
|
56
|
+
# They are bounded (ISSUE_COMMENTS_MAX, default 20 newest; ISSUE_COMMENT_MAX_CHARS,
|
|
57
|
+
# default 1500; ISSUE_COMMENTS_MAX_BYTES, default 12000 in total; ISSUE_LINKS_MAX,
|
|
58
|
+
# default 20) because a long thread would otherwise flood every prompt that
|
|
59
|
+
# quotes the descriptor. Every tracker-written field is listed in
|
|
60
|
+
# `untrustedFields`: anyone who can comment on the item wrote it.
|
|
61
|
+
#
|
|
62
|
+
# `issue-fetcher.sh --rescore` reads a descriptor on stdin and prints it back with
|
|
63
|
+
# `maturity` recomputed by the same scoring below - the one scorer, reused by
|
|
64
|
+
# research-gate.mjs to re-run the check on a descriptor that carries verified
|
|
65
|
+
# research findings.
|
|
66
|
+
#
|
|
67
|
+
# `maturity.summary` is written for the person at the picker, so it renders in
|
|
68
|
+
# `prefs.global.outputLanguage`; every other field is machine-readable and
|
|
69
|
+
# stays English.
|
|
51
70
|
|
|
52
71
|
# Sourcing this file hands a caller the provider helpers - `jira_search`,
|
|
53
72
|
# `fetch_jira`, `fetch_github` and the `jira_curl` underneath them - without
|
|
@@ -61,6 +80,11 @@ set -euo pipefail
|
|
|
61
80
|
MA_IF_EXECUTED=0
|
|
62
81
|
[ "${BASH_SOURCE[0]}" = "$0" ] && MA_IF_EXECUTED=1
|
|
63
82
|
|
|
83
|
+
# Strict mode belongs to the executed resolver. A sourcing caller keeps its own
|
|
84
|
+
# error handling: jira-search.sh runs without errexit on purpose so that a
|
|
85
|
+
# failed query reaches its own warning and exit code.
|
|
86
|
+
if [ "$MA_IF_EXECUTED" = 1 ]; then set -euo pipefail; fi
|
|
87
|
+
|
|
64
88
|
INPUT="${1:-}"
|
|
65
89
|
if [ "$MA_IF_EXECUTED" = 1 ] && [ -z "$INPUT" ]; then
|
|
66
90
|
echo '{"error":"input required"}'
|
|
@@ -71,7 +95,11 @@ JIRA_TOKEN_KEY="${ACCOUNT_JIRA_TOKEN_KEY:-}"
|
|
|
71
95
|
JIRA_HOST="${ACCOUNT_JIRA_HOST:-}"
|
|
72
96
|
DEFAULT_OWNER="${ACCOUNT_DEFAULT_OWNER:-}"
|
|
73
97
|
DEFAULT_REPO="${ACCOUNT_DEFAULT_REPO:-}"
|
|
74
|
-
|
|
98
|
+
|
|
99
|
+
_MA_IF_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
100
|
+
# shellcheck source=/dev/null
|
|
101
|
+
if [ -f "$_MA_IF_HERE/user-facing.sh" ]; then . "$_MA_IF_HERE/user-facing.sh"; elif [ -f "$HOME/.claude/lib/user-facing.sh" ]; then . "$HOME/.claude/lib/user-facing.sh"; fi
|
|
102
|
+
if command -v ma_output_lang >/dev/null 2>&1; then SUMMARY_LANG=$(ma_output_lang); else SUMMARY_LANG=en; fi
|
|
75
103
|
|
|
76
104
|
# --- Detect input kind --------------------------------------------------------
|
|
77
105
|
detect_kind() {
|
|
@@ -105,6 +133,7 @@ detect_kind() {
|
|
|
105
133
|
}
|
|
106
134
|
|
|
107
135
|
KIND=$(detect_kind "$INPUT")
|
|
136
|
+
[ "$INPUT" = "--rescore" ] && KIND=rescore
|
|
108
137
|
|
|
109
138
|
# --- Helpers ------------------------------------------------------------------
|
|
110
139
|
slugify() {
|
|
@@ -134,6 +163,41 @@ branch_for() {
|
|
|
134
163
|
|
|
135
164
|
# --- Provider fetchers (return raw JSON or empty) -----------------------------
|
|
136
165
|
|
|
166
|
+
# Shared by both provider parses: the newest comments, oldest first, inside a
|
|
167
|
+
# count, a per-comment and a total byte budget. Prepended to each parse script
|
|
168
|
+
# so the bound is one piece of code, not two.
|
|
169
|
+
_PY_BOUND_COMMENTS='
|
|
170
|
+
import os
|
|
171
|
+
def _env_int(name, default):
|
|
172
|
+
v = os.environ.get(name)
|
|
173
|
+
try:
|
|
174
|
+
return int(v) if v not in (None, "") else default
|
|
175
|
+
except ValueError:
|
|
176
|
+
return default
|
|
177
|
+
def bound_comments(rows):
|
|
178
|
+
cap = _env_int("ISSUE_COMMENTS_MAX", 20)
|
|
179
|
+
chars = _env_int("ISSUE_COMMENT_MAX_CHARS", 1500)
|
|
180
|
+
budget = _env_int("ISSUE_COMMENTS_MAX_BYTES", 12000)
|
|
181
|
+
kept, used = [], 0
|
|
182
|
+
for r in reversed(rows):
|
|
183
|
+
if len(kept) >= cap:
|
|
184
|
+
break
|
|
185
|
+
body = (r.get("body") or "").replace("\x1f", " ")
|
|
186
|
+
truncated = False
|
|
187
|
+
if chars > 0 and len(body) > chars:
|
|
188
|
+
body, truncated = body[:chars] + "...", True
|
|
189
|
+
size = len(body.encode("utf-8"))
|
|
190
|
+
if budget > 0 and used + size > budget:
|
|
191
|
+
if kept:
|
|
192
|
+
break
|
|
193
|
+
body = body.encode("utf-8")[:max(budget - 3, 0)].decode("utf-8", "ignore") + "..."
|
|
194
|
+
truncated, size = True, len(body.encode("utf-8"))
|
|
195
|
+
kept.append(dict(r, body=body, truncated=truncated))
|
|
196
|
+
used += size
|
|
197
|
+
kept.reverse()
|
|
198
|
+
return kept
|
|
199
|
+
'
|
|
200
|
+
|
|
137
201
|
# Bearer-auth via a curl config fed through process substitution so the
|
|
138
202
|
# token never appears in argv (argv is visible to `ps` / process audit).
|
|
139
203
|
jira_auth_cfg() { printf 'header = "Authorization: Bearer %s"\n' "$1"; }
|
|
@@ -178,7 +242,7 @@ jira_curl() {
|
|
|
178
242
|
echo "ERR: Jira token not found in credential store ($JIRA_TOKEN_KEY)" >&2
|
|
179
243
|
return 1
|
|
180
244
|
fi
|
|
181
|
-
curl -sf -K <(jira_auth_cfg "$token") "https://$JIRA_HOST$path"
|
|
245
|
+
curl -sf -m 30 -K <(jira_auth_cfg "$token") "https://$JIRA_HOST$path"
|
|
182
246
|
}
|
|
183
247
|
|
|
184
248
|
# The field list is a parameter because the related-issue lookup wants a
|
|
@@ -207,7 +271,7 @@ fetch_github() {
|
|
|
207
271
|
return 1
|
|
208
272
|
fi
|
|
209
273
|
gh issue view "$num" --repo "$owner/$repo" \
|
|
210
|
-
--json number,title,labels,state,url,body 2>/dev/null \
|
|
274
|
+
--json number,title,labels,state,url,body,comments 2>/dev/null \
|
|
211
275
|
|| { echo "ERR: GitHub fetch failed for $owner/$repo#$num" >&2; return 1; }
|
|
212
276
|
}
|
|
213
277
|
|
|
@@ -217,7 +281,7 @@ fetch_github() {
|
|
|
217
281
|
|
|
218
282
|
# Build the descriptor JSON, then attach maturity. Pythonized so escaping stays sane.
|
|
219
283
|
emit_descriptor() {
|
|
220
|
-
python3 - "$
|
|
284
|
+
python3 - "$SUMMARY_LANG" "$@" <<'PY'
|
|
221
285
|
import json, sys, re
|
|
222
286
|
|
|
223
287
|
lang = sys.argv[1]
|
|
@@ -252,6 +316,21 @@ except Exception:
|
|
|
252
316
|
if not isinstance(related, list):
|
|
253
317
|
related = []
|
|
254
318
|
|
|
319
|
+
def _json_field(name, want):
|
|
320
|
+
try:
|
|
321
|
+
v = json.loads(fields.get(name) or "null")
|
|
322
|
+
except Exception:
|
|
323
|
+
v = None
|
|
324
|
+
return v if isinstance(v, want) else None
|
|
325
|
+
|
|
326
|
+
cblob = _json_field("comments", dict) or {}
|
|
327
|
+
comments = cblob.get("items") if isinstance(cblob.get("items"), list) else []
|
|
328
|
+
try:
|
|
329
|
+
comments_total = int(cblob.get("total") or len(comments))
|
|
330
|
+
except (TypeError, ValueError):
|
|
331
|
+
comments_total = len(comments)
|
|
332
|
+
links = _json_field("links", list) or []
|
|
333
|
+
|
|
255
334
|
descriptor = {
|
|
256
335
|
"kind": fields.get("kind"),
|
|
257
336
|
"key": fields.get("key") or None,
|
|
@@ -271,6 +350,10 @@ descriptor = {
|
|
|
271
350
|
"repo": fields.get("repo") or None,
|
|
272
351
|
"branchHint": fields.get("branchHint") or None,
|
|
273
352
|
"relatedIssues": related,
|
|
353
|
+
"comments": comments,
|
|
354
|
+
"commentsTotal": comments_total,
|
|
355
|
+
"links": links,
|
|
356
|
+
"untrustedFields": ["title", "description", "parentDescription", "relatedIssues", "comments", "links"],
|
|
274
357
|
}
|
|
275
358
|
extra = {}
|
|
276
359
|
if fields.get("needsRepoPicker"):
|
|
@@ -380,17 +463,21 @@ if [ "$MA_IF_EXECUTED" = 1 ]; then
|
|
|
380
463
|
case "$KIND" in
|
|
381
464
|
jira-id)
|
|
382
465
|
KEY="$INPUT"
|
|
383
|
-
raw=$(fetch_jira "$KEY") || raw=""
|
|
466
|
+
raw=$(fetch_jira "$KEY" "summary,status,issuetype,description,priority,resolution,fixVersions,parent,comment,issuelinks") || raw=""
|
|
467
|
+
commentsjson='{"items":[],"total":0}'; linksjson="[]"
|
|
384
468
|
if [ -n "$raw" ]; then
|
|
385
469
|
# Single python3 pass - emit all fields joined by U+001F (Unit Separator)
|
|
386
470
|
# so we avoid spawning 7 interpreter startups per Jira fetch
|
|
387
471
|
# (~200-300ms each). The separator is non-whitespace, so empty fields
|
|
388
472
|
# (e.g. unresolved issues) are preserved by `read` instead of collapsed.
|
|
389
473
|
# `read -d ''` lets descriptions with embedded newlines pass through.
|
|
390
|
-
parsed=$(printf '%s' "$raw" | python3 -c '
|
|
391
|
-
import json, sys
|
|
474
|
+
parsed=$(printf '%s' "$raw" | SELF_KEY="$KEY" JIRA_HOST="$JIRA_HOST" python3 -c "$_PY_BOUND_COMMENTS"'
|
|
475
|
+
import json, re, sys
|
|
392
476
|
d = json.load(sys.stdin)
|
|
393
477
|
f = d.get("fields", {}) or {}
|
|
478
|
+
host = os.environ.get("JIRA_HOST", "")
|
|
479
|
+
self_key = os.environ.get("SELF_KEY", "")
|
|
480
|
+
key_re = re.compile(r"^[A-Z][A-Z0-9]*-[0-9]+$")
|
|
394
481
|
def _name(x):
|
|
395
482
|
return (x or {}).get("name", "") or ""
|
|
396
483
|
title = f.get("summary", "") or ""
|
|
@@ -401,11 +488,47 @@ priority = _name(f.get("priority"))
|
|
|
401
488
|
resolution = _name(f.get("resolution"))
|
|
402
489
|
fixversions = ",".join((v or {}).get("name", "") for v in (f.get("fixVersions") or []))
|
|
403
490
|
parentkey = (f.get("parent") or {}).get("key", "") or ""
|
|
491
|
+
cblock = f.get("comment") or {}
|
|
492
|
+
crows = []
|
|
493
|
+
for c in cblock.get("comments") or []:
|
|
494
|
+
if not isinstance(c, dict):
|
|
495
|
+
continue
|
|
496
|
+
cid = str(c.get("id") or "")
|
|
497
|
+
crows.append({
|
|
498
|
+
"id": cid,
|
|
499
|
+
"author": ((c.get("author") or {}).get("displayName") or ""),
|
|
500
|
+
"created": c.get("created") or "",
|
|
501
|
+
"url": "https://%s/browse/%s?focusedCommentId=%s" % (host, self_key, cid) if host and cid else "",
|
|
502
|
+
"body": c.get("body") or "",
|
|
503
|
+
})
|
|
504
|
+
comments = {"items": bound_comments(crows), "total": int(cblock.get("total") or len(crows))}
|
|
505
|
+
links = []
|
|
506
|
+
for l in f.get("issuelinks") or []:
|
|
507
|
+
if not isinstance(l, dict):
|
|
508
|
+
continue
|
|
509
|
+
t = l.get("type") or {}
|
|
510
|
+
other, direction = (l.get("outwardIssue"), "outward") if l.get("outwardIssue") else (l.get("inwardIssue"), "inward")
|
|
511
|
+
other = other or {}
|
|
512
|
+
lkey = other.get("key") or ""
|
|
513
|
+
if not key_re.match(lkey):
|
|
514
|
+
continue
|
|
515
|
+
of = other.get("fields") or {}
|
|
516
|
+
links.append({
|
|
517
|
+
"key": lkey,
|
|
518
|
+
"relation": (t.get(direction) or t.get("name") or ""),
|
|
519
|
+
"direction": direction,
|
|
520
|
+
"summary": of.get("summary") or "",
|
|
521
|
+
"status": _name(of.get("status")),
|
|
522
|
+
"url": "https://%s/browse/%s" % (host, lkey) if host else "",
|
|
523
|
+
})
|
|
524
|
+
links = links[:max(_env_int("ISSUE_LINKS_MAX", 20), 0)]
|
|
404
525
|
parts = [title, itype, status, description, priority, resolution, fixversions, parentkey]
|
|
405
526
|
parts = [v.replace("\x1f", " ") for v in parts]
|
|
527
|
+
parts += [json.dumps(comments, ensure_ascii=False), json.dumps(links, ensure_ascii=False)]
|
|
406
528
|
sys.stdout.write("\x1f".join(parts))
|
|
407
529
|
')
|
|
408
|
-
IFS=$'\x1f' read -r -d '' title itype status description priority resolution fixversions parentkey <<< "$parsed" || true
|
|
530
|
+
IFS=$'\x1f' read -r -d '' title itype status description priority resolution fixversions parentkey commentsjson linksjson <<< "$parsed" || true
|
|
531
|
+
linksjson="${linksjson%$'\n'}"
|
|
409
532
|
# `<<<` appends a trailing newline to its input; `read -d ''` has no
|
|
410
533
|
# delimiter to stop on, so that newline folds into whichever field
|
|
411
534
|
# comes last - now parentkey. Strip it, or a genuinely-empty parent
|
|
@@ -534,12 +657,49 @@ sys.stdout.write(json.dumps(out[:cap], ensure_ascii=False))
|
|
|
534
657
|
"url=https://$JIRA_HOST/browse/$KEY" "branchHint=$branch" \
|
|
535
658
|
"priority=$priority" "resolution=$resolution" "fixVersions=$fixversions" \
|
|
536
659
|
"parentKey=$parentkey" "parentDescription=$parentdesc" \
|
|
537
|
-
"relatedIssues=$relatedjson"
|
|
660
|
+
"relatedIssues=$relatedjson" "comments=$commentsjson" "links=$linksjson"
|
|
538
661
|
;;
|
|
539
662
|
|
|
540
663
|
jira-url)
|
|
541
664
|
HOST=$(printf '%s' "$INPUT" | sed -E 's#https?://##; s#/.*##')
|
|
542
|
-
KEY=$(printf '%s' "$INPUT" | sed -E 's#.*/browse/##; s#[
|
|
665
|
+
KEY=$(printf '%s' "$INPUT" | sed -E 's#.*/browse/##; s#[/?#].*##')
|
|
666
|
+
# Re-dispatch only a real key. Anything else would fall through
|
|
667
|
+
# detect_kind to the freetext branch and start a run titled with a URL.
|
|
668
|
+
if ! [[ "$KEY" =~ ^[A-Z][A-Z0-9]*-[0-9]+$ ]]; then
|
|
669
|
+
echo '{"error":"no Jira issue key in URL"}'
|
|
670
|
+
exit 2
|
|
671
|
+
fi
|
|
672
|
+
# The link's host receives the Jira token on the re-dispatch, and a link is
|
|
673
|
+
# ticket or request text. HOST is the exact string curl will see, so it must
|
|
674
|
+
# be a bare host[:port] and name the configured Jira host; without one, only
|
|
675
|
+
# Atlassian Cloud, where the host is the tenant.
|
|
676
|
+
if ! [[ "$HOST" =~ ^[A-Za-z0-9.-]+(:[0-9]{1,5})?$ ]]; then
|
|
677
|
+
echo '{"error":"host-malformed"}'
|
|
678
|
+
exit 2
|
|
679
|
+
fi
|
|
680
|
+
_allowed="${JIRA_HOST:-}"
|
|
681
|
+
if [ -z "$_allowed" ]; then
|
|
682
|
+
_prefs="${MA_PREFS_FILE:-$HOME/.claude/multi-agent-preferences.json}"
|
|
683
|
+
_allowed=$(python3 - "$_prefs" <<'PY' 2>/dev/null || true
|
|
684
|
+
import json, sys
|
|
685
|
+
try:
|
|
686
|
+
print((json.load(open(sys.argv[1])).get("global", {}).get("hosts", {}) or {}).get("jira") or "")
|
|
687
|
+
except Exception:
|
|
688
|
+
print("")
|
|
689
|
+
PY
|
|
690
|
+
)
|
|
691
|
+
fi
|
|
692
|
+
_allowed=$(printf '%s' "$_allowed" | tr '[:upper:]' '[:lower:]' | sed -E 's#^https?://##; s#/.*##; s#:.*##; s#\.$##')
|
|
693
|
+
_host=$(printf '%s' "$HOST" | tr '[:upper:]' '[:lower:]' | sed -E 's#:.*##; s#\.$##')
|
|
694
|
+
if [ -n "$_allowed" ]; then
|
|
695
|
+
[ "$_host" = "$_allowed" ] || { echo '{"error":"host-not-allowed"}'; exit 2; }
|
|
696
|
+
else
|
|
697
|
+
case "$_host" in
|
|
698
|
+
*.atlassian.net) ;;
|
|
699
|
+
*) echo '{"error":"host-not-configured"}'; exit 2 ;;
|
|
700
|
+
esac
|
|
701
|
+
fi
|
|
702
|
+
unset _allowed _prefs _host
|
|
543
703
|
ACCOUNT_JIRA_HOST="$HOST" JIRA_HOST="$HOST" exec "$0" "$KEY"
|
|
544
704
|
;;
|
|
545
705
|
|
|
@@ -548,21 +708,38 @@ sys.stdout.write(json.dumps(out[:cap], ensure_ascii=False))
|
|
|
548
708
|
REPO=$(printf '%s' "$INPUT" | sed -E 's#https?://github.com/[^/]+/##; s#/.*##')
|
|
549
709
|
NUM=$(printf '%s' "$INPUT" | sed -E 's#.*/issues/##; s#[/?#].*##')
|
|
550
710
|
raw=$(fetch_github "$OWNER" "$REPO" "$NUM") || raw=""
|
|
711
|
+
commentsjson='{"items":[],"total":0}'
|
|
551
712
|
if [ -n "$raw" ]; then
|
|
552
713
|
# Single python3 pass - see jira-id branch for rationale on the
|
|
553
714
|
# U+001F separator + `read -d ''` combo.
|
|
554
|
-
parsed=$(printf '%s' "$raw" | python3 -c '
|
|
715
|
+
parsed=$(printf '%s' "$raw" | python3 -c "$_PY_BOUND_COMMENTS"'
|
|
555
716
|
import json, sys
|
|
556
717
|
d = json.load(sys.stdin)
|
|
557
718
|
title = d.get("title", "") or ""
|
|
558
719
|
labels = ",".join((l or {}).get("name", "") for l in (d.get("labels") or []))
|
|
559
720
|
status = d.get("state", "open") or "open"
|
|
560
721
|
description = d.get("body", "") or ""
|
|
722
|
+
crows = []
|
|
723
|
+
for c in d.get("comments") or []:
|
|
724
|
+
if not isinstance(c, dict):
|
|
725
|
+
continue
|
|
726
|
+
crows.append({
|
|
727
|
+
"id": str(c.get("id") or ""),
|
|
728
|
+
"author": ((c.get("author") or {}).get("login") or ""),
|
|
729
|
+
"created": c.get("createdAt") or "",
|
|
730
|
+
"url": c.get("url") or "",
|
|
731
|
+
"body": c.get("body") or "",
|
|
732
|
+
})
|
|
733
|
+
comments = {"items": bound_comments(crows), "total": len(crows)}
|
|
561
734
|
parts = [title, labels, status, description]
|
|
562
735
|
parts = [v.replace("\x1f", " ") for v in parts]
|
|
736
|
+
parts.append(json.dumps(comments, ensure_ascii=False))
|
|
563
737
|
sys.stdout.write("\x1f".join(parts))
|
|
564
738
|
')
|
|
565
|
-
IFS=$'\x1f' read -r -d '' title labels status description <<< "$parsed" || true
|
|
739
|
+
IFS=$'\x1f' read -r -d '' title labels status description commentsjson <<< "$parsed" || true
|
|
740
|
+
# Same `<<<` trailing newline as the jira-id branch; here the comments
|
|
741
|
+
# JSON is the last field and would carry it.
|
|
742
|
+
commentsjson="${commentsjson%$'\n'}"
|
|
566
743
|
else
|
|
567
744
|
title="(unfetched)"; labels=""; status="open"; description=""
|
|
568
745
|
fi
|
|
@@ -572,7 +749,7 @@ sys.stdout.write("\x1f".join(parts))
|
|
|
572
749
|
"kind=github" "key=$NUM" "title=$title" "type=$itype" "status=$status" \
|
|
573
750
|
"description=$description" "host=github.com" \
|
|
574
751
|
"url=https://github.com/$OWNER/$REPO/issues/$NUM" \
|
|
575
|
-
"owner=$OWNER" "repo=$REPO" "branchHint=$branch"
|
|
752
|
+
"owner=$OWNER" "repo=$REPO" "branchHint=$branch" "comments=$commentsjson"
|
|
576
753
|
;;
|
|
577
754
|
|
|
578
755
|
gh-short)
|
|
@@ -600,6 +777,46 @@ sys.stdout.write("\x1f".join(parts))
|
|
|
600
777
|
exec "$0" "https://github.com/$OWNER/$REPO/issues/$NUM"
|
|
601
778
|
;;
|
|
602
779
|
|
|
780
|
+
rescore)
|
|
781
|
+
# The descriptor's own fields, handed back to the one scorer. Read in full
|
|
782
|
+
# first: the argument list is built by a second process.
|
|
783
|
+
desc_in=$(cat)
|
|
784
|
+
rescore_args=()
|
|
785
|
+
while IFS= read -r -d '' a; do rescore_args+=("$a"); done < <(printf '%s' "$desc_in" | python3 -c '
|
|
786
|
+
import json, sys
|
|
787
|
+
try:
|
|
788
|
+
d = json.load(sys.stdin)
|
|
789
|
+
except Exception:
|
|
790
|
+
d = None
|
|
791
|
+
if not isinstance(d, dict):
|
|
792
|
+
sys.exit(0)
|
|
793
|
+
def s(v):
|
|
794
|
+
return "" if v is None else str(v)
|
|
795
|
+
comments = d.get("comments") if isinstance(d.get("comments"), list) else []
|
|
796
|
+
out = {
|
|
797
|
+
"kind": s(d.get("kind")), "key": s(d.get("key")), "title": s(d.get("title")),
|
|
798
|
+
"type": s(d.get("type")), "status": s(d.get("status")), "description": s(d.get("description")),
|
|
799
|
+
"priority": s(d.get("priority")), "resolution": s(d.get("resolution")),
|
|
800
|
+
"fixVersions": ",".join(s(v) for v in (d.get("fixVersions") or [])),
|
|
801
|
+
"parentKey": s(d.get("parentKey")), "parentDescription": s(d.get("parentDescription")),
|
|
802
|
+
"host": s(d.get("host")), "url": s(d.get("url")), "owner": s(d.get("owner")),
|
|
803
|
+
"repo": s(d.get("repo")), "branchHint": s(d.get("branchHint")),
|
|
804
|
+
"relatedIssues": json.dumps(d.get("relatedIssues") or [], ensure_ascii=False),
|
|
805
|
+
"comments": json.dumps({"items": comments, "total": d.get("commentsTotal") or len(comments)}, ensure_ascii=False),
|
|
806
|
+
"links": json.dumps(d.get("links") or [], ensure_ascii=False),
|
|
807
|
+
}
|
|
808
|
+
if d.get("needsRepoPicker"):
|
|
809
|
+
out["needsRepoPicker"] = "1"
|
|
810
|
+
for k, v in out.items():
|
|
811
|
+
sys.stdout.write("%s=%s\0" % (k, v.replace("\0", "")))
|
|
812
|
+
')
|
|
813
|
+
if [ "${#rescore_args[@]}" -eq 0 ]; then
|
|
814
|
+
echo '{"error":"--rescore needs a descriptor JSON object on stdin"}'
|
|
815
|
+
exit 2
|
|
816
|
+
fi
|
|
817
|
+
emit_descriptor "${rescore_args[@]}"
|
|
818
|
+
;;
|
|
819
|
+
|
|
603
820
|
freetext)
|
|
604
821
|
title="$INPUT"
|
|
605
822
|
branch=$(branch_for "" "Task" "$title")
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* json-file-lock.mjs - synchronous read-modify-write of a small JSON file
|
|
3
|
+
* under a lock, replaced atomically with owner-only permissions.
|
|
4
|
+
*
|
|
5
|
+
* The lock is `<file>.lock`, created the way write-state.mjs creates it: the
|
|
6
|
+
* owning PID is written to a private stage file that is hard-linked into place,
|
|
7
|
+
* so the lock never exists without its PID. The two helpers therefore exclude
|
|
8
|
+
* each other on the same file. A lock whose PID is no longer alive is
|
|
9
|
+
* reclaimed by inode identity; a lock with no readable PID is reclaimed only
|
|
10
|
+
* once it is older than `staleMs`.
|
|
11
|
+
*
|
|
12
|
+
* The write goes to a temp file in the same directory, created exclusively at
|
|
13
|
+
* `mode`, and is renamed over the target, so a reader sees the old document or
|
|
14
|
+
* the new one and never a partial write.
|
|
15
|
+
*
|
|
16
|
+
* @module pipeline/lib/json-file-lock
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
chmodSync,
|
|
21
|
+
linkSync,
|
|
22
|
+
readFileSync,
|
|
23
|
+
renameSync,
|
|
24
|
+
rmSync,
|
|
25
|
+
statSync,
|
|
26
|
+
unlinkSync,
|
|
27
|
+
writeFileSync,
|
|
28
|
+
} from "node:fs";
|
|
29
|
+
import { basename, dirname, join } from "node:path";
|
|
30
|
+
import { readRegularJson } from "./regular-file.mjs";
|
|
31
|
+
|
|
32
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
33
|
+
const DEFAULT_STALE_MS = 30_000;
|
|
34
|
+
const POLL_MS = 50;
|
|
35
|
+
|
|
36
|
+
export class FileLockTimeout extends Error {
|
|
37
|
+
constructor(lockPath, timeoutMs) {
|
|
38
|
+
super(`lock timeout on ${lockPath} after ${timeoutMs}ms`);
|
|
39
|
+
this.name = "FileLockTimeout";
|
|
40
|
+
this.code = "LOCK_TIMEOUT";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const pause = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
45
|
+
|
|
46
|
+
function staleIdentity(lockPath, staleMs) {
|
|
47
|
+
let pid;
|
|
48
|
+
let st;
|
|
49
|
+
try {
|
|
50
|
+
pid = parseInt(readFileSync(lockPath, "utf8").trim(), 10);
|
|
51
|
+
st = statSync(lockPath);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const age = Date.now() - st.mtimeMs;
|
|
56
|
+
if (!Number.isInteger(pid) || pid <= 0) return age > staleMs ? st : null;
|
|
57
|
+
try {
|
|
58
|
+
process.kill(pid, 0);
|
|
59
|
+
return null;
|
|
60
|
+
} catch (err) {
|
|
61
|
+
return err.code === "ESRCH" ? st : null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function acquire(lockPath, timeoutMs, staleMs) {
|
|
66
|
+
const stage = `${lockPath}.${process.pid}.stage`;
|
|
67
|
+
const deadline = Date.now() + timeoutMs;
|
|
68
|
+
for (;;) {
|
|
69
|
+
try {
|
|
70
|
+
writeFileSync(stage, String(process.pid), { mode: 0o600 });
|
|
71
|
+
linkSync(stage, lockPath);
|
|
72
|
+
const ino = statSync(lockPath).ino;
|
|
73
|
+
unlinkSync(stage);
|
|
74
|
+
return ino;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
rmSync(stage, { force: true });
|
|
77
|
+
if (err.code !== "EEXIST") throw err;
|
|
78
|
+
}
|
|
79
|
+
const stale = staleIdentity(lockPath, staleMs);
|
|
80
|
+
if (stale) {
|
|
81
|
+
try {
|
|
82
|
+
const now = statSync(lockPath);
|
|
83
|
+
if (now.ino === stale.ino && now.mtimeMs === stale.mtimeMs) unlinkSync(lockPath);
|
|
84
|
+
} catch {
|
|
85
|
+
/* gone already */
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (Date.now() >= deadline) throw new FileLockTimeout(lockPath, timeoutMs);
|
|
90
|
+
pause(POLL_MS);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function release(lockPath, ino) {
|
|
95
|
+
try {
|
|
96
|
+
if (statSync(lockPath).ino === ino) unlinkSync(lockPath);
|
|
97
|
+
} catch {
|
|
98
|
+
/* gone already */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Run `fn` while holding `<path>.lock`.
|
|
104
|
+
*
|
|
105
|
+
* @template T
|
|
106
|
+
* @param {string} path
|
|
107
|
+
* @param {() => T} fn
|
|
108
|
+
* @param {{timeoutMs?: number, staleMs?: number}} [opts]
|
|
109
|
+
* @returns {T}
|
|
110
|
+
*/
|
|
111
|
+
export function withFileLockSync(
|
|
112
|
+
path,
|
|
113
|
+
fn,
|
|
114
|
+
{ timeoutMs = DEFAULT_TIMEOUT_MS, staleMs = DEFAULT_STALE_MS } = {},
|
|
115
|
+
) {
|
|
116
|
+
const lockPath = `${path}.lock`;
|
|
117
|
+
const ino = acquire(lockPath, timeoutMs, staleMs);
|
|
118
|
+
try {
|
|
119
|
+
return fn();
|
|
120
|
+
} finally {
|
|
121
|
+
release(lockPath, ino);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Replace `path` with `data` as JSON, atomically, at `mode`.
|
|
127
|
+
*
|
|
128
|
+
* @param {string} path
|
|
129
|
+
* @param {unknown} data
|
|
130
|
+
* @param {{mode?: number}} [opts]
|
|
131
|
+
*/
|
|
132
|
+
export function writeJsonAtomicSync(path, data, { mode = 0o600 } = {}) {
|
|
133
|
+
const tmp = join(dirname(path), `.${basename(path)}.${process.pid}.tmp`);
|
|
134
|
+
rmSync(tmp, { force: true });
|
|
135
|
+
try {
|
|
136
|
+
writeFileSync(tmp, `${JSON.stringify(data, null, 2)}\n`, { mode, flag: "wx" });
|
|
137
|
+
chmodSync(tmp, mode);
|
|
138
|
+
renameSync(tmp, path);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
rmSync(tmp, { force: true });
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Lock, read, mutate and atomically replace a JSON file. `mutate` receives the
|
|
147
|
+
* parsed document (or `fallback` when the file is missing or unreadable) and
|
|
148
|
+
* returns the next one; returning `undefined` writes nothing.
|
|
149
|
+
*
|
|
150
|
+
* @param {string} path
|
|
151
|
+
* @param {(current: any) => any} mutate
|
|
152
|
+
* @param {{fallback?: any, mode?: number, timeoutMs?: number, staleMs?: number}} [opts]
|
|
153
|
+
* @returns {any} the document written, or undefined when nothing was
|
|
154
|
+
*/
|
|
155
|
+
export function updateJsonFileSync(path, mutate, { fallback = null, mode = 0o600, ...lock } = {}) {
|
|
156
|
+
return withFileLockSync(
|
|
157
|
+
path,
|
|
158
|
+
() => {
|
|
159
|
+
const next = mutate(readRegularJson(path, fallback));
|
|
160
|
+
if (next === undefined) return undefined;
|
|
161
|
+
writeJsonAtomicSync(path, next, { mode });
|
|
162
|
+
return next;
|
|
163
|
+
},
|
|
164
|
+
lock,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** A lock timeout from `env.WRITE_STATE_LOCK_TIMEOUT_MS`, the knob write-state reads. */
|
|
169
|
+
export function lockTimeoutFrom(env = process.env) {
|
|
170
|
+
const n = Number(env?.WRITE_STATE_LOCK_TIMEOUT_MS);
|
|
171
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_TIMEOUT_MS;
|
|
172
|
+
}
|
|
@@ -52,15 +52,8 @@ done
|
|
|
52
52
|
|
|
53
53
|
emit() { printf '%s\n' "$1"; exit 0; }
|
|
54
54
|
|
|
55
|
-
case "$CALL_SITE" in
|
|
56
|
-
subagent|bulk-read|research) ;;
|
|
57
|
-
*) emit "$DEFAULT_RUNG" ;;
|
|
58
|
-
esac
|
|
59
|
-
|
|
60
55
|
[ -n "$DEFAULT_RUNG" ] || DEFAULT_RUNG="sonnet"
|
|
61
56
|
|
|
62
|
-
command -v jq >/dev/null 2>&1 || emit "$DEFAULT_RUNG"
|
|
63
|
-
|
|
64
57
|
PREFS=""
|
|
65
58
|
for candidate in \
|
|
66
59
|
"${MULTI_AGENT_PREFS:-}" \
|
|
@@ -69,6 +62,23 @@ for candidate in \
|
|
|
69
62
|
do
|
|
70
63
|
[ -n "$candidate" ] && [ -f "$candidate" ] && { PREFS="$candidate"; break; }
|
|
71
64
|
done
|
|
65
|
+
|
|
66
|
+
# The fable rung exists only while modelFallback.fableEnabled is true, and it
|
|
67
|
+
# ships false. A caller whose default is fable - a command that runs without
|
|
68
|
+
# Phase 0, such as review - gets the documented resolution, opus, on every path
|
|
69
|
+
# out of here: routing off, out of scope, no rule, or nothing readable.
|
|
70
|
+
FABLE_ON=false
|
|
71
|
+
if [ -n "$PREFS" ] && command -v jq >/dev/null 2>&1; then
|
|
72
|
+
FABLE_ON=$(jq -r '.global.modelFallback.fableEnabled // false' "$PREFS" 2>/dev/null || echo false)
|
|
73
|
+
fi
|
|
74
|
+
[ "$DEFAULT_RUNG" = "fable" ] && [ "$FABLE_ON" != "true" ] && DEFAULT_RUNG="opus"
|
|
75
|
+
|
|
76
|
+
case "$CALL_SITE" in
|
|
77
|
+
subagent|bulk-read|research) ;;
|
|
78
|
+
*) emit "$DEFAULT_RUNG" ;;
|
|
79
|
+
esac
|
|
80
|
+
|
|
81
|
+
command -v jq >/dev/null 2>&1 || emit "$DEFAULT_RUNG"
|
|
72
82
|
[ -n "$PREFS" ] || emit "$DEFAULT_RUNG"
|
|
73
83
|
|
|
74
84
|
ENABLED=$(jq -r '.global.modelRouting.enabled // false' "$PREFS" 2>/dev/null) || emit "$DEFAULT_RUNG"
|
|
@@ -98,11 +108,10 @@ MATCH=$(jq -r \
|
|
|
98
108
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
99
109
|
COST_TABLE="$HERE/../scripts/cost-table.json"
|
|
100
110
|
|
|
101
|
-
# The fable rung answers only when the fable switch is on
|
|
102
|
-
# over what is available; it is not a second way to
|
|
103
|
-
# `/multi-agent:model off` would stop meaning anything the
|
|
104
|
-
# fable.
|
|
105
|
-
FABLE_ON=$(jq -r '.global.modelFallback.fableEnabled // false' "$PREFS" 2>/dev/null)
|
|
111
|
+
# The fable rung answers only when the fable switch is on (FABLE_ON, read
|
|
112
|
+
# above). Routing is a policy over what is available; it is not a second way to
|
|
113
|
+
# turn a rung on, or `/multi-agent:model off` would stop meaning anything the
|
|
114
|
+
# moment a rule named fable.
|
|
106
115
|
|
|
107
116
|
record() {
|
|
108
117
|
[ -x "$HERE/../scripts/log-metric.sh" ] || return 0
|