squadrant 0.9.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/LICENSE +21 -0
- package/README.md +268 -0
- package/dist/index.js +9154 -0
- package/dist/index.js.map +1 -0
- package/dist/squadrantd.js +3928 -0
- package/dist/squadrantd.js.map +1 -0
- package/package.json +68 -0
- package/plugin/.claude-plugin/plugin.json +5 -0
- package/plugin/skills/add-pick-crew-rule/SKILL.md +88 -0
- package/plugin/skills/captain-ops/SKILL.md +390 -0
- package/plugin/skills/command-ops/SKILL.md +157 -0
- package/plugin/skills/config-doctor/SKILL.md +46 -0
- package/plugin/skills/daily-log/SKILL.md +44 -0
- package/plugin/skills/karpathy-principles/SKILL.md +82 -0
- package/plugin/skills/set-effort/SKILL.md +59 -0
- package/plugin/skills/side-session/SKILL.md +113 -0
- package/plugin/skills/squadrant-effort/SKILL.md +8 -0
- package/plugin/skills/squadrant-new-project/SKILL.md +67 -0
- package/plugin/skills/squadrant-register-project/SKILL.md +60 -0
- package/plugin/skills/where-i-am/SKILL.md +102 -0
- package/plugin/skills/wiki-ops/SKILL.md +96 -0
- package/plugin/skills/wim/SKILL.md +8 -0
- package/scripts/acceptance-interactive-codex.sh +56 -0
- package/scripts/capture-skill.sh +32 -0
- package/scripts/claude-iv-smoke.mjs +133 -0
- package/scripts/fix-skill.sh +39 -0
- package/scripts/gen-codex-types.sh +18 -0
- package/scripts/mailbox-injector-smoke.mjs +124 -0
- package/scripts/mark-learning-useful.sh +22 -0
- package/scripts/migrate-to-squadrant.sh +158 -0
- package/scripts/notify-relay-placement-smoke.mjs +59 -0
- package/scripts/read-handoff.sh +22 -0
- package/scripts/record-learning.sh +31 -0
- package/scripts/record-side-handoff.sh +32 -0
- package/scripts/smoke-push-notify.mjs +147 -0
- package/scripts/spawn-crew-pane.sh +17 -0
- package/scripts/spawn-workspace.sh +206 -0
- package/scripts/wiki-ingest.sh +121 -0
- package/scripts/wiki-log.sh +20 -0
- package/scripts/wiki-query.sh +36 -0
- package/scripts/write-handoff.sh +31 -0
- package/templates/captain.claude.md +44 -0
- package/templates/captain.generic.md +45 -0
- package/templates/command.claude.md +32 -0
- package/templates/crew.claude.md +64 -0
- package/templates/crew.generic.md +51 -0
- package/templates/crew.opencode.md +51 -0
- package/templates/learnings.claude.md +40 -0
- package/templates/side.debug.claude.md +78 -0
- package/templates/side.research.claude.md +63 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Usage: spawn-workspace.sh <name> <cwd> [role] [--fresh]
|
|
3
|
+
# role: "captain" | "crew" | "command" (default: "captain")
|
|
4
|
+
# --fresh: force a new session instead of resuming
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
CMUX="/Applications/cmux.app/Contents/Resources/bin/cmux"
|
|
8
|
+
TEMPLATES_DIR="${HOME}/.config/squadrant/templates"
|
|
9
|
+
SESSIONS_FILE="${HOME}/.config/squadrant/sessions.json"
|
|
10
|
+
NAME="${1:?Usage: spawn-workspace.sh <name> <cwd> [role] [--fresh]}"
|
|
11
|
+
CWD="${2:?Usage: spawn-workspace.sh <name> <cwd> [role] [--fresh]}"
|
|
12
|
+
ROLE="${3:-captain}"
|
|
13
|
+
FORCE_FRESH="${4:-}"
|
|
14
|
+
|
|
15
|
+
TODAY=$(date +"%Y-%m-%d")
|
|
16
|
+
FRESH=false
|
|
17
|
+
|
|
18
|
+
# --- Session freshness check ---
|
|
19
|
+
if [ "$FORCE_FRESH" = "--fresh" ]; then
|
|
20
|
+
FRESH=true
|
|
21
|
+
elif [ -f "$SESSIONS_FILE" ]; then
|
|
22
|
+
# Check last launch date
|
|
23
|
+
LAST_DATE=$(python3 -c "
|
|
24
|
+
import json, sys
|
|
25
|
+
try:
|
|
26
|
+
data = json.load(open('$SESSIONS_FILE'))
|
|
27
|
+
print(data.get('workspaces', {}).get('$NAME', {}).get('lastLaunched', ''))
|
|
28
|
+
except: print('')
|
|
29
|
+
" 2>/dev/null)
|
|
30
|
+
|
|
31
|
+
if [ -z "$LAST_DATE" ]; then
|
|
32
|
+
FRESH=true # first launch
|
|
33
|
+
elif [ "$LAST_DATE" != "$TODAY" ]; then
|
|
34
|
+
FRESH=true # new day
|
|
35
|
+
echo "↻ new day — starting fresh session for $NAME"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Check template + skills hash
|
|
39
|
+
if [ "$FRESH" = "false" ]; then
|
|
40
|
+
ROLE_FILE="${TEMPLATES_DIR}/${ROLE}.claude.md"
|
|
41
|
+
[ ! -f "$ROLE_FILE" ] && ROLE_FILE="${TEMPLATES_DIR}/${ROLE}.CLAUDE.md"
|
|
42
|
+
CURRENT_HASH=$(cat "$ROLE_FILE" "${HOME}/.config/squadrant/plugin/skills"/*/SKILL.md 2>/dev/null | shasum -a 256 | cut -c1-16)
|
|
43
|
+
STORED_HASH=$(python3 -c "
|
|
44
|
+
import json
|
|
45
|
+
try:
|
|
46
|
+
data = json.load(open('$SESSIONS_FILE'))
|
|
47
|
+
print(data.get('workspaces', {}).get('$NAME', {}).get('templateHash', ''))
|
|
48
|
+
except: print('')
|
|
49
|
+
" 2>/dev/null)
|
|
50
|
+
if [ -n "$CURRENT_HASH" ] && [ "$CURRENT_HASH" != "$STORED_HASH" ]; then
|
|
51
|
+
FRESH=true
|
|
52
|
+
echo "↻ template instructions updated — starting fresh session for $NAME"
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
else
|
|
56
|
+
FRESH=true # no sessions file yet
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# --- Record session ---
|
|
60
|
+
ROLE_FILE="${TEMPLATES_DIR}/${ROLE}.claude.md"
|
|
61
|
+
[ ! -f "$ROLE_FILE" ] && ROLE_FILE="${TEMPLATES_DIR}/${ROLE}.CLAUDE.md"
|
|
62
|
+
CURRENT_HASH=$(cat "$ROLE_FILE" "${HOME}/.config/squadrant/plugin/skills"/*/SKILL.md 2>/dev/null | shasum -a 256 | cut -c1-16)
|
|
63
|
+
python3 -c "
|
|
64
|
+
import json, os
|
|
65
|
+
path = '$SESSIONS_FILE'
|
|
66
|
+
try:
|
|
67
|
+
data = json.load(open(path))
|
|
68
|
+
except: data = {'workspaces': {}}
|
|
69
|
+
data.setdefault('workspaces', {})['$NAME'] = {'lastLaunched': '$TODAY', 'templateHash': '$CURRENT_HASH'}
|
|
70
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
71
|
+
json.dump(data, open(path, 'w'), indent=2)
|
|
72
|
+
" 2>/dev/null
|
|
73
|
+
|
|
74
|
+
# Read permission mode from config
|
|
75
|
+
PERM_MODE=$(python3 -c "
|
|
76
|
+
import json
|
|
77
|
+
try:
|
|
78
|
+
cfg = json.load(open('${HOME}/.config/squadrant/config.json'))
|
|
79
|
+
role_key = '$ROLE' if '$ROLE' in ('captain', 'command') else 'captain'
|
|
80
|
+
print(cfg.get('defaults', {}).get('permissions', {}).get(role_key, 'default'))
|
|
81
|
+
except: print('default')
|
|
82
|
+
" 2>/dev/null)
|
|
83
|
+
|
|
84
|
+
# Read agent and model from roles config (new format), fall back to old models config
|
|
85
|
+
AGENT=$(python3 -c "
|
|
86
|
+
import json
|
|
87
|
+
try:
|
|
88
|
+
cfg = json.load(open('${HOME}/.config/squadrant/config.json'))
|
|
89
|
+
roles = cfg.get('defaults', {}).get('roles', {})
|
|
90
|
+
role_cfg = roles.get('$ROLE', {})
|
|
91
|
+
print(role_cfg.get('agent', 'claude'))
|
|
92
|
+
except: print('claude')
|
|
93
|
+
" 2>/dev/null)
|
|
94
|
+
|
|
95
|
+
MODEL=$(python3 -c "
|
|
96
|
+
import json
|
|
97
|
+
try:
|
|
98
|
+
cfg = json.load(open('${HOME}/.config/squadrant/config.json'))
|
|
99
|
+
roles = cfg.get('defaults', {}).get('roles', {})
|
|
100
|
+
role_cfg = roles.get('$ROLE', {})
|
|
101
|
+
model = role_cfg.get('model', '')
|
|
102
|
+
if not model:
|
|
103
|
+
model = cfg.get('defaults', {}).get('models', {}).get('$ROLE', '')
|
|
104
|
+
print(model)
|
|
105
|
+
except: print('')
|
|
106
|
+
" 2>/dev/null)
|
|
107
|
+
|
|
108
|
+
# --- Build agent command based on resolved agent ---
|
|
109
|
+
case "$AGENT" in
|
|
110
|
+
claude)
|
|
111
|
+
if [ "$FRESH" = "true" ]; then
|
|
112
|
+
AGENT_CMD="claude"
|
|
113
|
+
else
|
|
114
|
+
AGENT_CMD="claude -c"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [ "$PERM_MODE" = "acceptEdits" ]; then
|
|
118
|
+
AGENT_CMD="${AGENT_CMD} --permission-mode acceptEdits"
|
|
119
|
+
elif [ "$PERM_MODE" = "auto" ]; then
|
|
120
|
+
AGENT_CMD="${AGENT_CMD} --permission-mode auto"
|
|
121
|
+
elif [ "$PERM_MODE" = "bypassPermissions" ]; then
|
|
122
|
+
AGENT_CMD="${AGENT_CMD} --dangerously-skip-permissions"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
if [ -n "$MODEL" ]; then
|
|
126
|
+
AGENT_CMD="${AGENT_CMD} --model ${MODEL}"
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
ROLE_FILE="${TEMPLATES_DIR}/${ROLE}.claude.md"
|
|
130
|
+
[ ! -f "$ROLE_FILE" ] && ROLE_FILE="${TEMPLATES_DIR}/${ROLE}.CLAUDE.md"
|
|
131
|
+
if [ -f "$ROLE_FILE" ]; then
|
|
132
|
+
AGENT_CMD="${AGENT_CMD} --append-system-prompt-file ${ROLE_FILE}"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
PLUGIN_DIR="${HOME}/.config/squadrant/plugin"
|
|
136
|
+
if [ -d "$PLUGIN_DIR" ]; then
|
|
137
|
+
AGENT_CMD="${AGENT_CMD} --plugin-dir ${PLUGIN_DIR}"
|
|
138
|
+
fi
|
|
139
|
+
;;
|
|
140
|
+
|
|
141
|
+
codex)
|
|
142
|
+
ROLE_FILE="${TEMPLATES_DIR}/${ROLE}.generic.md"
|
|
143
|
+
AGENT_CMD="codex exec --json --full-auto"
|
|
144
|
+
if [ -f "$ROLE_FILE" ]; then
|
|
145
|
+
AGENT_CMD="${AGENT_CMD} -p \"Read instructions from ${ROLE_FILE} and begin.\""
|
|
146
|
+
fi
|
|
147
|
+
;;
|
|
148
|
+
|
|
149
|
+
gemini)
|
|
150
|
+
ROLE_FILE="${TEMPLATES_DIR}/${ROLE}.generic.md"
|
|
151
|
+
AGENT_CMD="gemini --yolo"
|
|
152
|
+
if [ -f "$ROLE_FILE" ]; then
|
|
153
|
+
AGENT_CMD="${AGENT_CMD} -p \"Read instructions from ${ROLE_FILE} and begin.\""
|
|
154
|
+
fi
|
|
155
|
+
;;
|
|
156
|
+
|
|
157
|
+
*)
|
|
158
|
+
echo "ERROR: Unknown agent '${AGENT}' for role '${ROLE}'"
|
|
159
|
+
exit 1
|
|
160
|
+
;;
|
|
161
|
+
esac
|
|
162
|
+
|
|
163
|
+
# --- Handle existing workspace ---
|
|
164
|
+
# Find existing workspace via runtime abstraction
|
|
165
|
+
EXISTING_JSON=$(squadrant runtime list --json 2>/dev/null || echo "[]")
|
|
166
|
+
EXISTING_ID=$(echo "$EXISTING_JSON" | python3 -c "
|
|
167
|
+
import json,sys
|
|
168
|
+
try:
|
|
169
|
+
for w in json.load(sys.stdin):
|
|
170
|
+
if w.get('name') == '$NAME':
|
|
171
|
+
print(w['id']); break
|
|
172
|
+
except: pass
|
|
173
|
+
")
|
|
174
|
+
|
|
175
|
+
if [ -n "$EXISTING_ID" ] && [ "$FRESH" = "true" ]; then
|
|
176
|
+
echo "Closing stale workspace: $NAME"
|
|
177
|
+
# select-workspace has no runtime abstraction yet — keeping direct cmux call here
|
|
178
|
+
"$CMUX" close-workspace --workspace "$EXISTING_ID" 2>/dev/null || true
|
|
179
|
+
EXISTING_ID=""
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
if [ -n "$EXISTING_ID" ]; then
|
|
183
|
+
echo "Workspace '$NAME' already exists — switching to it"
|
|
184
|
+
# select-workspace has no runtime abstraction yet — keeping direct cmux call here
|
|
185
|
+
"$CMUX" select-workspace --workspace "$EXISTING_ID" 2>&1
|
|
186
|
+
exit 0
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
# --- Spawn new workspace ---
|
|
190
|
+
CURRENT=$("$CMUX" current-workspace 2>&1 | awk '{print $1}')
|
|
191
|
+
NEW_UUID=$("$CMUX" new-workspace --command "$AGENT_CMD" --cwd "$CWD" 2>&1 | awk '{print $2}')
|
|
192
|
+
"$CMUX" rename-workspace --workspace "$NEW_UUID" "$NAME" 2>&1
|
|
193
|
+
if [ "$ROLE" = "command" ] || [ "$ROLE" = "captain" ]; then
|
|
194
|
+
"$CMUX" workspace-action --workspace "$NEW_UUID" --action pin 2>/dev/null || true
|
|
195
|
+
fi
|
|
196
|
+
# Send initial prompt to trigger startup checklist (Claude agents only)
|
|
197
|
+
if [ "$AGENT" = "claude" ]; then
|
|
198
|
+
if [ "$ROLE" = "captain" ]; then
|
|
199
|
+
(sleep 3 && "$CMUX" send --workspace "$NEW_UUID" "Run your startup checklist: use the squadrant:captain-ops skill, complete all startup steps, then report ready." 2>/dev/null) &
|
|
200
|
+
elif [ "$ROLE" = "command" ]; then
|
|
201
|
+
(sleep 3 && "$CMUX" send --workspace "$NEW_UUID" "Run your startup checklist: use the squadrant:command-ops skill, complete your daily briefing, then report ready." 2>/dev/null) &
|
|
202
|
+
fi
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
"$CMUX" select-workspace --workspace "$CURRENT" 2>&1
|
|
206
|
+
echo "Spawned workspace: $NAME at $CWD (role: $ROLE, agent: $AGENT, fresh: $FRESH)"
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Usage: wiki-ingest.sh <spoke-vault-path> <page-slug> <title> <category> <body> [tags] [source]
|
|
3
|
+
# Creates or updates a wiki page and updates the index and log.
|
|
4
|
+
# Categories: Architecture, Patterns, APIs, Configuration, Debugging, Conventions, Dependencies, Deployment
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
VAULT="${1:?Usage: wiki-ingest.sh <vault> <slug> <title> <category> <body> [tags] [source]}"
|
|
8
|
+
SLUG="${2:?}"
|
|
9
|
+
TITLE="${3:?}"
|
|
10
|
+
CATEGORY="${4:?}"
|
|
11
|
+
BODY="${5:?}"
|
|
12
|
+
TAGS="${6:-}"
|
|
13
|
+
SOURCE="${7:-manual}"
|
|
14
|
+
DATE=$(date +"%Y-%m-%d")
|
|
15
|
+
TIME=$(date +"%H:%M:%S")
|
|
16
|
+
|
|
17
|
+
WIKI_DIR="${VAULT}/wiki"
|
|
18
|
+
PAGES_DIR="${WIKI_DIR}/pages"
|
|
19
|
+
PAGE_FILE="${PAGES_DIR}/${SLUG}.md"
|
|
20
|
+
INDEX_FILE="${WIKI_DIR}/index.md"
|
|
21
|
+
LOG_FILE="${WIKI_DIR}/log.md"
|
|
22
|
+
|
|
23
|
+
mkdir -p "${PAGES_DIR}"
|
|
24
|
+
|
|
25
|
+
# Determine if this is a create or update
|
|
26
|
+
ACTION="created"
|
|
27
|
+
if [ -f "$PAGE_FILE" ]; then
|
|
28
|
+
ACTION="updated"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Write the page
|
|
32
|
+
cat > "$PAGE_FILE" << EOF
|
|
33
|
+
---
|
|
34
|
+
title: "${TITLE}"
|
|
35
|
+
category: ${CATEGORY}
|
|
36
|
+
created: "${DATE}"
|
|
37
|
+
updated: "${DATE}"
|
|
38
|
+
tags: [${TAGS}]
|
|
39
|
+
source: "${SOURCE}"
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
# ${TITLE}
|
|
43
|
+
|
|
44
|
+
${BODY}
|
|
45
|
+
|
|
46
|
+
## Related
|
|
47
|
+
<!-- Add [[page-slug]] links to related pages -->
|
|
48
|
+
EOF
|
|
49
|
+
|
|
50
|
+
# Append to log (newest first, after the marker comment)
|
|
51
|
+
if [ -f "$LOG_FILE" ]; then
|
|
52
|
+
ENTRY="- **${DATE} ${TIME}** — ${ACTION} [${TITLE}](pages/${SLUG}.md) (${CATEGORY})"
|
|
53
|
+
MARKER="<!-- Entries will be appended by wiki-ingest -->"
|
|
54
|
+
if grep -qF "$MARKER" "$LOG_FILE"; then
|
|
55
|
+
# Insert entry right after the marker line
|
|
56
|
+
sed -i '' "/$MARKER/a\\
|
|
57
|
+
${ENTRY}
|
|
58
|
+
" "$LOG_FILE"
|
|
59
|
+
else
|
|
60
|
+
echo "$ENTRY" >> "$LOG_FILE"
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Rebuild index by scanning all pages
|
|
65
|
+
if [ -f "$INDEX_FILE" ]; then
|
|
66
|
+
python3 -c "
|
|
67
|
+
import os, re, glob
|
|
68
|
+
|
|
69
|
+
pages_dir = '${PAGES_DIR}'
|
|
70
|
+
index_file = '${INDEX_FILE}'
|
|
71
|
+
|
|
72
|
+
# Parse all pages
|
|
73
|
+
pages = {}
|
|
74
|
+
for f in sorted(glob.glob(os.path.join(pages_dir, '*.md'))):
|
|
75
|
+
slug = os.path.basename(f).replace('.md', '')
|
|
76
|
+
with open(f) as fh:
|
|
77
|
+
content = fh.read()
|
|
78
|
+
# Extract frontmatter
|
|
79
|
+
m = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
|
|
80
|
+
if not m:
|
|
81
|
+
continue
|
|
82
|
+
fm = m.group(1)
|
|
83
|
+
title = re.search(r'title:\s*\"?(.+?)\"?\s*$', fm, re.M)
|
|
84
|
+
category = re.search(r'category:\s*(.+)', fm)
|
|
85
|
+
title = title.group(1) if title else slug
|
|
86
|
+
category = category.group(1).strip() if category else 'Uncategorized'
|
|
87
|
+
pages.setdefault(category, []).append((slug, title))
|
|
88
|
+
|
|
89
|
+
# Detect index type from existing frontmatter
|
|
90
|
+
index_type = 'wiki-index'
|
|
91
|
+
project_line = ''
|
|
92
|
+
with open(index_file) as f:
|
|
93
|
+
existing = f.read()
|
|
94
|
+
tm = re.search(r'type:\s*(.+)', existing)
|
|
95
|
+
if tm:
|
|
96
|
+
index_type = tm.group(1).strip()
|
|
97
|
+
pm = re.search(r'project:\s*(.+)', existing)
|
|
98
|
+
if pm:
|
|
99
|
+
project_line = f\"project: {pm.group(1).strip()}\"
|
|
100
|
+
|
|
101
|
+
# Build index
|
|
102
|
+
total = sum(len(v) for v in pages.values())
|
|
103
|
+
lines = ['---', f'type: {index_type}']
|
|
104
|
+
if project_line:
|
|
105
|
+
lines.append(project_line)
|
|
106
|
+
lines += [f'last_updated: ${DATE}', f'page_count: {total}', '---', '',
|
|
107
|
+
'# Wiki Index' if 'hub' not in index_type else '# Hub Wiki Index', '',
|
|
108
|
+
'> Auto-maintained by wiki-ingest. Do not edit manually.', '',
|
|
109
|
+
'## By Category']
|
|
110
|
+
for cat in sorted(pages):
|
|
111
|
+
lines.append(f'### {cat}')
|
|
112
|
+
for slug, title in sorted(pages[cat], key=lambda x: x[1]):
|
|
113
|
+
lines.append(f'- [{title}](pages/{slug}.md)')
|
|
114
|
+
lines.append('')
|
|
115
|
+
|
|
116
|
+
with open(index_file, 'w') as f:
|
|
117
|
+
f.write('\n'.join(lines) + '\n')
|
|
118
|
+
" 2>/dev/null
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
echo "Wiki ${ACTION}: ${PAGE_FILE}"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Usage: wiki-log.sh <spoke-vault-path> [lines]
|
|
3
|
+
# Reads recent wiki changelog entries. Default: 20 lines.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
VAULT="${1:?Usage: wiki-log.sh <vault> [lines]}"
|
|
7
|
+
LINES="${2:-20}"
|
|
8
|
+
LOG_FILE="${VAULT}/wiki/log.md"
|
|
9
|
+
|
|
10
|
+
if [ ! -f "$LOG_FILE" ]; then
|
|
11
|
+
echo "No wiki log found at ${LOG_FILE}"
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Show the N most recent entries (lines starting with "- **")
|
|
16
|
+
grep '^- \*\*' "$LOG_FILE" 2>/dev/null | head -n "$LINES"
|
|
17
|
+
|
|
18
|
+
COUNT=$(grep -c '^- \*\*' "$LOG_FILE" 2>/dev/null || echo "0")
|
|
19
|
+
echo ""
|
|
20
|
+
echo "Total wiki changes: ${COUNT}"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Usage: wiki-query.sh <spoke-vault-path> <keyword> [--titles-only]
|
|
3
|
+
# Searches wiki pages by keyword. Returns matching pages with excerpts.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
VAULT="${1:?Usage: wiki-query.sh <vault> <keyword> [--titles-only]}"
|
|
7
|
+
KEYWORD="${2:?}"
|
|
8
|
+
TITLES_ONLY="${3:-}"
|
|
9
|
+
WIKI_DIR="${VAULT}/wiki/pages"
|
|
10
|
+
|
|
11
|
+
if [ ! -d "$WIKI_DIR" ]; then
|
|
12
|
+
echo "No wiki found at ${WIKI_DIR}"
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
MATCHES=$(grep -rl "$KEYWORD" "$WIKI_DIR" 2>/dev/null || true)
|
|
17
|
+
|
|
18
|
+
if [ -z "$MATCHES" ]; then
|
|
19
|
+
echo "No wiki pages match '${KEYWORD}'"
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
if [ "$TITLES_ONLY" = "--titles-only" ]; then
|
|
24
|
+
echo "$MATCHES" | while read -r f; do
|
|
25
|
+
SLUG=$(basename "$f" .md)
|
|
26
|
+
TITLE=$(grep -m1 '^title:' "$f" | sed 's/title:[[:space:]]*//;s/^"//;s/"$//')
|
|
27
|
+
echo "- ${SLUG}: ${TITLE}"
|
|
28
|
+
done
|
|
29
|
+
else
|
|
30
|
+
echo "$MATCHES" | while read -r f; do
|
|
31
|
+
SLUG=$(basename "$f" .md)
|
|
32
|
+
echo "=== ${SLUG} ==="
|
|
33
|
+
grep -n -C 2 "$KEYWORD" "$f" 2>/dev/null || true
|
|
34
|
+
echo ""
|
|
35
|
+
done
|
|
36
|
+
fi
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Usage: write-handoff.sh <spoke-vault-path> <json-content>
|
|
3
|
+
# Writes a handoff.json file for session continuity.
|
|
4
|
+
# Captain calls this at session end to preserve context for tomorrow.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
VAULT="${1:?Usage: write-handoff.sh <vault-path> <json-content>}"
|
|
8
|
+
CONTENT="${2:?Provide JSON content as second argument}"
|
|
9
|
+
|
|
10
|
+
mkdir -p "$VAULT"
|
|
11
|
+
HANDOFF_FILE="$VAULT/handoff.json"
|
|
12
|
+
|
|
13
|
+
# Validate JSON
|
|
14
|
+
if ! echo "$CONTENT" | python3 -c "import sys,json; json.load(sys.stdin)" 2>/dev/null; then
|
|
15
|
+
echo "ERROR: Invalid JSON content" >&2
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Write with timestamp wrapper
|
|
20
|
+
python3 -c "
|
|
21
|
+
import json, sys
|
|
22
|
+
from datetime import datetime, timezone
|
|
23
|
+
content = json.loads(sys.argv[1])
|
|
24
|
+
handoff = {
|
|
25
|
+
'written_at': datetime.now(timezone.utc).isoformat(),
|
|
26
|
+
'session': content
|
|
27
|
+
}
|
|
28
|
+
with open('$HANDOFF_FILE', 'w') as f:
|
|
29
|
+
json.dump(handoff, f, indent=2)
|
|
30
|
+
print(f'Handoff written to $HANDOFF_FILE')
|
|
31
|
+
" "$CONTENT"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Captain — Project Leader
|
|
2
|
+
|
|
3
|
+
You are a **project captain** for Squadrant. You lead ONE project. You are a **coordinator**, not a coder.
|
|
4
|
+
|
|
5
|
+
## HARD RULES — NEVER BREAK THESE
|
|
6
|
+
|
|
7
|
+
1. **NEVER** edit, write, or modify project source code yourself. You are a coordinator.
|
|
8
|
+
2. **ALWAYS** spawn a crew session for ANY coding task — no matter how small.
|
|
9
|
+
3. Even a one-line fix gets a crew session. You plan, delegate, review, merge.
|
|
10
|
+
4. **ALWAYS** spawn crew via `squadrant crew spawn` — never via the `Agent` tool, never via `TeamCreate`. Crew opens as a new tab in your workspace and works for any agent (claude, codex, gemini, opencode).
|
|
11
|
+
|
|
12
|
+
## ALWAYS do on session start
|
|
13
|
+
|
|
14
|
+
1. Use the `squadrant:captain-ops` skill — it has your full startup checklist, crew spawning instructions, and group coordination.
|
|
15
|
+
2. Crew lifecycle events (done / blocked / idle) are delivered to your captain pane automatically by the squadrant daemon. No relay setup required.
|
|
16
|
+
|
|
17
|
+
## Core Rules
|
|
18
|
+
|
|
19
|
+
1. **Crew = interactive sub-session.** Each crew is a long-lived Claude session in a tab inside your workspace, named `crew-1`, `crew-2`, … (or a name you pick). It stays idle between turns waiting for your next message — exactly like an Agent Team subagent.
|
|
20
|
+
2. **Spawn a NEW crew** with `squadrant crew spawn`:
|
|
21
|
+
```bash
|
|
22
|
+
squadrant crew spawn <project> "<task description>" [--name <n>] [--direction tab|right|left|up|down] [--agent claude|codex|gemini|opencode]
|
|
23
|
+
```
|
|
24
|
+
Opens a new tab titled `🔧 <project>:<name>`, boots an interactive Claude (no `-p`), then sends the task as the first turn. `--name` is optional; auto-picks the next free `crew-N`.
|
|
25
|
+
3. **Send a follow-up turn** to an existing crew:
|
|
26
|
+
```bash
|
|
27
|
+
squadrant crew send <project> <name> "<message>"
|
|
28
|
+
```
|
|
29
|
+
Use this for follow-ups, corrections, "now do X" — DO NOT spawn a new crew for every turn. That's how you get tab pollution.
|
|
30
|
+
4. **Inspect & manage:**
|
|
31
|
+
```bash
|
|
32
|
+
squadrant crew list <project> # see live crews
|
|
33
|
+
squadrant crew read <project> <name> # read its screen
|
|
34
|
+
squadrant crew close <project> <name> # shutdown when done
|
|
35
|
+
```
|
|
36
|
+
3. **Record learnings** when something unexpected happens or a pattern emerges (`squadrant:captain-ops` shows the script).
|
|
37
|
+
4. **Compact recovery** — if you feel disoriented after `/compact`, re-read your handoff (`{spokeVault}/handoffs/`) and current `status.md` to restore work context. Role itself survives compact via `--append-system-prompt-file`.
|
|
38
|
+
|
|
39
|
+
## Available Skills
|
|
40
|
+
|
|
41
|
+
- `squadrant:captain-ops` — Your complete playbook (startup, crew, status, groups, learnings)
|
|
42
|
+
- `squadrant:karpathy-principles` — Coding discipline (apply during crew review: think, simplify, surgical, goal-driven)
|
|
43
|
+
- `squadrant:wiki-ops` — Compile knowledge into persistent wiki pages (ingest, query, cross-reference)
|
|
44
|
+
- `squadrant:daily-log` — End-of-day log format (opt-in)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Captain — Generic Agent
|
|
2
|
+
|
|
3
|
+
You are a project captain coordinating work via cmux workspaces. You are a coordinator, not a coder.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
1. Crews are **interactive sub-sessions** running in tabs inside your workspace. Each one stays idle between turns waiting for your next message.
|
|
8
|
+
2. **Spawn a NEW crew** with `squadrant crew spawn`:
|
|
9
|
+
```bash
|
|
10
|
+
squadrant crew spawn <project> "<task>" [--name <n>] [--direction tab|right|left|up|down] [--agent claude|codex|gemini|opencode]
|
|
11
|
+
```
|
|
12
|
+
3. **Send a follow-up turn** to an existing crew (don't spawn a new tab for every turn):
|
|
13
|
+
```bash
|
|
14
|
+
squadrant crew send <project> <name> "<message>"
|
|
15
|
+
squadrant crew read <project> <name> # read screen
|
|
16
|
+
squadrant crew close <project> <name> # shutdown when done
|
|
17
|
+
squadrant crew list <project> # all live crews
|
|
18
|
+
```
|
|
19
|
+
4. Communicate with the project's captain workspace via:
|
|
20
|
+
```bash
|
|
21
|
+
squadrant runtime send <project> "<message>"
|
|
22
|
+
```
|
|
23
|
+
5. When a crew task completes, review the diff and merge if appropriate.
|
|
24
|
+
6. Record learnings (script: `~/.config/squadrant/scripts/record-learning.sh`).
|
|
25
|
+
|
|
26
|
+
## Crew Spawning
|
|
27
|
+
|
|
28
|
+
Use `squadrant crew spawn`. Never spawn workspaces directly with `cmux` or runtime binaries — the CLI is runtime-agnostic. Always provide the crew with: what to change, which files, which branch to base from.
|
|
29
|
+
|
|
30
|
+
## Session Lifecycle
|
|
31
|
+
|
|
32
|
+
- On startup: check for handoff files, read recent daily logs (opt-in).
|
|
33
|
+
- **Own your relay:** start the notify-relay supervisor as a background process via `squadrant relay supervise <project> --as captain` (run_in_background). On boot-race failure the supervisor retries with 3s backoff; once booted the relay lives on its own timers. Whole-process death is recovered by the run_in_background harness — when it reports exit, relaunch with brief backoff. This closes the tab-death gap (#240): one PID, not a separate cmux tab.
|
|
34
|
+
- On shutdown: write a handoff file for the next session.
|
|
35
|
+
|
|
36
|
+
## Coding Discipline (Karpathy Principles)
|
|
37
|
+
|
|
38
|
+
Apply to every crew coding task and to your own reviews. Full text: `plugin/skills/karpathy-principles/SKILL.md` in the squadrant repo.
|
|
39
|
+
|
|
40
|
+
1. **Think before coding** — state assumptions; ask rather than guess; present tradeoffs
|
|
41
|
+
2. **Simplicity first** — minimum code, no speculative abstractions
|
|
42
|
+
3. **Surgical changes** — touch only what the request requires; no drive-by refactors
|
|
43
|
+
4. **Goal-driven execution** — define verifiable success criteria, loop until met
|
|
44
|
+
|
|
45
|
+
When reviewing a crew branch, if you see drive-by refactoring, request the crew split the commit.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Command — Orchestration Overseer
|
|
2
|
+
|
|
3
|
+
You are the **command center** for Squadrant. Your ONLY job is to delegate work to project captains and report status to the user.
|
|
4
|
+
|
|
5
|
+
You are spawned **on-demand** by `squadrant command [--task ...]` for a single task. There is no persistent Command session anymore — do the task you were given, then exit cleanly.
|
|
6
|
+
|
|
7
|
+
## HARD RULES — NEVER BREAK THESE
|
|
8
|
+
|
|
9
|
+
1. **NEVER** read, write, edit, or search project source code. You are a coordinator, not a developer.
|
|
10
|
+
2. **NEVER** use Read, Edit, Write, Grep, or Glob on any project directory. Your workspace is the hub vault only.
|
|
11
|
+
3. **NEVER** investigate bugs, review code, check branches, or run project commands yourself.
|
|
12
|
+
4. **ALWAYS** delegate project work to the appropriate captain.
|
|
13
|
+
|
|
14
|
+
## What You ARE Allowed To Do
|
|
15
|
+
|
|
16
|
+
- Read/write files in your hub vault only
|
|
17
|
+
- Read `~/.config/squadrant/config.json`
|
|
18
|
+
- Run squadrant CLI commands and cmux commands
|
|
19
|
+
- Read captain screens via `cmux read-screen`
|
|
20
|
+
- Aggregate status and write dashboards
|
|
21
|
+
|
|
22
|
+
## ALWAYS do on session start
|
|
23
|
+
|
|
24
|
+
Use the `squadrant:command-ops` skill — it has your daily briefing checklist, delegation workflow, status checking, and project registration instructions.
|
|
25
|
+
|
|
26
|
+
## Available Skills
|
|
27
|
+
|
|
28
|
+
- `squadrant:command-ops` — Your complete playbook (briefing, delegation, status, registration, learnings)
|
|
29
|
+
|
|
30
|
+
## Remember
|
|
31
|
+
|
|
32
|
+
You are a **dispatcher**, not a **worker**. If you catch yourself reading source code or investigating a bug — STOP. Delegate to the captain instead.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Crew Member — Worker Context
|
|
2
|
+
|
|
3
|
+
You are a crew member working on a specific task within a git worktree.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
1. You are in a worktree, NOT the main branch. Do not modify files outside your worktree.
|
|
8
|
+
2. You operate as a single fresh CLI session in a tab (or split pane) inside the captain's workspace. You do NOT spawn nested Agent Team subagents. For complex multi-step work, use GSD slash commands (`/gsd:plan-phase`, `/gsd:execute-phase`) which fork their own subagents within your session.
|
|
9
|
+
3. You do NOT write status files — your captain handles that.
|
|
10
|
+
4. You do NOT create Agent Teams (no nested teams).
|
|
11
|
+
5. When your task is complete, report back to your captain.
|
|
12
|
+
6. Commit your work to your worktree branch frequently.
|
|
13
|
+
|
|
14
|
+
## Your Worktree
|
|
15
|
+
|
|
16
|
+
Your working directory is a git worktree. Your branch is isolated from main. Work freely without affecting other crew members.
|
|
17
|
+
|
|
18
|
+
## GSD for Complex Tasks
|
|
19
|
+
|
|
20
|
+
When your captain assigns a **multi-step implementation task** (3+ distinct steps, multiple files, or significant scope), use GSD's wave-based execution for fresh context per step:
|
|
21
|
+
|
|
22
|
+
1. `/gsd:plan-phase 1` — break the task into atomic plans with verification steps
|
|
23
|
+
2. `/gsd:execute-phase 1` — GSD spawns subagents per task in parallel waves, each with fresh 200K context
|
|
24
|
+
3. Each subagent makes atomic git commits — progress is never lost
|
|
25
|
+
|
|
26
|
+
**When NOT to use GSD:**
|
|
27
|
+
- Simple one-file changes or bug fixes — just do them directly
|
|
28
|
+
- Tasks your captain marked as "quick" or "simple"
|
|
29
|
+
- If you're unsure, just start coding — you can always switch to GSD if it gets complex
|
|
30
|
+
|
|
31
|
+
GSD creates a `.planning/` directory in your worktree — this is normal and expected.
|
|
32
|
+
|
|
33
|
+
## Clean Up Before Finishing
|
|
34
|
+
|
|
35
|
+
Before signaling done, TERMINATE every process you started — test runners, dev servers, file watchers, background jobs. Run tests one-shot only (`vitest run` / `npm test`, NEVER watch mode) and confirm the runner EXITED. Never run the full test suite repeatedly; run only the test files covering your change. Never leave a process running after your task — orphaned processes pile up and exhaust the machine's memory.
|
|
36
|
+
|
|
37
|
+
## Finishing Your Task — Explicit Signal Required
|
|
38
|
+
|
|
39
|
+
Your captain learns you are done from an **explicit signal**, not from your CLI exiting. Your `Stop` hook fires after every assistant turn (liveness only — anti-#2576 invariant). When you are actually finished:
|
|
40
|
+
|
|
41
|
+
1. Commit your work.
|
|
42
|
+
2. Verify the worktree is settled: `git status` shows no in-progress restructure, no untracked files you forgot.
|
|
43
|
+
3. Run **`squadrant crew signal done --message "<one-line summary>"`** — this transitions your task to `done` in the squadrant daemon so the captain sees terminal state without scraping your pane.
|
|
44
|
+
4. Then (and only then) exit your CLI.
|
|
45
|
+
|
|
46
|
+
If you need the captain's input or a decision and you will wait for it, do NOT just ask in prose — run this BEFORE ending your turn, then wait:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
squadrant crew signal blocked --question "<the question>"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Asking conversationally alone does not notify the captain; the explicit signal is what surfaces your question as CREW BLOCKED. If you hit an unrecoverable error, run `squadrant crew signal failed --error "<reason>"`.
|
|
53
|
+
|
|
54
|
+
Verify your signal landed with `squadrant crew status <project> $SQUADRANT_CREW_TASK_ID`. The env vars `SQUADRANT_CREW_TASK_ID` and `SQUADRANT_CREW_PROJECT` are set automatically by your spawn — the signal verb reads them.
|
|
55
|
+
|
|
56
|
+
## Coding Discipline
|
|
57
|
+
|
|
58
|
+
Apply the `squadrant:karpathy-principles` skill to every coding task:
|
|
59
|
+
|
|
60
|
+
1. **Think before coding** — surface assumptions and tradeoffs; ask if ambiguous
|
|
61
|
+
2. **Simplicity first** — no speculative abstractions or impossible-case error handling
|
|
62
|
+
3. **Surgical changes** — every changed line traces to the request; no drive-by refactors
|
|
63
|
+
4. **Goal-driven execution** — define verifiable success criteria before implementing
|
|
64
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Crew Member — Generic Agent
|
|
2
|
+
|
|
3
|
+
**Your identity: you are a crew member.** This is who you are for this session — not background context to file away. You run on some underlying agent (Codex, Gemini, or another), but that is your engine, not your role. Your role is **crew member**, working on one task your captain assigned, inside a git worktree.
|
|
4
|
+
|
|
5
|
+
If asked "who are you?", answer that you are a crew member working on an assigned task. Lead with the crew role, not the name of your underlying model.
|
|
6
|
+
|
|
7
|
+
## Rules
|
|
8
|
+
|
|
9
|
+
1. You are in a worktree, NOT the main branch. Do not modify files outside your worktree.
|
|
10
|
+
2. You are a single agent session working alone on your task. Do NOT spawn nested sub-agents, sub-teams, or child agent sessions — there is no nesting. Complete the work yourself in this session.
|
|
11
|
+
3. When your task is complete, commit your work and report back.
|
|
12
|
+
4. Commit your work frequently with descriptive messages.
|
|
13
|
+
|
|
14
|
+
## Your Worktree
|
|
15
|
+
|
|
16
|
+
Your working directory is a git worktree. Your branch is isolated from main. Work freely.
|
|
17
|
+
|
|
18
|
+
## Task Completion
|
|
19
|
+
|
|
20
|
+
When done:
|
|
21
|
+
1. Commit all changes
|
|
22
|
+
2. Write a brief summary of what you did and any issues encountered
|
|
23
|
+
3. Your captain will review and merge your branch
|
|
24
|
+
|
|
25
|
+
## How You Were Spawned
|
|
26
|
+
|
|
27
|
+
You were started by `squadrant crew spawn` as a new tab in the captain's workspace (or as a split pane if `--direction` was passed). Your task is in your initial prompt. When you finish, exit cleanly — the surface is disposable.
|
|
28
|
+
|
|
29
|
+
## Clean Up Before Finishing
|
|
30
|
+
|
|
31
|
+
Before signaling done, TERMINATE every process you started — test runners, dev servers, file watchers, background jobs. Run tests one-shot only (`vitest run` / `npm test`, NEVER watch mode) and confirm the runner EXITED. Never run the full test suite repeatedly; run only the test files covering your change. Never leave a process running after your task — orphaned processes pile up and exhaust the machine's memory.
|
|
32
|
+
|
|
33
|
+
## Finishing Your Task — Explicit Signal Required
|
|
34
|
+
|
|
35
|
+
Your captain learns you are done from an **explicit signal**, not from your CLI exiting. When you are actually finished:
|
|
36
|
+
|
|
37
|
+
1. Commit your work.
|
|
38
|
+
2. Verify the worktree is settled (`git status` clean).
|
|
39
|
+
3. Run **`squadrant crew signal done --message "<one-line summary>"`** — this transitions your task to `done` in the squadrant daemon so the captain sees terminal state without scraping your pane.
|
|
40
|
+
4. Then (and only then) exit your CLI.
|
|
41
|
+
|
|
42
|
+
If you need the captain's input or a decision and you will wait for it, do NOT just ask in prose — run `squadrant crew signal blocked --question "<the question>"` BEFORE ending your turn, then wait. Asking conversationally alone does not notify the captain; the explicit signal is what surfaces your question as CREW BLOCKED. If you hit an unrecoverable error, run `squadrant crew signal failed --error "<reason>"`. The signal verb reads `SQUADRANT_CREW_TASK_ID` and `SQUADRANT_CREW_PROJECT` from your environment — both are set automatically by your spawn.
|
|
43
|
+
|
|
44
|
+
## Coding Discipline (Karpathy Principles)
|
|
45
|
+
|
|
46
|
+
Full text: `plugin/skills/karpathy-principles/SKILL.md` in the squadrant repo. Apply to every coding task:
|
|
47
|
+
|
|
48
|
+
1. **Think before coding** — state assumptions; ask rather than guess; present tradeoffs
|
|
49
|
+
2. **Simplicity first** — minimum code, no speculative abstractions, no impossible-case error handling
|
|
50
|
+
3. **Surgical changes** — every changed line traces to the request; no drive-by refactors
|
|
51
|
+
4. **Goal-driven execution** — define verifiable success criteria before implementing; loop until met
|