tmux-agent 0.1.0
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.
- package/.codex/skills/speckit/SKILL.md +173 -0
- package/.codex/skills/speckit/assets/templates/checklist-template.md +49 -0
- package/.codex/skills/speckit/assets/templates/notes-entrypoints-template.md +11 -0
- package/.codex/skills/speckit/assets/templates/notes-questions-template.md +7 -0
- package/.codex/skills/speckit/assets/templates/notes-readme-template.md +36 -0
- package/.codex/skills/speckit/assets/templates/notes-session-template.md +21 -0
- package/.codex/skills/speckit/assets/templates/plan-template.md +126 -0
- package/.codex/skills/speckit/assets/templates/spec-template.md +135 -0
- package/.codex/skills/speckit/assets/templates/tasks-template.md +269 -0
- package/.codex/skills/speckit/references/acceptance.md +183 -0
- package/.codex/skills/speckit/references/analyze.md +186 -0
- package/.codex/skills/speckit/references/checklist.md +302 -0
- package/.codex/skills/speckit/references/clarify-auto.md +69 -0
- package/.codex/skills/speckit/references/clarify-detailed.md +78 -0
- package/.codex/skills/speckit/references/clarify.md +189 -0
- package/.codex/skills/speckit/references/constitution.md +90 -0
- package/.codex/skills/speckit/references/group.md +89 -0
- package/.codex/skills/speckit/references/implement-task.md +115 -0
- package/.codex/skills/speckit/references/implement.md +129 -0
- package/.codex/skills/speckit/references/notes.md +82 -0
- package/.codex/skills/speckit/references/plan-deep.md +87 -0
- package/.codex/skills/speckit/references/plan-from-questions.md +115 -0
- package/.codex/skills/speckit/references/plan-from-review.md +89 -0
- package/.codex/skills/speckit/references/plan.md +97 -0
- package/.codex/skills/speckit/references/review-plan.md +156 -0
- package/.codex/skills/speckit/references/specify.md +246 -0
- package/.codex/skills/speckit/references/tasks.md +155 -0
- package/.codex/skills/speckit/references/taskstoissues.md +33 -0
- package/.codex/skills/speckit/scripts/bash/check-prerequisites.sh +206 -0
- package/.codex/skills/speckit/scripts/bash/common.sh +191 -0
- package/.codex/skills/speckit/scripts/bash/create-new-feature.sh +259 -0
- package/.codex/skills/speckit/scripts/bash/extract-coded-points.sh +322 -0
- package/.codex/skills/speckit/scripts/bash/extract-spec-ids.sh +238 -0
- package/.codex/skills/speckit/scripts/bash/extract-tasks.sh +295 -0
- package/.codex/skills/speckit/scripts/bash/extract-user-stories.sh +312 -0
- package/.codex/skills/speckit/scripts/bash/setup-notes.sh +182 -0
- package/.codex/skills/speckit/scripts/bash/setup-plan.sh +110 -0
- package/.codex/skills/speckit/scripts/bash/show-todo-tasks.sh +257 -0
- package/.codex/skills/speckit/scripts/bash/spec-group-checklist.sh +402 -0
- package/.codex/skills/speckit/scripts/bash/spec-group-members.sh +215 -0
- package/.codex/skills/speckit/scripts/bash/spec-registry-graph.sh +399 -0
- package/.specify/memory/constitution.md +67 -0
- package/.specify/templates/agent-file-template.md +28 -0
- package/.specify/templates/checklist-template.md +49 -0
- package/.specify/templates/plan-template.md +126 -0
- package/.specify/templates/spec-template.md +135 -0
- package/.specify/templates/tasks-template.md +269 -0
- package/README.md +128 -0
- package/README.zh-CN.md +127 -0
- package/bun.lock +269 -0
- package/dist/cli/commands/codex/forkHome.js +88 -0
- package/dist/cli/commands/codex/send.js +55 -0
- package/dist/cli/commands/codex/sessionInfo.js +42 -0
- package/dist/cli/commands/codex/spawn.js +68 -0
- package/dist/cli/commands/find.js +26 -0
- package/dist/cli/commands/paneKill.js +33 -0
- package/dist/cli/commands/paneSpawn.js +40 -0
- package/dist/cli/commands/paneTitle.js +33 -0
- package/dist/cli/commands/read.js +34 -0
- package/dist/cli/commands/send.js +51 -0
- package/dist/cli/commands/snapshot.js +19 -0
- package/dist/cli/commands/ui/select.js +41 -0
- package/dist/cli/commands/windowKill.js +25 -0
- package/dist/cli/commands/windowLs.js +15 -0
- package/dist/cli/commands/windowNew.js +28 -0
- package/dist/cli/commands/windowRename.js +25 -0
- package/dist/cli/index.js +365 -0
- package/dist/cli/parse.js +39 -0
- package/dist/lib/codex/forkHome.js +101 -0
- package/dist/lib/codex/isCodexPane.js +55 -0
- package/dist/lib/codex/send.js +58 -0
- package/dist/lib/codex/sessionInfo.js +449 -0
- package/dist/lib/codex/spawn.js +246 -0
- package/dist/lib/contracts/types.js +2 -0
- package/dist/lib/fs/safeRm.js +32 -0
- package/dist/lib/io/readStdin.js +14 -0
- package/dist/lib/os/process.js +55 -0
- package/dist/lib/output/format.js +95 -0
- package/dist/lib/proc/lsof.js +42 -0
- package/dist/lib/proc/ps.js +60 -0
- package/dist/lib/targeting/errors.js +13 -0
- package/dist/lib/targeting/resolvePaneTarget.js +91 -0
- package/dist/lib/targeting/resolveWindowTarget.js +40 -0
- package/dist/lib/targeting/scope.js +58 -0
- package/dist/lib/tmux/capturePane.js +20 -0
- package/dist/lib/tmux/exec.js +66 -0
- package/dist/lib/tmux/paneOps.js +29 -0
- package/dist/lib/tmux/paste.js +23 -0
- package/dist/lib/tmux/sendKeys.js +47 -0
- package/dist/lib/tmux/session.js +29 -0
- package/dist/lib/tmux/snapshotPanes.js +46 -0
- package/dist/lib/tmux/snapshotWindows.js +24 -0
- package/dist/lib/tmux/windowOps.js +32 -0
- package/dist/lib/ui/popupSelect.js +432 -0
- package/dist/lib/ui/popupSupport.js +76 -0
- package/package.json +23 -0
- package/src/cli/commands/codex/forkHome.ts +141 -0
- package/src/cli/commands/codex/send.ts +83 -0
- package/src/cli/commands/codex/sessionInfo.ts +59 -0
- package/src/cli/commands/codex/spawn.ts +90 -0
- package/src/cli/commands/find.ts +40 -0
- package/src/cli/commands/paneKill.ts +49 -0
- package/src/cli/commands/paneSpawn.ts +53 -0
- package/src/cli/commands/paneTitle.ts +50 -0
- package/src/cli/commands/read.ts +48 -0
- package/src/cli/commands/send.ts +71 -0
- package/src/cli/commands/snapshot.ts +28 -0
- package/src/cli/commands/ui/select.ts +49 -0
- package/src/cli/commands/windowKill.ts +35 -0
- package/src/cli/commands/windowLs.ts +20 -0
- package/src/cli/commands/windowNew.ts +40 -0
- package/src/cli/commands/windowRename.ts +36 -0
- package/src/cli/index.ts +430 -0
- package/src/lib/codex/forkHome.ts +148 -0
- package/src/lib/codex/isCodexPane.ts +56 -0
- package/src/lib/codex/send.ts +84 -0
- package/src/lib/codex/sessionInfo.ts +521 -0
- package/src/lib/codex/spawn.ts +305 -0
- package/src/lib/contracts/types.ts +30 -0
- package/src/lib/fs/safeRm.ts +32 -0
- package/src/lib/io/readStdin.ts +11 -0
- package/src/lib/output/format.ts +105 -0
- package/src/lib/proc/lsof.ts +44 -0
- package/src/lib/proc/ps.ts +70 -0
- package/src/lib/targeting/errors.ts +25 -0
- package/src/lib/targeting/resolvePaneTarget.ts +106 -0
- package/src/lib/targeting/resolveWindowTarget.ts +45 -0
- package/src/lib/targeting/scope.ts +76 -0
- package/src/lib/tmux/capturePane.ts +21 -0
- package/src/lib/tmux/exec.ts +90 -0
- package/src/lib/tmux/paneOps.ts +35 -0
- package/src/lib/tmux/paste.ts +20 -0
- package/src/lib/tmux/sendKeys.ts +72 -0
- package/src/lib/tmux/session.ts +27 -0
- package/src/lib/tmux/snapshotPanes.ts +52 -0
- package/src/lib/tmux/snapshotWindows.ts +23 -0
- package/src/lib/tmux/windowOps.ts +43 -0
- package/src/lib/ui/popupSelect.ts +561 -0
- package/src/lib/ui/popupSupport.ts +84 -0
- package/tests/e2e/codexForkHome.test.ts +146 -0
- package/tests/e2e/codexSessionInfo.test.ts +112 -0
- package/tests/e2e/codexTuiSend.test.ts +68 -0
- package/tests/integration/codexSpawn.test.ts +113 -0
- package/tests/integration/paneOps.test.ts +60 -0
- package/tests/integration/sendRead.test.ts +52 -0
- package/tests/integration/snapshot.test.ts +39 -0
- package/tests/integration/tmuxHarness.ts +39 -0
- package/tests/integration/windowOps.test.ts +60 -0
- package/tests/unit/codexSend.test.ts +105 -0
- package/tests/unit/codexSessionInfo.test.ts +88 -0
- package/tests/unit/codexSpawn.test.ts +34 -0
- package/tests/unit/keys.test.ts +30 -0
- package/tests/unit/outputFormat.test.ts +52 -0
- package/tests/unit/popupSelect.test.ts +77 -0
- package/tests/unit/popupSupport.test.ts +109 -0
- package/tests/unit/resolvePaneTarget.test.ts +43 -0
- package/tests/unit/resolveWindowTarget.test.ts +36 -0
- package/tests/unit/safeRm.test.ts +41 -0
- package/tests/unit/scope.test.ts +57 -0
- package/tsconfig.json +14 -0
- package/vitest.config.ts +16 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Render a dependency graph from one or more spec-registry.json files.
|
|
4
|
+
#
|
|
5
|
+
# A "Spec Group" is any spec directory that contains `spec-registry.json`.
|
|
6
|
+
# You can pass group ids (e.g. 046) or registry file paths.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# spec-registry-graph.sh <group|registry.json>... [--all] [--focus <id>] [--direction upstream|downstream|both] [--format mermaid|text] [--json]
|
|
10
|
+
#
|
|
11
|
+
# Examples:
|
|
12
|
+
# spec-registry-graph.sh 046
|
|
13
|
+
# spec-registry-graph.sh 046 --focus 050
|
|
14
|
+
# spec-registry-graph.sh --all --focus 045 --direction downstream
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
JSON_MODE=false
|
|
19
|
+
ALL=false
|
|
20
|
+
FOCUS=""
|
|
21
|
+
DIRECTION="both"
|
|
22
|
+
FORMAT="mermaid"
|
|
23
|
+
SOURCES=()
|
|
24
|
+
|
|
25
|
+
usage() {
|
|
26
|
+
cat << 'EOF'
|
|
27
|
+
Usage:
|
|
28
|
+
spec-registry-graph.sh <group|registry.json>... [--all] [--focus <id>] [--direction upstream|downstream|both] [--format mermaid|text] [--json]
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
<group|registry.json> Group spec id (e.g., 046 or 046-core-ng-roadmap) OR a direct path to spec-registry.json
|
|
32
|
+
|
|
33
|
+
Options:
|
|
34
|
+
--all Scan all `specs/*/spec-registry.json` and merge as one graph
|
|
35
|
+
--focus <id> Only render the dependency closure around this spec id (e.g. 045 / 045-xxx / specs/045-*/...)
|
|
36
|
+
--direction <dir> Closure direction for --focus: upstream | downstream | both (default: both)
|
|
37
|
+
--format <fmt> Output format: mermaid | text (default: mermaid). Ignored in --json mode.
|
|
38
|
+
--json Output machine JSON: nodes/edges/conflicts
|
|
39
|
+
--help, -h Show help
|
|
40
|
+
|
|
41
|
+
Notes:
|
|
42
|
+
- Edge direction: `A dependsOn B` renders as `B --> A` (B must be done before A).
|
|
43
|
+
- Nodes referenced in dependsOn but not defined in any loaded registry are shown as "external".
|
|
44
|
+
EOF
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
while [[ $# -gt 0 ]]; do
|
|
48
|
+
case "$1" in
|
|
49
|
+
--json)
|
|
50
|
+
JSON_MODE=true
|
|
51
|
+
shift
|
|
52
|
+
;;
|
|
53
|
+
--all)
|
|
54
|
+
ALL=true
|
|
55
|
+
shift
|
|
56
|
+
;;
|
|
57
|
+
--focus)
|
|
58
|
+
if [[ $# -lt 2 || "${2:-}" == --* ]]; then
|
|
59
|
+
echo "ERROR: --focus requires a value." >&2
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
FOCUS="$2"
|
|
63
|
+
shift 2
|
|
64
|
+
;;
|
|
65
|
+
--direction)
|
|
66
|
+
if [[ $# -lt 2 || "${2:-}" == --* ]]; then
|
|
67
|
+
echo "ERROR: --direction requires a value." >&2
|
|
68
|
+
exit 1
|
|
69
|
+
fi
|
|
70
|
+
DIRECTION="$2"
|
|
71
|
+
shift 2
|
|
72
|
+
;;
|
|
73
|
+
--format)
|
|
74
|
+
if [[ $# -lt 2 || "${2:-}" == --* ]]; then
|
|
75
|
+
echo "ERROR: --format requires a value." >&2
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
FORMAT="$2"
|
|
79
|
+
shift 2
|
|
80
|
+
;;
|
|
81
|
+
--help|-h)
|
|
82
|
+
usage
|
|
83
|
+
exit 0
|
|
84
|
+
;;
|
|
85
|
+
--*)
|
|
86
|
+
echo "ERROR: Unknown option '$1'. Use --help for usage information." >&2
|
|
87
|
+
exit 1
|
|
88
|
+
;;
|
|
89
|
+
*)
|
|
90
|
+
SOURCES+=("$1")
|
|
91
|
+
shift
|
|
92
|
+
;;
|
|
93
|
+
esac
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
if [[ "$ALL" != true && ${#SOURCES[@]} -eq 0 ]]; then
|
|
97
|
+
echo "ERROR: Provide at least one <group|registry.json>, or use --all." >&2
|
|
98
|
+
usage >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
case "$DIRECTION" in
|
|
103
|
+
upstream|downstream|both) ;;
|
|
104
|
+
*)
|
|
105
|
+
echo "ERROR: Invalid --direction: $DIRECTION (use upstream|downstream|both)" >&2
|
|
106
|
+
exit 1
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
|
|
110
|
+
case "$FORMAT" in
|
|
111
|
+
mermaid|text) ;;
|
|
112
|
+
*)
|
|
113
|
+
echo "ERROR: Invalid --format: $FORMAT (use mermaid|text)" >&2
|
|
114
|
+
exit 1
|
|
115
|
+
;;
|
|
116
|
+
esac
|
|
117
|
+
|
|
118
|
+
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
119
|
+
CHECK="$SCRIPT_DIR/check-prerequisites.sh"
|
|
120
|
+
source "$SCRIPT_DIR/common.sh"
|
|
121
|
+
|
|
122
|
+
repo_root="$(get_repo_root)"
|
|
123
|
+
|
|
124
|
+
REGISTRIES=()
|
|
125
|
+
|
|
126
|
+
if [[ "$ALL" == true ]]; then
|
|
127
|
+
shopt -s nullglob
|
|
128
|
+
for f in "$repo_root/specs"/*/spec-registry.json; do
|
|
129
|
+
REGISTRIES+=("$f")
|
|
130
|
+
done
|
|
131
|
+
shopt -u nullglob
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
for s in "${SOURCES[@]}"; do
|
|
135
|
+
if [[ -f "$s" && "$s" == *.json ]]; then
|
|
136
|
+
REGISTRIES+=("$s")
|
|
137
|
+
continue
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
paths_json="$("$CHECK" --json --paths-only --feature "$s")"
|
|
141
|
+
feature_dir="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["FEATURE_DIR"])' <<<"$paths_json")"
|
|
142
|
+
registry_json="$feature_dir/spec-registry.json"
|
|
143
|
+
if [[ ! -f "$registry_json" ]]; then
|
|
144
|
+
echo "ERROR: spec-registry.json not found for group '$s' at $registry_json" >&2
|
|
145
|
+
echo "Hint: add a machine-readable registry file (SSoT) under the group feature directory." >&2
|
|
146
|
+
exit 1
|
|
147
|
+
fi
|
|
148
|
+
REGISTRIES+=("$registry_json")
|
|
149
|
+
done
|
|
150
|
+
|
|
151
|
+
if [[ ${#REGISTRIES[@]} -eq 0 ]]; then
|
|
152
|
+
echo "ERROR: No registry files found." >&2
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
python3 - "$repo_root" "$JSON_MODE" "$FORMAT" "$FOCUS" "$DIRECTION" "${REGISTRIES[@]}" << 'PY'
|
|
157
|
+
from __future__ import annotations
|
|
158
|
+
|
|
159
|
+
import json
|
|
160
|
+
import re
|
|
161
|
+
import sys
|
|
162
|
+
from dataclasses import dataclass, field
|
|
163
|
+
from pathlib import Path
|
|
164
|
+
from typing import Any, Literal
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
Format = Literal["mermaid", "text"]
|
|
168
|
+
Direction = Literal["upstream", "downstream", "both"]
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
repo_root = Path(sys.argv[1])
|
|
172
|
+
json_mode = sys.argv[2].lower() == "true"
|
|
173
|
+
fmt: Format = sys.argv[3] # type: ignore[assignment]
|
|
174
|
+
focus_raw = sys.argv[4].strip()
|
|
175
|
+
direction: Direction = sys.argv[5] # type: ignore[assignment]
|
|
176
|
+
registry_files = [Path(p) for p in sys.argv[6:]]
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def normalize_id(raw: str) -> str:
|
|
180
|
+
raw = raw.strip()
|
|
181
|
+
if raw.startswith("specs/"):
|
|
182
|
+
raw = raw[len("specs/") :]
|
|
183
|
+
raw = raw.strip().strip("/")
|
|
184
|
+
|
|
185
|
+
m = re.match(r"^(?P<prefix>\d{3})", raw)
|
|
186
|
+
return m.group("prefix") if m else raw
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@dataclass
|
|
190
|
+
class Node:
|
|
191
|
+
id: str
|
|
192
|
+
defined: bool = False
|
|
193
|
+
dir: str | None = None
|
|
194
|
+
status: str | None = None
|
|
195
|
+
groups: set[str] = field(default_factory=set)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@dataclass(frozen=True)
|
|
199
|
+
class Edge:
|
|
200
|
+
dep: str
|
|
201
|
+
target: str
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
nodes: dict[str, Node] = {}
|
|
205
|
+
edges: set[Edge] = set()
|
|
206
|
+
conflicts: list[dict[str, Any]] = []
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def get_node(node_id: str) -> Node:
|
|
210
|
+
if node_id not in nodes:
|
|
211
|
+
nodes[node_id] = Node(id=node_id)
|
|
212
|
+
return nodes[node_id]
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def load_registry(path: Path) -> dict[str, Any]:
|
|
216
|
+
data = json.loads(path.read_text(encoding="utf-8", errors="replace"))
|
|
217
|
+
if not isinstance(data, dict):
|
|
218
|
+
raise ValueError(f"registry must be a JSON object: {path}")
|
|
219
|
+
return data
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
for registry_file in registry_files:
|
|
223
|
+
data = load_registry(registry_file)
|
|
224
|
+
group = str(data.get("group") or registry_file.parent.name)
|
|
225
|
+
entries = data.get("entries", [])
|
|
226
|
+
if not isinstance(entries, list):
|
|
227
|
+
raise ValueError(f"entries must be a list: {registry_file}")
|
|
228
|
+
|
|
229
|
+
for entry in entries:
|
|
230
|
+
if isinstance(entry, str):
|
|
231
|
+
entry_id = normalize_id(entry)
|
|
232
|
+
n = get_node(entry_id)
|
|
233
|
+
n.groups.add(group)
|
|
234
|
+
continue
|
|
235
|
+
|
|
236
|
+
if not isinstance(entry, dict):
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
entry_id = normalize_id(str(entry.get("id") or entry.get("dir") or ""))
|
|
240
|
+
if not entry_id:
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
entry_dir = entry.get("dir")
|
|
244
|
+
entry_status = entry.get("status")
|
|
245
|
+
entry_deps = entry.get("dependsOn", [])
|
|
246
|
+
|
|
247
|
+
if entry_deps is None:
|
|
248
|
+
entry_deps = []
|
|
249
|
+
if not isinstance(entry_deps, list):
|
|
250
|
+
entry_deps = []
|
|
251
|
+
|
|
252
|
+
n = get_node(entry_id)
|
|
253
|
+
n.groups.add(group)
|
|
254
|
+
|
|
255
|
+
if n.defined and (n.dir != entry_dir or n.status != entry_status):
|
|
256
|
+
conflicts.append(
|
|
257
|
+
{
|
|
258
|
+
"id": entry_id,
|
|
259
|
+
"existing": {"dir": n.dir, "status": n.status, "groups": sorted(n.groups)},
|
|
260
|
+
"incoming": {"dir": entry_dir, "status": entry_status, "group": group, "file": str(registry_file)},
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
if not n.defined:
|
|
265
|
+
n.defined = True
|
|
266
|
+
n.dir = str(entry_dir) if isinstance(entry_dir, str) else None
|
|
267
|
+
n.status = str(entry_status) if isinstance(entry_status, str) else None
|
|
268
|
+
|
|
269
|
+
for dep in entry_deps:
|
|
270
|
+
if dep is None:
|
|
271
|
+
continue
|
|
272
|
+
dep_id = normalize_id(str(dep))
|
|
273
|
+
if not dep_id:
|
|
274
|
+
continue
|
|
275
|
+
get_node(dep_id)
|
|
276
|
+
edges.add(Edge(dep=dep_id, target=entry_id))
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def build_adj(edges_set: set[Edge]) -> tuple[dict[str, set[str]], dict[str, set[str]]]:
|
|
280
|
+
upstream: dict[str, set[str]] = {}
|
|
281
|
+
downstream: dict[str, set[str]] = {}
|
|
282
|
+
for e in edges_set:
|
|
283
|
+
upstream.setdefault(e.target, set()).add(e.dep)
|
|
284
|
+
downstream.setdefault(e.dep, set()).add(e.target)
|
|
285
|
+
return upstream, downstream
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
up, down = build_adj(edges)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def closure(start: str, *, direction: Direction) -> set[str]:
|
|
292
|
+
seen: set[str] = set()
|
|
293
|
+
queue: list[str] = [start]
|
|
294
|
+
while queue:
|
|
295
|
+
cur = queue.pop(0)
|
|
296
|
+
if cur in seen:
|
|
297
|
+
continue
|
|
298
|
+
seen.add(cur)
|
|
299
|
+
if direction in ("upstream", "both"):
|
|
300
|
+
for nxt in sorted(up.get(cur, set())):
|
|
301
|
+
if nxt not in seen:
|
|
302
|
+
queue.append(nxt)
|
|
303
|
+
if direction in ("downstream", "both"):
|
|
304
|
+
for nxt in sorted(down.get(cur, set())):
|
|
305
|
+
if nxt not in seen:
|
|
306
|
+
queue.append(nxt)
|
|
307
|
+
return seen
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
focus_id = normalize_id(focus_raw) if focus_raw else ""
|
|
311
|
+
if focus_id:
|
|
312
|
+
keep = closure(focus_id, direction=direction)
|
|
313
|
+
nodes = {k: v for k, v in nodes.items() if k in keep}
|
|
314
|
+
edges = {e for e in edges if e.dep in keep and e.target in keep}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def mermaid_id(spec_id: str) -> str:
|
|
318
|
+
# Mermaid node ids should be identifiers; we prefix with "S" and keep digits.
|
|
319
|
+
safe = re.sub(r"[^0-9A-Za-z_]", "_", spec_id)
|
|
320
|
+
if not safe:
|
|
321
|
+
safe = "unknown"
|
|
322
|
+
if safe[0].isdigit():
|
|
323
|
+
safe = "S" + safe
|
|
324
|
+
return safe
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def sort_key(n: Node) -> tuple[int, str]:
|
|
328
|
+
# Prefer numeric ids first.
|
|
329
|
+
m = re.match(r"^(\d{3})$", n.id)
|
|
330
|
+
if m:
|
|
331
|
+
return (0, m.group(1))
|
|
332
|
+
return (1, n.id)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
sorted_nodes = sorted(nodes.values(), key=sort_key)
|
|
336
|
+
sorted_edges = sorted(edges, key=lambda e: (e.dep, e.target))
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
if json_mode:
|
|
340
|
+
payload = {
|
|
341
|
+
"focus": focus_id or None,
|
|
342
|
+
"direction": direction,
|
|
343
|
+
"registries": [str(p) for p in registry_files],
|
|
344
|
+
"nodes": [
|
|
345
|
+
{
|
|
346
|
+
"id": n.id,
|
|
347
|
+
"defined": n.defined,
|
|
348
|
+
"dir": n.dir,
|
|
349
|
+
"status": n.status,
|
|
350
|
+
"groups": sorted(n.groups),
|
|
351
|
+
}
|
|
352
|
+
for n in sorted_nodes
|
|
353
|
+
],
|
|
354
|
+
"edges": [{"dep": e.dep, "target": e.target} for e in sorted_edges],
|
|
355
|
+
"conflicts": conflicts,
|
|
356
|
+
}
|
|
357
|
+
print(json.dumps(payload, ensure_ascii=False))
|
|
358
|
+
raise SystemExit(0)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
if fmt == "text":
|
|
362
|
+
for n in sorted_nodes:
|
|
363
|
+
deps = sorted(up.get(n.id, set()))
|
|
364
|
+
status = n.status or ("external" if not n.defined else "")
|
|
365
|
+
deps_str = ",".join(deps) if deps else "-"
|
|
366
|
+
print(f"{n.id}\t{status}\tdependsOn:{deps_str}")
|
|
367
|
+
raise SystemExit(0)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# mermaid
|
|
371
|
+
print("graph TD")
|
|
372
|
+
|
|
373
|
+
for n in sorted_nodes:
|
|
374
|
+
mid = mermaid_id(n.id)
|
|
375
|
+
label = n.id
|
|
376
|
+
if n.status:
|
|
377
|
+
label = f"{label} · {n.status}"
|
|
378
|
+
elif not n.defined:
|
|
379
|
+
label = f"{label} · external"
|
|
380
|
+
# Use double quotes via Mermaid bracket syntax.
|
|
381
|
+
print(f' {mid}["{label}"]')
|
|
382
|
+
|
|
383
|
+
for e in sorted_edges:
|
|
384
|
+
a = mermaid_id(e.dep)
|
|
385
|
+
b = mermaid_id(e.target)
|
|
386
|
+
print(f" {a} --> {b}")
|
|
387
|
+
|
|
388
|
+
# style external nodes
|
|
389
|
+
externals = [n for n in sorted_nodes if not n.defined]
|
|
390
|
+
if externals:
|
|
391
|
+
print("")
|
|
392
|
+
print(" classDef external stroke-dasharray: 5 5;")
|
|
393
|
+
for n in externals:
|
|
394
|
+
print(f" class {mermaid_id(n.id)} external;")
|
|
395
|
+
|
|
396
|
+
if conflicts:
|
|
397
|
+
print("")
|
|
398
|
+
print(f"%% WARNING: registry conflicts detected: {len(conflicts)} (use --json to inspect)")
|
|
399
|
+
PY
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Project Constitution (Spec Kit)
|
|
2
|
+
|
|
3
|
+
> This file captures project-level hard constraints, governance, and quality gates. Customize it for your project. If it conflicts with reality, treat this file as the source of truth and update templates and workflows accordingly.
|
|
4
|
+
|
|
5
|
+
## I. Parallel Development Safety (Non-Negotiable)
|
|
6
|
+
|
|
7
|
+
- Assume the working tree may contain unrelated, uncommitted changes from other parallel tasks.
|
|
8
|
+
- Prohibit destructive Git commands unless explicitly requested by the user:
|
|
9
|
+
- `git restore` / `git checkout -- <path>` / `git reset` / `git clean` / `git stash`
|
|
10
|
+
- Do not run VCS write operations unless explicitly requested by the user:
|
|
11
|
+
- `git add` / `git commit` / `git push` / `git rebase` / `git merge` / `git cherry-pick`
|
|
12
|
+
- Use read-only commands for inspection (e.g., `git status` / `git diff`).
|
|
13
|
+
|
|
14
|
+
## II. Spec-Driven Artifact Boundaries (`.specify/` + `specs/`)
|
|
15
|
+
|
|
16
|
+
- Feature directory: `specs/<NNN-*>/`
|
|
17
|
+
- `spec.md`: requirements and success criteria (WHAT/WHY). Avoid implementation details (HOW).
|
|
18
|
+
- `plan.md`: implementation approach and landing spots (HOW), including key decisions, risks, and validation strategy.
|
|
19
|
+
- `tasks.md`: executable task breakdown (dependencies/order/parallel markers). Check items off during implementation.
|
|
20
|
+
- If there is conflict or ambiguity: fix `spec.md` / `plan.md` first, then change code, to avoid spec/implementation drift.
|
|
21
|
+
|
|
22
|
+
## II.a CLI Context Compatibility (Tmux In/Out)
|
|
23
|
+
|
|
24
|
+
- CLI commands SHOULD be usable both inside and outside tmux.
|
|
25
|
+
- If a command depends on an attached tmux client or “inside tmux” context (e.g., current client defaults, `display-popup`):
|
|
26
|
+
- It MUST detect the required context and refuse to run when the context is missing.
|
|
27
|
+
- It MUST provide an actionable error (how to supply an explicit target like `--session`, or run inside tmux).
|
|
28
|
+
- Default targeting rules MUST be explicit, deterministic, and overridable (avoid silent fallbacks that can cause mis-targeting).
|
|
29
|
+
|
|
30
|
+
## II.b LLM-Friendly CLI Output Contract (Non-Negotiable)
|
|
31
|
+
|
|
32
|
+
- stdout MUST be consumable by automation (humans/LLMs/shell pipelines):
|
|
33
|
+
- If `--json` is supported, it MUST output single-line JSON to stdout and nothing else.
|
|
34
|
+
- Explanatory text and debug output MUST NOT be mixed into stdout (use stderr).
|
|
35
|
+
- Errors MUST be actionable:
|
|
36
|
+
- stderr MUST start with `Error:` for user-visible failures.
|
|
37
|
+
- Exit code MUST be non-zero on failure.
|
|
38
|
+
- Tracebacks/stacks MUST NOT be printed for expected user errors.
|
|
39
|
+
- IDs MUST be native and stable:
|
|
40
|
+
- Do not invent synthetic identifiers; prefer tmux native IDs (pane/window ids) with indexes only as convenience within a defined scope.
|
|
41
|
+
- Any write action MUST resolve and lock the stable native ID before performing side effects; ambiguity MUST fail with zero side effects.
|
|
42
|
+
|
|
43
|
+
## III. Quality Gates (Must Be Explicit and Executed Before Shipping)
|
|
44
|
+
|
|
45
|
+
- Each feature must state which gates will be run and what counts as “pass” in `plan.md` (project-specific):
|
|
46
|
+
- typecheck / lint / test / build / e2e / contract tests
|
|
47
|
+
- For high-risk changes (security, data consistency, payments, permissions, etc.): include regression validation and a clear failure mitigation/rollback strategy.
|
|
48
|
+
|
|
49
|
+
## IV. Performance & Observability (Enforced When Applicable)
|
|
50
|
+
|
|
51
|
+
- If this feature touches performance-sensitive paths or external performance boundaries: define budgets and a baseline in `plan.md`, and record reproducible evidence under `specs/<id>/perf/`.
|
|
52
|
+
- Key flows must have diagnosable signals (logs/metrics/traces or equivalent events), including:
|
|
53
|
+
- Correlation identifiers (requestId/sessionId/entityId, etc.)
|
|
54
|
+
- PII/sensitive data policy
|
|
55
|
+
- Toggles and cost model (overhead when enabled/disabled)
|
|
56
|
+
|
|
57
|
+
## V. Breaking Changes Policy (Must Choose Explicitly)
|
|
58
|
+
|
|
59
|
+
> Define whether breaking changes are allowed and how migrations are handled.
|
|
60
|
+
|
|
61
|
+
- Policy: `semver`
|
|
62
|
+
- If introducing a breaking change: include migration steps and a blast radius checklist in `plan.md` and the PR/change notes.
|
|
63
|
+
|
|
64
|
+
## VI. Template Locations (Optional)
|
|
65
|
+
|
|
66
|
+
- Default templates: `.specify/templates/*`
|
|
67
|
+
- If you want `$speckit` to be self-contained within the repo: mirror a copy into `.codex/skills/speckit/assets/templates/*` (keep them updated together to avoid drift).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# [PROJECT NAME] Development Guidelines
|
|
2
|
+
|
|
3
|
+
Auto-generated from all feature plans. Last updated: [DATE]
|
|
4
|
+
|
|
5
|
+
## Active Technologies
|
|
6
|
+
|
|
7
|
+
[EXTRACTED FROM ALL PLAN.MD FILES]
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
[ACTUAL STRUCTURE FROM PLANS]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Commands
|
|
16
|
+
|
|
17
|
+
[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES]
|
|
18
|
+
|
|
19
|
+
## Code Style
|
|
20
|
+
|
|
21
|
+
[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE]
|
|
22
|
+
|
|
23
|
+
## Recent Changes
|
|
24
|
+
|
|
25
|
+
[LAST 3 FEATURES AND WHAT THEY ADDED]
|
|
26
|
+
|
|
27
|
+
<!-- MANUAL ADDITIONS START -->
|
|
28
|
+
<!-- MANUAL ADDITIONS END -->
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# [CHECKLIST TYPE] Checklist: [FEATURE NAME]
|
|
2
|
+
|
|
3
|
+
**Purpose**: [Brief description of what this checklist covers]
|
|
4
|
+
**Created**: [DATE]
|
|
5
|
+
**Feature**: [Link to spec.md or relevant documentation]
|
|
6
|
+
|
|
7
|
+
**Note**: This checklist is generated by the `$speckit checklist` stage based on feature context and requirements.
|
|
8
|
+
|
|
9
|
+
<!--
|
|
10
|
+
============================================================================
|
|
11
|
+
IMPORTANT: The checklist items below are SAMPLE ITEMS for illustration only.
|
|
12
|
+
|
|
13
|
+
The $speckit checklist stage MUST replace these with actual items based on:
|
|
14
|
+
- User's specific checklist request
|
|
15
|
+
- Feature requirements from spec.md
|
|
16
|
+
- Technical context from plan.md
|
|
17
|
+
- Implementation details from tasks.md
|
|
18
|
+
|
|
19
|
+
DO NOT keep these sample items in the generated checklist file.
|
|
20
|
+
============================================================================
|
|
21
|
+
-->
|
|
22
|
+
|
|
23
|
+
## [Category 1]
|
|
24
|
+
|
|
25
|
+
- [ ] CHK001 First checklist item with clear action
|
|
26
|
+
- [ ] CHK002 Second checklist item
|
|
27
|
+
- [ ] CHK003 Third checklist item
|
|
28
|
+
|
|
29
|
+
## [Category 2]
|
|
30
|
+
|
|
31
|
+
- [ ] CHK004 Another category item
|
|
32
|
+
- [ ] CHK005 Item with specific criteria
|
|
33
|
+
- [ ] CHK006 Final item in this category
|
|
34
|
+
|
|
35
|
+
## Performance & Observability _(if applicable)_
|
|
36
|
+
|
|
37
|
+
- [ ] CHK0XX Performance budget + baseline measurement recorded (env/tool documented)
|
|
38
|
+
- [ ] CHK0XX No critical regression (or justified in Complexity Tracking)
|
|
39
|
+
- [ ] CHK0XX Logs/metrics/traces updated for key flows; PII/overhead reviewed
|
|
40
|
+
- [ ] CHK0XX Diagnostics include stable identifiers to correlate requests/sessions/entities
|
|
41
|
+
- [ ] CHK0XX Data consistency boundaries documented (transaction/atomicity/ordering as applicable)
|
|
42
|
+
- [ ] CHK0XX Breaking change declared and migration note linked (per project policy)
|
|
43
|
+
|
|
44
|
+
## Notes
|
|
45
|
+
|
|
46
|
+
- Check items off as completed: `[x]`
|
|
47
|
+
- Add comments or findings inline
|
|
48
|
+
- Link to relevant resources or documentation
|
|
49
|
+
- Items are numbered sequentially for easy reference
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Implementation Plan: [FEATURE]
|
|
2
|
+
|
|
3
|
+
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
|
|
4
|
+
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
|
|
5
|
+
|
|
6
|
+
**Note**: This template is copied into `specs/[###-feature-name]/plan.md` by the
|
|
7
|
+
Speckit plan workflow (`setup-plan.sh`).
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
[Extract from feature spec: primary requirement + technical approach from research]
|
|
12
|
+
|
|
13
|
+
## Technical Context
|
|
14
|
+
|
|
15
|
+
<!--
|
|
16
|
+
ACTION REQUIRED: Replace the content in this section with the technical details
|
|
17
|
+
for the project. The structure here is presented in advisory capacity to guide
|
|
18
|
+
the iteration process.
|
|
19
|
+
-->
|
|
20
|
+
|
|
21
|
+
**Language/Version**: [e.g., Python 3.11, Swift 5.9, Rust 1.75 or NEEDS CLARIFICATION]
|
|
22
|
+
**Primary Dependencies**: [e.g., React, Next.js, FastAPI, Postgres, Redis or NEEDS CLARIFICATION]
|
|
23
|
+
**Storage**: [if applicable, e.g., files / N/A]
|
|
24
|
+
**Testing**: [e.g., Vitest, Jest, Pytest, Go test or NEEDS CLARIFICATION]
|
|
25
|
+
**Target Platform**: [e.g., Node.js 20+, browsers, iOS/Android]
|
|
26
|
+
**Project Type**: [single repo / monorepo / packages + apps]
|
|
27
|
+
**Performance Goals**: [budgets + measurement method (benchmark/profile) or NEEDS CLARIFICATION]
|
|
28
|
+
**Constraints**: [include diagnostics overhead budgets if applicable]
|
|
29
|
+
**Scale/Scope**: [domain-specific, e.g., 10k users, 1M LOC, 50 screens or NEEDS CLARIFICATION]
|
|
30
|
+
|
|
31
|
+
## Constitution Check
|
|
32
|
+
|
|
33
|
+
_GATE: Must pass before implementation. Re-check after Phase 1 design._
|
|
34
|
+
|
|
35
|
+
- Answer the following BEFORE starting implementation, and re-check after Phase 1:
|
|
36
|
+
- What user value is delivered, and how is it validated (acceptance scenarios / SC-*)?
|
|
37
|
+
- What public/external surfaces change (API/CLI/UI/config), and what is the compatibility policy?
|
|
38
|
+
- What docs/specs must be updated first (docs-first where applicable) to avoid drift?
|
|
39
|
+
- What are the key risks (security, data loss, privacy, performance) and mitigations?
|
|
40
|
+
- If performance-sensitive: what budgets/baselines exist, and how are regressions prevented?
|
|
41
|
+
- What diagnostics/observability signals are required for triage (logs/metrics/traces)?
|
|
42
|
+
- What quality gates will be run before merge (typecheck / lint / test / build), and what counts as “pass”?
|
|
43
|
+
|
|
44
|
+
## Perf Evidence Plan (IF APPLICABLE)
|
|
45
|
+
|
|
46
|
+
> If this feature touches performance-sensitive paths or external performance boundaries: this section must be filled. Otherwise mark it as `N/A`.
|
|
47
|
+
|
|
48
|
+
- Baseline semantics: before/after code change OR A/B strategy (choose one)
|
|
49
|
+
- envId: <os-arch.cpu.runtime-version>
|
|
50
|
+
- tool: <benchmark/profile tool name>
|
|
51
|
+
- collect (before): `specs/<id>/perf/before.<envId>.<tag>.<ext>`
|
|
52
|
+
- collect (after): `specs/<id>/perf/after.<envId>.<tag>.<ext>`
|
|
53
|
+
- diff: `specs/<id>/perf/diff.before__after.<ext>` (if applicable)
|
|
54
|
+
- Failure policy: if results are not comparable (env/tool changes, insufficient sample size, high variance) → re-run or narrow the scope; do not draw hard conclusions when not comparable.
|
|
55
|
+
|
|
56
|
+
## Project Structure
|
|
57
|
+
|
|
58
|
+
### Documentation (this feature)
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
specs/[###-feature]/
|
|
62
|
+
├── plan.md # This file ($speckit plan output)
|
|
63
|
+
├── research.md # Phase 0 output ($speckit plan)
|
|
64
|
+
├── data-model.md # Phase 1 output ($speckit plan)
|
|
65
|
+
├── quickstart.md # Phase 1 output ($speckit plan)
|
|
66
|
+
├── contracts/ # Phase 1 output ($speckit plan)
|
|
67
|
+
├── notes/ # Optional: handoff notes / entry points ($speckit notes)
|
|
68
|
+
└── tasks.md # Phase 2 output ($speckit tasks - NOT created by $speckit plan)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Source Code (repository root)
|
|
72
|
+
|
|
73
|
+
<!--
|
|
74
|
+
ACTION REQUIRED: Replace the placeholder tree below with the concrete layout
|
|
75
|
+
for this feature. Delete unused options and expand the chosen structure with
|
|
76
|
+
real paths (e.g., apps/admin, packages/something). The delivered plan must
|
|
77
|
+
not include Option labels.
|
|
78
|
+
-->
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
# [REMOVE IF UNUSED] Option 1: Single project (DEFAULT)
|
|
82
|
+
src/
|
|
83
|
+
├── models/
|
|
84
|
+
├── services/
|
|
85
|
+
├── cli/
|
|
86
|
+
└── lib/
|
|
87
|
+
|
|
88
|
+
tests/
|
|
89
|
+
├── contract/
|
|
90
|
+
├── integration/
|
|
91
|
+
└── unit/
|
|
92
|
+
|
|
93
|
+
# [REMOVE IF UNUSED] Option 2: Web application (when "frontend" + "backend" detected)
|
|
94
|
+
backend/
|
|
95
|
+
├── src/
|
|
96
|
+
│ ├── models/
|
|
97
|
+
│ ├── services/
|
|
98
|
+
│ └── api/
|
|
99
|
+
└── tests/
|
|
100
|
+
|
|
101
|
+
frontend/
|
|
102
|
+
├── src/
|
|
103
|
+
│ ├── components/
|
|
104
|
+
│ ├── pages/
|
|
105
|
+
│ └── services/
|
|
106
|
+
└── tests/
|
|
107
|
+
|
|
108
|
+
# [REMOVE IF UNUSED] Option 3: Mobile + API (when "iOS/Android" detected)
|
|
109
|
+
api/
|
|
110
|
+
└── [same as backend above]
|
|
111
|
+
|
|
112
|
+
ios/ or android/
|
|
113
|
+
└── [platform-specific structure: feature modules, UI flows, platform tests]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Structure Decision**: [Document the selected structure and reference the real
|
|
117
|
+
directories captured above]
|
|
118
|
+
|
|
119
|
+
## Complexity Tracking
|
|
120
|
+
|
|
121
|
+
> **Fill ONLY if Constitution Check has violations that must be justified**
|
|
122
|
+
|
|
123
|
+
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
|
124
|
+
| -------------------------- | ------------------ | ------------------------------------ |
|
|
125
|
+
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
|
|
126
|
+
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
|