@jiggai/recipes 0.2.17 → 0.2.20

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 CHANGED
@@ -56,8 +56,8 @@ openclaw recipes dispatch \
56
56
 
57
57
  ## Commands (high level)
58
58
  - `openclaw recipes list|show|status`
59
- - `openclaw recipes scaffold` (agent → `workspace-<agentId>`)
60
- - `openclaw recipes scaffold-team` (team → `workspace-<teamId>` + `roles/<role>/`)
59
+ - `openclaw recipes scaffold` (agent → `workspace-<agentId>` + writes workspace recipe `~/.openclaw/workspace/recipes/<agentId>.md` by default)
60
+ - `openclaw recipes scaffold-team` (team → `workspace-<teamId>` + `roles/<role>/` + writes workspace recipe `~/.openclaw/workspace/recipes/<teamId>.md` by default)
61
61
  - `openclaw recipes install <idOrSlug> [--yes] [--global|--agent-id <id>|--team-id <id>]` (skills: global or scoped)
62
62
  - `openclaw recipes bind|unbind|bindings` (multi-agent routing)
63
63
  - `openclaw recipes dispatch ...` (request → inbox + ticket + assignment)
@@ -193,6 +193,87 @@ Default tool policy:
193
193
  - allows `group:runtime`
194
194
  - does not deny `exec`
195
195
 
196
+ ---
197
+
198
+ # Vertical packs (bundled team recipes)
199
+
200
+ ## 11) `business-team` (team)
201
+ **Use when:** you want a general-purpose business execution team.
202
+
203
+ Scaffold:
204
+ ```bash
205
+ openclaw recipes scaffold-team business-team --team-id business-team-team --apply-config
206
+ ```
207
+
208
+ Roles:
209
+ - lead, ops, sales, marketing, finance, analyst
210
+
211
+ ## 12) `law-firm-team` (team)
212
+ **Use when:** you want a legal practice workflow: intake → research → drafting → compliance.
213
+
214
+ Scaffold:
215
+ ```bash
216
+ openclaw recipes scaffold-team law-firm-team --team-id law-firm-team-team --apply-config
217
+ ```
218
+
219
+ Roles:
220
+ - lead, intake, researcher, drafter, compliance, ops
221
+
222
+ ## 13) `clinic-team` (team)
223
+ **Use when:** you want a clinic ops workflow: intake/scheduling/billing/compliance/patient education.
224
+
225
+ Scaffold:
226
+ ```bash
227
+ openclaw recipes scaffold-team clinic-team --team-id clinic-team-team --apply-config
228
+ ```
229
+
230
+ Roles:
231
+ - lead, intake, scheduler, billing, compliance, educator
232
+
233
+ ## 14) `construction-team` (team)
234
+ **Use when:** you want a construction delivery workflow: PM/estimation/scheduling/safety/procurement.
235
+
236
+ Scaffold:
237
+ ```bash
238
+ openclaw recipes scaffold-team construction-team --team-id construction-team-team --apply-config
239
+ ```
240
+
241
+ Roles:
242
+ - lead, pm, estimator, scheduler, safety, procurement
243
+
244
+ ## 15) `financial-planner-team` (team)
245
+ **Use when:** you want a financial planning practice workflow.
246
+
247
+ Scaffold:
248
+ ```bash
249
+ openclaw recipes scaffold-team financial-planner-team --team-id financial-planner-team-team --apply-config
250
+ ```
251
+
252
+ Roles:
253
+ - lead, advisor, analyst, tax, insurance, ops
254
+
255
+ ## 16) `stock-trader-team` (team)
256
+ **Use when:** you want a trading workflow: research/signals/risk/journaling.
257
+
258
+ Scaffold:
259
+ ```bash
260
+ openclaw recipes scaffold-team stock-trader-team --team-id stock-trader-team-team --apply-config
261
+ ```
262
+
263
+ Roles:
264
+ - lead, researcher, signals, risk, journal, ops
265
+
266
+ ## 17) `crypto-trader-team` (team)
267
+ **Use when:** you want a crypto trading workflow with onchain research.
268
+
269
+ Scaffold:
270
+ ```bash
271
+ openclaw recipes scaffold-team crypto-trader-team --team-id crypto-trader-team-team --apply-config
272
+ ```
273
+
274
+ Roles:
275
+ - lead, onchain, news, risk, ops, journal
276
+
196
277
  ## Copying and modifying bundled recipes
197
278
  A good workflow is:
198
279
  1) Inspect:
package/docs/COMMANDS.md CHANGED
@@ -41,9 +41,15 @@ openclaw recipes scaffold project-manager --agent-id pm --name "Project Manager"
41
41
  Options:
42
42
  - `--agent-id <id>` (required)
43
43
  - `--name <name>`
44
+ - `--recipe-id <recipeId>` (workspace recipe id to write; default: `<agentId>`)
45
+ - `--auto-increment` (if the workspace recipe id is taken, pick `<agentId>-2/-3/...`)
46
+ - `--overwrite-recipe` (overwrite the generated workspace recipe file if it already exists)
44
47
  - `--overwrite` (overwrite recipe-managed files)
45
48
  - `--apply-config` (write/update `agents.list[]` in OpenClaw config)
46
49
 
50
+ Also writes a workspace recipe file:
51
+ - `~/.openclaw/workspace/recipes/<recipeId>.md`
52
+
47
53
  ## `scaffold-team <recipeId>`
48
54
 
49
55
  ### Cron installation config
@@ -64,9 +70,15 @@ openclaw recipes scaffold-team development-team \
64
70
  Options:
65
71
  - `--team-id <teamId>` (required)
66
72
  - **Must end with `-team`** (enforced)
73
+ - `--recipe-id <recipeId>` (workspace recipe id to write; default: `<teamId>`)
74
+ - `--auto-increment` (if the workspace recipe id is taken, pick `<teamId>-2/-3/...`)
75
+ - `--overwrite-recipe` (overwrite the generated workspace recipe file if it already exists)
67
76
  - `--overwrite`
68
77
  - `--apply-config`
69
78
 
79
+ Also writes a workspace recipe file:
80
+ - `~/.openclaw/workspace/recipes/<recipeId>.md`
81
+
70
82
  Creates a shared team workspace root:
71
83
 
72
84
  - `~/.openclaw/workspace-<teamId>/...`
package/index.ts CHANGED
@@ -2092,7 +2092,10 @@ const recipesPlugin = {
2092
2092
  .argument("<recipeId>", "Recipe id")
2093
2093
  .requiredOption("--agent-id <id>", "Agent id")
2094
2094
  .option("--name <name>", "Agent display name")
2095
+ .option("--recipe-id <recipeId>", "Workspace recipe id to write (default: <agentId>)")
2095
2096
  .option("--overwrite", "Overwrite existing recipe-managed files")
2097
+ .option("--overwrite-recipe", "Overwrite the generated workspace recipe file (workspace/recipes/<agentId>.md) if it already exists")
2098
+ .option("--auto-increment", "If the workspace recipe id is taken, pick the next available <agentId>-2/-3/...")
2096
2099
  .option("--apply-config", "Write the agent into openclaw config (agents.list)")
2097
2100
  .action(async (recipeId: string, options: any) => {
2098
2101
  const loaded = await loadRecipeById(api, recipeId);
@@ -2113,19 +2116,86 @@ const recipesPlugin = {
2113
2116
  return;
2114
2117
  }
2115
2118
 
2119
+ const agentId = String(options.agentId);
2116
2120
  const baseWorkspace = api.config.agents?.defaults?.workspace ?? "~/.openclaw/workspace";
2117
2121
  // Put standalone agent workspaces alongside the default workspace (same parent dir).
2118
- const resolvedWorkspaceRoot = path.resolve(baseWorkspace, "..", `workspace-${options.agentId}`);
2122
+ const resolvedWorkspaceRoot = path.resolve(baseWorkspace, "..", `workspace-${agentId}`);
2123
+
2124
+ // Also create a workspace recipe file for this installed agent.
2125
+ // This establishes a stable, editable recipe id that matches the agent id (no custom- prefix).
2126
+ const recipesDir = path.join(workspaceRoot, "recipes");
2127
+ await ensureDir(recipesDir);
2128
+
2129
+ const overwriteRecipe = !!options.overwriteRecipe;
2130
+ const autoIncrement = !!options.autoIncrement;
2131
+
2132
+ function suggestedRecipeIds(baseId: string) {
2133
+ return [`${baseId}-2`, `${baseId}-3`, `${baseId}-4`];
2134
+ }
2135
+
2136
+ async function recipeIdTaken(id: string) {
2137
+ const filePath = path.join(recipesDir, `${id}.md`);
2138
+ if (await fileExists(filePath)) return true;
2139
+ try {
2140
+ await loadRecipeById(api, id);
2141
+ return true;
2142
+ } catch {
2143
+ return false;
2144
+ }
2145
+ }
2146
+
2147
+ async function pickRecipeId(baseId: string) {
2148
+ if (!(await recipeIdTaken(baseId))) return baseId;
2149
+ if (overwriteRecipe) {
2150
+ const basePath = path.join(recipesDir, `${baseId}.md`);
2151
+ if (!(await fileExists(basePath))) {
2152
+ throw new Error(
2153
+ `Recipe id is already taken by a non-workspace recipe: ${baseId}. Choose a different id (e.g. ${baseId}-2) or pass --auto-increment.`,
2154
+ );
2155
+ }
2156
+ return baseId;
2157
+ }
2158
+
2159
+ if (autoIncrement) {
2160
+ let n = 2;
2161
+ while (n < 1000) {
2162
+ const candidate = `${baseId}-${n}`;
2163
+ if (!(await recipeIdTaken(candidate))) return candidate;
2164
+ n += 1;
2165
+ }
2166
+ throw new Error(`No available recipe id found for ${baseId} (tried up to -999)`);
2167
+ }
2168
+
2169
+ const suggestions = suggestedRecipeIds(baseId);
2170
+ const msg = [
2171
+ `Recipe id already exists: ${baseId}`,
2172
+ `Refusing to overwrite workspace recipe: recipes/${baseId}.md`,
2173
+ `Pick a different recipe id and re-run with --recipe-id. Suggestions: ${suggestions.join(", ")}`,
2174
+ ...suggestions.map((s) => ` openclaw recipes scaffold ${recipeId} --agent-id ${agentId} --recipe-id ${s}`),
2175
+ `Or re-run with --auto-increment to pick ${baseId}-2/-3/... automatically, or pass --overwrite-recipe to overwrite the existing workspace recipe file.`,
2176
+ ].join("\n");
2177
+ throw new Error(msg);
2178
+ }
2179
+
2180
+ const explicitRecipeId = typeof options.recipeId === "string" ? String(options.recipeId).trim() : "";
2181
+ const baseRecipeId = explicitRecipeId || agentId;
2182
+ const workspaceRecipeId = await pickRecipeId(baseRecipeId);
2183
+
2184
+ const recipeFilePath = path.join(recipesDir, `${workspaceRecipeId}.md`);
2185
+ const parsed = parseFrontmatter(loaded.md);
2186
+ const fm = { ...parsed.frontmatter, id: workspaceRecipeId, name: parsed.frontmatter.name ?? recipe.name ?? workspaceRecipeId };
2187
+ const nextMd = `---\n${YAML.stringify(fm)}---\n${parsed.body}`;
2188
+ await writeFileSafely(recipeFilePath, nextMd, overwriteRecipe ? "overwrite" : "createOnly");
2119
2189
 
2120
2190
  const result = await scaffoldAgentFromRecipe(api, recipe, {
2121
- agentId: options.agentId,
2191
+ agentId,
2122
2192
  agentName: options.name,
2123
2193
  update: !!options.overwrite,
2124
2194
  filesRootDir: resolvedWorkspaceRoot,
2125
2195
  workspaceRootDir: resolvedWorkspaceRoot,
2126
2196
  vars: {
2127
- agentId: options.agentId,
2128
- agentName: options.name ?? recipe.name ?? options.agentId,
2197
+ agentId,
2198
+ agentName: options.name ?? recipe.name ?? agentId,
2129
2199
  },
2130
2200
  });
2131
2201
 
@@ -2147,8 +2217,11 @@ const recipesPlugin = {
2147
2217
  .command("scaffold-team")
2148
2218
  .description("Scaffold a team (shared workspace + multiple agents) from a team recipe")
2149
2219
  .argument("<recipeId>", "Recipe id")
2150
- .requiredOption("-t, --team-id <teamId>", "Team id (must end with -team)")
2220
+ .requiredOption("-t, --team-id <teamId>", "Team id")
2221
+ .option("--recipe-id <recipeId>", "Custom workspace recipe id to write (default: <teamId>)")
2151
2222
  .option("--overwrite", "Overwrite existing recipe-managed files")
2223
+ .option("--overwrite-recipe", "Overwrite the generated workspace recipe file (workspace/recipes/<teamId>.md) if it already exists")
2224
+ .option("--auto-increment", "If the workspace recipe id is taken, pick the next available <teamId>-2/-3/...")
2152
2225
  .option("--apply-config", "Write all team agents into openclaw config (agents.list)")
2153
2226
  .action(async (recipeId: string, options: any) => {
2154
2227
  const loaded = await loadRecipeById(api, recipeId);
@@ -2157,9 +2230,6 @@ const recipesPlugin = {
2157
2230
  throw new Error(`Recipe is not a team recipe: kind=${recipe.kind}`);
2158
2231
  }
2159
2232
  const teamId = String(options.teamId);
2160
- if (!teamId.endsWith("-team")) {
2161
- throw new Error("teamId must end with -team");
2162
- }
2163
2233
 
2164
2234
  const cfg = getCfg(api);
2165
2235
  const baseWorkspace = api.config.agents?.defaults?.workspace;
@@ -2177,6 +2247,57 @@ const recipesPlugin = {
2177
2247
  const teamDir = path.resolve(baseWorkspace, "..", `workspace-${teamId}`);
2178
2248
  await ensureDir(teamDir);
2179
2249
 
2250
+
2251
+ // Also create a workspace recipe file for this installed team.
2252
+ // This establishes a stable, editable recipe id that matches the team id (no custom- prefix).
2253
+ const recipesDir = path.join(baseWorkspace, "recipes");
2254
+ await ensureDir(recipesDir);
2255
+
2256
+ const overwriteRecipe = !!options.overwriteRecipe;
2257
+ const autoIncrement = !!options.autoIncrement;
2258
+
2259
+ function suggestedRecipeIds(baseId: string) {
2260
+ const today = new Date().toISOString().slice(0, 10);
2261
+ return [`${baseId}-v2`, `${baseId}-${today}`, `${baseId}-alt`];
2262
+ }
2263
+
2264
+ async function pickRecipeId(baseId: string) {
2265
+ const basePath = path.join(recipesDir, `${baseId}.md`);
2266
+ if (!(await fileExists(basePath))) return baseId;
2267
+ if (overwriteRecipe) return baseId;
2268
+ if (autoIncrement) {
2269
+ let n = 2;
2270
+ while (n < 1000) {
2271
+ const candidate = `${baseId}-${n}`;
2272
+ const candidatePath = path.join(recipesDir, `${candidate}.md`);
2273
+ if (!(await fileExists(candidatePath))) return candidate;
2274
+ n += 1;
2275
+ }
2276
+ throw new Error(`No available recipe id found for ${baseId} (tried up to -999)`);
2277
+ }
2278
+
2279
+ const suggestions = suggestedRecipeIds(baseId);
2280
+ const msg = [
2281
+ `Workspace recipe already exists: recipes/${baseId}.md`,
2282
+ `Choose a different recipe id (recommended) and re-run with --recipe-id, for example:`,
2283
+ ...suggestions.map((s) => ` openclaw recipes scaffold-team ${recipeId} -t ${teamId} --recipe-id ${s}`),
2284
+ `Or re-run with --auto-increment to pick ${baseId}-2/-3/... automatically, or --overwrite-recipe to overwrite the existing file.`,
2285
+ ].join("\n");
2286
+ throw new Error(msg);
2287
+ }
2288
+
2289
+ const explicitRecipeId = typeof options.recipeId === "string" ? String(options.recipeId).trim() : "";
2290
+ const baseRecipeId = explicitRecipeId || teamId;
2291
+ const workspaceRecipeId = await pickRecipeId(baseRecipeId);
2292
+
2293
+ // Write the recipe file, copying the source recipe markdown but forcing frontmatter.id to the chosen id.
2294
+ // Default: createOnly; overwrite only when --overwrite-recipe is set.
2295
+ const recipeFilePath = path.join(recipesDir, `${workspaceRecipeId}.md`);
2296
+ const parsed = parseFrontmatter(loaded.md);
2297
+ const fm = { ...parsed.frontmatter, id: workspaceRecipeId, name: parsed.frontmatter.name ?? recipe.name ?? workspaceRecipeId };
2298
+ const nextMd = `---\n${YAML.stringify(fm)}---\n${parsed.body}`;
2299
+ await writeFileSafely(recipeFilePath, nextMd, overwriteRecipe ? "overwrite" : "createOnly");
2300
+
2180
2301
  const rolesDir = path.join(teamDir, "roles");
2181
2302
  await ensureDir(rolesDir);
2182
2303
  const notesDir = path.join(teamDir, "notes");
@@ -2222,14 +2343,22 @@ const recipesPlugin = {
2222
2343
 
2223
2344
  const planPath = path.join(notesDir, "plan.md");
2224
2345
  const statusPath = path.join(notesDir, "status.md");
2346
+ const goalsIndexPath = path.join(notesDir, "GOALS.md");
2347
+ const goalsDir = path.join(notesDir, "goals");
2348
+ const goalsReadmePath = path.join(goalsDir, "README.md");
2225
2349
  const ticketsPath = path.join(teamDir, "TICKETS.md");
2226
2350
 
2227
2351
  const planMd = `# Plan — ${teamId}\n\n- (empty)\n`;
2228
2352
  const statusMd = `# Status — ${teamId}\n\n- (empty)\n`;
2353
+ const goalsIndexMd = `# Goals — ${teamId}\n\nThis folder is the canonical home for goals.\n\n## How to use\n- Create one markdown file per goal under: notes/goals/\n- Add a link here for discoverability\n\n## Goals\n- (empty)\n`;
2354
+ const goalsReadmeMd = `# Goals folder — ${teamId}\n\nCreate one markdown file per goal in this directory.\n\nRecommended file naming:\n- short, kebab-case, no leading numbers (e.g. \`reduce-support-backlog.md\`)\n\nLink goals from:\n- notes/GOALS.md\n`;
2229
2355
  const ticketsMd = `# Tickets — ${teamId}\n\n## Naming\n- Backlog tickets live in work/backlog/\n- In-progress tickets live in work/in-progress/\n- Testing tickets live in work/testing/\n- Done tickets live in work/done/\n- Filename ordering is the queue: 0001-..., 0002-...\n\n## Stages\n- backlog → in-progress → testing → done\n\n## QA handoff\n- When work is ready for QA: move the ticket to \`work/testing/\` and assign to test.\n\n## Required fields\nEach ticket should include:\n- Title\n- Context\n- Requirements\n- Acceptance criteria\n- Owner (dev/devops/lead/test)\n- Status (queued/in-progress/testing/done)\n\n## Example\n\n\`\`\`md\n# 0001-example-ticket\n\nOwner: dev\nStatus: queued\n\n## Context\n...\n\n## Requirements\n- ...\n\n## Acceptance criteria\n- ...\n\`\`\`\n`;
2230
2356
 
2357
+ await ensureDir(goalsDir);
2231
2358
  await writeFileSafely(planPath, planMd, overwrite ? "overwrite" : "createOnly");
2232
2359
  await writeFileSafely(statusPath, statusMd, overwrite ? "overwrite" : "createOnly");
2360
+ await writeFileSafely(goalsIndexPath, goalsIndexMd, overwrite ? "overwrite" : "createOnly");
2361
+ await writeFileSafely(goalsReadmePath, goalsReadmeMd, overwrite ? "overwrite" : "createOnly");
2233
2362
  await writeFileSafely(ticketsPath, ticketsMd, overwrite ? "overwrite" : "createOnly");
2234
2363
 
2235
2364
  const agents = recipe.agents ?? [];
@@ -2,7 +2,7 @@
2
2
  "id": "recipes",
3
3
  "name": "Recipes",
4
4
  "description": "Markdown recipes that scaffold agents and teams (workspace-local).",
5
- "version": "0.2.17",
5
+ "version": "0.2.20",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jiggai/recipes",
3
- "version": "0.2.17",
3
+ "version": "0.2.20",
4
4
  "description": "ClawRecipes plugin for OpenClaw (markdown recipes -> scaffold agents/teams)",
5
5
  "main": "index.ts",
6
6
  "type": "commonjs",
@@ -24,6 +24,8 @@
24
24
  "scripts": {
25
25
  "test": "vitest run",
26
26
  "test:smoke": "node scripts/scaffold-smoke.mjs",
27
+ "test:provenance-smoke": "node scripts/scaffold-team-provenance-smoke.mjs",
28
+ "test:agent-recipefile-smoke": "node scripts/scaffold-agent-recipefile-smoke.mjs",
27
29
  "smoke": "node scripts/scaffold-smoke.mjs",
28
30
  "scaffold:smoke": "node scripts/scaffold-smoke.mjs"
29
31
  },
@@ -0,0 +1,174 @@
1
+ ---
2
+ id: business-team
3
+ name: Business Team
4
+ version: 0.1.0
5
+ description: A small generalist business team (ops, sales, marketing, finance, analyst) that runs execution through a shared workspace.
6
+ kind: team
7
+ cronJobs:
8
+ - id: lead-triage-loop
9
+ name: "Lead triage loop"
10
+ schedule: "*/30 7-23 * * 1-5"
11
+ timezone: "America/New_York"
12
+ message: "Automated lead triage loop (Business Team): triage inbox/tickets, assign work, and update notes/status.md."
13
+ enabledByDefault: false
14
+ - id: execution-loop
15
+ name: "Execution loop"
16
+ schedule: "*/30 7-23 * * 1-5"
17
+ timezone: "America/New_York"
18
+ message: "Automated execution loop (Business Team): make progress on in-progress tickets and update notes/status.md."
19
+ enabledByDefault: false
20
+ requiredSkills: []
21
+ team:
22
+ teamId: business-team
23
+ agents:
24
+ - role: lead
25
+ name: Business Ops Lead
26
+ tools:
27
+ profile: "coding"
28
+ allow: ["group:fs", "group:web", "group:runtime"]
29
+ deny: ["exec"]
30
+ - role: ops
31
+ name: Operations Manager
32
+ tools:
33
+ profile: "coding"
34
+ allow: ["group:fs", "group:web"]
35
+ deny: ["exec"]
36
+ - role: sales
37
+ name: Sales / Partnerships
38
+ tools:
39
+ profile: "coding"
40
+ allow: ["group:fs", "group:web"]
41
+ deny: ["exec"]
42
+ - role: marketing
43
+ name: Marketing / Growth
44
+ tools:
45
+ profile: "coding"
46
+ allow: ["group:fs", "group:web"]
47
+ deny: ["exec"]
48
+ - role: finance
49
+ name: Finance / Bookkeeping
50
+ tools:
51
+ profile: "coding"
52
+ allow: ["group:fs", "group:web"]
53
+ deny: ["exec"]
54
+ - role: analyst
55
+ name: Business Analyst
56
+ tools:
57
+ profile: "coding"
58
+ allow: ["group:fs", "group:web"]
59
+ deny: ["exec"]
60
+
61
+ templates:
62
+ lead.soul: |
63
+ # SOUL.md
64
+
65
+ You are the Business Ops Lead / Dispatcher for {{teamId}}.
66
+
67
+ Core job:
68
+ - Convert incoming requests into scoped tickets.
69
+ - Assign to the right role (ops/sales/marketing/finance/analyst).
70
+ - Keep priorities clear and measurable.
71
+ - Maintain a single source of truth in the shared workspace.
72
+
73
+ lead.agents: |
74
+ # AGENTS.md
75
+
76
+ Team: {{teamId}}
77
+ Team directory: {{teamDir}}
78
+
79
+ ## Shared workspace
80
+ - inbox/ — incoming requests and raw notes
81
+ - work/backlog/ — normalized tickets (0001-...)
82
+ - work/in-progress/ — active tickets
83
+ - work/testing/ — reviews/verification
84
+ - work/done/ — completed tickets + DONE notes
85
+ - notes/plan.md — current plan (curated)
86
+ - notes/status.md — current status snapshot
87
+ - shared-context/ — shared references + append-only outputs
88
+ - outbox/ — final deliverables (emails/copy/plans)
89
+
90
+ ## Role routing
91
+ - ops → process, vendors, internal operations, SOPs
92
+ - sales → outreach sequences, CRM notes, partnership drafts
93
+ - marketing → positioning, campaigns, landing-page copy
94
+ - finance → pricing, forecasts, bookkeeping checklists
95
+ - analyst → research, competitive scans, metrics
96
+
97
+ ## Operating rhythm
98
+ 1) Triage inbox/ → tickets.
99
+ 2) Keep WIP small (max 1–2 active tickets per role).
100
+ 3) Every work session updates notes/status.md.
101
+
102
+ ops.soul: |
103
+ # SOUL.md
104
+
105
+ You are the Operations Manager on {{teamId}}.
106
+
107
+ You create SOPs, checklists, and lightweight processes that remove friction.
108
+
109
+ ops.agents: |
110
+ # AGENTS.md
111
+
112
+ Output:
113
+ - SOPs/checklists → shared-context/sops/
114
+ - Vendor notes → shared-context/vendors/
115
+ - Operational plans → outbox/
116
+
117
+ sales.soul: |
118
+ # SOUL.md
119
+
120
+ You are Sales / Partnerships on {{teamId}}.
121
+
122
+ You write outreach, qualify leads, and draft partnership terms.
123
+
124
+ sales.agents: |
125
+ # AGENTS.md
126
+
127
+ Output:
128
+ - Outreach sequences → outbox/sales/
129
+ - Call notes → shared-context/sales/call-notes/
130
+ - Partnership drafts → outbox/partnerships/
131
+
132
+ marketing.soul: |
133
+ # SOUL.md
134
+
135
+ You are Marketing / Growth on {{teamId}}.
136
+
137
+ You create crisp positioning, campaigns, and landing-page copy that converts.
138
+
139
+ marketing.agents: |
140
+ # AGENTS.md
141
+
142
+ Output:
143
+ - Positioning/messaging → shared-context/marketing/
144
+ - Campaign plans → outbox/marketing/
145
+ - Landing page copy → outbox/marketing/landing-pages/
146
+
147
+ finance.soul: |
148
+ # SOUL.md
149
+
150
+ You are Finance / Bookkeeping on {{teamId}}.
151
+
152
+ You build simple, defensible pricing and forecasts; you keep a paper trail.
153
+
154
+ finance.agents: |
155
+ # AGENTS.md
156
+
157
+ Output:
158
+ - Pricing memos → outbox/finance/
159
+ - Forecasts → outbox/finance/
160
+ - Bookkeeping checklists → shared-context/finance/
161
+
162
+ analyst.soul: |
163
+ # SOUL.md
164
+
165
+ You are the Business Analyst on {{teamId}}.
166
+
167
+ You turn ambiguous questions into structured analysis with clear recommendations.
168
+
169
+ analyst.agents: |
170
+ # AGENTS.md
171
+
172
+ Output:
173
+ - Research briefs → outbox/research/
174
+ - Metrics definitions/dashboards notes → shared-context/metrics/