spec-agent-cli 0.1.0__py3-none-any.whl
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.
- spec_agent/__init__.py +11 -0
- spec_agent/__main__.py +9 -0
- spec_agent/cli.py +70 -0
- spec_agent/commands/__init__.py +1 -0
- spec_agent/commands/init.py +29 -0
- spec_agent/installer.py +201 -0
- spec_agent/package_assets.py +56 -0
- spec_agent/resources/project-rules.md +18 -0
- spec_agent/resources/skills/spec-drift-sync/SKILL.md +60 -0
- spec_agent/resources/skills/spec-drift-sync/scripts/check.py +404 -0
- spec_agent/resources/skills/spec-evolution/SKILL.md +57 -0
- spec_agent/resources/skills/spec-evolution/scripts/record.py +126 -0
- spec_agent/resources/skills/spec-evolution/scripts/timeline.py +138 -0
- spec_agent/resources/skills/spec-request-flow/SKILL.md +78 -0
- spec_agent/resources/skills/spec-request-flow/assets/SPEC.template.md +41 -0
- spec_agent/resources/skills/spec-request-flow/assets/feature-acceptance.template.md +46 -0
- spec_agent/resources/skills/spec-request-flow/assets/feature-spec.template.md +71 -0
- spec_agent/resources/skills/spec-request-flow/references/spec-format.md +60 -0
- spec_agent/resources/skills/spec-request-flow/scripts/create_feature.py +63 -0
- spec_agent_cli-0.1.0.dist-info/METADATA +86 -0
- spec_agent_cli-0.1.0.dist-info/RECORD +24 -0
- spec_agent_cli-0.1.0.dist-info/WHEEL +4 -0
- spec_agent_cli-0.1.0.dist-info/entry_points.txt +2 -0
- spec_agent_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render deterministic Markdown and Mermaid from an evolution JSONL log."""
|
|
3
|
+
|
|
4
|
+
# spec: ELOG-005
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import json
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, NamedTuple
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EvolutionEvent(NamedTuple):
|
|
16
|
+
event_id: str
|
|
17
|
+
timestamp: str
|
|
18
|
+
source: str
|
|
19
|
+
title: str
|
|
20
|
+
task_type: str
|
|
21
|
+
intent: str
|
|
22
|
+
behavior_ids: tuple[str, ...]
|
|
23
|
+
decision: str
|
|
24
|
+
rationale: str
|
|
25
|
+
spec_delta: str
|
|
26
|
+
supersedes: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def read_events(events_log: Path) -> list[EvolutionEvent]:
|
|
30
|
+
events: list[EvolutionEvent] = []
|
|
31
|
+
for line_number, line in enumerate(
|
|
32
|
+
events_log.read_text(encoding="utf-8").splitlines(), 1
|
|
33
|
+
):
|
|
34
|
+
if not line.strip():
|
|
35
|
+
continue
|
|
36
|
+
try:
|
|
37
|
+
value = json.loads(line)
|
|
38
|
+
except json.JSONDecodeError as error:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
f"invalid JSONL at {events_log}: line {line_number}: {error.msg}"
|
|
41
|
+
) from error
|
|
42
|
+
if not isinstance(value, dict):
|
|
43
|
+
raise ValueError(
|
|
44
|
+
f"invalid JSONL at {events_log}: line {line_number}: expected object"
|
|
45
|
+
)
|
|
46
|
+
events.append(parse_event(value, f"line {line_number}"))
|
|
47
|
+
return sorted(events, key=lambda event: (_timestamp_key(event.timestamp), event.source))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def parse_event(value: dict[str, Any], source: str) -> EvolutionEvent:
|
|
51
|
+
return EvolutionEvent(
|
|
52
|
+
event_id=_text(value.get("id")),
|
|
53
|
+
timestamp=_text(value.get("timestamp")),
|
|
54
|
+
source=source,
|
|
55
|
+
title=_text(value.get("title")),
|
|
56
|
+
task_type=_text(value.get("task_type")),
|
|
57
|
+
intent=_text(value.get("user_intent")),
|
|
58
|
+
behavior_ids=_string_list(value.get("behavior_ids")),
|
|
59
|
+
decision=_text(value.get("decision")),
|
|
60
|
+
rationale=_text(value.get("rationale")),
|
|
61
|
+
spec_delta=_text(value.get("spec_delta")),
|
|
62
|
+
supersedes=_text(value.get("supersedes")),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def render_markdown(events: list[EvolutionEvent]) -> str:
|
|
67
|
+
lines = [
|
|
68
|
+
"<!-- Generated from spec/evolution/events.jsonl by .agents/skills/spec-evolution/scripts/timeline.py; do not edit. -->",
|
|
69
|
+
"# Specification Evolution Timeline",
|
|
70
|
+
"",
|
|
71
|
+
]
|
|
72
|
+
for event in events:
|
|
73
|
+
behaviors = ", ".join(event.behavior_ids) or "missing"
|
|
74
|
+
lines.extend(
|
|
75
|
+
[
|
|
76
|
+
f"## {event.timestamp} — {event.title}",
|
|
77
|
+
"",
|
|
78
|
+
f"- Event: {event.event_id}",
|
|
79
|
+
f"- Task type: {event.task_type}",
|
|
80
|
+
f"- User intent: {event.intent}",
|
|
81
|
+
f"- Behavior IDs: {behaviors}",
|
|
82
|
+
f"- Product decision: {event.decision}",
|
|
83
|
+
f"- Rationale: {event.rationale}",
|
|
84
|
+
f"- Specification delta: {event.spec_delta}",
|
|
85
|
+
f"- Supersedes: {event.supersedes}",
|
|
86
|
+
"",
|
|
87
|
+
]
|
|
88
|
+
)
|
|
89
|
+
lines.extend(
|
|
90
|
+
["## Mermaid", "", "```mermaid", "timeline", " title Specification Evolution"]
|
|
91
|
+
)
|
|
92
|
+
for event in events:
|
|
93
|
+
label = _mermaid_text(f"{event.event_id} — {event.title}")
|
|
94
|
+
lines.append(f" {event.timestamp} : {label}")
|
|
95
|
+
lines.extend(["```", ""])
|
|
96
|
+
return "\n".join(lines)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def main(argv: list[str] | None = None) -> int:
|
|
100
|
+
parser = argparse.ArgumentParser(description="Render specification evolution events.")
|
|
101
|
+
parser.add_argument("--log", "--events", dest="events", type=Path, required=True)
|
|
102
|
+
parser.add_argument("--output", type=Path)
|
|
103
|
+
args = parser.parse_args(argv)
|
|
104
|
+
if not args.events.is_file():
|
|
105
|
+
parser.error(f"event log does not exist: {args.events}")
|
|
106
|
+
output = render_markdown(read_events(args.events))
|
|
107
|
+
if args.output:
|
|
108
|
+
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
109
|
+
args.output.write_text(output, encoding="utf-8")
|
|
110
|
+
else:
|
|
111
|
+
print(output, end="")
|
|
112
|
+
return 0
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _text(value: Any) -> str:
|
|
116
|
+
return value.strip() if isinstance(value, str) and value.strip() else "missing"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _string_list(value: Any) -> tuple[str, ...]:
|
|
120
|
+
if not isinstance(value, list):
|
|
121
|
+
return ()
|
|
122
|
+
return tuple(item.strip() for item in value if isinstance(item, str) and item.strip())
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _timestamp_key(value: str) -> tuple[int, str]:
|
|
126
|
+
try:
|
|
127
|
+
datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
128
|
+
return (0, value)
|
|
129
|
+
except ValueError:
|
|
130
|
+
return (1, value)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _mermaid_text(value: str) -> str:
|
|
134
|
+
return value.replace(":", "-").replace("\n", " ").strip() or "missing"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec-request-flow
|
|
3
|
+
description: Use when a user asks to add, change, fix, refactor, design, or implement repository behavior under spec-driven development, including vague requests and follow-ups that modify an existing feature even when specifications are not mentioned.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Spec Request Flow
|
|
7
|
+
|
|
8
|
+
Produce an approved, implementation-independent product specification, then stop with
|
|
9
|
+
a discoverable Code Agent handoff.
|
|
10
|
+
|
|
11
|
+
<HARD-GATE>
|
|
12
|
+
Do not scaffold or write specification files before the user approves the presented
|
|
13
|
+
product decisions. Do not create an implementation plan, choose repository files,
|
|
14
|
+
write code or tests, dispatch implementation, or perform engineering verification at
|
|
15
|
+
any point in this skill.
|
|
16
|
+
</HARD-GATE>
|
|
17
|
+
|
|
18
|
+
## Explore context
|
|
19
|
+
|
|
20
|
+
1. Read applicable `AGENTS.md`, root `SPEC.md`, the existing feature packet, relevant
|
|
21
|
+
product behavior, and related `spec/evolution/events.jsonl` entries.
|
|
22
|
+
2. Separate evidence, outcomes, conflicts, assumptions, and unknowns.
|
|
23
|
+
3. If the request contains independent subsystems, propose decomposition and process
|
|
24
|
+
one independently valuable feature at a time.
|
|
25
|
+
|
|
26
|
+
## Clarify product behavior
|
|
27
|
+
|
|
28
|
+
1. Ask one blocking question at a time. Do not ask what repository evidence answers.
|
|
29
|
+
2. For each material product decision, present 2-3 approaches, trade-offs, and a
|
|
30
|
+
recommendation. Keep choices about observable behavior, not technical solutions.
|
|
31
|
+
3. Cover actors, rules, conceptual data, primary and alternate flows, failures,
|
|
32
|
+
recovery, states, edges, permissions, privacy, defaults, constraints, non-goals,
|
|
33
|
+
and measurable outcomes.
|
|
34
|
+
4. Label every unresolved item `blocking` or `non-blocking`. Never turn an assumption
|
|
35
|
+
into an accepted requirement.
|
|
36
|
+
|
|
37
|
+
## Requirements-completeness gate
|
|
38
|
+
|
|
39
|
+
Verify every category above before presenting the product decision. Continue while any
|
|
40
|
+
blocking item remains. Defer one only when the user approves a stated default.
|
|
41
|
+
|
|
42
|
+
## Product-decision approval gate
|
|
43
|
+
|
|
44
|
+
Present the proposed product behavior and ask the user to approve or revise it. Do not
|
|
45
|
+
scaffold or write either specification file while approval is absent or ambiguous.
|
|
46
|
+
|
|
47
|
+
## Write and self-review the specification
|
|
48
|
+
|
|
49
|
+
1. Reuse `spec/features/<feature-slug>/` when present; otherwise run
|
|
50
|
+
`scripts/create_feature.py <feature-slug> --title "Feature Title"`.
|
|
51
|
+
2. Read [references/spec-format.md](references/spec-format.md). Write both `spec.md`
|
|
52
|
+
and `acceptance.md`; add both paths to root `SPEC.md`.
|
|
53
|
+
3. Keep both files strictly product-facing. Do not include code paths, symbols,
|
|
54
|
+
frameworks, storage choices, ordered engineering steps, test files, commands,
|
|
55
|
+
deployment instructions, or code backlinks.
|
|
56
|
+
4. Run the complete self-review on both files and fix every finding.
|
|
57
|
+
|
|
58
|
+
## Written-spec approval gate
|
|
59
|
+
|
|
60
|
+
Show both paths and summarize their delta. Ask for explicit written-spec approval. If
|
|
61
|
+
revised, update both files, rerun self-review, and ask again.
|
|
62
|
+
|
|
63
|
+
## Completion and Code Agent handoff
|
|
64
|
+
|
|
65
|
+
After approval, mark both files `approved`, use `spec-evolution` to record the approved
|
|
66
|
+
product decision, and stop. `spec-drift-sync` is used later as a read-only observer;
|
|
67
|
+
it is not implementation work for this skill.
|
|
68
|
+
|
|
69
|
+
Output exactly the following, replacing `[SPEC_PATH]` with the actual `spec.md` path:
|
|
70
|
+
|
|
71
|
+
Spec complete. To implement this, give your Code Agent the following prompt: 'Read the specification at [SPEC_PATH] and generate an implementation plan.'
|
|
72
|
+
|
|
73
|
+
Do not append planning advice, implementation steps, or an offer to write code.
|
|
74
|
+
|
|
75
|
+
## Later changes
|
|
76
|
+
|
|
77
|
+
A later prompt updates the same two-file packet and preserves stable behavior IDs.
|
|
78
|
+
Return to clarification and both approval gates for every normative product change.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: SPEC-PROJECT
|
|
3
|
+
title: Project Specification Index
|
|
4
|
+
status: draft
|
|
5
|
+
version: 0.1.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Project Specification Index
|
|
9
|
+
|
|
10
|
+
## Purpose and boundaries
|
|
11
|
+
|
|
12
|
+
Describe the product, users, goals, constraints, and non-goals.
|
|
13
|
+
|
|
14
|
+
## Source-of-truth rules
|
|
15
|
+
|
|
16
|
+
Identify normative product specifications, generated traceability data, and historical
|
|
17
|
+
product-decision records without mixing their authority.
|
|
18
|
+
|
|
19
|
+
## Repository-wide behaviors
|
|
20
|
+
|
|
21
|
+
PROJECT-001: Write one testable current-state requirement.
|
|
22
|
+
|
|
23
|
+
## Feature specifications
|
|
24
|
+
|
|
25
|
+
List each accepted or active feature and link both normative product files:
|
|
26
|
+
|
|
27
|
+
- `spec/features/feature-name/spec.md`
|
|
28
|
+
- `spec/features/feature-name/acceptance.md`
|
|
29
|
+
|
|
30
|
+
## Repository-wide interfaces, state, and failures
|
|
31
|
+
|
|
32
|
+
Describe externally observable contracts, persistent state, invariants, failure
|
|
33
|
+
behavior, security, and privacy constraints.
|
|
34
|
+
|
|
35
|
+
## Open questions
|
|
36
|
+
|
|
37
|
+
List unresolved decisions. Do not represent them as accepted requirements.
|
|
38
|
+
|
|
39
|
+
## Acceptance criteria
|
|
40
|
+
|
|
41
|
+
- AC-PROJECT-001: State observable evidence for PROJECT-001.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: {{BEHAVIOR_PREFIX}}-ACCEPTANCE
|
|
3
|
+
title: {{FEATURE_TITLE}} Acceptance Specification
|
|
4
|
+
status: draft
|
|
5
|
+
related_spec: spec.md
|
|
6
|
+
created: {{DATE}}
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# {{FEATURE_TITLE}} Acceptance Specification
|
|
10
|
+
|
|
11
|
+
This file defines observable product acceptance for the rules in `spec.md`. It does
|
|
12
|
+
not select implementation files, technologies, test frameworks, or commands.
|
|
13
|
+
|
|
14
|
+
## Acceptance scope
|
|
15
|
+
|
|
16
|
+
State which product outcomes and actors this acceptance specification covers.
|
|
17
|
+
|
|
18
|
+
## Behavior acceptance
|
|
19
|
+
|
|
20
|
+
For every business-rule ID in `spec.md`, describe observable Given/When/Then outcomes.
|
|
21
|
+
Use acceptance IDs beginning with `AC-{{BEHAVIOR_PREFIX}}-001`.
|
|
22
|
+
|
|
23
|
+
## Alternative and failure acceptance
|
|
24
|
+
|
|
25
|
+
Cover alternate paths, denied actions, invalid inputs, failures, and recovery outcomes.
|
|
26
|
+
|
|
27
|
+
## Edge cases
|
|
28
|
+
|
|
29
|
+
List boundary conditions, empty states, concurrency-visible outcomes, repeated actions,
|
|
30
|
+
partial completion, and other product-significant edges.
|
|
31
|
+
|
|
32
|
+
## Exception policy
|
|
33
|
+
|
|
34
|
+
State permitted exceptions, who may authorize them, and the observable audit outcome.
|
|
35
|
+
|
|
36
|
+
## Permissions and privacy acceptance
|
|
37
|
+
|
|
38
|
+
Describe observable authorization, disclosure, retention, and privacy outcomes.
|
|
39
|
+
|
|
40
|
+
## Acceptance dependencies
|
|
41
|
+
|
|
42
|
+
List product assumptions or external business conditions required to evaluate outcomes.
|
|
43
|
+
|
|
44
|
+
## Unresolved acceptance decisions
|
|
45
|
+
|
|
46
|
+
Label each item `blocking` or `non-blocking`. Approved acceptance has no blocking item.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: {{BEHAVIOR_PREFIX}}
|
|
3
|
+
title: {{FEATURE_TITLE}}
|
|
4
|
+
status: draft
|
|
5
|
+
created: {{DATE}}
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{FEATURE_TITLE}} Specification
|
|
9
|
+
|
|
10
|
+
Companion acceptance specification: `acceptance.md`.
|
|
11
|
+
|
|
12
|
+
## Problem and desired outcome
|
|
13
|
+
|
|
14
|
+
State the user or business problem, the desired observable outcome, and why it matters.
|
|
15
|
+
|
|
16
|
+
## Users and actors
|
|
17
|
+
|
|
18
|
+
Identify each human, organization, or external system involved and its product role.
|
|
19
|
+
|
|
20
|
+
## Scope and non-goals
|
|
21
|
+
|
|
22
|
+
State included product behavior, boundaries, constraints, and explicit non-goals.
|
|
23
|
+
|
|
24
|
+
## Definitions
|
|
25
|
+
|
|
26
|
+
Define product terms that could otherwise have multiple interpretations.
|
|
27
|
+
|
|
28
|
+
## Approved product decisions
|
|
29
|
+
|
|
30
|
+
Record the chosen product behavior, alternatives considered, trade-offs, rationale,
|
|
31
|
+
and approving authority. Do not prescribe a technical solution.
|
|
32
|
+
|
|
33
|
+
## Conceptual data model
|
|
34
|
+
|
|
35
|
+
Describe business entities, relationships, ownership, lifecycle, and invariants without
|
|
36
|
+
choosing storage technology or repository structures.
|
|
37
|
+
|
|
38
|
+
## Business rules
|
|
39
|
+
|
|
40
|
+
Add current-state normative requirements beginning with `{{BEHAVIOR_PREFIX}}-001`.
|
|
41
|
+
Use observable `MUST`, `SHOULD`, or `MAY` language and keep IDs stable.
|
|
42
|
+
|
|
43
|
+
## Preconditions and postconditions
|
|
44
|
+
|
|
45
|
+
Define what must be true before each material action and what becomes true afterward.
|
|
46
|
+
|
|
47
|
+
## Primary user flows
|
|
48
|
+
|
|
49
|
+
Describe successful actor journeys in business terms.
|
|
50
|
+
|
|
51
|
+
## Alternative and failure flows
|
|
52
|
+
|
|
53
|
+
Describe alternate paths, rejected actions, failures, and recovery behavior.
|
|
54
|
+
|
|
55
|
+
## States and transitions
|
|
56
|
+
|
|
57
|
+
Define product-visible states, allowed transitions, terminal states, and invariants.
|
|
58
|
+
|
|
59
|
+
## Permissions, privacy, and compliance
|
|
60
|
+
|
|
61
|
+
Define who may do what, protected information, retention or disclosure rules, and
|
|
62
|
+
applicable compliance constraints. State when a category is not applicable.
|
|
63
|
+
|
|
64
|
+
## Constraints and defaults
|
|
65
|
+
|
|
66
|
+
Record product constraints, default behavior, compatibility expectations, and non-goals.
|
|
67
|
+
|
|
68
|
+
## Assumptions and unresolved decisions
|
|
69
|
+
|
|
70
|
+
Label each item `blocking` or `non-blocking`. An approved specification contains no
|
|
71
|
+
blocking item; deferred non-blocking items include an accepted default.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Specification Format
|
|
2
|
+
|
|
3
|
+
Root `SPEC.md` is the product-wide contract and feature index. Each material feature
|
|
4
|
+
lives in `spec/features/<feature-slug>/` and contains exactly two product artifacts:
|
|
5
|
+
|
|
6
|
+
- `spec.md`: normative product rules, actors, conceptual data, flows, states,
|
|
7
|
+
permissions, constraints, and approved decisions;
|
|
8
|
+
- `acceptance.md`: observable acceptance scenarios, failure outcomes, edge cases,
|
|
9
|
+
and exception policy mapped to stable behavior IDs.
|
|
10
|
+
|
|
11
|
+
Both files describe **what must be true**, never how software will be built. They must
|
|
12
|
+
not contain repository paths, code symbols, libraries, frameworks, storage choices,
|
|
13
|
+
implementation sequences, test filenames, commands, deployment instructions, or code
|
|
14
|
+
backlinks. Root `SPEC.md` links both files. Derived code linkage belongs only in
|
|
15
|
+
`spec/traceability.json`, which is not normative.
|
|
16
|
+
|
|
17
|
+
## Lifecycle
|
|
18
|
+
|
|
19
|
+
- `draft`: product behavior or acceptance remains incomplete or unapproved;
|
|
20
|
+
- `product-approved`: the presented product decisions are approved and may be written;
|
|
21
|
+
- `approved`: both self-reviewed files received explicit written-spec approval;
|
|
22
|
+
- `superseded`: a later approved contract replaced this feature specification.
|
|
23
|
+
|
|
24
|
+
Implementation and verification status never changes the meaning or status of an
|
|
25
|
+
approved product specification. Keep behavior IDs stable across revisions. Record
|
|
26
|
+
approved transitions in evolution history; keep current truth in the two spec files.
|
|
27
|
+
|
|
28
|
+
Use `scripts/create_feature.py <feature-slug> --title "Feature Title"` to create the
|
|
29
|
+
two-file packet without overwriting existing work. Replace every instruction with
|
|
30
|
+
feature-specific content before requesting approval.
|
|
31
|
+
|
|
32
|
+
## Requirements-completeness checklist
|
|
33
|
+
|
|
34
|
+
Before writing, evaluate actors, business rules, conceptual entities and invariants,
|
|
35
|
+
primary and alternate flows, failure and recovery behavior, states, edge cases,
|
|
36
|
+
permissions, privacy, defaults, constraints, non-goals, and measurable outcomes.
|
|
37
|
+
Label every unresolved item `blocking` or `non-blocking`; approved specs have no
|
|
38
|
+
blocking items.
|
|
39
|
+
|
|
40
|
+
## Spec self-review
|
|
41
|
+
|
|
42
|
+
Run every check on both files before requesting written-spec approval.
|
|
43
|
+
|
|
44
|
+
1. **Placeholder scan:** Remove templates, empty required sections, `TBD`, `TODO`, and
|
|
45
|
+
vague promises.
|
|
46
|
+
2. **Internal consistency:** Ensure terms, rules, data, flows, states, and outcomes do
|
|
47
|
+
not contradict one another.
|
|
48
|
+
3. **Scope:** Keep one independently valuable feature per packet.
|
|
49
|
+
4. **Ambiguity:** Define actors, terms, authority, defaults, boundaries, and outcomes.
|
|
50
|
+
5. **Testability:** Make every rule observable without prescribing engineering tests.
|
|
51
|
+
6. **Failure, security, and privacy:** State failure, recovery, authorization, data
|
|
52
|
+
handling, and non-disclosure outcomes, or explicitly mark them not applicable.
|
|
53
|
+
7. **Unique ownership:** Give each normative rule one stable behavior ID and one owner.
|
|
54
|
+
8. **Acceptance coverage:** Map every behavior ID to at least one acceptance scenario;
|
|
55
|
+
ensure every scenario names the behavior it covers.
|
|
56
|
+
9. **Implementation-content scan:** Remove paths, code names, technology choices,
|
|
57
|
+
ordered build steps, commands, and backlinks.
|
|
58
|
+
|
|
59
|
+
After approval, revise product meaning only through the complete clarification and
|
|
60
|
+
approval workflow. Git owns line diffs; evolution history owns decision sequence.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Create a non-overwriting two-file feature specification packet."""
|
|
3
|
+
|
|
4
|
+
# spec: FSP-001, FSP-002, FSP-003
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import re
|
|
10
|
+
from datetime import UTC, datetime
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
SLUG = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
15
|
+
TEMPLATES = {
|
|
16
|
+
"spec.md": "feature-spec.template.md",
|
|
17
|
+
"acceptance.md": "feature-acceptance.template.md",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_packet(repo: Path, slug: str, title: str | None = None) -> Path:
|
|
22
|
+
if not SLUG.fullmatch(slug):
|
|
23
|
+
raise ValueError("feature slug must use lowercase kebab-case")
|
|
24
|
+
|
|
25
|
+
feature_title = title or slug.replace("-", " ").title()
|
|
26
|
+
behavior_prefix = slug.upper()
|
|
27
|
+
created = datetime.now(UTC).date().isoformat()
|
|
28
|
+
assets = Path(__file__).resolve().parent.parent / "assets"
|
|
29
|
+
replacements = {
|
|
30
|
+
"{{FEATURE_TITLE}}": feature_title,
|
|
31
|
+
"{{BEHAVIOR_PREFIX}}": behavior_prefix,
|
|
32
|
+
"{{DATE}}": created,
|
|
33
|
+
}
|
|
34
|
+
rendered: dict[str, str] = {}
|
|
35
|
+
for output_name, template_name in TEMPLATES.items():
|
|
36
|
+
text = (assets / template_name).read_text(encoding="utf-8")
|
|
37
|
+
for token, value in replacements.items():
|
|
38
|
+
text = text.replace(token, value)
|
|
39
|
+
rendered[output_name] = text
|
|
40
|
+
|
|
41
|
+
target = repo.resolve() / "spec/features" / slug
|
|
42
|
+
target.mkdir(parents=True, exist_ok=False)
|
|
43
|
+
for output_name, text in rendered.items():
|
|
44
|
+
(target / output_name).write_text(text, encoding="utf-8")
|
|
45
|
+
return target
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def main(argv: list[str] | None = None) -> int:
|
|
49
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
50
|
+
parser.add_argument("slug")
|
|
51
|
+
parser.add_argument("--title")
|
|
52
|
+
parser.add_argument("--repo", type=Path, default=Path("."))
|
|
53
|
+
args = parser.parse_args(argv)
|
|
54
|
+
try:
|
|
55
|
+
target = create_packet(args.repo, args.slug, args.title)
|
|
56
|
+
except (FileExistsError, FileNotFoundError, OSError, ValueError) as error:
|
|
57
|
+
parser.error(str(error))
|
|
58
|
+
print(target)
|
|
59
|
+
return 0
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: spec-agent-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A strict Spec Agent workflow for spec-driven development
|
|
5
|
+
Author: Spec Agent contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: agent-skills,ai-agent,cli,spec,spec-driven-development
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: typer>=0.12
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Spec Agent CLI
|
|
25
|
+
|
|
26
|
+
Spec Agent turns unclear feature requests into approved, product-only specifications
|
|
27
|
+
before any implementation planning begins. It installs three Agent Skills under the
|
|
28
|
+
open `.agents/skills` convention and keeps specification evolution and code drift
|
|
29
|
+
observation available without mixing them into implementation work.
|
|
30
|
+
|
|
31
|
+
## Install
|
|
32
|
+
|
|
33
|
+
Python 3.11 or newer is required.
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
uv tool install spec-agent-cli
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Alternatives:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
pipx install spec-agent-cli
|
|
43
|
+
pip install spec-agent-cli
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Initialize a repository
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
cd /path/to/your-project
|
|
50
|
+
spec-agent init
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This creates only missing project files and installs:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
.agents/skills/spec-request-flow/
|
|
57
|
+
.agents/skills/spec-drift-sync/
|
|
58
|
+
.agents/skills/spec-evolution/
|
|
59
|
+
AGENTS.md # managed rules block; user content is preserved
|
|
60
|
+
SPEC.md # created only when absent
|
|
61
|
+
spec/features/
|
|
62
|
+
spec/evolution/events.jsonl
|
|
63
|
+
spec/evolution/timeline.md
|
|
64
|
+
spec/traceability.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Use `spec-agent init --check` for a read-only status check. Use
|
|
68
|
+
`spec-agent init --force` to refresh locally modified managed skill files after a CLI
|
|
69
|
+
upgrade. `--force` never replaces project specifications or history.
|
|
70
|
+
|
|
71
|
+
## Start a feature
|
|
72
|
+
|
|
73
|
+
Ask a compatible agent:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
Use spec-request-flow to define feature X. Do not implement it.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The Spec Agent repeatedly clarifies blocking product questions, compares meaningful
|
|
80
|
+
product approaches, writes `spec.md` and `acceptance.md`, asks for approval, records
|
|
81
|
+
the approved product evolution, and then gives you the prompt for a separate Code
|
|
82
|
+
Agent.
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
spec_agent/__init__.py,sha256=PBkRLfmwQJkk4ZmVw1C3HyD88cq_RogMOQkDAW3SLis,195
|
|
2
|
+
spec_agent/__main__.py,sha256=ePZoyjzMWVBd_04PZCxBhYD7wzEOEAYrqg9_a07Xgd0,130
|
|
3
|
+
spec_agent/cli.py,sha256=msq9XQhceHeaEuCfpZ4AOXGEU4rfaW4l_uZ9lNBZr8s,1748
|
|
4
|
+
spec_agent/installer.py,sha256=L-0hSOmw6-IiYpRlK0qX_E_Urw9Xp5ji-aVWzrpbRLM,6300
|
|
5
|
+
spec_agent/package_assets.py,sha256=I4VwlpmgBIB8VFsWoPKdcL_U327ScG1oJ-SPW7PmAsE,1613
|
|
6
|
+
spec_agent/commands/__init__.py,sha256=zXGX0Y-dHjhJ31h43oz3PIKz0QwkCldqv4yIfT2hgdY,42
|
|
7
|
+
spec_agent/commands/init.py,sha256=X3HW8eyrzISOdsxdERC8L7XIqSQjcJ9ZcL1GaLgeAJY,941
|
|
8
|
+
spec_agent/resources/project-rules.md,sha256=BFoo1zaB_IrXohtqNc4WDrUGwaQBVS6PodRzG3BYbPY,852
|
|
9
|
+
spec_agent/resources/skills/spec-drift-sync/SKILL.md,sha256=jcMf6lUV0pb9Va0LeD4lVdwZmIqE68-b83n_Xg09k20,2758
|
|
10
|
+
spec_agent/resources/skills/spec-drift-sync/scripts/check.py,sha256=sFXZ-KF12l9ZsSsiKbliV48owOzby2IJE9AimAYIEUo,12806
|
|
11
|
+
spec_agent/resources/skills/spec-evolution/SKILL.md,sha256=2Gu4b9VFr_JFCVaOg7hP2_2vrRcBKqbnB4Xq33GHacM,2758
|
|
12
|
+
spec_agent/resources/skills/spec-evolution/scripts/record.py,sha256=18OK-4DuEgzxFd5Z8IOOASY3P7AxPRXwcZN3PHgRAPs,4688
|
|
13
|
+
spec_agent/resources/skills/spec-evolution/scripts/timeline.py,sha256=NDmJsq2IobNp1tPEUM8Lwp9248NT7BfxYGwchUxxVwU,4550
|
|
14
|
+
spec_agent/resources/skills/spec-request-flow/SKILL.md,sha256=KJNHmRGBY6UKVpfoI5E6uJ_W0GCvnaaxRehaJP1iA6c,3664
|
|
15
|
+
spec_agent/resources/skills/spec-request-flow/assets/SPEC.template.md,sha256=gp810LXihTuxf9G5xfptdj3pfP23_-Kzq2fNkwju9fU,1021
|
|
16
|
+
spec_agent/resources/skills/spec-request-flow/assets/feature-acceptance.template.md,sha256=WI13jyXSEE_sy_4lh_X1ophro1mPJ9G6b7mcRx7zpOM,1390
|
|
17
|
+
spec_agent/resources/skills/spec-request-flow/assets/feature-spec.template.md,sha256=U1DzVyvbfSifwHT9bRehe-rEBH9lPEWp5tkedzylCYs,2071
|
|
18
|
+
spec_agent/resources/skills/spec-request-flow/references/spec-format.md,sha256=yxUgeGUHmcJtpLsnE8TWiJUTZY8aPlCUjCY9wKgq2hY,3290
|
|
19
|
+
spec_agent/resources/skills/spec-request-flow/scripts/create_feature.py,sha256=dcTnIPiADOAMFsU0tmLBBIQerRe_zk3VrvFbLhNTzTM,2029
|
|
20
|
+
spec_agent_cli-0.1.0.dist-info/METADATA,sha256=mhHpbejCL2N2wENKACxh2vWkZVJ32vVTsnorNrYFg3Q,2470
|
|
21
|
+
spec_agent_cli-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
22
|
+
spec_agent_cli-0.1.0.dist-info/entry_points.txt,sha256=JdoIoefBGxdPylEMpX-KP1SS1gFWN9qHCZ75BH0mlco,51
|
|
23
|
+
spec_agent_cli-0.1.0.dist-info/licenses/LICENSE,sha256=5AbBR_7xP68pz9dd83GJT3lopItANjZe5cGHfAQNRRM,1080
|
|
24
|
+
spec_agent_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Spec Agent contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|