@jiggai/recipes 0.3.3 → 0.3.5

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
@@ -180,7 +180,6 @@ Most users should focus on:
180
180
  - Daily shipping/pull requests of ClawRecipes features
181
181
  - Improve recipes with more detailed agent files
182
182
  - Add ability to install skills for agents through ClawKitchen
183
- - Integrate ClawKitchen with ClawMarket to pull new recipes
184
183
 
185
184
  ## License
186
185
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jiggai/recipes",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "ClawRecipes plugin for OpenClaw (markdown recipes -> scaffold agents/teams)",
5
5
  "main": "index.ts",
6
6
  "type": "commonjs",
@@ -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: (recipe.files ?? []).map((f) => ({
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
- workspaceRootDir: teamDir,
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
+ }