@really-knows-ai/foundry 1.1.0 → 1.2.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.
@@ -2,8 +2,9 @@
2
2
  * Foundry plugin for OpenCode.ai
3
3
  *
4
4
  * All skills are always registered. Individual skills check for foundry/ dir.
5
- * - If foundry/ exists: pipeline context + multi-model agent registration
5
+ * - If foundry/ exists: pipeline context injected into first message
6
6
  * - If foundry/ does not exist: minimal prompt guiding user to init-foundry
7
+ * Multi-model agents are managed as .opencode/agents/foundry-*.md files via the refresh-agents skill.
7
8
  */
8
9
 
9
10
  import path from 'path';
@@ -13,7 +14,6 @@ import { fileURLToPath } from 'url';
13
14
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
15
  const packageRoot = path.resolve(__dirname, '../..');
15
16
  const allSkillsDir = path.join(packageRoot, 'skills');
16
- const initSkillDir = path.join(allSkillsDir, 'init-foundry');
17
17
 
18
18
  function getBootstrapContent(directory) {
19
19
  const foundryDir = path.join(directory, 'foundry');
@@ -39,7 +39,8 @@ Available skills:
39
39
  - **Pipeline:** forge, quench, appraise, cycle, flow, sort, hitl
40
40
  - **Helpers:** add-artefact-type, add-law, add-appraiser, add-cycle, add-flow, init-foundry
41
41
 
42
- Multi-model routing: The Foundry plugin has auto-registered \`foundry-*\` sub-agents for each available model.
42
+ Multi-model routing: Foundry uses \`foundry-*\` sub-agents defined as markdown files in \`.opencode/agents/\`.
43
+ Run the \`refresh-agents\` skill to regenerate them after adding or removing providers.
43
44
  Cycle definitions can specify per-stage models via the \`models\` frontmatter map. Appraisers can override with their own \`model\` field.
44
45
 
45
46
  To start a flow, use the \`flow\` skill. All user content lives under foundry/.
@@ -47,10 +48,7 @@ Scripts are located at: ${path.join(packageRoot, 'scripts')}
47
48
  </FOUNDRY_CONTEXT>`;
48
49
  }
49
50
 
50
- export const FoundryPlugin = async ({ client, directory }) => {
51
- const foundryDir = path.join(directory, 'foundry');
52
- const foundryExists = fs.existsSync(foundryDir) && fs.statSync(foundryDir).isDirectory();
53
-
51
+ export const FoundryPlugin = async ({ directory }) => {
54
52
  return {
55
53
  config: async (config) => {
56
54
  config.skills = config.skills || {};
@@ -60,31 +58,6 @@ export const FoundryPlugin = async ({ client, directory }) => {
60
58
  if (!config.skills.paths.includes(allSkillsDir)) {
61
59
  config.skills.paths.push(allSkillsDir);
62
60
  }
63
-
64
- if (foundryExists) {
65
- // Register per-model subagents for multi-model stage routing
66
- try {
67
- const providers = await client.provider.list();
68
- config.agent = config.agent || {};
69
- for (const provider of providers) {
70
- if (!provider.models) continue;
71
- const modelKeys = Array.isArray(provider.models)
72
- ? provider.models
73
- : Object.keys(provider.models);
74
- for (const modelKey of modelKeys) {
75
- const agentName = `foundry-${provider.id}-${modelKey}`;
76
- config.agent[agentName] = {
77
- model: `${provider.id}/${modelKey}`,
78
- mode: 'subagent',
79
- hidden: true,
80
- description: `Foundry stage agent using ${provider.id}/${modelKey}`,
81
- };
82
- }
83
- }
84
- } catch (err) {
85
- console.warn('[foundry] Failed to discover models for agent registration:', err.message);
86
- }
87
- }
88
61
  },
89
62
 
90
63
  'experimental.chat.messages.transform': async (_input, output) => {
package/README.md CHANGED
@@ -4,9 +4,9 @@ A skill-driven framework for governed artefact generation and evaluation using A
4
4
 
5
5
  ## Compatibility
6
6
 
7
- - **OpenCode** — full support, multi-model routing via plugin-registered agents
7
+ - **OpenCode** — full support, multi-model routing via file-based agents
8
8
 
9
- Multi-model support enables model diversity across pipeline stages. The Foundry plugin auto-discovers available models at startup and registers them as hidden sub-agents. Cycle definitions specify which model each stage uses. Tools limited to a single model lose model-diversity but still get personality-based diversity.
9
+ Multi-model support enables model diversity across pipeline stages. Foundry agents are defined as `.opencode/agents/foundry-*.md` files, generated by the `refresh-agents` skill (also run during `init-foundry`). Cycle definitions specify which model each stage uses. Tools limited to a single model lose model-diversity but still get personality-based diversity.
10
10
 
11
11
  ## Installation
12
12
 
@@ -235,7 +235,7 @@ The generator can decline subjective feedback with a justification, but an appra
235
235
 
236
236
  ### Multi-model stage routing
237
237
 
238
- Cycle definitions specify which model each stage uses via a `models` map. The Foundry plugin auto-discovers available models and registers them as `foundry-*` sub-agents. Individual appraisers can override the cycle-level model. Resolution order: appraiser `model` → cycle `models.<stage>` → session default. Multiple personalities catch different issues. Consolidation is union with dedup — one appraiser flagging an issue is enough.
238
+ Cycle definitions specify which model each stage uses via a `models` map. The `refresh-agents` skill generates `foundry-*` agent files in `.opencode/agents/` from available models. Individual appraisers can override the cycle-level model. Resolution order: appraiser `model` → cycle `models.<stage>` → session default. Multiple personalities catch different issues. Consolidation is union with dedup — one appraiser flagging an issue is enough.
239
239
 
240
240
  ### Input artefacts are read-only
241
241
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@really-knows-ai/foundry",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
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",
package/scripts/sort.js CHANGED
@@ -76,24 +76,32 @@ function parseFeedback(text, cycle, artefacts) {
76
76
  const items = [];
77
77
  let currentFile = null;
78
78
  let inFeedback = false;
79
+ let feedbackLevel = 0; // 1 for '# Feedback', 2 for '## Feedback'
79
80
 
80
81
  for (const line of text.split('\n')) {
81
82
  const stripped = line.trim();
82
83
 
83
- if (stripped === '# Feedback') {
84
+ if (stripped === '# Feedback' || stripped === '## Feedback') {
84
85
  inFeedback = true;
86
+ feedbackLevel = stripped.startsWith('## ') ? 2 : 1;
85
87
  continue;
86
88
  }
87
89
 
88
- if (inFeedback && stripped.startsWith('# ') && stripped !== '# Feedback') {
89
- inFeedback = false;
90
- continue;
90
+ // Exit feedback on a heading at the same or higher level
91
+ if (inFeedback && /^#{1,2} /.test(stripped)) {
92
+ const level = stripped.startsWith('## ') ? 2 : 1;
93
+ if (level <= feedbackLevel && stripped !== '# Feedback' && stripped !== '## Feedback') {
94
+ inFeedback = false;
95
+ continue;
96
+ }
91
97
  }
92
98
 
93
99
  if (!inFeedback) continue;
94
100
 
95
- if (stripped.startsWith('## ')) {
96
- currentFile = stripped.slice(3).trim();
101
+ // File sub-headings are one level below the Feedback heading
102
+ const fileHeadingPrefix = feedbackLevel === 1 ? '## ' : '### ';
103
+ if (stripped.startsWith(fileHeadingPrefix)) {
104
+ currentFile = stripped.slice(fileHeadingPrefix.length).trim();
97
105
  continue;
98
106
  }
99
107
 
@@ -37,7 +37,7 @@ If any of these are missing, ask.
37
37
 
38
38
  For each stage in the cycle (forge, quench, appraise), ask the user if they want to specify a model:
39
39
 
40
- > Each stage can optionally run on a specific model for model diversity. Available models are registered as `foundry-*` agents by the Foundry plugin.
40
+ > Each stage can optionally run on a specific model for model diversity. Available models are listed as `foundry-*` agent files in `.opencode/agents/`. Run the `list-agents` skill to see them.
41
41
  >
42
42
  > For each stage, specify a model ID (e.g., `openai/gpt-4o`) or leave blank to use the session's default model:
43
43
  > - forge: ___
@@ -54,8 +54,18 @@ Model diversity is configured at two levels: the cycle definition sets a default
54
54
  - Union of all issues — if any one appraiser flags it, it's feedback
55
55
  - De-duplicate: merge overlapping observations into a single feedback item
56
56
  - Preserve which appraiser(s) raised each issue (for traceability)
57
- 9. Write consolidated feedback to WORK.md:
58
- - `- [ ] <description of the issue> #law:<law-id>`
57
+ 9. Write consolidated feedback to WORK.md under the artefact's file heading:
58
+
59
+ Feedback MUST be scoped to the artefact file. Under `## Feedback`, create a `### <file-path>` sub-heading matching the artefact's File column from the artefacts table, then write feedback items beneath it:
60
+
61
+ ```markdown
62
+ ## Feedback
63
+
64
+ ### foundry/output/haiku/pissed-off-spaghetti.md
65
+ - [ ] The imagery lacks originality #law:vivid-imagery
66
+ ```
67
+
68
+ If the `## Feedback` section or the file sub-heading already exists (e.g., quench already wrote validation feedback there), append items under the existing heading. Never write feedback items without a file sub-heading — the sort script cannot parse them.
59
69
  10. If no appraiser found any issues, the artefact clears appraisal
60
70
 
61
71
  ## Dispatch
@@ -120,7 +130,7 @@ If there are no issues, return an empty list.
120
130
 
121
131
  ## Reviewing actioned and wont-fix feedback
122
132
 
123
- On subsequent passes, appraisers also evaluate previously actioned and wont-fix items:
133
+ On subsequent passes, appraisers also evaluate previously actioned and wont-fix items under the artefact's `### <file-path>` heading:
124
134
 
125
135
  - `[x]` actioned items: appraiser checks whether the change actually addresses the issue
126
136
  - If yes: mark `| approved`
@@ -146,3 +156,4 @@ After completing the appraisal consolidation, append an entry to `WORK.history.y
146
156
  - You do not revise the artefact
147
157
  - You do not check deterministic rules — that is the quench skill's job
148
158
  - You do not filter out feedback because only one appraiser raised it — one is enough
159
+ - You do not write feedback items without a file sub-heading under `## Feedback`
@@ -22,7 +22,7 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
22
22
  3. Create `WORK.md` in the root following the spec in `docs/work-spec.md`:
23
23
  - Set frontmatter: flow id, first cycle id, first stage
24
24
  - Write the goal (from flow definition + human context)
25
- - Empty artefacts table
25
+ - Empty artefacts table with all four columns: `| File | Type | Cycle | Status |`
26
26
  - Empty feedback section
27
27
  4. Execute each foundry cycle in order by reading its definition from `foundry/cycles/<cycle-id>.md`
28
28
  5. Update the frontmatter cursor as each foundry cycle starts (set `cycle` to the new cycle id)
@@ -26,11 +26,11 @@ Before running this skill, verify that the `foundry/` directory exists in the pr
26
26
  6. If the foundry cycle has inputs, read the input artefacts (read-only context)
27
27
  7. Produce the artefact, respecting all applicable laws from the start
28
28
  8. Write the artefact to the location specified in the artefact type definition
29
- 9. Register the artefact in the WORK.md artefacts table (file, type, cycle, status: draft)
29
+ 9. Register the artefact in the WORK.md artefacts table. The table MUST have all four columns — File, Type, Cycle, Status. Set cycle to the current cycle id from WORK.md frontmatter. Set status to `draft`.
30
30
 
31
31
  ### Revision (feedback exists in WORK.md)
32
32
 
33
- 1. Read `WORK.md` — find unresolved feedback items for the artefact
33
+ 1. Read `WORK.md` — find unresolved feedback items for the artefact. Feedback is scoped by file: look under `## Feedback` for the `### <file-path>` sub-heading that matches the artefact's File column in the artefacts table. Only items under that heading belong to this artefact.
34
34
  2. Read the artefact
35
35
  3. If the foundry cycle has inputs, read the input artefacts (read-only context)
36
36
  4. For each unresolved feedback item, either:
@@ -29,18 +29,24 @@ Set up the `foundry/` directory structure in the current project.
29
29
  appraisers/.gitkeep
30
30
  ```
31
31
 
32
- 3. **Commit the structure**
32
+ 3. **Generate foundry agent files**
33
+
34
+ Run the `refresh-agents` skill to generate `.opencode/agents/foundry-*.md` files for multi-model routing.
35
+
36
+ 4. **Commit the structure**
33
37
 
34
38
  ```bash
35
- git add foundry/
39
+ git add foundry/ .opencode/agents/foundry-*.md
36
40
  git commit -m "feat: initialize Foundry project structure"
37
41
  ```
38
42
 
39
- 4. **Guide next steps**
43
+ 5. **Guide next steps**
40
44
 
41
45
  Tell the user:
42
46
 
43
- > Foundry is initialized. Here's how to set up your first pipeline:
47
+ > Foundry is initialized. **Restart OpenCode** for the new foundry agents to take effect.
48
+ >
49
+ > Here's how to set up your first pipeline:
44
50
  >
45
51
  > 1. **Define an artefact type** — use the `add-artefact-type` skill
46
52
  > 2. **Add laws** — use the `add-law` skill to define quality criteria
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: list-agents
3
+ description: Use when you need to see which foundry-* sub-agents are available for multi-model routing.
4
+ ---
5
+
6
+ # List Agents
7
+
8
+ Output all available `foundry-*` sub-agents.
9
+
10
+ ## Protocol
11
+
12
+ 1. List all files matching `.opencode/agents/foundry-*.md`
13
+ 2. For each file, read the `model` field from its YAML frontmatter
14
+ 3. Output each agent name and its model, one per line
15
+
16
+ ## Output format
17
+
18
+ ```
19
+ foundry-<provider>-<model> → <provider>/<model>
20
+ ```
21
+
22
+ If no `foundry-*.md` files are found, output:
23
+
24
+ > No foundry agent files found. Run the `refresh-agents` skill to generate them.
@@ -26,10 +26,23 @@ This skill only runs if `foundry/artefacts/<type>/validation.md` exists. If ther
26
26
  4. For each validation entry:
27
27
  - Substitute `{file}` in the command with the artefact path
28
28
  - Run the command
29
- - If exit code is non-zero: add feedback to WORK.md:
30
- - `- [ ] <failure description from validation.md> #validation`
29
+ - If exit code is non-zero: add feedback to WORK.md under the artefact's file heading
31
30
  5. If all commands exit zero, add no new feedback
32
31
 
32
+ ## Feedback format
33
+
34
+ Feedback MUST be scoped to the artefact file it applies to. Under `## Feedback`, create a `### <file-path>` sub-heading matching the artefact's File column from the artefacts table, then write feedback items beneath it:
35
+
36
+ ```markdown
37
+ ## Feedback
38
+
39
+ ### foundry/output/haiku/pissed-off-spaghetti.md
40
+ - [ ] The haiku does not have exactly 3 lines. #validation
41
+ - [ ] One or more lines do not match the 5-7-5 syllable pattern. #validation
42
+ ```
43
+
44
+ If the `## Feedback` section or the file sub-heading already exists, append items under the existing heading. Never write feedback items without a file sub-heading — the sort script cannot parse them.
45
+
33
46
  ## Reviewing actioned feedback
34
47
 
35
48
  On subsequent passes, the quench skill re-runs the relevant command for previously actioned items:
@@ -59,3 +72,4 @@ After completing validation (whether issues were found or not), append an entry
59
72
  - You do not evaluate laws — that is the appraise skill's job
60
73
  - You do not invent validation rules — you only run commands from the validation file
61
74
  - You do not duplicate feedback that already exists in WORK.md
75
+ - You do not write feedback items without a file sub-heading under `## Feedback`
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: refresh-agents
3
+ description: Use when initializing Foundry or after adding/removing providers to regenerate foundry-* agent files for multi-model routing.
4
+ ---
5
+
6
+ # Refresh Agents
7
+
8
+ Regenerate `.opencode/agents/foundry-*.md` files from the currently available models.
9
+
10
+ ## Protocol
11
+
12
+ 1. Run `opencode models` to get all available `provider/model` IDs
13
+ 2. Create `.opencode/agents/` directory if it does not exist
14
+ 3. Delete all existing `.opencode/agents/foundry-*.md` files (stale agents from removed providers)
15
+ 4. For each model line in the output, generate a markdown agent file
16
+
17
+ ### Agent file format
18
+
19
+ Filename: `.opencode/agents/foundry-<provider>-<model-key>.md`
20
+
21
+ Where `<provider>-<model-key>` is the model ID with `/` replaced by `-`.
22
+
23
+ Example: model `opencode/claude-sonnet-4` produces `.opencode/agents/foundry-opencode-claude-sonnet-4.md`
24
+
25
+ Content:
26
+
27
+ ```markdown
28
+ ---
29
+ description: "Foundry stage agent using <provider>/<model-key>"
30
+ mode: subagent
31
+ model: "<provider>/<model-key>"
32
+ hidden: true
33
+ ---
34
+ You are a Foundry stage agent. Follow the skill instructions provided in your task prompt exactly.
35
+ ```
36
+
37
+ 5. After writing all files, output:
38
+
39
+ > Generated `<count>` foundry agent files in `.opencode/agents/`.
40
+ > **Restart OpenCode** for the new agents to take effect.