@jiggai/recipes 0.4.52 → 0.4.54

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.
@@ -56,7 +56,7 @@ When a node's `text` field contains JSON, ClawRecipes automatically extracts nes
56
56
  ### Available Template Variables
57
57
  - `{{nodeId.text}}` — Raw JSON string
58
58
  - `{{nodeId.title}}` — Extracted: "Product Launch"
59
- - `{{nodeId.approved_json}}` — For non-string values: "true"
59
+ - `{{nodeId.approved}}` — For non-string values: "true"
60
60
 
61
61
  ### Deeply Nested Extraction
62
62
  If the JSON contains nested objects, fields are flattened:
@@ -68,7 +68,7 @@ If the JSON contains nested objects, fields are flattened:
68
68
  ```
69
69
 
70
70
  Available as:
71
- - `{{nodeId.meta_json}}` — Full meta object as JSON string
71
+ - `{{nodeId.meta}}` — Full meta object as JSON string
72
72
  - `{{nodeId.author}}` — "John" (if meta.author is a string)
73
73
 
74
74
  ## LLM Node Structured Output
@@ -92,7 +92,7 @@ LLM nodes with `outputFields` configuration produce predictable JSON structures:
92
92
  ```
93
93
  Title: {{nodeId.title}}
94
94
  Tags: {{nodeId.tags}}
95
- Metadata: {{nodeId.metadata_json}}
95
+ Metadata: {{nodeId.metadata}}
96
96
  ```
97
97
 
98
98
  ## Usage Examples
@@ -144,7 +144,7 @@ Template substitution happens in the workflow worker during node execution. The
144
144
  1. **Global vars** are built from run metadata and timestamps
145
145
  2. **Node outputs** are loaded from each completed node's output file
146
146
  3. **JSON parsing** attempts to extract fields from the `text` field
147
- 4. **Nested extraction** flattens nested objects with `_json` suffixes for non-strings
147
+ 4. **Nested extraction** flattens nested objects, stringifying non-string values under their declared field name
148
148
  5. **Template replacement** applies all variables using simple string substitution
149
149
 
150
150
  ### Performance Notes
@@ -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.4.52",
5
+ "version": "0.4.54",
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.4.52",
3
+ "version": "0.4.54",
4
4
  "description": "ClawRecipes plugin for OpenClaw (markdown recipes -> scaffold agents/teams)",
5
5
  "main": "index.ts",
6
6
  "type": "commonjs",
@@ -161,26 +161,42 @@ export async function generateKitchenManifest(opts: GenerateManifestOptions): Pr
161
161
  };
162
162
  }
163
163
 
164
- // Fetch agents and recipes via CLI (reuses existing OpenClaw infrastructure)
164
+ // Read agents directly from config (avoids subprocess which can silently fail)
165
165
  let agents: AgentManifestEntry[] = [];
166
166
  try {
167
- const res = await api.runtime.system.runCommandWithTimeout(
168
- ['openclaw', 'agents', 'list', '--json'],
169
- { timeoutMs: 15_000 },
170
- );
171
- if (res.code === 0 && res.stdout) {
172
- agents = JSON.parse(res.stdout) as AgentManifestEntry[];
167
+ const list = (api.config as { agents?: { list?: Array<Record<string, unknown>> } }).agents?.list;
168
+ if (Array.isArray(list)) {
169
+ agents = list.map((a) => ({
170
+ id: String(a.id ?? ''),
171
+ identityName: typeof (a.identity as Record<string, unknown> | undefined)?.name === 'string'
172
+ ? (a.identity as { name: string }).name
173
+ : undefined,
174
+ workspace: typeof a.workspace === 'string' ? a.workspace : undefined,
175
+ model: typeof a.model === 'string' ? a.model : undefined,
176
+ isDefault: a.default === true,
177
+ })).filter((a) => a.id);
173
178
  }
174
179
  } catch { /* best-effort */ }
175
180
 
176
- let recipes: RecipeManifestEntry[] = [];
181
+ // Read recipes from filesystem (avoids subprocess which can silently fail)
182
+ const recipes: RecipeManifestEntry[] = [];
177
183
  try {
178
- const res = await api.runtime.system.runCommandWithTimeout(
179
- ['openclaw', 'recipes', 'list'],
180
- { timeoutMs: 15_000 },
181
- );
182
- if (res.code === 0 && res.stdout) {
183
- recipes = JSON.parse(res.stdout) as RecipeManifestEntry[];
184
+ const { listRecipeFiles } = await import('./recipes');
185
+ const { getRecipesConfig } = await import('./config');
186
+ const { parseFrontmatter } = await import('./recipe-frontmatter');
187
+ const cfg = getRecipesConfig(api.config);
188
+ const files = await listRecipeFiles(api, cfg);
189
+ for (const f of files) {
190
+ const md = await fs.readFile(f.path, 'utf8');
191
+ const { frontmatter } = parseFrontmatter(md);
192
+ if (frontmatter.id && frontmatter.name) {
193
+ recipes.push({
194
+ id: frontmatter.id,
195
+ name: frontmatter.name,
196
+ kind: frontmatter.kind === 'team' ? 'team' : 'agent',
197
+ source: f.source,
198
+ });
199
+ }
184
200
  }
185
201
  } catch { /* best-effort */ }
186
202
 
@@ -167,14 +167,14 @@ async function buildTemplateVars(
167
167
  if (typeof nestedValue === 'string') {
168
168
  vars[`${nid}.${nestedKey}`] = nestedValue;
169
169
  } else if (nestedValue !== null && nestedValue !== undefined) {
170
- vars[`${nid}.${nestedKey}_json`] = JSON.stringify(nestedValue);
170
+ vars[`${nid}.${nestedKey}`] = JSON.stringify(nestedValue);
171
171
  }
172
172
  }
173
173
  }
174
174
  } catch { /* nested parse fail is fine */ }
175
175
  }
176
176
  } else if (value !== null && value !== undefined) {
177
- vars[`${nid}.${key}_json`] = JSON.stringify(value);
177
+ vars[`${nid}.${key}`] = JSON.stringify(value);
178
178
  }
179
179
  }
180
180
  }