@farmslot/skills 0.1.0 → 0.1.1
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/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/README.md +7 -2
- package/bin/farmslot-checklist.mjs +2 -0
- package/package.json +6 -3
- package/scripts/mark-checklist-step.cjs +277 -0
- package/skills/project-adopt/SKILL.md +2 -2
- package/skills/recipe-cook/SKILL.md +1 -1
- package/skills/recipe-cook/references/TASK.md +1 -1
- package/skills/recipe-harness/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,4 +4,10 @@ All notable changes to `@farmslot/skills` are tracked here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.1.1 - 2026-06-30
|
|
8
|
+
|
|
9
|
+
- Single-path mark CLI for worker terminal signals.
|
|
10
|
+
|
|
11
|
+
## 0.1.0 - 2026-06-28
|
|
12
|
+
|
|
7
13
|
- Add the generic recipe-first skills package and migrate the former top-level recipe cooking kit into `skills/recipe-cook`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arthur Breton
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Public guide: https://farmslot.io/docs/guides/recipe-skills-adoption
|
|
|
6
6
|
|
|
7
7
|
## What This Package Is
|
|
8
8
|
|
|
9
|
-
`@farmslot/skills` is the recipe-first adoption kit. It teaches an agent how to write, review, diagnose, and adopt validation recipes before a team installs the full Farmslot
|
|
9
|
+
`@farmslot/skills` is the recipe-first adoption kit. It teaches an agent how to write, review, diagnose, and adopt validation recipes before a team installs the full Farmslot framework.
|
|
10
10
|
|
|
11
11
|
It does not require Gateway, Command Center, a pool file, slots, or Companion for first use.
|
|
12
12
|
|
|
@@ -49,7 +49,7 @@ Supported layouts:
|
|
|
49
49
|
2. Recipe runner: add a local command that can execute one recipe and emit artifacts.
|
|
50
50
|
3. Project recipe layer: add recipes, runner manifests, project actions, and artifact conventions.
|
|
51
51
|
4. Farmslot project integration: add `project.json` hooks for Command Center and slots.
|
|
52
|
-
5. Full
|
|
52
|
+
5. Full framework: adopt multi-slot dispatch, live steering, Companion supervision, and retrospectives.
|
|
53
53
|
|
|
54
54
|
## Source layout
|
|
55
55
|
|
|
@@ -61,6 +61,7 @@ Supported layouts:
|
|
|
61
61
|
|
|
62
62
|
## Maintenance rules
|
|
63
63
|
|
|
64
|
+
- `scripts/mark-checklist-step.cjs` mirrors consensys-skills `recipe-harness/scripts/mark-checklist-step.cjs`. Edit the recipe-harness copy first; sync the mirror before shipping Farmslot gateway or npm changes.
|
|
64
65
|
- Keep package payloads self-contained; consumers should not need the Farmslot monorepo.
|
|
65
66
|
- Update `CHANGELOG.md` before packaging or publishing.
|
|
66
67
|
- Run the package readiness guard when files move, bins change, or exports are added.
|
|
@@ -72,3 +73,7 @@ yarn workspace @farmslot/skills build
|
|
|
72
73
|
yarn workspace @farmslot/skills test
|
|
73
74
|
node scripts/quality/check-farmslot-package-readiness.mjs --packages @farmslot/skills
|
|
74
75
|
```
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
|
|
79
|
+
MIT. See [LICENSE](LICENSE).
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farmslot/skills",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
8
|
+
"farmslot-checklist": "./bin/farmslot-checklist.mjs",
|
|
8
9
|
"farmslot-skills": "./bin/farmslot-skills.mjs"
|
|
9
10
|
},
|
|
10
11
|
"exports": {
|
|
@@ -14,11 +15,12 @@
|
|
|
14
15
|
"default": "./dist/index.js"
|
|
15
16
|
},
|
|
16
17
|
"./skills/*": "./skills/*/SKILL.md",
|
|
17
|
-
"./schemas/*": "./schemas/*"
|
|
18
|
+
"./schemas/*": "./schemas/*",
|
|
19
|
+
"./scripts/mark-checklist-step.cjs": "./scripts/mark-checklist-step.cjs"
|
|
18
20
|
},
|
|
19
21
|
"scripts": {
|
|
20
22
|
"typecheck": "tsc --noEmit --project tsconfig.json",
|
|
21
|
-
"test": "node test/prepare-cooking-run.test.cjs && node test/run-cooking-lane.test.cjs && node test/validator-loop.test.cjs && node test/package-exports.test.cjs",
|
|
23
|
+
"test": "node test/mark-checklist-step.test.cjs && node test/prepare-cooking-run.test.cjs && node test/run-cooking-lane.test.cjs && node test/validator-loop.test.cjs && node test/package-exports.test.cjs",
|
|
22
24
|
"format": "prettier --write --ignore-path ../../.prettierignore .",
|
|
23
25
|
"format:check": "prettier --check --ignore-path ../../.prettierignore .",
|
|
24
26
|
"lint": "eslint \"src/**/*.ts\"",
|
|
@@ -53,6 +55,7 @@
|
|
|
53
55
|
],
|
|
54
56
|
"files": [
|
|
55
57
|
"README.md",
|
|
58
|
+
"LICENSE",
|
|
56
59
|
"CHANGELOG.md",
|
|
57
60
|
"bin/**/*.mjs",
|
|
58
61
|
"dist/**/*.js",
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
|
|
5
|
+
const START_COMMANDS = new Set(['start']);
|
|
6
|
+
const TERMINAL_COMMANDS = new Set(['complete', 'no-change', 'blocked']);
|
|
7
|
+
const NO_CHANGE_REPORT = path.join('artifacts', 'no-change-report.md');
|
|
8
|
+
|
|
9
|
+
const usageLine =
|
|
10
|
+
'usage: mark <step-number|start|complete|no-change|blocked> [--reason text] [--already-fixed] [--mark-last] [--no-self-review]';
|
|
11
|
+
|
|
12
|
+
function printHelp() {
|
|
13
|
+
console.log(
|
|
14
|
+
[
|
|
15
|
+
usageLine,
|
|
16
|
+
'',
|
|
17
|
+
'Bootstrap: ./mark start — worker-owned SIGNAL.json with status running (no checklist box).',
|
|
18
|
+
'Progress: ./mark 1, ./mark 2, ... — checks the box and appends checklistTiming.',
|
|
19
|
+
'Terminal:',
|
|
20
|
+
' ./mark complete [--mark-last] [--no-self-review]',
|
|
21
|
+
' ./mark no-change --reason "..." [--already-fixed] [--mark-last]',
|
|
22
|
+
' ./mark blocked --reason "..." [--mark-last]',
|
|
23
|
+
'Do not write SIGNAL.json by hand or with echo.',
|
|
24
|
+
].join('\n'),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function usage() {
|
|
29
|
+
console.error(usageLine);
|
|
30
|
+
console.error('run ./mark --help for details');
|
|
31
|
+
process.exit(2);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const args = process.argv.slice(2);
|
|
35
|
+
if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
|
|
36
|
+
printHelp();
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const [taskPath, signalPath, stepRaw, ...rest] = args;
|
|
41
|
+
if (stepRaw === '--help' || stepRaw === '-h') {
|
|
42
|
+
printHelp();
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
if (!taskPath || !signalPath || !stepRaw) usage();
|
|
46
|
+
|
|
47
|
+
const opts = {};
|
|
48
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
49
|
+
const key = rest[i];
|
|
50
|
+
if (key === '--mark-last' || key === '--already-fixed' || key === '--no-self-review') {
|
|
51
|
+
opts[key.slice(2)] = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const value = rest[i + 1];
|
|
55
|
+
if (!key?.startsWith('--') || value === undefined) usage();
|
|
56
|
+
opts[key.slice(2)] = value;
|
|
57
|
+
i += 1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const stepToken = String(stepRaw).toLowerCase();
|
|
61
|
+
const isStartCommand = START_COMMANDS.has(stepToken);
|
|
62
|
+
const terminalCommand = TERMINAL_COMMANDS.has(stepToken) ? stepToken : null;
|
|
63
|
+
let stepNumber = null;
|
|
64
|
+
if (!isStartCommand && !terminalCommand) {
|
|
65
|
+
stepNumber = Number(stepRaw);
|
|
66
|
+
if (!Number.isInteger(stepNumber) || stepNumber < 1) usage();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (isStartCommand && Object.keys(opts).length > 0) usage();
|
|
70
|
+
if (terminalCommand === 'no-change' || terminalCommand === 'blocked') {
|
|
71
|
+
if (!opts.reason?.trim()) {
|
|
72
|
+
console.error(`${terminalCommand} requires --reason`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const SIGNAL_PASSTHROUGH_KEYS = ['role', 'contextId', 'prNumber'];
|
|
78
|
+
|
|
79
|
+
function pickSignalPassthrough(signal) {
|
|
80
|
+
const out = {};
|
|
81
|
+
for (const key of SIGNAL_PASSTHROUGH_KEYS) {
|
|
82
|
+
if (signal[key] !== undefined) out[key] = signal[key];
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resolveTerminalPreset(command) {
|
|
88
|
+
switch (command) {
|
|
89
|
+
case 'complete':
|
|
90
|
+
return { status: 'complete', outcome: 'success', disposition: 'fixed' };
|
|
91
|
+
case 'no-change':
|
|
92
|
+
return {
|
|
93
|
+
status: 'complete',
|
|
94
|
+
outcome: 'success',
|
|
95
|
+
disposition: opts['already-fixed'] ? 'already_fixed' : 'not_reproducible',
|
|
96
|
+
};
|
|
97
|
+
case 'blocked':
|
|
98
|
+
return { status: 'blocked', outcome: 'partial', disposition: 'blocked' };
|
|
99
|
+
default:
|
|
100
|
+
usage();
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function assertNoChangeReport(taskDir) {
|
|
106
|
+
const reportAbs = path.join(taskDir, NO_CHANGE_REPORT);
|
|
107
|
+
if (!fs.existsSync(reportAbs) || fs.statSync(reportAbs).size === 0) {
|
|
108
|
+
console.error(`missing required report: ${NO_CHANGE_REPORT}`);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
return NO_CHANGE_REPORT;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function buildSignalUpdate(signal, terminal, target, timing, events, now, taskPath, taskDir) {
|
|
115
|
+
const base = {
|
|
116
|
+
...pickSignalPassthrough(signal),
|
|
117
|
+
step: target?.label ?? signal.step ?? 'complete',
|
|
118
|
+
checklistTiming: {
|
|
119
|
+
schemaVersion: 1,
|
|
120
|
+
source: timing.source || path.basename(taskPath),
|
|
121
|
+
events,
|
|
122
|
+
},
|
|
123
|
+
timestamp: now,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (!terminal) {
|
|
127
|
+
return { ...base, status: 'running' };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const preset = resolveTerminalPreset(terminal.command);
|
|
131
|
+
const next = {
|
|
132
|
+
...base,
|
|
133
|
+
status: preset.status,
|
|
134
|
+
outcome: preset.outcome,
|
|
135
|
+
disposition: preset.disposition,
|
|
136
|
+
...(opts.reason ? { reason: opts.reason } : {}),
|
|
137
|
+
...(terminal.command === 'complete' && opts['no-self-review']
|
|
138
|
+
? { needsSelfReview: false }
|
|
139
|
+
: {}),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (terminal.command === 'no-change') {
|
|
143
|
+
next.evidence = { reportPath: assertNoChangeReport(taskDir) };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return next;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function atomicWrite(file, content, mode) {
|
|
150
|
+
const dir = path.dirname(file);
|
|
151
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
152
|
+
const tmp = path.join(dir, `.${path.basename(file)}.${process.pid}.tmp`);
|
|
153
|
+
fs.writeFileSync(tmp, content, mode ? { mode } : undefined);
|
|
154
|
+
fs.renameSync(tmp, file);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function readJson(file) {
|
|
158
|
+
try {
|
|
159
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
160
|
+
} catch (err) {
|
|
161
|
+
if (err && err.code === 'ENOENT') return {};
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function stripLabel(raw) {
|
|
167
|
+
return raw
|
|
168
|
+
.replace(/^\*\*(.*?)\*\*\s*[—-]?\s*/, '$1 ')
|
|
169
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
170
|
+
.replace(/\s+/g, ' ')
|
|
171
|
+
.trim();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function parseChecklist(markdown) {
|
|
175
|
+
const lines = markdown.split(/\n/);
|
|
176
|
+
const items = [];
|
|
177
|
+
let inFence = false;
|
|
178
|
+
let seen = 0;
|
|
179
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
180
|
+
if (/^\s*```/.test(lines[i])) {
|
|
181
|
+
inFence = !inFence;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (inFence) continue;
|
|
185
|
+
const match = lines[i].match(/^(\s*[-*]\s+\[)( |x|X)(\]\s+)(.*)$/);
|
|
186
|
+
if (!match) continue;
|
|
187
|
+
seen += 1;
|
|
188
|
+
items.push({
|
|
189
|
+
lineIndex: i,
|
|
190
|
+
stepNumber: seen,
|
|
191
|
+
checked: match[2].toLowerCase() === 'x',
|
|
192
|
+
prefix: match[1],
|
|
193
|
+
suffix: match[3],
|
|
194
|
+
rawLabel: match[4],
|
|
195
|
+
label: stripLabel(match[4]),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return { lines, items };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function markStepInLines(lines, item) {
|
|
202
|
+
if (item.checked) return false;
|
|
203
|
+
lines[item.lineIndex] = `${item.prefix}x${item.suffix}${item.rawLabel}`;
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function resolveTarget(taskPath, stepNumber, markLast) {
|
|
208
|
+
const original = fs.readFileSync(taskPath, 'utf8');
|
|
209
|
+
const parsed = parseChecklist(original);
|
|
210
|
+
let item = null;
|
|
211
|
+
if (stepNumber != null) {
|
|
212
|
+
item = parsed.items.find((entry) => entry.stepNumber === stepNumber) ?? null;
|
|
213
|
+
if (!item) {
|
|
214
|
+
console.error(`checklist step ${stepNumber} not found in ${taskPath}`);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
} else if (markLast) {
|
|
218
|
+
const unchecked = parsed.items.filter((entry) => !entry.checked);
|
|
219
|
+
item = unchecked.length ? unchecked[unchecked.length - 1] : null;
|
|
220
|
+
}
|
|
221
|
+
let updated = original;
|
|
222
|
+
if (item) {
|
|
223
|
+
const nextLines = [...parsed.lines];
|
|
224
|
+
markStepInLines(nextLines, item);
|
|
225
|
+
updated = nextLines.join('\n');
|
|
226
|
+
if (updated !== original) atomicWrite(taskPath, updated);
|
|
227
|
+
}
|
|
228
|
+
return item
|
|
229
|
+
? { stepNumber: item.stepNumber, label: item.label }
|
|
230
|
+
: { stepNumber: null, label: 'complete' };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const taskDir = path.dirname(signalPath);
|
|
234
|
+
const target = isStartCommand
|
|
235
|
+
? { stepNumber: null, label: 'started' }
|
|
236
|
+
: resolveTarget(taskPath, stepNumber, Boolean(terminalCommand && opts['mark-last']));
|
|
237
|
+
|
|
238
|
+
const now = new Date().toISOString();
|
|
239
|
+
const signal = readJson(signalPath);
|
|
240
|
+
const timing =
|
|
241
|
+
signal.checklistTiming && typeof signal.checklistTiming === 'object'
|
|
242
|
+
? signal.checklistTiming
|
|
243
|
+
: { schemaVersion: 1, source: path.basename(taskPath), events: [] };
|
|
244
|
+
const events = Array.isArray(timing.events) ? [...timing.events] : [];
|
|
245
|
+
|
|
246
|
+
if (
|
|
247
|
+
!isStartCommand &&
|
|
248
|
+
target.stepNumber != null &&
|
|
249
|
+
!events.some(
|
|
250
|
+
(event) =>
|
|
251
|
+
event && (event.stepNumber === target.stepNumber || event.index === target.stepNumber - 1),
|
|
252
|
+
)
|
|
253
|
+
) {
|
|
254
|
+
events.push({ stepNumber: target.stepNumber, label: target.label, checkedAt: now });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const next = buildSignalUpdate(
|
|
258
|
+
signal,
|
|
259
|
+
terminalCommand ? { command: terminalCommand } : null,
|
|
260
|
+
target,
|
|
261
|
+
timing,
|
|
262
|
+
events,
|
|
263
|
+
now,
|
|
264
|
+
taskPath,
|
|
265
|
+
taskDir,
|
|
266
|
+
);
|
|
267
|
+
atomicWrite(signalPath, `${JSON.stringify(next, null, 2)}\n`, 0o644);
|
|
268
|
+
|
|
269
|
+
if (isStartCommand) {
|
|
270
|
+
console.log('signal started');
|
|
271
|
+
} else if (terminalCommand) {
|
|
272
|
+
console.log(
|
|
273
|
+
`signal ${terminalCommand}: status=${next.status} disposition=${next.disposition ?? 'n/a'}`,
|
|
274
|
+
);
|
|
275
|
+
} else {
|
|
276
|
+
console.log(`marked ${target.stepNumber}: ${target.label}`);
|
|
277
|
+
}
|
|
@@ -17,7 +17,7 @@ Recommend a pragmatic adoption path for recipe-first validation.
|
|
|
17
17
|
1. Recipe runner: add a local command that executes one recipe and emits artifacts.
|
|
18
18
|
2. Project recipe layer: add recipes, runner manifest, project actions, and artifact conventions.
|
|
19
19
|
3. Farmslot project integration: add `project.json` hooks for slot-based execution.
|
|
20
|
-
4. Full
|
|
20
|
+
4. Full framework: adopt Gateway, Command Center, multi-slot dispatch, Companion, and retrospectives.
|
|
21
21
|
|
|
22
22
|
## Workflow
|
|
23
23
|
|
|
@@ -28,6 +28,6 @@ Recommend a pragmatic adoption path for recipe-first validation.
|
|
|
28
28
|
|
|
29
29
|
## Hard Rules
|
|
30
30
|
|
|
31
|
-
- Do not prescribe the full Farmslot
|
|
31
|
+
- Do not prescribe the full Farmslot framework when a local recipe runner is enough.
|
|
32
32
|
- Do not add domain-specific assumptions.
|
|
33
33
|
- Do not require a project to change its test stack before proving the first useful recipe.
|
|
@@ -61,7 +61,7 @@ If an interactive slot exists, do not stop after pasting or emitting JSON in cha
|
|
|
61
61
|
|
|
62
62
|
The harness order is mandatory.
|
|
63
63
|
Do not silently batch or skip steps.
|
|
64
|
-
Mark each completed checklist step `[x]`
|
|
64
|
+
Mark each completed checklist step immediately. If the run-local task has a `mark` helper, use `./mark N` (1-based); otherwise change `[ ]` to `[x]` yourself. Do not batch checkbox updates.
|
|
65
65
|
|
|
66
66
|
## Output Contract
|
|
67
67
|
|
|
@@ -10,7 +10,7 @@ Do not edit the template in-place during a cooking run.
|
|
|
10
10
|
|
|
11
11
|
Every time you complete a checklist step:
|
|
12
12
|
|
|
13
|
-
1. immediately change `- [ ]` to `- [x]`
|
|
13
|
+
1. run `./mark N` if present (1-based); otherwise immediately change `- [ ]` to `- [x]`
|
|
14
14
|
2. update `STATUS` if needed
|
|
15
15
|
3. only then move to the next step
|
|
16
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recipe-harness
|
|
3
|
-
description: Help run or install a local recipe runtime without assuming the full Farmslot
|
|
3
|
+
description: Help run or install a local recipe runtime without assuming the full Farmslot framework
|
|
4
4
|
compatibility: Claude, Codex, Cursor, and Markdown skill runners with shell access
|
|
5
5
|
metadata:
|
|
6
6
|
package: '@farmslot/skills'
|