devague 0.3.2__tar.gz → 0.4.0__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.
Files changed (100) hide show
  1. devague-0.4.0/.claude/skills/spec-to-plan/SKILL.md +149 -0
  2. devague-0.4.0/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +226 -0
  3. devague-0.4.0/.claude/skills/think/SKILL.md +171 -0
  4. devague-0.4.0/.claude/skills/think/scripts/think.sh +239 -0
  5. {devague-0.3.2 → devague-0.4.0}/.flake8 +1 -1
  6. {devague-0.3.2 → devague-0.4.0}/CHANGELOG.md +18 -0
  7. {devague-0.3.2 → devague-0.4.0}/CLAUDE.md +52 -14
  8. devague-0.4.0/PKG-INFO +58 -0
  9. devague-0.4.0/README.md +41 -0
  10. {devague-0.3.2 → devague-0.4.0}/devague/cli/__init__.py +2 -0
  11. devague-0.4.0/devague/cli/_commands/plan.py +436 -0
  12. devague-0.4.0/devague/cli/_plans.py +29 -0
  13. devague-0.4.0/devague/plan.py +179 -0
  14. devague-0.4.0/devague/plan_convergence.py +142 -0
  15. devague-0.4.0/devague/plan_store.py +60 -0
  16. devague-0.4.0/devague/render/plan_md.py +91 -0
  17. {devague-0.3.2 → devague-0.4.0}/docs/skill-sources.md +19 -0
  18. devague-0.4.0/docs/superpowers/specs/2026-05-23-devague-spec-to-plan-design.md +198 -0
  19. {devague-0.3.2 → devague-0.4.0}/pyproject.toml +2 -2
  20. devague-0.4.0/tests/test_cli_plan.py +291 -0
  21. devague-0.4.0/tests/test_plan.py +102 -0
  22. devague-0.4.0/tests/test_plan_convergence.py +121 -0
  23. devague-0.4.0/tests/test_plan_store.py +67 -0
  24. devague-0.4.0/tests/test_render_plan.py +71 -0
  25. devague-0.4.0/tests/test_spec_to_plan_skill.py +124 -0
  26. devague-0.4.0/tests/test_think_skill.py +144 -0
  27. {devague-0.3.2 → devague-0.4.0}/uv.lock +1 -1
  28. devague-0.3.2/PKG-INFO +0 -23
  29. devague-0.3.2/README.md +0 -6
  30. {devague-0.3.2 → devague-0.4.0}/.claude/skills/cicd/SKILL.md +0 -0
  31. {devague-0.3.2 → devague-0.4.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  32. {devague-0.3.2 → devague-0.4.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  33. {devague-0.3.2 → devague-0.4.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  34. {devague-0.3.2 → devague-0.4.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  35. {devague-0.3.2 → devague-0.4.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  36. {devague-0.3.2 → devague-0.4.0}/.claude/skills/communicate/SKILL.md +0 -0
  37. {devague-0.3.2 → devague-0.4.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  38. {devague-0.3.2 → devague-0.4.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  39. {devague-0.3.2 → devague-0.4.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  40. {devague-0.3.2 → devague-0.4.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  41. {devague-0.3.2 → devague-0.4.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  42. {devague-0.3.2 → devague-0.4.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  43. {devague-0.3.2 → devague-0.4.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  44. {devague-0.3.2 → devague-0.4.0}/.claude/skills/run-tests/SKILL.md +0 -0
  45. {devague-0.3.2 → devague-0.4.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  46. {devague-0.3.2 → devague-0.4.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
  47. {devague-0.3.2 → devague-0.4.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  48. {devague-0.3.2 → devague-0.4.0}/.claude/skills/version-bump/SKILL.md +0 -0
  49. {devague-0.3.2 → devague-0.4.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
  50. {devague-0.3.2 → devague-0.4.0}/.claude/skills.local.yaml.example +0 -0
  51. {devague-0.3.2 → devague-0.4.0}/.github/workflows/publish.yml +0 -0
  52. {devague-0.3.2 → devague-0.4.0}/.github/workflows/security-checks.yml +0 -0
  53. {devague-0.3.2 → devague-0.4.0}/.github/workflows/tests.yml +0 -0
  54. {devague-0.3.2 → devague-0.4.0}/.gitignore +0 -0
  55. {devague-0.3.2 → devague-0.4.0}/.markdownlint-cli2.yaml +0 -0
  56. {devague-0.3.2 → devague-0.4.0}/.pre-commit-config.yaml +0 -0
  57. {devague-0.3.2 → devague-0.4.0}/LICENSE +0 -0
  58. {devague-0.3.2 → devague-0.4.0}/culture.yaml +0 -0
  59. {devague-0.3.2 → devague-0.4.0}/devague/__init__.py +0 -0
  60. {devague-0.3.2 → devague-0.4.0}/devague/__main__.py +0 -0
  61. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/__init__.py +0 -0
  62. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/capture.py +0 -0
  63. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/confirm.py +0 -0
  64. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/converge.py +0 -0
  65. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/explain.py +0 -0
  66. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/export.py +0 -0
  67. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/interrogate.py +0 -0
  68. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/learn.py +0 -0
  69. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/list_frames.py +0 -0
  70. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/new.py +0 -0
  71. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/park.py +0 -0
  72. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/reject.py +0 -0
  73. {devague-0.3.2 → devague-0.4.0}/devague/cli/_commands/show.py +0 -0
  74. {devague-0.3.2 → devague-0.4.0}/devague/cli/_errors.py +0 -0
  75. {devague-0.3.2 → devague-0.4.0}/devague/cli/_frames.py +0 -0
  76. {devague-0.3.2 → devague-0.4.0}/devague/cli/_output.py +0 -0
  77. {devague-0.3.2 → devague-0.4.0}/devague/convergence.py +0 -0
  78. {devague-0.3.2 → devague-0.4.0}/devague/frame.py +0 -0
  79. {devague-0.3.2 → devague-0.4.0}/devague/render/__init__.py +0 -0
  80. {devague-0.3.2 → devague-0.4.0}/devague/render/frame_md.py +0 -0
  81. {devague-0.3.2 → devague-0.4.0}/devague/render/spec_md.py +0 -0
  82. {devague-0.3.2 → devague-0.4.0}/devague/store.py +0 -0
  83. {devague-0.3.2 → devague-0.4.0}/docs/superpowers/plans/2026-05-22-specifix-onboarding.md +0 -0
  84. {devague-0.3.2 → devague-0.4.0}/docs/superpowers/plans/2026-05-23-devague-rename.md +0 -0
  85. {devague-0.3.2 → devague-0.4.0}/docs/superpowers/plans/2026-05-23-devague-working-backwards-engine.md +0 -0
  86. {devague-0.3.2 → devague-0.4.0}/docs/superpowers/specs/2026-05-22-specifix-onboarding-design.md +0 -0
  87. {devague-0.3.2 → devague-0.4.0}/docs/superpowers/specs/2026-05-23-devague-working-backwards-design.md +0 -0
  88. {devague-0.3.2 → devague-0.4.0}/sonar-project.properties +0 -0
  89. {devague-0.3.2 → devague-0.4.0}/tests/__init__.py +0 -0
  90. {devague-0.3.2 → devague-0.4.0}/tests/test_cli_affordances.py +0 -0
  91. {devague-0.3.2 → devague-0.4.0}/tests/test_cli_chassis.py +0 -0
  92. {devague-0.3.2 → devague-0.4.0}/tests/test_cli_converge_export.py +0 -0
  93. {devague-0.3.2 → devague-0.4.0}/tests/test_cli_errors.py +0 -0
  94. {devague-0.3.2 → devague-0.4.0}/tests/test_cli_moves.py +0 -0
  95. {devague-0.3.2 → devague-0.4.0}/tests/test_cli_output.py +0 -0
  96. {devague-0.3.2 → devague-0.4.0}/tests/test_convergence.py +0 -0
  97. {devague-0.3.2 → devague-0.4.0}/tests/test_frame.py +0 -0
  98. {devague-0.3.2 → devague-0.4.0}/tests/test_package.py +0 -0
  99. {devague-0.3.2 → devague-0.4.0}/tests/test_render.py +0 -0
  100. {devague-0.3.2 → devague-0.4.0}/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 "$@"
@@ -0,0 +1,171 @@
1
+ ---
2
+ name: think
3
+ description: >
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
14
+ AgentCulture mesh — it is NOT vendored from steward like the other skills here.
15
+ ---
16
+
17
+ # think — work an idea backwards into a buildable spec
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
24
+ backwards**: you start from the announcement you'd make if it had already
25
+ shipped, then build an **Announcement Frame** by capturing claims, pressure
26
+ -testing them, parking what's still genuinely unknown, and only exporting once
27
+ the frame converges.
28
+
29
+ The CLI is **deterministic and move-driven** — it is *not* a wizard. There is no
30
+ fixed sequence of prompts. **You (the agent) choose the next move; the CLI just
31
+ tracks state and tells you what's still missing.** Run `devague learn` for the
32
+ canonical ten-stage arc and `devague explain <move>` for any single move.
33
+
34
+ This skill is the operator: a portable wrapper plus one helper (`status`) that
35
+ reads the convergence gate and tells you the recommended next move.
36
+
37
+ ## How to run
38
+
39
+ The entry point is `scripts/think.sh`. Invoke it from the repository you are
40
+ speccing (frames persist under `.devague/` in the current directory):
41
+
42
+ ```bash
43
+ bash .claude/skills/think/scripts/think.sh <move> [args...]
44
+ bash .claude/skills/think/scripts/think.sh status
45
+ ```
46
+
47
+ It resolves the CLI portably — an installed `devague` on `PATH` (the normal
48
+ case), falling back to `uv run devague` when you are inside the devague checkout.
49
+ If neither resolves it prints an install hint (`uv tool install devague`). Every
50
+ move except `status` is forwarded verbatim, so you can equally call the CLI
51
+ directly (`devague <move> …`) when it is installed; the wrapper exists for
52
+ portable resolution and the `status` helper.
53
+
54
+ ### Moves
55
+
56
+ | Move | What it does |
57
+ |------|--------------|
58
+ | `new "<announcement>"` | Start a frame from the announcement (the first move). Seeds an auto-confirmed `announcement` claim. |
59
+ | `capture --kind <kind> "<text>"` | Record + classify a claim. `--origin llm` lands it as `proposed`. |
60
+ | `interrogate <id> --honesty "…"` | Attach an honesty condition (what must be true). Also `--hard-question`, `--risk`, `--contradicts`, `--blocking`. |
61
+ | `confirm <id>` / `reject <id>` | Resolve a claim (`c*`) or honesty condition (`h*`). **User-only decision.** |
62
+ | `park "<text>" --kind <kind>` | Move uncertainty into first-class open vagueness instead of forcing an answer. |
63
+ | `converge` | Evaluate the gate; list remaining gaps. |
64
+ | `export` | Write the buildable spec to `docs/specs/` — only after `converge` passes. |
65
+ | `show` / `list` | Render a frame / list frames (`--json` for raw state). |
66
+ | `learn` / `explain <move>` | Teach the method / explain one move. |
67
+
68
+ Claim kinds: `announcement`, `audience`, `after_state`, `before_state`,
69
+ `why_it_matters`, `boundary`, `success_signal`, `open_question`. Vagueness kinds:
70
+ `unknown_nonblocking`, `unknown_blocking`, `out_of_scope`, `follow_up`.
71
+
72
+ These are exactly the kinds the **shipped CLI enforces** (`CLAIM_KINDS` /
73
+ `VAGUENESS_KINDS` in `devague/frame.py`) — the skill documents the surface as
74
+ built, so every command here passes the CLI's `choices=` validation. A fuller
75
+ proposed type/state set, plus the formal per-move input/output/transition
76
+ contract, is tracked on the CLI side in
77
+ [#5](https://github.com/agentculture/devague/issues/5); for the authoritative
78
+ live shape of any move, run it with `--json` (or `devague learn --json` /
79
+ `devague explain <move>`). When the CLI's contract grows, re-sync this list.
80
+
81
+ ### `status` — the next-move helper
82
+
83
+ `status` is a wrapper-only verb (the CLI has no `status`). It reads
84
+ `converge --json` + `list --json` and prints where the current frame stands, the
85
+ remaining gaps, and the recommended next move derived from the first gap.
86
+ `converge --json` currently emits `{passed, missing}`, which is what the helper
87
+ consumes; if [#5](https://github.com/agentculture/devague/issues/5) enriches that
88
+ payload (e.g. structured `blockers` / `warnings` / `required_next_moves`),
89
+ `status` will surface the richer fields then.
90
+
91
+ ```text
92
+ frame: my-feature (1 frame total)
93
+ convergence: NOT passed — 2 gap(s):
94
+ - missing a 'boundary' / non-goal claim
95
+ - claim c2 has no confirmed honesty condition
96
+
97
+ recommended next move (first gap):
98
+ devague capture --kind boundary "<text>"
99
+ ```
100
+
101
+ Run it whenever you're unsure what to do next.
102
+
103
+ ## Hard rules (do not violate)
104
+
105
+ These are the point of the method — convergence must mean something.
106
+
107
+ - **LLM proposals stay proposed.** A claim captured with `--origin llm`, and any
108
+ honesty condition you (the agent) propose, lands as `proposed`. **Never
109
+ `confirm` your own proposal.** Confirmation is a user-only decision — surface
110
+ the proposal and let the user confirm or reject it. Proposed content must not
111
+ silently become an authoritative requirement.
112
+ - **Honesty conditions route through the user.** Propose them freely with
113
+ `interrogate --honesty`; the user owns whether they hold.
114
+ - **Converge, don't vibe.** `export` is gated on `converge` passing. Never claim
115
+ the frame is ready on a hunch — run `converge` (or `status`) and resolve every
116
+ listed gap. The gate requires confirmed `announcement` / `audience` /
117
+ `after_state`, a `before_state` or `why_it_matters`, a `boundary`, a
118
+ `success_signal`, a confirmed honesty condition on every spec-affecting claim,
119
+ and no unresolved blocking vagueness or hard question.
120
+ - **Park real unknowns; don't paper over them.** If something is genuinely
121
+ unknown, `park` it (blocking or non-blocking) rather than fabricating an
122
+ answer. Blocking vagueness holds back convergence — by design.
123
+
124
+ ## Output contract
125
+
126
+ Results go to **stdout**, diagnostics and errors to **stderr** — a strict split
127
+ you can rely on when parsing. Pass `--json` to any move for a structured payload
128
+ on the same stream. Exit code `0` on success, non-zero on user error (with a
129
+ `hint:` line). Frames live under `.devague/` in the current directory.
130
+
131
+ ## Worked example
132
+
133
+ A short end-to-end session (the kind you'd run to spec a feature like
134
+ [devague#5](https://github.com/agentculture/devague/issues/5)):
135
+
136
+ ```bash
137
+ d() { bash .claude/skills/think/scripts/think.sh "$@"; }
138
+
139
+ d new "Devague ships a documented spec contract"
140
+ d capture --kind audience "devague + the assisting LLM"
141
+ d capture --kind after_state "a vague idea becomes a buildable, pressure-tested spec"
142
+ d capture --kind why_it_matters "specs converge on evidence, not vibes"
143
+ d capture --kind boundary "not a full PRD generator; no fixed wizard"
144
+ d capture --kind success_signal "a frame exports only after the gate passes"
145
+
146
+ # Pressure-test a claim, then let the USER confirm the condition:
147
+ d interrogate c1 --honesty "the contract round-trips: save -> load -> identical frame"
148
+ # ...user reviews and runs: d confirm h1
149
+
150
+ # Park a genuine unknown instead of guessing:
151
+ d park "exact JSON schema versioning policy" --kind unknown_nonblocking
152
+
153
+ d status # what's left + the next move
154
+ d converge # gate; resolve any listed gaps
155
+ d export # writes docs/specs/<slug>.md once converged
156
+ ```
157
+
158
+ The exported spec-md is a buildable artifact. The next leg is the sibling
159
+ **`/spec-to-plan`** skill: `devague plan new --frame <slug>` seeds a plan from the
160
+ converged frame and works it forward into a buildable plan (it can equally feed
161
+ `superpowers:writing-plans` or a normal implementation PR).
162
+
163
+ ## Provenance
164
+
165
+ This is a **first-party** skill — its origin is `agentculture/devague`, where the
166
+ devague agent maintains it alongside the tool it operates (dogfooding). It is the
167
+ *inverse* of the other skills under `.claude/skills/`, which devague vendors
168
+ **from** steward. When this skill is ready, steward pulls it **from** devague and
169
+ broadcasts it to the rest of the AgentCulture mesh. The `cite, don't import`
170
+ policy still holds: downstream repos copy it, they don't symlink or depend on it.
171
+ See `docs/skill-sources.md`.