@really-knows-ai/foundry 2.0.0 → 2.1.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/.opencode/plugins/foundry.js +70 -11
- package/package.json +2 -2
- package/scripts/lib/slug.js +33 -0
- package/scripts/sort.js +11 -2
- package/skills/flow/SKILL.md +2 -2
- package/skills/refresh-agents/SKILL.md +6 -3
- package/skills/sort/SKILL.md +47 -9
- package/skills/upgrade-foundry/SKILL.md +19 -5
|
@@ -17,13 +17,39 @@ import { parseFrontmatter, createWorkfile, setFrontmatterField, getFrontmatterFi
|
|
|
17
17
|
import { parseArtefactsTable, addArtefactRow, setArtefactStatus } from '../../scripts/lib/artefacts.js';
|
|
18
18
|
import { addFeedbackItem, actionFeedbackItem, wontfixFeedbackItem, resolveFeedbackItem, listFeedback } from '../../scripts/lib/feedback.js';
|
|
19
19
|
import { getCycleDefinition, getArtefactType, getLaws, getValidation, getAppraisers, getFlow, selectAppraisers } from '../../scripts/lib/config.js';
|
|
20
|
+
import { slugify } from '../../scripts/lib/slug.js';
|
|
20
21
|
import { runSort } from '../../scripts/sort.js';
|
|
21
|
-
import { execSync } from 'child_process';
|
|
22
|
+
import { execSync, execFileSync } from 'child_process';
|
|
22
23
|
|
|
23
24
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
25
|
const packageRoot = path.resolve(__dirname, '../..');
|
|
25
26
|
const allSkillsDir = path.join(packageRoot, 'skills');
|
|
26
27
|
|
|
28
|
+
function listFlows(foundryDir) {
|
|
29
|
+
const flowsDir = path.join(foundryDir, 'flows');
|
|
30
|
+
if (!fs.existsSync(flowsDir)) return [];
|
|
31
|
+
const flows = [];
|
|
32
|
+
for (const entry of readdirSync(flowsDir)) {
|
|
33
|
+
if (!entry.endsWith('.md') || entry === '.gitkeep') continue;
|
|
34
|
+
try {
|
|
35
|
+
const text = readFileSync(path.join(flowsDir, entry), 'utf-8');
|
|
36
|
+
const fmMatch = text.match(/^---\n([\s\S]*?)\n---/);
|
|
37
|
+
if (!fmMatch) continue;
|
|
38
|
+
const fm = fmMatch[1];
|
|
39
|
+
const idMatch = fm.match(/^id:\s*(.+)$/m);
|
|
40
|
+
const nameMatch = fm.match(/^name:\s*(.+)$/m);
|
|
41
|
+
const startingMatch = fm.match(/^starting-cycles:\s*\n((?:\s*-\s*.+\n?)+)/m);
|
|
42
|
+
const id = idMatch ? idMatch[1].trim() : entry.replace(/\.md$/, '');
|
|
43
|
+
const name = nameMatch ? nameMatch[1].trim() : id;
|
|
44
|
+
const startingCycles = startingMatch
|
|
45
|
+
? startingMatch[1].split('\n').map(l => l.replace(/^\s*-\s*/, '').trim()).filter(Boolean)
|
|
46
|
+
: [];
|
|
47
|
+
flows.push({ id, name, startingCycles });
|
|
48
|
+
} catch { /* skip bad files */ }
|
|
49
|
+
}
|
|
50
|
+
return flows;
|
|
51
|
+
}
|
|
52
|
+
|
|
27
53
|
function getBootstrapContent(directory) {
|
|
28
54
|
const foundryDir = path.join(directory, 'foundry');
|
|
29
55
|
const foundryExists = fs.existsSync(foundryDir) && fs.statSync(foundryDir).isDirectory();
|
|
@@ -37,6 +63,14 @@ and guide you through defining artefact types, laws, appraisers, cycles, and flo
|
|
|
37
63
|
</FOUNDRY_CONTEXT>`;
|
|
38
64
|
}
|
|
39
65
|
|
|
66
|
+
const flows = listFlows(foundryDir);
|
|
67
|
+
const flowList = flows.length > 0
|
|
68
|
+
? flows.map(f => {
|
|
69
|
+
const sc = f.startingCycles.length > 0 ? ` — starting cycles: ${f.startingCycles.join(', ')}` : '';
|
|
70
|
+
return `- \`${f.id}\` — ${f.name}${sc}`;
|
|
71
|
+
}).join('\n')
|
|
72
|
+
: '- (no flows defined yet — use the `add-flow` skill to create one)';
|
|
73
|
+
|
|
40
74
|
return `<FOUNDRY_CONTEXT>
|
|
41
75
|
Foundry is active in this project. The foundry/ directory contains the project's artefact definitions,
|
|
42
76
|
laws, appraisers, cycles, and flows.
|
|
@@ -44,15 +78,32 @@ laws, appraisers, cycles, and flows.
|
|
|
44
78
|
Foundry is a skill-driven framework for governed artefact generation and evaluation.
|
|
45
79
|
The pipeline: forge (produce) → quench (deterministic checks) → appraise (subjective evaluation) → iterate.
|
|
46
80
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
81
|
+
## Defined flows
|
|
82
|
+
|
|
83
|
+
${flowList}
|
|
84
|
+
|
|
85
|
+
**CRITICAL ROUTING RULE:** When the user references any flow above — by id (e.g. "creative-flow"),
|
|
86
|
+
by name (e.g. "Creative Flow"), or by clear paraphrase (e.g. "the creative flow", "use the creative pipeline") —
|
|
87
|
+
invoke the \`flow\` skill DIRECTLY with that flow's id. Do NOT invoke brainstorming, do NOT explore the
|
|
88
|
+
codebase, do NOT ask clarifying questions about what to build. The flow's cycles already define the
|
|
89
|
+
work. The user's request text (e.g. "make a haiku about X") is the goal to pass to the flow.
|
|
50
90
|
|
|
51
|
-
|
|
91
|
+
Brainstorming applies to NEW features being added to foundry itself (new cycles, new artefact types,
|
|
92
|
+
new skills). It does NOT apply to running an existing, defined flow.
|
|
93
|
+
|
|
94
|
+
## Available skills
|
|
95
|
+
|
|
96
|
+
- **Pipeline:** forge, quench, appraise, cycle, flow, sort, human-appraise
|
|
97
|
+
- **Authoring:** add-artefact-type, add-law, add-appraiser, add-cycle, add-flow, init-foundry
|
|
98
|
+
- **Maintenance:** upgrade-foundry, refresh-agents, list-agents
|
|
99
|
+
|
|
100
|
+
## Multi-model routing
|
|
101
|
+
|
|
102
|
+
Foundry uses \`foundry-*\` sub-agents defined as markdown files in \`.opencode/agents/\`.
|
|
52
103
|
Run the \`refresh-agents\` skill to regenerate them after adding or removing providers.
|
|
53
104
|
Cycle definitions can specify per-stage models via the \`models\` frontmatter map. Appraisers can override with their own \`model\` field.
|
|
54
105
|
|
|
55
|
-
|
|
106
|
+
All user content lives under foundry/.
|
|
56
107
|
Scripts are located at: ${path.join(packageRoot, 'scripts')}
|
|
57
108
|
</FOUNDRY_CONTEXT>`;
|
|
58
109
|
}
|
|
@@ -129,8 +180,8 @@ export const FoundryPlugin = async ({ directory }) => {
|
|
|
129
180
|
args: {
|
|
130
181
|
flow: tool.schema.string().describe('Flow name'),
|
|
131
182
|
cycle: tool.schema.string().describe('Cycle name'),
|
|
132
|
-
stages: tool.schema.array(tool.schema.string()).describe('Ordered stage names'),
|
|
133
|
-
maxIterations: tool.schema.number().describe('Maximum iterations'),
|
|
183
|
+
stages: tool.schema.array(tool.schema.string()).optional().describe('Ordered stage names'),
|
|
184
|
+
maxIterations: tool.schema.number().optional().describe('Maximum iterations'),
|
|
134
185
|
goal: tool.schema.string().describe('Goal text'),
|
|
135
186
|
models: tool.schema.string().optional().describe('Per-stage model overrides as JSON object, e.g. \'{"forge":"openai/gpt-4o"}\''),
|
|
136
187
|
},
|
|
@@ -139,7 +190,13 @@ export const FoundryPlugin = async ({ directory }) => {
|
|
|
139
190
|
if (existsSync(workPath)) {
|
|
140
191
|
return JSON.stringify({ error: 'WORK.md already exists' });
|
|
141
192
|
}
|
|
142
|
-
const fm = { flow: args.flow, cycle: args.cycle
|
|
193
|
+
const fm = { flow: args.flow, cycle: args.cycle };
|
|
194
|
+
if (args.stages) {
|
|
195
|
+
fm.stages = enrichStages(args.stages, args.cycle);
|
|
196
|
+
}
|
|
197
|
+
if (args.maxIterations !== undefined) {
|
|
198
|
+
fm.maxIterations = args.maxIterations;
|
|
199
|
+
}
|
|
143
200
|
if (args.models) {
|
|
144
201
|
fm.models = parseModelsValue(args.models);
|
|
145
202
|
}
|
|
@@ -374,8 +431,10 @@ export const FoundryPlugin = async ({ directory }) => {
|
|
|
374
431
|
description: tool.schema.string().describe('Branch description suffix'),
|
|
375
432
|
},
|
|
376
433
|
async execute(args, context) {
|
|
377
|
-
const
|
|
378
|
-
|
|
434
|
+
const flowSlug = slugify(args.flowId);
|
|
435
|
+
const descSlug = slugify(args.description);
|
|
436
|
+
const branch = `work/${flowSlug}-${descSlug}`;
|
|
437
|
+
execFileSync('git', ['checkout', '-b', branch], { cwd: context.worktree, encoding: 'utf8', stdio: 'pipe' });
|
|
379
438
|
return JSON.stringify({ ok: true, branch });
|
|
380
439
|
},
|
|
381
440
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@really-knows-ai/foundry",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A structured framework for AI-driven artefact creation with deterministic routing, quality gates, and iterative refinement cycles.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": ".opencode/plugins/foundry.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"node": ">=18.3.0"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
|
-
"test": "node --test
|
|
28
|
+
"test": "node --test"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@opencode-ai/plugin": "^1.4.0",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slug utilities for generating shell-safe, git-ref-safe identifiers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert an arbitrary string into a URL/git-branch-friendly slug.
|
|
7
|
+
*
|
|
8
|
+
* Rules:
|
|
9
|
+
* - Strips diacritics (e.g. "café" → "cafe")
|
|
10
|
+
* - Lowercases
|
|
11
|
+
* - Replaces any run of non-[a-z0-9] characters with a single dash
|
|
12
|
+
* - Trims leading/trailing dashes
|
|
13
|
+
*
|
|
14
|
+
* Throws if the input is not a string or if the resulting slug is empty.
|
|
15
|
+
*/
|
|
16
|
+
export function slugify(input) {
|
|
17
|
+
if (typeof input !== 'string') {
|
|
18
|
+
throw new TypeError(`slugify: expected string, got ${typeof input}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const slug = input
|
|
22
|
+
.normalize('NFD')
|
|
23
|
+
.replace(/\p{Diacritic}/gu, '')
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
26
|
+
.replace(/^-+|-+$/g, '');
|
|
27
|
+
|
|
28
|
+
if (slug.length === 0) {
|
|
29
|
+
throw new Error(`slugify: input produced empty slug (input: ${JSON.stringify(input)})`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return slug;
|
|
33
|
+
}
|
package/scripts/sort.js
CHANGED
|
@@ -209,7 +209,7 @@ function checkModifiedFiles(lastBase, foundryDir, cycleDef, cycle, io = defaultI
|
|
|
209
209
|
// Exported runSort — structured result for programmatic use
|
|
210
210
|
// ---------------------------------------------------------------------------
|
|
211
211
|
|
|
212
|
-
export function runSort({ workPath = 'WORK.md', historyPath = 'WORK.history.yaml', foundryDir = 'foundry', cycleDef } = {}, io = defaultIO) {
|
|
212
|
+
export function runSort({ workPath = 'WORK.md', historyPath = 'WORK.history.yaml', foundryDir = 'foundry', cycleDef, agentsDir = '.opencode/agents' } = {}, io = defaultIO) {
|
|
213
213
|
if (!io.exists(workPath)) {
|
|
214
214
|
return { route: 'blocked', details: 'WORK.md not found' };
|
|
215
215
|
}
|
|
@@ -255,7 +255,16 @@ export function runSort({ workPath = 'WORK.md', historyPath = 'WORK.history.yaml
|
|
|
255
255
|
const routeBase = baseStage(route);
|
|
256
256
|
if (frontmatter.models && frontmatter.models[routeBase]) {
|
|
257
257
|
const modelId = frontmatter.models[routeBase];
|
|
258
|
-
model = `foundry-${modelId.replace(
|
|
258
|
+
model = `foundry-${modelId.replace(/[/.]/g, '-')}`;
|
|
259
|
+
|
|
260
|
+
// Fail-fast: required subagent file must exist
|
|
261
|
+
const agentPath = `${agentsDir}/${model}.md`;
|
|
262
|
+
if (!io.exists(agentPath)) {
|
|
263
|
+
return {
|
|
264
|
+
route: 'violation',
|
|
265
|
+
details: `Missing required subagent: ${model}.md is not present in ${agentsDir}/. Run the refresh-agents skill to regenerate agent files, then restart.`,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
259
268
|
}
|
|
260
269
|
|
|
261
270
|
return { route, ...(model ? { model } : {}) };
|
package/skills/flow/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: flow
|
|
3
3
|
type: composite
|
|
4
|
-
description:
|
|
4
|
+
description: Runs a defined foundry flow to produce artefacts. Use this whenever the user references a flow by id, name, or paraphrase (e.g. "use the creative flow", "run creative-flow"). Do not brainstorm — the flow's cycles already define the work. The user's request is the goal to pass in.
|
|
5
5
|
composes: [cycle]
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -23,7 +23,7 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
23
23
|
- If only one starting cycle, use it
|
|
24
24
|
- If multiple starting cycles, check whether the user's request makes the choice obvious (e.g., "write a haiku" clearly maps to `create-haiku`)
|
|
25
25
|
- If ambiguous, prompt the user to choose
|
|
26
|
-
4. Call `foundry_workfile_create` with the flow ID, chosen cycle ID, and goal
|
|
26
|
+
4. Call `foundry_workfile_create` with **only** the flow ID, chosen cycle ID, and goal — do **not** pass `stages` or `maxIterations`. The `cycle` skill will read the cycle definition and populate those via `foundry_workfile_set` in the next step.
|
|
27
27
|
5. Execute the cycle by invoking the cycle skill
|
|
28
28
|
|
|
29
29
|
## Between cycles
|
|
@@ -16,11 +16,14 @@ Regenerate `.opencode/agents/foundry-*.md` files from the currently available mo
|
|
|
16
16
|
|
|
17
17
|
### Agent file format
|
|
18
18
|
|
|
19
|
-
Filename: `.opencode/agents/foundry-<
|
|
19
|
+
Filename: `.opencode/agents/foundry-<slug>.md`
|
|
20
20
|
|
|
21
|
-
Where `<
|
|
21
|
+
Where `<slug>` is the model ID with **both** `/` and `.` replaced by `-`. This keeps filenames shell-safe and unambiguous.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Examples:
|
|
24
|
+
- `opencode/claude-sonnet-4` → `.opencode/agents/foundry-opencode-claude-sonnet-4.md`
|
|
25
|
+
- `github-copilot/claude-sonnet-4.6` → `.opencode/agents/foundry-github-copilot-claude-sonnet-4-6.md`
|
|
26
|
+
- `github-copilot/gpt-5.4` → `.opencode/agents/foundry-github-copilot-gpt-5-4.md`
|
|
24
27
|
|
|
25
28
|
Content:
|
|
26
29
|
|
package/skills/sort/SKILL.md
CHANGED
|
@@ -6,7 +6,7 @@ description: Deterministic routing for a foundry cycle. Runs the foundry_sort to
|
|
|
6
6
|
|
|
7
7
|
# Sort
|
|
8
8
|
|
|
9
|
-
You are the central dispatcher for a foundry cycle. You call the `foundry_sort` tool to determine what stage to execute next, then
|
|
9
|
+
You are the central dispatcher for a foundry cycle. You call the `foundry_sort` tool to determine what stage to execute next, then dispatch that stage to a fresh subagent.
|
|
10
10
|
|
|
11
11
|
## Prerequisites
|
|
12
12
|
|
|
@@ -21,21 +21,59 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
|
|
|
21
21
|
2. Call `foundry_history_append` with the current cycle, stage `"sort"`, and a comment explaining the routing decision in natural language. This is your audit trail — if something goes wrong, this comment is what someone will read to understand what happened.
|
|
22
22
|
|
|
23
23
|
3. Act on the route:
|
|
24
|
-
- `forge:*` — dispatch
|
|
25
|
-
- `quench:*` — dispatch
|
|
26
|
-
- `appraise:*` — dispatch
|
|
27
|
-
- `human-appraise:*` — invoke the human-appraise skill (
|
|
24
|
+
- `forge:*` — **dispatch** (see §Dispatch below)
|
|
25
|
+
- `quench:*` — **dispatch**
|
|
26
|
+
- `appraise:*` — **dispatch**. Note: the appraise skill handles its own per-appraiser model resolution internally.
|
|
27
|
+
- `human-appraise:*` — invoke the human-appraise skill inline (human stage, no subagent)
|
|
28
28
|
- `done` — foundry cycle is complete, return to the cycle skill
|
|
29
29
|
- `blocked` — foundry cycle is blocked (iteration limit hit with unresolved feedback), return to the cycle skill
|
|
30
|
-
- `violation` — file
|
|
30
|
+
- `violation` — a validation, file-modification, or missing-subagent violation was detected (see `details`). The cycle halts — call `foundry_artefacts_set_status` with status `"blocked"` for each affected artefact, and return to the cycle skill. If `details` mentions a missing subagent, tell the user to run the `refresh-agents` skill and restart.
|
|
31
31
|
|
|
32
32
|
4. After the subagent completes, call `foundry_history_append` with the current cycle, the **dispatched stage alias** (e.g., `forge:write-haiku`), and a comment summarizing what the subagent reported doing. This is critical — sort is the only reliable writer of stage history. Subagents must NOT write their own history entries.
|
|
33
33
|
|
|
34
34
|
5. After logging the stage history, call `foundry_sort` again. Repeat from step 1 until it returns `done`, `blocked`, or `violation`.
|
|
35
35
|
|
|
36
|
+
## Dispatch
|
|
37
|
+
|
|
38
|
+
Every forge, quench, and appraise stage runs in a **fresh subagent**. Never inline the stage work in the orchestrator conversation — even if the chosen model happens to match the orchestrator's model. The orchestrator's job is to route and log, nothing else.
|
|
39
|
+
|
|
40
|
+
### Choosing the subagent
|
|
41
|
+
|
|
42
|
+
- If `foundry_sort` returned a `model` field in its response, use that value verbatim as `subagent_type`. It is already in `foundry-<slug>` form (the tool does the slug computation by replacing both `/` and `.` with `-` in the model ID).
|
|
43
|
+
- If `foundry_sort` returned **no** `model` field (the cycle has no `models:` map, or no entry for this stage base), dispatch to the default general-purpose subagent: `general`.
|
|
44
|
+
|
|
45
|
+
### Dispatch call shape
|
|
46
|
+
|
|
47
|
+
Use the `task` tool:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
task tool:
|
|
51
|
+
subagent_type: <model-slug-from-foundry_sort-response, or "general">
|
|
52
|
+
description: "Run <stage-alias> for <cycle-id>"
|
|
53
|
+
prompt: |
|
|
54
|
+
You are a Foundry stage agent. Invoke the <stage-base> skill and follow its instructions exactly.
|
|
55
|
+
|
|
56
|
+
Current cycle: <cycle-id>
|
|
57
|
+
Current stage: <stage-alias>
|
|
58
|
+
Working directory: <worktree>
|
|
59
|
+
|
|
60
|
+
When done, report back a brief summary of what you did. Do NOT call foundry_history_append — the orchestrator handles history.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Substitute:
|
|
64
|
+
- `<stage-alias>` — the full route string from `foundry_sort` (e.g., `forge:write-haiku`)
|
|
65
|
+
- `<stage-base>` — the base of the alias (e.g., `forge`, `quench`, `appraise`)
|
|
66
|
+
- `<cycle-id>` — the current cycle ID from WORK.md frontmatter
|
|
67
|
+
- `<worktree>` — the current working directory
|
|
68
|
+
|
|
69
|
+
### Missing subagent (fail-fast)
|
|
70
|
+
|
|
71
|
+
The `foundry_sort` tool verifies that the required `.opencode/agents/foundry-<slug>.md` file exists before returning a `model`. If it doesn't, sort returns `{route: 'violation', details: 'Missing required subagent: ...'}`. Handle this as described in step 3 above — halt the cycle, mark artefacts blocked, and instruct the user to run the `refresh-agents` skill.
|
|
72
|
+
|
|
36
73
|
## What you do NOT do
|
|
37
74
|
|
|
38
|
-
- You do not make routing decisions yourself — the tool decides
|
|
39
|
-
- You do not skip calling `foundry_sort
|
|
40
|
-
- You do not override the tool's output
|
|
75
|
+
- You do not make routing decisions yourself — the tool decides.
|
|
76
|
+
- You do not skip calling `foundry_sort`.
|
|
77
|
+
- You do not override the tool's output.
|
|
41
78
|
- You do not skip the history entry — every sort invocation gets a `sort` entry, and every completed stage gets a stage entry (e.g., `forge:write-haiku`). You are the sole writer of history.
|
|
79
|
+
- You do **not** inline forge/quench/appraise work — always dispatch to a subagent via the `task` tool, even when the resolved model matches the orchestrator's own model.
|
|
@@ -27,12 +27,17 @@ Read all configuration files:
|
|
|
27
27
|
- `foundry/laws/*.md` — global laws
|
|
28
28
|
- `foundry/appraisers/*.md` — appraiser definitions
|
|
29
29
|
|
|
30
|
+
Also scan `.opencode/agents/foundry-*.md` for agent-filename migration (see §2).
|
|
31
|
+
|
|
30
32
|
For each file, parse the frontmatter and body content.
|
|
31
33
|
|
|
32
34
|
### 2. Detect what needs migration
|
|
33
35
|
|
|
34
36
|
Check each file against the current expected format:
|
|
35
37
|
|
|
38
|
+
**Agent files (v2.1 migration):**
|
|
39
|
+
- Any `.opencode/agents/foundry-*.md` filename containing a `.` character? → needs renaming to all-dashes format. The v2.1 naming convention replaces both `/` and `.` in the model ID with `-`. For example, `foundry-github-copilot-claude-sonnet-4.6.md` must become `foundry-github-copilot-claude-sonnet-4-6.md`. The inner `model:` frontmatter field is **not** changed — only the filename.
|
|
40
|
+
|
|
36
41
|
**Flows:**
|
|
37
42
|
- Has `starting-cycles` field? If not → needs DAG migration
|
|
38
43
|
- Has ordered numbered list under `## Cycles`? → needs conversion to unordered list
|
|
@@ -84,7 +89,16 @@ Present a grouped summary of all issues found:
|
|
|
84
89
|
|
|
85
90
|
If nothing needs migration, say so and stop.
|
|
86
91
|
|
|
87
|
-
### 4. Migrate
|
|
92
|
+
### 4. Migrate agent files (v2.1)
|
|
93
|
+
|
|
94
|
+
For each `.opencode/agents/foundry-*.md` file with a `.` in its filename:
|
|
95
|
+
- Compute the new filename by replacing all `.` with `-` (keep the `.md` extension)
|
|
96
|
+
- `git mv <old> <new>` to preserve history
|
|
97
|
+
- Do **not** modify the file contents — the `model:` field inside retains its original dots
|
|
98
|
+
|
|
99
|
+
After renaming, remind the user: **Restart OpenCode** for the new agent filenames to register.
|
|
100
|
+
|
|
101
|
+
### 5. Migrate flows
|
|
88
102
|
|
|
89
103
|
For each flow needing migration:
|
|
90
104
|
- Show the current ordered cycle list
|
|
@@ -93,7 +107,7 @@ For each flow needing migration:
|
|
|
93
107
|
- Present the proposed `starting-cycles` and confirm
|
|
94
108
|
- Convert numbered `## Cycles` list to unordered
|
|
95
109
|
|
|
96
|
-
###
|
|
110
|
+
### 6. Migrate cycles
|
|
97
111
|
|
|
98
112
|
For each cycle needing migration:
|
|
99
113
|
|
|
@@ -112,20 +126,20 @@ For each cycle needing migration:
|
|
|
112
126
|
|
|
113
127
|
Remove `hitl` from stages and add `human-appraise` config if enabled.
|
|
114
128
|
|
|
115
|
-
###
|
|
129
|
+
### 7. Migrate other config
|
|
116
130
|
|
|
117
131
|
For artefact types, appraisers, laws, and validation with issues:
|
|
118
132
|
- Present each issue with a suggested fix
|
|
119
133
|
- Ask the user to confirm or adjust
|
|
120
134
|
|
|
121
|
-
###
|
|
135
|
+
### 8. Present migration plan
|
|
122
136
|
|
|
123
137
|
Before writing anything, show the complete list of changes:
|
|
124
138
|
- Group by category
|
|
125
139
|
- Show each file and the specific changes
|
|
126
140
|
- Ask for confirmation
|
|
127
141
|
|
|
128
|
-
###
|
|
142
|
+
### 9. Apply changes
|
|
129
143
|
|
|
130
144
|
- Update all affected files
|
|
131
145
|
- Commit with message: `[foundry] upgrade: migrate to current format`
|