@really-knows-ai/foundry 3.0.2 → 3.2.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.
Files changed (42) hide show
  1. package/README.md +12 -15
  2. package/dist/.opencode/plugins/foundry-tools/agent-refresh.js +184 -0
  3. package/dist/.opencode/plugins/foundry-tools/helpers.js +17 -20
  4. package/dist/.opencode/plugins/foundry-tools/refresh-agents-tool.js +27 -0
  5. package/dist/.opencode/plugins/foundry.js +114 -5
  6. package/dist/CHANGELOG.md +161 -0
  7. package/dist/README.md +12 -15
  8. package/dist/agents/foundry.md +37 -0
  9. package/dist/docs/README.md +1 -1
  10. package/dist/docs/architecture.md +8 -5
  11. package/dist/docs/concepts.md +2 -2
  12. package/dist/docs/getting-started.md +55 -135
  13. package/dist/docs/tools.md +21 -1
  14. package/dist/scripts/lib/foundational-guards.js +1 -1
  15. package/dist/scripts/lib/memory/admin/init.js +1 -1
  16. package/dist/scripts/sort.js +1 -1
  17. package/dist/skills/add-appraiser/SKILL.md +19 -34
  18. package/dist/skills/add-artefact-type/SKILL.md +19 -22
  19. package/dist/skills/add-cycle/SKILL.md +28 -37
  20. package/dist/skills/add-extractor/SKILL.md +21 -35
  21. package/dist/skills/add-flow/SKILL.md +43 -88
  22. package/dist/skills/add-law/SKILL.md +19 -24
  23. package/dist/skills/add-memory-edge-type/SKILL.md +12 -20
  24. package/dist/skills/add-memory-entity-type/SKILL.md +10 -19
  25. package/dist/skills/appraise/SKILL.md +1 -1
  26. package/dist/skills/change-embedding-model/SKILL.md +7 -11
  27. package/dist/skills/drop-memory-edge-type/SKILL.md +7 -11
  28. package/dist/skills/drop-memory-entity-type/SKILL.md +7 -11
  29. package/dist/skills/dry-run/SKILL.md +11 -28
  30. package/dist/skills/flow/SKILL.md +2 -2
  31. package/dist/skills/forge/SKILL.md +1 -1
  32. package/dist/skills/human-appraise/SKILL.md +1 -1
  33. package/dist/skills/init-memory/SKILL.md +12 -25
  34. package/dist/skills/list-agents/SKILL.md +1 -1
  35. package/dist/skills/quench/SKILL.md +1 -1
  36. package/dist/skills/refresh-agents/SKILL.md +4 -26
  37. package/dist/skills/rename-memory-edge-type/SKILL.md +7 -11
  38. package/dist/skills/rename-memory-entity-type/SKILL.md +7 -11
  39. package/dist/skills/reset-memory/SKILL.md +10 -18
  40. package/dist/skills/upgrade-foundry/SKILL.md +3 -3
  41. package/package.json +2 -1
  42. package/dist/skills/init-foundry/SKILL.md +0 -91
package/README.md CHANGED
@@ -105,7 +105,7 @@ Add the plugin to `opencode.json`:
105
105
 
106
106
  Restart OpenCode so the plugin registers its tools and skills. You will see new
107
107
  tools and skills become available in OpenCode's command palette once the restart
108
- completes. The `init-foundry` skill and flow-management tools are now ready to use.
108
+ completes. Flow-management tools are now ready to use.
109
109
 
110
110
  ---
111
111
 
@@ -132,26 +132,23 @@ Add the plugin to `opencode.json` (see Install section above):
132
132
 
133
133
  Then restart OpenCode so the plugin registers its tools and skills. You will see new
134
134
  tools and skills become available in OpenCode's command palette once the restart
135
- completes. The `init-foundry` skill and flow-management tools are now ready to use.
135
+ completes. Flow-management tools are now ready to use.
136
136
 
137
137
  ### Phase 2 — Initialise
138
138
 
139
- Open OpenCode in your project repo and say:
139
+ Restart OpenCode after adding the plugin. On boot, the plugin's config hook runs
140
+ a decision tree: if `foundry/` is missing or its VERSION does not match the
141
+ installed plugin version, it bootstraps the directory structure, generates
142
+ `foundry-<model>` stage agent files, installs the user-facing `Foundry` guide
143
+ agent, and tells you to restart again.
140
144
 
141
- ```
142
- > run init-foundry
143
- ```
144
-
145
- Foundry scaffolds a `foundry/` directory, generates one `foundry-<model>` agent file
146
- per model available in your session, commits the structure, and then asks you to
147
- restart. All the foundational configuration directories are created; you will
148
- populate them next.
149
-
150
- Restart OpenCode so the new `foundry-<model>` agents register — multi-model dispatch cannot route to agents it cannot discover.
145
+ Restart OpenCode a second time so the new agents register. After the restart,
146
+ switch to the **Foundry** agent. The Foundry agent is the normal interface for
147
+ authoring and running Foundry workflows.
151
148
 
152
- ### Phase 3 — Build a flow without writing one
149
+ ### Phase 3 — Ask the Foundry agent for a flow
153
150
 
154
- Ask Foundry to set up a flow:
151
+ With the **Foundry** agent active, ask it to set up a flow:
155
152
 
156
153
  ```
157
154
  > set up a flow that writes haikus
@@ -0,0 +1,184 @@
1
+ // Shared agent-refresh utility for the Foundry plugin.
2
+ // Provides refreshAgents, detectChanges, and writeFoundryGuideAgent
3
+ // used by both the config hook and the foundry_refresh_agents tool.
4
+
5
+ import path from 'path';
6
+ import { execFileSync } from 'child_process';
7
+ import { mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync, existsSync } from 'fs';
8
+ import { createHash } from 'crypto';
9
+
10
+ const AGENT_FRONTMATTER_TEMPLATE = `---
11
+ description: "Foundry stage agent using MODEL_ID"
12
+ mode: subagent
13
+ model: "MODEL_ID"
14
+ hidden: true
15
+ ---
16
+ You are a Foundry stage agent. Follow the skill instructions provided in your task prompt exactly.
17
+ `;
18
+
19
+ function makeSlug(modelId) {
20
+ return modelId.replace(/[/.]/g, '-');
21
+ }
22
+
23
+ function buildAgentContent(modelId) {
24
+ return AGENT_FRONTMATTER_TEMPLATE.replace(/MODEL_ID/g, modelId);
25
+ }
26
+
27
+ function listModels(worktree) {
28
+ const stdout = execFileSync('opencode', ['models'], {
29
+ cwd: worktree,
30
+ encoding: 'utf8',
31
+ stdio: ['pipe', 'pipe', 'pipe'],
32
+ });
33
+ return stdout
34
+ .split('\n')
35
+ .map(line => line.trim())
36
+ .filter(line => line.length > 0);
37
+ }
38
+
39
+ function deleteStaleAgents(agentsDir) {
40
+ let existing;
41
+ try {
42
+ existing = readdirSync(agentsDir);
43
+ } catch {
44
+ existing = [];
45
+ }
46
+ for (const entry of existing) {
47
+ if (entry.startsWith('foundry-') && entry.endsWith('.md')) {
48
+ unlinkSync(path.join(agentsDir, entry));
49
+ }
50
+ }
51
+ }
52
+
53
+ function writeAgentFiles(agentsDir, models) {
54
+ for (const modelId of models) {
55
+ const slug = makeSlug(modelId);
56
+ const filePath = path.join(agentsDir, `foundry-${slug}.md`);
57
+ writeFileSync(filePath, buildAgentContent(modelId), 'utf8');
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Snapshot the current foundry-*.md agent files in the agents directory.
63
+ * Returns a plain object mapping filename → sha256 hex digest.
64
+ * Returns an empty object when the directory does not exist.
65
+ */
66
+ function takeSnapshot(agentsDir) {
67
+ const snapshot = {};
68
+ try {
69
+ const entries = readdirSync(agentsDir)
70
+ .filter(e => e.startsWith('foundry-') && e.endsWith('.md'))
71
+ .sort();
72
+ for (const entry of entries) {
73
+ const content = readFileSync(path.join(agentsDir, entry));
74
+ snapshot[entry] = createHash('sha256').update(content).digest('hex');
75
+ }
76
+ } catch {
77
+ // Directory does not exist yet — empty snapshot
78
+ }
79
+ return snapshot;
80
+ }
81
+
82
+ /**
83
+ * Compare two snapshots for equality.
84
+ * Returns true when both have the same files with the same content hashes.
85
+ */
86
+ function snapshotsEqual(a, b) {
87
+ const aKeys = Object.keys(a).sort();
88
+ const bKeys = Object.keys(b).sort();
89
+ if (aKeys.length !== bKeys.length) return false;
90
+ return aKeys.every(k => a[k] === b[k]);
91
+ }
92
+
93
+ /**
94
+ * Run `opencode models`, delete stale foundry-*.md agent files,
95
+ * and write new ones for each model returned.
96
+ *
97
+ * @param {string} worktree - Absolute path to the project worktree root.
98
+ * @returns {{ ok: true, count: number } | { ok: false, error: string }}
99
+ */
100
+ export function refreshAgents(worktree) {
101
+ try {
102
+ const models = listModels(worktree);
103
+ if (models.length === 0) {
104
+ return { ok: false, error: 'No models returned by `opencode models`. Is the opencode CLI available?' };
105
+ }
106
+
107
+ const agentsDir = path.join(worktree, '.opencode', 'agents');
108
+ mkdirSync(agentsDir, { recursive: true });
109
+ deleteStaleAgents(agentsDir);
110
+ writeAgentFiles(agentsDir, models);
111
+
112
+ return { ok: true, count: models.length };
113
+ } catch (err) {
114
+ return { ok: false, error: err.message ?? String(err) };
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Snapshot the current foundry-*.md agent files, run refreshAgents,
120
+ * then compare the before/after file sets to detect changes.
121
+ *
122
+ * When refreshAgents returns { ok: false, error }, the error is
123
+ * propagated immediately without performing a comparison.
124
+ *
125
+ * @param {string} worktree - Absolute path to the project worktree root.
126
+ * @returns {{ ok: true, changed: boolean, count: number } | { ok: false, error: string }}
127
+ */
128
+ export function detectChanges(worktree) {
129
+ const agentsDir = path.join(worktree, '.opencode', 'agents');
130
+ const before = takeSnapshot(agentsDir);
131
+
132
+ const result = refreshAgents(worktree);
133
+ if (!result.ok) {
134
+ return result;
135
+ }
136
+
137
+ const after = takeSnapshot(agentsDir);
138
+ const changed = !snapshotsEqual(before, after);
139
+
140
+ return { ok: true, changed, count: result.count };
141
+ }
142
+
143
+ /**
144
+ * Resolve the guide agent source path within the installed package.
145
+ * Prefers dist/agents/foundry.md and falls back to src/agents/foundry.md.
146
+ */
147
+ function resolveGuideSource(packageRoot) {
148
+ const distPath = path.join(packageRoot, 'dist', 'agents', 'foundry.md');
149
+ if (existsSync(distPath)) return distPath;
150
+ return path.join(packageRoot, 'src', 'agents', 'foundry.md');
151
+ }
152
+
153
+ /**
154
+ * Copy the Foundry guide agent (foundry.md) from the installed package
155
+ * to the project's .opencode/agents/ directory.
156
+ *
157
+ * Resolves the source from `packageRoot/dist/agents/foundry.md` and
158
+ * falls back to `packageRoot/src/agents/foundry.md` when the dist
159
+ * path does not exist. Skips writing when the target file already
160
+ * exists (uses existsSync check).
161
+ *
162
+ * @param {string} worktree - Absolute path to the project worktree root.
163
+ * @param {string} packageRoot - Absolute path to the installed package root.
164
+ * @returns {{ ok: true, written: boolean } | { ok: false, error: string }}
165
+ */
166
+ export function writeFoundryGuideAgent(worktree, packageRoot) {
167
+ const targetDir = path.join(worktree, '.opencode', 'agents');
168
+ const targetPath = path.join(targetDir, 'foundry.md');
169
+
170
+ if (existsSync(targetPath)) {
171
+ return { ok: true, written: false };
172
+ }
173
+
174
+ const sourcePath = resolveGuideSource(packageRoot);
175
+
176
+ try {
177
+ const content = readFileSync(sourcePath, 'utf8');
178
+ mkdirSync(targetDir, { recursive: true });
179
+ writeFileSync(targetPath, content, 'utf8');
180
+ return { ok: true, written: true };
181
+ } catch (err) {
182
+ return { ok: false, error: `Failed to write guide agent: ${err.message ?? String(err)}` };
183
+ }
184
+ }
@@ -94,14 +94,13 @@ function buildFoundryNotInitializedMessage() {
94
94
  return `<FOUNDRY_CONTEXT>
95
95
  Foundry is installed but not initialised in this project. There is no foundry/ directory.
96
96
 
97
- To set up Foundry, use the \`init-foundry\` skill. This will create the foundry/ directory structure
98
- and guide you through defining artefact types, laws, appraisers, cycles, and flows.
97
+ To set up Foundry, initialise the project first. Initialisation creates the foundry/ directory structure, installs the user-facing Foundry agent, and generates model-routing stage agents. After initialisation, restart OpenCode and switch to the Foundry agent.
99
98
  </FOUNDRY_CONTEXT>`;
100
99
  }
101
100
 
102
101
  function buildFlowList(flows) {
103
102
  if (flows.length === 0) {
104
- return '- (no flows defined yet — use the `add-flow` skill to create one)';
103
+ return '- (no flows defined yet — ask the Foundry agent to set one up)';
105
104
  }
106
105
  return flows.map(f => {
107
106
  const sc = f.startingCycles.length > 0 ? ` — starting cycles: ${f.startingCycles.join(', ')}` : '';
@@ -121,34 +120,32 @@ The pipeline: assay (populate memory) → forge (produce) → quench (determinis
121
120
 
122
121
  ${flowList}
123
122
 
124
- **CRITICAL ROUTING RULE:** When the user references any flow above — by id (e.g. "creative-flow"),
125
- by name (e.g. "Creative Flow"), or by clear paraphrase (e.g. "the creative flow", "use the creative pipeline") —
126
- invoke the \`flow\` skill DIRECTLY with that flow's id. Do NOT invoke brainstorming, do NOT explore the
127
- codebase, do NOT ask clarifying questions about what to build. The flow's cycles already define the
128
- work. The user's request text (e.g. "make a haiku about X") is the goal to pass to the flow.
123
+ When the user references any flow above — by id (e.g. "creative-flow"),
124
+ by name (e.g. "Creative Flow"), or by clear paraphrase (e.g. "the creative flow",
125
+ "use the creative pipeline") ask the Foundry agent to run that flow with the user's
126
+ request as the goal. The Foundry agent handles cycle selection, work-branch creation, and
127
+ orchestration automatically.
129
128
 
130
- Brainstorming applies to NEW features being added to foundry itself (new cycles, new artefact types,
131
- new skills). It does NOT apply to running an existing, defined flow.
129
+ ## Foundry agent capabilities
132
130
 
133
- ## Available skills
134
-
135
- - **Pipeline:** assay, forge, quench, appraise, orchestrate, flow, human-appraise
136
- - **Authoring:** add-artefact-type, add-law, add-appraiser, add-cycle, add-flow, add-memory-entity-type, add-memory-edge-type, add-extractor, init-foundry
137
- - **Maintenance:** upgrade-foundry, refresh-agents, list-agents, init-memory, change-embedding-model, dry-run
138
- - **Memory Admin:** drop-memory-entity-type, drop-memory-edge-type, rename-memory-entity-type, rename-memory-edge-type, reset-memory
131
+ The Foundry agent has internal workflows for pipeline execution, authoring, maintenance, memory administration, and dry-run trials. Present these capabilities as Foundry outcomes instead of naming internal skills.
139
132
 
140
133
  ## Multi-model routing
141
134
 
142
- Foundry uses \`foundry-*\` sub-agents defined as markdown files in \`.opencode/agents/\`.
143
- Run the \`refresh-agents\` skill to regenerate them after adding or removing providers.
144
- Cycle definitions can specify per-stage models via the \`models\` frontmatter map. Appraisers can override with their own \`model\` field.
135
+ Foundry uses generated \`foundry-*\` stage agents for cycle stage dispatch. The user-facing \`Foundry\` agent is installed as \`.opencode/agents/foundry.md\` and should be used for authoring and running Foundry workflows.
145
136
 
146
137
  All user content lives under foundry/.
147
138
  Scripts are located at: ${path.join(packageRoot, 'scripts')}
148
139
  </FOUNDRY_CONTEXT>`;
149
140
  }
150
141
 
151
- export function getBootstrapContent(directory, packageRoot) {
142
+ export function getBootstrapContent(directory, packageRoot, restartNeeded = false) {
143
+ if (restartNeeded) {
144
+ return `<FOUNDRY_CONTEXT>
145
+ Foundry initialised. Restart OpenCode so the Foundry agent and model-routing agents register. After restart, switch to the Foundry agent to author and run workflows.
146
+ </FOUNDRY_CONTEXT>`;
147
+ }
148
+
152
149
  const foundryDir = path.join(directory, 'foundry');
153
150
  const foundryExists = existsSync(foundryDir) && statSync(foundryDir).isDirectory();
154
151
 
@@ -0,0 +1,27 @@
1
+ import { refreshAgents } from './agent-refresh.js';
2
+
3
+ export function createRefreshAgentsTool({ tool }) {
4
+ return {
5
+ foundry_refresh_agents: tool({
6
+ description: 'Regenerate .opencode/agents/foundry-*.md stage-agent files from the currently available models.',
7
+ args: {},
8
+ async execute(_args, context) {
9
+ try {
10
+ const result = refreshAgents(context.worktree);
11
+ if (!result.ok) {
12
+ return JSON.stringify({
13
+ ok: false,
14
+ error: `foundry_refresh_agents: ${result.error}`,
15
+ });
16
+ }
17
+ return JSON.stringify(result);
18
+ } catch (err) {
19
+ return JSON.stringify({
20
+ ok: false,
21
+ error: `foundry_refresh_agents: ${err.message ?? String(err)}`,
22
+ });
23
+ }
24
+ },
25
+ }),
26
+ };
27
+ }
@@ -1,17 +1,22 @@
1
1
  /**
2
2
  * Foundry plugin for OpenCode.ai
3
3
  *
4
- * All skills are always registered. Individual skills check for foundry/ dir.
5
- * - If foundry/ exists: pipeline context injected into first message
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.
4
+ * The config hook runs a boot decision tree on every plugin load: if foundry/
5
+ * is missing or its VERSION mismatches, it bootstraps the directory structure,
6
+ * agent files, and guide agent, then sets a restartNeeded flag. If VERSION
7
+ * matches but the agent file set changed, only agents are refreshed. The
8
+ * message-transform hook injects either a restart prompt or the full Foundry
9
+ * context based on the restartNeeded flag. All skills are always registered;
10
+ * individual skills check for foundry/ dir.
8
11
  */
9
12
 
10
13
  import path from 'path';
11
14
  import { fileURLToPath } from 'url';
15
+ import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
12
16
  import { tool } from '@opencode-ai/plugin';
13
17
  import { createPendingStore } from '../../scripts/lib/pending.js';
14
18
  import { getBootstrapContent } from './foundry-tools/helpers.js';
19
+ import { refreshAgents, detectChanges, writeFoundryGuideAgent } from './foundry-tools/agent-refresh.js';
15
20
  import { createHistoryTools } from './foundry-tools/history-tools.js';
16
21
  import { createStageTools } from './foundry-tools/stage-tools.js';
17
22
  import { createWorkfileTools } from './foundry-tools/workfile-tools.js';
@@ -29,11 +34,103 @@ import { createMemoryTools } from './foundry-tools/memory-tools.js';
29
34
  import { createMemoryAdminTools } from './foundry-tools/memory-admin-tools.js';
30
35
  import { createSnapshotTools } from './foundry-tools/snapshot-tools.js';
31
36
  import { createAttestationTools } from './foundry-tools/attestation-tools.js';
37
+ import { createRefreshAgentsTool } from './foundry-tools/refresh-agents-tool.js';
32
38
 
33
39
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
34
40
  const packageRoot = path.resolve(__dirname, '../..');
35
41
  const allSkillsDir = path.join(packageRoot, 'skills');
36
42
 
43
+ // Module-level flag shared between config and message-transform hooks.
44
+ let restartNeeded = false;
45
+
46
+ // -- Bootstrap helpers --
47
+
48
+ function bootstrapDirectories(worktree) {
49
+ const foundryDir = path.join(worktree, 'foundry');
50
+ mkdirSync(foundryDir, { recursive: true });
51
+ for (const sub of ['artefacts', 'flows', 'cycles', 'laws', 'appraisers']) {
52
+ const subDir = path.join(foundryDir, sub);
53
+ mkdirSync(subDir, { recursive: true });
54
+ const gitkeep = path.join(subDir, '.gitkeep');
55
+ if (!existsSync(gitkeep)) {
56
+ writeFileSync(gitkeep, '', 'utf8');
57
+ }
58
+ }
59
+ }
60
+
61
+ function ensureNewlineSuffix(str) {
62
+ if (str !== '' && !str.endsWith('\n')) return str + '\n';
63
+ return str;
64
+ }
65
+
66
+ function bootstrapGitignore(worktree) {
67
+ const gitignorePath = path.join(worktree, '.gitignore');
68
+ let content = '';
69
+ if (existsSync(gitignorePath)) {
70
+ content = readFileSync(gitignorePath, 'utf8');
71
+ }
72
+ content = ensureNewlineSuffix(content);
73
+ const existingLines = content.split('\n').map(l => l.trim());
74
+ const lines = ['.snapshots/', 'node_modules/', '.DS_Store'];
75
+ for (const line of lines) {
76
+ if (existingLines.includes(line)) continue;
77
+ content += `${line}\n`;
78
+ }
79
+ writeFileSync(gitignorePath, content, 'utf8');
80
+ }
81
+
82
+ function runBootstrapSequence(worktree, pkgRoot) {
83
+ bootstrapDirectories(worktree);
84
+ bootstrapGitignore(worktree);
85
+ refreshAgents(worktree);
86
+ writeFoundryGuideAgent(worktree, pkgRoot);
87
+ const pkg = JSON.parse(readFileSync(path.join(pkgRoot, 'package.json'), 'utf8'));
88
+ writeFileSync(path.join(worktree, 'foundry', 'VERSION'), pkg.version, 'utf8');
89
+ }
90
+
91
+ function checkVersionMatch(foundryDir, pkgRoot) {
92
+ try {
93
+ const installedVersion = readFileSync(path.join(foundryDir, 'VERSION'), 'utf8').trim();
94
+ const pkg = JSON.parse(readFileSync(path.join(pkgRoot, 'package.json'), 'utf8'));
95
+ return installedVersion === pkg.version;
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ function isFoundryPopulated(worktree) {
102
+ const foundryDir = path.join(worktree, 'foundry');
103
+ if (!existsSync(foundryDir)) return false;
104
+ return readdirSync(foundryDir).some(e => e !== '.gitkeep');
105
+ }
106
+
107
+ function ensureGuideAgent(worktree, pkgRoot) {
108
+ const guideAgentPath = path.join(worktree, '.opencode', 'agents', 'foundry.md');
109
+ if (!existsSync(guideAgentPath)) {
110
+ writeFoundryGuideAgent(worktree, pkgRoot);
111
+ return true;
112
+ }
113
+ return false;
114
+ }
115
+
116
+ function runConfigBootstrap(worktree, pkgRoot) {
117
+ if (!isFoundryPopulated(worktree)) {
118
+ runBootstrapSequence(worktree, pkgRoot);
119
+ return true;
120
+ }
121
+
122
+ const foundryDir = path.join(worktree, 'foundry');
123
+ if (!checkVersionMatch(foundryDir, pkgRoot)) {
124
+ runBootstrapSequence(worktree, pkgRoot);
125
+ return true;
126
+ }
127
+
128
+ const result = detectChanges(worktree);
129
+ const changed = result.ok && result.changed;
130
+ const guideWritten = ensureGuideAgent(worktree, pkgRoot);
131
+ return changed || guideWritten;
132
+ }
133
+
37
134
  export { buildCyclePromptExtras } from './foundry-tools/helpers.js';
38
135
 
39
136
  function buildTools(createTool, pending) {
@@ -55,6 +152,7 @@ function buildTools(createTool, pending) {
55
152
  ...createMemoryAdminTools({ tool: createTool }),
56
153
  ...createSnapshotTools({ tool: createTool }),
57
154
  ...createAttestationTools({ tool: createTool }),
155
+ ...createRefreshAgentsTool({ tool: createTool }),
58
156
  };
59
157
  }
60
158
 
@@ -82,10 +180,17 @@ export const FoundryPlugin = async ({ directory }) => {
82
180
  if (!config.skills.paths.includes(allSkillsDir)) {
83
181
  config.skills.paths.push(allSkillsDir);
84
182
  }
183
+
184
+ // Boot decision tree: bootstrap or detect changes, then set restart flag
185
+ try {
186
+ restartNeeded = runConfigBootstrap(directory, packageRoot);
187
+ } catch (err) {
188
+ console.error('Foundry bootstrap error:', err.message);
189
+ }
85
190
  },
86
191
 
87
192
  'experimental.chat.messages.transform': async (_input, output) => {
88
- const bootstrap = getBootstrapContent(directory, packageRoot);
193
+ const bootstrap = getBootstrapContent(directory, packageRoot, restartNeeded);
89
194
  if (!bootstrap) return;
90
195
 
91
196
  const firstUser = getFirstUserWithParts(output);
@@ -101,5 +206,9 @@ export const FoundryPlugin = async ({ directory }) => {
101
206
  };
102
207
 
103
208
  Object.defineProperty(plugin, Symbol.for('foundry.test.pending'), { value: pending });
209
+ Object.defineProperty(plugin, Symbol.for('foundry.test.restartNeeded'), {
210
+ get: () => restartNeeded,
211
+ configurable: true,
212
+ });
104
213
  return plugin;
105
214
  };
package/dist/CHANGELOG.md CHANGED
@@ -1,5 +1,166 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.2.0] - 2026-05-14
4
+
5
+ Plugin auto-bootstrapping release. Foundry now ensures the guide agent is
6
+ present on every plugin load and deletes the now-redundant `init-foundry`
7
+ skill. The project also ships a publish-release skill and documented
8
+ workflows for plans and git worktrees.
9
+
10
+ ### Added
11
+
12
+ - **Bootstrap logic** (`src/plugin.js`). On every `detectChanges` call,
13
+ Foundry ensures the guide agent file (`foundry.md`) exists in
14
+ `.opencode/agents/`, running the same deterministic agent-refresh path
15
+ that the `foundry_refresh_agents` tool uses. If the guide agent is
16
+ newly created, a post-install restart message is injected into the
17
+ detection context.
18
+ - **`publish-release` skill** (`.opencode/skills/publish-release/`). A
19
+ workflow skill that commits loose changes, runs the quality gate,
20
+ bumps the version, updates the changelog, tags, pushes, and publishes
21
+ to npm.
22
+
23
+ ### Changed
24
+
25
+ - **`init-foundry` skill removed.** The manual installation step is
26
+ replaced by deterministic auto-bootstrapping on plugin load.
27
+ `init-foundry` was the last on-ramp skill; new users no longer need
28
+ to run any skill after `pnpm add`.
29
+ - **Shared agent-refresh utility extracted** (`src/lib/agent-refresh.js`).
30
+ The deterministic agent generation logic that lived in
31
+ `scripts/tools/foundry_refresh_agents.js` is now a shared module,
32
+ consumed by both the tool and the `detectChanges` bootstrap path.
33
+ - **Phase reviews run in parallel** with partitioned iteration via
34
+ parallel implementer agents.
35
+ - **Implementer agent model** updated to `deepseek-v4-flash`.
36
+
37
+ ### Fixed
38
+
39
+ - **Guide agent now always present after `detectChanges`.** Previously
40
+ the guide agent could be absent until `init-foundry` or
41
+ `foundry_refresh_agents` was explicitly run.
42
+ - **Guide agent preserved during `foundry_refresh_agents`.** The refresh
43
+ tool now ensures `foundry.md` is generated alongside stage agents,
44
+ not just preserved.
45
+
46
+ ### Docs
47
+
48
+ - Git worktrees in `.worktrees/` directory documented.
49
+ - Plans directory workflow and phased planning skills documented.
50
+
51
+ ## [3.1.0] - 2026-05-11
52
+
53
+ The Foundry guide agent release. Foundry now ships a user-facing agent that
54
+ routes configuration authoring through Foundry concepts — users ask the Foundry
55
+ agent for outcomes, and the agent composes dependent work internally rather than
56
+ handing users a chain of individual tools and skills.
57
+
58
+ ### Added
59
+
60
+ - **Foundry guide agent** (`src/agents/foundry.md`). A user-facing agent with
61
+ identity marker *"You are the Foundry agent"* that maps user requests to
62
+ Foundry concepts, routes through the standard config-branch workflow, and
63
+ delegates to authoring skills internally.
64
+ - **`foundry_refresh_agents` tool.** Deterministic stage-agent generation: runs
65
+ `opencode models`, deletes stale `.opencode/agents/foundry-*.md` files, and
66
+ writes fresh agent files — one per available model. Replaces the prior
67
+ skill-only protocol where the LLM had to implement the logic with shell
68
+ commands.
69
+ - **`init-foundry` installs the guide agent.** Step 5 creates
70
+ `.opencode/agents/foundry.md` (preferring `dist/agents/foundry.md`, falling
71
+ back to `src/agents/foundry.md`), then instructs the user to restart OpenCode
72
+ and switch to the Foundry agent.
73
+ - **Comprehensive guidance audit tests.** `tests/skills/foundry-guidance-audit.test.js`
74
+ (357 lines) and `tests/skills/authoring-guidance.test.js` (45 lines) enforce
75
+ that skills avoid dead-end delegation patterns, route through the Foundry
76
+ agent, keep internal tool-call syntax out of normal user guidance, and handle
77
+ config branches and dependencies internally.
78
+ - **Packaging test** (`tests/plugin/packaging.test.js`). Verifies the published
79
+ package ships `dist/agents/` so `init-foundry` can locate the guide agent
80
+ template in a packaged install.
81
+
82
+ ### Changed
83
+
84
+ - **Authoring skills reworked around the Foundry agent.** `add-flow`,
85
+ `add-cycle`, `add-artefact-type`, `add-law`, and `add-appraiser` now include a
86
+ `## Foundry Agent Preflight` section; and frame user requests as Foundry
87
+ outcomes, compose missing dependencies internally in validation order, and
88
+ hide internal tool-call syntax from normal user guidance (`foundry_git_branch`,
89
+ `foundry_git_finish`, `foundry_config_create_*`).
90
+ - **Memory/config skills handle branches and dependencies internally.**
91
+ `init-memory`, `reset-memory`, `add-memory-entity-type`, `add-memory-edge-type`,
92
+ `add-extractor`, and the `rename-memory-*` / `drop-memory-*` /
93
+ `change-embedding-model` skills no longer tell the user to create config
94
+ branches or run prerequisite skills. All use *"move to a suitable `config/*`
95
+ branch internally when the current branch is safe"* with `work/*` /
96
+ `dry-run/*/*` / uncommitted-change guards.
97
+ - **`add-extractor` composes cycle wiring internally.** The skill updates
98
+ relevant cycle definitions rather than presenting manual frontmatter editing
99
+ as the normal outcome.
100
+ - **`add-cycle` hides generated stage-agent files.** Model selection guidance
101
+ now references *"Available session models are listed in your session
102
+ configuration"* instead of exposing `.opencode/agents/foundry-*.md`.
103
+ - **Existing-file recovery keeps the agent in the loop.** When
104
+ `foundry_config_create_*` returns `{ ok: false }` because the target file
105
+ already exists, `add-artefact-type`, `add-appraiser`, `add-law`, and
106
+ `add-cycle` now read the existing content, incorporate the user's requested
107
+ changes, propose the merged result, and commit — rather than telling the user
108
+ to *"edit by hand."*
109
+ - **Bootstrap context** (`helpers.js`) presents capabilities as Foundry
110
+ outcomes (*"ask the Foundry agent to add flow memory"*, *"ask the Foundry
111
+ agent to run that flow"*) instead of listing internal skills.
112
+ - **`getting-started.md` reworked.** The walkthrough now routes through the
113
+ Foundry agent, removes direct `foundry_git_branch({` /
114
+ `foundry_git_finish({` / `Run \`add-*\`` / `foundry_config_validate_*({` /
115
+ `foundry_config_create_*({` references, and uses `pnpm add -D
116
+ @really-knows-ai/foundry`.
117
+ - **`README.md` quick-start** updated for the install → init → ask-agent
118
+ workflow (`pnpm add -D @really-knows-ai/foundry`, ask the Foundry agent
119
+ to add a haiku flow).
120
+ - **`refresh-agents` skill** delegates to `foundry_refresh_agents()`. The skill
121
+ is now a thin wrapper around the deterministic tool.
122
+ - **`init-foundry`, `list-agents`, `sort.js`, `upgrade-foundry`** updated to
123
+ reference the `foundry_refresh_agents` tool.
124
+ - **Docs** (`architecture.md`, `concepts.md`, `tools.md`) updated to reference
125
+ guide-agent installation and the refresh tool. Tool count increased from 66 to
126
+ 67.
127
+
128
+ ### Fixed
129
+
130
+ - **`dist/agents/` added to the published package.** The build script already
131
+ copied `src/agents/` to `dist/agents/`, but the `files` array in
132
+ `package.json` excluded `dist/agents/`. `init-foundry` can now locate the
133
+ guide agent template in a packaged install.
134
+ - **`foundry_refresh_agents` preserves the guide agent.** Only
135
+ `foundry-*.md` stage agents are regenerated; `.opencode/agents/foundry.md`
136
+ survives refresh.
137
+
138
+ ## [3.0.3] - 2026-05-11
139
+
140
+ A patch release that makes agent-file generation deterministic by moving
141
+ `refresh-agents` from a skill-only protocol into a tested plugin tool.
142
+
143
+ ### Added
144
+
145
+ - **`foundry_refresh_agents` tool.** Runs `opencode models`, deletes stale
146
+ `.opencode/agents/foundry-*.md` files, and generates fresh agent files — one
147
+ per available model. The tool is idempotent, handles missing directories, and
148
+ returns `{ ok: true, count: <n> }` on success. This replaces the prior
149
+ skill-only protocol where the LLM had to implement the logic with shell
150
+ commands, which was error-prone and non-deterministic.
151
+
152
+ ### Changed
153
+
154
+ - **`refresh-agents` skill** now simply calls `foundry_refresh_agents()` and
155
+ reports the result.
156
+ - **`init-foundry` skill** step 4 now calls `foundry_refresh_agents()` instead
157
+ of instructing the LLM to run the `refresh-agents` skill.
158
+ - **`list-agents` skill** error message now references the tool.
159
+ - **`sort.js`** missing-agent error now references the tool.
160
+ - **`helpers.js`** bootstrap message now references the tool.
161
+ - **Docs** (`tools.md`, `getting-started.md`, `architecture.md`, `concepts.md`)
162
+ updated to reference the tool. Tool count increased from 65 to 66.
163
+
3
164
  ## [3.0.2] - 2026-05-11
4
165
 
5
166
  A documentation and tool-correctness patch driven by a failing