devague 0.3.3__tar.gz → 0.4.1__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.
- devague-0.4.1/.claude/skills/spec-to-plan/SKILL.md +149 -0
- devague-0.4.1/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +226 -0
- {devague-0.3.3/.claude/skills/devague → devague-0.4.1/.claude/skills/think}/SKILL.md +43 -18
- devague-0.3.3/.claude/skills/devague/scripts/devague.sh → devague-0.4.1/.claude/skills/think/scripts/think.sh +10 -5
- devague-0.4.1/.devague/current_plan +1 -0
- devague-0.4.1/.devague/frames/devague-now-ships-a-documented-spec-contract-every.json +263 -0
- devague-0.4.1/.devague/plans/devague-now-ships-a-documented-spec-contract-every.json +363 -0
- {devague-0.3.3 → devague-0.4.1}/CHANGELOG.md +26 -0
- {devague-0.3.3 → devague-0.4.1}/CLAUDE.md +52 -14
- devague-0.4.1/PKG-INFO +58 -0
- devague-0.4.1/README.md +41 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/__init__.py +2 -0
- devague-0.4.1/devague/cli/_commands/plan.py +436 -0
- devague-0.4.1/devague/cli/_plans.py +29 -0
- devague-0.4.1/devague/plan.py +179 -0
- devague-0.4.1/devague/plan_convergence.py +142 -0
- devague-0.4.1/devague/plan_store.py +60 -0
- {devague-0.3.3 → devague-0.4.1}/devague/render/frame_md.py +3 -3
- devague-0.4.1/devague/render/plan_md.py +95 -0
- {devague-0.3.3 → devague-0.4.1}/devague/render/spec_md.py +14 -12
- devague-0.4.1/docs/plans/devague-now-ships-a-documented-spec-contract-every.md +85 -0
- devague-0.4.1/docs/reviews/spec-contract-frame-review.md +150 -0
- {devague-0.3.3 → devague-0.4.1}/docs/skill-sources.md +13 -5
- devague-0.4.1/docs/specs/devague-now-ships-a-documented-spec-contract-every.md +55 -0
- devague-0.4.1/docs/superpowers/specs/2026-05-23-devague-spec-to-plan-design.md +198 -0
- {devague-0.3.3 → devague-0.4.1}/pyproject.toml +2 -2
- devague-0.4.1/tests/test_cli_plan.py +291 -0
- devague-0.4.1/tests/test_plan.py +102 -0
- devague-0.4.1/tests/test_plan_convergence.py +121 -0
- devague-0.4.1/tests/test_plan_store.py +67 -0
- devague-0.4.1/tests/test_render.py +81 -0
- devague-0.4.1/tests/test_render_plan.py +78 -0
- devague-0.4.1/tests/test_spec_to_plan_skill.py +124 -0
- devague-0.3.3/tests/test_devague_skill.py → devague-0.4.1/tests/test_think_skill.py +6 -5
- {devague-0.3.3 → devague-0.4.1}/uv.lock +1 -1
- devague-0.3.3/PKG-INFO +0 -23
- devague-0.3.3/README.md +0 -6
- devague-0.3.3/tests/test_render.py +0 -41
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/cicd/SKILL.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/cicd/scripts/workflow.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/communicate/SKILL.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/run-tests/SKILL.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/sonarclaude/SKILL.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/version-bump/SKILL.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills/version-bump/scripts/bump.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/.claude/skills.local.yaml.example +0 -0
- {devague-0.3.3 → devague-0.4.1}/.flake8 +0 -0
- {devague-0.3.3 → devague-0.4.1}/.github/workflows/publish.yml +0 -0
- {devague-0.3.3 → devague-0.4.1}/.github/workflows/security-checks.yml +0 -0
- {devague-0.3.3 → devague-0.4.1}/.github/workflows/tests.yml +0 -0
- {devague-0.3.3 → devague-0.4.1}/.gitignore +0 -0
- {devague-0.3.3 → devague-0.4.1}/.markdownlint-cli2.yaml +0 -0
- {devague-0.3.3 → devague-0.4.1}/.pre-commit-config.yaml +0 -0
- {devague-0.3.3 → devague-0.4.1}/LICENSE +0 -0
- {devague-0.3.3 → devague-0.4.1}/culture.yaml +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/__init__.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/__main__.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/__init__.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/capture.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/confirm.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/converge.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/explain.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/export.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/interrogate.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/learn.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/list_frames.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/new.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/park.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/reject.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_commands/show.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_errors.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_frames.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/cli/_output.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/convergence.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/frame.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/render/__init__.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/devague/store.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/docs/superpowers/plans/2026-05-22-specifix-onboarding.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/docs/superpowers/plans/2026-05-23-devague-rename.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/docs/superpowers/plans/2026-05-23-devague-working-backwards-engine.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/docs/superpowers/specs/2026-05-22-specifix-onboarding-design.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/docs/superpowers/specs/2026-05-23-devague-working-backwards-design.md +0 -0
- {devague-0.3.3 → devague-0.4.1}/sonar-project.properties +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/__init__.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_cli_affordances.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_cli_chassis.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_cli_converge_export.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_cli_errors.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_cli_moves.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_cli_output.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_convergence.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_frame.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_package.py +0 -0
- {devague-0.3.3 → devague-0.4.1}/tests/test_store.py +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec-to-plan
|
|
3
|
+
description: >
|
|
4
|
+
Turn a converged devague spec into a buildable plan by working forwards (the
|
|
5
|
+
spec→plan leg; drives the `devague plan` CLI group). Seed a plan from a
|
|
6
|
+
converged frame, add tasks that collectively cover every coverage target (the
|
|
7
|
+
frame's confirmed claims + honesty conditions), give each task acceptance
|
|
8
|
+
criteria and an honest dependency order, park genuine unknowns as first-class
|
|
9
|
+
risks, and export a plan only once it *converges*. Use when the user says
|
|
10
|
+
"spec to plan", "stp", "turn this spec into a plan", "plan this spec", "make a
|
|
11
|
+
build plan", or after the /think skill exports a spec. Authored and maintained
|
|
12
|
+
in agentculture/devague (origin = devague); steward pulls this skill from here
|
|
13
|
+
and broadcasts it to the AgentCulture mesh — it is NOT vendored from steward
|
|
14
|
+
like the other skills here.
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# spec-to-plan — work a converged spec forwards into a buildable plan
|
|
18
|
+
|
|
19
|
+
The skill is named **`spec-to-plan`**; the product/CLI it drives is the
|
|
20
|
+
**`devague plan`** command group. (The prior leg — turning a vague idea into a
|
|
21
|
+
spec — is the sibling **`/think`** skill.) It is the **forward** peer of the
|
|
22
|
+
working-backwards spec engine: where `/think` converges on *what* to build,
|
|
23
|
+
`/spec-to-plan` converges on *how* to build it.
|
|
24
|
+
|
|
25
|
+
A plan is seeded from a **converged frame** and tracks **tasks** against the
|
|
26
|
+
spec's **coverage targets**. The CLI is **deterministic and move-driven** — you
|
|
27
|
+
(the agent) choose the next move; the CLI tracks state and tells you what's still
|
|
28
|
+
missing. Run `devague plan learn` for the method and `devague plan explain
|
|
29
|
+
<move>` for any single move.
|
|
30
|
+
|
|
31
|
+
## How to run
|
|
32
|
+
|
|
33
|
+
The entry point is `scripts/spec-to-plan.sh`. Invoke it from the repository you
|
|
34
|
+
are speccing (plans persist under `.devague/` in the current directory, alongside
|
|
35
|
+
the frames they derive from):
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bash .claude/skills/spec-to-plan/scripts/spec-to-plan.sh <move> [args...]
|
|
39
|
+
bash .claude/skills/spec-to-plan/scripts/spec-to-plan.sh status
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
It resolves the CLI portably — an installed `devague` on `PATH` (the normal
|
|
43
|
+
case), falling back to `uv run devague` inside the devague checkout, else an
|
|
44
|
+
install hint. Every move except `status` is forwarded verbatim as `devague plan
|
|
45
|
+
<move>`, so you can equally call the CLI directly (`devague plan <move> …`).
|
|
46
|
+
|
|
47
|
+
### Moves
|
|
48
|
+
|
|
49
|
+
| Move | What it does |
|
|
50
|
+
|------|--------------|
|
|
51
|
+
| `new --frame <slug>` | Seed a plan from a **converged** frame. Derives the coverage targets (`c*`/`h*`) the plan must satisfy. Refuses an unconverged frame. |
|
|
52
|
+
| `task "<summary>"` | Add a task. `--accept "<crit>"`, `--dep <tN>`, `--covers <c*/h*>` (each repeatable); `--origin llm` lands it `proposed`. |
|
|
53
|
+
| `accept <tN> "<crit>"` | Add an acceptance criterion to a task. |
|
|
54
|
+
| `depend <tN> --on <tM>` | Record that task `tN` depends on `tM`. |
|
|
55
|
+
| `cover <tN> --target <c*/h*>` | Mark a task as covering a coverage target. |
|
|
56
|
+
| `confirm <tN>` / `reject <tN>` | Resolve a task. **User-only decision.** |
|
|
57
|
+
| `risk "<text>" --kind <kind>` | Record a first-class plan risk (`--task <tN>` to attach). |
|
|
58
|
+
| `converge` | Evaluate the gate against the **live** source frame; list remaining gaps. |
|
|
59
|
+
| `export` | Write the buildable plan to `docs/plans/` — only after `converge` passes. |
|
|
60
|
+
| `show` / `list` | Render a plan / list plans (`--json` for raw state). |
|
|
61
|
+
| `learn` / `explain <move>` | Teach the method / explain one move. |
|
|
62
|
+
|
|
63
|
+
Risk kinds (shared with the frame engine): `unknown_nonblocking`,
|
|
64
|
+
`unknown_blocking`, `out_of_scope`, `follow_up`.
|
|
65
|
+
|
|
66
|
+
### `status` — the next-move helper
|
|
67
|
+
|
|
68
|
+
`status` is a wrapper-only verb. It reads `devague plan converge --json` +
|
|
69
|
+
`devague plan list --json` and prints where the current plan stands, the
|
|
70
|
+
remaining gaps, and the recommended next move derived from the first gap.
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
plan: my-feature (1 plan total)
|
|
74
|
+
convergence: NOT passed — 2 gap(s):
|
|
75
|
+
- coverage target c5 (boundary) has no confirmed task
|
|
76
|
+
- task t2 has no acceptance criteria
|
|
77
|
+
|
|
78
|
+
recommended next move (first gap):
|
|
79
|
+
cover c5: devague plan task "<summary>" --covers c5 --accept "<...>"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Run it whenever you're unsure what to do next.
|
|
83
|
+
|
|
84
|
+
## Hard rules (do not violate)
|
|
85
|
+
|
|
86
|
+
These are the point of the method — convergence must mean something.
|
|
87
|
+
|
|
88
|
+
- **Seed from a converged spec only.** `plan new` refuses a frame that hasn't
|
|
89
|
+
converged. The plan's coverage targets *are* the spec's confirmed claims and
|
|
90
|
+
honesty conditions — there is nothing honest to plan against until the spec
|
|
91
|
+
converges.
|
|
92
|
+
- **LLM proposals stay proposed.** A task captured with `--origin llm` lands as
|
|
93
|
+
`proposed`. **Never `confirm` your own proposal.** Confirmation is a user-only
|
|
94
|
+
decision — surface the proposed task and let the user confirm or reject it.
|
|
95
|
+
- **Cover every target; criteria on every task.** The gate requires every
|
|
96
|
+
coverage target to be covered by a confirmed task, and every confirmed task to
|
|
97
|
+
carry at least one acceptance criterion. Don't hand-wave a task as "done-ish."
|
|
98
|
+
- **Keep the graph honest.** Dependencies must reference real tasks and form an
|
|
99
|
+
acyclic graph; the gate rejects dangling deps and cycles.
|
|
100
|
+
- **Park real unknowns as risks; don't paper over them.** A genuinely unknown
|
|
101
|
+
decision is an `unknown_blocking` risk — it holds back convergence, by design.
|
|
102
|
+
- **Converge against the live frame.** `converge`/`export` re-load the source
|
|
103
|
+
frame every time. If the frame was deleted or has regressed below convergence,
|
|
104
|
+
they refuse — re-converge the spec (in `/think`) first.
|
|
105
|
+
|
|
106
|
+
## Output contract
|
|
107
|
+
|
|
108
|
+
Results go to **stdout**, diagnostics and errors to **stderr** — a strict split
|
|
109
|
+
you can rely on when parsing. Pass `--json` to any move for a structured payload.
|
|
110
|
+
Exit code `0` on success, non-zero on user error (with a `hint:` line). Plans
|
|
111
|
+
live under `.devague/plans/` in the current directory; the exported plan-md lands
|
|
112
|
+
in `docs/plans/`.
|
|
113
|
+
|
|
114
|
+
## Worked example
|
|
115
|
+
|
|
116
|
+
Picking up after `/think` exported a spec for the frame `my-feature`:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
p() { bash .claude/skills/spec-to-plan/scripts/spec-to-plan.sh "$@"; }
|
|
120
|
+
|
|
121
|
+
p new --frame my-feature # seeds the plan + its coverage targets
|
|
122
|
+
p show # see the c*/h* targets you must cover
|
|
123
|
+
|
|
124
|
+
p task "Build the core engine" --accept "engine has a convergence gate" \
|
|
125
|
+
--covers c1 --covers c3
|
|
126
|
+
p task "Pressure-test honesty conditions" --dep t1 --covers h1 --covers h2 \
|
|
127
|
+
--accept "every honesty condition maps to a test"
|
|
128
|
+
|
|
129
|
+
# Park a genuine unknown instead of guessing:
|
|
130
|
+
p risk "exact rollout sequencing" --kind unknown_nonblocking
|
|
131
|
+
|
|
132
|
+
p status # what's left + the next move
|
|
133
|
+
p converge # gate; resolve any listed gaps
|
|
134
|
+
p export # writes docs/plans/my-feature.md once converged
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The exported plan-md is a buildable artifact: topologically ordered tasks, each
|
|
138
|
+
with acceptance criteria and the spec targets it covers. It feeds directly into
|
|
139
|
+
implementation (or `superpowers:writing-plans`).
|
|
140
|
+
|
|
141
|
+
## Provenance
|
|
142
|
+
|
|
143
|
+
This is a **first-party** skill — its origin is `agentculture/devague`, where the
|
|
144
|
+
devague agent maintains it alongside the tool it operates (dogfooding), next to
|
|
145
|
+
its sibling `/think`. It is the *inverse* of the other skills under
|
|
146
|
+
`.claude/skills/`, which devague vendors **from** steward. When ready, steward
|
|
147
|
+
pulls it **from** devague and broadcasts it to the rest of the AgentCulture mesh.
|
|
148
|
+
The `cite, don't import` policy still holds: downstream repos copy it, they don't
|
|
149
|
+
symlink or depend on it. See `docs/skill-sources.md`.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# spec-to-plan.sh — drive devague's spec→plan engine (the /spec-to-plan skill).
|
|
3
|
+
#
|
|
4
|
+
# The skill is named `spec-to-plan`; the product/CLI it drives is `devague` (the
|
|
5
|
+
# idea→spec half lives in the sibling /think skill). This wrapper forwards every
|
|
6
|
+
# move to `devague plan <move>` verbatim, and adds one value-add subcommand —
|
|
7
|
+
# `status` — that reads the plan convergence gate and names the recommended next
|
|
8
|
+
# move. It is the forward leg: seed a plan from a *converged* frame, then work it
|
|
9
|
+
# into a buildable plan.
|
|
10
|
+
#
|
|
11
|
+
# Origin: authored and maintained in agentculture/devague. steward pulls this
|
|
12
|
+
# skill from here and broadcasts it to the rest of the AgentCulture mesh, so it
|
|
13
|
+
# is written to run anywhere — portable bash, no devague-checkout assumptions.
|
|
14
|
+
#
|
|
15
|
+
# Plans persist under .devague/ in the current directory (alongside frames), so
|
|
16
|
+
# run from the repo you are speccing.
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
# ── resolve the devague CLI (mesh-first, then local-dev fallback) ───────────
|
|
21
|
+
DEVAGUE=()
|
|
22
|
+
resolve_devague() {
|
|
23
|
+
if command -v devague >/dev/null 2>&1; then
|
|
24
|
+
DEVAGUE=(devague) # installed tool — the normal mesh case
|
|
25
|
+
return 0
|
|
26
|
+
fi
|
|
27
|
+
# Local-dev fallback: inside the devague checkout, run via uv.
|
|
28
|
+
local dir="$PWD"
|
|
29
|
+
while [ -n "$dir" ] && [ "$dir" != "/" ]; do
|
|
30
|
+
if [ -f "$dir/pyproject.toml" ] \
|
|
31
|
+
&& grep -q '^name = "devague"' "$dir/pyproject.toml" 2>/dev/null; then
|
|
32
|
+
if command -v uv >/dev/null 2>&1; then
|
|
33
|
+
DEVAGUE=(uv run devague)
|
|
34
|
+
return 0
|
|
35
|
+
fi
|
|
36
|
+
break
|
|
37
|
+
fi
|
|
38
|
+
dir=$(dirname "$dir")
|
|
39
|
+
done
|
|
40
|
+
cat >&2 <<'EOF'
|
|
41
|
+
error: devague CLI not found.
|
|
42
|
+
hint: install it with `uv tool install devague` (or `pipx install devague`),
|
|
43
|
+
or run from inside the devague checkout with `uv` available.
|
|
44
|
+
https://github.com/agentculture/devague
|
|
45
|
+
EOF
|
|
46
|
+
return 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
usage() {
|
|
50
|
+
cat <<'EOF'
|
|
51
|
+
spec-to-plan.sh — drive devague's spec→plan engine (the /spec-to-plan skill).
|
|
52
|
+
|
|
53
|
+
Usage:
|
|
54
|
+
spec-to-plan.sh <move> [args...] forward a `devague plan` move
|
|
55
|
+
spec-to-plan.sh status [--plan S] where the plan stands + the next move
|
|
56
|
+
spec-to-plan.sh help this help
|
|
57
|
+
|
|
58
|
+
Moves (forwarded to `devague plan`; run `devague plan learn` for the method):
|
|
59
|
+
new start a plan from a CONVERGED frame (--frame <slug>)
|
|
60
|
+
task add a task (--accept / --dep / --covers, --origin user|llm)
|
|
61
|
+
accept add an acceptance criterion to a task
|
|
62
|
+
depend record that a task depends on another (--on)
|
|
63
|
+
cover mark a task as covering a coverage target (c*/h*)
|
|
64
|
+
confirm confirm a task (USER-only decision)
|
|
65
|
+
reject reject a task
|
|
66
|
+
risk record a first-class plan risk
|
|
67
|
+
converge check whether the plan can export
|
|
68
|
+
export write the buildable plan (only after converge passes)
|
|
69
|
+
show / list render a plan / list plans
|
|
70
|
+
learn teach the method | explain <move> explain one move
|
|
71
|
+
|
|
72
|
+
Plans persist under .devague/ in the current directory — run from the repo you
|
|
73
|
+
are speccing. Results go to stdout, diagnostics to stderr; pass --json to any
|
|
74
|
+
move for structured output.
|
|
75
|
+
|
|
76
|
+
Note: `status` is a wrapper-only verb; everything else is forwarded verbatim as
|
|
77
|
+
`devague plan <move>`, so new plan moves work without editing this script.
|
|
78
|
+
|
|
79
|
+
Prior leg: a plan is seeded from a converged frame produced by the /think skill.
|
|
80
|
+
EOF
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# ── status: read the plan convergence gate and recommend the next move ──────
|
|
84
|
+
cmd_status() {
|
|
85
|
+
local list_json conv_out conv_err conv_rc req_plan="" prev="" tmp_err
|
|
86
|
+
|
|
87
|
+
for arg in "$@"; do
|
|
88
|
+
case "$prev" in --plan) req_plan="$arg" ;; esac
|
|
89
|
+
case "$arg" in --plan=*) req_plan="${arg#--plan=}" ;; esac
|
|
90
|
+
prev="$arg"
|
|
91
|
+
done
|
|
92
|
+
|
|
93
|
+
list_json="$("${DEVAGUE[@]}" plan list --json 2>/dev/null || true)"
|
|
94
|
+
|
|
95
|
+
tmp_err="$(mktemp)"
|
|
96
|
+
set +e
|
|
97
|
+
conv_out="$("${DEVAGUE[@]}" plan converge --json "$@" 2>"$tmp_err")"
|
|
98
|
+
conv_rc=$?
|
|
99
|
+
set -e
|
|
100
|
+
conv_err="$(cat "$tmp_err")"
|
|
101
|
+
rm -f "$tmp_err"
|
|
102
|
+
|
|
103
|
+
DEVAGUE_LIST_JSON="$list_json" \
|
|
104
|
+
DEVAGUE_CONV_JSON="$conv_out" \
|
|
105
|
+
DEVAGUE_CONV_ERR="$conv_err" \
|
|
106
|
+
DEVAGUE_CONV_RC="$conv_rc" \
|
|
107
|
+
DEVAGUE_REQ_PLAN="$req_plan" \
|
|
108
|
+
python3 - <<'PY'
|
|
109
|
+
import json
|
|
110
|
+
import os
|
|
111
|
+
import re
|
|
112
|
+
import sys
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def load(name):
|
|
116
|
+
raw = os.environ.get(name, "").strip()
|
|
117
|
+
if not raw:
|
|
118
|
+
return None
|
|
119
|
+
try:
|
|
120
|
+
return json.loads(raw)
|
|
121
|
+
except json.JSONDecodeError:
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
lst = load("DEVAGUE_LIST_JSON") or {}
|
|
126
|
+
conv = load("DEVAGUE_CONV_JSON")
|
|
127
|
+
conv_err = os.environ.get("DEVAGUE_CONV_ERR", "").strip()
|
|
128
|
+
req_plan = os.environ.get("DEVAGUE_REQ_PLAN", "").strip()
|
|
129
|
+
try:
|
|
130
|
+
conv_rc = int(os.environ.get("DEVAGUE_CONV_RC", "0") or "0")
|
|
131
|
+
except ValueError:
|
|
132
|
+
conv_rc = 0
|
|
133
|
+
|
|
134
|
+
plans = lst.get("plans") or []
|
|
135
|
+
current = lst.get("current")
|
|
136
|
+
|
|
137
|
+
if not plans:
|
|
138
|
+
print("no plans yet — seed one from a converged frame:")
|
|
139
|
+
print(' devague plan new --frame "<slug>"')
|
|
140
|
+
print(" (the frame must have converged in the /think skill first)")
|
|
141
|
+
sys.exit(0)
|
|
142
|
+
|
|
143
|
+
shown = req_plan or current or "(none selected)"
|
|
144
|
+
total = len(plans)
|
|
145
|
+
print(f"plan: {shown} ({total} plan{'s' if total != 1 else ''} total)")
|
|
146
|
+
|
|
147
|
+
if conv is None:
|
|
148
|
+
# A non-zero converge exit is a real error (deleted/regressed source frame,
|
|
149
|
+
# bad --plan) — relay devague's own error:/hint: lines instead of masking it.
|
|
150
|
+
if conv_rc != 0 and conv_err:
|
|
151
|
+
sys.stderr.write(conv_err + "\n")
|
|
152
|
+
sys.exit(conv_rc)
|
|
153
|
+
print("convergence: unknown (could not evaluate the plan)")
|
|
154
|
+
print("next move: devague plan show # inspect the plan")
|
|
155
|
+
sys.exit(0)
|
|
156
|
+
|
|
157
|
+
if conv.get("passed"):
|
|
158
|
+
print("convergence: PASSED ✓")
|
|
159
|
+
print("next move: devague plan export # write the buildable plan")
|
|
160
|
+
sys.exit(0)
|
|
161
|
+
|
|
162
|
+
missing = conv.get("missing") or []
|
|
163
|
+
print(f"convergence: NOT passed — {len(missing)} gap(s):")
|
|
164
|
+
for gap in missing:
|
|
165
|
+
print(f" - {gap}")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def suggest(gap):
|
|
169
|
+
if "no tasks yet" in gap:
|
|
170
|
+
return 'devague plan task "<summary>" --covers <c*/h*> --accept "<criterion>"'
|
|
171
|
+
m = re.search(r"coverage target (\w+) ", gap)
|
|
172
|
+
if m:
|
|
173
|
+
tid = m.group(1)
|
|
174
|
+
return (
|
|
175
|
+
f'cover {tid}: devague plan task "<summary>" --covers {tid} --accept "<...>"'
|
|
176
|
+
f" (or: devague plan cover <tN> --target {tid})"
|
|
177
|
+
)
|
|
178
|
+
m = re.search(r"task (t\d+) has no acceptance", gap)
|
|
179
|
+
if m:
|
|
180
|
+
return f'devague plan accept {m.group(1)} "<acceptance criterion>"'
|
|
181
|
+
m = re.search(r"task (t\d+) still proposed", gap)
|
|
182
|
+
if m:
|
|
183
|
+
tid = m.group(1)
|
|
184
|
+
return (
|
|
185
|
+
f"this is an LLM proposal — the USER decides:"
|
|
186
|
+
f" devague plan confirm {tid} (or: devague plan reject {tid})"
|
|
187
|
+
)
|
|
188
|
+
m = re.search(r"task (t\d+) depends on unknown task (t\d+)", gap)
|
|
189
|
+
if m:
|
|
190
|
+
return f"fix {m.group(1)}'s dependency on missing {m.group(2)} (add it, or drop the dep)"
|
|
191
|
+
if "dependency cycle" in gap:
|
|
192
|
+
return "break the dependency cycle: re-point one task's --dep so the graph is acyclic"
|
|
193
|
+
m = re.search(r"blocking risk (r\d+)", gap)
|
|
194
|
+
if m:
|
|
195
|
+
return f"resolve {m.group(1)}: cover it with a task, or re-record it as non-blocking"
|
|
196
|
+
return "devague plan show # inspect and decide"
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
if missing:
|
|
200
|
+
print()
|
|
201
|
+
print("recommended next move (first gap):")
|
|
202
|
+
print(f" {suggest(missing[0])}")
|
|
203
|
+
PY
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
main() {
|
|
207
|
+
case "${1:-help}" in
|
|
208
|
+
help | -h | --help)
|
|
209
|
+
usage
|
|
210
|
+
return 0
|
|
211
|
+
;;
|
|
212
|
+
status)
|
|
213
|
+
shift
|
|
214
|
+
resolve_devague
|
|
215
|
+
cmd_status "$@"
|
|
216
|
+
;;
|
|
217
|
+
*)
|
|
218
|
+
# Forward everything else to `devague plan <move>` verbatim, so the
|
|
219
|
+
# CLI's own parser owns the plan surface.
|
|
220
|
+
resolve_devague
|
|
221
|
+
exec "${DEVAGUE[@]}" plan "$@"
|
|
222
|
+
;;
|
|
223
|
+
esac
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
main "$@"
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: think
|
|
3
3
|
description: >
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
("pretend it shipped"),
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
spec", "announcement frame", or "devague", or when a feature request is too
|
|
11
|
-
vague to build yet.
|
|
12
|
-
|
|
4
|
+
Think a vague feature idea into a buildable spec by working backwards (the
|
|
5
|
+
idea→spec leg; drives the `devague` CLI). Start from the announcement
|
|
6
|
+
("pretend it shipped"), capture and classify claims, interrogate them with
|
|
7
|
+
honesty conditions and hard questions, park open vagueness as a first-class
|
|
8
|
+
object, and export a spec only once the frame *converges*. Use when the user
|
|
9
|
+
says "think this through", "spec this", "work backwards", "turn this idea into
|
|
10
|
+
a spec", "announcement frame", or "devague", or when a feature request is too
|
|
11
|
+
vague to build yet. Once a spec exports, hand off to the sibling /spec-to-plan
|
|
12
|
+
skill to turn it into a plan. Authored and maintained in agentculture/devague
|
|
13
|
+
(origin = devague); steward pulls this skill from here and broadcasts it to the
|
|
13
14
|
AgentCulture mesh — it is NOT vendored from steward like the other skills here.
|
|
14
15
|
---
|
|
15
16
|
|
|
16
|
-
#
|
|
17
|
+
# think — work an idea backwards into a buildable spec
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
The skill is named **`think`**; the product/CLI it drives is **`devague`**. (The
|
|
20
|
+
forward leg — turning a converged spec into a plan — is the sibling
|
|
21
|
+
**`/spec-to-plan`** skill, which drives `devague plan`.)
|
|
22
|
+
|
|
23
|
+
`think` turns a vague feature idea into a buildable spec by **working
|
|
19
24
|
backwards**: you start from the announcement you'd make if it had already
|
|
20
25
|
shipped, then build an **Announcement Frame** by capturing claims, pressure
|
|
21
26
|
-testing them, parking what's still genuinely unknown, and only exporting once
|
|
@@ -31,12 +36,12 @@ reads the convergence gate and tells you the recommended next move.
|
|
|
31
36
|
|
|
32
37
|
## How to run
|
|
33
38
|
|
|
34
|
-
The entry point is `scripts/
|
|
39
|
+
The entry point is `scripts/think.sh`. Invoke it from the repository you are
|
|
35
40
|
speccing (frames persist under `.devague/` in the current directory):
|
|
36
41
|
|
|
37
42
|
```bash
|
|
38
|
-
bash .claude/skills/
|
|
39
|
-
bash .claude/skills/
|
|
43
|
+
bash .claude/skills/think/scripts/think.sh <move> [args...]
|
|
44
|
+
bash .claude/skills/think/scripts/think.sh status
|
|
40
45
|
```
|
|
41
46
|
|
|
42
47
|
It resolves the CLI portably — an installed `devague` on `PATH` (the normal
|
|
@@ -129,7 +134,7 @@ A short end-to-end session (the kind you'd run to spec a feature like
|
|
|
129
134
|
[devague#5](https://github.com/agentculture/devague/issues/5)):
|
|
130
135
|
|
|
131
136
|
```bash
|
|
132
|
-
d() { bash .claude/skills/
|
|
137
|
+
d() { bash .claude/skills/think/scripts/think.sh "$@"; }
|
|
133
138
|
|
|
134
139
|
d new "Devague ships a documented spec contract"
|
|
135
140
|
d capture --kind audience "devague + the assisting LLM"
|
|
@@ -150,8 +155,28 @@ d converge # gate; resolve any listed gaps
|
|
|
150
155
|
d export # writes docs/specs/<slug>.md once converged
|
|
151
156
|
```
|
|
152
157
|
|
|
153
|
-
The exported spec-md is a buildable artifact
|
|
154
|
-
|
|
158
|
+
The exported spec-md is a buildable artifact.
|
|
159
|
+
|
|
160
|
+
## After export — commit, then hand off
|
|
161
|
+
|
|
162
|
+
Once `export` writes the spec **and the user has reviewed it**, close the
|
|
163
|
+
idea→spec leg cleanly before moving on:
|
|
164
|
+
|
|
165
|
+
1. **Commit the spec.** Commit the exported `docs/specs/<slug>.md` (along with
|
|
166
|
+
the `.devague/<slug>.json` frame state and any review artifact under
|
|
167
|
+
`docs/reviews/`) so the converged frame is durable in history, not just on
|
|
168
|
+
disk. Use a focused message, e.g. `git commit -m "spec: <slug> (devague
|
|
169
|
+
/think)"`. The frame and the spec are the evidence trail for every confirmed
|
|
170
|
+
claim — keep them together. (Per the repo's standing convention this normally
|
|
171
|
+
becomes a branch + PR via the `cicd` skill; commit-only is fine when the user
|
|
172
|
+
asks for it.)
|
|
173
|
+
2. **Hand off to `/spec-to-plan`.** The forward leg is the sibling skill:
|
|
174
|
+
`devague plan new --frame <slug>` seeds a plan from the converged frame and
|
|
175
|
+
works it forward into a buildable plan (it can equally feed
|
|
176
|
+
`superpowers:writing-plans` or a normal implementation PR).
|
|
177
|
+
|
|
178
|
+
Don't pause for a "what next?" menu after a reviewed export — the standing flow
|
|
179
|
+
is **commit, then `/spec-to-plan`**.
|
|
155
180
|
|
|
156
181
|
## Provenance
|
|
157
182
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
2
|
+
# think.sh — drive devague's working-backwards idea→spec engine (the /think skill).
|
|
3
3
|
#
|
|
4
|
+
# The skill is named `think`; the product/CLI it drives is `devague` (the spec→plan
|
|
5
|
+
# half lives in the sibling /spec-to-plan skill, which drives `devague plan`).
|
|
4
6
|
# devague turns a vague feature idea into a buildable spec by working backwards.
|
|
5
7
|
# This wrapper is the agent-facing operator for the deterministic devague CLI:
|
|
6
8
|
# it resolves the CLI portably, forwards every move verbatim, and adds one
|
|
@@ -47,12 +49,12 @@ EOF
|
|
|
47
49
|
|
|
48
50
|
usage() {
|
|
49
51
|
cat <<'EOF'
|
|
50
|
-
|
|
52
|
+
think.sh — drive devague's working-backwards idea→spec engine (the /think skill).
|
|
51
53
|
|
|
52
54
|
Usage:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
think.sh <move> [args...] forward a devague move
|
|
56
|
+
think.sh status [--frame S] where the frame stands + the next move
|
|
57
|
+
think.sh help this help
|
|
56
58
|
|
|
57
59
|
Moves (forwarded to the devague CLI; run `devague learn` for the full method):
|
|
58
60
|
new start a frame from the announcement ("pretend it shipped")
|
|
@@ -73,6 +75,9 @@ any move for structured output.
|
|
|
73
75
|
Note: `status` is a wrapper-only verb (the CLI has no `status`); everything
|
|
74
76
|
else is forwarded verbatim, so new devague moves work without editing this
|
|
75
77
|
script.
|
|
78
|
+
|
|
79
|
+
Next leg: once a frame exports a spec, hand off to the /spec-to-plan skill
|
|
80
|
+
(`devague plan ...`) to turn that spec into a buildable plan.
|
|
76
81
|
EOF
|
|
77
82
|
}
|
|
78
83
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
devague-now-ships-a-documented-spec-contract-every
|