@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 +2 -2
- package/docs/BUNDLED_RECIPES.md +81 -0
- package/docs/COMMANDS.md +12 -0
- package/index.ts +137 -8
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -1
- package/recipes/default/business-team.md +174 -0
- package/recipes/default/clinic-team.md +157 -0
- package/recipes/default/construction-team.md +161 -0
- package/recipes/default/crypto-trader-team.md +157 -0
- package/recipes/default/financial-planner-team.md +160 -0
- package/recipes/default/law-firm-team.md +163 -0
- package/recipes/default/stock-trader-team.md +160 -0
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)
|
package/docs/BUNDLED_RECIPES.md
CHANGED
|
@@ -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-${
|
|
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
|
|
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
|
|
2128
|
-
agentName: options.name ?? recipe.name ??
|
|
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
|
|
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 ?? [];
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jiggai/recipes",
|
|
3
|
-
"version": "0.2.
|
|
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/
|