stepgate 0.1.0__tar.gz → 0.2.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.
- {stepgate-0.1.0 → stepgate-0.2.0}/PKG-INFO +6 -2
- {stepgate-0.1.0 → stepgate-0.2.0}/README.md +5 -1
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/__init__.py +1 -1
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/cli.py +10 -6
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/commands/init_cmd.py +4 -1
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/commands/lifecycle.py +18 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/tests/test_cli_smoke.py +43 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/.gitignore +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/LICENSE +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/pyproject.toml +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/__main__.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/commands/__init__.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/commands/doctor.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/commands/views.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/model.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/render.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/src/stepgate/store.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/tests/conftest.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/tests/test_init.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/tests/test_state_machine.py +0 -0
- {stepgate-0.1.0 → stepgate-0.2.0}/tests/test_store.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stepgate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A step-gated micro-change protocol CLI for coding agents: propose, approve, execute, verify — one small step at a time.
|
|
5
5
|
Project-URL: Homepage, https://github.com/LeoCostta/stepgate
|
|
6
6
|
Project-URL: Repository, https://github.com/LeoCostta/stepgate
|
|
@@ -135,7 +135,11 @@ all sessions and agents, append-only.
|
|
|
135
135
|
| `stepgate verify --evidence "..."` | Record verification evidence |
|
|
136
136
|
| `stepgate close` | Close a verified micro-change |
|
|
137
137
|
| `stepgate abandon --reason "..."` | Cleanly abandon from any non-terminal state |
|
|
138
|
-
| `stepgate next --suggest "..."` |
|
|
138
|
+
| `stepgate next [--suggest "..."]` | With `--suggest`, record a next-step suggestion; without it, show the current one |
|
|
139
|
+
|
|
140
|
+
The four flow verbs also accept Portuguese aliases — English stays the default:
|
|
141
|
+
`aprovar` = `approve`, `rejeitar` = `reject`, `fechar` = `close`,
|
|
142
|
+
`proximo` = `next`.
|
|
139
143
|
| `stepgate status` | Current session + aggregated project view |
|
|
140
144
|
| `stepgate history [--session X] [--since DATE]` | Append-only, cross-session log |
|
|
141
145
|
| `stepgate doctor` | Report corrupted/invalid state files (fixes nothing) |
|
|
@@ -106,7 +106,11 @@ all sessions and agents, append-only.
|
|
|
106
106
|
| `stepgate verify --evidence "..."` | Record verification evidence |
|
|
107
107
|
| `stepgate close` | Close a verified micro-change |
|
|
108
108
|
| `stepgate abandon --reason "..."` | Cleanly abandon from any non-terminal state |
|
|
109
|
-
| `stepgate next --suggest "..."` |
|
|
109
|
+
| `stepgate next [--suggest "..."]` | With `--suggest`, record a next-step suggestion; without it, show the current one |
|
|
110
|
+
|
|
111
|
+
The four flow verbs also accept Portuguese aliases — English stays the default:
|
|
112
|
+
`aprovar` = `approve`, `rejeitar` = `reject`, `fechar` = `close`,
|
|
113
|
+
`proximo` = `next`.
|
|
110
114
|
| `stepgate status` | Current session + aggregated project view |
|
|
111
115
|
| `stepgate history [--session X] [--since DATE]` | Append-only, cross-session log |
|
|
112
116
|
| `stepgate doctor` | Report corrupted/invalid state files (fixes nothing) |
|
|
@@ -44,13 +44,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
44
44
|
p = sub.add_parser("show", help="show the active proposal rendered as prose")
|
|
45
45
|
_add_session_flags(p)
|
|
46
46
|
|
|
47
|
-
p = sub.add_parser("approve", help="PENDING -> APPROVED")
|
|
47
|
+
p = sub.add_parser("approve", aliases=["aprovar"], help="PENDING -> APPROVED")
|
|
48
48
|
p.add_argument("--adjust", action="store_true", help="approve with an adjusted/reduced scope")
|
|
49
49
|
p.add_argument("--scope", help="comma-separated adjusted scope (files/areas)")
|
|
50
50
|
p.add_argument("--note", help="note describing the adjustment")
|
|
51
51
|
_add_session_flags(p)
|
|
52
52
|
|
|
53
|
-
p = sub.add_parser("reject", help="PENDING -> REJECTED")
|
|
53
|
+
p = sub.add_parser("reject", aliases=["rejeitar"], help="PENDING -> REJECTED")
|
|
54
54
|
p.add_argument("--note", required=True, help="reason for rejection")
|
|
55
55
|
_add_session_flags(p)
|
|
56
56
|
|
|
@@ -63,15 +63,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
63
63
|
p.add_argument("--evidence", required=True, help="tests/runs/evidence demonstrating the result")
|
|
64
64
|
_add_session_flags(p)
|
|
65
65
|
|
|
66
|
-
p = sub.add_parser("close", help="VERIFIED -> CLOSED")
|
|
66
|
+
p = sub.add_parser("close", aliases=["fechar"], help="VERIFIED -> CLOSED")
|
|
67
67
|
_add_session_flags(p)
|
|
68
68
|
|
|
69
69
|
p = sub.add_parser("abandon", help="any non-terminal state -> ABANDONED")
|
|
70
70
|
p.add_argument("--reason", required=True, help="why the proposal is being abandoned")
|
|
71
71
|
_add_session_flags(p)
|
|
72
72
|
|
|
73
|
-
p = sub.add_parser("next", help="record a next-step suggestion (does not open a proposal)")
|
|
74
|
-
p.add_argument("--suggest",
|
|
73
|
+
p = sub.add_parser("next", aliases=["proximo"], help="show or record a next-step suggestion (does not open a proposal)")
|
|
74
|
+
p.add_argument("--suggest", help="the suggested next step; omit to show the current one")
|
|
75
75
|
_add_session_flags(p)
|
|
76
76
|
|
|
77
77
|
p = sub.add_parser("status", help="current session state + aggregated project view")
|
|
@@ -115,8 +115,12 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
115
115
|
"history": views.cmd_history,
|
|
116
116
|
"doctor": doctor.cmd_doctor,
|
|
117
117
|
}
|
|
118
|
+
# argparse fills args.command with the alias actually typed, so map the
|
|
119
|
+
# convenience aliases back to their canonical command name.
|
|
120
|
+
aliases = {"aprovar": "approve", "rejeitar": "reject", "fechar": "close", "proximo": "next"}
|
|
121
|
+
command = aliases.get(args.command, args.command)
|
|
118
122
|
try:
|
|
119
|
-
return handlers[
|
|
123
|
+
return handlers[command](args)
|
|
120
124
|
except StepgateError as exc:
|
|
121
125
|
print(f"stepgate: error: {exc}", file=sys.stderr)
|
|
122
126
|
return 1
|
|
@@ -54,7 +54,8 @@ the scope of *execution*, never the depth of *investigation*. Then:
|
|
|
54
54
|
`stepgate exec-log --summary "..." --files "a,b"`.
|
|
55
55
|
4. Verify with real evidence: `stepgate verify --evidence "npm test: 12 passed"`.
|
|
56
56
|
5. After the user runs `stepgate close`, suggest (don't start) the next step:
|
|
57
|
-
`stepgate next --suggest "..."`.
|
|
57
|
+
`stepgate next --suggest "..."`. Running `stepgate next` with no `--suggest`
|
|
58
|
+
just shows the currently recorded suggestion, without changing anything.
|
|
58
59
|
|
|
59
60
|
**Rules:**
|
|
60
61
|
- Approval is per micro-change, never cumulative. One approval is not a blanket
|
|
@@ -68,6 +69,8 @@ the scope of *execution*, never the depth of *investigation*. Then:
|
|
|
68
69
|
proposing, or when closing a cycle) — not as a habitual check.
|
|
69
70
|
- stepgate never blocks edits, commits, or the user. It records and makes the
|
|
70
71
|
flow visible; deviating from it is visible, never silent.
|
|
72
|
+
- The four flow verbs accept Portuguese aliases (English stays the default):
|
|
73
|
+
`aprovar` = approve, `rejeitar` = reject, `fechar` = close, `proximo` = next.
|
|
71
74
|
{END_MARKER}"""
|
|
72
75
|
|
|
73
76
|
|
|
@@ -8,6 +8,8 @@ import json
|
|
|
8
8
|
import subprocess
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
11
13
|
from stepgate import render
|
|
12
14
|
from stepgate.model import (
|
|
13
15
|
APPROVED,
|
|
@@ -228,6 +230,22 @@ def cmd_abandon(args) -> int:
|
|
|
228
230
|
def cmd_next(args) -> int:
|
|
229
231
|
store = Store.find()
|
|
230
232
|
session = resolve_session(store, args, active_only=False)
|
|
233
|
+
if args.suggest is None:
|
|
234
|
+
if session.pending_suggestion:
|
|
235
|
+
render.console.print(
|
|
236
|
+
Text.assemble(
|
|
237
|
+
("Suggested next step", "bold green"),
|
|
238
|
+
(f" ({session.name}): ", "dim"),
|
|
239
|
+
session.pending_suggestion,
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
else:
|
|
243
|
+
render.info(
|
|
244
|
+
f"No next-step suggestion recorded in session "
|
|
245
|
+
f"[bold magenta]{session.name}[/]. Record one with "
|
|
246
|
+
"'stepgate next --suggest \"...\"'."
|
|
247
|
+
)
|
|
248
|
+
return 0
|
|
231
249
|
session.pending_suggestion = args.suggest
|
|
232
250
|
store.save_session(session)
|
|
233
251
|
store.append_history(session.name, session.agent, "next-suggest", {"suggestion": args.suggest})
|
|
@@ -54,6 +54,49 @@ def test_full_cycle(project, plan_file, capsys):
|
|
|
54
54
|
assert read_state(project).pending_suggestion is None
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def test_next_without_suggest_shows_current(project, plan_file, capsys):
|
|
58
|
+
run("propose", "--agent", "claude", "--file", str(plan_file))
|
|
59
|
+
run("next", "--agent", "claude", "--suggest", "wire SalaJogo.tsx via supabase.rpc")
|
|
60
|
+
store = Store.find(project)
|
|
61
|
+
before = len(store.read_history())
|
|
62
|
+
capsys.readouterr()
|
|
63
|
+
|
|
64
|
+
# read-only: prints the recorded suggestion, writes nothing
|
|
65
|
+
assert run("next", "--agent", "claude") == 0
|
|
66
|
+
assert "supabase.rpc" in capsys.readouterr().out
|
|
67
|
+
assert len(Store.find(project).read_history()) == before
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_next_without_suggest_and_none_recorded(project, plan_file, capsys):
|
|
71
|
+
run("propose", "--agent", "claude", "--file", str(plan_file))
|
|
72
|
+
capsys.readouterr()
|
|
73
|
+
assert run("next", "--agent", "claude") == 0
|
|
74
|
+
assert "No next-step suggestion" in capsys.readouterr().out
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_portuguese_aliases(project, plan_file, capsys):
|
|
78
|
+
run("propose", "--agent", "claude", "--file", str(plan_file))
|
|
79
|
+
# 'aprovar' == approve
|
|
80
|
+
assert run("aprovar", "--agent", "claude") == 0
|
|
81
|
+
assert read_state(project).proposal.state == "APPROVED"
|
|
82
|
+
run("exec-log", "--agent", "claude", "--summary", "done")
|
|
83
|
+
run("verify", "--agent", "claude", "--evidence", "ok")
|
|
84
|
+
# 'fechar' == close
|
|
85
|
+
assert run("fechar", "--agent", "claude") == 0
|
|
86
|
+
assert read_state(project).proposal.state == "CLOSED"
|
|
87
|
+
# 'proximo' == next: records with --suggest, reads without it
|
|
88
|
+
assert run("proximo", "--agent", "claude", "--suggest", "wire it up") == 0
|
|
89
|
+
capsys.readouterr()
|
|
90
|
+
assert run("proximo", "--agent", "claude") == 0
|
|
91
|
+
assert "wire it up" in capsys.readouterr().out
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_rejeitar_alias(project, plan_file):
|
|
95
|
+
run("propose", "--agent", "claude", "--file", str(plan_file))
|
|
96
|
+
assert run("rejeitar", "--agent", "claude", "--note", "abordagem errada") == 0
|
|
97
|
+
assert read_state(project).proposal.state == "REJECTED"
|
|
98
|
+
|
|
99
|
+
|
|
57
100
|
def test_cannot_skip_states_via_cli(project, plan_file, capsys):
|
|
58
101
|
run("propose", "--agent", "claude", "--file", str(plan_file))
|
|
59
102
|
assert run("exec-log", "--agent", "claude", "--summary", "sneaky") == 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|