@firatcand/roster 0.1.0 → 0.4.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 +7 -2
- package/bin/roster.js +7092 -1167
- package/data/plan-ceilings.yaml +57 -0
- package/package.json +8 -2
- package/skills/chief-of-staff/SKILL.md +158 -2
- package/templates/hooks/banner.sh +47 -0
- package/templates/scaffold/conventions.md +35 -8
- package/templates/scaffold/dreamer/README.md +1 -1
- package/templates/scaffold/gtm/sdr/README.md +1 -6
- package/templates/scaffold/logs/cron/.gitkeep +1 -0
- package/templates/scaffold/ops/EXPERT.md +5 -5
- package/templates/scaffold/scripts/archive-project.sh +98 -0
- package/templates/scaffold/scripts/audit-agent.sh +299 -0
- package/templates/scaffold/scripts/audit-project.sh +361 -0
- package/templates/scaffold/scripts/audit-repo.sh +240 -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 +136 -0
- package/templates/scaffold/scripts/lib/functions.sh +17 -5
- package/templates/scaffold/scripts/new-agent-instance.sh +114 -0
- package/templates/scaffold/scripts/new-agent.sh +410 -0
- package/templates/scaffold/scripts/remove-agent-from-project.sh +67 -0
- package/templates/scaffold/scripts/rename-project.sh +118 -0
- package/templates/scaffold/scripts/unarchive-project.sh +115 -0
|
@@ -0,0 +1,114 @@
|
|
|
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"
|
|
@@ -0,0 +1,410 @@
|
|
|
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 project'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> for <project>\` or \`run <plan-name> on <project>\`**:
|
|
74
|
+
- Load \`$fn/$name/plans/<plan-name>.yaml\`. If it doesn't exist, list available plans and ask user to pick.
|
|
75
|
+
- Load \`projects/<project>/CLAUDE.md\` and relevant guidelines.
|
|
76
|
+
- Load \`$fn/$name/projects/<project>/config/default.yaml\`.
|
|
77
|
+
- Validate that all required tool bindings are non-TODO. Abort if not.
|
|
78
|
+
- Execute the plan steps. Substitute \`\${tools.X.Y}\`, \`\${inputs.X}\`, \`\${config.X}\`.
|
|
79
|
+
- Log to \`$fn/$name/projects/<project>/log/runs/<YYYY-MM>/<YYYY-MM-DD-HHMM>.md\`.
|
|
80
|
+
- Surface HITL approvals per the plan's approval_channel.
|
|
81
|
+
|
|
82
|
+
2. **If only a project is named (no plan)**:
|
|
83
|
+
- List available plans from \`$fn/$name/plans/\` with descriptions. Ask user to pick.
|
|
84
|
+
|
|
85
|
+
3. **If neither plan nor project is named**:
|
|
86
|
+
- List available projects and plans. Ask user to specify both.
|
|
87
|
+
|
|
88
|
+
4. **For ad-hoc strategic work**: suggest invoking \`$fn/EXPERT.md\` instead.
|
|
89
|
+
|
|
90
|
+
## Constraints
|
|
91
|
+
|
|
92
|
+
- Only run plans that exist as files in \`$fn/$name/plans/\`.
|
|
93
|
+
- Don't bypass approval gates.
|
|
94
|
+
- File writes go to the instance's \`log/runs/\` unless the plan explicitly writes elsewhere.
|
|
95
|
+
EOF
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if ! is_valid_function "$FN"; then
|
|
99
|
+
echo "ERROR: '$FN' is not a registered function." >&2
|
|
100
|
+
echo "Registered functions:" >&2
|
|
101
|
+
read_functions | sed 's/^/ - /' >&2
|
|
102
|
+
echo "" >&2
|
|
103
|
+
echo "To add a new function: bash scripts/create-function.sh $FN" >&2
|
|
104
|
+
exit 1
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
if ! [[ "$NAME" =~ ^[a-z][a-z0-9-]*$ ]]; then
|
|
108
|
+
echo "ERROR: Agent name must be lowercase, alphanumeric + hyphens." >&2
|
|
109
|
+
exit 1
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# --slash-only branch: write only the slash command file and exit.
|
|
113
|
+
# Requires agent tree to exist; refuses to clobber an existing slash command.
|
|
114
|
+
if [ "$SLASH_ONLY" = "1" ]; then
|
|
115
|
+
if [ ! -d "$TARGET" ]; then
|
|
116
|
+
echo "ERROR: agent tree '$FN/$NAME' does not exist at $TARGET." >&2
|
|
117
|
+
echo " --slash-only is a recovery flag — create the agent tree first with:" >&2
|
|
118
|
+
echo " bash scripts/new-agent.sh $FN $NAME" >&2
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
COMMANDS_DIR="$ROOT/.claude/commands"
|
|
122
|
+
SLASH_CMD_FILE="$COMMANDS_DIR/$NAME.md"
|
|
123
|
+
if [ -f "$SLASH_CMD_FILE" ]; then
|
|
124
|
+
echo "ERROR: slash command already exists at .claude/commands/$NAME.md. Refusing to clobber." >&2
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
127
|
+
mkdir -p "$COMMANDS_DIR"
|
|
128
|
+
write_slash_command "$SLASH_CMD_FILE" "$FN" "$NAME"
|
|
129
|
+
echo "Created: .claude/commands/$NAME.md"
|
|
130
|
+
exit 0
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
if [ -d "$TARGET" ]; then
|
|
134
|
+
echo "ERROR: Agent '$FN/$NAME' already exists at $TARGET" >&2
|
|
135
|
+
exit 1
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
echo "Creating agent: $FN/$NAME"
|
|
139
|
+
|
|
140
|
+
mkdir -p "$TARGET"/{subagents,playbook,logs,projects/_template,.claude/skills,.claude/plugins}
|
|
141
|
+
|
|
142
|
+
# agent.md stub
|
|
143
|
+
cat > "$TARGET/agent.md" << EOF
|
|
144
|
+
# $NAME
|
|
145
|
+
|
|
146
|
+
## Purpose
|
|
147
|
+
|
|
148
|
+
<One paragraph: what this agent does, why it exists.>
|
|
149
|
+
|
|
150
|
+
## Inputs
|
|
151
|
+
|
|
152
|
+
The orchestrator expects:
|
|
153
|
+
|
|
154
|
+
- \`project\`: project slug
|
|
155
|
+
- <other inputs>
|
|
156
|
+
|
|
157
|
+
Read at runtime:
|
|
158
|
+
|
|
159
|
+
- \`agent.md\` (this file)
|
|
160
|
+
- \`projects/<project>/<this-agent>/config/default.yaml\`
|
|
161
|
+
- \`projects/<project>/CLAUDE.md\`
|
|
162
|
+
- \`projects/<project>/guidelines/voice.md\`
|
|
163
|
+
- <other guidelines this agent uses>
|
|
164
|
+
- \`<this-agent>/projects/<project>/playbook/\` — project-scoped lessons
|
|
165
|
+
- \`<this-agent>/playbook/\` — global lessons
|
|
166
|
+
|
|
167
|
+
## Steps
|
|
168
|
+
|
|
169
|
+
1. Resolve config and context
|
|
170
|
+
2. <step>
|
|
171
|
+
3. <step>
|
|
172
|
+
|
|
173
|
+
## Subagents
|
|
174
|
+
|
|
175
|
+
- <subagent-name>.md — <one-liner>
|
|
176
|
+
|
|
177
|
+
## Tools
|
|
178
|
+
|
|
179
|
+
Agent-scoped MCPs at \`<this-agent>/.mcp.json\`:
|
|
180
|
+
- <tool/MCP> — <purpose>
|
|
181
|
+
|
|
182
|
+
Universal MCPs (Slack, Google Drive) inherited from agent-team root.
|
|
183
|
+
|
|
184
|
+
## Outputs
|
|
185
|
+
|
|
186
|
+
Run file at \`projects/<project>/<this-agent>/log/runs/<YYYY-MM>/<YYYY-MM-DD-HHMM>.md\`. See \`conventions.md\` § "Run file format".
|
|
187
|
+
|
|
188
|
+
## Approval
|
|
189
|
+
|
|
190
|
+
\`approval_channel: auto\` — in-session if interactive, Slack \`#${FN}\` if cron (resolved via \`SLACK_HITL_CHANNEL_$(echo "$FN" | tr '[:lower:]-' '[:upper:]_')\` in \`.env\`).
|
|
191
|
+
|
|
192
|
+
## Lessons protocol
|
|
193
|
+
|
|
194
|
+
Log candidate lessons inline in run output under \`## Candidate lessons\`. Don't write to \`playbook/\` directly during runs — that's the dreamer's job.
|
|
195
|
+
|
|
196
|
+
## Failure modes
|
|
197
|
+
|
|
198
|
+
- <known failure mode>: <handling>
|
|
199
|
+
EOF
|
|
200
|
+
|
|
201
|
+
# README
|
|
202
|
+
cat > "$TARGET/README.md" << EOF
|
|
203
|
+
# $NAME
|
|
204
|
+
|
|
205
|
+
<One-line description.>
|
|
206
|
+
|
|
207
|
+
## Files
|
|
208
|
+
|
|
209
|
+
- \`agent.md\` — orchestrator contract
|
|
210
|
+
- \`subagents/\` — specialized roles
|
|
211
|
+
- \`playbook/\` — global lessons (one file per lesson)
|
|
212
|
+
- \`logs/\` — agent-level operational logs
|
|
213
|
+
- \`.claude/\` — agent-scoped skills, plugins
|
|
214
|
+
- \`.mcp.json\` — agent-scoped MCPs (CREATE THIS — see template comment)
|
|
215
|
+
- \`projects/\` — per-project instances
|
|
216
|
+
|
|
217
|
+
## Invocation
|
|
218
|
+
|
|
219
|
+
From a project instance session:
|
|
220
|
+
|
|
221
|
+
\`\`\`bash
|
|
222
|
+
cd $FN/$NAME/projects/<project>/
|
|
223
|
+
claude
|
|
224
|
+
"Run $NAME on <inputs>"
|
|
225
|
+
\`\`\`
|
|
226
|
+
|
|
227
|
+
From cron: see ROS-39 (Phase 2.5 scheduling primitives — wrapper layout + install script land then).
|
|
228
|
+
|
|
229
|
+
## Configuration
|
|
230
|
+
|
|
231
|
+
Per-project: \`projects/<proj>/$FN/$NAME/config/default.yaml\` (created by \`new-agent-instance.sh\`).
|
|
232
|
+
|
|
233
|
+
## Outputs
|
|
234
|
+
|
|
235
|
+
\`projects/<proj>/$FN/$NAME/log/runs/<YYYY-MM>/<YYYY-MM-DD-HHMM>.md\`
|
|
236
|
+
EOF
|
|
237
|
+
|
|
238
|
+
# .mcp.json stub
|
|
239
|
+
cat > "$TARGET/.mcp.json" << EOF
|
|
240
|
+
{
|
|
241
|
+
"_comment": "Agent-scoped MCPs for $NAME. Available when working in this agent's tree (including project instances). Add MCPs this agent specifically needs. Universal MCPs (Slack, Google Drive) are inherited from agent-team/.mcp.json.",
|
|
242
|
+
"mcpServers": {}
|
|
243
|
+
}
|
|
244
|
+
EOF
|
|
245
|
+
|
|
246
|
+
# .claude/settings.json
|
|
247
|
+
cat > "$TARGET/.claude/settings.json" << EOF
|
|
248
|
+
{
|
|
249
|
+
"_comment": "Agent-scoped Claude Code settings for $NAME."
|
|
250
|
+
}
|
|
251
|
+
EOF
|
|
252
|
+
|
|
253
|
+
# Subagent template
|
|
254
|
+
cat > "$TARGET/subagents/_template.md" << 'EOF'
|
|
255
|
+
# <Subagent Name>
|
|
256
|
+
|
|
257
|
+
## Role
|
|
258
|
+
<One paragraph: narrow job, single responsibility.>
|
|
259
|
+
|
|
260
|
+
## Inputs
|
|
261
|
+
<What the orchestrator passes in.>
|
|
262
|
+
|
|
263
|
+
## Output
|
|
264
|
+
<Structured output the orchestrator can parse.>
|
|
265
|
+
|
|
266
|
+
## Tools
|
|
267
|
+
<Named tools this subagent uses.>
|
|
268
|
+
|
|
269
|
+
## Boundaries
|
|
270
|
+
<What this subagent does NOT do.>
|
|
271
|
+
|
|
272
|
+
## Quality bar
|
|
273
|
+
<Specific criteria for acceptable output.>
|
|
274
|
+
EOF
|
|
275
|
+
|
|
276
|
+
# Project instance template
|
|
277
|
+
mkdir -p "$TARGET/projects/_template/config"
|
|
278
|
+
mkdir -p "$TARGET/projects/_template/playbook"
|
|
279
|
+
mkdir -p "$TARGET/projects/_template/log/runs"
|
|
280
|
+
mkdir -p "$TARGET/projects/_template/log/feedback"
|
|
281
|
+
|
|
282
|
+
cat > "$TARGET/projects/_template/config/default.yaml" << EOF
|
|
283
|
+
---
|
|
284
|
+
agent: $NAME
|
|
285
|
+
project: <project-slug>
|
|
286
|
+
created: <YYYY-MM-DD>
|
|
287
|
+
last_modified: <YYYY-MM-DD>
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
# $NAME Config — <Project>
|
|
291
|
+
|
|
292
|
+
# See $FN/$NAME/agent.md § "Inputs" for required fields.
|
|
293
|
+
# Use prose comments to explain "why" alongside "what".
|
|
294
|
+
EOF
|
|
295
|
+
|
|
296
|
+
cat > "$TARGET/projects/_template/asset-references.md" << EOF
|
|
297
|
+
# Asset references — $NAME / <project>
|
|
298
|
+
|
|
299
|
+
This agent uses these assets from \`projects/<project>/guidelines/asset-links.md\`:
|
|
300
|
+
|
|
301
|
+
- <e.g., specific asset by name>
|
|
302
|
+
EOF
|
|
303
|
+
|
|
304
|
+
touch "$TARGET/playbook/.gitkeep"
|
|
305
|
+
touch "$TARGET/logs/.gitkeep"
|
|
306
|
+
touch "$TARGET/.claude/skills/.gitkeep"
|
|
307
|
+
touch "$TARGET/.claude/plugins/.gitkeep"
|
|
308
|
+
touch "$TARGET/projects/_template/playbook/.gitkeep"
|
|
309
|
+
touch "$TARGET/projects/_template/log/runs/.gitkeep"
|
|
310
|
+
touch "$TARGET/projects/_template/log/feedback/.gitkeep"
|
|
311
|
+
|
|
312
|
+
# === Tool bindings prompt (added by tool-bindings workflow) ===
|
|
313
|
+
# Ask the user whether to define tools now. If yes, collect tool names and scaffold
|
|
314
|
+
# a stub `## Tools and bindings` section in the new agent.md.
|
|
315
|
+
|
|
316
|
+
echo ""
|
|
317
|
+
NEW_AGENT_MD="$ROOT/$FN/$NAME/agent.md"
|
|
318
|
+
|
|
319
|
+
# Skip prompt entirely if non-interactive (no TTY) or AGENT_TEAM_NO_CONFIRM=1
|
|
320
|
+
if [ -t 0 ] && [ "${AGENT_TEAM_NO_CONFIRM:-0}" != "1" ]; then
|
|
321
|
+
read -r -p "Define tools and bindings now? (y/N): " DEFINE_TOOLS
|
|
322
|
+
DEFINE_TOOLS="${DEFINE_TOOLS:-N}"
|
|
323
|
+
|
|
324
|
+
if [[ "$DEFINE_TOOLS" =~ ^[Yy]$ ]]; then
|
|
325
|
+
echo ""
|
|
326
|
+
echo "Which tools is this agent using?"
|
|
327
|
+
echo " Enter comma-separated names (e.g., gmail, drive, attio, heyreach)."
|
|
328
|
+
echo " Or press Enter to skip."
|
|
329
|
+
read -r -p " > " TOOLS_LINE
|
|
330
|
+
|
|
331
|
+
if [ -n "$TOOLS_LINE" ]; then
|
|
332
|
+
# Normalize: split by comma, trim whitespace, lowercase
|
|
333
|
+
TOOL_NAMES=()
|
|
334
|
+
IFS=',' read -ra RAW_TOOLS <<< "$TOOLS_LINE"
|
|
335
|
+
for raw in "${RAW_TOOLS[@]}"; do
|
|
336
|
+
clean="$(echo "$raw" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' | tr '[:upper:]' '[:lower:]')"
|
|
337
|
+
if [[ -n "$clean" && "$clean" =~ ^[a-z][a-z0-9_-]*$ ]]; then
|
|
338
|
+
TOOL_NAMES+=("$clean")
|
|
339
|
+
elif [ -n "$clean" ]; then
|
|
340
|
+
echo " WARN: skipping invalid tool name '$clean' (must be lowercase, start with letter, only [a-z0-9_-])" >&2
|
|
341
|
+
fi
|
|
342
|
+
done
|
|
343
|
+
|
|
344
|
+
if [ ${#TOOL_NAMES[@]} -gt 0 ]; then
|
|
345
|
+
{
|
|
346
|
+
echo ""
|
|
347
|
+
echo "## Tools and bindings"
|
|
348
|
+
echo ""
|
|
349
|
+
echo "Per-project tool bindings expected by this agent. Chief-of-staff prompts for these when scaffolding a new agent-instance. Values land in \`projects/<project>/config/default.yaml\` under a \`tools:\` key."
|
|
350
|
+
echo ""
|
|
351
|
+
echo "Fill in each tool's bindings below. Schema: each binding has a \`required\` flag (true/false) and a \`description\`."
|
|
352
|
+
echo ""
|
|
353
|
+
echo '```yaml'
|
|
354
|
+
for tool in "${TOOL_NAMES[@]}"; do
|
|
355
|
+
echo "$tool:"
|
|
356
|
+
echo " # TODO: define bindings"
|
|
357
|
+
echo " # <binding_name>:"
|
|
358
|
+
echo " # required: true"
|
|
359
|
+
echo " # description: \"...\""
|
|
360
|
+
done
|
|
361
|
+
echo '```'
|
|
362
|
+
} >> "$NEW_AGENT_MD"
|
|
363
|
+
|
|
364
|
+
echo ""
|
|
365
|
+
echo "✓ Added '## Tools and bindings' to $NEW_AGENT_MD with stubs for: ${TOOL_NAMES[*]}"
|
|
366
|
+
echo " Edit agent.md to fill in actual bindings before adding instances."
|
|
367
|
+
else
|
|
368
|
+
echo " No valid tool names provided. Skipping section."
|
|
369
|
+
fi
|
|
370
|
+
else
|
|
371
|
+
echo " Empty input. Skipping section."
|
|
372
|
+
fi
|
|
373
|
+
else
|
|
374
|
+
echo "(Skipped tool definition. Add a '## Tools and bindings' section manually later if needed.)"
|
|
375
|
+
fi
|
|
376
|
+
else
|
|
377
|
+
# Non-interactive: skip the prompt entirely. User can add the section manually.
|
|
378
|
+
:
|
|
379
|
+
fi
|
|
380
|
+
# === End tool bindings prompt ===
|
|
381
|
+
|
|
382
|
+
# === Plans directory ===
|
|
383
|
+
PLANS_DIR="$TARGET/plans"
|
|
384
|
+
mkdir -p "$PLANS_DIR"
|
|
385
|
+
touch "$PLANS_DIR/.gitkeep"
|
|
386
|
+
echo "Created: $FN/$NAME/plans/"
|
|
387
|
+
|
|
388
|
+
# === Slash command file ===
|
|
389
|
+
COMMANDS_DIR="$ROOT/.claude/commands"
|
|
390
|
+
mkdir -p "$COMMANDS_DIR"
|
|
391
|
+
SLASH_CMD_FILE="$COMMANDS_DIR/$NAME.md"
|
|
392
|
+
|
|
393
|
+
if [ ! -f "$SLASH_CMD_FILE" ]; then
|
|
394
|
+
write_slash_command "$SLASH_CMD_FILE" "$FN" "$NAME"
|
|
395
|
+
echo "Created: .claude/commands/$NAME.md"
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
echo ""
|
|
399
|
+
echo "✓ Agent '$FN/$NAME' created at $TARGET"
|
|
400
|
+
echo ""
|
|
401
|
+
echo "Next steps:"
|
|
402
|
+
echo " 1. Fill in $TARGET/agent.md (purpose, inputs, steps, subagents, tools, outputs)"
|
|
403
|
+
echo " 2. Add subagents: cp $TARGET/subagents/_template.md $TARGET/subagents/<name>.md and fill in"
|
|
404
|
+
echo " 3. Add agent-scoped MCPs to $TARGET/.mcp.json if needed (HeyReach, Apollo, etc.)"
|
|
405
|
+
echo " 4. Update $TARGET/README.md with a real description"
|
|
406
|
+
echo " 5. Add at least one plan to $TARGET/plans/ (e.g., $TARGET/plans/<plan-name>.yaml)"
|
|
407
|
+
echo " 6. Edit .claude/commands/$NAME.md to fill in the description"
|
|
408
|
+
echo " 7. Add an instance to a project: bash scripts/new-agent-instance.sh <project> $FN $NAME"
|
|
409
|
+
echo ""
|
|
410
|
+
echo "Reference: see gtm/sdr/ for a complete example."
|
|
@@ -0,0 +1,67 @@
|
|
|
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"
|