@firatcand/roster 0.1.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.
Files changed (75) hide show
  1. package/README.md +77 -215
  2. package/agents/lesson-drafter.md +3 -8
  3. package/agents/pattern-detector.md +0 -1
  4. package/bin/roster.js +7233 -1197
  5. package/data/plan-ceilings.yaml +57 -0
  6. package/package.json +8 -3
  7. package/skills/chief-of-staff/SKILL.md +199 -59
  8. package/skills/dreamer/SKILL.md +8 -7
  9. package/skills/roster-orchestrator/SKILL.md +53 -25
  10. package/templates/CLAUDE.project.template.md +1 -1
  11. package/templates/CONTEXT.template.md +2 -2
  12. package/templates/gitignore-defaults.txt +2 -0
  13. package/templates/hooks/banner.sh +47 -0
  14. package/templates/scaffold/chief-of-staff/README.md +16 -24
  15. package/templates/scaffold/chief-of-staff/agent.md +22 -32
  16. package/templates/scaffold/chief-of-staff/plans/audit-agent.yaml +4 -4
  17. package/templates/scaffold/chief-of-staff/plans/audit-repo.yaml +5 -4
  18. package/templates/scaffold/chief-of-staff/plans/create-agent.yaml +5 -34
  19. package/templates/scaffold/config/project.yaml.template +10 -0
  20. package/templates/scaffold/conventions.md +188 -173
  21. package/templates/scaffold/dreamer/README.md +2 -2
  22. package/templates/scaffold/dreamer/agent.md +0 -1
  23. package/templates/scaffold/dreamer/plans/nightly-reflection.yaml +23 -37
  24. package/templates/scaffold/dreamer/subagents/lesson-drafter.md +2 -7
  25. package/templates/scaffold/{projects/_demo/guidelines → guidelines}/asset-links.md +4 -0
  26. package/templates/scaffold/{projects/_demo/guidelines → guidelines}/brand-book.md +4 -0
  27. package/templates/scaffold/{projects/_demo/guidelines → guidelines}/messaging.md +4 -0
  28. package/templates/scaffold/{projects/_demo/guidelines → guidelines}/voice.md +4 -0
  29. package/templates/scaffold/logs/cron/.gitkeep +1 -0
  30. package/templates/scaffold/ops/EXPERT.md +5 -5
  31. package/templates/scaffold/scripts/audit-agent.sh +326 -0
  32. package/templates/scaffold/scripts/audit-repo.sh +218 -0
  33. package/templates/scaffold/scripts/create-function.sh +267 -0
  34. package/templates/scaffold/scripts/lib/README.md +6 -1
  35. package/templates/scaffold/scripts/lib/bindings-prompt.sh +53 -0
  36. package/templates/scaffold/scripts/lib/functions.sh +17 -5
  37. package/templates/scaffold/scripts/new-agent.sh +416 -0
  38. package/templates/scaffold/scripts/rename-agent.sh +91 -0
  39. package/templates/scaffold/scripts/save-state.sh +32 -0
  40. package/agents/critic.md +0 -74
  41. package/agents/enricher.md +0 -56
  42. package/agents/promotion-arbiter.md +0 -71
  43. package/agents/prospector.md +0 -51
  44. package/agents/writer.md +0 -58
  45. package/skills/sdr/SKILL.md +0 -147
  46. package/templates/scaffold/chief-of-staff/plans/add-agent-to-project.yaml +0 -45
  47. package/templates/scaffold/chief-of-staff/plans/archive-project.yaml +0 -51
  48. package/templates/scaffold/chief-of-staff/plans/audit-project.yaml +0 -34
  49. package/templates/scaffold/chief-of-staff/plans/create-project.yaml +0 -65
  50. package/templates/scaffold/chief-of-staff/plans/remove-agent-from-project.yaml +0 -50
  51. package/templates/scaffold/chief-of-staff/plans/rename-project.yaml +0 -62
  52. package/templates/scaffold/chief-of-staff/plans/unarchive-project.yaml +0 -41
  53. package/templates/scaffold/dreamer/subagents/promotion-arbiter.md +0 -64
  54. package/templates/scaffold/gtm/sdr/.claude/settings.json +0 -3
  55. package/templates/scaffold/gtm/sdr/.mcp.json +0 -21
  56. package/templates/scaffold/gtm/sdr/README.md +0 -46
  57. package/templates/scaffold/gtm/sdr/agent.md +0 -136
  58. package/templates/scaffold/gtm/sdr/plans/cold-outreach.yaml +0 -92
  59. package/templates/scaffold/gtm/sdr/projects/_demo/asset-references.md +0 -7
  60. package/templates/scaffold/gtm/sdr/projects/_demo/config/default.yaml +0 -69
  61. package/templates/scaffold/gtm/sdr/projects/_demo/log/feedback/.gitkeep +0 -0
  62. package/templates/scaffold/gtm/sdr/projects/_demo/log/runs/.gitkeep +0 -0
  63. package/templates/scaffold/gtm/sdr/projects/_demo/playbook/.gitkeep +0 -0
  64. package/templates/scaffold/gtm/sdr/subagents/critic.md +0 -67
  65. package/templates/scaffold/gtm/sdr/subagents/enricher.md +0 -49
  66. package/templates/scaffold/gtm/sdr/subagents/prospector.md +0 -44
  67. package/templates/scaffold/gtm/sdr/subagents/writer.md +0 -51
  68. package/templates/scaffold/projects/_demo/CLAUDE.md +0 -35
  69. package/templates/scaffold/projects/_demo/README.md +0 -16
  70. package/templates/scaffold/projects/_demo/assets/.gitkeep +0 -0
  71. package/templates/scaffold/projects/_demo/config/default.yaml +0 -28
  72. package/templates/scaffold/projects/_demo/state.md +0 -11
  73. package/templates/scaffold/scripts/new-project.sh +0 -125
  74. /package/templates/scaffold/gtm/{sdr/playbook/.gitkeep → .gitkeep} +0 -0
  75. /package/templates/scaffold/{projects/_demo/guidelines → guidelines}/icps/_persona-template.md +0 -0
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env bash
2
+ # new-agent.sh — scaffolds a new global agent under a function category
3
+ #
4
+ # Usage:
5
+ # bash scripts/new-agent.sh <function> <agent-name>
6
+ # bash scripts/new-agent.sh --slash-only <function> <agent-name>
7
+ #
8
+ # --slash-only is the recovery flag invoked by chief-of-staff's guided-mode
9
+ # atomic-write transaction when the slash command file fails to land after
10
+ # the agent tree has already been written (see ROS-52). It writes only
11
+ # .claude/commands/<agent>.md, with strict no-clobber, and requires the
12
+ # agent tree to exist.
13
+
14
+ set -euo pipefail
15
+
16
+ print_usage() {
17
+ cat >&2 <<EOF
18
+ Usage:
19
+ $0 <function> <agent-name>
20
+ $0 --slash-only <function> <agent-name>
21
+
22
+ Function: any slug registered in .config/functions.yaml
23
+ Example: $0 gtm content-agent
24
+
25
+ --slash-only: recovery flag. Writes only .claude/commands/<agent-name>.md.
26
+ Requires the agent tree at <function>/<agent-name>/ to already exist.
27
+ Exits non-zero (no clobber) if the slash command file already exists.
28
+ EOF
29
+ }
30
+
31
+ SLASH_ONLY=0
32
+ if [ "${1:-}" = "--slash-only" ]; then
33
+ SLASH_ONLY=1
34
+ shift
35
+ fi
36
+
37
+ if [ $# -ne 2 ]; then
38
+ print_usage
39
+ exit 1
40
+ fi
41
+
42
+ FN="$1"
43
+ NAME="$2"
44
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
45
+ TARGET="$ROOT/$FN/$NAME"
46
+
47
+ source "$ROOT/scripts/lib/functions.sh"
48
+
49
+ # write_slash_command <abs-file-path> <function> <agent-name>
50
+ # Canonical writer for .claude/commands/<agent>.md. Both the full-install
51
+ # path and --slash-only call this so the two paths cannot drift over time
52
+ # (see docs/learnings/2026-Q2/installer-mutations-need-drift-detector-mirror.md).
53
+ write_slash_command() {
54
+ local file="$1" fn="$2" name="$3"
55
+ # ROS-62: quote the description so YAML-special characters in $fn or the
56
+ # placeholder body don't trip the I4 YAML parser when invariants run.
57
+ cat > "$file" <<EOF
58
+ ---
59
+ name: $name
60
+ description: "$fn agent — TODO: fill in description"
61
+ ---
62
+
63
+ # /$name
64
+
65
+ You are operating the \`$fn/$name\` agent. Load \`$fn/$name/agent.md\` and the workspace's relevant context.
66
+
67
+ The user request is: \$ARGUMENTS
68
+
69
+ ## Routing logic
70
+
71
+ Parse the user request:
72
+
73
+ 1. **If it matches \`run <plan-name>\`**:
74
+ - Load \`$fn/$name/plans/<plan-name>.yaml\`. If it doesn't exist, list available plans and ask user to pick.
75
+ - Load \`$fn/$name/config.yaml\` and resolve env via \`resolveAgentEnv\` (\`$fn/$name/.env\` overrides workspace \`/.env\`).
76
+ - Load workspace guidelines referenced under \`config.yaml\` \`guideline_refs:\` (e.g., \`/guidelines/voice.md\`, \`/guidelines/icps/\`, \`/guidelines/messaging.md\`).
77
+ - Validate that all required tool bindings have non-empty env vars. Abort with a clear message if not.
78
+ - Execute the plan steps. Substitute \`\${tools.X.env_var}\`, \`\${inputs.X}\`, \`\${config.X}\`.
79
+ - Log to \`$fn/$name/logs/runs/<YYYY-MM>/<YYYY-MM-DD-HHMM>.md\`.
80
+ - Surface HITL approvals per the plan's \`approval_channel\`.
81
+
82
+ 2. **If no plan is named**:
83
+ - List available plans from \`$fn/$name/plans/\` with descriptions. Ask user to pick.
84
+
85
+ 3. **For ad-hoc strategic work**: suggest invoking \`$fn/EXPERT.md\` instead.
86
+
87
+ ## Constraints
88
+
89
+ - Only run plans that exist as files in \`$fn/$name/plans/\`.
90
+ - Don't bypass approval gates.
91
+ - File writes go to \`$fn/$name/logs/runs/\` unless the plan explicitly writes elsewhere.
92
+ EOF
93
+ }
94
+
95
+ if ! is_valid_function "$FN"; then
96
+ echo "ERROR: '$FN' is not a registered function." >&2
97
+ echo "Registered functions:" >&2
98
+ read_functions | sed 's/^/ - /' >&2
99
+ echo "" >&2
100
+ echo "To add a new function: bash scripts/create-function.sh $FN" >&2
101
+ exit 1
102
+ fi
103
+
104
+ if ! [[ "$NAME" =~ ^[a-z][a-z0-9-]*$ ]]; then
105
+ echo "ERROR: Agent name must be lowercase, alphanumeric + hyphens." >&2
106
+ exit 1
107
+ fi
108
+
109
+ # --slash-only branch: write only the slash command file and exit.
110
+ # Requires agent tree to exist; refuses to clobber an existing slash command.
111
+ if [ "$SLASH_ONLY" = "1" ]; then
112
+ if [ ! -d "$TARGET" ]; then
113
+ echo "ERROR: agent tree '$FN/$NAME' does not exist at $TARGET." >&2
114
+ echo " --slash-only is a recovery flag — create the agent tree first with:" >&2
115
+ echo " bash scripts/new-agent.sh $FN $NAME" >&2
116
+ exit 1
117
+ fi
118
+ COMMANDS_DIR="$ROOT/.claude/commands"
119
+ SLASH_CMD_FILE="$COMMANDS_DIR/$NAME.md"
120
+ if [ -f "$SLASH_CMD_FILE" ]; then
121
+ echo "ERROR: slash command already exists at .claude/commands/$NAME.md. Refusing to clobber." >&2
122
+ exit 1
123
+ fi
124
+ mkdir -p "$COMMANDS_DIR"
125
+ write_slash_command "$SLASH_CMD_FILE" "$FN" "$NAME"
126
+ echo "Created: .claude/commands/$NAME.md"
127
+ exit 0
128
+ fi
129
+
130
+ if [ -d "$TARGET" ]; then
131
+ echo "ERROR: Agent '$FN/$NAME' already exists at $TARGET" >&2
132
+ exit 1
133
+ fi
134
+
135
+ echo "Creating agent: $FN/$NAME"
136
+
137
+ mkdir -p "$TARGET"/{subagents,playbook,pending,logs/runs,logs/feedback,.claude/skills,.claude/plugins}
138
+
139
+ # agent.md stub
140
+ cat > "$TARGET/agent.md" << EOF
141
+ # $NAME
142
+
143
+ ## Purpose
144
+
145
+ <One paragraph: what this agent does, why it exists.>
146
+
147
+ ## Inputs
148
+
149
+ The orchestrator expects per-plan inputs (declared in each plan's \`inputs:\` block).
150
+
151
+ Read at runtime:
152
+
153
+ - \`agent.md\` (this file)
154
+ - \`config.yaml\` (workspace-root-relative guideline refs + tool bindings)
155
+ - Workspace guidelines referenced under \`config.yaml\` \`guideline_refs:\` (e.g., \`/guidelines/voice.md\`, \`/guidelines/icps/\`, \`/guidelines/messaging.md\`)
156
+ - \`playbook/\` — validated lessons (single playbook per agent)
157
+
158
+ Env resolution: \`<this-agent>/.env\` overrides workspace \`/.env\`. Required tool env vars validated before the plan runs.
159
+
160
+ ## Plans
161
+
162
+ Named plans this agent runs (files in \`plans/<plan>.yaml\`). One-line description per plan.
163
+ No default plan — invoking without a plan triggers an interactive "which plan?" prompt.
164
+
165
+ - <plan-name>: <one-liner>
166
+
167
+ ## Subagents
168
+
169
+ - <subagent-name>.md — <one-liner>
170
+
171
+ ## Outputs
172
+
173
+ Run file at \`logs/runs/<YYYY-MM>/<YYYY-MM-DD-HHMM>.md\`. See \`conventions.md\` § "Run file format".
174
+ Per-plan output schemas live in each plan's \`outputs:\` block.
175
+
176
+ ## Approval
177
+
178
+ \`approval_channel: auto\` — in-session if interactive, Slack \`#${FN}\` if cron (resolved via \`SLACK_HITL_CHANNEL_$(echo "$FN" | tr '[:lower:]-' '[:upper:]_')\` in workspace \`/.env\`).
179
+
180
+ ## Lessons protocol
181
+
182
+ Log candidate lessons inline in run output under \`## Candidate lessons\`. Don't write to \`playbook/\` directly during runs — that's the dreamer's job.
183
+
184
+ ## Failure modes
185
+
186
+ - <known failure mode>: <handling>
187
+ EOF
188
+
189
+ # README
190
+ cat > "$TARGET/README.md" << EOF
191
+ # $NAME
192
+
193
+ <One-line description.>
194
+
195
+ ## Files
196
+
197
+ - \`agent.md\` — orchestrator contract (behavioral prompt, plans list, tool bindings schema)
198
+ - \`config.yaml\` — guideline refs + tool bindings (workspace-root paths)
199
+ - \`.env\` — agent-scoped env overrides (gitignored, 0600 — optional, inherits from workspace \`/.env\`)
200
+ - \`plans/\` — named workflows (\`<plan>.yaml\`)
201
+ - \`subagents/\` — specialized roles
202
+ - \`playbook/\` — validated lessons (single playbook per agent)
203
+ - \`pending/\` — HITL items awaiting approval
204
+ - \`logs/runs/\`, \`logs/feedback/\` — run outputs + mirrored feedback
205
+ - \`asset-references.md\` — which workspace assets this agent uses (thin pointer)
206
+ - \`.claude/\` — agent-scoped Claude Code config (skills, plugins, settings)
207
+ - \`.mcp.json\` — agent-scoped MCPs
208
+
209
+ ## Invocation
210
+
211
+ From the workspace root:
212
+
213
+ \`\`\`bash
214
+ claude
215
+ > /$NAME run <plan-name>
216
+ \`\`\`
217
+
218
+ Or via natural language:
219
+
220
+ \`\`\`
221
+ "Run $FN/$NAME using the <plan-name> plan"
222
+ \`\`\`
223
+
224
+ From cron: see ADR-0001 (subscription-billed scheduling via native local schedulers). Install via \`roster schedule install $FN/$NAME <plan> --cron "<expr>" --tool claude|codex\`.
225
+
226
+ ## Configuration
227
+
228
+ \`config.yaml\` (this agent) — guideline refs + tool bindings.
229
+ Workspace \`/.env\` (root) + optional \`<this-agent>/.env\` for agent-scoped overrides.
230
+
231
+ ## Outputs
232
+
233
+ \`logs/runs/<YYYY-MM>/<YYYY-MM-DD-HHMM>.md\` — one file per invocation.
234
+ EOF
235
+
236
+ # .mcp.json stub
237
+ cat > "$TARGET/.mcp.json" << EOF
238
+ {
239
+ "_comment": "Agent-scoped MCPs for $NAME. Available when working in this agent's tree. Add MCPs this agent specifically needs. Universal MCPs (Slack, Google Drive) are inherited from the workspace .mcp.json.",
240
+ "mcpServers": {}
241
+ }
242
+ EOF
243
+
244
+ # .claude/settings.json
245
+ cat > "$TARGET/.claude/settings.json" << EOF
246
+ {
247
+ "_comment": "Agent-scoped Claude Code settings for $NAME."
248
+ }
249
+ EOF
250
+
251
+ # Subagent template
252
+ cat > "$TARGET/subagents/_template.md" << 'EOF'
253
+ # <Subagent Name>
254
+
255
+ ## Role
256
+ <One paragraph: narrow job, single responsibility.>
257
+
258
+ ## Inputs
259
+ <What the orchestrator passes in.>
260
+
261
+ ## Output
262
+ <Structured output the orchestrator can parse.>
263
+
264
+ ## Tools
265
+ <Named tools this subagent uses.>
266
+
267
+ ## Boundaries
268
+ <What this subagent does NOT do.>
269
+
270
+ ## Quality bar
271
+ <Specific criteria for acceptable output.>
272
+ EOF
273
+
274
+ # config.yaml — agent config (guideline refs + tool bindings)
275
+ # Minimal stub. Until the Phase 2 env-merge loader lands, bindings are
276
+ # hand-edited: copy the ## Tools and bindings YAML block from agent.md
277
+ # into the tools: mapping below, then fill the corresponding env vars
278
+ # in workspace /.env (or this agent's .env).
279
+ cat > "$TARGET/config.yaml" << EOF
280
+ agent: $FN/$NAME
281
+ plans_dir: ./plans/
282
+
283
+ # Workspace-root-relative refs. Loader rejects literal absolute fs paths
284
+ # like /Users/, /home/, /etc/, /var/, /tmp/, /opt/.
285
+ guideline_refs:
286
+ voice: /guidelines/voice.md
287
+ icps: /guidelines/icps/
288
+ messaging: /guidelines/messaging.md
289
+ # add more as needed:
290
+ # brand_book: /guidelines/brand-book.md
291
+ # do_and_dont: /guidelines/do-and-dont.md
292
+ # compliance: /guidelines/compliance.md
293
+ # competitors: /guidelines/competitors.md
294
+
295
+ # Tool bindings. Each tool entry names the env var, required flag, and a
296
+ # short description. Env vars themselves live in workspace /.env (or are
297
+ # overridden in this agent's .env).
298
+ tools: {}
299
+ EOF
300
+
301
+ # asset-references.md — thin pointer at agent root
302
+ cat > "$TARGET/asset-references.md" << EOF
303
+ # Asset references — $FN/$NAME
304
+
305
+ This agent uses these assets from \`guidelines/asset-links.md\`:
306
+
307
+ - <e.g., specific asset by name>
308
+ EOF
309
+
310
+ touch "$TARGET/playbook/.gitkeep"
311
+ touch "$TARGET/pending/.gitkeep"
312
+ touch "$TARGET/logs/runs/.gitkeep"
313
+ touch "$TARGET/logs/feedback/.gitkeep"
314
+ touch "$TARGET/.claude/skills/.gitkeep"
315
+ touch "$TARGET/.claude/plugins/.gitkeep"
316
+
317
+ # === Tool bindings prompt (added by tool-bindings workflow) ===
318
+ # Ask the user whether to define tools now. If yes, collect tool names and scaffold
319
+ # a stub `## Tools and bindings` section in the new agent.md.
320
+
321
+ echo ""
322
+ NEW_AGENT_MD="$ROOT/$FN/$NAME/agent.md"
323
+
324
+ # Skip prompt entirely if non-interactive (no TTY) or AGENT_TEAM_NO_CONFIRM=1
325
+ if [ -t 0 ] && [ "${AGENT_TEAM_NO_CONFIRM:-0}" != "1" ]; then
326
+ read -r -p "Define tools and bindings now? (y/N): " DEFINE_TOOLS
327
+ DEFINE_TOOLS="${DEFINE_TOOLS:-N}"
328
+
329
+ if [[ "$DEFINE_TOOLS" =~ ^[Yy]$ ]]; then
330
+ echo ""
331
+ echo "Which tools is this agent using?"
332
+ echo " Enter comma-separated names (e.g., gmail, drive, attio, heyreach)."
333
+ echo " Or press Enter to skip."
334
+ read -r -p " > " TOOLS_LINE
335
+
336
+ if [ -n "$TOOLS_LINE" ]; then
337
+ # Normalize: split by comma, trim whitespace, lowercase
338
+ TOOL_NAMES=()
339
+ IFS=',' read -ra RAW_TOOLS <<< "$TOOLS_LINE"
340
+ for raw in "${RAW_TOOLS[@]}"; do
341
+ clean="$(echo "$raw" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' | tr '[:upper:]' '[:lower:]')"
342
+ if [[ -n "$clean" && "$clean" =~ ^[a-z][a-z0-9_-]*$ ]]; then
343
+ TOOL_NAMES+=("$clean")
344
+ elif [ -n "$clean" ]; then
345
+ echo " WARN: skipping invalid tool name '$clean' (must be lowercase, start with letter, only [a-z0-9_-])" >&2
346
+ fi
347
+ done
348
+
349
+ if [ ${#TOOL_NAMES[@]} -gt 0 ]; then
350
+ {
351
+ echo ""
352
+ echo "## Tools and bindings"
353
+ echo ""
354
+ echo "Tool bindings expected by this agent. Until the Phase 2 env-merge loader ships, configure them by hand: mirror the YAML block below into \`<agent>/config.yaml\` under a \`tools:\` key, and put the env var values in workspace \`/.env\` (or \`<agent>/.env\` for agent-scoped overrides)."
355
+ echo ""
356
+ echo "Fill in each tool's bindings below. Schema per conventions.md § \"Tool bindings\": each tool has a \`env_var\` (the env-var name the runtime reads), a \`required\` flag (true/false), and a one-line \`description\`."
357
+ echo ""
358
+ echo '```yaml'
359
+ for tool in "${TOOL_NAMES[@]}"; do
360
+ tool_env=$(echo "$tool" | tr '[:lower:]-' '[:upper:]_')
361
+ echo "$tool:"
362
+ echo " env_var: ${tool_env}_API_KEY # TODO: confirm env var name"
363
+ echo " required: true # TODO: confirm required vs optional"
364
+ echo " description: \"\" # TODO: one-line description"
365
+ done
366
+ echo '```'
367
+ } >> "$NEW_AGENT_MD"
368
+
369
+ echo ""
370
+ echo "✓ Added '## Tools and bindings' to $NEW_AGENT_MD with stubs for: ${TOOL_NAMES[*]}"
371
+ echo " Edit agent.md to fill in env_var names + descriptions, then mirror the tools: block into <agent>/config.yaml by hand."
372
+ echo " (Phase 2 will add an env-merge loader + automated config.yaml/.env wiring; until then this step is manual.)"
373
+ else
374
+ echo " No valid tool names provided. Skipping section."
375
+ fi
376
+ else
377
+ echo " Empty input. Skipping section."
378
+ fi
379
+ else
380
+ echo "(Skipped tool definition. Add a '## Tools and bindings' section manually later if needed.)"
381
+ fi
382
+ else
383
+ # Non-interactive: skip the prompt entirely. User can add the section manually.
384
+ :
385
+ fi
386
+ # === End tool bindings prompt ===
387
+
388
+ # === Plans directory ===
389
+ PLANS_DIR="$TARGET/plans"
390
+ mkdir -p "$PLANS_DIR"
391
+ touch "$PLANS_DIR/.gitkeep"
392
+ echo "Created: $FN/$NAME/plans/"
393
+
394
+ # === Slash command file ===
395
+ COMMANDS_DIR="$ROOT/.claude/commands"
396
+ mkdir -p "$COMMANDS_DIR"
397
+ SLASH_CMD_FILE="$COMMANDS_DIR/$NAME.md"
398
+
399
+ if [ ! -f "$SLASH_CMD_FILE" ]; then
400
+ write_slash_command "$SLASH_CMD_FILE" "$FN" "$NAME"
401
+ echo "Created: .claude/commands/$NAME.md"
402
+ fi
403
+
404
+ echo ""
405
+ echo "✓ Agent '$FN/$NAME' created at $TARGET"
406
+ echo ""
407
+ echo "Next steps:"
408
+ echo " 1. Fill in $TARGET/agent.md (purpose, inputs, plans, subagents, tools, outputs)"
409
+ echo " 2. Fill in $TARGET/config.yaml (guideline_refs, tools)"
410
+ echo " 3. Add subagents: cp $TARGET/subagents/_template.md $TARGET/subagents/<name>.md and fill in"
411
+ echo " 4. Add agent-scoped MCPs to $TARGET/.mcp.json if needed (HeyReach, Apollo, etc.)"
412
+ echo " 5. Update $TARGET/README.md with a real description"
413
+ echo " 6. Add at least one plan to $TARGET/plans/ (e.g., $TARGET/plans/<plan-name>.yaml)"
414
+ echo " 7. Edit .claude/commands/$NAME.md to fill in the description"
415
+ echo ""
416
+ echo "Reference: see gtm/sdr/ for a complete example."
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env bash
2
+ # rename-agent.sh — renames a global agent everywhere it appears
3
+ # Usage: bash scripts/rename-agent.sh <function> <old> <new>
4
+
5
+ set -euo pipefail
6
+
7
+ if [ $# -ne 3 ]; then
8
+ echo "Usage: $0 <function> <old> <new>"
9
+ exit 1
10
+ fi
11
+
12
+ FN="$1"
13
+ OLD="$2"
14
+ NEW="$3"
15
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
16
+
17
+ if ! [[ "$NEW" =~ ^[a-z][a-z0-9-]*$ ]]; then
18
+ echo "ERROR: New slug must be lowercase, alphanumeric + hyphens, starting with a letter."
19
+ exit 1
20
+ fi
21
+
22
+ OLD_DIR="$ROOT/$FN/$OLD"
23
+ NEW_DIR="$ROOT/$FN/$NEW"
24
+
25
+ if [ ! -d "$OLD_DIR" ]; then
26
+ echo "ERROR: Agent '$FN/$OLD' not found at $OLD_DIR"
27
+ exit 1
28
+ fi
29
+
30
+ if [ -d "$NEW_DIR" ]; then
31
+ echo "ERROR: Agent '$FN/$NEW' already exists at $NEW_DIR"
32
+ exit 1
33
+ fi
34
+
35
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
36
+
37
+ mv "$OLD_DIR" "$NEW_DIR"
38
+ echo " Moved: $FN/$OLD/ → $FN/$NEW/"
39
+
40
+ SED_INPLACE=(-i)
41
+ if [[ "$(uname)" == "Darwin" ]]; then
42
+ SED_INPLACE=(-i '')
43
+ fi
44
+
45
+ # Broad replace on prose files only — these are markdown stubs where the
46
+ # old slug appears in headings / paths and we want every reference updated.
47
+ for f in "$NEW_DIR"/agent.md "$NEW_DIR"/README.md "$NEW_DIR"/asset-references.md; do
48
+ [ -f "$f" ] && sed "${SED_INPLACE[@]}" "s|$OLD|$NEW|g" "$f"
49
+ done
50
+
51
+ # config.yaml: only the identity field. A broad replace would corrupt env-var
52
+ # names, comments, and any value that legitimately contains $OLD.
53
+ CFG="$NEW_DIR/config.yaml"
54
+ if [ -f "$CFG" ]; then
55
+ sed "${SED_INPLACE[@]}" "s|^agent: $FN/$OLD$|agent: $FN/$NEW|" "$CFG"
56
+ fi
57
+
58
+ while IFS= read -r f; do
59
+ case "$f" in
60
+ *"_archive/"*) continue ;;
61
+ *"/logs/"*) continue ;;
62
+ *"/log/runs/"*) continue ;;
63
+ *"/log/feedback/"*) continue ;;
64
+ *"/playbook/"*) continue ;;
65
+ *) sed "${SED_INPLACE[@]}" "s|$FN/$OLD|$FN/$NEW|g" "$f" ;;
66
+ esac
67
+ done < <(grep -rl "$FN/$OLD" --include='*.md' --include='*.yaml' --include='*.json' --include='*.sh' --include='*.txt' "$ROOT" 2>/dev/null)
68
+
69
+ OLD_CMD="$ROOT/.claude/commands/$OLD.md"
70
+ NEW_CMD="$ROOT/.claude/commands/$NEW.md"
71
+ if [ -f "$OLD_CMD" ]; then
72
+ mv "$OLD_CMD" "$NEW_CMD"
73
+ sed "${SED_INPLACE[@]}" "s|$OLD|$NEW|g" "$NEW_CMD"
74
+ echo " Renamed slash command: .claude/commands/$OLD.md → $NEW.md"
75
+ fi
76
+
77
+ LOG_DIR="$ROOT/chief-of-staff/logs/$(date +%Y-%m)"
78
+ mkdir -p "$LOG_DIR"
79
+ LOG_FILE="$LOG_DIR/operations-$(date +%Y-%m-%d).md"
80
+ {
81
+ echo ""
82
+ echo "## $TIMESTAMP — rename-agent: $FN/$OLD → $FN/$NEW"
83
+ } >> "$LOG_FILE"
84
+
85
+ echo ""
86
+ echo "Files mentioning '$OLD' that were NOT auto-updated (review manually):"
87
+ grep -rln "$OLD" "$ROOT" 2>/dev/null | grep -v "_archive\|/logs/\|/log/runs/\|/log/feedback/\|/playbook/" | sed "s|$ROOT/| - |" || echo " (none found)"
88
+
89
+ echo ""
90
+ echo "✓ Rename complete."
91
+ echo " Operation log: $LOG_FILE"
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ # save-state.sh — write workspace state.md
3
+ #
4
+ # In v1, workspace = project. state.md lives at workspace root.
5
+ # Format: see conventions.md § "State file format". Normally Claude writes
6
+ # state.md directly when asked. This script exists for external invocation
7
+ # or scripted updates.
8
+ #
9
+ # Usage: bash scripts/save-state.sh "<state notes (multiline OK)>"
10
+
11
+ set -euo pipefail
12
+
13
+ if [ $# -lt 1 ]; then
14
+ echo "Usage: $0 \"<state notes>\""
15
+ exit 1
16
+ fi
17
+
18
+ NOTES="$*"
19
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
20
+ STATE_FILE="$ROOT/state.md"
21
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
22
+
23
+ cat > "$STATE_FILE" << EOF
24
+ ---
25
+ updated: $TIMESTAMP
26
+ ---
27
+
28
+ $NOTES
29
+ EOF
30
+
31
+ echo "✓ Saved workspace state"
32
+ echo " $STATE_FILE"
package/agents/critic.md DELETED
@@ -1,74 +0,0 @@
1
- ---
2
- name: critic
3
- description: "Reviews an outreach draft for tone, accuracy, brand fit, risk, compliance, and do-and-don't violations. Returns pass/fail with specific feedback. Used by the sdr skill. Does not rewrite drafts — the writer iterates."
4
- version: "0.1.0"
5
- owner_skill: sdr
6
- ---
7
-
8
- # Critic
9
-
10
- ## Role
11
-
12
- Review a draft for tone, accuracy, brand fit, risk, compliance, and do-and-don't violations. Returns pass/fail with specific feedback. Does not rewrite — that's the writer's job on next iteration.
13
-
14
- ## Inputs
15
-
16
- - `draft` (object): output from the writer
17
- - `prospect` (object): the enriched prospect
18
- - `voice` (markdown): project's voice doc
19
- - `icps` (markdown): all relevant persona docs
20
- - `do_and_dont` (markdown, optional)
21
- - `compliance` (markdown, optional)
22
- - `competitors` (markdown, optional)
23
- - `lessons` (markdown): relevant project + global lessons
24
- - `iteration` (int): current iteration count (orchestrator caps at 2)
25
-
26
- ## Output
27
-
28
- ```yaml
29
- verdict: pass | fail
30
- score: 0-10
31
- issues:
32
- - severity: blocker | major | minor
33
- category: voice | accuracy | risk | cta | length | personalization | compliance | do-and-dont | competitor-handling
34
- description: "..."
35
- suggested_fix: "..."
36
- voice_match: 0-10
37
- personalization: 0-10
38
- risk_flags: []
39
- ```
40
-
41
- `verdict: pass` requires:
42
- - Zero blocker issues
43
- - Zero major issues
44
- - voice_match ≥ 7
45
- - personalization ≥ 6
46
- - No risk flags
47
- - Zero do-and-dont violations
48
- - Zero compliance violations
49
-
50
- Otherwise `fail`. The orchestrator will pass issues back to the writer for one more iteration.
51
-
52
- ## Tools
53
-
54
- None. Pure review from inputs.
55
-
56
- ## Boundaries
57
-
58
- - Do NOT rewrite the draft. List issues; the writer rewrites.
59
- - Do NOT invent facts to test against. Use only inputs.
60
- - Be strict on accuracy, risk, compliance, and do-and-dont; less strict on style preferences (those go in `minor`).
61
- - A "blocker" would embarrass the user. A "major" hurts effectiveness. A "minor" is polish.
62
-
63
- ## Risk categories to flag
64
-
65
- - Unverifiable claims about the prospect or company
66
- - Aggressive, manipulative, or pressuring language
67
- - Misrepresentation of sender's relationship to prospect
68
- - Compliance issues (GDPR, CAN-SPAM, platform ToS — see `compliance.md` if provided)
69
- - Anything that would damage the project's brand if seen publicly
70
- - Improper handling of competitor mentions (see `competitors.md` if provided)
71
-
72
- ## Quality bar
73
-
74
- A pass means: I, the user, would be willing to put my name on this and send it. No exceptions.
@@ -1,56 +0,0 @@
1
- ---
2
- name: enricher
3
- description: "Fills in missing fields on existing prospects (recent posts, company news, mutual signals) via Apollo, HeyReach, and web search. Used by the sdr skill before drafting outreach. Does not score, does not filter, does not contact."
4
- version: "0.1.0"
5
- owner_skill: sdr
6
- ---
7
-
8
- # Enricher
9
-
10
- ## Role
11
-
12
- Take an existing prospect list and fill in missing fields needed for personalized outreach. Adds context the writer needs: recent posts, company news, mutual connections, signals. Does not contact, does not score.
13
-
14
- ## Inputs
15
-
16
- - `prospects` (array): list from prospector or external source
17
- - `required_fields` (array): which fields must be filled — e.g., `[recent_post, company_news, role_tenure]`
18
- - `enrichment_depth` (string): `light` (search results only) | `deep` (web fetch + multi-source)
19
-
20
- ## Output
21
-
22
- Same prospects array, with new fields added. Mark unfillable fields explicitly:
23
-
24
- ```yaml
25
- prospects:
26
- - name: "Alice Example"
27
- role: "Head of Growth"
28
- company: "ExampleCo"
29
- enrichment:
30
- recent_post:
31
- url: "..."
32
- snippet: "..."
33
- date: "2026-04-22"
34
- company_news:
35
- - { headline: "...", url: "...", date: "..." }
36
- role_tenure_months: 8
37
- mutual_signals: []
38
- enrichment_status: complete # complete | partial | failed
39
- enrichment_notes: ""
40
- ```
41
-
42
- ## Tools
43
-
44
- - Apollo.io MCP: `apollo_people_match`, `apollo_organizations_enrich`, `apollo_organizations_job_postings`
45
- - Web search + web_fetch: recent posts, news, signals
46
- - HeyReach MCP: `get_lead` for LinkedIn URL-based lookup
47
-
48
- ## Boundaries
49
-
50
- - Do NOT score or filter — return everything, even partially enriched.
51
- - Do NOT make assumptions about missing fields. Mark explicitly.
52
- - Cap web_fetch at 3 sources per prospect for deep enrichment.
53
-
54
- ## Quality bar
55
-
56
- A prospect with `enrichment_status: complete` must have all required fields. Otherwise `partial` with notes on what's missing.