@firatcand/roster 0.4.0 → 1.0.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/README.md +77 -220
- package/agents/lesson-drafter.md +3 -8
- package/agents/pattern-detector.md +0 -1
- package/bin/roster.js +168 -57
- package/package.json +2 -3
- package/skills/chief-of-staff/SKILL.md +62 -78
- package/skills/dreamer/SKILL.md +8 -7
- package/skills/roster-orchestrator/SKILL.md +53 -25
- package/templates/CLAUDE.project.template.md +1 -1
- package/templates/CONTEXT.template.md +2 -2
- package/templates/gitignore-defaults.txt +2 -0
- package/templates/scaffold/chief-of-staff/README.md +16 -24
- package/templates/scaffold/chief-of-staff/agent.md +22 -32
- package/templates/scaffold/chief-of-staff/plans/audit-agent.yaml +4 -4
- package/templates/scaffold/chief-of-staff/plans/audit-repo.yaml +5 -4
- package/templates/scaffold/chief-of-staff/plans/create-agent.yaml +5 -34
- package/templates/scaffold/config/project.yaml.template +10 -0
- package/templates/scaffold/conventions.md +159 -171
- package/templates/scaffold/dreamer/README.md +2 -2
- package/templates/scaffold/dreamer/agent.md +0 -1
- package/templates/scaffold/dreamer/plans/nightly-reflection.yaml +23 -37
- package/templates/scaffold/dreamer/subagents/lesson-drafter.md +2 -7
- package/templates/scaffold/{projects/_demo/guidelines → guidelines}/asset-links.md +4 -0
- package/templates/scaffold/{projects/_demo/guidelines → guidelines}/brand-book.md +4 -0
- package/templates/scaffold/{projects/_demo/guidelines → guidelines}/messaging.md +4 -0
- package/templates/scaffold/{projects/_demo/guidelines → guidelines}/voice.md +4 -0
- package/templates/scaffold/scripts/audit-agent.sh +74 -47
- package/templates/scaffold/scripts/audit-repo.sh +27 -49
- package/templates/scaffold/scripts/create-function.sh +1 -1
- package/templates/scaffold/scripts/lib/README.md +1 -1
- package/templates/scaffold/scripts/lib/bindings-prompt.sh +41 -124
- package/templates/scaffold/scripts/new-agent.sh +97 -91
- package/templates/scaffold/scripts/rename-agent.sh +91 -0
- package/templates/scaffold/scripts/save-state.sh +32 -0
- package/agents/critic.md +0 -74
- package/agents/enricher.md +0 -56
- package/agents/promotion-arbiter.md +0 -71
- package/agents/prospector.md +0 -51
- package/agents/writer.md +0 -58
- package/skills/sdr/SKILL.md +0 -147
- package/templates/scaffold/chief-of-staff/plans/add-agent-to-project.yaml +0 -45
- package/templates/scaffold/chief-of-staff/plans/archive-project.yaml +0 -51
- package/templates/scaffold/chief-of-staff/plans/audit-project.yaml +0 -34
- package/templates/scaffold/chief-of-staff/plans/create-project.yaml +0 -65
- package/templates/scaffold/chief-of-staff/plans/remove-agent-from-project.yaml +0 -50
- package/templates/scaffold/chief-of-staff/plans/rename-project.yaml +0 -62
- package/templates/scaffold/chief-of-staff/plans/unarchive-project.yaml +0 -41
- package/templates/scaffold/dreamer/subagents/promotion-arbiter.md +0 -64
- package/templates/scaffold/gtm/sdr/.claude/settings.json +0 -3
- package/templates/scaffold/gtm/sdr/.mcp.json +0 -21
- package/templates/scaffold/gtm/sdr/README.md +0 -41
- package/templates/scaffold/gtm/sdr/agent.md +0 -136
- package/templates/scaffold/gtm/sdr/plans/cold-outreach.yaml +0 -92
- package/templates/scaffold/gtm/sdr/projects/_demo/asset-references.md +0 -7
- package/templates/scaffold/gtm/sdr/projects/_demo/config/default.yaml +0 -69
- package/templates/scaffold/gtm/sdr/projects/_demo/log/feedback/.gitkeep +0 -0
- package/templates/scaffold/gtm/sdr/projects/_demo/log/runs/.gitkeep +0 -0
- package/templates/scaffold/gtm/sdr/projects/_demo/playbook/.gitkeep +0 -0
- package/templates/scaffold/gtm/sdr/subagents/critic.md +0 -67
- package/templates/scaffold/gtm/sdr/subagents/enricher.md +0 -49
- package/templates/scaffold/gtm/sdr/subagents/prospector.md +0 -44
- package/templates/scaffold/gtm/sdr/subagents/writer.md +0 -51
- package/templates/scaffold/projects/_demo/CLAUDE.md +0 -35
- package/templates/scaffold/projects/_demo/README.md +0 -16
- package/templates/scaffold/projects/_demo/assets/.gitkeep +0 -0
- package/templates/scaffold/projects/_demo/config/default.yaml +0 -28
- package/templates/scaffold/projects/_demo/state.md +0 -11
- package/templates/scaffold/scripts/archive-project.sh +0 -98
- package/templates/scaffold/scripts/audit-project.sh +0 -361
- package/templates/scaffold/scripts/new-agent-instance.sh +0 -114
- package/templates/scaffold/scripts/new-project.sh +0 -125
- package/templates/scaffold/scripts/remove-agent-from-project.sh +0 -67
- package/templates/scaffold/scripts/rename-project.sh +0 -118
- package/templates/scaffold/scripts/unarchive-project.sh +0 -115
- /package/templates/scaffold/gtm/{sdr/playbook/.gitkeep → .gitkeep} +0 -0
- /package/templates/scaffold/{projects/_demo/guidelines → guidelines}/icps/_persona-template.md +0 -0
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# audit-project.sh — checks project completeness, reports issues with suggested fixes
|
|
3
|
-
# Usage: bash scripts/audit-project.sh <project>
|
|
4
|
-
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
if [ $# -ne 1 ]; then
|
|
8
|
-
echo "Usage: $0 <project>"
|
|
9
|
-
exit 1
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
PROJECT="$1"
|
|
13
|
-
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
14
|
-
PROJECT_DIR="$ROOT/projects/$PROJECT"
|
|
15
|
-
|
|
16
|
-
if [ ! -d "$PROJECT_DIR" ]; then
|
|
17
|
-
echo "ERROR: Project '$PROJECT' not found at $PROJECT_DIR"
|
|
18
|
-
exit 1
|
|
19
|
-
fi
|
|
20
|
-
|
|
21
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
22
|
-
RUN_TIME=$(date +%Y-%m-%d-%H%M)
|
|
23
|
-
LOG_DIR="$ROOT/chief-of-staff/logs/$(date +%Y-%m)"
|
|
24
|
-
mkdir -p "$LOG_DIR"
|
|
25
|
-
REPORT="$LOG_DIR/audit-$PROJECT-$RUN_TIME.md"
|
|
26
|
-
|
|
27
|
-
# Buffers
|
|
28
|
-
FAILURES=()
|
|
29
|
-
WARNINGS=()
|
|
30
|
-
PASSED=()
|
|
31
|
-
|
|
32
|
-
# Check helper: file exists and isn't template content
|
|
33
|
-
is_template() {
|
|
34
|
-
local file="$1"
|
|
35
|
-
# Heuristic: contains a placeholder pattern like <something descriptive>
|
|
36
|
-
# We use the simple heuristic of looking for "<3 adjectives" or "<list>" — common template strings
|
|
37
|
-
if grep -qE '<[a-z0-9 ,/.:-]+>' "$file" 2>/dev/null; then
|
|
38
|
-
return 0
|
|
39
|
-
fi
|
|
40
|
-
return 1
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
check_required_guideline() {
|
|
44
|
-
local file="$1"
|
|
45
|
-
local rel="${file#$ROOT/}"
|
|
46
|
-
if [ ! -f "$file" ]; then
|
|
47
|
-
FAILURES+=("[$rel] required file missing")
|
|
48
|
-
FAILURES+=(" → Suggested fix: copy from projects/_template/${rel#projects/$PROJECT/}")
|
|
49
|
-
return
|
|
50
|
-
fi
|
|
51
|
-
if is_template "$file"; then
|
|
52
|
-
FAILURES+=("[$rel] still contains template placeholders (e.g., <list>, <3 adjectives>)")
|
|
53
|
-
FAILURES+=(" → Suggested fix: edit $rel to replace all <placeholder> markers with real content")
|
|
54
|
-
return
|
|
55
|
-
fi
|
|
56
|
-
PASSED+=("[$rel] OK (filled in)")
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
check_optional_guideline() {
|
|
60
|
-
local file="$1"
|
|
61
|
-
local rel="${file#$ROOT/}"
|
|
62
|
-
if [ ! -f "$file" ]; then
|
|
63
|
-
WARNINGS+=("[$rel] optional file missing")
|
|
64
|
-
WARNINGS+=(" → Suggested fix: copy from projects/_template/${rel#projects/$PROJECT/} if needed")
|
|
65
|
-
return
|
|
66
|
-
fi
|
|
67
|
-
PASSED+=("[$rel] present")
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
# === Required guidelines ===
|
|
71
|
-
check_required_guideline "$PROJECT_DIR/guidelines/voice.md"
|
|
72
|
-
check_required_guideline "$PROJECT_DIR/guidelines/design.md"
|
|
73
|
-
check_required_guideline "$PROJECT_DIR/guidelines/design-tokens.md"
|
|
74
|
-
check_required_guideline "$PROJECT_DIR/guidelines/brand-book.md"
|
|
75
|
-
check_required_guideline "$PROJECT_DIR/guidelines/messaging.md"
|
|
76
|
-
check_required_guideline "$PROJECT_DIR/guidelines/asset-links.md"
|
|
77
|
-
|
|
78
|
-
# ICPs: at least one non-template file
|
|
79
|
-
if [ ! -d "$PROJECT_DIR/guidelines/icps" ]; then
|
|
80
|
-
FAILURES+=("[projects/$PROJECT/guidelines/icps/] directory missing")
|
|
81
|
-
FAILURES+=(" → Suggested fix: mkdir -p $PROJECT_DIR/guidelines/icps && cp projects/_template/guidelines/icps/_persona-template.md $PROJECT_DIR/guidelines/icps/")
|
|
82
|
-
else
|
|
83
|
-
ICP_COUNT=$(find "$PROJECT_DIR/guidelines/icps" -type f -name '*.md' -not -name '_persona-template.md' | wc -l)
|
|
84
|
-
if [ "$ICP_COUNT" -eq 0 ]; then
|
|
85
|
-
FAILURES+=("[projects/$PROJECT/guidelines/icps/] no persona file (only _persona-template.md or empty)")
|
|
86
|
-
FAILURES+=(" → Suggested fix: cp projects/_template/guidelines/icps/_persona-template.md $PROJECT_DIR/guidelines/icps/<persona-slug>.md and fill in")
|
|
87
|
-
else
|
|
88
|
-
PASSED+=("[projects/$PROJECT/guidelines/icps/] $ICP_COUNT persona file(s)")
|
|
89
|
-
fi
|
|
90
|
-
fi
|
|
91
|
-
|
|
92
|
-
# === Optional guidelines ===
|
|
93
|
-
check_optional_guideline "$PROJECT_DIR/guidelines/do-and-dont.md"
|
|
94
|
-
check_optional_guideline "$PROJECT_DIR/guidelines/compliance.md"
|
|
95
|
-
check_optional_guideline "$PROJECT_DIR/guidelines/competitors.md"
|
|
96
|
-
|
|
97
|
-
# === Project root files ===
|
|
98
|
-
if [ -f "$PROJECT_DIR/CLAUDE.md" ]; then
|
|
99
|
-
if grep -q '<Project Name>' "$PROJECT_DIR/CLAUDE.md" 2>/dev/null; then
|
|
100
|
-
FAILURES+=("[projects/$PROJECT/CLAUDE.md] still contains <Project Name> placeholder")
|
|
101
|
-
FAILURES+=(" → Suggested fix: edit projects/$PROJECT/CLAUDE.md and fill in identity, audience, agents")
|
|
102
|
-
else
|
|
103
|
-
PASSED+=("[projects/$PROJECT/CLAUDE.md] filled in")
|
|
104
|
-
fi
|
|
105
|
-
else
|
|
106
|
-
FAILURES+=("[projects/$PROJECT/CLAUDE.md] missing")
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
if [ -f "$PROJECT_DIR/state.md" ]; then
|
|
110
|
-
PASSED+=("[projects/$PROJECT/state.md] present")
|
|
111
|
-
else
|
|
112
|
-
WARNINGS+=("[projects/$PROJECT/state.md] missing")
|
|
113
|
-
WARNINGS+=(" → Suggested fix: cp projects/_template/state.md $PROJECT_DIR/state.md")
|
|
114
|
-
fi
|
|
115
|
-
|
|
116
|
-
# === Agent instances ===
|
|
117
|
-
INSTANCES=()
|
|
118
|
-
while IFS= read -r path; do
|
|
119
|
-
INSTANCES+=("$path")
|
|
120
|
-
done < <(find "$ROOT" -type d -path "*/projects/$PROJECT" -not -path "*/_template/*" -not -path "*/_archive/*" 2>/dev/null | grep -v "^$PROJECT_DIR$" || true)
|
|
121
|
-
|
|
122
|
-
LISTED_INSTANCES=()
|
|
123
|
-
if [ -f "$PROJECT_DIR/CLAUDE.md" ]; then
|
|
124
|
-
while IFS= read -r line; do
|
|
125
|
-
LISTED_INSTANCES+=("$line")
|
|
126
|
-
done < <(grep -oE '`[a-z0-9-]+/[a-z0-9-]+/projects/[a-z0-9-]+/`' "$PROJECT_DIR/CLAUDE.md" 2>/dev/null | tr -d '`' || true)
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
for inst in "${INSTANCES[@]}"; do
|
|
130
|
-
REL="${inst#$ROOT/}"
|
|
131
|
-
|
|
132
|
-
# config/default.yaml
|
|
133
|
-
CONFIG="$inst/config/default.yaml"
|
|
134
|
-
if [ ! -f "$CONFIG" ]; then
|
|
135
|
-
FAILURES+=("[$REL/config/default.yaml] missing")
|
|
136
|
-
else
|
|
137
|
-
# Try YAML parse (use python3 if available, else just check file is non-empty)
|
|
138
|
-
if command -v python3 >/dev/null 2>&1; then
|
|
139
|
-
if ! python3 -c "import yaml; list(yaml.safe_load_all(open('$CONFIG')))" 2>/dev/null; then
|
|
140
|
-
FAILURES+=("[$REL/config/default.yaml] YAML parse error")
|
|
141
|
-
FAILURES+=(" → Suggested fix: check YAML syntax with: python3 -c 'import yaml; list(yaml.safe_load_all(open(\"$CONFIG\"))'")
|
|
142
|
-
else
|
|
143
|
-
# Check project field matches folder
|
|
144
|
-
DECLARED_PROJECT=$(python3 -c "import yaml; docs = list(yaml.safe_load_all(open('$CONFIG'))); print((docs[0] or {}).get('project', ''))" 2>/dev/null || echo "")
|
|
145
|
-
if [ "$DECLARED_PROJECT" != "$PROJECT" ]; then
|
|
146
|
-
FAILURES+=("[$REL/config/default.yaml] project field is '$DECLARED_PROJECT', expected '$PROJECT'")
|
|
147
|
-
FAILURES+=(" → Suggested fix: edit $REL/config/default.yaml and set 'project: $PROJECT'")
|
|
148
|
-
else
|
|
149
|
-
PASSED+=("[$REL/config/default.yaml] valid YAML, project matches")
|
|
150
|
-
fi
|
|
151
|
-
fi
|
|
152
|
-
else
|
|
153
|
-
PASSED+=("[$REL/config/default.yaml] present (YAML not validated, python3 missing)")
|
|
154
|
-
fi
|
|
155
|
-
fi
|
|
156
|
-
|
|
157
|
-
# asset-references.md
|
|
158
|
-
if [ -f "$inst/asset-references.md" ]; then
|
|
159
|
-
PASSED+=("[$REL/asset-references.md] present")
|
|
160
|
-
else
|
|
161
|
-
WARNINGS+=("[$REL/asset-references.md] missing")
|
|
162
|
-
fi
|
|
163
|
-
|
|
164
|
-
# === Tool bindings: TODO required → fail; TODO optional → warn ===
|
|
165
|
-
# Resolve agent.md from the instance path: $inst is .../<fn>/<agent>/projects/<project>
|
|
166
|
-
INST_PARENT="$(cd "$inst/../.." && pwd)"
|
|
167
|
-
AGENT_MD_FOR_INST="$INST_PARENT/agent.md"
|
|
168
|
-
if [ -f "$CONFIG" ] && [ -f "$AGENT_MD_FOR_INST" ] && command -v python3 >/dev/null 2>&1; then
|
|
169
|
-
BINDING_REPORT=$(CONFIG_PATH="$CONFIG" AGENT_MD_PATH="$AGENT_MD_FOR_INST" REL_PATH="$REL" python3 << 'PYEOF' 2>/dev/null || true
|
|
170
|
-
import os, re, sys
|
|
171
|
-
config_path = os.environ["CONFIG_PATH"]
|
|
172
|
-
agent_md = os.environ["AGENT_MD_PATH"]
|
|
173
|
-
rel = os.environ["REL_PATH"]
|
|
174
|
-
|
|
175
|
-
try:
|
|
176
|
-
import yaml
|
|
177
|
-
except ImportError:
|
|
178
|
-
sys.exit(0)
|
|
179
|
-
|
|
180
|
-
with open(agent_md) as f:
|
|
181
|
-
am = f.read()
|
|
182
|
-
sm = re.search(r'## Tools and bindings.*?\n```yaml\n(.*?)\n```', am, re.DOTALL)
|
|
183
|
-
if not sm:
|
|
184
|
-
sys.exit(0)
|
|
185
|
-
try:
|
|
186
|
-
schema = yaml.safe_load(sm.group(1)) or {}
|
|
187
|
-
except Exception:
|
|
188
|
-
sys.exit(0)
|
|
189
|
-
|
|
190
|
-
with open(config_path) as f:
|
|
191
|
-
raw = f.read()
|
|
192
|
-
|
|
193
|
-
# Walk lines inside the tools: block; capture (tool, key) pairs whose value is TODO.
|
|
194
|
-
in_tools = False
|
|
195
|
-
current_tool = None
|
|
196
|
-
todos = []
|
|
197
|
-
for line in raw.split("\n"):
|
|
198
|
-
if re.match(r'^tools:\s*$', line):
|
|
199
|
-
in_tools = True
|
|
200
|
-
continue
|
|
201
|
-
if not in_tools:
|
|
202
|
-
continue
|
|
203
|
-
if re.match(r'^[A-Za-z]', line):
|
|
204
|
-
# left the tools block (back to top-level key)
|
|
205
|
-
in_tools = False
|
|
206
|
-
current_tool = None
|
|
207
|
-
continue
|
|
208
|
-
tm = re.match(r'^ ([a-z_][a-z0-9_]*):\s*$', line)
|
|
209
|
-
if tm:
|
|
210
|
-
current_tool = tm.group(1)
|
|
211
|
-
continue
|
|
212
|
-
bm = re.match(r'^ ([a-z_][a-z0-9_]*):\s*#\s*TODO\b', line)
|
|
213
|
-
if bm and current_tool:
|
|
214
|
-
todos.append((current_tool, bm.group(1)))
|
|
215
|
-
|
|
216
|
-
for tool, key in todos:
|
|
217
|
-
tool_schema = schema.get(tool, {}) if isinstance(schema, dict) else {}
|
|
218
|
-
key_schema = tool_schema.get(key, {}) if isinstance(tool_schema, dict) else {}
|
|
219
|
-
required = bool(key_schema.get("required", False)) if isinstance(key_schema, dict) else False
|
|
220
|
-
severity = "FAIL" if required else "WARN"
|
|
221
|
-
print(f"{severity}\t{tool}.{key}")
|
|
222
|
-
PYEOF
|
|
223
|
-
)
|
|
224
|
-
while IFS=$'\t' read -r severity binding; do
|
|
225
|
-
[ -z "$severity" ] && continue
|
|
226
|
-
if [ "$severity" = "FAIL" ]; then
|
|
227
|
-
FAILURES+=("[$REL/config/default.yaml] required tool binding '$binding' is TODO")
|
|
228
|
-
FAILURES+=(" → Suggested fix: edit $REL/config/default.yaml and set tools.$binding to a real value")
|
|
229
|
-
else
|
|
230
|
-
WARNINGS+=("[$REL/config/default.yaml] optional tool binding '$binding' is TODO (will be skipped at runtime)")
|
|
231
|
-
fi
|
|
232
|
-
done <<< "$BINDING_REPORT"
|
|
233
|
-
fi
|
|
234
|
-
|
|
235
|
-
# Required directories
|
|
236
|
-
for d in log/runs log/feedback playbook; do
|
|
237
|
-
if [ -d "$inst/$d" ]; then
|
|
238
|
-
PASSED+=("[$REL/$d/] present")
|
|
239
|
-
else
|
|
240
|
-
WARNINGS+=("[$REL/$d/] missing (will be auto-created on first run)")
|
|
241
|
-
fi
|
|
242
|
-
done
|
|
243
|
-
|
|
244
|
-
# Is this instance listed in CLAUDE.md?
|
|
245
|
-
EXPECTED_PATH=$(echo "$REL/" | sed 's|/projects/.*|/projects/'"$PROJECT"'/|')
|
|
246
|
-
FOUND=0
|
|
247
|
-
for listed in "${LISTED_INSTANCES[@]}"; do
|
|
248
|
-
if [ "$listed" = "$EXPECTED_PATH" ]; then
|
|
249
|
-
FOUND=1
|
|
250
|
-
break
|
|
251
|
-
fi
|
|
252
|
-
done
|
|
253
|
-
if [ $FOUND -eq 0 ]; then
|
|
254
|
-
WARNINGS+=("[$REL] instance not listed in projects/$PROJECT/CLAUDE.md ## Active agent instances")
|
|
255
|
-
WARNINGS+=(" → Suggested fix: add a line under '## Active agent instances' in CLAUDE.md")
|
|
256
|
-
fi
|
|
257
|
-
done
|
|
258
|
-
|
|
259
|
-
# Listed instances that don't exist
|
|
260
|
-
for listed in "${LISTED_INSTANCES[@]}"; do
|
|
261
|
-
if [ ! -d "$ROOT/${listed%/}" ]; then
|
|
262
|
-
WARNINGS+=("[$listed] listed in CLAUDE.md but folder does not exist")
|
|
263
|
-
WARNINGS+=(" → Suggested fix: either create the instance with new-agent-instance.sh or remove the line from CLAUDE.md")
|
|
264
|
-
fi
|
|
265
|
-
done
|
|
266
|
-
|
|
267
|
-
# === Determine status ===
|
|
268
|
-
if [ ${#FAILURES[@]} -gt 0 ]; then
|
|
269
|
-
STATUS="fail"
|
|
270
|
-
elif [ ${#WARNINGS[@]} -gt 0 ]; then
|
|
271
|
-
STATUS="warn"
|
|
272
|
-
else
|
|
273
|
-
STATUS="pass"
|
|
274
|
-
fi
|
|
275
|
-
|
|
276
|
-
# Count items (each FAILURE/WARNING entry is sometimes 2 lines: msg + suggested fix)
|
|
277
|
-
# Real count is half the FAILURES/WARNINGS for entries with → Suggested fix
|
|
278
|
-
count_items() {
|
|
279
|
-
local arr=("$@")
|
|
280
|
-
local n=0
|
|
281
|
-
for item in "${arr[@]}"; do
|
|
282
|
-
if ! [[ "$item" =~ ^[[:space:]]*→ ]]; then
|
|
283
|
-
n=$((n+1))
|
|
284
|
-
fi
|
|
285
|
-
done
|
|
286
|
-
echo $n
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
N_FAIL=0
|
|
290
|
-
for item in "${FAILURES[@]:-}"; do
|
|
291
|
-
[ -z "$item" ] && continue
|
|
292
|
-
[[ "$item" =~ ^[[:space:]]+→ ]] && continue
|
|
293
|
-
N_FAIL=$((N_FAIL + 1))
|
|
294
|
-
done
|
|
295
|
-
N_WARN=0
|
|
296
|
-
for item in "${WARNINGS[@]:-}"; do
|
|
297
|
-
[ -z "$item" ] && continue
|
|
298
|
-
[[ "$item" =~ ^[[:space:]]+→ ]] && continue
|
|
299
|
-
N_WARN=$((N_WARN + 1))
|
|
300
|
-
done
|
|
301
|
-
N_PASS=${#PASSED[@]}
|
|
302
|
-
|
|
303
|
-
# === Write report ===
|
|
304
|
-
{
|
|
305
|
-
echo "---"
|
|
306
|
-
echo "operation: audit-project"
|
|
307
|
-
echo "project: $PROJECT"
|
|
308
|
-
echo "ran: $TIMESTAMP"
|
|
309
|
-
echo "status: $STATUS"
|
|
310
|
-
echo "---"
|
|
311
|
-
echo ""
|
|
312
|
-
echo "# Audit: $PROJECT"
|
|
313
|
-
echo ""
|
|
314
|
-
echo "## Summary"
|
|
315
|
-
echo "- $N_PASS passed"
|
|
316
|
-
echo "- $N_WARN warnings"
|
|
317
|
-
echo "- $N_FAIL failures"
|
|
318
|
-
echo ""
|
|
319
|
-
if [ $N_FAIL -gt 0 ]; then
|
|
320
|
-
echo "## Failures"
|
|
321
|
-
for line in "${FAILURES[@]}"; do
|
|
322
|
-
echo "- $line"
|
|
323
|
-
done
|
|
324
|
-
echo ""
|
|
325
|
-
fi
|
|
326
|
-
if [ $N_WARN -gt 0 ]; then
|
|
327
|
-
echo "## Warnings"
|
|
328
|
-
for line in "${WARNINGS[@]}"; do
|
|
329
|
-
echo "- $line"
|
|
330
|
-
done
|
|
331
|
-
echo ""
|
|
332
|
-
fi
|
|
333
|
-
if [ $N_PASS -gt 0 ]; then
|
|
334
|
-
echo "## Passed"
|
|
335
|
-
for line in "${PASSED[@]}"; do
|
|
336
|
-
echo "- $line"
|
|
337
|
-
done
|
|
338
|
-
fi
|
|
339
|
-
} > "$REPORT"
|
|
340
|
-
|
|
341
|
-
# === Print summary ===
|
|
342
|
-
echo "Audit: $PROJECT — $STATUS"
|
|
343
|
-
echo " Passed: $N_PASS"
|
|
344
|
-
echo " Warnings: $N_WARN"
|
|
345
|
-
echo " Failures: $N_FAIL"
|
|
346
|
-
echo ""
|
|
347
|
-
if [ $N_FAIL -gt 0 ]; then
|
|
348
|
-
echo "Failures:"
|
|
349
|
-
for line in "${FAILURES[@]}"; do
|
|
350
|
-
echo " $line"
|
|
351
|
-
done
|
|
352
|
-
echo ""
|
|
353
|
-
fi
|
|
354
|
-
if [ $N_WARN -gt 0 ] && [ $N_WARN -le 5 ]; then
|
|
355
|
-
echo "Warnings:"
|
|
356
|
-
for line in "${WARNINGS[@]}"; do
|
|
357
|
-
echo " $line"
|
|
358
|
-
done
|
|
359
|
-
echo ""
|
|
360
|
-
fi
|
|
361
|
-
echo "Full report: $REPORT"
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# new-agent-instance.sh — adds an instance of a global agent to a project
|
|
3
|
-
# Usage: bash scripts/new-agent-instance.sh <project> <function> <agent-name>
|
|
4
|
-
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
if [ $# -ne 3 ]; then
|
|
8
|
-
echo "Usage: $0 <project> <function> <agent-name>"
|
|
9
|
-
echo "Example: $0 myproject gtm sdr"
|
|
10
|
-
exit 1
|
|
11
|
-
fi
|
|
12
|
-
|
|
13
|
-
PROJECT="$1"
|
|
14
|
-
FN="$2"
|
|
15
|
-
AGENT="$3"
|
|
16
|
-
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
17
|
-
|
|
18
|
-
source "$ROOT/scripts/lib/functions.sh"
|
|
19
|
-
|
|
20
|
-
if ! is_valid_function "$FN"; then
|
|
21
|
-
echo "ERROR: '$FN' is not a registered function." >&2
|
|
22
|
-
echo "Registered functions:" >&2
|
|
23
|
-
read_functions | sed 's/^/ - /' >&2
|
|
24
|
-
exit 1
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
PROJECT_DIR="$ROOT/projects/$PROJECT"
|
|
28
|
-
GLOBAL_AGENT_DIR="$ROOT/$FN/$AGENT"
|
|
29
|
-
INSTANCE_DIR="$GLOBAL_AGENT_DIR/projects/$PROJECT"
|
|
30
|
-
INSTANCE_TEMPLATE="$GLOBAL_AGENT_DIR/projects/_template"
|
|
31
|
-
|
|
32
|
-
if [ ! -d "$PROJECT_DIR" ]; then
|
|
33
|
-
echo "ERROR: Project '$PROJECT' not found at $PROJECT_DIR"
|
|
34
|
-
echo "Create it first: bash scripts/new-project.sh $PROJECT"
|
|
35
|
-
exit 1
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
if [ ! -d "$GLOBAL_AGENT_DIR" ]; then
|
|
39
|
-
echo "ERROR: Global agent '$FN/$AGENT' not found at $GLOBAL_AGENT_DIR"
|
|
40
|
-
echo "Create it first: bash scripts/new-agent.sh $FN $AGENT"
|
|
41
|
-
exit 1
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
if [ -d "$INSTANCE_DIR" ]; then
|
|
45
|
-
echo "ERROR: Instance already exists at $INSTANCE_DIR"
|
|
46
|
-
exit 1
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
if [ ! -d "$INSTANCE_TEMPLATE" ]; then
|
|
50
|
-
echo "ERROR: Agent has no _template instance at $INSTANCE_TEMPLATE"
|
|
51
|
-
echo "(Recreate the agent or create the template manually.)"
|
|
52
|
-
exit 1
|
|
53
|
-
fi
|
|
54
|
-
|
|
55
|
-
CURRENT_MONTH=$(date -u +"%Y-%m")
|
|
56
|
-
TODAY=$(date -u +"%Y-%m-%d")
|
|
57
|
-
|
|
58
|
-
echo "Adding instance of $FN/$AGENT to project $PROJECT"
|
|
59
|
-
|
|
60
|
-
# Copy the template
|
|
61
|
-
cp -R "$INSTANCE_TEMPLATE" "$INSTANCE_DIR"
|
|
62
|
-
|
|
63
|
-
# Portable sed
|
|
64
|
-
SED_INPLACE=(-i)
|
|
65
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
66
|
-
SED_INPLACE=(-i '')
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
# Patch placeholders in config and asset-references
|
|
70
|
-
sed "${SED_INPLACE[@]}" "s/<project-slug>/$PROJECT/g" "$INSTANCE_DIR/config/default.yaml"
|
|
71
|
-
sed "${SED_INPLACE[@]}" "s/<YYYY-MM-DD>/$TODAY/g" "$INSTANCE_DIR/config/default.yaml"
|
|
72
|
-
sed "${SED_INPLACE[@]}" "s/<Project>/$PROJECT/g" "$INSTANCE_DIR/config/default.yaml"
|
|
73
|
-
sed "${SED_INPLACE[@]}" "s/<project>/$PROJECT/g" "$INSTANCE_DIR/asset-references.md"
|
|
74
|
-
|
|
75
|
-
# Set up current month in log dirs
|
|
76
|
-
mkdir -p "$INSTANCE_DIR/log/runs/$CURRENT_MONTH" "$INSTANCE_DIR/log/feedback/$CURRENT_MONTH"
|
|
77
|
-
touch "$INSTANCE_DIR/log/runs/$CURRENT_MONTH/.gitkeep"
|
|
78
|
-
touch "$INSTANCE_DIR/log/feedback/$CURRENT_MONTH/.gitkeep"
|
|
79
|
-
|
|
80
|
-
# Prompt for tool bindings if the global agent.md has them
|
|
81
|
-
AGENT_MD="$GLOBAL_AGENT_DIR/agent.md"
|
|
82
|
-
INSTANCE_CONFIG="$INSTANCE_DIR/config/default.yaml"
|
|
83
|
-
if [ -f "$AGENT_MD" ] && grep -q '^## Tools and bindings' "$AGENT_MD"; then
|
|
84
|
-
echo ""
|
|
85
|
-
echo "=== Tool bindings for $FN/$AGENT in $PROJECT ==="
|
|
86
|
-
echo "Enter values for each binding. Press Enter (or type 'skip') to leave as TODO."
|
|
87
|
-
bash "$ROOT/scripts/lib/bindings-prompt.sh" "$AGENT_MD" "$INSTANCE_CONFIG" || \
|
|
88
|
-
echo "WARNING: bindings-prompt failed; tools: block not appended. Edit $INSTANCE_CONFIG manually."
|
|
89
|
-
else
|
|
90
|
-
echo ""
|
|
91
|
-
echo "(Agent has no '## Tools and bindings' section in agent.md — skipping binding prompt)"
|
|
92
|
-
fi
|
|
93
|
-
|
|
94
|
-
# Pull list of expected MCPs and skills from agent's .mcp.json (rough — just shows what file exists)
|
|
95
|
-
echo ""
|
|
96
|
-
echo "✓ Instance added: $INSTANCE_DIR"
|
|
97
|
-
echo ""
|
|
98
|
-
echo "Reminders:"
|
|
99
|
-
echo " - Edit $INSTANCE_DIR/config/default.yaml (see $GLOBAL_AGENT_DIR/agent.md § Inputs)"
|
|
100
|
-
echo " - Edit $INSTANCE_DIR/asset-references.md to list which assets this agent uses"
|
|
101
|
-
echo " - Verify project has required guidelines:"
|
|
102
|
-
echo " $PROJECT_DIR/guidelines/voice.md"
|
|
103
|
-
echo " $PROJECT_DIR/guidelines/icps/*.md (at least one)"
|
|
104
|
-
echo " $PROJECT_DIR/guidelines/asset-links.md"
|
|
105
|
-
echo " - Verify agent's MCPs are configured in $GLOBAL_AGENT_DIR/.mcp.json"
|
|
106
|
-
echo ""
|
|
107
|
-
echo "Test from a session:"
|
|
108
|
-
echo " cd $INSTANCE_DIR/"
|
|
109
|
-
echo " claude"
|
|
110
|
-
echo " \"Run $AGENT — dry run, just show me the plan\""
|
|
111
|
-
echo ""
|
|
112
|
-
# Scheduling: register with the native desktop scheduler via `roster schedule install`.
|
|
113
|
-
# See docs/SCHEDULING.md and docs/adr/0001-scheduling-architecture.md for the model.
|
|
114
|
-
echo "Optionally schedule this agent: roster schedule install $FN/$AGENT <plan> --cron \"<expr>\" --tool claude|codex"
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# new-project.sh — scaffold a new project substrate inside a roster workspace.
|
|
3
|
-
#
|
|
4
|
-
# Usage:
|
|
5
|
-
# bash scripts/new-project.sh <project-name> [<function>]
|
|
6
|
-
#
|
|
7
|
-
# Arguments:
|
|
8
|
-
# project-name Free-form name. Normalized to kebab-case (lowercase,
|
|
9
|
-
# [a-z0-9-], non-alphanumeric runs collapsed to '-').
|
|
10
|
-
# Examples:
|
|
11
|
-
# "My Co" -> my-co
|
|
12
|
-
# "foo bar/baz" -> foo-bar-baz
|
|
13
|
-
# "Acme Corp 2" -> acme-corp-2
|
|
14
|
-
# function Optional. If provided, must be registered in
|
|
15
|
-
# .config/functions.yaml (gtm, product, design, ops, ...).
|
|
16
|
-
#
|
|
17
|
-
# Creates:
|
|
18
|
-
# projects/<slug>/
|
|
19
|
-
# guidelines/.gitkeep
|
|
20
|
-
# config/default.yaml (project-level config skeleton)
|
|
21
|
-
# state.md (frontmatter + 5-line stub)
|
|
22
|
-
#
|
|
23
|
-
# Exit codes:
|
|
24
|
-
# 0 success
|
|
25
|
-
# 1 usage / validation error
|
|
26
|
-
|
|
27
|
-
set -euo pipefail
|
|
28
|
-
|
|
29
|
-
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
30
|
-
# shellcheck disable=SC1091
|
|
31
|
-
source "$ROOT/scripts/lib/functions.sh"
|
|
32
|
-
|
|
33
|
-
usage() {
|
|
34
|
-
echo "Usage: $0 <project-name> [<function>]" >&2
|
|
35
|
-
echo " project-name Free-form; normalized to kebab-case" >&2
|
|
36
|
-
echo " function Optional; must be in .config/functions.yaml" >&2
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
|
|
40
|
-
usage
|
|
41
|
-
exit 1
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
RAW_NAME="$1"
|
|
45
|
-
FUNCTION="${2:-}"
|
|
46
|
-
|
|
47
|
-
normalize() {
|
|
48
|
-
# 1. lowercase
|
|
49
|
-
# 2. replace any run of non-alphanumeric chars with a single '-'
|
|
50
|
-
# 3. trim leading/trailing '-'
|
|
51
|
-
printf '%s' "$1" \
|
|
52
|
-
| tr '[:upper:]' '[:lower:]' \
|
|
53
|
-
| sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//'
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
SLUG="$(normalize "$RAW_NAME")"
|
|
57
|
-
|
|
58
|
-
if [ -z "$SLUG" ]; then
|
|
59
|
-
echo "ERROR: project name '$RAW_NAME' is empty after normalization" >&2
|
|
60
|
-
exit 1
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
if ! [[ "$SLUG" =~ ^[a-z][a-z0-9-]*$ ]]; then
|
|
64
|
-
echo "ERROR: normalized slug '$SLUG' must start with a letter and contain only a-z, 0-9, '-'" >&2
|
|
65
|
-
exit 1
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
if [ -n "$FUNCTION" ]; then
|
|
69
|
-
if ! is_valid_function "$FUNCTION"; then
|
|
70
|
-
echo "ERROR: function '$FUNCTION' is not registered in .config/functions.yaml" >&2
|
|
71
|
-
echo "Registered functions:" >&2
|
|
72
|
-
read_functions 2>/dev/null | sed 's/^/ - /' >&2 || echo " (registry empty or missing)" >&2
|
|
73
|
-
exit 1
|
|
74
|
-
fi
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
TARGET="$ROOT/projects/$SLUG"
|
|
78
|
-
|
|
79
|
-
if [ -e "$TARGET" ]; then
|
|
80
|
-
echo "ERROR: project '$SLUG' already exists at $TARGET" >&2
|
|
81
|
-
exit 1
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
TIMESTAMP="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
85
|
-
|
|
86
|
-
mkdir -p "$TARGET/guidelines" "$TARGET/config" "$TARGET/assets"
|
|
87
|
-
touch "$TARGET/guidelines/.gitkeep" "$TARGET/assets/.gitkeep"
|
|
88
|
-
|
|
89
|
-
cat >"$TARGET/config/default.yaml" <<EOF
|
|
90
|
-
---
|
|
91
|
-
project: $SLUG
|
|
92
|
-
created: ${TIMESTAMP%T*}
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
# Project-level config for $SLUG.
|
|
96
|
-
# Cross-agent defaults. Agent-scoped instance config lives at:
|
|
97
|
-
# <function>/<agent>/projects/$SLUG/config/default.yaml
|
|
98
|
-
|
|
99
|
-
display_name: $SLUG
|
|
100
|
-
stage: early
|
|
101
|
-
motion: outbound
|
|
102
|
-
approval_channel: auto
|
|
103
|
-
EOF
|
|
104
|
-
|
|
105
|
-
cat >"$TARGET/state.md" <<EOF
|
|
106
|
-
---
|
|
107
|
-
updated: $TIMESTAMP
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
Last task: (none yet)
|
|
111
|
-
Active artifacts: (none)
|
|
112
|
-
Open questions: (none)
|
|
113
|
-
Next session: fill in guidelines/voice.md and at least one ICP
|
|
114
|
-
Notes: created via scripts/new-project.sh
|
|
115
|
-
EOF
|
|
116
|
-
|
|
117
|
-
echo "✓ Project '$SLUG' created at projects/$SLUG/"
|
|
118
|
-
echo ""
|
|
119
|
-
echo "Next steps:"
|
|
120
|
-
echo " 1. Fill projects/$SLUG/guidelines/voice.md (3 adjectives + tone)"
|
|
121
|
-
echo " 2. Add at least one ICP under projects/$SLUG/guidelines/icps/"
|
|
122
|
-
echo " 3. Edit projects/$SLUG/config/default.yaml — set display_name, stage, motion"
|
|
123
|
-
if [ -n "$FUNCTION" ]; then
|
|
124
|
-
echo " 4. Wire an agent instance via chief-of-staff: add-agent-to-project project=$SLUG function=$FUNCTION agent=<name>"
|
|
125
|
-
fi
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# remove-agent-from-project.sh — archives a single agent instance from a project
|
|
3
|
-
# Usage: bash scripts/remove-agent-from-project.sh <project> <function> <agent>
|
|
4
|
-
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
if [ $# -ne 3 ]; then
|
|
8
|
-
echo "Usage: $0 <project> <function> <agent>"
|
|
9
|
-
exit 1
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
PROJECT="$1"
|
|
13
|
-
FN="$2"
|
|
14
|
-
AGENT="$3"
|
|
15
|
-
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
16
|
-
INSTANCE_DIR="$ROOT/$FN/$AGENT/projects/$PROJECT"
|
|
17
|
-
|
|
18
|
-
if [ ! -d "$INSTANCE_DIR" ]; then
|
|
19
|
-
echo "ERROR: Instance not found at $INSTANCE_DIR"
|
|
20
|
-
exit 1
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
DATE=$(date +%Y-%m-%d)
|
|
24
|
-
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
25
|
-
|
|
26
|
-
# Determine archive suffix
|
|
27
|
-
SUFFIX="$DATE"
|
|
28
|
-
COUNTER=2
|
|
29
|
-
while [ -d "$ROOT/_archive/$FN/$AGENT/projects/$PROJECT-$SUFFIX" ]; do
|
|
30
|
-
SUFFIX="$DATE-$COUNTER"
|
|
31
|
-
COUNTER=$((COUNTER + 1))
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
echo "Removing instance: $FN/$AGENT/projects/$PROJECT (archiving with suffix $SUFFIX)"
|
|
35
|
-
|
|
36
|
-
mkdir -p "$ROOT/_archive/$FN/$AGENT/projects"
|
|
37
|
-
mv "$INSTANCE_DIR" "$ROOT/_archive/$FN/$AGENT/projects/$PROJECT-$SUFFIX"
|
|
38
|
-
echo " Moved: $FN/$AGENT/projects/$PROJECT/ → _archive/$FN/$AGENT/projects/$PROJECT-$SUFFIX/"
|
|
39
|
-
|
|
40
|
-
# Update project CLAUDE.md — remove the line referencing this instance
|
|
41
|
-
PROJECT_CLAUDE="$ROOT/projects/$PROJECT/CLAUDE.md"
|
|
42
|
-
if [ -f "$PROJECT_CLAUDE" ]; then
|
|
43
|
-
# Remove lines that reference the removed instance under "Active agent instances"
|
|
44
|
-
# Match patterns like "- `gtm/sdr/projects/<project>/` — ..."
|
|
45
|
-
SED_INPLACE=(-i)
|
|
46
|
-
if [[ "$(uname)" == "Darwin" ]]; then
|
|
47
|
-
SED_INPLACE=(-i '')
|
|
48
|
-
fi
|
|
49
|
-
sed "${SED_INPLACE[@]}" "/^- \`$FN\/$AGENT\/projects\/$PROJECT\//d" "$PROJECT_CLAUDE"
|
|
50
|
-
echo " Updated: projects/$PROJECT/CLAUDE.md (removed instance line)"
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
# Operation log
|
|
54
|
-
LOG_DIR="$ROOT/chief-of-staff/logs/$(date +%Y-%m)"
|
|
55
|
-
mkdir -p "$LOG_DIR"
|
|
56
|
-
LOG_FILE="$LOG_DIR/operations-$(date +%Y-%m-%d).md"
|
|
57
|
-
{
|
|
58
|
-
echo ""
|
|
59
|
-
echo "## $TIMESTAMP — remove-agent-from-project"
|
|
60
|
-
echo "Project: $PROJECT, Agent: $FN/$AGENT"
|
|
61
|
-
echo "Archive suffix: $SUFFIX"
|
|
62
|
-
} >> "$LOG_FILE"
|
|
63
|
-
|
|
64
|
-
echo ""
|
|
65
|
-
echo "✓ Instance removed (archived)."
|
|
66
|
-
echo " To restore: move _archive/$FN/$AGENT/projects/$PROJECT-$SUFFIX back to $FN/$AGENT/projects/$PROJECT"
|
|
67
|
-
echo " Operation log: $LOG_FILE"
|