rollout-cli 0.4.1__tar.gz → 0.4.2__tar.gz
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.
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/ask-colleague/SKILL.md +36 -14
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +101 -36
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/CHANGELOG.md +6 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/PKG-INFO +1 -1
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/pyproject.toml +1 -1
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/apply.sh +6 -6
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/check.sh +1 -1
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/files/SKILL.md.tmpl +5 -1
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/files/scripts/ask-colleague.sh +101 -36
- rollout_cli-0.4.2/recipes/ask-colleague-revendor/manifest.toml +27 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/uv.lock +1 -1
- rollout_cli-0.4.1/recipes/ask-colleague-revendor/manifest.toml +0 -25
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/agent-config/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/agent-config/scripts/show.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/ask-colleague/prompts/review.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/ask-colleague/prompts/write.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/cicd/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/cicd/scripts/workflow.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/communicate/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/mass-update/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/mass-update/scripts/mass-update.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/run-tests/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/sonarclaude/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/spec-to-plan/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/think/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/think/scripts/think.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/version-bump/SKILL.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/version-bump/scripts/bump.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills.local.yaml.example +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.devague/frames/mass-update-now-clones-each-target-repo-into-a-git.json +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.devague/frames/rollout-cli-ships-rollout-define-an-org-wide-chang.json +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.devague/plans/mass-update-now-clones-each-target-repo-into-a-git.json +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.devague/plans/rollout-cli-ships-rollout-define-an-org-wide-chang.json +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.flake8 +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.github/workflows/publish.yml +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.github/workflows/tests.yml +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.gitignore +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/.markdownlint-cli2.yaml +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/AGENTS.colleague.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/CLAUDE.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/LICENSE +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/README.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/culture.yaml +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/docs/plans/2026-06-06-rollout-cli-ships-rollout-define-an-org-wide-chang.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/docs/plans/2026-06-12-mass-update-now-clones-each-target-repo-into-a-git.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/docs/skill-sources.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/docs/specs/2026-06-06-rollout-cli-ships-rollout-define-an-org-wide-chang.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/docs/specs/2026-06-12-mass-update-now-clones-each-target-repo-into-a-git.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/apache-relicense/apply.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/apache-relicense/check.sh +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/apache-relicense/files/LICENSE +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/apache-relicense/manifest.toml +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/README.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/files/prompts/explore.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/files/prompts/review.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/files/prompts/write.md +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/__init__.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/__main__.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/__init__.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/__init__.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/apply.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/cli.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/doctor.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/explain.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/learn.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/overview.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/plan.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_commands/whoami.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_errors.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/cli/_output.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/__init__.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/bump.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/devex_pr.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/gitops.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/orchestrate.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/preflight.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/recipe.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/report.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/core/targets.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/explain/__init__.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/rollout/explain/catalog.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/sonar-project.properties +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/__init__.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_apache_relicense.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_apply_cmd.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_bump.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_cli.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_cli_introspection.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_cli_wiring.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_devex_pr.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_e2e_apache_relicense.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_explain_catalog.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_gitops.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_mass_update.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_orchestrate.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_plan_cmd.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_preflight.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_recipe.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_report.py +0 -0
- {rollout_cli-0.4.1 → rollout_cli-0.4.2}/tests/test_targets.py +0 -0
|
@@ -7,18 +7,20 @@ description: >
|
|
|
7
7
|
isn't a stronger model; it's a second, independent mind, and that diversity is
|
|
8
8
|
the value: `ask-colleague review` gets a candid second opinion on a diff,
|
|
9
9
|
`ask-colleague explore` gets a fresh read of an area, `ask-colleague write`
|
|
10
|
-
hands off a small implementation,
|
|
11
|
-
work item (the ROI loop)
|
|
10
|
+
hands off a small implementation, `ask-colleague feedback` grades a finished
|
|
11
|
+
work item (the ROI loop), and `ask-colleague clean` reaps stale/corrupt
|
|
12
|
+
`colleague/*` branches a crashed run left behind (which can break `git fetch`).
|
|
13
|
+
Reach for it REFLEXIVELY, the way you'd lean over to the
|
|
12
14
|
teammate at the next desk — not only when asked: before you present or open a PR
|
|
13
15
|
on a non-trivial committed diff, run `review` for a diverse second opinion; for a
|
|
14
16
|
fresh read of an unfamiliar area whose answer is independent of your current
|
|
15
17
|
context, run `explore`. Both are read-only — isolated in a throwaway git
|
|
16
|
-
worktree, zero side effects — so the reflex is always safe; the
|
|
17
|
-
`write --apply` / `write --pr` still needs the user's go-ahead. Triggers when the
|
|
18
|
+
worktree, zero side effects to your tree/branch — so the reflex is always safe; the
|
|
19
|
+
side-effecting `write --apply` / `write --pr` still needs the user's go-ahead. Triggers when the
|
|
18
20
|
user says "ask colleague", "ask a colleague to review/explore/write this", "have
|
|
19
21
|
colleague take a look", "get a second opinion", "ask the other model", "rate that
|
|
20
|
-
work item" — and still on the legacy "outsource this".
|
|
21
|
-
opinion to verify and own, never authority.
|
|
22
|
+
work item", "clean up a crashed colleague run" — and still on the legacy "outsource this".
|
|
23
|
+
Colleague's output is a second opinion to verify and own, never authority.
|
|
22
24
|
---
|
|
23
25
|
|
|
24
26
|
# ask-colleague — lean on colleague as a different mind
|
|
@@ -95,10 +97,11 @@ else an install hint.
|
|
|
95
97
|
|
|
96
98
|
| Verb | What it does | Side effects |
|
|
97
99
|
|------|--------------|--------------|
|
|
98
|
-
| `explore "<question or area>"` | Read-only investigation of the repo; the model reads and reports findings. | **None** — runs in a throwaway worktree at HEAD. |
|
|
99
|
-
| `review "<what to focus on>" [--base main]` | A diverse second opinion on the **committed** diff (`<base>...HEAD`). | **None** — throwaway worktree
|
|
100
|
-
| `write "<task>" [--apply\|--pr]` | Implement a change. **Previews by default** (throwaway worktree, prints the would-be diff); `--apply` lands a work branch in place; `--pr` pushes + opens a PR. | **None** by default (preview); a `colleague/<id>` work branch / PR only with `--apply` / `--pr`. |
|
|
100
|
+
| `explore "<question or area>"` | Read-only investigation of the repo; the model reads and reports findings. | **None** to your working tree / branch — runs in a throwaway worktree at HEAD; writes only a gradable run artifact under the gitignored `.colleague/` bookkeeping dir. |
|
|
101
|
+
| `review "<what to focus on>" [--base main]` | A diverse second opinion on the **committed** diff (`<base>...HEAD`). | **None** to your working tree / branch — throwaway worktree, committed changes only; writes only a gradable run artifact under the gitignored `.colleague/` bookkeeping dir. |
|
|
102
|
+
| `write "<task>" [--apply\|--pr]` | Implement a change. **Previews by default** (throwaway worktree, prints the would-be diff); `--apply` lands a work branch in place; `--pr` pushes + opens a PR. | **None** to your working tree / branch by default (preview); a `colleague/<id>` work branch / PR only with `--apply` / `--pr`. |
|
|
101
103
|
| `feedback <id\|last> [--rating N]` | **Grade a finished work item** (the ROI loop). With `--rating N` (1–5, plus `--notes`) it records feedback; without, it shows the work item's existing feedback. `last` resolves the most recent work item in `--repo`. | Writes `.colleague/<id>.feedback.json` only when `--rating` is given; read-only otherwise. |
|
|
104
|
+
| `clean [--dry-run]` | **Reap what a crashed run left behind** (#162): stale/corrupt `colleague/*` branches + orphaned 0-byte `.colleague/` artifacts that can wedge `git fetch`. Scoped strictly to `colleague/*` (never touches an unrelated branch); conservative with `.git/objects` (reports 0-byte loose objects + suggests `git prune`, never deletes them). A thin pass-through to `colleague clean`. | Deletes corrupt `colleague/<id>` refs + 0-byte `.colleague/` artifacts in `--repo`; `--dry-run` changes nothing. |
|
|
102
105
|
|
|
103
106
|
### Options
|
|
104
107
|
|
|
@@ -116,10 +119,15 @@ else an install hint.
|
|
|
116
119
|
| `--rating N` | (`feedback`) record a 1–5 quality rating for the work item. |
|
|
117
120
|
| `--notes "..."` | (`feedback`) free-text notes stored with the rating. |
|
|
118
121
|
| `--by NAME` | (`feedback`) who is grading (default: colleague's resolved identity). |
|
|
122
|
+
| `--dry-run` | (`clean`) report what would be reaped without changing anything. |
|
|
123
|
+
| `--json` | (any verb) machine-readable output: stdout carries **only** the result JSON, every diagnostic/digest line goes to stderr. |
|
|
119
124
|
|
|
120
125
|
The result printed to stdout is the work item's `TaskResult.summary` (plus
|
|
121
126
|
`changed_files` / work branch for `write`), parsed from `colleague work
|
|
122
|
-
--json`. Per-step progress streams to stderr while it runs.
|
|
127
|
+
--json`. Per-step progress streams to stderr while it runs. Pass `--json` to get
|
|
128
|
+
the raw `TaskResult` on stdout instead of the human digest (the drive verbs emit
|
|
129
|
+
the normalized `TaskResult`; `feedback` / `clean` forward `--json` to colleague),
|
|
130
|
+
keeping stdout valid JSON for a machine consumer while diagnostics stay on stderr.
|
|
123
131
|
|
|
124
132
|
## When to reach for which verb
|
|
125
133
|
|
|
@@ -140,6 +148,11 @@ The result printed to stdout is the work item's `TaskResult.summary` (plus
|
|
|
140
148
|
says how *good* it was — together they let you compute the **ROI of asking
|
|
141
149
|
colleague** and decide whether to ask again (and which backend). Grade the most
|
|
142
150
|
recent work item with `ask-colleague feedback last --rating 4 --notes "…"`.
|
|
151
|
+
- **clean** — recovery, not routine. A crashed / interrupted `write --apply` can
|
|
152
|
+
leave a dangling `colleague/<id>` branch pointing at half-written (0-byte)
|
|
153
|
+
objects that **breaks `git fetch` / `git pull`**. Run `ask-colleague clean`
|
|
154
|
+
(or `colleague clean`) to reap it — start with `--dry-run` to see what it would
|
|
155
|
+
remove. It only ever touches `colleague/*` refs and `.colleague/` artifacts.
|
|
143
156
|
|
|
144
157
|
## Hard rules (do not violate)
|
|
145
158
|
|
|
@@ -168,11 +181,20 @@ The result printed to stdout is the work item's `TaskResult.summary` (plus
|
|
|
168
181
|
uncommitted work, commit it first.
|
|
169
182
|
- The default backend is whatever single model is running locally; a multi-model
|
|
170
183
|
fleet (different model per verb) is separate infrastructure.
|
|
184
|
+
- **Every verb writes bookkeeping under `.colleague/`** (run artifacts for
|
|
185
|
+
explore/review/write; feedback records; the `last_work` pointer) — none of it
|
|
186
|
+
in your tracked tree, but in a repo that does **not** already gitignore
|
|
187
|
+
`.colleague/` it shows up as untracked files. **Add `.colleague/` to your
|
|
188
|
+
`.gitignore`** (keep `!/.colleague/commands/` if you commit command templates).
|
|
189
|
+
- **A crashed run can wedge `git fetch`.** A `write --apply` interrupted
|
|
190
|
+
mid-commit can leave a dangling `colleague/<id>` branch + 0-byte artifacts;
|
|
191
|
+
`ask-colleague clean` recovers it. A SIGKILL/OOM *during* the commit can still
|
|
192
|
+
corrupt git objects (git/filesystem durability, not the skill's to guarantee)
|
|
193
|
+
— which is exactly what `clean` is for.
|
|
171
194
|
|
|
172
195
|
## Provenance
|
|
173
196
|
|
|
174
197
|
This is a **first-party** colleague skill — colleague is its origin. It is
|
|
175
|
-
the inverse of the other skills under `.claude/skills/`, which
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
symlink or depend on it.
|
|
198
|
+
the inverse of the other skills under `.claude/skills/`, which rollout-cli
|
|
199
|
+
vendors *from* guildmaster. See `docs/skill-sources.md`. The `cite, don't import`
|
|
200
|
+
policy holds: downstream repos copy it, they don't symlink or depend on it.
|
{rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/ask-colleague/scripts/ask-colleague.sh
RENAMED
|
@@ -100,6 +100,8 @@ Options:
|
|
|
100
100
|
--rating N (feedback) record a 1-5 quality rating for the drive
|
|
101
101
|
--notes "..." (feedback) free-text notes to store with the rating
|
|
102
102
|
--by NAME (feedback) who is grading (default: colleague's resolved identity)
|
|
103
|
+
--json Machine-readable output: stdout carries only the result JSON,
|
|
104
|
+
every diagnostic/digest line goes to stderr (any verb)
|
|
103
105
|
|
|
104
106
|
explore/review run in a throwaway git worktree at HEAD — they cannot touch your
|
|
105
107
|
working tree or branch. review compares <base>...HEAD (committed changes only).
|
|
@@ -127,16 +129,23 @@ case "$VERB" in
|
|
|
127
129
|
;;
|
|
128
130
|
esac
|
|
129
131
|
|
|
130
|
-
#
|
|
131
|
-
# mid-run error
|
|
132
|
-
|
|
132
|
+
# Verify the external tools a given verb path actually needs are on PATH — fail
|
|
133
|
+
# fast with a clear message, not an opaque mid-run error. The required set is
|
|
134
|
+
# verb-specific (the caller passes it once the verb + flags are known, below):
|
|
135
|
+
# feedback/clean are thin pass-throughs to `colleague` plus the shared git
|
|
136
|
+
# work-tree guard, so they need only git; the drive verbs (explore/review/write)
|
|
137
|
+
# also render a prompt and parse colleague's --json result via python3, and the
|
|
138
|
+
# worktree-isolated paths additionally need mktemp. grep is only used by the
|
|
139
|
+
# uv-fallback resolver, which degrades to the clear "colleague not found" message
|
|
140
|
+
# when absent, so it is not a hard requirement here.
|
|
141
|
+
require_tools() { # $@ = tool names this verb path needs
|
|
133
142
|
local missing=() t
|
|
134
|
-
for t in
|
|
143
|
+
for t in "$@"; do
|
|
135
144
|
command -v "$t" >/dev/null 2>&1 || missing+=("$t")
|
|
136
145
|
done
|
|
137
146
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
138
147
|
echo "error: missing required tool(s): ${missing[*]}" >&2
|
|
139
|
-
echo "hint:
|
|
148
|
+
echo "hint: '$VERB' needs these on PATH: $*" >&2
|
|
140
149
|
exit 2
|
|
141
150
|
fi
|
|
142
151
|
}
|
|
@@ -151,8 +160,6 @@ need_value() { # $1 = remaining arg count ($#), $2 = flag name
|
|
|
151
160
|
}
|
|
152
161
|
}
|
|
153
162
|
|
|
154
|
-
require_tools
|
|
155
|
-
|
|
156
163
|
# ── defaults + flag parsing ─────────────────────────────────────────────────
|
|
157
164
|
REPO="."
|
|
158
165
|
BASE="main"
|
|
@@ -170,6 +177,7 @@ RATING=""
|
|
|
170
177
|
NOTES=""
|
|
171
178
|
BY=""
|
|
172
179
|
ARG=""
|
|
180
|
+
JSON_OUT=0
|
|
173
181
|
|
|
174
182
|
while [[ $# -gt 0 ]]; do
|
|
175
183
|
case "$1" in
|
|
@@ -187,6 +195,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
187
195
|
--rating) need_value "$#" "$1"; RATING="$2"; shift 2 ;;
|
|
188
196
|
--notes) need_value "$#" "$1"; NOTES="$2"; shift 2 ;;
|
|
189
197
|
--by) need_value "$#" "$1"; BY="$2"; shift 2 ;;
|
|
198
|
+
--json) JSON_OUT=1; shift ;;
|
|
190
199
|
-h | --help) usage; exit 0 ;;
|
|
191
200
|
--) shift; while [[ $# -gt 0 ]]; do ARG="${ARG:+$ARG }$1"; shift; done ;;
|
|
192
201
|
# unknown option -> user-input error (#161)
|
|
@@ -195,6 +204,23 @@ while [[ $# -gt 0 ]]; do
|
|
|
195
204
|
esac
|
|
196
205
|
done
|
|
197
206
|
|
|
207
|
+
# Now that the verb and its flags are known, require only the tools THIS path
|
|
208
|
+
# uses. feedback/clean shell straight to `colleague` (+ the git work-tree guard
|
|
209
|
+
# below), so they need only git — not python3/mktemp (qodo: the old blanket check
|
|
210
|
+
# failed those verbs in minimal envs). write --apply/--pr lands in place with no
|
|
211
|
+
# throwaway worktree, so it needs no mktemp either.
|
|
212
|
+
case "$VERB" in
|
|
213
|
+
feedback | clean) require_tools git ;;
|
|
214
|
+
write)
|
|
215
|
+
if [[ "$APPLY" -eq 1 || "$OPEN_PR" -eq 1 ]]; then
|
|
216
|
+
require_tools git python3
|
|
217
|
+
else
|
|
218
|
+
require_tools git python3 mktemp # preview runs in a throwaway worktree
|
|
219
|
+
fi
|
|
220
|
+
;;
|
|
221
|
+
*) require_tools git python3 mktemp ;; # explore / review
|
|
222
|
+
esac
|
|
223
|
+
|
|
198
224
|
# clean takes no description argument; every other verb requires one. (All of the
|
|
199
225
|
# guards below are user-input errors -> exit 1, per the policy comment at the top.)
|
|
200
226
|
if [[ "$VERB" == "clean" ]]; then
|
|
@@ -262,9 +288,10 @@ print_result() {
|
|
|
262
288
|
# empty (its artifact was discarded with the worktree, so it is not gradable).
|
|
263
289
|
# $3 (optional): exit code from the colleague drive command, propagated to
|
|
264
290
|
# the caller when the drive itself failed.
|
|
265
|
-
ASK_COLLEAGUE_REAL_ARTIFACT_DIR="${1:-}" ASK_COLLEAGUE_GRADABLE="${2:-}" ASK_COLLEAGUE_DRIVE_RC="${3:-}" python3 -c '
|
|
291
|
+
ASK_COLLEAGUE_REAL_ARTIFACT_DIR="${1:-}" ASK_COLLEAGUE_GRADABLE="${2:-}" ASK_COLLEAGUE_DRIVE_RC="${3:-}" ASK_COLLEAGUE_JSON="${JSON_OUT:-0}" python3 -c '
|
|
266
292
|
import sys, json, os
|
|
267
293
|
raw = sys.stdin.read().strip()
|
|
294
|
+
json_mode = os.environ.get("ASK_COLLEAGUE_JSON") == "1"
|
|
268
295
|
if not raw:
|
|
269
296
|
sys.stderr.write("error: colleague produced no result on stdout (see diagnostics above)\n")
|
|
270
297
|
sys.exit(2)
|
|
@@ -275,40 +302,69 @@ except Exception:
|
|
|
275
302
|
sys.stderr.write(raw[:2000] + "\n")
|
|
276
303
|
sys.exit(2)
|
|
277
304
|
ok = d.get("status") == "ok"
|
|
278
|
-
out = sys.stdout if ok else sys.stderr
|
|
279
305
|
tid = d.get("task_id") or ""
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
306
|
+
# Resolve the artifact path to the preserved copy when the drive ran in a
|
|
307
|
+
# throwaway worktree (read-only verbs); the raw JSON points into the now-deleted
|
|
308
|
+
# worktree, so both the digest and the --json output report the real location.
|
|
309
|
+
ap = d.get("artifacts_path")
|
|
310
|
+
real_dir = os.environ.get("ASK_COLLEAGUE_REAL_ARTIFACT_DIR") or ""
|
|
311
|
+
if ap and real_dir:
|
|
312
|
+
ap = os.path.join(real_dir, os.path.basename(ap))
|
|
283
313
|
# A drive that stopped without calling finish (colleague#142) or exhausted its
|
|
284
314
|
# step budget did NOT deliver an authoritative result — its summary is the model
|
|
285
315
|
# trailing off mid-task. Warn so the caller treats it as a partial, not a verdict.
|
|
286
|
-
# The warning is a DIAGNOSTIC -> always stderr (never stdout), so
|
|
287
|
-
#
|
|
316
|
+
# The warning is a DIAGNOSTIC -> always stderr (never stdout), so both the digest
|
|
317
|
+
# and --json keep a clean, machine-readable stdout (no single quotes in this body).
|
|
288
318
|
if d.get("stopped_without_finish"):
|
|
289
319
|
print("warning: drive ended without calling finish — treat the summary as a", file=sys.stderr)
|
|
290
320
|
print(" partial (the model stopped mid-task), not an authoritative result.", file=sys.stderr)
|
|
291
321
|
elif d.get("not_finished"):
|
|
292
322
|
print("warning: drive ran out of steps without finishing — summary is partial.", file=sys.stderr)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
323
|
+
if json_mode:
|
|
324
|
+
# --json contract: stdout carries ONLY the TaskResult JSON; every
|
|
325
|
+
# human/diagnostic line already went to stderr above. The exit code still
|
|
326
|
+
# reflects drive success/failure.
|
|
327
|
+
#
|
|
328
|
+
# artifacts_path mirrors the digest gate (ASK_COLLEAGUE_GRADABLE): it is only
|
|
329
|
+
# meaningful when the artifact SURVIVES. A preview (run_preview passes empty
|
|
330
|
+
# gradable) drives in a throwaway worktree the EXIT trap deletes, so the raw
|
|
331
|
+
# path points at a dir that is gone by the time the caller reads it — drop it
|
|
332
|
+
# rather than hand a machine consumer a dead path (#186 qodo finding-3). When
|
|
333
|
+
# gradable, rewrite it to the preserved copy in the real repo.
|
|
334
|
+
gradable = os.environ.get("ASK_COLLEAGUE_GRADABLE") == "1"
|
|
335
|
+
if not gradable:
|
|
336
|
+
d.pop("artifacts_path", None)
|
|
337
|
+
elif ap:
|
|
338
|
+
d["artifacts_path"] = ap
|
|
339
|
+
json.dump(d, sys.stdout)
|
|
340
|
+
sys.stdout.write("\n")
|
|
341
|
+
# The task:/grade: hints are diagnostics -> stderr (stdout stays pure JSON).
|
|
342
|
+
# task_id is already in the payload, but echoing the copy-paste grade hint
|
|
343
|
+
# keeps the convention every work item follows (rule 907536) without breaking
|
|
344
|
+
# the stdout contract. Gated on gradable, exactly like the digest below.
|
|
345
|
+
if tid and gradable:
|
|
346
|
+
print("task:", tid, file=sys.stderr)
|
|
347
|
+
print("grade:", "ask-colleague feedback", tid, "--rating N", file=sys.stderr)
|
|
348
|
+
else:
|
|
349
|
+
out = sys.stdout if ok else sys.stderr
|
|
350
|
+
print("status:", d.get("status"), file=out)
|
|
351
|
+
if tid:
|
|
352
|
+
print("task:", tid, file=out)
|
|
353
|
+
print(file=out)
|
|
354
|
+
print((d.get("summary") or "").rstrip(), file=out)
|
|
355
|
+
cf = d.get("changed_files") or []
|
|
356
|
+
if cf:
|
|
357
|
+
print("\nchanged files:", ", ".join(cf), file=out)
|
|
358
|
+
if d.get("branch"):
|
|
359
|
+
print("drive branch:", d["branch"], file=out)
|
|
360
|
+
if ap and os.environ.get("ASK_COLLEAGUE_GRADABLE") == "1":
|
|
361
|
+
print("artifact:", ap, file=out)
|
|
362
|
+
# A drive is gradable whenever its artifact survives — including a FAILED
|
|
363
|
+
# drive (colleague writes an artifact on failure too, h5): a failure rated
|
|
364
|
+
# 1/5 is exactly the ROI signal, so the hint must not be gated on `ok` (#139
|
|
365
|
+
# qodo). It prints to `out` (stderr on failure), matching the failure digest.
|
|
366
|
+
if tid and os.environ.get("ASK_COLLEAGUE_GRADABLE") == "1":
|
|
367
|
+
print("grade:", "ask-colleague feedback", tid, "--rating N", file=out)
|
|
312
368
|
if ok:
|
|
313
369
|
sys.exit(0)
|
|
314
370
|
else:
|
|
@@ -471,11 +527,15 @@ run_preview() {
|
|
|
471
527
|
local prc=0
|
|
472
528
|
printf '%s' "$out" | print_result "" "" "$rc" || prc=$?
|
|
473
529
|
if [[ "$prc" -eq 0 ]]; then
|
|
530
|
+
# In --json mode stdout is reserved for the result JSON print_result just
|
|
531
|
+
# emitted, so the would-be patch is a diagnostic -> stderr (fd 2).
|
|
532
|
+
local diff_fd=1
|
|
533
|
+
[[ "$JSON_OUT" -eq 1 ]] && diff_fd=2
|
|
474
534
|
if [[ -n "$patch" ]]; then
|
|
475
|
-
printf '\n--- preview diff (NOT applied — pass --apply to land it) ---\n'
|
|
476
|
-
printf '%s\n' "$patch"
|
|
535
|
+
printf '\n--- preview diff (NOT applied — pass --apply to land it) ---\n' >&"$diff_fd"
|
|
536
|
+
printf '%s\n' "$patch" >&"$diff_fd"
|
|
477
537
|
else
|
|
478
|
-
printf '\n(preview: no file changes reported; NOT applied)\n'
|
|
538
|
+
printf '\n(preview: no file changes reported; NOT applied)\n' >&"$diff_fd"
|
|
479
539
|
fi
|
|
480
540
|
fi
|
|
481
541
|
return "$prc"
|
|
@@ -543,6 +603,9 @@ run_feedback() {
|
|
|
543
603
|
cmd+=(show "$ref")
|
|
544
604
|
fi
|
|
545
605
|
cmd+=(--repo "$REPO")
|
|
606
|
+
# colleague feedback supports --json natively; forward the operator's request
|
|
607
|
+
# so stdout stays machine-readable end-to-end.
|
|
608
|
+
[[ "$JSON_OUT" -eq 1 ]] && cmd+=(--json)
|
|
546
609
|
"${cmd[@]}"
|
|
547
610
|
}
|
|
548
611
|
|
|
@@ -556,6 +619,8 @@ run_feedback() {
|
|
|
556
619
|
run_clean() {
|
|
557
620
|
local cmd=("${COLLEAGUE[@]}" clean --repo "$REPO")
|
|
558
621
|
[[ "$DRY_RUN" -eq 1 ]] && cmd+=(--dry-run)
|
|
622
|
+
# colleague clean supports --json natively; forward it for machine-readable output.
|
|
623
|
+
[[ "$JSON_OUT" -eq 1 ]] && cmd+=(--json)
|
|
559
624
|
"${cmd[@]}"
|
|
560
625
|
}
|
|
561
626
|
|
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/). This project
|
|
6
6
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.2] - 2026-06-13
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- ask-colleague: sync SKILL.md + script to the canonical colleague source (documents the `clean` verb; script +137 lines). rollout-cli's copy was stale vs its origin (agentculture/colleague). Provenance line kept repo-correct.
|
|
13
|
+
|
|
8
14
|
## [0.4.1] - 2026-06-13
|
|
9
15
|
|
|
10
16
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rollout-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Cross-repo propagation workflow built on refactor-cli — apply a transformation across many repos at once.
|
|
5
5
|
Project-URL: Homepage, https://github.com/agentculture/rollout-cli
|
|
6
6
|
Project-URL: Issues, https://github.com/agentculture/rollout-cli/issues
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# apply.sh — re-vendor the ask-colleague skill wrapper from
|
|
2
|
+
# apply.sh — re-vendor the ask-colleague skill wrapper from its canonical
|
|
3
|
+
# colleague source.
|
|
3
4
|
# Usage: apply.sh [REPO_DIR] (default: current directory)
|
|
4
5
|
#
|
|
5
6
|
# Idempotent. Two cases, one code path:
|
|
6
|
-
# * Existing install — overwrite scripts/ask-colleague.sh ONLY
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# never clobbered here.
|
|
7
|
+
# * Existing install — overwrite scripts/ask-colleague.sh ONLY (wrapper-only
|
|
8
|
+
# re-vendor); a downstream's SKILL.md carries a repo-specific provenance token
|
|
9
|
+
# (and may intentionally diverge), so it is never clobbered here.
|
|
10
10
|
# * Skill absent — lay down the full skill (wrapper + SKILL.md + prompts),
|
|
11
11
|
# substituting the target repo's nick into SKILL.md's provenance line.
|
|
12
12
|
set -euo pipefail
|
|
@@ -18,7 +18,7 @@ DEST="${REPO}/.claude/skills/ask-colleague"
|
|
|
18
18
|
|
|
19
19
|
mkdir -p "${DEST}/scripts" "${DEST}/prompts"
|
|
20
20
|
|
|
21
|
-
# (1) Wrapper — always overwrite verbatim (the
|
|
21
|
+
# (1) Wrapper — always overwrite verbatim (the canonical wrapper payload).
|
|
22
22
|
cp "${SRC}/scripts/ask-colleague.sh" "${DEST}/scripts/ask-colleague.sh"
|
|
23
23
|
chmod 0755 "${DEST}/scripts/ask-colleague.sh"
|
|
24
24
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# check.sh — classify a target repo for the ask-colleague-revendor recipe.
|
|
3
3
|
# Usage: check.sh [REPO_DIR] (default: current directory)
|
|
4
4
|
# Prints exactly one status word to stdout and exits 0:
|
|
5
|
-
# "done" — wrapper already byte-matches the vendored
|
|
5
|
+
# "done" — wrapper already byte-matches the vendored canonical wrapper
|
|
6
6
|
# "ok" — wrapper missing or stale (recipe adds / refreshes it)
|
|
7
7
|
# "needs-attention" — not a version-bumpable agent repo (no pyproject version)
|
|
8
8
|
set -euo pipefail
|
|
@@ -120,10 +120,14 @@ else an install hint.
|
|
|
120
120
|
| `--notes "..."` | (`feedback`) free-text notes stored with the rating. |
|
|
121
121
|
| `--by NAME` | (`feedback`) who is grading (default: colleague's resolved identity). |
|
|
122
122
|
| `--dry-run` | (`clean`) report what would be reaped without changing anything. |
|
|
123
|
+
| `--json` | (any verb) machine-readable output: stdout carries **only** the result JSON, every diagnostic/digest line goes to stderr. |
|
|
123
124
|
|
|
124
125
|
The result printed to stdout is the work item's `TaskResult.summary` (plus
|
|
125
126
|
`changed_files` / work branch for `write`), parsed from `colleague work
|
|
126
|
-
--json`. Per-step progress streams to stderr while it runs.
|
|
127
|
+
--json`. Per-step progress streams to stderr while it runs. Pass `--json` to get
|
|
128
|
+
the raw `TaskResult` on stdout instead of the human digest (the drive verbs emit
|
|
129
|
+
the normalized `TaskResult`; `feedback` / `clean` forward `--json` to colleague),
|
|
130
|
+
keeping stdout valid JSON for a machine consumer while diagnostics stay on stderr.
|
|
127
131
|
|
|
128
132
|
## When to reach for which verb
|
|
129
133
|
|
|
@@ -100,6 +100,8 @@ Options:
|
|
|
100
100
|
--rating N (feedback) record a 1-5 quality rating for the drive
|
|
101
101
|
--notes "..." (feedback) free-text notes to store with the rating
|
|
102
102
|
--by NAME (feedback) who is grading (default: colleague's resolved identity)
|
|
103
|
+
--json Machine-readable output: stdout carries only the result JSON,
|
|
104
|
+
every diagnostic/digest line goes to stderr (any verb)
|
|
103
105
|
|
|
104
106
|
explore/review run in a throwaway git worktree at HEAD — they cannot touch your
|
|
105
107
|
working tree or branch. review compares <base>...HEAD (committed changes only).
|
|
@@ -127,16 +129,23 @@ case "$VERB" in
|
|
|
127
129
|
;;
|
|
128
130
|
esac
|
|
129
131
|
|
|
130
|
-
#
|
|
131
|
-
# mid-run error
|
|
132
|
-
|
|
132
|
+
# Verify the external tools a given verb path actually needs are on PATH — fail
|
|
133
|
+
# fast with a clear message, not an opaque mid-run error. The required set is
|
|
134
|
+
# verb-specific (the caller passes it once the verb + flags are known, below):
|
|
135
|
+
# feedback/clean are thin pass-throughs to `colleague` plus the shared git
|
|
136
|
+
# work-tree guard, so they need only git; the drive verbs (explore/review/write)
|
|
137
|
+
# also render a prompt and parse colleague's --json result via python3, and the
|
|
138
|
+
# worktree-isolated paths additionally need mktemp. grep is only used by the
|
|
139
|
+
# uv-fallback resolver, which degrades to the clear "colleague not found" message
|
|
140
|
+
# when absent, so it is not a hard requirement here.
|
|
141
|
+
require_tools() { # $@ = tool names this verb path needs
|
|
133
142
|
local missing=() t
|
|
134
|
-
for t in
|
|
143
|
+
for t in "$@"; do
|
|
135
144
|
command -v "$t" >/dev/null 2>&1 || missing+=("$t")
|
|
136
145
|
done
|
|
137
146
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
138
147
|
echo "error: missing required tool(s): ${missing[*]}" >&2
|
|
139
|
-
echo "hint:
|
|
148
|
+
echo "hint: '$VERB' needs these on PATH: $*" >&2
|
|
140
149
|
exit 2
|
|
141
150
|
fi
|
|
142
151
|
}
|
|
@@ -151,8 +160,6 @@ need_value() { # $1 = remaining arg count ($#), $2 = flag name
|
|
|
151
160
|
}
|
|
152
161
|
}
|
|
153
162
|
|
|
154
|
-
require_tools
|
|
155
|
-
|
|
156
163
|
# ── defaults + flag parsing ─────────────────────────────────────────────────
|
|
157
164
|
REPO="."
|
|
158
165
|
BASE="main"
|
|
@@ -170,6 +177,7 @@ RATING=""
|
|
|
170
177
|
NOTES=""
|
|
171
178
|
BY=""
|
|
172
179
|
ARG=""
|
|
180
|
+
JSON_OUT=0
|
|
173
181
|
|
|
174
182
|
while [[ $# -gt 0 ]]; do
|
|
175
183
|
case "$1" in
|
|
@@ -187,6 +195,7 @@ while [[ $# -gt 0 ]]; do
|
|
|
187
195
|
--rating) need_value "$#" "$1"; RATING="$2"; shift 2 ;;
|
|
188
196
|
--notes) need_value "$#" "$1"; NOTES="$2"; shift 2 ;;
|
|
189
197
|
--by) need_value "$#" "$1"; BY="$2"; shift 2 ;;
|
|
198
|
+
--json) JSON_OUT=1; shift ;;
|
|
190
199
|
-h | --help) usage; exit 0 ;;
|
|
191
200
|
--) shift; while [[ $# -gt 0 ]]; do ARG="${ARG:+$ARG }$1"; shift; done ;;
|
|
192
201
|
# unknown option -> user-input error (#161)
|
|
@@ -195,6 +204,23 @@ while [[ $# -gt 0 ]]; do
|
|
|
195
204
|
esac
|
|
196
205
|
done
|
|
197
206
|
|
|
207
|
+
# Now that the verb and its flags are known, require only the tools THIS path
|
|
208
|
+
# uses. feedback/clean shell straight to `colleague` (+ the git work-tree guard
|
|
209
|
+
# below), so they need only git — not python3/mktemp (qodo: the old blanket check
|
|
210
|
+
# failed those verbs in minimal envs). write --apply/--pr lands in place with no
|
|
211
|
+
# throwaway worktree, so it needs no mktemp either.
|
|
212
|
+
case "$VERB" in
|
|
213
|
+
feedback | clean) require_tools git ;;
|
|
214
|
+
write)
|
|
215
|
+
if [[ "$APPLY" -eq 1 || "$OPEN_PR" -eq 1 ]]; then
|
|
216
|
+
require_tools git python3
|
|
217
|
+
else
|
|
218
|
+
require_tools git python3 mktemp # preview runs in a throwaway worktree
|
|
219
|
+
fi
|
|
220
|
+
;;
|
|
221
|
+
*) require_tools git python3 mktemp ;; # explore / review
|
|
222
|
+
esac
|
|
223
|
+
|
|
198
224
|
# clean takes no description argument; every other verb requires one. (All of the
|
|
199
225
|
# guards below are user-input errors -> exit 1, per the policy comment at the top.)
|
|
200
226
|
if [[ "$VERB" == "clean" ]]; then
|
|
@@ -262,9 +288,10 @@ print_result() {
|
|
|
262
288
|
# empty (its artifact was discarded with the worktree, so it is not gradable).
|
|
263
289
|
# $3 (optional): exit code from the colleague drive command, propagated to
|
|
264
290
|
# the caller when the drive itself failed.
|
|
265
|
-
ASK_COLLEAGUE_REAL_ARTIFACT_DIR="${1:-}" ASK_COLLEAGUE_GRADABLE="${2:-}" ASK_COLLEAGUE_DRIVE_RC="${3:-}" python3 -c '
|
|
291
|
+
ASK_COLLEAGUE_REAL_ARTIFACT_DIR="${1:-}" ASK_COLLEAGUE_GRADABLE="${2:-}" ASK_COLLEAGUE_DRIVE_RC="${3:-}" ASK_COLLEAGUE_JSON="${JSON_OUT:-0}" python3 -c '
|
|
266
292
|
import sys, json, os
|
|
267
293
|
raw = sys.stdin.read().strip()
|
|
294
|
+
json_mode = os.environ.get("ASK_COLLEAGUE_JSON") == "1"
|
|
268
295
|
if not raw:
|
|
269
296
|
sys.stderr.write("error: colleague produced no result on stdout (see diagnostics above)\n")
|
|
270
297
|
sys.exit(2)
|
|
@@ -275,40 +302,69 @@ except Exception:
|
|
|
275
302
|
sys.stderr.write(raw[:2000] + "\n")
|
|
276
303
|
sys.exit(2)
|
|
277
304
|
ok = d.get("status") == "ok"
|
|
278
|
-
out = sys.stdout if ok else sys.stderr
|
|
279
305
|
tid = d.get("task_id") or ""
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
306
|
+
# Resolve the artifact path to the preserved copy when the drive ran in a
|
|
307
|
+
# throwaway worktree (read-only verbs); the raw JSON points into the now-deleted
|
|
308
|
+
# worktree, so both the digest and the --json output report the real location.
|
|
309
|
+
ap = d.get("artifacts_path")
|
|
310
|
+
real_dir = os.environ.get("ASK_COLLEAGUE_REAL_ARTIFACT_DIR") or ""
|
|
311
|
+
if ap and real_dir:
|
|
312
|
+
ap = os.path.join(real_dir, os.path.basename(ap))
|
|
283
313
|
# A drive that stopped without calling finish (colleague#142) or exhausted its
|
|
284
314
|
# step budget did NOT deliver an authoritative result — its summary is the model
|
|
285
315
|
# trailing off mid-task. Warn so the caller treats it as a partial, not a verdict.
|
|
286
|
-
# The warning is a DIAGNOSTIC -> always stderr (never stdout), so
|
|
287
|
-
#
|
|
316
|
+
# The warning is a DIAGNOSTIC -> always stderr (never stdout), so both the digest
|
|
317
|
+
# and --json keep a clean, machine-readable stdout (no single quotes in this body).
|
|
288
318
|
if d.get("stopped_without_finish"):
|
|
289
319
|
print("warning: drive ended without calling finish — treat the summary as a", file=sys.stderr)
|
|
290
320
|
print(" partial (the model stopped mid-task), not an authoritative result.", file=sys.stderr)
|
|
291
321
|
elif d.get("not_finished"):
|
|
292
322
|
print("warning: drive ran out of steps without finishing — summary is partial.", file=sys.stderr)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
323
|
+
if json_mode:
|
|
324
|
+
# --json contract: stdout carries ONLY the TaskResult JSON; every
|
|
325
|
+
# human/diagnostic line already went to stderr above. The exit code still
|
|
326
|
+
# reflects drive success/failure.
|
|
327
|
+
#
|
|
328
|
+
# artifacts_path mirrors the digest gate (ASK_COLLEAGUE_GRADABLE): it is only
|
|
329
|
+
# meaningful when the artifact SURVIVES. A preview (run_preview passes empty
|
|
330
|
+
# gradable) drives in a throwaway worktree the EXIT trap deletes, so the raw
|
|
331
|
+
# path points at a dir that is gone by the time the caller reads it — drop it
|
|
332
|
+
# rather than hand a machine consumer a dead path (#186 qodo finding-3). When
|
|
333
|
+
# gradable, rewrite it to the preserved copy in the real repo.
|
|
334
|
+
gradable = os.environ.get("ASK_COLLEAGUE_GRADABLE") == "1"
|
|
335
|
+
if not gradable:
|
|
336
|
+
d.pop("artifacts_path", None)
|
|
337
|
+
elif ap:
|
|
338
|
+
d["artifacts_path"] = ap
|
|
339
|
+
json.dump(d, sys.stdout)
|
|
340
|
+
sys.stdout.write("\n")
|
|
341
|
+
# The task:/grade: hints are diagnostics -> stderr (stdout stays pure JSON).
|
|
342
|
+
# task_id is already in the payload, but echoing the copy-paste grade hint
|
|
343
|
+
# keeps the convention every work item follows (rule 907536) without breaking
|
|
344
|
+
# the stdout contract. Gated on gradable, exactly like the digest below.
|
|
345
|
+
if tid and gradable:
|
|
346
|
+
print("task:", tid, file=sys.stderr)
|
|
347
|
+
print("grade:", "ask-colleague feedback", tid, "--rating N", file=sys.stderr)
|
|
348
|
+
else:
|
|
349
|
+
out = sys.stdout if ok else sys.stderr
|
|
350
|
+
print("status:", d.get("status"), file=out)
|
|
351
|
+
if tid:
|
|
352
|
+
print("task:", tid, file=out)
|
|
353
|
+
print(file=out)
|
|
354
|
+
print((d.get("summary") or "").rstrip(), file=out)
|
|
355
|
+
cf = d.get("changed_files") or []
|
|
356
|
+
if cf:
|
|
357
|
+
print("\nchanged files:", ", ".join(cf), file=out)
|
|
358
|
+
if d.get("branch"):
|
|
359
|
+
print("drive branch:", d["branch"], file=out)
|
|
360
|
+
if ap and os.environ.get("ASK_COLLEAGUE_GRADABLE") == "1":
|
|
361
|
+
print("artifact:", ap, file=out)
|
|
362
|
+
# A drive is gradable whenever its artifact survives — including a FAILED
|
|
363
|
+
# drive (colleague writes an artifact on failure too, h5): a failure rated
|
|
364
|
+
# 1/5 is exactly the ROI signal, so the hint must not be gated on `ok` (#139
|
|
365
|
+
# qodo). It prints to `out` (stderr on failure), matching the failure digest.
|
|
366
|
+
if tid and os.environ.get("ASK_COLLEAGUE_GRADABLE") == "1":
|
|
367
|
+
print("grade:", "ask-colleague feedback", tid, "--rating N", file=out)
|
|
312
368
|
if ok:
|
|
313
369
|
sys.exit(0)
|
|
314
370
|
else:
|
|
@@ -471,11 +527,15 @@ run_preview() {
|
|
|
471
527
|
local prc=0
|
|
472
528
|
printf '%s' "$out" | print_result "" "" "$rc" || prc=$?
|
|
473
529
|
if [[ "$prc" -eq 0 ]]; then
|
|
530
|
+
# In --json mode stdout is reserved for the result JSON print_result just
|
|
531
|
+
# emitted, so the would-be patch is a diagnostic -> stderr (fd 2).
|
|
532
|
+
local diff_fd=1
|
|
533
|
+
[[ "$JSON_OUT" -eq 1 ]] && diff_fd=2
|
|
474
534
|
if [[ -n "$patch" ]]; then
|
|
475
|
-
printf '\n--- preview diff (NOT applied — pass --apply to land it) ---\n'
|
|
476
|
-
printf '%s\n' "$patch"
|
|
535
|
+
printf '\n--- preview diff (NOT applied — pass --apply to land it) ---\n' >&"$diff_fd"
|
|
536
|
+
printf '%s\n' "$patch" >&"$diff_fd"
|
|
477
537
|
else
|
|
478
|
-
printf '\n(preview: no file changes reported; NOT applied)\n'
|
|
538
|
+
printf '\n(preview: no file changes reported; NOT applied)\n' >&"$diff_fd"
|
|
479
539
|
fi
|
|
480
540
|
fi
|
|
481
541
|
return "$prc"
|
|
@@ -543,6 +603,9 @@ run_feedback() {
|
|
|
543
603
|
cmd+=(show "$ref")
|
|
544
604
|
fi
|
|
545
605
|
cmd+=(--repo "$REPO")
|
|
606
|
+
# colleague feedback supports --json natively; forward the operator's request
|
|
607
|
+
# so stdout stays machine-readable end-to-end.
|
|
608
|
+
[[ "$JSON_OUT" -eq 1 ]] && cmd+=(--json)
|
|
546
609
|
"${cmd[@]}"
|
|
547
610
|
}
|
|
548
611
|
|
|
@@ -556,6 +619,8 @@ run_feedback() {
|
|
|
556
619
|
run_clean() {
|
|
557
620
|
local cmd=("${COLLEAGUE[@]}" clean --repo "$REPO")
|
|
558
621
|
[[ "$DRY_RUN" -eq 1 ]] && cmd+=(--dry-run)
|
|
622
|
+
# colleague clean supports --json natively; forward it for machine-readable output.
|
|
623
|
+
[[ "$JSON_OUT" -eq 1 ]] && cmd+=(--json)
|
|
559
624
|
"${cmd[@]}"
|
|
560
625
|
}
|
|
561
626
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name = "ask-colleague-revendor"
|
|
2
|
+
# The wrapper is always rewritten. The remaining paths are written only on a
|
|
3
|
+
# fresh add (skill absent); declared here so recipe introspection covers every
|
|
4
|
+
# file apply.sh may touch.
|
|
5
|
+
touched_files = [
|
|
6
|
+
".claude/skills/ask-colleague/scripts/ask-colleague.sh",
|
|
7
|
+
".claude/skills/ask-colleague/SKILL.md",
|
|
8
|
+
".claude/skills/ask-colleague/prompts/explore.md",
|
|
9
|
+
".claude/skills/ask-colleague/prompts/review.md",
|
|
10
|
+
".claude/skills/ask-colleague/prompts/write.md",
|
|
11
|
+
]
|
|
12
|
+
bump_level = "patch"
|
|
13
|
+
changelog_text = """\
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **Re-vendored the `ask-colleague` skill wrapper from its canonical colleague
|
|
17
|
+
source** (`.claude/skills/ask-colleague/scripts/ask-colleague.sh`): the wrapper
|
|
18
|
+
now supports `--json` machine-readable output — stdout carries **only** the
|
|
19
|
+
result JSON (the drive verbs emit the normalized `TaskResult`; `feedback` /
|
|
20
|
+
`clean` forward `--json` to colleague) while every diagnostic/digest line goes
|
|
21
|
+
to stderr, keeping stdout valid JSON for a machine consumer. This supersedes the
|
|
22
|
+
earlier wrapper-only colleague#183 snapshot (which predated `--json`). The
|
|
23
|
+
bundled `SKILL.md` template documents the new verb/flag; an existing downstream
|
|
24
|
+
`SKILL.md` is left untouched (it carries a repo-specific provenance token and
|
|
25
|
+
may diverge). Where the skill was absent, it is added fresh (wrapper +
|
|
26
|
+
`SKILL.md` + prompts).
|
|
27
|
+
"""
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
name = "ask-colleague-revendor"
|
|
2
|
-
# The wrapper is always rewritten. The remaining paths are written only on a
|
|
3
|
-
# fresh add (skill absent); declared here so recipe introspection covers every
|
|
4
|
-
# file apply.sh may touch.
|
|
5
|
-
touched_files = [
|
|
6
|
-
".claude/skills/ask-colleague/scripts/ask-colleague.sh",
|
|
7
|
-
".claude/skills/ask-colleague/SKILL.md",
|
|
8
|
-
".claude/skills/ask-colleague/prompts/explore.md",
|
|
9
|
-
".claude/skills/ask-colleague/prompts/review.md",
|
|
10
|
-
".claude/skills/ask-colleague/prompts/write.md",
|
|
11
|
-
]
|
|
12
|
-
bump_level = "patch"
|
|
13
|
-
changelog_text = """\
|
|
14
|
-
### Changed
|
|
15
|
-
|
|
16
|
-
- **Re-vendored the `ask-colleague` skill wrapper from colleague#183**
|
|
17
|
-
(`.claude/skills/ask-colleague/scripts/ask-colleague.sh`): `resolve_colleague()`
|
|
18
|
-
now honors `--repo` for the `uv` local-dev fallback, the `colleague drive`
|
|
19
|
-
tri-state exit code (0/1/2) propagates end-to-end instead of collapsing to 1,
|
|
20
|
-
and a non-preserved read-only run no longer prints a dead `artifact:` line into
|
|
21
|
-
a throwaway worktree. Wrapper-only — an existing `SKILL.md` and prompt
|
|
22
|
-
templates are left untouched (they carry a repo-specific provenance token and
|
|
23
|
-
may diverge). Where the skill was absent, it is added fresh (wrapper +
|
|
24
|
-
`SKILL.md` + prompts). Refs: colleague#183, #180, #181.
|
|
25
|
-
"""
|
|
File without changes
|
{rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/agent-config/data/backend-fingerprints.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rollout_cli-0.4.1 → rollout_cli-0.4.2}/.claude/skills/pypi-maintainer/scripts/switch-source.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/files/prompts/explore.md
RENAMED
|
File without changes
|
{rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/files/prompts/review.md
RENAMED
|
File without changes
|
{rollout_cli-0.4.1 → rollout_cli-0.4.2}/recipes/ask-colleague-revendor/files/prompts/write.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|