@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.
- package/README.md +77 -215
- package/agents/lesson-drafter.md +3 -8
- package/agents/pattern-detector.md +0 -1
- package/bin/roster.js +7233 -1197
- package/data/plan-ceilings.yaml +57 -0
- package/package.json +8 -3
- package/skills/chief-of-staff/SKILL.md +199 -59
- 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/hooks/banner.sh +47 -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 +188 -173
- 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/logs/cron/.gitkeep +1 -0
- package/templates/scaffold/ops/EXPERT.md +5 -5
- package/templates/scaffold/scripts/audit-agent.sh +326 -0
- package/templates/scaffold/scripts/audit-repo.sh +218 -0
- package/templates/scaffold/scripts/create-function.sh +267 -0
- package/templates/scaffold/scripts/lib/README.md +6 -1
- package/templates/scaffold/scripts/lib/bindings-prompt.sh +53 -0
- package/templates/scaffold/scripts/lib/functions.sh +17 -5
- package/templates/scaffold/scripts/new-agent.sh +416 -0
- 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 -46
- 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/new-project.sh +0 -125
- /package/templates/scaffold/gtm/{sdr/playbook/.gitkeep → .gitkeep} +0 -0
- /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.
|
package/agents/enricher.md
DELETED
|
@@ -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.
|