syncade 0.6.2__py3-none-any.whl

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.
Files changed (177) hide show
  1. syncade/__init__.py +3 -0
  2. syncade/__main__.py +6 -0
  3. syncade/adapters/__init__.py +0 -0
  4. syncade/adapters/anthropic.py +457 -0
  5. syncade/adapters/base.py +221 -0
  6. syncade/adapters/fake.py +73 -0
  7. syncade/adapters/fake_common.py +29 -0
  8. syncade/adapters/fake_producer_audit_draft.py +460 -0
  9. syncade/adapters/fake_reviewer_synth.py +310 -0
  10. syncade/adapters/openai.py +484 -0
  11. syncade/adapters/openai_parsing.py +119 -0
  12. syncade/adapters/producer.py +221 -0
  13. syncade/adapters/producer_anthropic.py +300 -0
  14. syncade/adapters/producer_openai.py +226 -0
  15. syncade/adapters/registry.py +81 -0
  16. syncade/auth_check.py +554 -0
  17. syncade/auth_preflight.py +342 -0
  18. syncade/base_resolution.py +214 -0
  19. syncade/billing.py +141 -0
  20. syncade/checks_config.py +113 -0
  21. syncade/cli/__init__.py +546 -0
  22. syncade/cli/auth_gate.py +59 -0
  23. syncade/cli/config_keys.py +135 -0
  24. syncade/cli/config_list.py +82 -0
  25. syncade/cli/config_menu_rows.py +166 -0
  26. syncade/cli/config_mode.py +609 -0
  27. syncade/cli/config_overrides.py +122 -0
  28. syncade/cli/config_tui.py +476 -0
  29. syncade/cli/doctor_mode.py +72 -0
  30. syncade/cli/gc_mode.py +109 -0
  31. syncade/cli/install_skill.py +514 -0
  32. syncade/cli/metrics_mode.py +363 -0
  33. syncade/cli/modes.py +573 -0
  34. syncade/cli/parser.py +450 -0
  35. syncade/cli/parser_types.py +137 -0
  36. syncade/cli/paths.py +38 -0
  37. syncade/cli/preflight_paths.py +90 -0
  38. syncade/cli/resolve.py +116 -0
  39. syncade/cli/resume_mode.py +324 -0
  40. syncade/cli/toml_writer.py +410 -0
  41. syncade/cli/validate.py +421 -0
  42. syncade/config.py +478 -0
  43. syncade/config_auth.py +310 -0
  44. syncade/config_cold.py +209 -0
  45. syncade/config_gc.py +55 -0
  46. syncade/config_loader.py +182 -0
  47. syncade/config_loop.py +282 -0
  48. syncade/config_producer.py +222 -0
  49. syncade/config_retry.py +49 -0
  50. syncade/config_types.py +59 -0
  51. syncade/diff_filter.py +437 -0
  52. syncade/dispatcher.py +571 -0
  53. syncade/doctor.py +425 -0
  54. syncade/doctor_env.py +218 -0
  55. syncade/doctor_preview.py +524 -0
  56. syncade/doctor_types.py +28 -0
  57. syncade/exit_codes.py +82 -0
  58. syncade/findings.py +242 -0
  59. syncade/findings_json.py +456 -0
  60. syncade/gc.py +211 -0
  61. syncade/gc_execute.py +372 -0
  62. syncade/gc_protection.py +129 -0
  63. syncade/gc_types.py +50 -0
  64. syncade/gc_worktrees.py +200 -0
  65. syncade/git_object_id.py +12 -0
  66. syncade/git_preconditions.py +389 -0
  67. syncade/logging.py +289 -0
  68. syncade/metrics/__init__.py +32 -0
  69. syncade/metrics/aggregate.py +550 -0
  70. syncade/metrics/schema.py +221 -0
  71. syncade/orchestrator/__init__.py +61 -0
  72. syncade/orchestrator/_runs_dir.py +24 -0
  73. syncade/orchestrator/branch_advance.py +165 -0
  74. syncade/orchestrator/branch_guard.py +98 -0
  75. syncade/orchestrator/budget.py +107 -0
  76. syncade/orchestrator/escalation_coverage.py +81 -0
  77. syncade/orchestrator/loop.py +611 -0
  78. syncade/orchestrator/loop_dispatch_check.py +112 -0
  79. syncade/orchestrator/loop_finalize.py +404 -0
  80. syncade/orchestrator/loop_preflight.py +131 -0
  81. syncade/orchestrator/loop_resume.py +91 -0
  82. syncade/orchestrator/loop_rmtree.py +70 -0
  83. syncade/orchestrator/loop_round_step.py +599 -0
  84. syncade/orchestrator/prior_round.py +336 -0
  85. syncade/orchestrator/producer_phase.py +169 -0
  86. syncade/orchestrator/results.py +306 -0
  87. syncade/orchestrator/resume.py +96 -0
  88. syncade/orchestrator/resume_load.py +483 -0
  89. syncade/orchestrator/resume_plan.py +554 -0
  90. syncade/orchestrator/resume_target.py +215 -0
  91. syncade/orchestrator/resume_types.py +182 -0
  92. syncade/orchestrator/reviewer_template_failure.py +99 -0
  93. syncade/orchestrator/round.py +573 -0
  94. syncade/orchestrator/round_checks.py +91 -0
  95. syncade/orchestrator/round_no_changes.py +369 -0
  96. syncade/orchestrator/round_predispatch.py +212 -0
  97. syncade/orchestrator/verdict.py +279 -0
  98. syncade/persistence/__init__.py +189 -0
  99. syncade/persistence/_atomic.py +33 -0
  100. syncade/persistence/_clusters.py +70 -0
  101. syncade/persistence/_findings_verdict.py +201 -0
  102. syncade/persistence/_markdown.py +286 -0
  103. syncade/persistence/_validation.py +37 -0
  104. syncade/persistence/checks.py +249 -0
  105. syncade/persistence/decision_needed.py +289 -0
  106. syncade/persistence/findings_md.py +389 -0
  107. syncade/persistence/handoff.py +389 -0
  108. syncade/persistence/handoff_classify.py +196 -0
  109. syncade/persistence/last_reviewed.py +67 -0
  110. syncade/persistence/loop_manifest.py +165 -0
  111. syncade/persistence/loop_summary.py +352 -0
  112. syncade/persistence/loop_summary_text.py +428 -0
  113. syncade/persistence/producer.py +250 -0
  114. syncade/persistence/reviewer.py +198 -0
  115. syncade/persistence/round_manifest.py +238 -0
  116. syncade/persistence/run_init.py +153 -0
  117. syncade/persistence/run_summary.py +585 -0
  118. syncade/persistence/run_summary_next_steps.py +443 -0
  119. syncade/persistence/synth.py +242 -0
  120. syncade/persistence/test_run.py +152 -0
  121. syncade/presets.py +36 -0
  122. syncade/pricing_config.py +72 -0
  123. syncade/process.py +600 -0
  124. syncade/producer.py +189 -0
  125. syncade/producer_attempt.py +463 -0
  126. syncade/producer_escalation.py +146 -0
  127. syncade/producer_git.py +199 -0
  128. syncade/producer_result.py +205 -0
  129. syncade/prompts.py +448 -0
  130. syncade/prompts_loader.py +238 -0
  131. syncade/retry.py +159 -0
  132. syncade/run_inputs.py +40 -0
  133. syncade/run_status.py +198 -0
  134. syncade/selfcheck.py +471 -0
  135. syncade/skills/claude/README.md +221 -0
  136. syncade/skills/claude/SKILL.md +625 -0
  137. syncade/skills/codex/README.md +116 -0
  138. syncade/skills/codex/SKILL.md +574 -0
  139. syncade/snapshot.py +598 -0
  140. syncade/spec_audit.py +437 -0
  141. syncade/spec_audit_schema.py +190 -0
  142. syncade/spec_draft.py +423 -0
  143. syncade/spec_source.py +135 -0
  144. syncade/synthesis.py +428 -0
  145. syncade/synthesis_clusters.py +203 -0
  146. syncade/synthesis_repair.py +230 -0
  147. syncade/synthesis_schema.py +65 -0
  148. syncade/synthesizer/__init__.py +38 -0
  149. syncade/synthesizer/constants.py +33 -0
  150. syncade/synthesizer/driver.py +531 -0
  151. syncade/synthesizer/rendering.py +63 -0
  152. syncade/synthesizer/result.py +73 -0
  153. syncade/synthesizer/validation.py +421 -0
  154. syncade/synthesizer/workspace.py +208 -0
  155. syncade/templates/presets/balanced.toml +13 -0
  156. syncade/templates/presets/cheap.toml +12 -0
  157. syncade/templates/presets/thorough.toml +9 -0
  158. syncade/templates/producer.md +231 -0
  159. syncade/templates/reviewer.md +279 -0
  160. syncade/templates/reviewer_adversarial.md +164 -0
  161. syncade/templates/reviewer_codex.md +165 -0
  162. syncade/templates/spec_audit.md +168 -0
  163. syncade/templates/spec_draft.md +62 -0
  164. syncade/templates/synthesizer.md +204 -0
  165. syncade/test_runner.py +476 -0
  166. syncade/test_runner_classify.py +98 -0
  167. syncade/transcript.py +150 -0
  168. syncade/usage.py +407 -0
  169. syncade/worktree.py +497 -0
  170. syncade/worktree_env.py +133 -0
  171. syncade/worktree_paths.py +139 -0
  172. syncade-0.6.2.dist-info/METADATA +314 -0
  173. syncade-0.6.2.dist-info/RECORD +177 -0
  174. syncade-0.6.2.dist-info/WHEEL +5 -0
  175. syncade-0.6.2.dist-info/entry_points.txt +2 -0
  176. syncade-0.6.2.dist-info/licenses/LICENSE +202 -0
  177. syncade-0.6.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,221 @@
1
+ # syncade Claude Code skill
2
+
3
+ Bridge between Claude Code's slash-command interface and the syncade
4
+ external review orchestrator. Operators type `/syncade <pr-doc>` from
5
+ inside Claude Code; this skill orchestrates the safety check
6
+ + confirmation gate + the actual review loop, then summarizes inline.
7
+
8
+ ## Quick start
9
+
10
+ ```
11
+ /syncade path/to/pr.md
12
+ /syncade review path/to/pr.md for 2 rounds against main
13
+ /syncade dogfood a PR single pass
14
+ ```
15
+
16
+ The skill also handles **configuration** without running a review — "/syncade config",
17
+ "change my producer to gpt-5", "set rounds to 2", "show my config" — rendering a config
18
+ **menu** (`syncade --config list --all`) you browse and edit via
19
+ `syncade --config set <key> <value>` (writes the global `~/.syncade/config.toml`, or the
20
+ repo's with `--repo`). See "Configuring syncade" in the SKILL.
21
+
22
+ Both the structured form and clear natural language work: the skill
23
+ maps your request onto the CLI shape `syncade [--openspec [ID] | PR_DOC]
24
+ [--base REF | --scope SCOPE] [--max-rounds N]` — at the skill (markdown) layer
25
+ only; the skill adds no Python of its own (PR-B added `--scope`, PR-C added
26
+ `--openspec`, the flags it targets). The interactive Claude will:
27
+
28
+ 0. **Resolve intent** — turn natural language into an exact command. It asks
29
+ one short question if the target / round count / base is ambiguous, and
30
+ **maps scope phrases** ("review what I just did" → `--scope local`,
31
+ "everything" → `--scope everything`, "since last review" →
32
+ `--scope since-last-review`) onto the `--scope` flag (PR-B); Python derives
33
+ the concrete base, the skill never hand-picks one.
34
+ 1. Validate the resolved PR doc is readable (and any explicit base ref).
35
+ 2. Run `syncade --auth-check` (~5–10 s safety check).
36
+ 3. Run `syncade --selfcheck` (~30 s safety check).
37
+ 4. Print the safety-check result + the EXACT resolved command + expected
38
+ timing and ask you to confirm with `go` / `cancel`. After the safety check
39
+ the skill goes straight to the reviewer loop — no brief-check, no automatic
40
+ spec-audit in between.
41
+ 5. On `go`, run the resolved command and stream output to chat.
42
+ 6. Read `<run-dir>/loop-summary.md` and print the verdict + round
43
+ count + finding count.
44
+ 7. If the producer committed, list each commit with its short SHA +
45
+ subject and a `git reset --hard <round-0-sha>` rollback pointer.
46
+
47
+ ### Natural-language phrasings the skill understands
48
+
49
+ - **Round count:** `for 1 round`, `for 2 rounds`, `max rounds 3`, `single pass`
50
+ (= 1 round). Valid: 1–10. Omit it and your config's `max_rounds` stays
51
+ authoritative.
52
+ - **Base ref:** `against main`, `from origin/main`, `base HEAD~3`, or literal
53
+ `--base <ref>` (validated with `git rev-parse` before running). Omit it and
54
+ current CLI behavior stays authoritative.
55
+ - **Scope (PR-B):** `everything` (the branch point off the default branch),
56
+ `what I just did` / `my recent changes` (`--scope local` — local-ahead
57
+ commits vs the branch's upstream), `since last review` / `what's new`
58
+ (`--scope since-last-review` — the recorded last-reviewed SHA for this
59
+ branch). Mutually exclusive with an explicit base ref; an explicit ref wins.
60
+ `local` with no upstream and `since-last-review` with no prior record fall
61
+ back to the branch point with a one-line note rather than erroring.
62
+ - **OpenSpec (PR-C):** `review the openspec change <id>` / `run syncade on my
63
+ openspec proposal` → `--openspec <id>` (or bare `--openspec` to auto-resolve a
64
+ single active change). syncade assembles `openspec/changes/<id>/` (proposal +
65
+ spec deltas) into the spec it reviews against — reading the markdown directly,
66
+ no `openspec` binary needed. Replaces the PR-doc path (one spec source);
67
+ `--base`/`--scope` still set the diff base.
68
+ - **Draft-spec / tier C (PR-D + PR-E):** no brief at all? `I didn't write a spec,
69
+ review what we did` / `draft a spec from this session`. A cold drafter reads the
70
+ session transcript for *intent* (the skill resolves the transcript path; it never
71
+ summarizes the conversation itself) and writes a draft spec with an "Assumptions
72
+ to confirm" section. **PR-E ties this into one pane:** syncade drafts → you
73
+ **ratify it right here in chat** (it shows the draft + the flagged assumptions
74
+ verbatim and asks "what did I miss?"; your corrections edit the spec) → the blind
75
+ review loop runs — no second command. A manufactured spec is never reviewed
76
+ against unratified.
77
+ - **Target:** an explicit path always wins; a PR number / `this PR` resolve only
78
+ when exactly one readable PR doc matches, else the skill asks.
79
+
80
+ ## Prerequisites
81
+
82
+ - `claude` CLI installed and authenticated (`claude --version` works).
83
+ - `codex` CLI installed and authenticated (`codex login status`
84
+ reports "Logged in...").
85
+ - `syncade` installed and on `PATH` (`syncade --version` works from any
86
+ directory — see the repo README's "Global install").
87
+ - macOS or Linux (the skill assumes `bash` / `zsh`; Windows-WSL is
88
+ fine the same way running `syncade` manually is).
89
+
90
+ ## When to use vs. terminal
91
+
92
+ | Situation | Skill | Terminal |
93
+ |---|---|---|
94
+ | First syncade run on a PR brief | yes — gets you pre-flight diagnostics + confirmation gate | works, but you'll want to run `--auth-check` and `--selfcheck` yourself |
95
+ | Repeat dogfood after a code change | yes — same pre-flights catch token rotation cheaply | yes if you've already verified your auth recently |
96
+ | You only want auth diagnostics, not a review | no — terminal: `syncade --auth-check` is ~5 s | yes |
97
+ | You only want producer-commit diagnostics | no — terminal: `syncade --selfcheck` is ~30 s | yes |
98
+ | You only want a spec audit of the brief | no — terminal: `syncade --spec-audit <pr-doc>` (manual; not part of the skill flow) is ~30–90 s | yes |
99
+ | Resuming a prior run | terminal: `syncade --resume <run-id>` | yes |
100
+
101
+ The skill is opt-in. Operators running `syncade <pr-doc>` from a
102
+ terminal still get the same loop; they just don't get the pre-flight
103
+ + confirmation gate.
104
+
105
+ ## Failure modes
106
+
107
+ ### Auth-check fails (step 2)
108
+
109
+ The skill stops. Output shows which provider failed and the
110
+ remediation:
111
+
112
+ - `[auth-check] anthropic: FAILED (...). Run 'claude' interactively to re-authenticate.`
113
+ - `[auth-check] openai: FAILED (...). Run 'codex login' to re-authenticate.`
114
+
115
+ Fix the named provider and re-run `/syncade <pr-doc>`.
116
+
117
+ ### Selfcheck fails (step 3)
118
+
119
+ The skill stops. Common causes:
120
+
121
+ - **Producer stalled** — the producer edited files but never
122
+ committed, so the round has nothing to accept. This is not a
123
+ permissions misconfiguration: `[producer] permissions` accepts
124
+ **only** `"yolo"` (schema-enforced), precisely because sandboxed
125
+ modes cannot commit headlessly — claude's `acceptEdits` prompts for
126
+ bash, and codex's `workspace-write` blocks `.git/index.lock` writes.
127
+ Look at the producer's own output instead: it usually means the model
128
+ described a fix without running `git commit`.
129
+ - **Producer subprocess error** — the producer CLI itself failed.
130
+ The selfcheck workspace is preserved; the path is the last stderr
131
+ line. `cat <workspace>/round-0/producer.error.txt` for the cause.
132
+ - **Producer committed but didn't add the marker** — the producer's
133
+ prompt template may be broken. Inspect
134
+ `<workspace>/round-0/producer.stdout` to see what it actually did.
135
+
136
+ ### Operator cancels at step 4
137
+
138
+ The skill prints `[syncade] cancelled at operator confirmation` and
139
+ exits. Nothing was modified.
140
+
141
+ ### `syncade <pr-doc>` fails mid-loop (step 5)
142
+
143
+ The skill still tries to read `loop-summary.md`. If it exists, the
144
+ verdict is presented; if not, the skill points at the highest-numbered
145
+ round dir under `<run-dir>/`. Common exit codes:
146
+
147
+ | Exit | Meaning | Where to look |
148
+ |---|---|---|
149
+ | 20 | max rounds reached, no SHIP | every round's `findings.md` |
150
+ | 25 | stopped at a phase boundary — YOUR budget ceiling, or the PROVIDER's usage limit (the summary says which) | resume with `syncade --resume`; raise the limit if it was yours, wait for the window if it was the provider's |
151
+ | 30 | non-dismissed blocker, or producer stalled, or tests failed | last round's `findings.md` or `test-run.stdout` |
152
+ | 40 | reviewer/synthesizer/producer subprocess error | last round's `<provider>.stderr` or `producer.error.txt` |
153
+ | 50 | config error | the message says exactly which TOML field |
154
+ | 60 | worktree error or dirty-tree refusal | the message names the cause |
155
+ | 70 | output unparseable | `<reviewer>.stdout` for the actual reviewer output |
156
+
157
+ Full table at the exit-code table.
158
+
159
+ ## Producer commits on your branch
160
+
161
+ When `max_rounds > 1` (the default), the producer can commit to the
162
+ current branch between rounds. The blast radius:
163
+
164
+ - **Each producer commit lands on the operator's branch**, not on a
165
+ separate branch. Reviewer worktrees share `.git`, so the next
166
+ round's reviewers see the new commit.
167
+ - **Branch advance is fast-forward only.** If the producer makes an
168
+ orphan commit (which shouldn't happen — the producer's prompt
169
+ requests a real commit on top of HEAD), the orchestrator refuses
170
+ to advance the branch and exits 30 with `producer_stalled`.
171
+ - **Step 7 of the skill surfaces every commit** with its SHA + subject
172
+ before declaring the run done. Inspect each via `git show <sha>`
173
+ before merging.
174
+ - **Rollback is `git reset --hard <round-0-starting-sha>`**. The
175
+ starting SHA is in `<run-dir>/loop-manifest.json` at
176
+ `rounds[0].snapshot.commit_sha`. The skill prints this command in
177
+ step 7; you don't have to look it up.
178
+
179
+ If you want to keep some producer commits and reject others, use
180
+ `git reset --hard <round-0-sha>` + `git cherry-pick <sha>` for the
181
+ ones you keep, or `git revert <sha>` to invert specific ones in
182
+ place.
183
+
184
+ ## What the skill does not do
185
+
186
+ - Doesn't reimplement syncade. It's a Bash wrapper over the syncade
187
+ CLI; everything the skill does is also doable from a terminal.
188
+ - Doesn't review code itself. The interactive Claude reading this
189
+ skill is the operator's UI, not a syncade reviewer. The reviewer
190
+ subprocesses (`claude -p`, `codex exec`) run with no shared
191
+ context with this session.
192
+
193
+ ## Where this skill lives
194
+
195
+ `.claude/skills/syncade/` in the syncade repo, so `/syncade` is available only
196
+ when you work inside this repo.
197
+
198
+ **To use it in any project**, install it at the user level with one command:
199
+
200
+ ```bash
201
+ syncade --install-skill claude # copies it to ~/.claude/skills/syncade/
202
+ ```
203
+
204
+ This works from any installed copy of syncade (the skill ships inside the
205
+ package) — `pip install .` from a checkout, or `pip install git+<repo-url>`.
206
+ syncade is not published on PyPI. Skills load at session start, so restart Claude Code (or open a session
207
+ in the target project) to pick it up. The `syncade` CLI it shells out to must be on
208
+ your `PATH`. Developers who want the installed skill to track a checkout can
209
+ `ln -sfn "$PWD/.claude/skills/syncade" ~/.claude/skills/syncade` instead.
210
+
211
+ ## Codex counterpart (single source)
212
+
213
+ There is a sibling Codex skill at `.codex/skills/syncade/` (install into
214
+ `~/.codex/skills/`). It is the **same** skill: the load-bearing Step 2 →
215
+ Invariants workflow span is shared and kept byte-identical by
216
+ `tests/skills/test_skill_drift.py` — the two copies are wrapped in
217
+ `<!-- SYNCADE-SHARED:start/end -->` markers, and **any edit to the shared span
218
+ must be made in both copies** or the drift test fails. Only the harness-specific
219
+ head differs (invocation phrasing; Claude's tier-C draft-from-session vs the
220
+ Codex copy's graceful degrade, which lands draft-from-session in a follow-up; and
221
+ `CLAUDE.md` vs `AGENTS.md`). See the Codex skill format and `path/to/pr.md` for the brief.