@farmslot/agent-runtime 0.2.0 → 0.3.0
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 +14 -0
- package/bin/farmslot-agent.mjs +6 -0
- package/dist/execution-template/create.d.ts +7 -0
- package/dist/execution-template/create.d.ts.map +1 -0
- package/dist/execution-template/create.js +63 -0
- package/dist/execution-template/create.js.map +1 -0
- package/dist/execution-template/execution-template.test.d.ts +2 -0
- package/dist/execution-template/execution-template.test.d.ts.map +1 -0
- package/dist/execution-template/execution-template.test.js +340 -0
- package/dist/execution-template/execution-template.test.js.map +1 -0
- package/dist/execution-template/frontmatter.d.ts +16 -0
- package/dist/execution-template/frontmatter.d.ts.map +1 -0
- package/dist/execution-template/frontmatter.js +110 -0
- package/dist/execution-template/frontmatter.js.map +1 -0
- package/dist/execution-template/index.d.ts +7 -0
- package/dist/execution-template/index.d.ts.map +1 -0
- package/dist/execution-template/index.js +6 -0
- package/dist/execution-template/index.js.map +1 -0
- package/dist/execution-template/infer.d.ts +25 -0
- package/dist/execution-template/infer.d.ts.map +1 -0
- package/dist/execution-template/infer.js +122 -0
- package/dist/execution-template/infer.js.map +1 -0
- package/dist/execution-template/lint.d.ts +6 -0
- package/dist/execution-template/lint.d.ts.map +1 -0
- package/dist/execution-template/lint.js +203 -0
- package/dist/execution-template/lint.js.map +1 -0
- package/dist/execution-template/resolve.d.ts +12 -0
- package/dist/execution-template/resolve.d.ts.map +1 -0
- package/dist/execution-template/resolve.js +126 -0
- package/dist/execution-template/resolve.js.map +1 -0
- package/dist/execution-template/types.d.ts +66 -0
- package/dist/execution-template/types.d.ts.map +1 -0
- package/dist/execution-template/types.js +3 -0
- package/dist/execution-template/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
- package/scripts/check-task-artifact-contract.mjs +208 -63
- package/scripts/execution-template-cli.mjs +203 -0
- package/scripts/mark-checklist-step.cjs +26 -8
- package/scripts/worker-terminal-contract.cjs +23 -21
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.3.0 - 2026-07-24
|
|
6
|
+
|
|
7
|
+
- Fail artifact validation when the installed protocol rejects the canonical Recipe v1 envelope.
|
|
8
|
+
|
|
9
|
+
- **BREAKING:** Task artifact checks require Recipe v1 resolution metadata and exact reachable dependency documents.
|
|
10
|
+
- security: resolved dependency reads use digest-derived paths only.
|
|
11
|
+
- Validate authored recipes separately from their executed `artifacts/recipe-run` package.
|
|
12
|
+
|
|
13
|
+
- fix: worker-template lint now fails templates that omit `./mark` entirely when the resolved contract requires a terminal signal — previously such templates were skipped and produced no author-time issue (mark-less templates stay valid for `requireSignal: false` flows such as interactive pr-complete).
|
|
14
|
+
- fix: builtin `self-review` / `self-review-fix` contracts require the reviewer artifacts the templates actually produce (`artifacts/review-feedback.md` / `artifacts/report.md`) instead of `artifacts/learnings.md`, and the runtime flow-report mapping + heading inference cover both flows — unbreaks `yarn quality:worker-templates`, which was failing on main.
|
|
15
|
+
- fix: recognize CI-fix worker headings as the `ci-fix` flow and lint task-dir `mark --checklist ...` terminal commands, so `CI-FIX-SIGNAL.json` uses the intended report contract.
|
|
16
|
+
- build: declare `tsx` as a devDependency — the execution-template tests run through the workspace-scoped `run-tsx-tests.mjs` runner (`yarn exec tsx`), which cannot resolve an undeclared binary on a clean CI install.
|
|
17
|
+
- feat: add shared Markdown execution-template resolver/lint/new (ADR-049) with `farmslot-agent execution-template` CLI.
|
|
18
|
+
- refactor: rename the branch-maintenance flow `merge-main` → `update-branch` in the worker terminal contract, checklist marker, and task artifact contract.
|
|
5
19
|
- Active-development baseline; add user-facing changes here before release or package publication.
|
|
6
20
|
|
|
7
21
|
## 0.2.0 - 2026-07-12
|
package/bin/farmslot-agent.mjs
CHANGED
|
@@ -19,6 +19,7 @@ function usage(exitCode = 0) {
|
|
|
19
19
|
' recipe-quality build [--input input.json] [--output artifacts/recipe-quality.json]',
|
|
20
20
|
' (flags override top-level input fields; training fields are merged)',
|
|
21
21
|
' contract resolve --flow <flow> [--project-config path] [--mode mode]',
|
|
22
|
+
' execution-template <list|lint|new> [options]',
|
|
22
23
|
].join('\n');
|
|
23
24
|
(exitCode === 0 ? console.log : console.error)(text);
|
|
24
25
|
process.exit(exitCode);
|
|
@@ -470,6 +471,11 @@ async function main() {
|
|
|
470
471
|
resolveContract(rest);
|
|
471
472
|
} else if (command === 'recipe-quality' && subcommand === 'build') {
|
|
472
473
|
await buildRecipeQuality(rest);
|
|
474
|
+
} else if (command === 'execution-template') {
|
|
475
|
+
runNode(
|
|
476
|
+
path.join(packageRoot, 'scripts', 'execution-template-cli.mjs'),
|
|
477
|
+
[subcommand, ...rest].filter((arg) => arg !== undefined),
|
|
478
|
+
);
|
|
473
479
|
} else {
|
|
474
480
|
usage(2);
|
|
475
481
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CreateExecutionTemplateOptions } from './types.js';
|
|
2
|
+
/** Create a starter Markdown execution template with minimal optional frontmatter. */
|
|
3
|
+
export declare function createExecutionTemplate(options: CreateExecutionTemplateOptions): {
|
|
4
|
+
path: string;
|
|
5
|
+
created: boolean;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/execution-template/create.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,8BAA8B,EAAoB,MAAM,YAAY,CAAC;AAgCnF,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,8BAA8B,GAAG;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB,CAoDA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { inferFlowFromPath, inferRunModeFromBasename } from './infer.js';
|
|
4
|
+
import { lintExecutionTemplateText } from './lint.js';
|
|
5
|
+
function defaultTitle(flow, runMode) {
|
|
6
|
+
const modeLabel = runMode ? ` (${runMode})` : '';
|
|
7
|
+
return `${flow} execution template${modeLabel}`;
|
|
8
|
+
}
|
|
9
|
+
function renderTemplate(input) {
|
|
10
|
+
const meta = [];
|
|
11
|
+
if (input.runMode)
|
|
12
|
+
meta.push(`runMode: ${input.runMode}`);
|
|
13
|
+
if (input.platforms.length > 0 && !(input.platforms.length === 1 && input.platforms[0] === '*')) {
|
|
14
|
+
meta.push(`platforms: [${input.platforms.join(', ')}]`);
|
|
15
|
+
}
|
|
16
|
+
// No metadata → no frontmatter block: an empty fence pair is pointless and
|
|
17
|
+
// easy for permissive parsers to misread as an unterminated block.
|
|
18
|
+
const lines = meta.length > 0 ? ['---', ...meta, '---', ''] : [];
|
|
19
|
+
lines.push(`# ${input.title}`, '', 'Checklist:', '');
|
|
20
|
+
lines.push('- [ ] Read the task prompt and confirm acceptance criteria.', '- [ ] Implement the smallest correct change.', '- [ ] Run focused validation and attach evidence under `artifacts/`.', '- [ ] Mark the task complete, blocked, or no-change.', '');
|
|
21
|
+
return `${lines.join('\n')}\n`;
|
|
22
|
+
}
|
|
23
|
+
/** Create a starter Markdown execution template with minimal optional frontmatter. */
|
|
24
|
+
export function createExecutionTemplate(options) {
|
|
25
|
+
const absolute = path.resolve(options.path);
|
|
26
|
+
if (existsSync(absolute) && !options.force) {
|
|
27
|
+
throw new Error(`refusing to overwrite existing file: ${absolute} (pass force: true)`);
|
|
28
|
+
}
|
|
29
|
+
const basename = path.basename(absolute);
|
|
30
|
+
if (!basename.endsWith('.md')) {
|
|
31
|
+
throw new Error('template path must end with .md');
|
|
32
|
+
}
|
|
33
|
+
// Resolve and cross-check flow BEFORE anything touches the filesystem: the
|
|
34
|
+
// catalog derives flow from the filename, so a --flow that the filename does
|
|
35
|
+
// not encode (or contradicts) would create a template that resolves as a
|
|
36
|
+
// different flow than requested.
|
|
37
|
+
const fileFlow = inferFlowFromPath(absolute);
|
|
38
|
+
const flow = options.flow ?? fileFlow;
|
|
39
|
+
if (!flow) {
|
|
40
|
+
throw new Error('could not infer flow from the path; use a flow-prefixed filename (dev-autonomous.mobile.md) or a flow directory (fix-bug/core.md)');
|
|
41
|
+
}
|
|
42
|
+
if (options.flow && fileFlow && options.flow !== fileFlow) {
|
|
43
|
+
throw new Error(`requested flow '${options.flow}' contradicts the path's flow '${fileFlow}'; rename the file or drop --flow`);
|
|
44
|
+
}
|
|
45
|
+
if (options.flow && !fileFlow) {
|
|
46
|
+
throw new Error(`the path must encode the flow ('${options.flow}') for the catalog to resolve it — flow-prefixed filename or a ${options.flow}/ directory`);
|
|
47
|
+
}
|
|
48
|
+
const runMode = options.runMode ?? inferRunModeFromBasename(basename);
|
|
49
|
+
const platforms = options.platforms ?? ['*'];
|
|
50
|
+
const title = options.title ?? defaultTitle(flow, runMode);
|
|
51
|
+
const body = renderTemplate({ flow, runMode, platforms, title });
|
|
52
|
+
// Lint the rendered TEXT before writing — a failed creation must leave no
|
|
53
|
+
// file behind (and must never destroy an existing file via force).
|
|
54
|
+
const issues = lintExecutionTemplateText(absolute, body).filter((issue) => issue.severity === 'error');
|
|
55
|
+
if (issues.length > 0) {
|
|
56
|
+
const detail = issues.map((issue) => issue.message).join('; ');
|
|
57
|
+
throw new Error(`template would fail lint: ${detail}`);
|
|
58
|
+
}
|
|
59
|
+
mkdirSync(path.dirname(absolute), { recursive: true });
|
|
60
|
+
writeFileSync(absolute, body, 'utf8');
|
|
61
|
+
return { path: absolute, created: true };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/execution-template/create.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-template.test.d.ts","sourceRoot":"","sources":["../../src/execution-template/execution-template.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
import { createExecutionTemplate } from './create.js';
|
|
7
|
+
import { parseMarkdownDocument } from './frontmatter.js';
|
|
8
|
+
import { catalogRelativeId, inferFlowFromBasename, inferRunModeFromBasename, inferTemplateMetadata, } from './infer.js';
|
|
9
|
+
import { lintExecutionTemplates, lintExecutionTemplateText } from './lint.js';
|
|
10
|
+
import { customTemplateSource, listExecutionTemplates, packageFlowTreeTemplateSource, projectWorkerTemplateSource, } from './resolve.js';
|
|
11
|
+
function withTemp(fn) {
|
|
12
|
+
const root = mkdtempSync(join(tmpdir(), 'farmslot-exec-tpl-'));
|
|
13
|
+
try {
|
|
14
|
+
fn(root);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
rmSync(root, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
test('inferFlowFromBasename uses longest Farmslot flow prefix', () => {
|
|
21
|
+
assert.equal(inferFlowFromBasename('fix-bug-autonomous.mobile.md'), 'fix-bug');
|
|
22
|
+
assert.equal(inferFlowFromBasename('pr-complete-interactive.md'), 'pr-complete');
|
|
23
|
+
assert.equal(inferFlowFromBasename('dev.md'), 'dev');
|
|
24
|
+
assert.equal(inferFlowFromBasename('review-pr.md'), 'review-pr');
|
|
25
|
+
assert.equal(inferFlowFromBasename('notes.md'), null);
|
|
26
|
+
});
|
|
27
|
+
test('inferRunModeFromBasename resolves only explicit mode tokens', () => {
|
|
28
|
+
// A bare flow name encodes no mode — review-pr/update-branch run interactive
|
|
29
|
+
// as often as autonomous, so nothing may default here.
|
|
30
|
+
assert.equal(inferRunModeFromBasename('dev.md'), null);
|
|
31
|
+
assert.equal(inferRunModeFromBasename('review-pr.md'), null);
|
|
32
|
+
assert.equal(inferRunModeFromBasename('dev-interactive.md'), 'interactive');
|
|
33
|
+
assert.equal(inferRunModeFromBasename('dev-autonomous.mobile.md'), 'autonomous');
|
|
34
|
+
assert.equal(inferRunModeFromBasename('fix-bug-interactive.extension.md'), 'interactive');
|
|
35
|
+
});
|
|
36
|
+
test('self-review-fix resolves as its own prefix, not self-review', () => {
|
|
37
|
+
assert.equal(inferFlowFromBasename('self-review-fix.md'), 'self-review-fix');
|
|
38
|
+
assert.equal(inferFlowFromBasename('self-review.md'), 'self-review');
|
|
39
|
+
});
|
|
40
|
+
test('catalog ids collide across worker-flat and flow-tree layouts', () => {
|
|
41
|
+
// Precedence/shadowing is keyed on id — a project worker-flat override and a
|
|
42
|
+
// package flow-tree base MUST normalize to the same id.
|
|
43
|
+
assert.equal(catalogRelativeId('fix-bug.core.md', 'worker-flat'), 'fix-bug/core');
|
|
44
|
+
assert.equal(catalogRelativeId('fix-bug/core.md', 'flow-tree'), 'fix-bug/core');
|
|
45
|
+
assert.equal(catalogRelativeId('dev.md', 'worker-flat'), 'dev/default');
|
|
46
|
+
assert.equal(catalogRelativeId('dev-interactive.md', 'worker-flat'), 'dev/interactive');
|
|
47
|
+
assert.equal(catalogRelativeId('notes.md', 'worker-flat'), 'notes');
|
|
48
|
+
});
|
|
49
|
+
test('inferTemplateMetadata prefers frontmatter then filename/heading', () => {
|
|
50
|
+
const meta = inferTemplateMetadata({
|
|
51
|
+
absolutePath: '/tmp/dev-autonomous.mobile.md',
|
|
52
|
+
relativePath: 'dev/dev-autonomous.mobile.md',
|
|
53
|
+
source: {
|
|
54
|
+
id: 'package:recipe-cook',
|
|
55
|
+
kind: 'package',
|
|
56
|
+
root: '/tmp',
|
|
57
|
+
layout: 'flow-tree',
|
|
58
|
+
},
|
|
59
|
+
text: `---
|
|
60
|
+
id: custom-dev-mobile
|
|
61
|
+
title: Custom Dev
|
|
62
|
+
runMode: autonomous
|
|
63
|
+
platforms: [mobile]
|
|
64
|
+
version: 2
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
# Ignored heading
|
|
68
|
+
|
|
69
|
+
- [ ] Do the work
|
|
70
|
+
`,
|
|
71
|
+
});
|
|
72
|
+
assert.equal(meta.id, 'custom-dev-mobile');
|
|
73
|
+
assert.equal(meta.title, 'Custom Dev');
|
|
74
|
+
assert.equal(meta.flow, 'dev');
|
|
75
|
+
assert.equal(meta.version, '2');
|
|
76
|
+
assert.equal(meta.runMode, 'autonomous');
|
|
77
|
+
assert.deepEqual(meta.platforms, ['mobile']);
|
|
78
|
+
});
|
|
79
|
+
test('listExecutionTemplates reports shadowing across project and package sources', () => {
|
|
80
|
+
withTemp((root) => {
|
|
81
|
+
const projectWorker = join(root, 'project', 'templates', 'worker');
|
|
82
|
+
const packageTpl = join(root, 'skills', 'templates');
|
|
83
|
+
mkdirSync(projectWorker, { recursive: true });
|
|
84
|
+
mkdirSync(join(packageTpl, 'dev'), { recursive: true });
|
|
85
|
+
writeFileSync(join(projectWorker, 'dev.md'), '# Legacy Dev\n\n- [ ] Farm step\n', 'utf8');
|
|
86
|
+
// Same explicit id as legacy mapping would not collide; create a colliding id via frontmatter.
|
|
87
|
+
writeFileSync(join(packageTpl, 'dev', 'dev-autonomous.mobile.md'), `---
|
|
88
|
+
id: dev/default
|
|
89
|
+
runMode: autonomous
|
|
90
|
+
platforms: [mobile]
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
# Shared Dev
|
|
94
|
+
|
|
95
|
+
- [ ] Shared step
|
|
96
|
+
`, 'utf8');
|
|
97
|
+
const entries = listExecutionTemplates({
|
|
98
|
+
sources: [
|
|
99
|
+
projectWorkerTemplateSource('metamask-mobile-farm', join(root, 'project', 'templates')),
|
|
100
|
+
packageFlowTreeTemplateSource('recipe-cook', packageTpl),
|
|
101
|
+
],
|
|
102
|
+
includeShadowed: true,
|
|
103
|
+
});
|
|
104
|
+
const winners = entries.filter((e) => !e.shadowedBy && e.id === 'dev/default');
|
|
105
|
+
const shadowed = entries.filter((e) => e.shadowedBy && e.id === 'dev/default');
|
|
106
|
+
assert.equal(winners.length, 1);
|
|
107
|
+
assert.equal(winners[0]?.sourceKind, 'project');
|
|
108
|
+
assert.equal(shadowed.length, 1);
|
|
109
|
+
assert.equal(shadowed[0]?.sourceKind, 'package');
|
|
110
|
+
assert.equal(shadowed[0]?.shadowedBy, 'project:metamask-mobile-farm');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
test('lintExecutionTemplates accepts valid checkboxes and rejects fix-ticket / bad boxes', () => {
|
|
114
|
+
withTemp((root) => {
|
|
115
|
+
const ok = join(root, 'dev-autonomous.mobile.md');
|
|
116
|
+
writeFileSync(ok, '# Ok\n\n- [ ] one\n- [x] two\n', 'utf8');
|
|
117
|
+
const okResult = lintExecutionTemplates(ok);
|
|
118
|
+
assert.equal(okResult.ok, true);
|
|
119
|
+
const bad = join(root, 'dev-bad.md');
|
|
120
|
+
writeFileSync(bad, '# Bad\n\n- [?] broken\nfix-ticket leftover\n', 'utf8');
|
|
121
|
+
const badResult = lintExecutionTemplates(bad);
|
|
122
|
+
assert.equal(badResult.ok, false);
|
|
123
|
+
assert.ok(badResult.issues.some((i) => /fix-ticket/.test(i.message)));
|
|
124
|
+
assert.ok(badResult.issues.some((i) => /checkbox/.test(i.message)));
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
test('createExecutionTemplate writes lint-clean starter with optional frontmatter', () => {
|
|
128
|
+
withTemp((root) => {
|
|
129
|
+
const target = join(root, 'dev', 'dev-autonomous.mobile.md');
|
|
130
|
+
const created = createExecutionTemplate({
|
|
131
|
+
path: target,
|
|
132
|
+
runMode: 'autonomous',
|
|
133
|
+
platforms: ['mobile'],
|
|
134
|
+
});
|
|
135
|
+
assert.equal(created.created, true);
|
|
136
|
+
const text = readFileSync(target, 'utf8');
|
|
137
|
+
assert.match(text, /runMode: autonomous/);
|
|
138
|
+
assert.match(text, /platforms: \[mobile\]/);
|
|
139
|
+
assert.match(text, /- \[ \] Read the task prompt/);
|
|
140
|
+
assert.equal(lintExecutionTemplates(target).ok, true);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
test('custom source outranks package source for the same id', () => {
|
|
144
|
+
withTemp((root) => {
|
|
145
|
+
const custom = join(root, 'custom');
|
|
146
|
+
const pkg = join(root, 'pkg');
|
|
147
|
+
mkdirSync(join(custom, 'dev'), { recursive: true });
|
|
148
|
+
mkdirSync(join(pkg, 'dev'), { recursive: true });
|
|
149
|
+
const body = (label) => `---\nid: shared-dev\n---\n\n# ${label}\n\n- [ ] step\n`;
|
|
150
|
+
writeFileSync(join(custom, 'dev', 'dev-autonomous.mobile.md'), body('custom'), 'utf8');
|
|
151
|
+
writeFileSync(join(pkg, 'dev', 'dev-autonomous.mobile.md'), body('package'), 'utf8');
|
|
152
|
+
const entries = listExecutionTemplates({
|
|
153
|
+
sources: [
|
|
154
|
+
packageFlowTreeTemplateSource('recipe-cook', pkg),
|
|
155
|
+
customTemplateSource('overlay', custom),
|
|
156
|
+
],
|
|
157
|
+
});
|
|
158
|
+
const winner = entries.find((e) => e.id === 'shared-dev' && !e.shadowedBy);
|
|
159
|
+
assert.equal(winner?.sourceKind, 'custom');
|
|
160
|
+
assert.equal(winner?.title, 'custom');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
test('shadowing resolves before filters — a filtered winner still shadows its duplicate', () => {
|
|
164
|
+
const dir = mkdtempSync(join(tmpdir(), 'et-filter-'));
|
|
165
|
+
try {
|
|
166
|
+
mkdirSync(join(dir, 'proj', 'worker'), { recursive: true });
|
|
167
|
+
mkdirSync(join(dir, 'pkg', 'dev'), { recursive: true });
|
|
168
|
+
// Project winner is mobile-only; package duplicate claims extension.
|
|
169
|
+
writeFileSync(join(dir, 'proj', 'worker', 'dev.md'), '---\nplatforms: [mobile]\n---\n\n# P\n\n- [ ] step\n');
|
|
170
|
+
writeFileSync(join(dir, 'pkg', 'dev', 'default.md'), '---\nplatforms: [extension]\n---\n\n# S\n\n- [ ] step\n');
|
|
171
|
+
const entries = listExecutionTemplates({
|
|
172
|
+
sources: [
|
|
173
|
+
projectWorkerTemplateSource('p', join(dir, 'proj')),
|
|
174
|
+
packageFlowTreeTemplateSource('s', join(dir, 'pkg')),
|
|
175
|
+
],
|
|
176
|
+
platform: 'extension',
|
|
177
|
+
});
|
|
178
|
+
// The package copy is shadowed by the (filtered-out) project winner — the
|
|
179
|
+
// filter must not resurrect it as effective.
|
|
180
|
+
const effective = entries.filter((e) => !e.shadowedBy);
|
|
181
|
+
assert.equal(effective.length, 0);
|
|
182
|
+
const shadowedEntry = entries.find((e) => e.shadowedBy);
|
|
183
|
+
assert.equal(shadowedEntry?.sourceKind, 'package');
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
rmSync(dir, { recursive: true, force: true });
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
test('same-kind precedence follows caller order, not source-id alphabet', () => {
|
|
190
|
+
const dir = mkdtempSync(join(tmpdir(), 'et-order-'));
|
|
191
|
+
try {
|
|
192
|
+
mkdirSync(join(dir, 'zeta', 'dev'), { recursive: true });
|
|
193
|
+
mkdirSync(join(dir, 'alpha', 'dev'), { recursive: true });
|
|
194
|
+
writeFileSync(join(dir, 'zeta', 'dev', 'default.md'), '# Z\n\n- [ ] step\n');
|
|
195
|
+
writeFileSync(join(dir, 'alpha', 'dev', 'default.md'), '# A\n\n- [ ] step\n');
|
|
196
|
+
const entries = listExecutionTemplates({
|
|
197
|
+
sources: [
|
|
198
|
+
customTemplateSource('zeta', join(dir, 'zeta')),
|
|
199
|
+
customTemplateSource('alpha', join(dir, 'alpha')),
|
|
200
|
+
],
|
|
201
|
+
});
|
|
202
|
+
const winner = entries.find((e) => e.id === 'dev/default' && !e.shadowedBy);
|
|
203
|
+
assert.equal(winner?.sourceId, 'custom:zeta');
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
rmSync(dir, { recursive: true, force: true });
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
test('create validates before writing: failure leaves no file and force never destroys', () => {
|
|
210
|
+
const dir = mkdtempSync(join(tmpdir(), 'et-create-'));
|
|
211
|
+
try {
|
|
212
|
+
// Un-prefixed filename with explicit flow: rejected BEFORE write.
|
|
213
|
+
assert.throws(() => createExecutionTemplate({ path: join(dir, 'evil.md'), flow: 'dev' }), /path must encode the flow/);
|
|
214
|
+
assert.equal(existsSync(join(dir, 'evil.md')), false);
|
|
215
|
+
// Contradicting --flow: rejected before write.
|
|
216
|
+
assert.throws(() => createExecutionTemplate({ path: join(dir, 'dev.md'), flow: 'review-pr' }), /contradicts the path's flow/);
|
|
217
|
+
assert.equal(existsSync(join(dir, 'dev.md')), false);
|
|
218
|
+
// force on an invalid request must not destroy the existing file.
|
|
219
|
+
const keep = join(dir, 'fix-bug.md');
|
|
220
|
+
writeFileSync(keep, '# keep\n\n- [ ] precious\n');
|
|
221
|
+
assert.throws(() => createExecutionTemplate({ path: keep, flow: 'dev', force: true }), /contradicts the path's flow/);
|
|
222
|
+
assert.match(readFileSync(keep, 'utf8'), /precious/);
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
rmSync(dir, { recursive: true, force: true });
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
test('lint rejects traversal-like frontmatter ids and flow/filename contradictions', () => {
|
|
229
|
+
const bad = lintExecutionTemplateText('/virtual/dev.md', '---\nid: ../../escape\nflow: review-pr\n---\n\n# T\n\n- [ ] step\n');
|
|
230
|
+
assert.ok(bad.some((i) => /safe catalog id/.test(i.message)));
|
|
231
|
+
assert.ok(bad.some((i) => /contradicts the path's flow/.test(i.message)));
|
|
232
|
+
});
|
|
233
|
+
test('lint exempts interactive templates from the checkbox requirement', () => {
|
|
234
|
+
const interactive = lintExecutionTemplateText('/virtual/dev-interactive.md', '# Conversational template\n\nNo checklist by design.\n');
|
|
235
|
+
assert.equal(interactive.filter((i) => i.severity === 'error').length, 0);
|
|
236
|
+
const autonomous = lintExecutionTemplateText('/virtual/dev.md', '# Missing checklist\n\nprose only\n');
|
|
237
|
+
assert.ok(autonomous.some((i) => /no parseable checkbox/.test(i.message)));
|
|
238
|
+
});
|
|
239
|
+
test('lint flags unterminated frontmatter instead of silently treating it as body', () => {
|
|
240
|
+
const issues = lintExecutionTemplateText('/virtual/dev.md', '---\nrunMode: autonomous\n\n# Heading\n\n- [ ] step\n');
|
|
241
|
+
assert.ok(issues.some((i) => /unterminated frontmatter/.test(i.message)));
|
|
242
|
+
});
|
|
243
|
+
test('create with no metadata renders without a frontmatter block and round-trips through list', () => {
|
|
244
|
+
const dir = mkdtempSync(join(tmpdir(), 'et-roundtrip-'));
|
|
245
|
+
try {
|
|
246
|
+
mkdirSync(join(dir, 'worker'), { recursive: true });
|
|
247
|
+
// Bare title: no runMode token in the filename, default platforms — the
|
|
248
|
+
// generated file must still lint clean and be discoverable.
|
|
249
|
+
createExecutionTemplate({ path: join(dir, 'worker', 'fix-bug.core.md'), title: 'Round trip' });
|
|
250
|
+
const text = readFileSync(join(dir, 'worker', 'fix-bug.core.md'), 'utf8');
|
|
251
|
+
assert.ok(!text.startsWith('---'), 'no empty frontmatter fence pair');
|
|
252
|
+
const entries = listExecutionTemplates({
|
|
253
|
+
sources: [projectWorkerTemplateSource('p', dir)],
|
|
254
|
+
});
|
|
255
|
+
const entry = entries.find((e) => e.id === 'fix-bug/core');
|
|
256
|
+
assert.equal(entry?.title, 'Round trip');
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
rmSync(dir, { recursive: true, force: true });
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
test('parser closes an empty frontmatter block', () => {
|
|
263
|
+
const parsed = parseMarkdownDocument('---\n---\n\n# T\n\n- [ ] step\n');
|
|
264
|
+
assert.notEqual(parsed.frontmatter, null);
|
|
265
|
+
assert.equal(parsed.heading, 'T');
|
|
266
|
+
});
|
|
267
|
+
test('flow-tree paths create and lint via the parent flow directory', () => {
|
|
268
|
+
const dir = mkdtempSync(join(tmpdir(), 'et-flowtree-'));
|
|
269
|
+
try {
|
|
270
|
+
mkdirSync(join(dir, 'fix-bug'), { recursive: true });
|
|
271
|
+
// Basename has no flow prefix — the fix-bug/ directory carries it.
|
|
272
|
+
createExecutionTemplate({ path: join(dir, 'fix-bug', 'core.md'), title: 'Tree base' });
|
|
273
|
+
const issues = lintExecutionTemplates(join(dir, 'fix-bug', 'core.md'));
|
|
274
|
+
assert.equal(issues.ok, true);
|
|
275
|
+
const entries = listExecutionTemplates({
|
|
276
|
+
sources: [packageFlowTreeTemplateSource('s', dir)],
|
|
277
|
+
});
|
|
278
|
+
assert.equal(entries.find((e) => e.id === 'fix-bug/core')?.title, 'Tree base');
|
|
279
|
+
// A contradicting --flow still fails against the directory flow.
|
|
280
|
+
assert.throws(() => createExecutionTemplate({ path: join(dir, 'fix-bug', 'other.md'), flow: 'dev' }), /contradicts the path's flow/);
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
rmSync(dir, { recursive: true, force: true });
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
test('parent flow directory wins over a contradicting basename, and lint flags it', () => {
|
|
287
|
+
// fix-bug/dev.md must resolve as fix-bug everywhere (catalog parity)…
|
|
288
|
+
assert.equal(catalogRelativeId('fix-bug/dev.md', 'flow-tree'), 'fix-bug/dev');
|
|
289
|
+
const issues = lintExecutionTemplateText('/tmp/fix-bug/dev.md', '# T\n\n- [ ] step\n');
|
|
290
|
+
// …and the misleading name is an authoring error.
|
|
291
|
+
assert.ok(issues.some((i) => /contradicts the flow directory/.test(i.message)));
|
|
292
|
+
});
|
|
293
|
+
test('mode tokens are exact, not substrings', () => {
|
|
294
|
+
assert.equal(inferRunModeFromBasename('dev-interactively.md'), null);
|
|
295
|
+
assert.equal(inferRunModeFromBasename('dev-autonomousness.md'), null);
|
|
296
|
+
});
|
|
297
|
+
test('a line merely starting with --- is not a closing fence', () => {
|
|
298
|
+
const parsed = parseMarkdownDocument('---\nrunMode: autonomous\n---not-a-fence\n\n# T\n');
|
|
299
|
+
assert.equal(parsed.frontmatter, null);
|
|
300
|
+
const issues = lintExecutionTemplateText('/virtual/dev.md', '---\nrunMode: autonomous\n---not-a-fence\n\n# T\n\n- [ ] step\n');
|
|
301
|
+
assert.ok(issues.some((i) => /unterminated frontmatter/.test(i.message)));
|
|
302
|
+
});
|
|
303
|
+
test('directory lint flags files that canonicalize to the same catalog id', () => {
|
|
304
|
+
const dir = mkdtempSync(join(tmpdir(), 'et-dup-'));
|
|
305
|
+
try {
|
|
306
|
+
writeFileSync(join(dir, 'dev-interactive.md'), '# A\n');
|
|
307
|
+
writeFileSync(join(dir, 'dev.interactive.md'), '# B\n');
|
|
308
|
+
const result = lintExecutionTemplates(dir);
|
|
309
|
+
assert.equal(result.ok, false);
|
|
310
|
+
assert.ok(result.issues.some((i) => /duplicate catalog id 'dev\/interactive'/.test(i.message)));
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
rmSync(dir, { recursive: true, force: true });
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
test('duplicate detection follows explicit frontmatter ids like the resolver', () => {
|
|
317
|
+
const dir = mkdtempSync(join(tmpdir(), 'et-dup2-'));
|
|
318
|
+
try {
|
|
319
|
+
// Same explicit id in differently-named files: a real collision.
|
|
320
|
+
writeFileSync(join(dir, 'dev.md'), '---\nid: shared\n---\n\n# A\n\n- [ ] s\n');
|
|
321
|
+
writeFileSync(join(dir, 'fix-bug.md'), '---\nid: shared\n---\n\n# B\n\n- [ ] s\n');
|
|
322
|
+
const collision = lintExecutionTemplates(dir);
|
|
323
|
+
assert.ok(collision.issues.some((i) => /duplicate catalog id 'shared'/.test(i.message)));
|
|
324
|
+
rmSync(join(dir, 'dev.md'));
|
|
325
|
+
rmSync(join(dir, 'fix-bug.md'));
|
|
326
|
+
// Path-colliding names with DIFFERENT explicit ids: not a collision.
|
|
327
|
+
writeFileSync(join(dir, 'dev-interactive.md'), '---\nid: one\n---\n\n# A\n');
|
|
328
|
+
writeFileSync(join(dir, 'dev.interactive.md'), '---\nid: two\n---\n\n# B\n');
|
|
329
|
+
const distinct = lintExecutionTemplates(dir);
|
|
330
|
+
assert.ok(!distinct.issues.some((i) => /duplicate catalog id/.test(i.message)));
|
|
331
|
+
}
|
|
332
|
+
finally {
|
|
333
|
+
rmSync(dir, { recursive: true, force: true });
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
test('a bare CR does not terminate a frontmatter fence', () => {
|
|
337
|
+
const parsed = parseMarkdownDocument('---\nrunMode: autonomous\n---\rnot-a-fence\n\n# T\n');
|
|
338
|
+
assert.equal(parsed.frontmatter, null);
|
|
339
|
+
});
|
|
340
|
+
//# sourceMappingURL=execution-template.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-template.test.js","sourceRoot":"","sources":["../../src/execution-template/execution-template.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ExecutionRunMode, ExecutionTemplateFrontmatter } from './types.js';
|
|
2
|
+
export interface ParsedMarkdownDocument {
|
|
3
|
+
frontmatter: ExecutionTemplateFrontmatter | null;
|
|
4
|
+
body: string;
|
|
5
|
+
heading: string | null;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Minimal frontmatter parser for optional execution-template metadata.
|
|
9
|
+
* Supports `key: value` and `key: [a, b]` only — no nested YAML documents.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseMarkdownDocument(text: string): ParsedMarkdownDocument;
|
|
12
|
+
export declare function normalizeRunMode(value: unknown): ExecutionRunMode | null;
|
|
13
|
+
export declare function normalizePlatforms(value: unknown): string[] | null;
|
|
14
|
+
export declare function frontmatterRunMode(frontmatter: ExecutionTemplateFrontmatter | null): ExecutionRunMode | null;
|
|
15
|
+
export declare function frontmatterPlatforms(frontmatter: ExecutionTemplateFrontmatter | null): string[] | null;
|
|
16
|
+
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/execution-template/frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAIjF,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,4BAA4B,GAAG,IAAI,CAAC;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AA0BD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,CAgD1E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAIxE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI,CAYlE;AAED,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,4BAA4B,GAAG,IAAI,GAC/C,gBAAgB,GAAG,IAAI,CAGzB;AAED,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,4BAA4B,GAAG,IAAI,GAC/C,MAAM,EAAE,GAAG,IAAI,CAGjB"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const RUN_MODES = new Set(['autonomous', 'interactive', 'validation']);
|
|
2
|
+
function stripQuotes(value) {
|
|
3
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
4
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
5
|
+
return value.slice(1, -1);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
function parseScalar(raw) {
|
|
10
|
+
const value = stripQuotes(raw.trim());
|
|
11
|
+
if (value === 'true')
|
|
12
|
+
return true;
|
|
13
|
+
if (value === 'false')
|
|
14
|
+
return false;
|
|
15
|
+
if (/^-?\d+(\.\d+)?$/.test(value))
|
|
16
|
+
return Number(value);
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function parseInlineArray(raw) {
|
|
20
|
+
const inner = raw.trim().slice(1, -1).trim();
|
|
21
|
+
if (!inner)
|
|
22
|
+
return [];
|
|
23
|
+
return inner.split(',').map((part) => parseScalar(part.trim()));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Minimal frontmatter parser for optional execution-template metadata.
|
|
27
|
+
* Supports `key: value` and `key: [a, b]` only — no nested YAML documents.
|
|
28
|
+
*/
|
|
29
|
+
export function parseMarkdownDocument(text) {
|
|
30
|
+
const normalized = text.replace(/^\uFEFF/, '');
|
|
31
|
+
let frontmatter = null;
|
|
32
|
+
let body = normalized;
|
|
33
|
+
if (normalized.startsWith('---\n') || normalized.startsWith('---\r\n')) {
|
|
34
|
+
// The closing fence must be exactly `---` on its own line — a line that
|
|
35
|
+
// merely STARTS with --- (e.g. `---not-a-fence`) is content, and accepting
|
|
36
|
+
// it would defeat unterminated-frontmatter detection. Search from 3 (the
|
|
37
|
+
// opening fence's own newline) so an EMPTY block still closes.
|
|
38
|
+
let end = -1;
|
|
39
|
+
for (let from = 3;;) {
|
|
40
|
+
const candidate = normalized.indexOf('\n---', from);
|
|
41
|
+
if (candidate === -1)
|
|
42
|
+
break;
|
|
43
|
+
const after = normalized.slice(candidate + 4, candidate + 6);
|
|
44
|
+
// Only EOF, LF, or CRLF terminate the fence — a bare CR is not a line
|
|
45
|
+
// boundary anywhere else in this parser.
|
|
46
|
+
if (after === '' || after.startsWith('\n') || after === '\r\n' || after.startsWith('\r\n')) {
|
|
47
|
+
end = candidate;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
from = candidate + 1;
|
|
51
|
+
}
|
|
52
|
+
if (end !== -1) {
|
|
53
|
+
const fmBlock = normalized.slice(4, end).replace(/\r/g, '');
|
|
54
|
+
const rest = normalized.slice(end + '\n---'.length).replace(/^\r?\n/, '');
|
|
55
|
+
frontmatter = {};
|
|
56
|
+
for (const line of fmBlock.split('\n')) {
|
|
57
|
+
const trimmed = line.trim();
|
|
58
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
59
|
+
continue;
|
|
60
|
+
const colon = trimmed.indexOf(':');
|
|
61
|
+
if (colon <= 0)
|
|
62
|
+
continue;
|
|
63
|
+
const key = trimmed.slice(0, colon).trim();
|
|
64
|
+
const rawValue = trimmed.slice(colon + 1).trim();
|
|
65
|
+
if (!key)
|
|
66
|
+
continue;
|
|
67
|
+
const value = rawValue.startsWith('[') && rawValue.endsWith(']')
|
|
68
|
+
? parseInlineArray(rawValue)
|
|
69
|
+
: parseScalar(rawValue);
|
|
70
|
+
frontmatter[key] = value;
|
|
71
|
+
}
|
|
72
|
+
body = rest;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const headingMatch = body.match(/^#\s+(.+)$/m);
|
|
76
|
+
const heading = headingMatch?.[1]?.trim() ?? null;
|
|
77
|
+
return { frontmatter, body, heading };
|
|
78
|
+
}
|
|
79
|
+
export function normalizeRunMode(value) {
|
|
80
|
+
if (typeof value !== 'string')
|
|
81
|
+
return null;
|
|
82
|
+
const normalized = value.trim().toLowerCase();
|
|
83
|
+
return RUN_MODES.has(normalized) ? normalized : null;
|
|
84
|
+
}
|
|
85
|
+
export function normalizePlatforms(value) {
|
|
86
|
+
if (value == null)
|
|
87
|
+
return null;
|
|
88
|
+
if (typeof value === 'string') {
|
|
89
|
+
const trimmed = value.trim();
|
|
90
|
+
return trimmed ? [trimmed] : null;
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(value))
|
|
93
|
+
return null;
|
|
94
|
+
const platforms = value
|
|
95
|
+
.filter((item) => typeof item === 'string')
|
|
96
|
+
.map((item) => item.trim())
|
|
97
|
+
.filter(Boolean);
|
|
98
|
+
return platforms.length > 0 ? platforms : null;
|
|
99
|
+
}
|
|
100
|
+
export function frontmatterRunMode(frontmatter) {
|
|
101
|
+
if (!frontmatter)
|
|
102
|
+
return null;
|
|
103
|
+
return normalizeRunMode(frontmatter.runMode ?? frontmatter.run_mode);
|
|
104
|
+
}
|
|
105
|
+
export function frontmatterPlatforms(frontmatter) {
|
|
106
|
+
if (!frontmatter)
|
|
107
|
+
return null;
|
|
108
|
+
return normalizePlatforms(frontmatter.platforms);
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=frontmatter.js.map
|