dsh-compound-engineering 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +160 -2
- package/README.zh.md +161 -0
- package/cordis.patch.yml +6 -0
- package/lib/frontmatter.js +167 -0
- package/lib/index.js +46 -0
- package/lib/provider.js +201 -0
- package/package.json +62 -4
- package/skills/CODING_STANDARDS.md +68 -0
- package/skills/ce-babysit-pr/SKILL.md +59 -0
- package/skills/ce-babysit-pr/references/branch-currency.md +22 -0
- package/skills/ce-babysit-pr/references/envelope.md +44 -0
- package/skills/ce-babysit-pr/references/pipeline.md +15 -0
- package/skills/ce-babysit-pr/references/report.md +20 -0
- package/skills/ce-babysit-pr/references/settle.md +37 -0
- package/skills/ce-babysit-pr/references/setup.md +33 -0
- package/skills/ce-babysit-pr/references/stack-commands.md +39 -0
- package/skills/ce-babysit-pr/references/stack.md +43 -0
- package/skills/ce-babysit-pr/references/tick.md +102 -0
- package/skills/ce-babysit-pr/references/watch-loop.md +222 -0
- package/skills/ce-babysit-pr/scripts/pr-snapshot +3656 -0
- package/skills/ce-bakeoff/SKILL.md +48 -0
- package/skills/ce-bakeoff/references/candidates.md +37 -0
- package/skills/ce-bakeoff/references/judging.md +19 -0
- package/skills/ce-bakeoff/references/output.md +11 -0
- package/skills/ce-bakeoff/references/verification.md +15 -0
- package/skills/ce-brainstorm/SKILL.md +69 -0
- package/skills/ce-brainstorm/references/agents/slack-researcher.md +127 -0
- package/skills/ce-brainstorm/references/approaches.md +57 -0
- package/skills/ce-brainstorm/references/bakeoff.md +9 -0
- package/skills/ce-brainstorm/references/blindspot-pass.md +70 -0
- package/skills/ce-brainstorm/references/brainstorm-sections.md +458 -0
- package/skills/ce-brainstorm/references/dialogue.md +89 -0
- package/skills/ce-brainstorm/references/handoff.md +187 -0
- package/skills/ce-brainstorm/references/html-rendering.md +638 -0
- package/skills/ce-brainstorm/references/interaction-rules.md +33 -0
- package/skills/ce-brainstorm/references/markdown-rendering.md +242 -0
- package/skills/ce-brainstorm/references/model-tiers.md +13 -0
- package/skills/ce-brainstorm/references/output-mode.md +26 -0
- package/skills/ce-brainstorm/references/phase-0.md +104 -0
- package/skills/ce-brainstorm/references/plan-write.md +32 -0
- package/skills/ce-brainstorm/references/product-pressure-test.md +42 -0
- package/skills/ce-brainstorm/references/reasoning-elevation.md +124 -0
- package/skills/ce-brainstorm/references/settled-decisions.md +45 -0
- package/skills/ce-brainstorm/references/synthesis-summary.md +149 -0
- package/skills/ce-brainstorm/references/universal-brainstorming.md +72 -0
- package/skills/ce-brainstorm/references/verdict-routing.md +26 -0
- package/skills/ce-brainstorm/references/visual-probes.md +164 -0
- package/skills/ce-brainstorm/scripts/elevation-dispatch.sh +288 -0
- package/skills/ce-brainstorm/scripts/light-webserver.js +1275 -0
- package/skills/ce-brainstorm/scripts/packs-resolve.py +737 -0
- package/skills/ce-brainstorm/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-code-review/SKILL.md +40 -0
- package/skills/ce-code-review/references/action-class-rubric.md +67 -0
- package/skills/ce-code-review/references/cross-model-recovery.md +38 -0
- package/skills/ce-code-review/references/cross-model-review.md +191 -0
- package/skills/ce-code-review/references/depth-paths.md +32 -0
- package/skills/ce-code-review/references/diff-scope.md +59 -0
- package/skills/ce-code-review/references/dispatch-reviewers.md +135 -0
- package/skills/ce-code-review/references/findings-schema.json +125 -0
- package/skills/ce-code-review/references/finish-input.md +118 -0
- package/skills/ce-code-review/references/finish-review.md +202 -0
- package/skills/ce-code-review/references/intent-and-plan.md +73 -0
- package/skills/ce-code-review/references/modes-and-output.md +156 -0
- package/skills/ce-code-review/references/persona-catalog.md +66 -0
- package/skills/ce-code-review/references/personas/adversarial-reviewer.md +111 -0
- package/skills/ce-code-review/references/personas/agent-native-reviewer.md +173 -0
- package/skills/ce-code-review/references/personas/api-contract-reviewer.md +44 -0
- package/skills/ce-code-review/references/personas/correctness-reviewer.md +48 -0
- package/skills/ce-code-review/references/personas/data-migration-reviewer.md +111 -0
- package/skills/ce-code-review/references/personas/deployment-verification-agent.md +157 -0
- package/skills/ce-code-review/references/personas/julik-frontend-races-reviewer.md +45 -0
- package/skills/ce-code-review/references/personas/learnings-researcher.md +260 -0
- package/skills/ce-code-review/references/personas/maintainability-reviewer.md +85 -0
- package/skills/ce-code-review/references/personas/performance-reviewer.md +45 -0
- package/skills/ce-code-review/references/personas/previous-comments-reviewer.md +59 -0
- package/skills/ce-code-review/references/personas/project-standards-reviewer.md +77 -0
- package/skills/ce-code-review/references/personas/reliability-reviewer.md +49 -0
- package/skills/ce-code-review/references/personas/security-reviewer.md +50 -0
- package/skills/ce-code-review/references/personas/swift-ios-reviewer.md +99 -0
- package/skills/ce-code-review/references/personas/testing-reviewer.md +51 -0
- package/skills/ce-code-review/references/review-output-template.md +172 -0
- package/skills/ce-code-review/references/scope.md +176 -0
- package/skills/ce-code-review/references/select-and-route.md +93 -0
- package/skills/ce-code-review/references/subagent-template.md +206 -0
- package/skills/ce-code-review/references/validator-batch-template.md +67 -0
- package/skills/ce-code-review/scripts/cross-model-adversarial-review.sh +1275 -0
- package/skills/ce-code-review/scripts/findings-mechanics.py +274 -0
- package/skills/ce-code-review/scripts/packs-resolve.py +737 -0
- package/skills/ce-code-review/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-code-review/scripts/review-scope.py +419 -0
- package/skills/ce-code-review/scripts/run-log.py +280 -0
- package/skills/ce-commit/SKILL.md +60 -0
- package/skills/ce-commit-push-pr/SKILL.md +69 -0
- package/skills/ce-commit-push-pr/references/apply-and-handoff.md +79 -0
- package/skills/ce-commit-push-pr/references/branch-creation.md +47 -0
- package/skills/ce-commit-push-pr/references/commit-and-push.md +26 -0
- package/skills/ce-commit-push-pr/references/compose.md +29 -0
- package/skills/ce-commit-push-pr/references/context.md +42 -0
- package/skills/ce-commit-push-pr/references/gh-stack-cli.md +83 -0
- package/skills/ce-commit-push-pr/references/pr-description-writing.md +208 -0
- package/skills/ce-commit-push-pr/references/stack-submit.md +81 -0
- package/skills/ce-compound/SKILL.md +79 -0
- package/skills/ce-compound/assets/resolution-template.md +96 -0
- package/skills/ce-compound/references/agents/best-practices-researcher.md +115 -0
- package/skills/ce-compound/references/agents/data-integrity-guardian.md +68 -0
- package/skills/ce-compound/references/agents/framework-docs-researcher.md +93 -0
- package/skills/ce-compound/references/agents/pattern-recognition-specialist.md +55 -0
- package/skills/ce-compound/references/agents/performance-oracle.md +108 -0
- package/skills/ce-compound/references/agents/security-sentinel.md +91 -0
- package/skills/ce-compound/references/agents/session-historian.md +82 -0
- package/skills/ce-compound/references/assembly.md +127 -0
- package/skills/ce-compound/references/concepts-vocabulary.md +98 -0
- package/skills/ce-compound/references/enhancement.md +42 -0
- package/skills/ce-compound/references/grounding-validation.md +89 -0
- package/skills/ce-compound/references/lightweight.md +56 -0
- package/skills/ce-compound/references/modes.md +13 -0
- package/skills/ce-compound/references/refresh-and-discoverability.md +96 -0
- package/skills/ce-compound/references/report.md +115 -0
- package/skills/ce-compound/references/research.md +144 -0
- package/skills/ce-compound/references/schema.yaml +236 -0
- package/skills/ce-compound/references/session-history.md +73 -0
- package/skills/ce-compound/references/yaml-schema.md +130 -0
- package/skills/ce-compound/scripts/packs-resolve.py +737 -0
- package/skills/ce-compound/scripts/session-history/__pycache__/dsh_session.cpython-314.pyc +0 -0
- package/skills/ce-compound/scripts/session-history/discover-sessions.sh +290 -0
- package/skills/ce-compound/scripts/session-history/discover_dsh.py +123 -0
- package/skills/ce-compound/scripts/session-history/dsh_session.py +225 -0
- package/skills/ce-compound/scripts/session-history/extract-errors.py +178 -0
- package/skills/ce-compound/scripts/session-history/extract-metadata.py +301 -0
- package/skills/ce-compound/scripts/session-history/extract-skeleton.py +346 -0
- package/skills/ce-compound/scripts/validate-doc-claims.py +497 -0
- package/skills/ce-compound/scripts/validate-frontmatter.py +137 -0
- package/skills/ce-compound-refresh/SKILL.md +88 -0
- package/skills/ce-compound-refresh/assets/resolution-template.md +96 -0
- package/skills/ce-compound-refresh/references/classify.md +42 -0
- package/skills/ce-compound-refresh/references/commit.md +7 -0
- package/skills/ce-compound-refresh/references/concepts-vocabulary.md +116 -0
- package/skills/ce-compound-refresh/references/discoverability.md +15 -0
- package/skills/ce-compound-refresh/references/investigate.md +23 -0
- package/skills/ce-compound-refresh/references/modes.md +27 -0
- package/skills/ce-compound-refresh/references/per-action-flows.md +142 -0
- package/skills/ce-compound-refresh/references/report.md +23 -0
- package/skills/ce-compound-refresh/references/schema.yaml +236 -0
- package/skills/ce-compound-refresh/references/scope.md +18 -0
- package/skills/ce-compound-refresh/references/worth-audit.md +35 -0
- package/skills/ce-compound-refresh/references/yaml-schema.md +130 -0
- package/skills/ce-compound-refresh/scripts/validate-doc-claims.py +497 -0
- package/skills/ce-compound-refresh/scripts/validate-frontmatter.py +137 -0
- package/skills/ce-debug/SKILL.md +122 -0
- package/skills/ce-debug/references/anti-patterns.md +91 -0
- package/skills/ce-debug/references/defense-in-depth.md +39 -0
- package/skills/ce-debug/references/fix.md +21 -0
- package/skills/ce-debug/references/investigate.md +114 -0
- package/skills/ce-debug/references/investigation-techniques.md +384 -0
- package/skills/ce-debug/references/pipeline-mode.md +78 -0
- package/skills/ce-debug/references/post-fix-handoff.md +60 -0
- package/skills/ce-debug/references/return-to-caller.md +57 -0
- package/skills/ce-doc-review/SKILL.md +75 -0
- package/skills/ce-doc-review/references/bulk-preview.md +146 -0
- package/skills/ce-doc-review/references/cross-model-review.md +207 -0
- package/skills/ce-doc-review/references/decision-primer.md +47 -0
- package/skills/ce-doc-review/references/dispatch.md +56 -0
- package/skills/ce-doc-review/references/document-intake.md +44 -0
- package/skills/ce-doc-review/references/findings-schema.json +85 -0
- package/skills/ce-doc-review/references/modes.md +20 -0
- package/skills/ce-doc-review/references/open-questions-defer.md +158 -0
- package/skills/ce-doc-review/references/persona-selection.md +41 -0
- package/skills/ce-doc-review/references/personas/adversarial-document-reviewer.md +109 -0
- package/skills/ce-doc-review/references/personas/coherence-reviewer.md +69 -0
- package/skills/ce-doc-review/references/personas/design-lens-reviewer.md +48 -0
- package/skills/ce-doc-review/references/personas/feasibility-reviewer.md +50 -0
- package/skills/ce-doc-review/references/personas/product-lens-reviewer.md +85 -0
- package/skills/ce-doc-review/references/personas/scope-guardian-reviewer.md +83 -0
- package/skills/ce-doc-review/references/personas/security-lens-reviewer.md +41 -0
- package/skills/ce-doc-review/references/personas/whole-doc-reviewer.md +25 -0
- package/skills/ce-doc-review/references/rendering-floor.md +175 -0
- package/skills/ce-doc-review/references/review-output-template.md +123 -0
- package/skills/ce-doc-review/references/subagent-template.md +188 -0
- package/skills/ce-doc-review/references/synthesis-and-presentation.md +350 -0
- package/skills/ce-doc-review/references/walkthrough.md +347 -0
- package/skills/ce-doc-review/scripts/cross-model-doc-review.sh +1270 -0
- package/skills/ce-doc-review/scripts/packs-resolve.py +737 -0
- package/skills/ce-doc-review/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-dogfood/SKILL.md +66 -0
- package/skills/ce-dogfood/agents/openai.yaml +2 -0
- package/skills/ce-dogfood/references/dogfood-report-template.md +97 -0
- package/skills/ce-dogfood/references/phases.md +167 -0
- package/skills/ce-dogfood/references/test-matrix-taxonomy.md +62 -0
- package/skills/ce-dogfood/scripts/packs-resolve.py +737 -0
- package/skills/ce-explain/SKILL.md +81 -0
- package/skills/ce-explain/references/agents/behavior-trace-scout.md +19 -0
- package/skills/ce-explain/references/agents/work-recap-scout.md +23 -0
- package/skills/ce-explain/references/check-in.md +18 -0
- package/skills/ce-explain/references/destinations.md +35 -0
- package/skills/ce-explain/references/explainer-html.md +29 -0
- package/skills/ce-explain/references/explainer-markdown.md +24 -0
- package/skills/ce-explain/references/intake.md +50 -0
- package/skills/ce-explain/references/orchestration.md +46 -0
- package/skills/ce-handoff/SKILL.md +54 -0
- package/skills/ce-handoff/references/create.md +88 -0
- package/skills/ce-handoff/references/resume.md +46 -0
- package/skills/ce-ideate/SKILL.md +75 -0
- package/skills/ce-ideate/references/agents/issue-intelligence-analyst.md +166 -0
- package/skills/ce-ideate/references/agents/learnings-researcher.md +247 -0
- package/skills/ce-ideate/references/agents/slack-researcher.md +127 -0
- package/skills/ce-ideate/references/agents/web-researcher.md +121 -0
- package/skills/ce-ideate/references/decomposition.md +39 -0
- package/skills/ce-ideate/references/divergent-ideation.md +112 -0
- package/skills/ce-ideate/references/grounding.md +138 -0
- package/skills/ce-ideate/references/html-rendering.md +638 -0
- package/skills/ce-ideate/references/ideation-sections.md +191 -0
- package/skills/ce-ideate/references/issue-intelligence.md +44 -0
- package/skills/ce-ideate/references/markdown-rendering.md +242 -0
- package/skills/ce-ideate/references/output-mode.md +34 -0
- package/skills/ce-ideate/references/post-ideation-workflow.md +169 -0
- package/skills/ce-ideate/references/scope-gates.md +148 -0
- package/skills/ce-ideate/references/universal-ideation.md +119 -0
- package/skills/ce-ideate/references/user-research-artifacts.md +28 -0
- package/skills/ce-ideate/references/web-research-cache.md +56 -0
- package/skills/ce-noslop/SKILL.md +45 -0
- package/skills/ce-noslop/references/patterns.md +61 -0
- package/skills/ce-noslop/references/terminology.md +3 -0
- package/skills/ce-optimize/SKILL.md +64 -0
- package/skills/ce-optimize/references/agents/learnings-researcher.md +247 -0
- package/skills/ce-optimize/references/agents/repo-research-analyst.md +258 -0
- package/skills/ce-optimize/references/example-expensive-benchmark-spec.yaml +88 -0
- package/skills/ce-optimize/references/example-hard-spec.yaml +64 -0
- package/skills/ce-optimize/references/example-judge-spec.yaml +83 -0
- package/skills/ce-optimize/references/example-text-target-spec.yaml +80 -0
- package/skills/ce-optimize/references/experiment-log-schema.yaml +563 -0
- package/skills/ce-optimize/references/experiment-prompt-template.md +143 -0
- package/skills/ce-optimize/references/judge-prompt-template.md +117 -0
- package/skills/ce-optimize/references/loop.md +286 -0
- package/skills/ce-optimize/references/measurement.md +168 -0
- package/skills/ce-optimize/references/optimize-spec-schema.yaml +570 -0
- package/skills/ce-optimize/references/persistence.md +93 -0
- package/skills/ce-optimize/references/spec.md +107 -0
- package/skills/ce-optimize/references/text-targets.md +51 -0
- package/skills/ce-optimize/references/usage-guide.md +147 -0
- package/skills/ce-optimize/references/wrap-up.md +54 -0
- package/skills/ce-optimize/scripts/decide.mjs +676 -0
- package/skills/ce-optimize/scripts/experiment-worktree.sh +330 -0
- package/skills/ce-optimize/scripts/measure.sh +140 -0
- package/skills/ce-optimize/scripts/parallel-probe.sh +134 -0
- package/skills/ce-plan/SKILL.md +60 -0
- package/skills/ce-plan/references/agents/agent-native-planning-strategist.md +66 -0
- package/skills/ce-plan/references/agents/architecture-strategist.md +46 -0
- package/skills/ce-plan/references/agents/best-practices-researcher.md +118 -0
- package/skills/ce-plan/references/agents/data-integrity-guardian.md +68 -0
- package/skills/ce-plan/references/agents/framework-docs-researcher.md +97 -0
- package/skills/ce-plan/references/agents/git-history-analyzer.md +40 -0
- package/skills/ce-plan/references/agents/learnings-researcher.md +268 -0
- package/skills/ce-plan/references/agents/pattern-recognition-specialist.md +55 -0
- package/skills/ce-plan/references/agents/performance-oracle.md +108 -0
- package/skills/ce-plan/references/agents/repo-research-analyst.md +262 -0
- package/skills/ce-plan/references/agents/security-sentinel.md +91 -0
- package/skills/ce-plan/references/agents/slack-researcher.md +131 -0
- package/skills/ce-plan/references/agents/spec-flow-analyzer.md +84 -0
- package/skills/ce-plan/references/agents/web-researcher.md +125 -0
- package/skills/ce-plan/references/approach-altitude.md +55 -0
- package/skills/ce-plan/references/bakeoff.md +9 -0
- package/skills/ce-plan/references/deepening-workflow.md +263 -0
- package/skills/ce-plan/references/final-review.md +134 -0
- package/skills/ce-plan/references/html-rendering.md +638 -0
- package/skills/ce-plan/references/intake.md +158 -0
- package/skills/ce-plan/references/markdown-rendering.md +242 -0
- package/skills/ce-plan/references/output-contracts.md +29 -0
- package/skills/ce-plan/references/output-mode.md +45 -0
- package/skills/ce-plan/references/plan-handoff.md +116 -0
- package/skills/ce-plan/references/plan-sections.md +489 -0
- package/skills/ce-plan/references/reasoning-elevation.md +124 -0
- package/skills/ce-plan/references/research.md +211 -0
- package/skills/ce-plan/references/resume.md +61 -0
- package/skills/ce-plan/references/settled-decisions.md +45 -0
- package/skills/ce-plan/references/structure.md +207 -0
- package/skills/ce-plan/references/synthesis-summary.md +260 -0
- package/skills/ce-plan/references/universal-planning.md +170 -0
- package/skills/ce-plan/scripts/elevation-dispatch.sh +288 -0
- package/skills/ce-plan/scripts/packs-resolve.py +737 -0
- package/skills/ce-plan/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-polish/SKILL.md +22 -0
- package/skills/ce-polish/agents/openai.yaml +2 -0
- package/skills/ce-polish/references/dev-server-astro.md +58 -0
- package/skills/ce-polish/references/dev-server-detection.md +40 -0
- package/skills/ce-polish/references/dev-server-next.md +61 -0
- package/skills/ce-polish/references/dev-server-nuxt.md +58 -0
- package/skills/ce-polish/references/dev-server-procfile.md +59 -0
- package/skills/ce-polish/references/dev-server-rails.md +50 -0
- package/skills/ce-polish/references/dev-server-remix.md +58 -0
- package/skills/ce-polish/references/dev-server-sveltekit.md +58 -0
- package/skills/ce-polish/references/dev-server-vite.md +48 -0
- package/skills/ce-polish/references/install-riffrec.md +35 -0
- package/skills/ce-polish/references/launch-json-schema.md +177 -0
- package/skills/ce-polish/references/live-loop.md +98 -0
- package/skills/ce-polish/references/live-remote.md +35 -0
- package/skills/ce-polish/references/live-start.md +93 -0
- package/skills/ce-polish/references/live-stream-contract.md +253 -0
- package/skills/ce-polish/references/run.md +67 -0
- package/skills/ce-polish/scripts/detect-project-type.sh +277 -0
- package/skills/ce-polish/scripts/detect-riffrec.sh +225 -0
- package/skills/ce-polish/scripts/live-endpoint.js +2841 -0
- package/skills/ce-polish/scripts/read-launch-json.sh +117 -0
- package/skills/ce-polish/scripts/resolve-package-manager.sh +95 -0
- package/skills/ce-polish/scripts/resolve-port.sh +307 -0
- package/skills/ce-pov/SKILL.md +64 -0
- package/skills/ce-pov/references/agents/external-evidence-researcher.md +26 -0
- package/skills/ce-pov/references/agents/pov-peer.md +43 -0
- package/skills/ce-pov/references/agents/precedent-activity-scout.md +21 -0
- package/skills/ce-pov/references/agents/project-grounding-scout.md +32 -0
- package/skills/ce-pov/references/boundaries.md +36 -0
- package/skills/ce-pov/references/cross-model-panel.md +596 -0
- package/skills/ce-pov/references/followup.md +9 -0
- package/skills/ce-pov/references/grounding.md +47 -0
- package/skills/ce-pov/references/intake.md +61 -0
- package/skills/ce-pov/references/invocation.md +47 -0
- package/skills/ce-pov/references/method.md +68 -0
- package/skills/ce-pov/references/pov-schema.json +56 -0
- package/skills/ce-pov/references/report.md +7 -0
- package/skills/ce-pov/scripts/cross-model-pov.sh +1110 -0
- package/skills/ce-pov/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-product-pulse/SKILL.md +81 -0
- package/skills/ce-product-pulse/agents/openai.yaml +2 -0
- package/skills/ce-product-pulse/references/config.md +20 -0
- package/skills/ce-product-pulse/references/interview.md +260 -0
- package/skills/ce-product-pulse/references/report-template.md +89 -0
- package/skills/ce-product-pulse/references/run.md +46 -0
- package/skills/ce-product-pulse/references/setup.md +43 -0
- package/skills/ce-promote/SKILL.md +90 -0
- package/skills/ce-promote/agents/openai.yaml +2 -0
- package/skills/ce-promote/references/spiral-cli.md +149 -0
- package/skills/ce-proof/SKILL.md +65 -0
- package/skills/ce-proof/references/api.md +176 -0
- package/skills/ce-proof/references/workflows.md +124 -0
- package/skills/ce-prototype/SKILL.md +59 -0
- package/skills/ce-prototype/assets/annotate.css +206 -0
- package/skills/ce-prototype/assets/annotate.js +837 -0
- package/skills/ce-prototype/references/annotation-loop.md +32 -0
- package/skills/ce-prototype/references/build.md +39 -0
- package/skills/ce-prototype/references/craft-floor.md +43 -0
- package/skills/ce-prototype/references/preview.md +118 -0
- package/skills/ce-prototype/references/scoping.md +38 -0
- package/skills/ce-prototype/references/write-back.md +37 -0
- package/skills/ce-prototype/scripts/light-webserver.js +1275 -0
- package/skills/ce-resolve-pr-feedback/SKILL.md +60 -0
- package/skills/ce-resolve-pr-feedback/references/agents/pr-comment-resolver.md +59 -0
- package/skills/ce-resolve-pr-feedback/references/evaluation-rubric.md +141 -0
- package/skills/ce-resolve-pr-feedback/references/full-mode.md +336 -0
- package/skills/ce-resolve-pr-feedback/references/pipeline-mode.md +23 -0
- package/skills/ce-resolve-pr-feedback/references/resume.md +60 -0
- package/skills/ce-resolve-pr-feedback/references/return-to-caller.md +111 -0
- package/skills/ce-resolve-pr-feedback/references/targeted-mode.md +48 -0
- package/skills/ce-resolve-pr-feedback/scripts/get-pr-comments +176 -0
- package/skills/ce-resolve-pr-feedback/scripts/get-thread-for-comment +84 -0
- package/skills/ce-resolve-pr-feedback/scripts/pending-feedback.py +287 -0
- package/skills/ce-resolve-pr-feedback/scripts/reply-to-pr-thread +57 -0
- package/skills/ce-resolve-pr-feedback/scripts/resolve-pr-thread +23 -0
- package/skills/ce-retune/SKILL.md +42 -0
- package/skills/ce-retune/agents/openai.yaml +2 -0
- package/skills/ce-retune/references/baseline-mining.md +122 -0
- package/skills/ce-retune/references/corpus-audit.md +128 -0
- package/skills/ce-retune/references/cut-passes.md +129 -0
- package/skills/ce-retune/references/halt-taxonomy.md +123 -0
- package/skills/ce-retune/references/noise-floor.md +131 -0
- package/skills/ce-retune/references/workflow-shapes.md +73 -0
- package/skills/ce-riffrec-feedback-analysis/SKILL.md +33 -0
- package/skills/ce-riffrec-feedback-analysis/references/analyzer.md +18 -0
- package/skills/ce-riffrec-feedback-analysis/references/compound-engineering-feedback-format.md +116 -0
- package/skills/ce-riffrec-feedback-analysis/references/extensive-analysis.md +98 -0
- package/skills/ce-riffrec-feedback-analysis/references/install-riffrec.md +27 -0
- package/skills/ce-riffrec-feedback-analysis/references/quick-bug-report.md +38 -0
- package/skills/ce-riffrec-feedback-analysis/scripts/analyze_riffrec_zip.py +1306 -0
- package/skills/ce-setup/SKILL.md +103 -0
- package/skills/ce-setup/agents/openai.yaml +2 -0
- package/skills/ce-setup/assets/compounding-directive.md +11 -0
- package/skills/ce-setup/assets/noslop-directive.md +7 -0
- package/skills/ce-setup/assets/pack-rule-template.md +11 -0
- package/skills/ce-setup/references/acpx-cache-warm.md +14 -0
- package/skills/ce-setup/references/config-template.yaml +210 -0
- package/skills/ce-setup/references/legacy-codex-tool-map.md +15 -0
- package/skills/ce-setup/references/pack-scaffold.md +45 -0
- package/skills/ce-setup/references/repo-fixes.md +121 -0
- package/skills/ce-setup/scripts/check-health +929 -0
- package/skills/ce-setup/scripts/packs-resolve.py +737 -0
- package/skills/ce-simplify-code/SKILL.md +76 -0
- package/skills/ce-simplify-code/references/personas/code-quality-reviewer.md +17 -0
- package/skills/ce-simplify-code/references/personas/code-reuse-reviewer.md +7 -0
- package/skills/ce-simplify-code/references/personas/comment-reviewer.md +24 -0
- package/skills/ce-simplify-code/references/personas/efficiency-reviewer.md +11 -0
- package/skills/ce-simplify-code/references/personas/test-value-reviewer.md +23 -0
- package/skills/ce-simplify-code/references/reviewer-dispatch.md +11 -0
- package/skills/ce-simplify-code/references/standalone-report.md +25 -0
- package/skills/ce-strategy/SKILL.md +61 -0
- package/skills/ce-strategy/references/grounding.md +22 -0
- package/skills/ce-strategy/references/interview.md +179 -0
- package/skills/ce-strategy/references/strategy-template.md +94 -0
- package/skills/ce-strategy/references/update-run.md +33 -0
- package/skills/ce-sweep/SKILL.md +93 -0
- package/skills/ce-sweep/agents/openai.yaml +2 -0
- package/skills/ce-sweep/references/agents/media-analyzer.md +54 -0
- package/skills/ce-sweep/references/interview.md +208 -0
- package/skills/ce-sweep/references/model-tiers.md +13 -0
- package/skills/ce-sweep/references/plan-template.md +62 -0
- package/skills/ce-sweep/references/run.md +126 -0
- package/skills/ce-sweep/references/sources/email.md +57 -0
- package/skills/ce-sweep/references/sources/github-issues.md +59 -0
- package/skills/ce-sweep/references/sources/slack.md +61 -0
- package/skills/ce-sweep/references/state-schema.md +157 -0
- package/skills/ce-sweep/references/subagent-template.md +60 -0
- package/skills/ce-sweep/scripts/analyze_riffrec_zip.py +1306 -0
- package/skills/ce-sweep/scripts/sweep-state.py +793 -0
- package/skills/ce-test-browser/SKILL.md +50 -0
- package/skills/ce-test-browser/references/agent-browser-driver.md +47 -0
- package/skills/ce-test-browser/references/pipeline-orchestration.md +45 -0
- package/skills/ce-test-browser/references/route-and-report.md +129 -0
- package/skills/ce-test-browser/scripts/resolve-port.sh +41 -0
- package/skills/ce-test-xcode/SKILL.md +26 -0
- package/skills/ce-test-xcode/agents/openai.yaml +2 -0
- package/skills/ce-test-xcode/references/setup-and-build.md +32 -0
- package/skills/ce-test-xcode/references/test-and-report.md +64 -0
- package/skills/ce-work/SKILL.md +62 -0
- package/skills/ce-work/references/agents/figma-design-sync.md +27 -0
- package/skills/ce-work/references/agents/implementation-worker.md +17 -0
- package/skills/ce-work/references/cross-model-execution.md +191 -0
- package/skills/ce-work/references/execution-engines.md +64 -0
- package/skills/ce-work/references/execution-strategy.md +73 -0
- package/skills/ce-work/references/implementation-loop.md +107 -0
- package/skills/ce-work/references/implementation-result-schema.json +52 -0
- package/skills/ce-work/references/input-triage.md +50 -0
- package/skills/ce-work/references/non-code-execution.md +23 -0
- package/skills/ce-work/references/return-to-caller.md +33 -0
- package/skills/ce-work/references/review-findings-followup.md +82 -0
- package/skills/ce-work/references/shipping-workflow.md +89 -0
- package/skills/ce-work/references/tracker-defer.md +150 -0
- package/skills/ce-work/references/work-intake.md +51 -0
- package/skills/ce-work/references/workspace-setup.md +27 -0
- package/skills/ce-work/scripts/__pycache__/unit_workspace_jobs.cpython-314.pyc +0 -0
- package/skills/ce-work/scripts/__pycache__/unit_workspace_state.cpython-314.pyc +0 -0
- package/skills/ce-work/scripts/cross-model-work.sh +1385 -0
- package/skills/ce-work/scripts/peer-job-runner.py +2594 -0
- package/skills/ce-work/scripts/unit-workspace.py +215 -0
- package/skills/ce-work/scripts/unit_workspace_ignored.py +93 -0
- package/skills/ce-work/scripts/unit_workspace_integration.py +677 -0
- package/skills/ce-work/scripts/unit_workspace_jobs.py +1216 -0
- package/skills/ce-work/scripts/unit_workspace_lifecycle.py +981 -0
- package/skills/ce-work/scripts/unit_workspace_state.py +1379 -0
- package/skills/ce-work/scripts/unit_workspace_transaction.py +612 -0
- package/skills/ce-worktree/SKILL.md +54 -0
- package/skills/lfg/SKILL.md +59 -0
- package/skills/lfg/references/debug-return.md +25 -0
- package/skills/lfg/references/intake.md +30 -0
- package/skills/lfg/references/next-work-handoff.md +96 -0
- package/skills/lfg/references/plan-brief.md +36 -0
- package/skills/lfg/references/review-followup.md +78 -0
- package/skills/lfg/references/shipping.md +65 -0
- package/skills/lfg/references/stage-routing.md +49 -0
- package/skills/lfg/references/task-visibility.md +11 -0
- package/skills/lfg/references/tracker-defer.md +150 -0
- package/skills/lfg/references/work-return.md +27 -0
- package/skills/wtf/SKILL.md +33 -0
- package/skills/wtf/agents/openai.yaml +2 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Compute fail-closed, deterministic scope facts for ce-code-review.
|
|
3
|
+
|
|
4
|
+
The helper never awards lite. It reports counts, path classes, and floors
|
|
5
|
+
the skill's Review depth gate reads. `hard_block_full` forces the full
|
|
6
|
+
spine: a path class the script can name, a file it could not count, or a
|
|
7
|
+
change whose executable non-test lines reach the full floor. Below that
|
|
8
|
+
floor, size is a fact the gate reads, never a decision; the agent judges
|
|
9
|
+
consequence.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import functools
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
CODE_EXTENSIONS = {
|
|
25
|
+
".rb", ".py", ".js", ".mjs", ".cjs", ".jsx", ".ts", ".tsx", ".go", ".rs",
|
|
26
|
+
".java", ".swift", ".kt", ".c", ".cc", ".cpp", ".cs", ".php",
|
|
27
|
+
".ex", ".exs", ".scala",
|
|
28
|
+
".sh", ".bash", ".zsh", ".fish", ".ps1", ".pl", ".pm", ".lua", ".dart",
|
|
29
|
+
".vue", ".svelte",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
SIGNAL_PATTERNS = {
|
|
33
|
+
"migrations": re.compile(
|
|
34
|
+
r"db/migrate/|schema\.(rb|sql)|/migrations?/|alembic|flyway|liquibase",
|
|
35
|
+
re.I,
|
|
36
|
+
),
|
|
37
|
+
"frontend": re.compile(
|
|
38
|
+
r"\.(tsx|jsx|vue|svelte|css|scss|html|erb|haml)$|/components?/|stimulus|turbo",
|
|
39
|
+
re.I,
|
|
40
|
+
),
|
|
41
|
+
"api": re.compile(
|
|
42
|
+
r"/(routes?|controllers?|api|serializers?|graphql)/|\.proto$|openapi|swagger",
|
|
43
|
+
re.I,
|
|
44
|
+
),
|
|
45
|
+
"swift-ios": re.compile(r"\.(swift|kt|pbxproj|xcconfig|entitlements)$", re.I),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Classes the script can name from paths alone. These force full; they do not
|
|
49
|
+
# award lite.
|
|
50
|
+
HARD_BLOCK_PATTERNS = {
|
|
51
|
+
"migrations": SIGNAL_PATTERNS["migrations"],
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Silent-pass guards the script can name from paths alone. These forbid lite
|
|
55
|
+
# (the change needs the adversarial read the focused path carries) but do not
|
|
56
|
+
# force full; consequence still decides focused versus full. Silent-pass guards
|
|
57
|
+
# outside these paths are the agent's question.
|
|
58
|
+
SILENT_PASS_PATTERNS = {
|
|
59
|
+
"ci": re.compile(
|
|
60
|
+
r"(^|/)\.github/workflows/|(^|/)\.gitlab-ci\.yml$|(^|/)\.gitlab-ci/"
|
|
61
|
+
r"|(^|/)Jenkinsfile$|(^|/)\.circleci/|(^|/)\.buildkite/",
|
|
62
|
+
re.I,
|
|
63
|
+
),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Executable non-test changed lines at or above this run the full spine; it
|
|
67
|
+
# matches the maintainability reviewer's trigger. Below it, consequence decides.
|
|
68
|
+
# Whole-file deletions never count toward it: they band separately, and that
|
|
69
|
+
# band can forbid lite but never forces full.
|
|
70
|
+
FULL_EXEC_LINE_MIN = 200
|
|
71
|
+
|
|
72
|
+
# Conventions recognized: tests?/spec/__tests__ directories; a .test./.spec.
|
|
73
|
+
# suffix; a test_*.py / conftest.py Python prefix; and a case-sensitive
|
|
74
|
+
# Test/Tests/Spec class-file suffix (Java/C#/Scala/Swift/Kotlin), so that
|
|
75
|
+
# Contest.java or Manifest.cs stays production code.
|
|
76
|
+
TEST_PATTERN = re.compile(
|
|
77
|
+
r"(^|/)(tests?|spec|__tests__)/"
|
|
78
|
+
r"|(^|/)[^/]+[._-](test|spec)\.[^/]+$"
|
|
79
|
+
r"|(^|/)(test_[^/]+|conftest)\.[^/]+$"
|
|
80
|
+
r"|(?-i:(^|/)[^/]+(Test|Tests|Spec)\.(java|kt|scala|swift|cs)$)",
|
|
81
|
+
re.I,
|
|
82
|
+
)
|
|
83
|
+
AGENT_SURFACE_PATTERN = re.compile(
|
|
84
|
+
r"(^|/)(skills?|agents?|prompts?|tools?|mcp|commands?)(/|$)|SKILL\.md$|"
|
|
85
|
+
r"(^|/)(AGENTS|CLAUDE|GEMINI)\.md$|\.cursor/|\.codex-plugin/|\.claude-plugin/",
|
|
86
|
+
re.I,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def git(*args: str) -> subprocess.CompletedProcess[str]:
|
|
91
|
+
return subprocess.run(
|
|
92
|
+
["git", *args], capture_output=True, text=True, check=False
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def valid_commit(ref: str | None) -> bool:
|
|
97
|
+
if not ref:
|
|
98
|
+
return False
|
|
99
|
+
return git("rev-parse", "--verify", "--quiet", f"{ref}^{{commit}}").returncode == 0
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def unique_merge_base(base: str, head: str) -> str | None:
|
|
103
|
+
result = git("merge-base", "--all", base, head)
|
|
104
|
+
candidates = [line for line in result.stdout.splitlines() if line]
|
|
105
|
+
if result.returncode != 0 or len(candidates) != 1:
|
|
106
|
+
return None
|
|
107
|
+
return candidates[0]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
DEFAULT_DOCS_ROOT = "docs"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def normalize_docs_root(docs_root: str | None) -> str:
|
|
114
|
+
"""Fall back to the default root for an unset, empty, or unsubstituted value.
|
|
115
|
+
|
|
116
|
+
The calling skill substitutes a resolved path for the ``<root>`` placeholder
|
|
117
|
+
before invoking this script. If that substitution is missing — the value is
|
|
118
|
+
empty, or still contains angle brackets (a literal ``<root>``) — treat it as
|
|
119
|
+
unset and use the default ``docs``, which is exactly the block's unset
|
|
120
|
+
behavior. This keeps the common default-config case correct even when the
|
|
121
|
+
caller forgets to substitute.
|
|
122
|
+
"""
|
|
123
|
+
if not docs_root or "<" in docs_root or ">" in docs_root:
|
|
124
|
+
return DEFAULT_DOCS_ROOT
|
|
125
|
+
return docs_root
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@functools.lru_cache(maxsize=None)
|
|
129
|
+
def repo_root() -> Path:
|
|
130
|
+
"""The repository root, matching how docs_root is resolved everywhere else.
|
|
131
|
+
|
|
132
|
+
docs_root is repo-relative (``<repo-root>/<docs_root>``), so the corpus
|
|
133
|
+
check must resolve against the git toplevel, not the current working
|
|
134
|
+
directory. ce-code-review can run from a subdirectory (``git diff`` still
|
|
135
|
+
works there), where ``Path.cwd()`` would join docs_root under the subdir and
|
|
136
|
+
wrongly report the corpus absent. Fall back to cwd when git can't answer.
|
|
137
|
+
"""
|
|
138
|
+
result = git("rev-parse", "--show-toplevel")
|
|
139
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
140
|
+
return Path(result.stdout.strip()).resolve()
|
|
141
|
+
return Path.cwd().resolve()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def has_learnings_corpus(docs_root: str | None) -> bool:
|
|
145
|
+
"""Whether a `<docs_root>/solutions` learnings corpus exists.
|
|
146
|
+
|
|
147
|
+
docs_root is the artifact root resolved by the calling skill (default
|
|
148
|
+
``docs``). Guard it the way the skill-prose rule does: normalize an
|
|
149
|
+
unset/placeholder value to the default, and treat a value that is absolute
|
|
150
|
+
or escapes the repository as absent rather than probing an out-of-repo path.
|
|
151
|
+
"""
|
|
152
|
+
docs_root = normalize_docs_root(docs_root)
|
|
153
|
+
if os.path.isabs(docs_root):
|
|
154
|
+
return False
|
|
155
|
+
repo = repo_root()
|
|
156
|
+
candidate = (repo / docs_root / "solutions").resolve()
|
|
157
|
+
if repo not in candidate.parents and candidate != repo:
|
|
158
|
+
return False
|
|
159
|
+
return candidate.is_dir()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
PACKS_RESOLVER = Path(__file__).resolve().parent / "packs-resolve.py"
|
|
163
|
+
# Parse-only mode does no git or cache work, so this bound only guards against a
|
|
164
|
+
# wedged interpreter; the helper is meant to be cheap and must never hang scope.
|
|
165
|
+
PACKS_RESOLVER_TIMEOUT = 30.0
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def declared_packs() -> tuple[bool | None, int]:
|
|
169
|
+
"""Whether the local CE config declares Compound Packs, from the config alone.
|
|
170
|
+
|
|
171
|
+
Runs the sibling resolver in `--declared-only` mode, which parses the
|
|
172
|
+
`packs:` list from both CE config layers and shape-checks each entry with no
|
|
173
|
+
git or cache work. Its `declared` is true when any entry parsed or the block
|
|
174
|
+
is malformed -- a broken declaration is still one the learnings pass must
|
|
175
|
+
surface in Coverage. ``None`` means the helper could not tell (resolver
|
|
176
|
+
missing, crashed, timed out, or answered without `declared`); the caller
|
|
177
|
+
then falls closed to reading the config's `packs:` key itself. The second
|
|
178
|
+
value keeps the `pack_roots` output slot and is always 0: nothing resolves
|
|
179
|
+
here.
|
|
180
|
+
"""
|
|
181
|
+
if not PACKS_RESOLVER.is_file():
|
|
182
|
+
return None, 0
|
|
183
|
+
try:
|
|
184
|
+
proc = subprocess.run(
|
|
185
|
+
[sys.executable, str(PACKS_RESOLVER), "--declared-only"],
|
|
186
|
+
capture_output=True,
|
|
187
|
+
text=True,
|
|
188
|
+
check=False,
|
|
189
|
+
cwd=repo_root(),
|
|
190
|
+
timeout=PACKS_RESOLVER_TIMEOUT,
|
|
191
|
+
)
|
|
192
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
193
|
+
return None, 0
|
|
194
|
+
if proc.returncode != 0:
|
|
195
|
+
return None, 0
|
|
196
|
+
try:
|
|
197
|
+
data = json.loads(proc.stdout)
|
|
198
|
+
except ValueError:
|
|
199
|
+
return None, 0
|
|
200
|
+
declared = data.get("declared") if isinstance(data, dict) else None
|
|
201
|
+
if not isinstance(declared, bool):
|
|
202
|
+
return None, 0
|
|
203
|
+
return declared, 0
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def repo_signals(docs_root: str | None, local_scope: bool) -> dict[str, object]:
|
|
207
|
+
"""Facts about the repo, not the diff: present in every result shape.
|
|
208
|
+
|
|
209
|
+
`declared_packs` describes the local checkout's config, which is not the
|
|
210
|
+
reviewed tree's config in remote scope, so it is evaluated only when
|
|
211
|
+
`local_scope` is true and reported as ``None`` otherwise.
|
|
212
|
+
"""
|
|
213
|
+
learnings_corpus = has_learnings_corpus(docs_root)
|
|
214
|
+
declared, pack_roots = declared_packs() if local_scope else (None, 0)
|
|
215
|
+
return {
|
|
216
|
+
"has_learnings_corpus": learnings_corpus,
|
|
217
|
+
"declared_packs": declared,
|
|
218
|
+
"pack_roots": pack_roots,
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def fail_closed(reason: str, signals: dict[str, object]) -> dict[str, object]:
|
|
223
|
+
return {
|
|
224
|
+
"status": "unknown",
|
|
225
|
+
"reason": reason,
|
|
226
|
+
"exec_lines": None,
|
|
227
|
+
"exec_nontest_lines": None,
|
|
228
|
+
"unclassified_lines": {},
|
|
229
|
+
"changed_lines": None,
|
|
230
|
+
"uncounted_files": 1,
|
|
231
|
+
"changed_files": [],
|
|
232
|
+
"signals": [],
|
|
233
|
+
"hard_block_classes": ["unknown-scope"],
|
|
234
|
+
"hard_block_full": True,
|
|
235
|
+
"silent_pass_classes": [],
|
|
236
|
+
"size_band": "unknown",
|
|
237
|
+
"deleted_files": [],
|
|
238
|
+
"deleted_exec_nontest_lines": None,
|
|
239
|
+
"deleted_band": "unknown",
|
|
240
|
+
"test_files_changed": False,
|
|
241
|
+
"agent_surface": False,
|
|
242
|
+
**signals,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def size_band_for(exec_nontest_lines: int | None) -> str:
|
|
247
|
+
"""Band the executable non-test lines: `large` is a full-spine floor.
|
|
248
|
+
|
|
249
|
+
Sources the extension list cannot name are not banded; they are reported
|
|
250
|
+
in `unclassified_lines` for the gate's consequence judgment.
|
|
251
|
+
"""
|
|
252
|
+
if exec_nontest_lines is None:
|
|
253
|
+
return "unknown"
|
|
254
|
+
if exec_nontest_lines >= FULL_EXEC_LINE_MIN:
|
|
255
|
+
return "large"
|
|
256
|
+
return "small"
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def deleted_band_for(deleted_files: list[str], deleted_exec_nontest_lines: int) -> str:
|
|
260
|
+
if not deleted_files:
|
|
261
|
+
return "none"
|
|
262
|
+
if deleted_exec_nontest_lines >= FULL_EXEC_LINE_MIN:
|
|
263
|
+
return "large"
|
|
264
|
+
return "small"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def matching_classes(
|
|
268
|
+
files: list[str], patterns: dict[str, re.Pattern[str]]
|
|
269
|
+
) -> list[str]:
|
|
270
|
+
return [
|
|
271
|
+
name
|
|
272
|
+
for name, pattern in patterns.items()
|
|
273
|
+
if any(pattern.search(file) for file in files)
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def numstat_path(name: str) -> str:
|
|
278
|
+
"""Return the destination path from a `git diff --numstat` rename display name."""
|
|
279
|
+
if " => " not in name:
|
|
280
|
+
return name
|
|
281
|
+
if "{" in name and "}" in name:
|
|
282
|
+
prefix, rest = name.split("{", 1)
|
|
283
|
+
old_new, suffix = rest.split("}", 1)
|
|
284
|
+
_, new = old_new.split(" => ", 1)
|
|
285
|
+
# A collapsed segment (`a/{b => }/c`) leaves an empty side, so the
|
|
286
|
+
# rebuilt path would carry `//` and miss every path-class pattern.
|
|
287
|
+
return re.sub(r"/{2,}", "/", f"{prefix}{new}{suffix}")
|
|
288
|
+
_, new = name.split(" => ", 1)
|
|
289
|
+
return new
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def main() -> int:
|
|
293
|
+
parser = argparse.ArgumentParser()
|
|
294
|
+
parser.add_argument("--base", required=True)
|
|
295
|
+
parser.add_argument("--head")
|
|
296
|
+
parser.add_argument("--docs-root", default="docs")
|
|
297
|
+
args = parser.parse_args()
|
|
298
|
+
|
|
299
|
+
# Remote scope (pr-remote / branch-remote) always passes --head, even when a
|
|
300
|
+
# best-effort fetch left it empty; the local config is not that tree's config.
|
|
301
|
+
repo = repo_signals(args.docs_root, local_scope=args.head is None)
|
|
302
|
+
|
|
303
|
+
if not valid_commit(args.base):
|
|
304
|
+
print(json.dumps(fail_closed("invalid base endpoint", repo), sort_keys=True))
|
|
305
|
+
return 0
|
|
306
|
+
if args.head is not None and not valid_commit(args.head):
|
|
307
|
+
print(json.dumps(fail_closed("invalid head endpoint", repo), sort_keys=True))
|
|
308
|
+
return 0
|
|
309
|
+
|
|
310
|
+
diff_args = [args.base]
|
|
311
|
+
if args.head:
|
|
312
|
+
merge_base = unique_merge_base(args.base, args.head)
|
|
313
|
+
if merge_base is None:
|
|
314
|
+
print(json.dumps(fail_closed("merge base unavailable or ambiguous", repo), sort_keys=True))
|
|
315
|
+
return 0
|
|
316
|
+
diff_args = [merge_base, args.head]
|
|
317
|
+
|
|
318
|
+
# Unquoted non-ASCII paths keep extension checks and manifest paths usable.
|
|
319
|
+
numstat = git("-c", "core.quotePath=false", "diff", "--numstat", *diff_args)
|
|
320
|
+
raw = git("-c", "core.quotePath=false", "diff", "--raw", *diff_args)
|
|
321
|
+
if numstat.returncode != 0 or raw.returncode != 0:
|
|
322
|
+
print(json.dumps(fail_closed("git diff failed", repo), sort_keys=True))
|
|
323
|
+
return 0
|
|
324
|
+
|
|
325
|
+
executable_mode_paths: set[str] = set()
|
|
326
|
+
deleted_paths: set[str] = set()
|
|
327
|
+
for line in raw.stdout.splitlines():
|
|
328
|
+
if "\t" not in line:
|
|
329
|
+
continue
|
|
330
|
+
meta, path_field = line.split("\t", 1)
|
|
331
|
+
fields = meta.lstrip(":").split(" ")
|
|
332
|
+
if len(fields) < 2:
|
|
333
|
+
continue
|
|
334
|
+
if fields[-1] == "D":
|
|
335
|
+
deleted_paths.add(path_field)
|
|
336
|
+
old_mode, new_mode = fields[0], fields[1]
|
|
337
|
+
mode = old_mode if new_mode == "000000" else new_mode
|
|
338
|
+
if not mode.endswith("755"):
|
|
339
|
+
continue
|
|
340
|
+
for path in path_field.split("\t"):
|
|
341
|
+
executable_mode_paths.add(path)
|
|
342
|
+
|
|
343
|
+
files: list[str] = []
|
|
344
|
+
executable_lines = 0
|
|
345
|
+
executable_nontest_lines = 0
|
|
346
|
+
unclassified_lines: dict[str, int] = {}
|
|
347
|
+
changed_lines = 0
|
|
348
|
+
uncounted = 0
|
|
349
|
+
deleted_files: list[str] = []
|
|
350
|
+
deleted_exec_nontest_lines = 0
|
|
351
|
+
for line in numstat.stdout.splitlines():
|
|
352
|
+
parts = line.split("\t")
|
|
353
|
+
if len(parts) < 3:
|
|
354
|
+
continue
|
|
355
|
+
added, deleted, name = parts[0], parts[1], parts[2]
|
|
356
|
+
resolved_name = numstat_path(name)
|
|
357
|
+
files.append(resolved_name)
|
|
358
|
+
ext = Path(resolved_name).suffix.lower()
|
|
359
|
+
is_exec = ext in CODE_EXTENSIONS or resolved_name in executable_mode_paths
|
|
360
|
+
is_test = bool(TEST_PATTERN.search(resolved_name))
|
|
361
|
+
if resolved_name in deleted_paths:
|
|
362
|
+
deleted_files.append(resolved_name)
|
|
363
|
+
if deleted.isdigit():
|
|
364
|
+
changed_lines += int(deleted)
|
|
365
|
+
if is_exec and not is_test:
|
|
366
|
+
deleted_exec_nontest_lines += int(deleted)
|
|
367
|
+
continue
|
|
368
|
+
if added == "-" or deleted == "-":
|
|
369
|
+
uncounted += 1
|
|
370
|
+
continue
|
|
371
|
+
try:
|
|
372
|
+
total = int(added) + int(deleted)
|
|
373
|
+
except ValueError:
|
|
374
|
+
uncounted += 1
|
|
375
|
+
continue
|
|
376
|
+
changed_lines += total
|
|
377
|
+
if is_exec:
|
|
378
|
+
executable_lines += total
|
|
379
|
+
if not is_test:
|
|
380
|
+
executable_nontest_lines += total
|
|
381
|
+
elif not is_test:
|
|
382
|
+
unclassified_lines[ext] = unclassified_lines.get(ext, 0) + total
|
|
383
|
+
|
|
384
|
+
files.sort()
|
|
385
|
+
deleted_files.sort()
|
|
386
|
+
signals = matching_classes(files, SIGNAL_PATTERNS)
|
|
387
|
+
hard_block_classes = matching_classes(files, HARD_BLOCK_PATTERNS)
|
|
388
|
+
silent_pass_classes = matching_classes(files, SILENT_PASS_PATTERNS)
|
|
389
|
+
if uncounted:
|
|
390
|
+
hard_block_classes.append("uncounted")
|
|
391
|
+
band = size_band_for(executable_nontest_lines)
|
|
392
|
+
|
|
393
|
+
result = {
|
|
394
|
+
"status": "complete",
|
|
395
|
+
"reason": None,
|
|
396
|
+
"exec_lines": executable_lines,
|
|
397
|
+
"exec_nontest_lines": executable_nontest_lines,
|
|
398
|
+
"unclassified_lines": unclassified_lines,
|
|
399
|
+
"changed_lines": changed_lines,
|
|
400
|
+
"uncounted_files": uncounted,
|
|
401
|
+
"changed_files": files,
|
|
402
|
+
"signals": signals,
|
|
403
|
+
"hard_block_classes": hard_block_classes,
|
|
404
|
+
"hard_block_full": bool(hard_block_classes) or band != "small",
|
|
405
|
+
"silent_pass_classes": silent_pass_classes,
|
|
406
|
+
"size_band": band,
|
|
407
|
+
"deleted_files": deleted_files,
|
|
408
|
+
"deleted_exec_nontest_lines": deleted_exec_nontest_lines,
|
|
409
|
+
"deleted_band": deleted_band_for(deleted_files, deleted_exec_nontest_lines),
|
|
410
|
+
"test_files_changed": any(TEST_PATTERN.search(file) for file in files),
|
|
411
|
+
"agent_surface": any(AGENT_SURFACE_PATTERN.search(file) for file in files),
|
|
412
|
+
**repo,
|
|
413
|
+
}
|
|
414
|
+
print(json.dumps(result, sort_keys=True))
|
|
415
|
+
return 0
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
if __name__ == "__main__":
|
|
419
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Record what a ce-code-review run cost, stage by stage.
|
|
3
|
+
|
|
4
|
+
`event` appends one JSON line per stage boundary to `<run-dir>/stages.jsonl`
|
|
5
|
+
as the run progresses, so a killed run still leaves parseable partial data.
|
|
6
|
+
`summarize` folds those lines, the run directory's artifact bytes, and any
|
|
7
|
+
peer usage file into a `cost` object merged into `<run-dir>/metadata.json`
|
|
8
|
+
without touching the fields already there. Neither subcommand ever fails
|
|
9
|
+
the run: an error leaves `cost.status` as `unavailable` with the reason.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
STAGES_FILE = "stages.jsonl"
|
|
22
|
+
METADATA_FILE = "metadata.json"
|
|
23
|
+
# Stages whose end marks the receipt being written; a run with one of these
|
|
24
|
+
# ended and no dangling start is complete.
|
|
25
|
+
RECEIPT_STAGES = {"report", "receipt"}
|
|
26
|
+
# The job directory holds detached-runner state, not review output.
|
|
27
|
+
EXCLUDED_DIRS = {"jobs"}
|
|
28
|
+
# Stages that produce candidates; merge and validate only filter them, so
|
|
29
|
+
# their counts stay per stage and never enter the total.
|
|
30
|
+
PRODUCING_STAGES = {"review", "peer", "dispatch"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def now_iso() -> str:
|
|
34
|
+
return datetime.now(timezone.utc).isoformat()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parse_iso(value: str) -> datetime | None:
|
|
38
|
+
try:
|
|
39
|
+
return datetime.fromisoformat(value)
|
|
40
|
+
except (TypeError, ValueError):
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def host_from_env() -> str:
|
|
45
|
+
"""The same attestation the cross-model reference uses, host only."""
|
|
46
|
+
env = os.environ
|
|
47
|
+
seen = [
|
|
48
|
+
host
|
|
49
|
+
for host, present in (
|
|
50
|
+
("claude", env.get("CLAUDECODE") == "1"),
|
|
51
|
+
("codex", any(env.get(k) for k in ("CODEX_SANDBOX", "CODEX_SANDBOX_NETWORK_DISABLED", "CODEX_SESSION_ID", "CODEX_THREAD_ID", "CODEX_CI"))),
|
|
52
|
+
("grok", env.get("GROK_AGENT") == "1" or bool(env.get("GROK_SESSION_ID"))),
|
|
53
|
+
)
|
|
54
|
+
if present
|
|
55
|
+
]
|
|
56
|
+
if env.get("OMPCODE"):
|
|
57
|
+
return "omp"
|
|
58
|
+
if len(seen) == 1:
|
|
59
|
+
return seen[0]
|
|
60
|
+
if seen:
|
|
61
|
+
return "unknown"
|
|
62
|
+
if env.get("CURSOR_AGENT") or env.get("CURSOR_CONVERSATION_ID"):
|
|
63
|
+
return "cursor"
|
|
64
|
+
if env.get("OPENCODE_TERMINAL"):
|
|
65
|
+
return "opencode"
|
|
66
|
+
return "unknown"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def parse_facts(items: list[str] | None) -> dict[str, object]:
|
|
70
|
+
facts: dict[str, object] = {}
|
|
71
|
+
for item in items or []:
|
|
72
|
+
key, sep, raw = item.partition("=")
|
|
73
|
+
if not sep or not key:
|
|
74
|
+
continue
|
|
75
|
+
try:
|
|
76
|
+
facts[key] = json.loads(raw)
|
|
77
|
+
except ValueError:
|
|
78
|
+
facts[key] = raw
|
|
79
|
+
return facts
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def append_line(run_dir: Path, record: dict[str, object]) -> None:
|
|
83
|
+
run_dir.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
line = json.dumps(record, sort_keys=True) + "\n"
|
|
85
|
+
with (run_dir / STAGES_FILE).open("a", encoding="utf-8") as handle:
|
|
86
|
+
handle.write(line)
|
|
87
|
+
handle.flush()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def cmd_event(args: argparse.Namespace) -> int:
|
|
91
|
+
run_dir = Path(args.run_dir)
|
|
92
|
+
if not args.end and not args.start:
|
|
93
|
+
print("run-log event: nothing to record; pass --end and/or --start", file=sys.stderr)
|
|
94
|
+
return 0
|
|
95
|
+
ts = now_iso()
|
|
96
|
+
extras: dict[str, object] = {}
|
|
97
|
+
if args.reviewers is not None:
|
|
98
|
+
extras["reviewers"] = args.reviewers
|
|
99
|
+
if args.candidates is not None:
|
|
100
|
+
extras["candidates"] = args.candidates
|
|
101
|
+
if args.tokens is not None:
|
|
102
|
+
extras["tokens"] = args.tokens
|
|
103
|
+
if args.note:
|
|
104
|
+
extras["note"] = args.note
|
|
105
|
+
facts = parse_facts(args.fact)
|
|
106
|
+
if facts:
|
|
107
|
+
extras["facts"] = facts
|
|
108
|
+
written = []
|
|
109
|
+
if args.end:
|
|
110
|
+
record = {"ts": ts, "stage": args.end, "phase": "end", **extras}
|
|
111
|
+
append_line(run_dir, record)
|
|
112
|
+
written.append(record)
|
|
113
|
+
if args.start:
|
|
114
|
+
record = {"ts": ts, "stage": args.start, "phase": "start"}
|
|
115
|
+
# Facts and counts describe the stage that just ended; a lone --start
|
|
116
|
+
# carries them so the scope stage can record its helper facts.
|
|
117
|
+
if not args.end:
|
|
118
|
+
record.update(extras)
|
|
119
|
+
append_line(run_dir, record)
|
|
120
|
+
written.append(record)
|
|
121
|
+
print(json.dumps({"written": written}, sort_keys=True))
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def read_events(run_dir: Path) -> tuple[list[dict[str, object]], int]:
|
|
126
|
+
path = run_dir / STAGES_FILE
|
|
127
|
+
if not path.is_file():
|
|
128
|
+
raise FileNotFoundError(str(path))
|
|
129
|
+
events: list[dict[str, object]] = []
|
|
130
|
+
truncated = 0
|
|
131
|
+
with path.open("r", encoding="utf-8") as handle:
|
|
132
|
+
for raw in handle:
|
|
133
|
+
line = raw.strip()
|
|
134
|
+
if not line:
|
|
135
|
+
continue
|
|
136
|
+
try:
|
|
137
|
+
parsed = json.loads(line)
|
|
138
|
+
except ValueError:
|
|
139
|
+
truncated += 1
|
|
140
|
+
continue
|
|
141
|
+
if isinstance(parsed, dict) and "stage" in parsed and "phase" in parsed:
|
|
142
|
+
events.append(parsed)
|
|
143
|
+
else:
|
|
144
|
+
truncated += 1
|
|
145
|
+
return events, truncated
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def artifact_bytes(run_dir: Path) -> int:
|
|
149
|
+
total = 0
|
|
150
|
+
for root, dirs, files in os.walk(run_dir):
|
|
151
|
+
dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS]
|
|
152
|
+
for name in files:
|
|
153
|
+
try:
|
|
154
|
+
total += (Path(root) / name).stat().st_size
|
|
155
|
+
except OSError:
|
|
156
|
+
continue
|
|
157
|
+
return total
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def peer_usage(run_dir: Path) -> dict[str, object] | None:
|
|
161
|
+
for path in sorted(run_dir.glob("adversarial-*-usage.json")):
|
|
162
|
+
try:
|
|
163
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
164
|
+
except (OSError, ValueError):
|
|
165
|
+
continue
|
|
166
|
+
if isinstance(data, dict):
|
|
167
|
+
provider = path.name[len("adversarial-"):-len("-usage.json")]
|
|
168
|
+
return {"provider": provider, **data}
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def build_cost(run_dir: Path) -> dict[str, object]:
|
|
173
|
+
events, truncated = read_events(run_dir)
|
|
174
|
+
stages: list[dict[str, object]] = []
|
|
175
|
+
order: list[str] = []
|
|
176
|
+
open_starts: dict[str, dict[str, object]] = {}
|
|
177
|
+
by_name: dict[str, dict[str, object]] = {}
|
|
178
|
+
scope_facts: dict[str, object] = {}
|
|
179
|
+
for event in events:
|
|
180
|
+
name = str(event["stage"])
|
|
181
|
+
if name not in by_name:
|
|
182
|
+
by_name[name] = {"stage": name}
|
|
183
|
+
order.append(name)
|
|
184
|
+
entry = by_name[name]
|
|
185
|
+
if event["phase"] == "start":
|
|
186
|
+
open_starts[name] = event
|
|
187
|
+
entry["started_at"] = event.get("ts")
|
|
188
|
+
elif event["phase"] == "end":
|
|
189
|
+
entry["ended_at"] = event.get("ts")
|
|
190
|
+
start = open_starts.pop(name, None)
|
|
191
|
+
if start is not None:
|
|
192
|
+
t0, t1 = parse_iso(str(start.get("ts"))), parse_iso(str(event.get("ts")))
|
|
193
|
+
if t0 and t1:
|
|
194
|
+
entry["elapsed_seconds"] = round((t1 - t0).total_seconds(), 3)
|
|
195
|
+
for key in ("reviewers", "candidates", "tokens", "note"):
|
|
196
|
+
if key in event:
|
|
197
|
+
entry[key] = event[key]
|
|
198
|
+
facts = event.get("facts")
|
|
199
|
+
if isinstance(facts, dict):
|
|
200
|
+
entry.setdefault("facts", {}).update(facts)
|
|
201
|
+
if name == "scope":
|
|
202
|
+
scope_facts.update(facts)
|
|
203
|
+
stages = [by_name[name] for name in order]
|
|
204
|
+
dangling = sorted(open_starts)
|
|
205
|
+
receipt_done = any(s.get("ended_at") for s in stages if s["stage"] in RECEIPT_STAGES)
|
|
206
|
+
status = "complete" if receipt_done and not dangling else "partial"
|
|
207
|
+
totals = {
|
|
208
|
+
"elapsed_seconds": round(sum(float(s.get("elapsed_seconds", 0) or 0) for s in stages), 3),
|
|
209
|
+
"reviewers": sum(int(s.get("reviewers", 0) or 0) for s in stages),
|
|
210
|
+
"candidates": sum(int(s.get("candidates", 0) or 0) for s in stages if s["stage"] in PRODUCING_STAGES),
|
|
211
|
+
"tokens": sum(int(s.get("tokens", 0) or 0) for s in stages if "tokens" in s),
|
|
212
|
+
"artifact_bytes": artifact_bytes(run_dir),
|
|
213
|
+
}
|
|
214
|
+
if not any("tokens" in s for s in stages):
|
|
215
|
+
del totals["tokens"]
|
|
216
|
+
cost: dict[str, object] = {
|
|
217
|
+
"status": status,
|
|
218
|
+
"host": host_from_env(),
|
|
219
|
+
"stages": stages,
|
|
220
|
+
"totals": totals,
|
|
221
|
+
"truncated_events": truncated,
|
|
222
|
+
}
|
|
223
|
+
if dangling:
|
|
224
|
+
cost["dangling_stages"] = dangling
|
|
225
|
+
if scope_facts:
|
|
226
|
+
cost["scope"] = scope_facts
|
|
227
|
+
peer = peer_usage(run_dir)
|
|
228
|
+
if peer is not None:
|
|
229
|
+
cost["peer"] = peer
|
|
230
|
+
return cost
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def cmd_summarize(args: argparse.Namespace) -> int:
|
|
234
|
+
run_dir = Path(args.run_dir)
|
|
235
|
+
try:
|
|
236
|
+
cost = build_cost(run_dir)
|
|
237
|
+
except FileNotFoundError as exc:
|
|
238
|
+
cost = {"status": "unavailable", "reason": f"no stage log: {exc}"}
|
|
239
|
+
except OSError as exc:
|
|
240
|
+
cost = {"status": "unavailable", "reason": str(exc)}
|
|
241
|
+
metadata_path = run_dir / METADATA_FILE
|
|
242
|
+
metadata: dict[str, object] = {}
|
|
243
|
+
if metadata_path.is_file():
|
|
244
|
+
try:
|
|
245
|
+
loaded = json.loads(metadata_path.read_text(encoding="utf-8"))
|
|
246
|
+
if isinstance(loaded, dict):
|
|
247
|
+
metadata = loaded
|
|
248
|
+
except (OSError, ValueError):
|
|
249
|
+
cost.setdefault("notes", []).append("metadata.json was unreadable and was rewritten")
|
|
250
|
+
metadata["cost"] = cost
|
|
251
|
+
run_dir.mkdir(parents=True, exist_ok=True)
|
|
252
|
+
tmp = metadata_path.with_suffix(".json.tmp")
|
|
253
|
+
tmp.write_text(json.dumps(metadata, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
254
|
+
os.replace(tmp, metadata_path)
|
|
255
|
+
print(json.dumps({"status": cost.get("status"), "metadata": str(metadata_path)}, sort_keys=True))
|
|
256
|
+
return 0
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def main() -> int:
|
|
260
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
261
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
262
|
+
ev = sub.add_parser("event", help="record a stage boundary")
|
|
263
|
+
ev.add_argument("--run-dir", required=True)
|
|
264
|
+
ev.add_argument("--end", help="stage that just ended")
|
|
265
|
+
ev.add_argument("--start", help="stage that starts now")
|
|
266
|
+
ev.add_argument("--reviewers", type=int)
|
|
267
|
+
ev.add_argument("--candidates", type=int)
|
|
268
|
+
ev.add_argument("--tokens", type=int)
|
|
269
|
+
ev.add_argument("--note")
|
|
270
|
+
ev.add_argument("--fact", action="append", help="key=value (value parsed as JSON when possible)")
|
|
271
|
+
ev.set_defaults(func=cmd_event)
|
|
272
|
+
sm = sub.add_parser("summarize", help="fold the stage log into metadata.json")
|
|
273
|
+
sm.add_argument("--run-dir", required=True)
|
|
274
|
+
sm.set_defaults(func=cmd_summarize)
|
|
275
|
+
args = parser.parse_args()
|
|
276
|
+
return args.func(args)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
if __name__ == "__main__":
|
|
280
|
+
raise SystemExit(main())
|