devague 0.11.1__tar.gz → 0.12.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.11.1 → devague-0.12.0}/CHANGELOG.md +16 -0
- {devague-0.11.1 → devague-0.12.0}/CLAUDE.md +11 -0
- {devague-0.11.1 → devague-0.12.0}/PKG-INFO +1 -1
- devague-0.12.0/devague/cli/_commands/learn.py +407 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/plan.py +5 -1
- {devague-0.11.1 → devague-0.12.0}/docs/llm-guidance.md +3 -0
- devague-0.12.0/docs/skills.md +179 -0
- {devague-0.11.1 → devague-0.12.0}/pyproject.toml +1 -1
- devague-0.12.0/tests/test_cli_learn.py +150 -0
- {devague-0.11.1 → devague-0.12.0}/uv.lock +1 -1
- devague-0.11.1/devague/cli/_commands/learn.py +0 -187
- devague-0.11.1/tests/test_cli_learn.py +0 -40
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/cicd/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/communicate/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/run-tests/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/think/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/think/scripts/think.sh +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/version-bump/SKILL.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/.claude/skills.local.yaml.example +0 -0
- {devague-0.11.1 → devague-0.12.0}/.devague/current_plan +0 -0
- {devague-0.11.1 → devague-0.12.0}/.devague/frames/devague-0-6-0-ships-the-human-review-loop-devague.json +0 -0
- {devague-0.11.1 → devague-0.12.0}/.devague/frames/devague-now-ships-a-documented-spec-contract-every.json +0 -0
- {devague-0.11.1 → devague-0.12.0}/.devague/frames/devague-turns-a-converged-plan-into-parallel-simpl.json +0 -0
- {devague-0.11.1 → devague-0.12.0}/.devague/plans/devague-0-6-0-ships-the-human-review-loop-devague.json +0 -0
- {devague-0.11.1 → devague-0.12.0}/.devague/plans/devague-now-ships-a-documented-spec-contract-every.json +0 -0
- {devague-0.11.1 → devague-0.12.0}/.devague/plans/devague-turns-a-converged-plan-into-parallel-simpl.json +0 -0
- {devague-0.11.1 → devague-0.12.0}/.flake8 +0 -0
- {devague-0.11.1 → devague-0.12.0}/.github/workflows/publish.yml +0 -0
- {devague-0.11.1 → devague-0.12.0}/.github/workflows/security-checks.yml +0 -0
- {devague-0.11.1 → devague-0.12.0}/.github/workflows/tests.yml +0 -0
- {devague-0.11.1 → devague-0.12.0}/.gitignore +0 -0
- {devague-0.11.1 → devague-0.12.0}/.markdownlint-cli2.yaml +0 -0
- {devague-0.11.1 → devague-0.12.0}/.pre-commit-config.yaml +0 -0
- {devague-0.11.1 → devague-0.12.0}/LICENSE +0 -0
- {devague-0.11.1 → devague-0.12.0}/README.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/culture.yaml +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/__init__.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/__main__.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/__init__.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/__init__.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/capture.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/confirm.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/converge.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/explain.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/export.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/interrogate.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/list_frames.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/new.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/park.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/question.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/reject.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/review.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/show.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_commands/status.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_errors.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_frames.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_output.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_paths.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_plans.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/cli/_status.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/convergence.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/frame.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/plan.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/plan_convergence.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/plan_store.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/questions_io.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/render/__init__.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/render/frame_md.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/render/plan_md.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/render/review_md.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/render/spec_md.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/devague/store.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/assign-to-workforce-worked-example.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/examples/contract-example.json +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/plans/2026-05-23-devague-0-6-0-ships-the-human-review-loop-devague.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/plans/2026-05-23-devague-now-ships-a-documented-spec-contract-every.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/plans/2026-05-23-devague-turns-a-converged-plan-into-parallel-simpl.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/reviews/spec-contract-frame-review.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/skill-sources.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/spec-contract.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/specs/2026-05-23-devague-0-6-0-ships-the-human-review-loop-devague.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/specs/2026-05-23-devague-now-ships-a-documented-spec-contract-every.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/specs/2026-05-23-devague-turns-a-converged-plan-into-parallel-simpl.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/superpowers/plans/2026-05-22-specifix-onboarding.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/superpowers/plans/2026-05-23-devague-rename.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/superpowers/plans/2026-05-23-devague-working-backwards-engine.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/superpowers/specs/2026-05-22-specifix-onboarding-design.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/superpowers/specs/2026-05-23-devague-spec-to-plan-design.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/docs/superpowers/specs/2026-05-23-devague-working-backwards-design.md +0 -0
- {devague-0.11.1 → devague-0.12.0}/sonar-project.properties +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/__init__.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_affordances.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_chassis.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_converge_export.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_errors.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_moves.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_output.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_paths.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_plan.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_question.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_review.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_cli_status.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_contract.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_convergence.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_frame.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_offline.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_package.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_plan.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_plan_convergence.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_plan_store.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_render.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_render_plan.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_review_loop_integration.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_review_loop_invariants.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_spec_to_plan_skill.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_store.py +0 -0
- {devague-0.11.1 → devague-0.12.0}/tests/test_think_skill.py +0 -0
|
@@ -5,6 +5,22 @@ 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.12.0] - 2026-05-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `devague learn` now teaches skill authoring (#34): an optional topic arg —
|
|
13
|
+
`devague learn skills` (and `skills:all` / `skills:NAME`) — emits a
|
|
14
|
+
self-contained recipe for authoring the three operator skills (think /
|
|
15
|
+
spec-to-plan / assign-to-workforce) in any runtime, framed as consent-gated,
|
|
16
|
+
no-clobber instructions the agent follows. The CLI never writes skill files
|
|
17
|
+
(#20); the agent does, with user consent. Bare `devague learn` appends the
|
|
18
|
+
condensed authoring section.
|
|
19
|
+
- `docs/skills.md`: new canonical authoring guide (file layout, frontmatter incl.
|
|
20
|
+
the `type:` gotcha for culture backends, the portable resolver pattern, the
|
|
21
|
+
skill-to-devague contract, and the three human gates), referenced from
|
|
22
|
+
`docs/llm-guidance.md` and `devague plan learn`.
|
|
23
|
+
|
|
8
24
|
## [0.11.1] - 2026-05-24
|
|
9
25
|
|
|
10
26
|
### Changed
|
|
@@ -4,6 +4,17 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
|
4
4
|
|
|
5
5
|
## Status
|
|
6
6
|
|
|
7
|
+
**`learn` now teaches skill authoring (0.12.0, #34).** `devague learn` gained an
|
|
8
|
+
optional topic arg: `devague learn skills` (and `skills:all` / `skills:<name>`)
|
|
9
|
+
emits a self-contained recipe for authoring the three operator skills (`think` /
|
|
10
|
+
`spec-to-plan` / `assign-to-workforce`) in any runtime — file layout, frontmatter
|
|
11
|
+
(incl. the `type:` gotcha for culture backends), the portable resolver pattern,
|
|
12
|
+
the skill↔devague contract, and the consent + no-clobber rules. It is framed as
|
|
13
|
+
instructions the *agent* follows: the CLI never writes skill files (it stays
|
|
14
|
+
deterministic and non-orchestrating, #20); the agent creates them with user
|
|
15
|
+
consent. Bare `devague learn` appends the condensed authoring section. Canonical
|
|
16
|
+
long-form guide: `docs/skills.md`.
|
|
17
|
+
|
|
7
18
|
**`status` internalised into the CLI (0.11.0, #30).** The next-move helper that
|
|
8
19
|
used to live as embedded Python inside the `think` / `spec-to-plan` skill
|
|
9
20
|
wrappers is now a first-class, read-only CLI verb — `devague status` and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devague
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.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
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"""``devague learn`` — teach the working-backwards method and the moves."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from devague import __version__
|
|
8
|
+
from devague.cli._errors import EXIT_USER_ERROR, DevagueError
|
|
9
|
+
from devague.cli._output import emit_result
|
|
10
|
+
|
|
11
|
+
MOVES = {
|
|
12
|
+
"new": "Start a frame from the announcement (pretend it shipped).",
|
|
13
|
+
"capture": "Record and classify a claim (audience, after_state, boundary, ...).",
|
|
14
|
+
"interrogate": "Pressure-test a claim: honesty conditions, hard questions, contradictions.",
|
|
15
|
+
"confirm": "Confirm a claim or honesty condition (user-only — no fabricated rigor).",
|
|
16
|
+
"reject": "Reject a claim or honesty condition.",
|
|
17
|
+
"park": "Move uncertainty into first-class open vagueness instead of forcing an answer.",
|
|
18
|
+
"converge": "Check whether the frame is solid enough to export a spec.",
|
|
19
|
+
"export": "Write the buildable spec — only once the frame converges.",
|
|
20
|
+
"status": "Report where the frame stands + the recommended next move (read-only).",
|
|
21
|
+
"show": "Render the Announcement Frame.",
|
|
22
|
+
"list": "List frames.",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
FIRST_QUESTION = "What's the announcement?"
|
|
26
|
+
SUPPORTING_PROMPT = (
|
|
27
|
+
"Pretend this shipped successfully. What would you announce to users, "
|
|
28
|
+
"teammates, or yourself?"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# The portable, runtime-agnostic operating contract for any assisting model
|
|
32
|
+
# (devague#19). The full version lives in the guidance doc; this is the core
|
|
33
|
+
# surfaced in every `learn`. These rules are what make convergence mean something.
|
|
34
|
+
#
|
|
35
|
+
# `docs/` is not shipped in the wheel (only the `devague` package is), and an
|
|
36
|
+
# installed devague is operated from an arbitrary repo — so a bare relative path
|
|
37
|
+
# wouldn't resolve for most consumers. The portable, always-resolvable reference
|
|
38
|
+
# is the canonical URL; the in-repo path is kept for contributors.
|
|
39
|
+
GUIDANCE_DOC_URL = "https://github.com/agentculture/devague/blob/main/docs/llm-guidance.md"
|
|
40
|
+
GUIDANCE_DOC_REPO_PATH = "docs/llm-guidance.md"
|
|
41
|
+
|
|
42
|
+
# What devague is NOT — the framing that keeps it from degrading into a form.
|
|
43
|
+
NOT_A = (
|
|
44
|
+
"a wizard (no fixed prompt sequence)",
|
|
45
|
+
"a scripted questionnaire (you don't read questions off a form)",
|
|
46
|
+
"a PRD generator (it never invents content to fill a template)",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Assign-to-workforce invocation guidance: when and how to fan out a converged
|
|
50
|
+
# plan's waves to a workforce. This is a cited skill convention, not an
|
|
51
|
+
# orchestration engine (devague#20). The three human gates ensure spec, plan,
|
|
52
|
+
# and PR pass human review.
|
|
53
|
+
ASSIGN_TO_WORKFORCE_GUIDANCE = {
|
|
54
|
+
"title": "Assign-to-workforce: parallel plan execution",
|
|
55
|
+
"when_to_fan_out": (
|
|
56
|
+
"Once a plan converges (all targets covered, all tasks have acceptance "
|
|
57
|
+
"criteria, dependency graph is acyclic), you can fan out its waves to "
|
|
58
|
+
"parallel agents working in isolated git worktrees."
|
|
59
|
+
),
|
|
60
|
+
"prerequisites": ("A converged plan with deterministic dependency waves (devague plan waves)."),
|
|
61
|
+
"human_gates": (
|
|
62
|
+
"The human approves at exactly three points: (1) the exported spec, "
|
|
63
|
+
"(2) the implementation split plan (plan/tasks map, per-task "
|
|
64
|
+
"agent/model assignment, go/no-go to workforce), and (3) the final PR. "
|
|
65
|
+
"The human is NOT in the per-task worktree-merge loop."
|
|
66
|
+
),
|
|
67
|
+
"worktree_isolation": (
|
|
68
|
+
"Each task runs in an isolated git worktree (one per task per wave). "
|
|
69
|
+
"This keeps file-contention safe: overlapping same-file changes "
|
|
70
|
+
"surface as merge conflicts at reconcile time, not live races."
|
|
71
|
+
),
|
|
72
|
+
"main_agent_merge_gate": (
|
|
73
|
+
"The main agent gates each subagent worktree merge with TDD: tests "
|
|
74
|
+
"pass before AND after merge. No human per-task merge decision."
|
|
75
|
+
),
|
|
76
|
+
"tdd_acceptance_criteria": (
|
|
77
|
+
"Each task carries TDD acceptance criteria (tests first) scoped "
|
|
78
|
+
"tightly enough for a simpler/cheaper model to build. The tests "
|
|
79
|
+
"validate that the task was built correctly — no re-deriving the "
|
|
80
|
+
"design needed."
|
|
81
|
+
),
|
|
82
|
+
"not_orchestration": (
|
|
83
|
+
"devague itself does not orchestrate: it does not spawn subagents, "
|
|
84
|
+
"manage worktrees, mark tasks done, or pick a backend. Orchestration "
|
|
85
|
+
"is a cited skill convention (assign-to-workforce), not part of the "
|
|
86
|
+
"deterministic CLI."
|
|
87
|
+
),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# The anti-fabrication rules. Agent-agnostic: repo-specific agreements live in
|
|
91
|
+
# your agent's main instruction file (AGENTS.md, CLAUDE.md, a system prompt, …),
|
|
92
|
+
# not here.
|
|
93
|
+
OPERATING_RULES = (
|
|
94
|
+
"LLM proposals stay proposed — capture your own ideas with --origin llm; "
|
|
95
|
+
"never confirm your own proposal. Confirmation is a user-only decision.",
|
|
96
|
+
"Honesty conditions route through the user — propose freely with "
|
|
97
|
+
"'interrogate --honesty'; the user owns whether each one holds.",
|
|
98
|
+
"Park real unknowns instead of papering over them — 'park' a genuine "
|
|
99
|
+
"unknown rather than writing confident prose that hides the gap.",
|
|
100
|
+
"Converge, don't vibe — 'export' is gated on 'converge'; resolve every "
|
|
101
|
+
"listed gap instead of declaring readiness on a hunch.",
|
|
102
|
+
"Order is adaptive — the ten stages are an artifact shape, not a mandatory "
|
|
103
|
+
"conversation order; capture what the user gives you and circle back.",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# The canonical guided sequence (devague#4). The engine is move-driven, not a
|
|
107
|
+
# rigid wizard — this is the recommended arc, with the move that advances each.
|
|
108
|
+
STAGES = [
|
|
109
|
+
("Announcement", "what are we saying shipped?", "new"),
|
|
110
|
+
("Audience", "who needs this?", "capture --kind audience"),
|
|
111
|
+
("After", "what changed for them?", "capture --kind after_state"),
|
|
112
|
+
("Matter", "why is it worth doing?", "capture --kind why_it_matters"),
|
|
113
|
+
("Before", "what pain made this necessary?", "capture --kind before_state"),
|
|
114
|
+
("Honest", "what must be true for the announcement to be honest?", "interrogate --honesty"),
|
|
115
|
+
("FAQ", "what hard questions remain?", "interrogate --hard-question"),
|
|
116
|
+
("Boundaries", "what are we not promising?", "capture --kind boundary"),
|
|
117
|
+
("Success", "how will we know?", "capture --kind success_signal"),
|
|
118
|
+
("Spec", "what should be built?", "converge -> export"),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
_TEXT = (
|
|
122
|
+
"devague turns a vague idea into a buildable spec by working backwards.\n\n"
|
|
123
|
+
f"First question: {FIRST_QUESTION}\n"
|
|
124
|
+
f" {SUPPORTING_PROMPT}\n\n"
|
|
125
|
+
"Start from that announcement, then build an Announcement Frame by capturing\n"
|
|
126
|
+
"claims, interrogating them, parking what's still vague, and converging.\n"
|
|
127
|
+
"The arc emerges from the moves; it is not a fixed wizard. You (the agent)\n"
|
|
128
|
+
"choose the next move; devague tracks state. LLM-proposed claims and honesty\n"
|
|
129
|
+
"conditions stay 'proposed' until the user confirms them.\n\n"
|
|
130
|
+
"Guided stages (the recommended sequence — drive them with the moves):\n"
|
|
131
|
+
+ "\n".join(
|
|
132
|
+
f" {i:>2}. {name:<13} {prompt} [{move}]"
|
|
133
|
+
for i, (name, prompt, move) in enumerate(STAGES, 1)
|
|
134
|
+
)
|
|
135
|
+
+ "\n\nMoves:\n"
|
|
136
|
+
+ "\n".join(f" {name:<11} {desc}" for name, desc in MOVES.items())
|
|
137
|
+
+ "\n\ndevague is NOT:\n"
|
|
138
|
+
+ "\n".join(f" - {n}" for n in NOT_A)
|
|
139
|
+
+ "\n\nOperating rules (the anti-fabrication contract — do not violate):\n"
|
|
140
|
+
+ "\n".join(f" - {r}" for r in OPERATING_RULES)
|
|
141
|
+
+ "\n\n"
|
|
142
|
+
+ "Assign-to-workforce: parallel plan execution via subagent-driven development\n"
|
|
143
|
+
f" When: {ASSIGN_TO_WORKFORCE_GUIDANCE['when_to_fan_out']}\n"
|
|
144
|
+
f" Prerequisites: {ASSIGN_TO_WORKFORCE_GUIDANCE['prerequisites']}\n"
|
|
145
|
+
f" Human gates (3): {ASSIGN_TO_WORKFORCE_GUIDANCE['human_gates']}\n"
|
|
146
|
+
f" Safety: {ASSIGN_TO_WORKFORCE_GUIDANCE['worktree_isolation']}\n"
|
|
147
|
+
f" Main agent: {ASSIGN_TO_WORKFORCE_GUIDANCE['main_agent_merge_gate']}\n"
|
|
148
|
+
f" TDD: {ASSIGN_TO_WORKFORCE_GUIDANCE['tdd_acceptance_criteria']}\n"
|
|
149
|
+
f" Scope: {ASSIGN_TO_WORKFORCE_GUIDANCE['not_orchestration']}\n"
|
|
150
|
+
+ "\n\nFull portable guidance for any assisting model:\n"
|
|
151
|
+
f" {GUIDANCE_DOC_URL}\n"
|
|
152
|
+
f" (in the devague repo: {GUIDANCE_DOC_REPO_PATH})\n"
|
|
153
|
+
"Agent-agnostic; your repo-specific agreements live in your agent's main\n"
|
|
154
|
+
"instruction file — AGENTS.md, CLAUDE.md, a system prompt — not there."
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# --- Authoring the three operator skills (devague#34) -----------------------
|
|
159
|
+
#
|
|
160
|
+
# `devague learn` knows the operator skills exist but never taught an agent how
|
|
161
|
+
# to *author* them. This section closes that gap: it is written as instructions
|
|
162
|
+
# the assisting agent follows — devague itself never writes skill files (it stays
|
|
163
|
+
# deterministic and non-orchestrating, #20); the agent does, with user consent.
|
|
164
|
+
#
|
|
165
|
+
# As with GUIDANCE_DOC_URL, neither `docs/` nor `.claude/skills/` ship in the
|
|
166
|
+
# wheel, so the canonical source is referenced by URL — always resolvable for an
|
|
167
|
+
# agent operating an installed devague from an arbitrary repo.
|
|
168
|
+
SKILLS_DOC_URL = "https://github.com/agentculture/devague/blob/main/docs/skills.md"
|
|
169
|
+
SKILLS_DOC_REPO_PATH = "docs/skills.md"
|
|
170
|
+
_SKILLS_BROWSE = "https://github.com/agentculture/devague/blob/main/.claude/skills"
|
|
171
|
+
_SKILLS_RAW = "https://raw.githubusercontent.com/agentculture/devague/main/.claude/skills"
|
|
172
|
+
|
|
173
|
+
# Consent + no-clobber rules — the agent behavior `learn` enforces.
|
|
174
|
+
SKILLS_CONSENT = (
|
|
175
|
+
"These are instructions for you, the assisting agent — not a CLI side effect. "
|
|
176
|
+
"devague never writes skill files; you do.",
|
|
177
|
+
"If the user explicitly asked you to set up or learn the operator skills, "
|
|
178
|
+
"create them now using the recipe and per-skill sources below.",
|
|
179
|
+
"If you reached this by default (e.g. a bare 'devague learn'), ASK the user "
|
|
180
|
+
"for permission before creating anything.",
|
|
181
|
+
"Never overwrite an existing skill of the same name — if <skills>/<name>/ "
|
|
182
|
+
"already exists, stop and surface the conflict instead of clobbering it.",
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# The authoring recipe. Structured so `--json` can carry it verbatim.
|
|
186
|
+
SKILL_AUTHORING = {
|
|
187
|
+
"layout": (
|
|
188
|
+
"Each skill is one directory in your runtime's skills folder "
|
|
189
|
+
"(Claude Code: .claude/skills/<name>/):\n"
|
|
190
|
+
" <skills>/<name>/SKILL.md frontmatter + the operating doc\n"
|
|
191
|
+
" <skills>/<name>/scripts/<name>.sh portable CLI resolver (executable)"
|
|
192
|
+
),
|
|
193
|
+
"frontmatter": (
|
|
194
|
+
"SKILL.md opens with YAML frontmatter:\n"
|
|
195
|
+
" name: <name>\n"
|
|
196
|
+
" description: >\n"
|
|
197
|
+
" one paragraph — what it does, when to use it, and that it is\n"
|
|
198
|
+
" authored in agentculture/devague.\n"
|
|
199
|
+
" type: command # REQUIRED by culture/agex backends; a SKILL.md\n"
|
|
200
|
+
" # without it is SILENTLY SKIPPED. Harmless on claude-code."
|
|
201
|
+
),
|
|
202
|
+
"resolver": (
|
|
203
|
+
"scripts/<name>.sh resolves the devague CLI portably and forwards every "
|
|
204
|
+
"move verbatim:\n"
|
|
205
|
+
" 1. an installed 'devague' on PATH (the normal case);\n"
|
|
206
|
+
" 2. else 'uv run devague' when inside a devague checkout;\n"
|
|
207
|
+
" 3. else print the hint 'uv tool install devague' and exit non-zero.\n"
|
|
208
|
+
"Copy the exact script from the per-skill source below — don't hand-write it."
|
|
209
|
+
),
|
|
210
|
+
"contract": (
|
|
211
|
+
"The skill drives the deterministic CLI and adds no logic of its own:\n"
|
|
212
|
+
" - drive devague through its moves; never edit .devague/ state by hand;\n"
|
|
213
|
+
" - LLM-proposed claims/honesty conditions stay 'proposed' — the user confirms;\n"
|
|
214
|
+
" - three human gates only — the exported spec, the implementation split\n"
|
|
215
|
+
" plan, and the final PR. devague never orchestrates (#20)."
|
|
216
|
+
),
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
# The three operator skills, in workflow order.
|
|
220
|
+
OPERATOR_SKILLS = (
|
|
221
|
+
{
|
|
222
|
+
"name": "think",
|
|
223
|
+
"leg": "idea -> spec (working backwards)",
|
|
224
|
+
"role": (
|
|
225
|
+
"Drives the flat devague verbs. Start from the announcement, capture "
|
|
226
|
+
"and classify claims, interrogate them, park open vagueness, and export "
|
|
227
|
+
"a spec only once the frame converges."
|
|
228
|
+
),
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"name": "spec-to-plan",
|
|
232
|
+
"leg": "spec -> plan (working forwards)",
|
|
233
|
+
"role": (
|
|
234
|
+
"Drives the 'devague plan' group. Seed a plan from a converged frame, "
|
|
235
|
+
"cover every target with TDD-accepted tasks in an acyclic order, and "
|
|
236
|
+
"export a plan only once it converges."
|
|
237
|
+
),
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"name": "assign-to-workforce",
|
|
241
|
+
"leg": "plan -> parallel implementation",
|
|
242
|
+
"role": (
|
|
243
|
+
"Reads 'devague plan waves' and fans out one agent per task per wave in "
|
|
244
|
+
"isolated git worktrees, with main-agent TDD-gated merges. Three human "
|
|
245
|
+
"gates; devague never orchestrates (#20)."
|
|
246
|
+
),
|
|
247
|
+
},
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
_SKILL_NAMES = tuple(s["name"] for s in OPERATOR_SKILLS)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _skill_source(name: str) -> dict[str, str]:
|
|
254
|
+
"""Canonical, always-resolvable source locations for one skill's files."""
|
|
255
|
+
return {
|
|
256
|
+
"browse": f"{_SKILLS_BROWSE}/{name}/SKILL.md",
|
|
257
|
+
"skill_md_raw": f"{_SKILLS_RAW}/{name}/SKILL.md",
|
|
258
|
+
"script_raw": f"{_SKILLS_RAW}/{name}/scripts/{name}.sh",
|
|
259
|
+
"repo_path": f".claude/skills/{name}/",
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _skills_payload(names: tuple[str, ...]) -> dict[str, object]:
|
|
264
|
+
"""Structured skills-authoring payload for the selected skill names."""
|
|
265
|
+
return {
|
|
266
|
+
"consent": list(SKILLS_CONSENT),
|
|
267
|
+
"authoring": SKILL_AUTHORING,
|
|
268
|
+
"operator_skills": [
|
|
269
|
+
{**s, **_skill_source(s["name"])} for s in OPERATOR_SKILLS if s["name"] in names
|
|
270
|
+
],
|
|
271
|
+
"doc_url": SKILLS_DOC_URL,
|
|
272
|
+
"doc_repo_path": SKILLS_DOC_REPO_PATH,
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _skills_text(names: tuple[str, ...], *, full: bool) -> str:
|
|
277
|
+
"""Render the skills-authoring section. `full` adds per-skill source URLs."""
|
|
278
|
+
parts = [
|
|
279
|
+
"Authoring your operator skills (create them with user consent)",
|
|
280
|
+
"=============================================================",
|
|
281
|
+
"",
|
|
282
|
+
"devague is driven by three operator skills. Recreate them in your own "
|
|
283
|
+
"runtime so you can drive devague the same way everywhere.",
|
|
284
|
+
"",
|
|
285
|
+
"Consent — read before creating anything:",
|
|
286
|
+
]
|
|
287
|
+
parts += [f" - {c}" for c in SKILLS_CONSENT]
|
|
288
|
+
parts += [
|
|
289
|
+
"",
|
|
290
|
+
"Recipe:",
|
|
291
|
+
SKILL_AUTHORING["layout"],
|
|
292
|
+
"",
|
|
293
|
+
SKILL_AUTHORING["frontmatter"],
|
|
294
|
+
"",
|
|
295
|
+
SKILL_AUTHORING["resolver"],
|
|
296
|
+
"",
|
|
297
|
+
SKILL_AUTHORING["contract"],
|
|
298
|
+
"",
|
|
299
|
+
"The three skills:",
|
|
300
|
+
]
|
|
301
|
+
for s in OPERATOR_SKILLS:
|
|
302
|
+
if s["name"] not in names:
|
|
303
|
+
continue
|
|
304
|
+
parts.append(f" {s['name']} [{s['leg']}]")
|
|
305
|
+
parts.append(f" {s['role']}")
|
|
306
|
+
if full:
|
|
307
|
+
src = _skill_source(s["name"])
|
|
308
|
+
parts.append(f" SKILL.md: {src['skill_md_raw']}")
|
|
309
|
+
parts.append(f" script: {src['script_raw']}")
|
|
310
|
+
parts.append(f" browse: {src['browse']}")
|
|
311
|
+
parts.append("")
|
|
312
|
+
if not full:
|
|
313
|
+
parts.append(
|
|
314
|
+
"Run 'devague learn skills:all' for the source URLs of all three, or "
|
|
315
|
+
"'devague learn skills:<name>' for one."
|
|
316
|
+
)
|
|
317
|
+
parts.append("")
|
|
318
|
+
parts += [
|
|
319
|
+
f"Full authoring guide: {SKILLS_DOC_URL}",
|
|
320
|
+
f" (in the devague repo: {SKILLS_DOC_REPO_PATH})",
|
|
321
|
+
]
|
|
322
|
+
return "\n".join(parts)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _resolve_topic(topic: str | None) -> tuple[str, tuple[str, ...]]:
|
|
326
|
+
"""Map a learn topic to (mode, skill-names). Raises on an unknown topic."""
|
|
327
|
+
if topic is None:
|
|
328
|
+
return "bare", _SKILL_NAMES
|
|
329
|
+
if topic == "skills":
|
|
330
|
+
return "skills", _SKILL_NAMES
|
|
331
|
+
if topic.startswith("skills:"):
|
|
332
|
+
sub = topic.split(":", 1)[1]
|
|
333
|
+
if sub == "all":
|
|
334
|
+
return "skills_all", _SKILL_NAMES
|
|
335
|
+
if sub in _SKILL_NAMES:
|
|
336
|
+
return "skill", (sub,)
|
|
337
|
+
raise DevagueError(
|
|
338
|
+
EXIT_USER_ERROR,
|
|
339
|
+
f"unknown skill: {sub}",
|
|
340
|
+
"skills: " + ", ".join(_SKILL_NAMES) + ", or 'all'",
|
|
341
|
+
)
|
|
342
|
+
raise DevagueError(
|
|
343
|
+
EXIT_USER_ERROR,
|
|
344
|
+
f"unknown learn topic: {topic}",
|
|
345
|
+
"topics: skills, skills:all, skills:<name>",
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def cmd_learn(args: argparse.Namespace) -> int:
|
|
350
|
+
mode, names = _resolve_topic(getattr(args, "topic", None))
|
|
351
|
+
json_mode = getattr(args, "json", False)
|
|
352
|
+
|
|
353
|
+
if mode == "bare":
|
|
354
|
+
if json_mode:
|
|
355
|
+
emit_result(
|
|
356
|
+
{
|
|
357
|
+
"tool": "devague",
|
|
358
|
+
"version": __version__,
|
|
359
|
+
"first_question": FIRST_QUESTION,
|
|
360
|
+
"supporting_prompt": SUPPORTING_PROMPT,
|
|
361
|
+
"stages": [
|
|
362
|
+
{"step": i, "name": name, "prompt": prompt, "move": move}
|
|
363
|
+
for i, (name, prompt, move) in enumerate(STAGES, 1)
|
|
364
|
+
],
|
|
365
|
+
"moves": list(MOVES),
|
|
366
|
+
"not_a": list(NOT_A),
|
|
367
|
+
"operating_rules": list(OPERATING_RULES),
|
|
368
|
+
"guidance_doc": GUIDANCE_DOC_URL,
|
|
369
|
+
"guidance_doc_repo_path": GUIDANCE_DOC_REPO_PATH,
|
|
370
|
+
"assign_to_workforce": ASSIGN_TO_WORKFORCE_GUIDANCE,
|
|
371
|
+
"skills": _skills_payload(names),
|
|
372
|
+
"summary": _TEXT,
|
|
373
|
+
},
|
|
374
|
+
json_mode=True,
|
|
375
|
+
)
|
|
376
|
+
else:
|
|
377
|
+
emit_result(_TEXT + "\n\n" + _skills_text(names, full=False), json_mode=False)
|
|
378
|
+
else:
|
|
379
|
+
# A skills topic: emit just the authoring section. The JSON envelope
|
|
380
|
+
# carries the same tool/version identity as the bare payload so the
|
|
381
|
+
# whole `learn` command family shares one schema (Qodo PR #35).
|
|
382
|
+
full = mode in ("skills_all", "skill")
|
|
383
|
+
if json_mode:
|
|
384
|
+
emit_result(
|
|
385
|
+
{
|
|
386
|
+
"tool": "devague",
|
|
387
|
+
"version": __version__,
|
|
388
|
+
"topic": getattr(args, "topic", None),
|
|
389
|
+
**_skills_payload(names),
|
|
390
|
+
},
|
|
391
|
+
json_mode=True,
|
|
392
|
+
)
|
|
393
|
+
else:
|
|
394
|
+
emit_result(_skills_text(names, full=full), json_mode=False)
|
|
395
|
+
return 0
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def register(sub: argparse._SubParsersAction) -> None:
|
|
399
|
+
p = sub.add_parser("learn", help="Teach devague's method and how to author its skills.")
|
|
400
|
+
p.add_argument(
|
|
401
|
+
"topic",
|
|
402
|
+
nargs="?",
|
|
403
|
+
default=None,
|
|
404
|
+
help="Optional: 'skills', 'skills:all', or 'skills:<name>' to teach skill authoring.",
|
|
405
|
+
)
|
|
406
|
+
p.add_argument("--json", action="store_true", help="Emit structured JSON.")
|
|
407
|
+
p.set_defaults(func=cmd_learn)
|
|
@@ -394,7 +394,11 @@ def cmd_plan_learn(args: argparse.Namespace) -> int:
|
|
|
394
394
|
"anti-fabrication rule as claims. A plan exports only once it converges:\n"
|
|
395
395
|
"every target covered by a confirmed task, every confirmed task has acceptance\n"
|
|
396
396
|
"criteria, the dependency graph is acyclic, and no blocking risk remains.\n\n"
|
|
397
|
-
"Moves:\n"
|
|
397
|
+
"Moves:\n"
|
|
398
|
+
+ "\n".join(f" {name:<9} {desc}" for name, desc in PLAN_MOVES.items())
|
|
399
|
+
+ "\n\nTo author the operator skills (think / spec-to-plan / "
|
|
400
|
+
"assign-to-workforce) in\nyour own runtime, run 'devague learn skills' "
|
|
401
|
+
"(with user consent)."
|
|
398
402
|
)
|
|
399
403
|
if getattr(args, "json", False):
|
|
400
404
|
emit_result(
|
|
@@ -126,5 +126,8 @@ same stream. Exit code is `0` on success, non-zero on user error (with a
|
|
|
126
126
|
- **Entity model + per-move contract:** [`spec-contract.md`](spec-contract.md).
|
|
127
127
|
- **Live shape of any move:** run it with `--json`, or `devague learn` /
|
|
128
128
|
`devague explain <move>`.
|
|
129
|
+
- **Authoring the operator skills:** [`skills.md`](skills.md), or run `devague
|
|
130
|
+
learn skills` — how to create the `think` / `spec-to-plan` /
|
|
131
|
+
`assign-to-workforce` skills in your own runtime (with user consent).
|
|
129
132
|
- **Repo-specific working agreements:** your agent's main instruction file
|
|
130
133
|
(`AGENTS.md`, `CLAUDE.md`, system prompt, …) — not this document.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Authoring devague's operator skills
|
|
2
|
+
|
|
3
|
+
This is the canonical guide for **authoring the three devague operator skills**
|
|
4
|
+
in an agent runtime — what files they need, where they live, the entry-point
|
|
5
|
+
shape, and the contract between a skill and devague's state. It is the long-form
|
|
6
|
+
companion to `devague learn skills`, which surfaces a condensed, always-available
|
|
7
|
+
version of the same recipe.
|
|
8
|
+
|
|
9
|
+
These three skills are devague's **outbound** skills — devague is their
|
|
10
|
+
origin/upstream (it dogfoods them to drive its own CLI), and steward re-vendors
|
|
11
|
+
them to the rest of the AgentCulture mesh under the `cite, don't import` policy.
|
|
12
|
+
See [`skill-sources.md`](skill-sources.md) for the provenance map. This guide is
|
|
13
|
+
about *creating* them in a runtime that doesn't have them yet.
|
|
14
|
+
|
|
15
|
+
## Consent — instructions for the assisting agent
|
|
16
|
+
|
|
17
|
+
devague **never writes skill files**; it stays a deterministic, non-orchestrating
|
|
18
|
+
CLI (#20). *You*, the agent, create the files — with the user in the loop:
|
|
19
|
+
|
|
20
|
+
- If the user explicitly asked you to set up or learn the operator skills,
|
|
21
|
+
create them now using the recipe below.
|
|
22
|
+
- If you reached this by default (for example a bare `devague learn`), **ask the
|
|
23
|
+
user for permission** before creating anything.
|
|
24
|
+
- **Never overwrite** an existing skill of the same name. If
|
|
25
|
+
`<skills>/<name>/` already exists, stop and surface the conflict to the user
|
|
26
|
+
instead of clobbering it.
|
|
27
|
+
|
|
28
|
+
## Minimum file structure
|
|
29
|
+
|
|
30
|
+
Each skill is one directory in your runtime's skills folder. For the Claude Code
|
|
31
|
+
backend that is `.claude/skills/<name>/`:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
<skills>/<name>/
|
|
35
|
+
├── SKILL.md # YAML frontmatter + the operating doc
|
|
36
|
+
└── scripts/
|
|
37
|
+
└── <name>.sh # portable CLI resolver (executable)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`SKILL.md` opens with YAML frontmatter:
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
---
|
|
44
|
+
name: <name>
|
|
45
|
+
description: >
|
|
46
|
+
One paragraph — what the skill does, when to use it (the trigger phrases),
|
|
47
|
+
and that it is authored in agentculture/devague.
|
|
48
|
+
type: command
|
|
49
|
+
---
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`type: command` is **required by the culture/agex backends** — a `SKILL.md`
|
|
53
|
+
without it is *silently skipped* by `backends/claude_code/probe.py`. It is
|
|
54
|
+
harmless on the `claude-code` backend, so always include it for portability.
|
|
55
|
+
|
|
56
|
+
The body of `SKILL.md` (after the frontmatter) is the operating doc: a short
|
|
57
|
+
intro, a "How to run" section, a moves/commands table, the hard rules, the
|
|
58
|
+
output contract, and a worked example. The three canonical skills are the
|
|
59
|
+
reference for this shape — copy from the sources linked below rather than
|
|
60
|
+
inventing structure.
|
|
61
|
+
|
|
62
|
+
## The entry-point script — portable CLI resolver
|
|
63
|
+
|
|
64
|
+
`scripts/<name>.sh` resolves the devague CLI portably and **forwards every move
|
|
65
|
+
verbatim**, so the CLI's own parser owns the surface and new devague moves work
|
|
66
|
+
without editing the script. The resolution order is:
|
|
67
|
+
|
|
68
|
+
1. an installed `devague` on `PATH` (the normal mesh case);
|
|
69
|
+
2. else `uv run devague` when inside a devague checkout (walk up to a
|
|
70
|
+
`pyproject.toml` whose `name = "devague"`);
|
|
71
|
+
3. else print the hint `uv tool install devague` and exit non-zero.
|
|
72
|
+
|
|
73
|
+
The shape (copy the exact script from the per-skill source — don't hand-write it):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
#!/usr/bin/env bash
|
|
77
|
+
set -euo pipefail
|
|
78
|
+
|
|
79
|
+
DEVAGUE=()
|
|
80
|
+
resolve_devague() {
|
|
81
|
+
if command -v devague >/dev/null 2>&1; then
|
|
82
|
+
DEVAGUE=(devague) # installed tool — the normal mesh case
|
|
83
|
+
return 0
|
|
84
|
+
fi
|
|
85
|
+
# Local-dev fallback: inside the devague checkout, run via uv.
|
|
86
|
+
local dir="$PWD"
|
|
87
|
+
while [ -n "$dir" ] && [ "$dir" != "/" ]; do
|
|
88
|
+
if [ -f "$dir/pyproject.toml" ] \
|
|
89
|
+
&& grep -q '^name = "devague"' "$dir/pyproject.toml" 2>/dev/null; then
|
|
90
|
+
command -v uv >/dev/null 2>&1 && { DEVAGUE=(uv run devague); return 0; }
|
|
91
|
+
break
|
|
92
|
+
fi
|
|
93
|
+
dir=$(dirname "$dir")
|
|
94
|
+
done
|
|
95
|
+
echo "error: devague CLI not found." >&2
|
|
96
|
+
echo "hint: install it with \`uv tool install devague\`." >&2
|
|
97
|
+
return 1
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
resolve_devague
|
|
101
|
+
exec "${DEVAGUE[@]}" "$@"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`assign-to-workforce` adds one orchestration layer on top of this resolver (a
|
|
105
|
+
`split-plan` subcommand that renders `devague plan waves --json` as a human-facing
|
|
106
|
+
table); the underlying `waves` call is still forwarded verbatim.
|
|
107
|
+
|
|
108
|
+
## The skill ↔ devague contract
|
|
109
|
+
|
|
110
|
+
A skill drives the deterministic CLI and adds no business logic of its own:
|
|
111
|
+
|
|
112
|
+
- **Drive devague through its moves; never edit `.devague/` state by hand.** The
|
|
113
|
+
CLI owns the frame/plan JSON under `.devague/`. A skill reads results from
|
|
114
|
+
stdout (`--json` for structured payloads) and acts on them — it does not poke
|
|
115
|
+
at the store directly.
|
|
116
|
+
- **LLM-proposed claims and honesty conditions stay `proposed`.** Confirmation is
|
|
117
|
+
a user-only decision; the agent surfaces proposals and lets the user confirm or
|
|
118
|
+
reject. This anti-fabrication contract is what makes convergence mean something
|
|
119
|
+
(see [`llm-guidance.md`](llm-guidance.md)).
|
|
120
|
+
- **Three human gates only:** the exported spec, the implementation split plan
|
|
121
|
+
(task map + per-task agent/model proposal + go/no-go), and the final PR.
|
|
122
|
+
devague never orchestrates — it only *describes* the dependency graph via
|
|
123
|
+
`devague plan waves` (#20). The fan-out, worktree management, and TDD-gated
|
|
124
|
+
merges live in the `assign-to-workforce` skill and the operating agent, not in
|
|
125
|
+
the CLI and not in a CI runner.
|
|
126
|
+
|
|
127
|
+
## The three operator skills
|
|
128
|
+
|
|
129
|
+
| Skill | Leg | What it drives |
|
|
130
|
+
|-------|-----|----------------|
|
|
131
|
+
| `think` | idea → spec (working backwards) | the flat `devague <move>` verbs |
|
|
132
|
+
| `spec-to-plan` | spec → plan (working forwards) | the `devague plan <move>` group |
|
|
133
|
+
| `assign-to-workforce` | plan → parallel implementation | reads `devague plan waves` (read-only) |
|
|
134
|
+
|
|
135
|
+
### `think` — idea → buildable spec
|
|
136
|
+
|
|
137
|
+
Start from the announcement ("pretend it shipped — what would you announce?"),
|
|
138
|
+
build an Announcement Frame by capturing and classifying claims, pressure-test
|
|
139
|
+
them with honesty conditions and hard questions, park genuine unknowns as
|
|
140
|
+
first-class open vagueness, and `export` a spec only once the frame **converges**.
|
|
141
|
+
|
|
142
|
+
- Source:
|
|
143
|
+
[`.claude/skills/think/`](https://github.com/agentculture/devague/blob/main/.claude/skills/think/SKILL.md)
|
|
144
|
+
(`SKILL.md` + `scripts/think.sh`).
|
|
145
|
+
|
|
146
|
+
### `spec-to-plan` — converged spec → buildable plan
|
|
147
|
+
|
|
148
|
+
Seed a plan from a **converged** frame (`devague plan new --frame <slug>`), add
|
|
149
|
+
tasks that cover every coverage target with acceptance criteria and an acyclic
|
|
150
|
+
dependency order, park unknowns as first-class risks, and `export` only once the
|
|
151
|
+
plan converges. Coaches small, file-disjoint, TDD-accepted tasks so the
|
|
152
|
+
downstream fan-out can run wide waves.
|
|
153
|
+
|
|
154
|
+
- Source:
|
|
155
|
+
[`.claude/skills/spec-to-plan/`](https://github.com/agentculture/devague/blob/main/.claude/skills/spec-to-plan/SKILL.md)
|
|
156
|
+
(`SKILL.md` + `scripts/spec-to-plan.sh`).
|
|
157
|
+
|
|
158
|
+
### `assign-to-workforce` — converged plan → parallel implementation
|
|
159
|
+
|
|
160
|
+
Reads `devague plan waves` (deterministic, read-only scheduling metadata) and
|
|
161
|
+
fans out independent tasks to **one agent per task per wave in isolated git
|
|
162
|
+
worktrees**, with **main-agent TDD-gated merges** (the task's tests pass before
|
|
163
|
+
*and* after the merge). Exactly three human gates; the final PR uses the `cicd`
|
|
164
|
+
skill (`agex pr open`).
|
|
165
|
+
|
|
166
|
+
- Source:
|
|
167
|
+
[`.claude/skills/assign-to-workforce/`](https://github.com/agentculture/devague/blob/main/.claude/skills/assign-to-workforce/SKILL.md)
|
|
168
|
+
(`SKILL.md` + `scripts/assign-to-workforce.sh`).
|
|
169
|
+
|
|
170
|
+
## See also
|
|
171
|
+
|
|
172
|
+
- `devague learn skills` / `devague learn skills:all` / `devague learn
|
|
173
|
+
skills:<name>` — the condensed, always-available form of this guide, with the
|
|
174
|
+
canonical source URLs for each skill (works for an agent operating an installed
|
|
175
|
+
devague with no checkout).
|
|
176
|
+
- [`llm-guidance.md`](llm-guidance.md) — the portable operating contract every
|
|
177
|
+
operator skill upholds.
|
|
178
|
+
- [`skill-sources.md`](skill-sources.md) — provenance and the `cite, don't
|
|
179
|
+
import` vendoring policy.
|