devague 0.10.0__tar.gz → 0.11.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.
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +9 -1
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/spec-to-plan/SKILL.md +15 -7
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +7 -104
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/think/SKILL.md +19 -14
- devague-0.11.0/.claude/skills/think/scripts/think.sh +101 -0
- {devague-0.10.0 → devague-0.11.0}/CHANGELOG.md +14 -0
- {devague-0.10.0 → devague-0.11.0}/CLAUDE.md +16 -5
- {devague-0.10.0 → devague-0.11.0}/PKG-INFO +1 -1
- {devague-0.10.0 → devague-0.11.0}/devague/cli/__init__.py +2 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/learn.py +1 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/plan.py +42 -0
- devague-0.11.0/devague/cli/_commands/status.py +53 -0
- devague-0.11.0/devague/cli/_status.py +99 -0
- {devague-0.10.0 → devague-0.11.0}/pyproject.toml +1 -1
- devague-0.11.0/tests/test_cli_status.py +204 -0
- {devague-0.10.0 → devague-0.11.0}/uv.lock +1 -1
- devague-0.10.0/.claude/skills/think/scripts/think.sh +0 -205
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/cicd/SKILL.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/communicate/SKILL.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/version-bump/SKILL.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/.claude/skills.local.yaml.example +0 -0
- {devague-0.10.0 → devague-0.11.0}/.devague/current_plan +0 -0
- {devague-0.10.0 → devague-0.11.0}/.devague/frames/devague-0-6-0-ships-the-human-review-loop-devague.json +0 -0
- {devague-0.10.0 → devague-0.11.0}/.devague/frames/devague-now-ships-a-documented-spec-contract-every.json +0 -0
- {devague-0.10.0 → devague-0.11.0}/.devague/frames/devague-turns-a-converged-plan-into-parallel-simpl.json +0 -0
- {devague-0.10.0 → devague-0.11.0}/.devague/plans/devague-0-6-0-ships-the-human-review-loop-devague.json +0 -0
- {devague-0.10.0 → devague-0.11.0}/.devague/plans/devague-now-ships-a-documented-spec-contract-every.json +0 -0
- {devague-0.10.0 → devague-0.11.0}/.devague/plans/devague-turns-a-converged-plan-into-parallel-simpl.json +0 -0
- {devague-0.10.0 → devague-0.11.0}/.flake8 +0 -0
- {devague-0.10.0 → devague-0.11.0}/.github/workflows/publish.yml +0 -0
- {devague-0.10.0 → devague-0.11.0}/.github/workflows/security-checks.yml +0 -0
- {devague-0.10.0 → devague-0.11.0}/.github/workflows/tests.yml +0 -0
- {devague-0.10.0 → devague-0.11.0}/.gitignore +0 -0
- {devague-0.10.0 → devague-0.11.0}/.markdownlint-cli2.yaml +0 -0
- {devague-0.10.0 → devague-0.11.0}/.pre-commit-config.yaml +0 -0
- {devague-0.10.0 → devague-0.11.0}/LICENSE +0 -0
- {devague-0.10.0 → devague-0.11.0}/README.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/culture.yaml +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/__init__.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/__main__.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/__init__.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/capture.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/confirm.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/converge.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/explain.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/export.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/interrogate.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/list_frames.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/new.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/park.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/question.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/reject.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/review.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_commands/show.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_errors.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_frames.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_output.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_paths.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/cli/_plans.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/convergence.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/frame.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/plan.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/plan_convergence.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/plan_store.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/questions_io.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/render/__init__.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/render/frame_md.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/render/plan_md.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/render/review_md.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/render/spec_md.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/devague/store.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/assign-to-workforce-worked-example.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/examples/contract-example.json +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/llm-guidance.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/plans/2026-05-23-devague-0-6-0-ships-the-human-review-loop-devague.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/plans/2026-05-23-devague-now-ships-a-documented-spec-contract-every.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/plans/2026-05-23-devague-turns-a-converged-plan-into-parallel-simpl.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/reviews/spec-contract-frame-review.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/skill-sources.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/spec-contract.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/specs/2026-05-23-devague-0-6-0-ships-the-human-review-loop-devague.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/specs/2026-05-23-devague-now-ships-a-documented-spec-contract-every.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/specs/2026-05-23-devague-turns-a-converged-plan-into-parallel-simpl.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/superpowers/plans/2026-05-22-specifix-onboarding.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/superpowers/plans/2026-05-23-devague-rename.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/superpowers/plans/2026-05-23-devague-working-backwards-engine.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/superpowers/specs/2026-05-22-specifix-onboarding-design.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/superpowers/specs/2026-05-23-devague-spec-to-plan-design.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/docs/superpowers/specs/2026-05-23-devague-working-backwards-design.md +0 -0
- {devague-0.10.0 → devague-0.11.0}/sonar-project.properties +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/__init__.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_affordances.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_chassis.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_converge_export.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_errors.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_learn.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_moves.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_output.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_paths.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_plan.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_question.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_cli_review.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_contract.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_convergence.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_frame.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_offline.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_package.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_plan.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_plan_convergence.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_plan_store.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_render.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_render_plan.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_review_loop_integration.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_review_loop_invariants.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_spec_to_plan_skill.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_store.py +0 -0
- {devague-0.10.0 → devague-0.11.0}/tests/test_think_skill.py +0 -0
{devague-0.10.0 → devague-0.11.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh
RENAMED
|
@@ -90,8 +90,14 @@ 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
94
|
tmp_err="$(mktemp)"
|
|
95
|
+
# Clean up the temp file on any exit path — including a signal between its
|
|
96
|
+
# creation and the explicit removal below — WITHOUT permanently changing the
|
|
97
|
+
# script's process-global EXIT handling: capture any prior EXIT trap, install
|
|
98
|
+
# ours, then restore it once the file is safely gone (#30; PR #31 review).
|
|
99
|
+
old_exit_trap="$(trap -p EXIT)"
|
|
100
|
+
trap 'rm -f "$tmp_err"' EXIT
|
|
95
101
|
set +e
|
|
96
102
|
waves_json="$("${DEVAGUE[@]}" plan waves --json "${extra_args[@]}" 2>"$tmp_err")"
|
|
97
103
|
waves_rc=$?
|
|
@@ -99,6 +105,8 @@ cmd_split_plan() {
|
|
|
99
105
|
local waves_err
|
|
100
106
|
waves_err="$(cat "$tmp_err")"
|
|
101
107
|
rm -f "$tmp_err"
|
|
108
|
+
trap - EXIT
|
|
109
|
+
eval "${old_exit_trap}" # empty string is a no-op; re-installs a prior trap if any
|
|
102
110
|
|
|
103
111
|
if [ "$waves_rc" -ne 0 ]; then
|
|
104
112
|
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
|
|
45
|
-
<move>`, so you can equally call the CLI directly
|
|
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
|
|
68
|
-
|
|
69
|
-
`status` is a
|
|
70
|
-
|
|
71
|
-
|
|
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)
|
|
@@ -67,6 +67,7 @@ Moves (forwarded to `devague plan`; run `devague plan learn` for the method):
|
|
|
67
67
|
converge check whether the plan can export
|
|
68
68
|
export write the buildable plan (only after converge passes)
|
|
69
69
|
waves emit deterministic dependency waves (scheduling metadata, not orchestration)
|
|
70
|
+
status where the plan stands + the recommended next move
|
|
70
71
|
show / list render a plan / list plans
|
|
71
72
|
learn teach the method | explain <move> explain one move
|
|
72
73
|
|
|
@@ -74,122 +75,24 @@ Plans persist under .devague/ in the current directory — run from the repo you
|
|
|
74
75
|
are speccing. Results go to stdout, diagnostics to stderr; pass --json to any
|
|
75
76
|
move for structured output.
|
|
76
77
|
|
|
77
|
-
Note:
|
|
78
|
-
|
|
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.)
|
|
79
81
|
|
|
80
82
|
Prior leg: a plan is seeded from a converged frame produced by the /think skill.
|
|
81
83
|
EOF
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
# ── status: read the plan convergence gate and recommend the next move ──────
|
|
85
|
-
cmd_status() {
|
|
86
|
-
local list_json conv_out conv_err conv_rc req_plan="" prev="" tmp_err
|
|
87
|
-
|
|
88
|
-
for arg in "$@"; do
|
|
89
|
-
case "$prev" in --plan) req_plan="$arg" ;; esac
|
|
90
|
-
case "$arg" in --plan=*) req_plan="${arg#--plan=}" ;; esac
|
|
91
|
-
prev="$arg"
|
|
92
|
-
done
|
|
93
|
-
|
|
94
|
-
list_json="$("${DEVAGUE[@]}" plan list --json 2>/dev/null || true)"
|
|
95
|
-
|
|
96
|
-
tmp_err="$(mktemp)"
|
|
97
|
-
set +e
|
|
98
|
-
conv_out="$("${DEVAGUE[@]}" plan converge --json "$@" 2>"$tmp_err")"
|
|
99
|
-
conv_rc=$?
|
|
100
|
-
set -e
|
|
101
|
-
conv_err="$(cat "$tmp_err")"
|
|
102
|
-
rm -f "$tmp_err"
|
|
103
|
-
|
|
104
|
-
DEVAGUE_LIST_JSON="$list_json" \
|
|
105
|
-
DEVAGUE_CONV_JSON="$conv_out" \
|
|
106
|
-
DEVAGUE_CONV_ERR="$conv_err" \
|
|
107
|
-
DEVAGUE_CONV_RC="$conv_rc" \
|
|
108
|
-
DEVAGUE_REQ_PLAN="$req_plan" \
|
|
109
|
-
python3 - <<'PY'
|
|
110
|
-
import json
|
|
111
|
-
import os
|
|
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("ready_for_plan"):
|
|
158
|
-
print("convergence: PASSED ✓")
|
|
159
|
-
for w in conv.get("warnings") or []:
|
|
160
|
-
print(f" ⚠ {w}")
|
|
161
|
-
print("next move: devague plan export # write the buildable plan")
|
|
162
|
-
sys.exit(0)
|
|
163
|
-
|
|
164
|
-
blockers = conv.get("blockers") or []
|
|
165
|
-
print(f"convergence: NOT passed — {len(blockers)} gap(s):")
|
|
166
|
-
for b in blockers:
|
|
167
|
-
print(f" - {b}")
|
|
168
|
-
for w in conv.get("warnings") or []:
|
|
169
|
-
print(f" ⚠ {w}")
|
|
170
|
-
|
|
171
|
-
moves = conv.get("required_next_moves") or []
|
|
172
|
-
if moves:
|
|
173
|
-
print()
|
|
174
|
-
print("recommended next move (first gap):")
|
|
175
|
-
print(f" {moves[0]}")
|
|
176
|
-
PY
|
|
177
|
-
}
|
|
178
|
-
|
|
179
86
|
main() {
|
|
180
87
|
case "${1:-help}" in
|
|
181
88
|
help | -h | --help)
|
|
182
89
|
usage
|
|
183
90
|
return 0
|
|
184
91
|
;;
|
|
185
|
-
status)
|
|
186
|
-
shift
|
|
187
|
-
resolve_devague
|
|
188
|
-
cmd_status "$@"
|
|
189
|
-
;;
|
|
190
92
|
*)
|
|
191
|
-
# Forward
|
|
192
|
-
#
|
|
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.
|
|
193
96
|
resolve_devague
|
|
194
97
|
exec "${DEVAGUE[@]}" plan "$@"
|
|
195
98
|
;;
|
|
@@ -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
|
|
35
|
-
|
|
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
|
|
51
|
-
directly (`devague <move> …`) when it is installed; the wrapper exists
|
|
52
|
-
portable resolution
|
|
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
|
|
88
|
-
|
|
89
|
-
`status` is a
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
`
|
|
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, forwards every move verbatim, and adds one
|
|
9
|
+
# value-add subcommand — `status` — that reads the convergence gate and names
|
|
10
|
+
# 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,20 @@ 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.0] - 2026-05-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `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`.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- 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).
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- `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).
|
|
21
|
+
|
|
8
22
|
## [0.10.0] - 2026-05-24
|
|
9
23
|
|
|
10
24
|
### 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)
|
|
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
|
|
206
|
-
|
|
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.
|
|
3
|
+
Version: 0.11.0
|
|
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)
|