devague 0.10.0__tar.gz → 0.11.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.
Files changed (136) hide show
  1. {devague-0.10.0 → devague-0.11.1}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +12 -1
  2. {devague-0.10.0 → devague-0.11.1}/.claude/skills/spec-to-plan/SKILL.md +15 -7
  3. devague-0.11.1/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +102 -0
  4. {devague-0.10.0 → devague-0.11.1}/.claude/skills/think/SKILL.md +19 -14
  5. devague-0.11.1/.claude/skills/think/scripts/think.sh +101 -0
  6. {devague-0.10.0 → devague-0.11.1}/CHANGELOG.md +24 -0
  7. {devague-0.10.0 → devague-0.11.1}/CLAUDE.md +16 -5
  8. {devague-0.10.0 → devague-0.11.1}/PKG-INFO +1 -1
  9. {devague-0.10.0 → devague-0.11.1}/devague/cli/__init__.py +2 -0
  10. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/learn.py +1 -0
  11. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/plan.py +42 -0
  12. devague-0.11.1/devague/cli/_commands/status.py +53 -0
  13. devague-0.11.1/devague/cli/_status.py +99 -0
  14. {devague-0.10.0 → devague-0.11.1}/pyproject.toml +1 -1
  15. devague-0.11.1/tests/test_cli_status.py +204 -0
  16. {devague-0.10.0 → devague-0.11.1}/uv.lock +1 -1
  17. devague-0.10.0/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -199
  18. devague-0.10.0/.claude/skills/think/scripts/think.sh +0 -205
  19. {devague-0.10.0 → devague-0.11.1}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  20. {devague-0.10.0 → devague-0.11.1}/.claude/skills/cicd/SKILL.md +0 -0
  21. {devague-0.10.0 → devague-0.11.1}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  22. {devague-0.10.0 → devague-0.11.1}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  23. {devague-0.10.0 → devague-0.11.1}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  24. {devague-0.10.0 → devague-0.11.1}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  25. {devague-0.10.0 → devague-0.11.1}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  26. {devague-0.10.0 → devague-0.11.1}/.claude/skills/communicate/SKILL.md +0 -0
  27. {devague-0.10.0 → devague-0.11.1}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  28. {devague-0.10.0 → devague-0.11.1}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  29. {devague-0.10.0 → devague-0.11.1}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  30. {devague-0.10.0 → devague-0.11.1}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  31. {devague-0.10.0 → devague-0.11.1}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  32. {devague-0.10.0 → devague-0.11.1}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  33. {devague-0.10.0 → devague-0.11.1}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  34. {devague-0.10.0 → devague-0.11.1}/.claude/skills/run-tests/SKILL.md +0 -0
  35. {devague-0.10.0 → devague-0.11.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
  36. {devague-0.10.0 → devague-0.11.1}/.claude/skills/sonarclaude/SKILL.md +0 -0
  37. {devague-0.10.0 → devague-0.11.1}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  38. {devague-0.10.0 → devague-0.11.1}/.claude/skills/version-bump/SKILL.md +0 -0
  39. {devague-0.10.0 → devague-0.11.1}/.claude/skills/version-bump/scripts/bump.py +0 -0
  40. {devague-0.10.0 → devague-0.11.1}/.claude/skills.local.yaml.example +0 -0
  41. {devague-0.10.0 → devague-0.11.1}/.devague/current_plan +0 -0
  42. {devague-0.10.0 → devague-0.11.1}/.devague/frames/devague-0-6-0-ships-the-human-review-loop-devague.json +0 -0
  43. {devague-0.10.0 → devague-0.11.1}/.devague/frames/devague-now-ships-a-documented-spec-contract-every.json +0 -0
  44. {devague-0.10.0 → devague-0.11.1}/.devague/frames/devague-turns-a-converged-plan-into-parallel-simpl.json +0 -0
  45. {devague-0.10.0 → devague-0.11.1}/.devague/plans/devague-0-6-0-ships-the-human-review-loop-devague.json +0 -0
  46. {devague-0.10.0 → devague-0.11.1}/.devague/plans/devague-now-ships-a-documented-spec-contract-every.json +0 -0
  47. {devague-0.10.0 → devague-0.11.1}/.devague/plans/devague-turns-a-converged-plan-into-parallel-simpl.json +0 -0
  48. {devague-0.10.0 → devague-0.11.1}/.flake8 +0 -0
  49. {devague-0.10.0 → devague-0.11.1}/.github/workflows/publish.yml +0 -0
  50. {devague-0.10.0 → devague-0.11.1}/.github/workflows/security-checks.yml +0 -0
  51. {devague-0.10.0 → devague-0.11.1}/.github/workflows/tests.yml +0 -0
  52. {devague-0.10.0 → devague-0.11.1}/.gitignore +0 -0
  53. {devague-0.10.0 → devague-0.11.1}/.markdownlint-cli2.yaml +0 -0
  54. {devague-0.10.0 → devague-0.11.1}/.pre-commit-config.yaml +0 -0
  55. {devague-0.10.0 → devague-0.11.1}/LICENSE +0 -0
  56. {devague-0.10.0 → devague-0.11.1}/README.md +0 -0
  57. {devague-0.10.0 → devague-0.11.1}/culture.yaml +0 -0
  58. {devague-0.10.0 → devague-0.11.1}/devague/__init__.py +0 -0
  59. {devague-0.10.0 → devague-0.11.1}/devague/__main__.py +0 -0
  60. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/__init__.py +0 -0
  61. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/capture.py +0 -0
  62. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/confirm.py +0 -0
  63. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/converge.py +0 -0
  64. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/explain.py +0 -0
  65. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/export.py +0 -0
  66. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/interrogate.py +0 -0
  67. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/list_frames.py +0 -0
  68. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/new.py +0 -0
  69. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/park.py +0 -0
  70. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/question.py +0 -0
  71. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/reject.py +0 -0
  72. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/review.py +0 -0
  73. {devague-0.10.0 → devague-0.11.1}/devague/cli/_commands/show.py +0 -0
  74. {devague-0.10.0 → devague-0.11.1}/devague/cli/_errors.py +0 -0
  75. {devague-0.10.0 → devague-0.11.1}/devague/cli/_frames.py +0 -0
  76. {devague-0.10.0 → devague-0.11.1}/devague/cli/_output.py +0 -0
  77. {devague-0.10.0 → devague-0.11.1}/devague/cli/_paths.py +0 -0
  78. {devague-0.10.0 → devague-0.11.1}/devague/cli/_plans.py +0 -0
  79. {devague-0.10.0 → devague-0.11.1}/devague/convergence.py +0 -0
  80. {devague-0.10.0 → devague-0.11.1}/devague/frame.py +0 -0
  81. {devague-0.10.0 → devague-0.11.1}/devague/plan.py +0 -0
  82. {devague-0.10.0 → devague-0.11.1}/devague/plan_convergence.py +0 -0
  83. {devague-0.10.0 → devague-0.11.1}/devague/plan_store.py +0 -0
  84. {devague-0.10.0 → devague-0.11.1}/devague/questions_io.py +0 -0
  85. {devague-0.10.0 → devague-0.11.1}/devague/render/__init__.py +0 -0
  86. {devague-0.10.0 → devague-0.11.1}/devague/render/frame_md.py +0 -0
  87. {devague-0.10.0 → devague-0.11.1}/devague/render/plan_md.py +0 -0
  88. {devague-0.10.0 → devague-0.11.1}/devague/render/review_md.py +0 -0
  89. {devague-0.10.0 → devague-0.11.1}/devague/render/spec_md.py +0 -0
  90. {devague-0.10.0 → devague-0.11.1}/devague/store.py +0 -0
  91. {devague-0.10.0 → devague-0.11.1}/docs/assign-to-workforce-worked-example.md +0 -0
  92. {devague-0.10.0 → devague-0.11.1}/docs/examples/contract-example.json +0 -0
  93. {devague-0.10.0 → devague-0.11.1}/docs/llm-guidance.md +0 -0
  94. {devague-0.10.0 → devague-0.11.1}/docs/plans/2026-05-23-devague-0-6-0-ships-the-human-review-loop-devague.md +0 -0
  95. {devague-0.10.0 → devague-0.11.1}/docs/plans/2026-05-23-devague-now-ships-a-documented-spec-contract-every.md +0 -0
  96. {devague-0.10.0 → devague-0.11.1}/docs/plans/2026-05-23-devague-turns-a-converged-plan-into-parallel-simpl.md +0 -0
  97. {devague-0.10.0 → devague-0.11.1}/docs/reviews/spec-contract-frame-review.md +0 -0
  98. {devague-0.10.0 → devague-0.11.1}/docs/skill-sources.md +0 -0
  99. {devague-0.10.0 → devague-0.11.1}/docs/spec-contract.md +0 -0
  100. {devague-0.10.0 → devague-0.11.1}/docs/specs/2026-05-23-devague-0-6-0-ships-the-human-review-loop-devague.md +0 -0
  101. {devague-0.10.0 → devague-0.11.1}/docs/specs/2026-05-23-devague-now-ships-a-documented-spec-contract-every.md +0 -0
  102. {devague-0.10.0 → devague-0.11.1}/docs/specs/2026-05-23-devague-turns-a-converged-plan-into-parallel-simpl.md +0 -0
  103. {devague-0.10.0 → devague-0.11.1}/docs/superpowers/plans/2026-05-22-specifix-onboarding.md +0 -0
  104. {devague-0.10.0 → devague-0.11.1}/docs/superpowers/plans/2026-05-23-devague-rename.md +0 -0
  105. {devague-0.10.0 → devague-0.11.1}/docs/superpowers/plans/2026-05-23-devague-working-backwards-engine.md +0 -0
  106. {devague-0.10.0 → devague-0.11.1}/docs/superpowers/specs/2026-05-22-specifix-onboarding-design.md +0 -0
  107. {devague-0.10.0 → devague-0.11.1}/docs/superpowers/specs/2026-05-23-devague-spec-to-plan-design.md +0 -0
  108. {devague-0.10.0 → devague-0.11.1}/docs/superpowers/specs/2026-05-23-devague-working-backwards-design.md +0 -0
  109. {devague-0.10.0 → devague-0.11.1}/sonar-project.properties +0 -0
  110. {devague-0.10.0 → devague-0.11.1}/tests/__init__.py +0 -0
  111. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_affordances.py +0 -0
  112. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_chassis.py +0 -0
  113. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_converge_export.py +0 -0
  114. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_errors.py +0 -0
  115. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_learn.py +0 -0
  116. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_moves.py +0 -0
  117. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_output.py +0 -0
  118. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_paths.py +0 -0
  119. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_plan.py +0 -0
  120. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_question.py +0 -0
  121. {devague-0.10.0 → devague-0.11.1}/tests/test_cli_review.py +0 -0
  122. {devague-0.10.0 → devague-0.11.1}/tests/test_contract.py +0 -0
  123. {devague-0.10.0 → devague-0.11.1}/tests/test_convergence.py +0 -0
  124. {devague-0.10.0 → devague-0.11.1}/tests/test_frame.py +0 -0
  125. {devague-0.10.0 → devague-0.11.1}/tests/test_offline.py +0 -0
  126. {devague-0.10.0 → devague-0.11.1}/tests/test_package.py +0 -0
  127. {devague-0.10.0 → devague-0.11.1}/tests/test_plan.py +0 -0
  128. {devague-0.10.0 → devague-0.11.1}/tests/test_plan_convergence.py +0 -0
  129. {devague-0.10.0 → devague-0.11.1}/tests/test_plan_store.py +0 -0
  130. {devague-0.10.0 → devague-0.11.1}/tests/test_render.py +0 -0
  131. {devague-0.10.0 → devague-0.11.1}/tests/test_render_plan.py +0 -0
  132. {devague-0.10.0 → devague-0.11.1}/tests/test_review_loop_integration.py +0 -0
  133. {devague-0.10.0 → devague-0.11.1}/tests/test_review_loop_invariants.py +0 -0
  134. {devague-0.10.0 → devague-0.11.1}/tests/test_spec_to_plan_skill.py +0 -0
  135. {devague-0.10.0 → devague-0.11.1}/tests/test_store.py +0 -0
  136. {devague-0.10.0 → devague-0.11.1}/tests/test_think_skill.py +0 -0
@@ -90,8 +90,17 @@ cmd_split_plan() {
90
90
  shift
91
91
  done
92
92
 
93
- local waves_json tmp_err waves_rc
93
+ local waves_json tmp_err waves_rc old_exit_trap
94
+ # Clean up the temp file on any exit path — including a signal after its
95
+ # creation — WITHOUT permanently changing the script's process-global EXIT
96
+ # handling. Capture any prior EXIT trap BEFORE mktemp (that capture forks a
97
+ # subshell, so doing it first keeps it out of the untracked-file window),
98
+ # then install our cleanup trap on the line immediately after mktemp, and
99
+ # restore the prior trap once the file is safely gone (#30; PR #31 review;
100
+ # devague#32).
101
+ old_exit_trap="$(trap -p EXIT)"
94
102
  tmp_err="$(mktemp)"
103
+ trap 'rm -f "$tmp_err"' EXIT
95
104
  set +e
96
105
  waves_json="$("${DEVAGUE[@]}" plan waves --json "${extra_args[@]}" 2>"$tmp_err")"
97
106
  waves_rc=$?
@@ -99,6 +108,8 @@ cmd_split_plan() {
99
108
  local waves_err
100
109
  waves_err="$(cat "$tmp_err")"
101
110
  rm -f "$tmp_err"
111
+ trap - EXIT
112
+ eval "${old_exit_trap}" # empty string is a no-op; re-installs a prior trap if any
102
113
 
103
114
  if [ "$waves_rc" -ne 0 ]; then
104
115
  printf '%s\n' "$waves_err" >&2
@@ -41,8 +41,9 @@ bash .claude/skills/spec-to-plan/scripts/spec-to-plan.sh status
41
41
 
42
42
  It resolves the CLI portably — an installed `devague` on `PATH` (the normal
43
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> …`).
44
+ install hint. Every move including `status` is forwarded verbatim as
45
+ `devague plan <move>`, so you can equally call the CLI directly
46
+ (`devague plan <move> …`).
46
47
 
47
48
  ### Moves
48
49
 
@@ -58,17 +59,24 @@ install hint. Every move except `status` is forwarded verbatim as `devague plan
58
59
  | `converge` | Evaluate the gate against the **live** source frame; list remaining gaps. |
59
60
  | `export` | Write the buildable plan to `docs/plans/` — only after `converge` passes. |
60
61
  | `waves` | Emit deterministic dependency waves (`{plan, waves}`) — scheduling metadata only, *not* orchestration. Read-only, works on an in-progress plan; refuses a cyclic/dangling graph. Devague describes the graph; an operator decides how to run it (#20). |
62
+ | `status` | Read-only: where the plan stands + the recommended next move, re-checked against the live frame (`--json` too). |
61
63
  | `show` / `list` | Render a plan / list plans (`--json` for raw state). |
62
64
  | `learn` / `explain <move>` | Teach the method / explain one move. |
63
65
 
64
66
  Risk kinds (shared with the frame engine): `unknown_nonblocking`,
65
67
  `unknown_blocking`, `out_of_scope`, `follow_up`.
66
68
 
67
- ### `status` — the next-move helper
68
-
69
- `status` is a wrapper-only verb. It reads `devague plan converge --json` +
70
- `devague plan list --json` and prints where the current plan stands, the
71
- remaining gaps, and the recommended next move derived from the first gap.
69
+ ### `status` — the next-move verb
70
+
71
+ `status` is a first-class, **read-only** CLI verb (`devague plan status`,
72
+ internalised from this wrapper in 0.11.0 issue
73
+ [#30](https://github.com/agentculture/devague/issues/30)). It composes
74
+ `devague plan list` + `devague plan converge` and prints where the current plan
75
+ stands, the remaining gaps, and the recommended next move derived from the first
76
+ gap. Like `converge`/`export` it re-checks the **live** source frame (so frame
77
+ drift surfaces as an error), but it never mutates state. Pass `--json` for the
78
+ structured payload (`{plan, total, ready_for_plan, blockers, warnings,
79
+ parked_items, required_next_moves}`).
72
80
 
73
81
  ```text
74
82
  plan: my-feature (1 plan total)
@@ -0,0 +1,102 @@
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 — including the `status` verb the CLI
7
+ # internalised in 0.11.0 (devague#30/#31), which reads the plan convergence gate
8
+ # and names the recommended next move. It is the forward leg: seed a plan from a
9
+ # *converged* frame, then work it 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
+ waves emit deterministic dependency waves (scheduling metadata, not orchestration)
70
+ status where the plan stands + the recommended next move
71
+ show / list render a plan / list plans
72
+ learn teach the method | explain <move> explain one move
73
+
74
+ Plans persist under .devague/ in the current directory — run from the repo you
75
+ are speccing. Results go to stdout, diagnostics to stderr; pass --json to any
76
+ move for structured output.
77
+
78
+ Note: every move — including `status` — is forwarded verbatim as `devague plan
79
+ <move>`, so new plan moves work without editing this script. (`status` was
80
+ internalised into the CLI in devague 0.11.0; it is no longer wrapper-only.)
81
+
82
+ Prior leg: a plan is seeded from a converged frame produced by the /think skill.
83
+ EOF
84
+ }
85
+
86
+ main() {
87
+ case "${1:-help}" in
88
+ help | -h | --help)
89
+ usage
90
+ return 0
91
+ ;;
92
+ *)
93
+ # Forward every move to `devague plan <move>` verbatim — including the
94
+ # internalised `status` verb (`devague plan status`) — so the CLI's own
95
+ # parser owns the plan surface.
96
+ resolve_devague
97
+ exec "${DEVAGUE[@]}" plan "$@"
98
+ ;;
99
+ esac
100
+ }
101
+
102
+ main "$@"
@@ -31,8 +31,9 @@ fixed sequence of prompts. **You (the agent) choose the next move; the CLI just
31
31
  tracks state and tells you what's still missing.** Run `devague learn` for the
32
32
  canonical ten-stage arc and `devague explain <move>` for any single move.
33
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.
34
+ This skill is the operator: a portable wrapper that resolves the CLI and
35
+ forwards every move verbatim including `status`, the read-only verb that reads
36
+ the convergence gate and tells you the recommended next move.
36
37
 
37
38
  ## How to run
38
39
 
@@ -47,9 +48,9 @@ bash .claude/skills/think/scripts/think.sh status
47
48
  It resolves the CLI portably — an installed `devague` on `PATH` (the normal
48
49
  case), falling back to `uv run devague` when you are inside the devague checkout.
49
50
  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.
51
+ move including `status` is forwarded verbatim, so you can equally call the
52
+ CLI directly (`devague <move> …`) when it is installed; the wrapper exists only
53
+ for portable resolution.
53
54
 
54
55
  ### Moves
55
56
 
@@ -64,6 +65,7 @@ portable resolution and the `status` helper.
64
65
  | `park "<text>" --kind <kind>` | Move uncertainty into first-class open vagueness instead of forcing an answer. |
65
66
  | `converge` | Evaluate the gate; list remaining gaps. |
66
67
  | `export` | Write the buildable spec to `docs/specs/` — only after `converge` passes. |
68
+ | `status` | Read-only: where the frame stands + the recommended next move (`--json` too). |
67
69
  | `show` / `list` | Render a frame / list frames (`--json` for raw state). |
68
70
  | `learn` / `explain <move>` | Teach the method / explain one move. |
69
71
 
@@ -84,15 +86,18 @@ per-move input/output/transition/error contract are documented in
84
86
  live shape of any move, run it with `--json` (or `devague learn --json` /
85
87
  `devague explain <move>`).
86
88
 
87
- ### `status` — the next-move helper
88
-
89
- `status` is a wrapper-only verb (the CLI has no `status`). It reads
90
- `converge --json` + `list --json` and prints where the current frame stands, the
91
- remaining gaps, and the recommended next move. `converge --json` emits the
92
- structured result `{ready_for_spec, blockers, warnings, parked_items,
93
- required_next_moves}` (issue [#5](https://github.com/agentculture/devague/issues/5));
94
- the helper reads `ready_for_spec`, lists the `blockers` and `warnings`, and shows
95
- `required_next_moves[0]` as the recommended move no longer deriving it itself.
89
+ ### `status` — the next-move verb
90
+
91
+ `status` is a first-class, **read-only** CLI verb (`devague status`, internalised
92
+ from this wrapper in 0.11.0 issue
93
+ [#30](https://github.com/agentculture/devague/issues/30)). It composes
94
+ `list` + `converge` and prints where the current frame stands, the remaining
95
+ gaps, and the recommended next move; it never mutates state (the
96
+ `drafting`↔`converged` transition stays in `converge`). It reports
97
+ `ready_for_spec`, lists the `blockers` and `warnings`, and shows
98
+ `required_next_moves[0]` as the recommended move. Pass `--json` for the same
99
+ fields as a structured payload (`{frame, total, ready_for_spec, blockers,
100
+ warnings, parked_items, required_next_moves}`).
96
101
 
97
102
  ```text
98
103
  frame: my-feature (1 frame total)
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env bash
2
+ # think.sh — drive devague's working-backwards idea→spec engine (the /think skill).
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`).
6
+ # devague turns a vague feature idea into a buildable spec by working backwards.
7
+ # This wrapper is the agent-facing operator for the deterministic devague CLI:
8
+ # it resolves the CLI portably and forwards every move verbatim — including the
9
+ # `status` verb the CLI internalised in 0.11.0 (devague#30/#31), which reads the
10
+ # convergence gate and names the recommended next move.
11
+ #
12
+ # Origin: authored and maintained in agentculture/devague. steward pulls this
13
+ # skill from here and broadcasts it to the rest of the AgentCulture mesh, so it
14
+ # is written to run anywhere — portable bash, no devague-checkout assumptions.
15
+ #
16
+ # Frames persist under .devague/ in the current directory, so run from the repo
17
+ # you are speccing.
18
+
19
+ set -euo pipefail
20
+
21
+ # ── resolve the devague CLI (mesh-first, then local-dev fallback) ───────────
22
+ DEVAGUE=()
23
+ resolve_devague() {
24
+ if command -v devague >/dev/null 2>&1; then
25
+ DEVAGUE=(devague) # installed tool — the normal mesh case
26
+ return 0
27
+ fi
28
+ # Local-dev fallback: inside the devague checkout, run via uv.
29
+ local dir="$PWD"
30
+ while [ -n "$dir" ] && [ "$dir" != "/" ]; do
31
+ if [ -f "$dir/pyproject.toml" ] \
32
+ && grep -q '^name = "devague"' "$dir/pyproject.toml" 2>/dev/null; then
33
+ if command -v uv >/dev/null 2>&1; then
34
+ DEVAGUE=(uv run devague)
35
+ return 0
36
+ fi
37
+ break
38
+ fi
39
+ dir=$(dirname "$dir")
40
+ done
41
+ cat >&2 <<'EOF'
42
+ error: devague CLI not found.
43
+ hint: install it with `uv tool install devague` (or `pipx install devague`),
44
+ or run from inside the devague checkout with `uv` available.
45
+ https://github.com/agentculture/devague
46
+ EOF
47
+ return 1
48
+ }
49
+
50
+ usage() {
51
+ cat <<'EOF'
52
+ think.sh — drive devague's working-backwards idea→spec engine (the /think skill).
53
+
54
+ Usage:
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
58
+
59
+ Moves (forwarded to the devague CLI; run `devague learn` for the full method):
60
+ new start a frame from the announcement ("pretend it shipped")
61
+ capture record + classify a claim (--kind audience|after_state|...)
62
+ interrogate pressure-test a claim (--honesty / --hard-question / --risk)
63
+ confirm confirm a claim or honesty condition (USER-only decision)
64
+ reject reject a claim or honesty condition
65
+ park record open vagueness instead of forcing an answer
66
+ converge check whether the frame can export a spec
67
+ export write the buildable spec (only after converge passes)
68
+ status where the frame stands + the recommended next move
69
+ show / list render a frame / list frames
70
+ learn teach the method | explain <move> explain one move
71
+
72
+ Frames persist under .devague/ in the current directory — run from the repo
73
+ you are speccing. Results go to stdout, diagnostics to stderr; pass --json to
74
+ any move for structured output.
75
+
76
+ Note: every move — including `status` — is forwarded verbatim to the devague
77
+ CLI, so new devague moves work without editing this script. (`status` was
78
+ internalised into the CLI in devague 0.11.0; it is no longer wrapper-only.)
79
+
80
+ Next leg: once a frame exports a spec, hand off to the /spec-to-plan skill
81
+ (`devague plan ...`) to turn that spec into a buildable plan.
82
+ EOF
83
+ }
84
+
85
+ main() {
86
+ case "${1:-help}" in
87
+ help | -h | --help)
88
+ usage
89
+ return 0
90
+ ;;
91
+ *)
92
+ # Forward every move to the CLI verbatim (including --version, the
93
+ # internalised `status` verb, and any future devague move), so its
94
+ # own parser owns the surface.
95
+ resolve_devague
96
+ exec "${DEVAGUE[@]}" "$@"
97
+ ;;
98
+ esac
99
+ }
100
+
101
+ main "$@"
@@ -5,6 +5,30 @@ 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.11.1] - 2026-05-24
9
+
10
+ ### Changed
11
+
12
+ - Trio skill-script header comments (`think.sh`, `spec-to-plan.sh`) no longer claim the wrapper *adds* a `status` subcommand — since 0.11.0 it is forwarded verbatim like every other move (devague#32, steward PR #58 review).
13
+
14
+ ### Fixed
15
+
16
+ - `assign-to-workforce.sh` now installs its `mktemp` cleanup `EXIT` trap on the line immediately after creating the temp file, capturing the prior trap beforehand so the subshell-forking capture no longer sits inside the untracked-file window (devague#32).
17
+
18
+ ## [0.11.0] - 2026-05-24
19
+
20
+ ### Added
21
+
22
+ - `devague status` and `devague plan status` — first-class, read-only CLI verbs that compose `list` + `converge` and report the convergence verdict, remaining gaps, and the recommended next move (`--json` too). Internalised from the `think` / `spec-to-plan` skill wrappers (#30); they never mutate state and the plan verb re-checks the live source frame for drift. Shared renderer in `devague/cli/_status.py`.
23
+
24
+ ### Changed
25
+
26
+ - The `think` / `spec-to-plan` skill wrappers are now thin: `status` is forwarded verbatim like every other move instead of being a wrapper-only verb implemented in embedded Python. This removed their `mktemp` temp-file and stdout-ordering hazards entirely (#30, Qodo via steward).
27
+
28
+ ### Fixed
29
+
30
+ - `assign-to-workforce.sh` no longer leaks its `mktemp` temp file if interrupted by a signal — cleanup is now registered with an `EXIT` trap (#30). Its split-plan orchestration presentation deliberately stays out of the deterministic CLI (#20).
31
+
8
32
  ## [0.10.0] - 2026-05-24
9
33
 
10
34
  ### Added
@@ -4,6 +4,16 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Status
6
6
 
7
+ **`status` internalised into the CLI (0.11.0, #30).** The next-move helper that
8
+ used to live as embedded Python inside the `think` / `spec-to-plan` skill
9
+ wrappers is now a first-class, read-only CLI verb — `devague status` and
10
+ `devague plan status` (sharing `devague/cli/_status.py`). Both compose
11
+ `list` + `converge` and report the verdict, gaps, and recommended next move
12
+ (`--json` too); neither mutates state. This removed the wrappers' `mktemp` +
13
+ embedded-stdout hazards (Qodo via steward); the trio's remaining `mktemp` (in
14
+ `assign-to-workforce.sh`, whose orchestration presentation deliberately stays
15
+ out of the deterministic CLI) gained a cleanup trap.
16
+
7
17
  **Human Review Loop landed (0.6.0, #17).** `devague review` (+ `--json`) lists
8
18
  every proposed claim + honesty condition with ids — un-gated by convergence,
9
19
  never mutating state — and writes a non-authoritative artifact to
@@ -26,12 +36,12 @@ required_next_moves}` (plans: `ready_for_plan`) — a hard break from the old
26
36
  The **frame engine** (idea→spec) — Frame domain model, JSON store, convergence
27
37
  gate, renderer registry, and the flat moves `new` / `capture` / `interrogate` /
28
38
  `confirm` / `reject` / `review` / `question` / `park` / `converge` / `export` /
29
- `show` / `list` / `learn` / `explain`. The **plan engine** (spec→plan) is its
30
- structural peer:
39
+ `status` / `show` / `list` / `learn` / `explain`. The **plan engine** (spec→plan)
40
+ is its structural peer:
31
41
  `devague/plan.py`, `plan_convergence.py`, `plan_store.py`, `render/plan_md.py`,
32
42
  and the nested group `devague plan <move>` (`new` / `task` / `accept` / `depend`
33
43
  / `cover` / `confirm` / `reject` / `risk` / `converge` / `export` / `waves` /
34
- `show` / `list` / `learn` / `explain`). The two operator skills are `/think` (idea→spec,
44
+ `status` / `show` / `list` / `learn` / `explain`). The two operator skills are `/think` (idea→spec,
35
45
  renamed from `/devague`) and `/spec-to-plan` (spec→plan). Coverage ≥ 95 %; all
36
46
  linters pass. Run `git ls-files` to see the real surface.
37
47
 
@@ -202,8 +212,9 @@ that unless the user asks otherwise. The established sibling shape is:
202
212
  split, `--json` support).
203
213
  - `devague/cli/_commands/` — one module per verb, each exposing `register()`.
204
214
  Frame verbs: `new`, `capture`, `interrogate`, `confirm`, `reject`, `park`,
205
- `converge`, `export`, `show`, `list`, `learn`, `explain`. The plan engine adds
206
- one module, `_commands/plan.py`, registering the nested `plan` subcommand group.
215
+ `converge`, `export`, `status`, `show`, `list`, `learn`, `explain` (`status`
216
+ shares `cli/_status.py` with the plan engine). The plan engine adds one module,
217
+ `_commands/plan.py`, registering the nested `plan` subcommand group.
207
218
  - Frame engine: `devague/frame.py`, `convergence.py`, `store.py`,
208
219
  `render/{spec_md,frame_md}.py`. Plan engine (its peer): `devague/plan.py`,
209
220
  `plan_convergence.py`, `plan_store.py`, `render/plan_md.py`, `cli/_plans.py`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devague
3
- Version: 0.10.0
3
+ Version: 0.11.1
4
4
  Summary: devague — turns a vague feature idea into a buildable spec, then a buildable plan.
5
5
  Project-URL: Homepage, https://github.com/agentculture/devague
6
6
  Project-URL: Issues, https://github.com/agentculture/devague/issues
@@ -69,6 +69,7 @@ def _build_parser() -> argparse.ArgumentParser:
69
69
  from devague.cli._commands import reject as _reject_cmd
70
70
  from devague.cli._commands import review as _review_cmd
71
71
  from devague.cli._commands import show as _show_cmd
72
+ from devague.cli._commands import status as _status_cmd
72
73
 
73
74
  _learn_cmd.register(sub)
74
75
  _explain_cmd.register(sub)
@@ -83,6 +84,7 @@ def _build_parser() -> argparse.ArgumentParser:
83
84
  _converge_cmd.register(sub)
84
85
  _export_cmd.register(sub)
85
86
  _plan_cmd.register(sub)
87
+ _status_cmd.register(sub)
86
88
  _show_cmd.register(sub)
87
89
  _list_cmd.register(sub)
88
90
 
@@ -16,6 +16,7 @@ MOVES = {
16
16
  "park": "Move uncertainty into first-class open vagueness instead of forcing an answer.",
17
17
  "converge": "Check whether the frame is solid enough to export a spec.",
18
18
  "export": "Write the buildable spec — only once the frame converges.",
19
+ "status": "Report where the frame stands + the recommended next move (read-only).",
19
20
  "show": "Render the Announcement Frame.",
20
21
  "list": "List frames.",
21
22
  }
@@ -21,6 +21,7 @@ from devague.cli._frames import resolve as resolve_frame
21
21
  from devague.cli._output import emit_result
22
22
  from devague.cli._paths import dated_name
23
23
  from devague.cli._plans import resolve_plan
24
+ from devague.cli._status import StatusLabels, emit_empty, emit_status
24
25
  from devague.convergence import evaluate as evaluate_frame
25
26
  from devague.frame import Frame
26
27
  from devague.plan import RISK_KINDS, Plan, dependency_waves, targets_from_frame, to_dict
@@ -46,10 +47,22 @@ PLAN_MOVES = {
46
47
  "converge": "Check whether the plan can export, against the live frame.",
47
48
  "export": "Write the buildable plan — only once the plan converges.",
48
49
  "waves": "Emit deterministic dependency waves (scheduling metadata, not orchestration).",
50
+ "status": "Report where the plan stands + the recommended next move (read-only).",
49
51
  "show": "Render the plan.",
50
52
  "list": "List plans.",
51
53
  }
52
54
 
55
+ _STATUS_LABELS = StatusLabels(
56
+ noun="plan",
57
+ ready_key="ready_for_plan",
58
+ export_move="devague plan export # write the buildable plan",
59
+ empty_text=(
60
+ "no plans yet — seed one from a converged frame:\n"
61
+ ' devague plan new --frame "<slug>"\n'
62
+ " (the frame must have converged in the /think skill first)"
63
+ ),
64
+ )
65
+
53
66
 
54
67
  # ── shared helpers ──────────────────────────────────────────────────────────
55
68
  def _load_source_frame(slug: str) -> Frame:
@@ -319,6 +332,31 @@ def cmd_plan_waves(args: argparse.Namespace) -> int:
319
332
  return 0
320
333
 
321
334
 
335
+ def cmd_plan_status(args: argparse.Namespace) -> int:
336
+ """Where the plan stands + the recommended next move — read-only.
337
+
338
+ Re-evaluates against the **live** source frame (like ``converge``/``export``),
339
+ so frame drift surfaces as a real error on stderr before any stdout. Internalised
340
+ from the ``spec-to-plan`` skill wrapper (issue #30); unlike ``converge`` it never
341
+ persists the ``drafting``↔``converged`` transition.
342
+ """
343
+ json_mode = getattr(args, "json", False)
344
+ slugs = plan_store.list_slugs()
345
+ if not slugs:
346
+ emit_empty(_STATUS_LABELS, json_mode=json_mode)
347
+ else:
348
+ # resolve_plan + _live raise here (before any stdout) on a bad --plan or a
349
+ # source frame that regressed below convergence — routed to stderr.
350
+ plan = resolve_plan(args.plan)
351
+ _frame, targets = _live(plan)
352
+ plan.targets = targets # in-memory refresh only; status does not save
353
+ result = evaluate_plan(plan, targets=targets)
354
+ emit_status(
355
+ _STATUS_LABELS, selected=plan.slug, total=len(slugs), result=result, json_mode=json_mode
356
+ )
357
+ return 0
358
+
359
+
322
360
  def cmd_plan_show(args: argparse.Namespace) -> int:
323
361
  plan = resolve_plan(args.plan)
324
362
  if getattr(args, "json", False):
@@ -465,6 +503,10 @@ def register(sub: argparse._SubParsersAction) -> None:
465
503
  _plan_opt(pwv)
466
504
  pwv.set_defaults(func=cmd_plan_waves)
467
505
 
506
+ pst = psub.add_parser("status", help="Where the plan stands + the recommended next move.")
507
+ _plan_opt(pst)
508
+ pst.set_defaults(func=cmd_plan_status)
509
+
468
510
  psh = psub.add_parser("show", help="Render the plan.")
469
511
  _plan_opt(psh)
470
512
  psh.set_defaults(func=cmd_plan_show)
@@ -0,0 +1,53 @@
1
+ """``devague status`` — where the frame stands + the recommended next move.
2
+
3
+ A read-only value-add verb that composes ``list`` and ``converge``: it reports
4
+ the selected frame, the convergence verdict (blockers and warnings), and, when
5
+ not converged, the next move for the first gap. This logic used to live in the
6
+ ``think`` skill wrapper's embedded Python (issue #30); internalising it makes it
7
+ deterministic and unit-testable and removes the wrapper's temp-file +
8
+ stdout-ordering hazards. ``status`` never mutates state — like ``review`` it only
9
+ reports (the ``drafting``↔``converged`` transition stays in ``converge``).
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+
16
+ from devague import store
17
+ from devague.cli._frames import resolve
18
+ from devague.cli._status import StatusLabels, emit_empty, emit_status
19
+ from devague.convergence import evaluate
20
+
21
+ _LABELS = StatusLabels(
22
+ noun="frame",
23
+ ready_key="ready_for_spec",
24
+ export_move="devague export # write the buildable spec",
25
+ empty_text=(
26
+ "no frames yet — start one:\n"
27
+ ' devague new "<announcement>"\n'
28
+ " first question: \"What's the announcement? Pretend this shipped"
29
+ ' successfully — what would you announce?"'
30
+ ),
31
+ )
32
+
33
+
34
+ def cmd_status(args: argparse.Namespace) -> int:
35
+ json_mode = getattr(args, "json", False)
36
+ slugs = store.list_slugs()
37
+ if not slugs:
38
+ emit_empty(_LABELS, json_mode=json_mode)
39
+ else:
40
+ # A bad --frame raises here (before any stdout) so the error reaches stderr.
41
+ frame = resolve(args.frame)
42
+ result = evaluate(frame)
43
+ emit_status(
44
+ _LABELS, selected=frame.slug, total=len(slugs), result=result, json_mode=json_mode
45
+ )
46
+ return 0
47
+
48
+
49
+ def register(sub: argparse._SubParsersAction) -> None:
50
+ p = sub.add_parser("status", help="Where the frame stands + the recommended next move.")
51
+ p.add_argument("--frame", help="Frame slug (default: current).")
52
+ p.add_argument("--json", action="store_true", help="Emit structured JSON.")
53
+ p.set_defaults(func=cmd_status)