@jiggai/recipes 0.3.2 → 0.3.4
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/package.json +1 -1
- package/recipes/default/development-team.md +158 -87
- package/src/handlers/team.ts +25 -3
- package/src/lib/recipe-lint.ts +44 -0
package/package.json
CHANGED
|
@@ -1,40 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: development-team
|
|
3
3
|
name: Development Team
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
description: A small engineering team with a shared workspace (lead, dev, devops, test) using file-first tickets.
|
|
6
6
|
kind: team
|
|
7
7
|
cronJobs:
|
|
8
8
|
- id: lead-triage-loop
|
|
9
9
|
name: "Lead triage loop"
|
|
10
|
-
schedule: "*/30
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
enabledByDefault: false
|
|
10
|
+
schedule: "*/30 7-23 * * 1-5"
|
|
11
|
+
timezone: "America/New_York"
|
|
12
|
+
message: "Automated lead triage loop: triage inbox/tickets, assign work, and update notes/status.md. Anti-stuck: if lowest in-progress is HARD BLOCKED, advance the next unblocked ticket (or pull from backlog). If in-progress is stale (>12h no dated update), comment or move it back."
|
|
13
|
+
enabledByDefault: true
|
|
15
14
|
- id: execution-loop
|
|
16
15
|
name: "Execution loop"
|
|
17
|
-
schedule: "*/30
|
|
18
|
-
agentId: "{{teamId}}-lead"
|
|
19
|
-
channel: "last"
|
|
20
|
-
message: "Execution loop (automated). Goal: make concrete progress on the lowest-numbered in-progress ticket. Run ticket hygiene; perform repo lint/build checks (avoid noisy tests unless defined); update ticket + notes/status.md. Send a message only when there is a material change."
|
|
21
|
-
enabledByDefault: false
|
|
22
|
-
- id: pr-watcher
|
|
23
|
-
name: "PR watcher"
|
|
24
|
-
schedule: "*/30 8-23 * * 1-5"
|
|
25
|
-
agentId: "{{teamId}}-lead"
|
|
26
|
-
channel: "last"
|
|
27
|
-
message: "PR watcher (automated). Goal: watch ticket-linked PR URLs; summarize failing checks; if merged, move tickets to done with a short completion report. Requires GitHub access/auth on the controller."
|
|
28
|
-
enabledByDefault: false
|
|
29
|
-
|
|
30
|
-
- id: testing-lane-loop
|
|
31
|
-
name: "Testing lane loop"
|
|
32
|
-
schedule: "*/30 0-1,7-23 * * *"
|
|
16
|
+
schedule: "*/30 7-23 * * 1-5"
|
|
33
17
|
timezone: "America/New_York"
|
|
34
|
-
|
|
35
|
-
channel: "last"
|
|
36
|
-
message: "Testing lane loop (automated). Goal: drain work/testing reliably. For each ticket in work/testing: follow the ticket's '## Verification steps'; record results under ## Comments; if it passes, complete the ticket; if it fails, write a clear repro + fix request and hand back to dev; if blocked on an unmerged PR/deploy, note and skip. Also keep notes/status.md current (3–5 bullets)."
|
|
18
|
+
message: "Automated execution loop: make progress on in-progress tickets, keep changes small/safe, and update notes/status.md."
|
|
37
19
|
enabledByDefault: false
|
|
20
|
+
# pr-watcher omitted (enable only when a real PR integration exists)
|
|
38
21
|
requiredSkills: []
|
|
39
22
|
team:
|
|
40
23
|
teamId: development-team
|
|
@@ -81,7 +64,6 @@ templates:
|
|
|
81
64
|
|
|
82
65
|
Team: {{teamId}}
|
|
83
66
|
Shared workspace: {{teamDir}}
|
|
84
|
-
Role: lead
|
|
85
67
|
|
|
86
68
|
## Guardrails (read → act → write)
|
|
87
69
|
|
|
@@ -90,26 +72,24 @@ templates:
|
|
|
90
72
|
- `notes/plan.md`
|
|
91
73
|
- `notes/status.md`
|
|
92
74
|
- `shared-context/priorities.md`
|
|
93
|
-
- the
|
|
94
|
-
|
|
95
|
-
During work (every loop):
|
|
96
|
-
1) Run hygiene:
|
|
97
|
-
- `./scripts/ticket-hygiene.sh`
|
|
75
|
+
- the relevant ticket(s)
|
|
98
76
|
|
|
99
77
|
After you act:
|
|
100
78
|
1) Write back:
|
|
101
|
-
- Update
|
|
102
|
-
- Keep `notes/status.md` current (
|
|
103
|
-
- Append detailed logs/output to `shared-context/agent-outputs/` (append-only).
|
|
79
|
+
- Update tickets with decisions/assignments.
|
|
80
|
+
- Keep `notes/status.md` current (3–5 bullets per active ticket).
|
|
104
81
|
|
|
105
82
|
## Curator model
|
|
106
83
|
|
|
107
|
-
You
|
|
84
|
+
You are the curator of:
|
|
108
85
|
- `notes/plan.md`
|
|
109
86
|
- `shared-context/priorities.md`
|
|
110
87
|
|
|
111
|
-
|
|
88
|
+
Everyone else should append to:
|
|
112
89
|
- `shared-context/agent-outputs/` (append-only)
|
|
90
|
+
- `shared-context/feedback/`
|
|
91
|
+
|
|
92
|
+
Your job is to periodically distill those inputs into the curated files.
|
|
113
93
|
|
|
114
94
|
## File-first workflow (tickets)
|
|
115
95
|
|
|
@@ -121,9 +101,29 @@ templates:
|
|
|
121
101
|
- `work/in-progress/` — tickets currently being executed
|
|
122
102
|
- `work/testing/` — tickets awaiting QA verification
|
|
123
103
|
- `work/done/` — completed tickets + completion notes
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- `
|
|
104
|
+
- `notes/plan.md` — current plan / priorities (curated)
|
|
105
|
+
- `notes/status.md` — current status snapshot
|
|
106
|
+
- `shared-context/` — shared context + append-only outputs
|
|
107
|
+
|
|
108
|
+
### Ticket numbering (critical)
|
|
109
|
+
- Backlog tickets MUST be named `0001-...md`, `0002-...md`, etc.
|
|
110
|
+
- The developer pulls the lowest-numbered ticket assigned to them.
|
|
111
|
+
|
|
112
|
+
### Ticket format
|
|
113
|
+
See `TICKETS.md` in the team root. Every ticket should include:
|
|
114
|
+
- Context
|
|
115
|
+
- Requirements
|
|
116
|
+
- Acceptance criteria
|
|
117
|
+
- Owner (dev/devops)
|
|
118
|
+
- Status
|
|
119
|
+
|
|
120
|
+
### Your responsibilities
|
|
121
|
+
- For every new request in `inbox/`, create a normalized ticket in `work/backlog/`.
|
|
122
|
+
- Curate `notes/plan.md` and `shared-context/priorities.md`.
|
|
123
|
+
- Keep `notes/status.md` updated.
|
|
124
|
+
- When work is ready for QA, move the ticket to `work/testing/` and assign it to the tester.
|
|
125
|
+
- Only after QA verification, move the ticket to `work/done/` (or use `openclaw recipes complete`).
|
|
126
|
+
- When a completion appears in `work/done/`, write a short summary into `outbox/`.
|
|
127
127
|
|
|
128
128
|
dev.soul: |
|
|
129
129
|
# SOUL.md
|
|
@@ -134,32 +134,48 @@ templates:
|
|
|
134
134
|
dev.agents: |
|
|
135
135
|
# AGENTS.md
|
|
136
136
|
|
|
137
|
-
Team: {{teamId}}
|
|
138
137
|
Shared workspace: {{teamDir}}
|
|
139
|
-
Role: dev
|
|
140
138
|
|
|
141
139
|
## Guardrails (read → act → write)
|
|
142
140
|
|
|
143
|
-
Before you change anything
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
Before you change anything:
|
|
142
|
+
1) Read:
|
|
143
|
+
- `notes/plan.md`
|
|
144
|
+
- `notes/status.md`
|
|
145
|
+
- `shared-context/priorities.md`
|
|
146
|
+
- the current ticket you’re working on
|
|
147
|
+
|
|
148
|
+
While working:
|
|
149
|
+
- Keep changes small and safe.
|
|
150
|
+
- Prefer file-first coordination over chat.
|
|
151
|
+
|
|
152
|
+
After you finish a work session (even if not done):
|
|
153
|
+
1) Write back:
|
|
154
|
+
- Update the ticket with what you did and what’s next.
|
|
155
|
+
- Add **3–5 bullets** to `notes/status.md` (what changed / what’s blocked).
|
|
156
|
+
- Append detailed output to `shared-context/agent-outputs/` (append-only).
|
|
157
|
+
|
|
158
|
+
Curator model:
|
|
159
|
+
- Lead curates `notes/plan.md` and `shared-context/priorities.md`.
|
|
160
|
+
- You should NOT edit curated files; propose changes via `agent-outputs/`.
|
|
161
|
+
|
|
162
|
+
## How you work (pull system)
|
|
163
|
+
|
|
164
|
+
1) Look in `work/in-progress/` for any ticket already assigned to you.
|
|
165
|
+
- If present: continue it.
|
|
148
166
|
|
|
149
|
-
|
|
150
|
-
|
|
167
|
+
2) Otherwise, pick the next ticket from `work/backlog/`:
|
|
168
|
+
- Choose the lowest-numbered `0001-...md` ticket assigned to `dev`.
|
|
151
169
|
|
|
152
|
-
|
|
153
|
-
- Update the ticket with what you did + verification steps.
|
|
154
|
-
- Check/respond in the ticket’s `## Comments` section (especially if you were pinged).
|
|
155
|
-
- Add 3–5 bullets to `notes/status.md`.
|
|
156
|
-
- Append detailed logs/output to `shared-context/agent-outputs/` (append-only).
|
|
170
|
+
3) Move the ticket file from `work/backlog/` → `work/in-progress/`.
|
|
157
171
|
|
|
158
|
-
|
|
172
|
+
4) Do the work.
|
|
173
|
+
|
|
174
|
+
5) Write a completion report into `work/done/` with:
|
|
175
|
+
- What changed
|
|
176
|
+
- How to test
|
|
177
|
+
- Any follow-ups
|
|
159
178
|
|
|
160
|
-
- Continue any ticket already assigned to you in `work/in-progress/`.
|
|
161
|
-
- Otherwise pull the lowest-numbered assigned ticket from `work/backlog/` and move it to `work/in-progress/`.
|
|
162
|
-
- Keep changes small and reversible.
|
|
163
179
|
devops.soul: |
|
|
164
180
|
# SOUL.md
|
|
165
181
|
|
|
@@ -169,26 +185,44 @@ templates:
|
|
|
169
185
|
devops.agents: |
|
|
170
186
|
# AGENTS.md
|
|
171
187
|
|
|
172
|
-
|
|
173
|
-
Shared workspace: {teamDir}
|
|
174
|
-
Role: devops
|
|
188
|
+
Shared workspace: {{teamDir}}
|
|
175
189
|
|
|
176
190
|
## Guardrails (read → act → write)
|
|
177
|
-
|
|
191
|
+
|
|
192
|
+
Before you change anything:
|
|
178
193
|
1) Read:
|
|
179
194
|
- `notes/plan.md`
|
|
180
195
|
- `notes/status.md`
|
|
181
|
-
-
|
|
182
|
-
-
|
|
196
|
+
- `shared-context/priorities.md`
|
|
197
|
+
- the current ticket you’re working on
|
|
183
198
|
|
|
184
|
-
After you
|
|
199
|
+
After you finish a work session:
|
|
185
200
|
1) Write back:
|
|
186
|
-
-
|
|
187
|
-
-
|
|
201
|
+
- Update the ticket with what you did + verification steps.
|
|
202
|
+
- Add **3–5 bullets** to `notes/status.md`.
|
|
203
|
+
- Append detailed output/logs to `shared-context/agent-outputs/` (append-only).
|
|
204
|
+
|
|
205
|
+
Curator model:
|
|
206
|
+
- Lead curates `notes/plan.md` and `shared-context/priorities.md`.
|
|
207
|
+
- You should NOT edit curated files; propose changes via `agent-outputs/`.
|
|
208
|
+
|
|
209
|
+
## How you work (pull system)
|
|
210
|
+
|
|
211
|
+
1) Look in `work/in-progress/` for any ticket already assigned to you.
|
|
212
|
+
- If present: continue it.
|
|
213
|
+
|
|
214
|
+
2) Otherwise, pick the next ticket from `work/backlog/`:
|
|
215
|
+
- Choose the lowest-numbered `0001-...md` ticket assigned to `devops`.
|
|
216
|
+
|
|
217
|
+
3) Move the ticket file from `work/backlog/` → `work/in-progress/`.
|
|
218
|
+
|
|
219
|
+
4) Do the work.
|
|
220
|
+
|
|
221
|
+
5) Write a completion report into `work/done/` with:
|
|
222
|
+
- What changed
|
|
223
|
+
- How to verify
|
|
224
|
+
- Rollback notes (if applicable)
|
|
188
225
|
|
|
189
|
-
## Workflow
|
|
190
|
-
- Prefer a pull model: wait for a clear task from the lead, or propose a scoped task.
|
|
191
|
-
- Keep work small and reversible.
|
|
192
226
|
lead.tools: |
|
|
193
227
|
# TOOLS.md
|
|
194
228
|
|
|
@@ -247,30 +281,68 @@ templates:
|
|
|
247
281
|
test.agents: |
|
|
248
282
|
# AGENTS.md
|
|
249
283
|
|
|
250
|
-
Team: {{teamId}}
|
|
251
284
|
Shared workspace: {{teamDir}}
|
|
252
|
-
Role: test
|
|
253
285
|
|
|
254
286
|
## Guardrails (read → act → write)
|
|
255
287
|
|
|
256
|
-
Before
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
288
|
+
Before verifying:
|
|
289
|
+
1) Read:
|
|
290
|
+
- `notes/plan.md`
|
|
291
|
+
- `notes/status.md`
|
|
292
|
+
- `shared-context/priorities.md`
|
|
293
|
+
- the ticket under test
|
|
261
294
|
|
|
262
|
-
After
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
- Add 3–5 bullets to `notes/status.md`.
|
|
268
|
-
- Append detailed logs/output to `shared-context/agent-outputs/` (append-only).
|
|
295
|
+
After each verification pass:
|
|
296
|
+
1) Write back:
|
|
297
|
+
- Add a short verification note to the ticket (pass/fail + evidence).
|
|
298
|
+
- Add **3–5 bullets** to `notes/status.md` (what’s verified / what’s blocked).
|
|
299
|
+
- Append detailed findings to `shared-context/feedback/` or `shared-context/agent-outputs/`.
|
|
269
300
|
|
|
270
|
-
|
|
301
|
+
Curator model:
|
|
302
|
+
- Lead curates `notes/plan.md` and `shared-context/priorities.md`.
|
|
303
|
+
- You should NOT edit curated files; propose changes via feedback/outputs.
|
|
304
|
+
|
|
305
|
+
## How you work
|
|
306
|
+
|
|
307
|
+
1) Look in `work/testing/` for tickets assigned to you.
|
|
308
|
+
|
|
309
|
+
2) For each ticket:
|
|
310
|
+
- Follow the ticket's "How to test" steps (if present)
|
|
311
|
+
- Validate acceptance criteria
|
|
312
|
+
- Write a short verification note (or failures) into the ticket itself or a sibling note.
|
|
313
|
+
|
|
314
|
+
3) If it passes:
|
|
315
|
+
- Move the ticket to `work/done/` (or ask the lead to do it).
|
|
316
|
+
|
|
317
|
+
4) If it fails:
|
|
318
|
+
- Move the ticket back to `work/in-progress/` and assign to the right owner.
|
|
319
|
+
|
|
320
|
+
## Cleanup after testing
|
|
321
|
+
|
|
322
|
+
If your test involved creating temporary resources (e.g., scaffolding test teams, creating test workspaces), **clean them up** after verification:
|
|
323
|
+
|
|
324
|
+
1) Remove test workspaces:
|
|
325
|
+
```bash
|
|
326
|
+
rm -rf ~/.openclaw/workspace-<test-team-id>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
2) Remove test agents from config (agents whose id starts with the test team id):
|
|
330
|
+
- Edit `~/.openclaw/openclaw.json` and remove entries from `agents.list[]`
|
|
331
|
+
- Or wait for `openclaw recipes remove-team` (once available)
|
|
332
|
+
|
|
333
|
+
3) Remove any cron jobs created for the test team:
|
|
334
|
+
```bash
|
|
335
|
+
openclaw cron list --all --json | grep "<test-team-id>"
|
|
336
|
+
openclaw cron remove <jobId>
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
4) Restart the gateway if you modified config:
|
|
340
|
+
```bash
|
|
341
|
+
openclaw gateway restart
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Naming convention:** When scaffolding test teams, use a prefix like `qa-<ticketNum>-` (e.g., `qa-0017-social-team`) so cleanup is easier.
|
|
271
345
|
|
|
272
|
-
- Work from `work/testing/`.
|
|
273
|
-
- Follow the ticket’s “How to test” steps and validate acceptance criteria.
|
|
274
346
|
test.tools: |
|
|
275
347
|
# TOOLS.md
|
|
276
348
|
|
|
@@ -328,4 +400,3 @@ Scaffolds a shared team workspace and four namespaced agents (lead/dev/devops/te
|
|
|
328
400
|
- allow groups: group:automation, group:fs, group:runtime, group:web
|
|
329
401
|
- deny: (none)
|
|
330
402
|
- Safety note: most bundled teams default to denying `exec` unless a role explicitly needs it.
|
|
331
|
-
|
package/src/handlers/team.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { pickRecipeId } from "../lib/recipe-id";
|
|
|
13
13
|
import { recipeIdTakenForTeam, validateRecipeAndSkills, writeWorkspaceRecipeFile } from "../lib/scaffold-utils";
|
|
14
14
|
import { scaffoldAgentFromRecipe } from "./scaffold";
|
|
15
15
|
import { reconcileRecipeCronJobs } from "./cron";
|
|
16
|
+
import { lintRecipe } from "../lib/recipe-lint";
|
|
16
17
|
|
|
17
18
|
async function ensureTeamDirectoryStructure(
|
|
18
19
|
teamDir: string,
|
|
@@ -119,6 +120,19 @@ async function scaffoldTeamAgents(
|
|
|
119
120
|
): Promise<AgentScaffoldResult[]> {
|
|
120
121
|
const agents = recipe.agents ?? [];
|
|
121
122
|
if (!agents.length) throw new Error("Team recipe must include agents[]");
|
|
123
|
+
|
|
124
|
+
// Resilience: some custom recipes may define templates but forget files[].
|
|
125
|
+
// In that case, scaffold a minimal per-role file set.
|
|
126
|
+
const baseFiles = (recipe.files ?? []).length
|
|
127
|
+
? (recipe.files ?? [])
|
|
128
|
+
: [
|
|
129
|
+
{ path: "SOUL.md", template: "soul", mode: "createOnly" },
|
|
130
|
+
{ path: "AGENTS.md", template: "agents", mode: "createOnly" },
|
|
131
|
+
{ path: "TOOLS.md", template: "tools", mode: "createOnly" },
|
|
132
|
+
{ path: "STATUS.md", template: "status", mode: "createOnly" },
|
|
133
|
+
{ path: "NOTES.md", template: "notes", mode: "createOnly" },
|
|
134
|
+
];
|
|
135
|
+
|
|
122
136
|
const results: AgentScaffoldResult[] = [];
|
|
123
137
|
for (const a of agents) {
|
|
124
138
|
const role = a.role;
|
|
@@ -131,9 +145,9 @@ async function scaffoldTeamAgents(
|
|
|
131
145
|
requiredSkills: recipe.requiredSkills,
|
|
132
146
|
optionalSkills: recipe.optionalSkills,
|
|
133
147
|
templates: recipe.templates,
|
|
134
|
-
files:
|
|
148
|
+
files: baseFiles.map((f) => ({
|
|
135
149
|
...f,
|
|
136
|
-
template: f.template.includes(".") ? f.template : `${role}.${f.template}`,
|
|
150
|
+
template: String(f.template).includes(".") ? f.template : `${role}.${f.template}`,
|
|
137
151
|
})),
|
|
138
152
|
tools: a.tools ?? recipe.tools,
|
|
139
153
|
};
|
|
@@ -143,7 +157,8 @@ async function scaffoldTeamAgents(
|
|
|
143
157
|
agentName,
|
|
144
158
|
update: overwrite,
|
|
145
159
|
filesRootDir: roleDir,
|
|
146
|
-
|
|
160
|
+
// IMPORTANT: per-role workspace so the Kitchen UI / agent files panel reads the role files.
|
|
161
|
+
workspaceRootDir: roleDir,
|
|
147
162
|
vars: { teamId, teamDir, role, agentId, agentName, roleDir },
|
|
148
163
|
});
|
|
149
164
|
results.push({ role, agentId, ...r });
|
|
@@ -178,6 +193,13 @@ export async function handleScaffoldTeam(
|
|
|
178
193
|
};
|
|
179
194
|
}
|
|
180
195
|
const { loaded, recipe, cfg, workspaceRoot: baseWorkspace } = validation;
|
|
196
|
+
|
|
197
|
+
// Lint (warn-only) for common team scaffolding pitfalls.
|
|
198
|
+
for (const issue of lintRecipe(recipe)) {
|
|
199
|
+
if (issue.level === "warn") console.warn(`[recipes] WARN ${issue.code}: ${issue.message}`);
|
|
200
|
+
else console.warn(`[recipes] ${issue.code}: ${issue.message}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
181
203
|
const teamId = String(options.teamId);
|
|
182
204
|
const teamDir = path.resolve(baseWorkspace, "..", `workspace-${teamId}`);
|
|
183
205
|
await ensureDir(teamDir);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { RecipeFrontmatter } from "./recipe-frontmatter";
|
|
2
|
+
|
|
3
|
+
export type RecipeLintIssue = { level: "warn" | "error"; code: string; message: string };
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Lightweight recipe lint for common scaffolding pitfalls.
|
|
7
|
+
* Intentionally conservative: warnings should be actionable and low-noise.
|
|
8
|
+
*/
|
|
9
|
+
export function lintRecipe(recipe: RecipeFrontmatter): RecipeLintIssue[] {
|
|
10
|
+
const issues: RecipeLintIssue[] = [];
|
|
11
|
+
|
|
12
|
+
if ((recipe.kind ?? "") === "team") {
|
|
13
|
+
const agents = recipe.agents ?? [];
|
|
14
|
+
const files = recipe.files ?? [];
|
|
15
|
+
const templates = recipe.templates ?? {};
|
|
16
|
+
|
|
17
|
+
if (agents.length && files.length === 0) {
|
|
18
|
+
const hasRoleTemplates = Object.keys(templates).some((k) => k.includes(".") && /(\.soul|\.agents|\.tools|\.status|\.notes)$/.test(k));
|
|
19
|
+
issues.push({
|
|
20
|
+
level: "warn",
|
|
21
|
+
code: "team.missing_files",
|
|
22
|
+
message:
|
|
23
|
+
`Team recipe has agents[] but no files[]. Per-role workspaces will be empty. ` +
|
|
24
|
+
`Add a files: section (e.g. SOUL.md/AGENTS.md/TOOLS.md/STATUS.md/NOTES.md) ` +
|
|
25
|
+
`or rely on scaffold defaults. ${hasRoleTemplates ? "(Detected role templates; likely meant to scaffold role files.)" : ""}`,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Templates exist but won't be used if files[] doesn't reference them.
|
|
30
|
+
if (agents.length && Object.keys(templates).length && files.length) {
|
|
31
|
+
const baseTemplates = new Set(files.map((f) => String(f.template ?? "").trim()).filter(Boolean));
|
|
32
|
+
const missing = ["soul", "agents", "tools"].filter((t) => !baseTemplates.has(t) && ![...baseTemplates].some((x) => x.endsWith(`.${t}`)));
|
|
33
|
+
if (missing.length) {
|
|
34
|
+
issues.push({
|
|
35
|
+
level: "warn",
|
|
36
|
+
code: "team.files.missing_core_templates",
|
|
37
|
+
message: `Team recipe files[] is missing some common templates (${missing.join(", ")}). This may be intentional, but usually each role should have SOUL.md/AGENTS.md/TOOLS.md at minimum.`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return issues;
|
|
44
|
+
}
|