specrails-core 6.2.1 → 6.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.
@@ -6,13 +6,13 @@ import { recordSuccessfulInstall } from '../util/registry.js';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { InstallerError } from '../util/errors.js';
8
8
  import { runCommand } from '../util/exec.js';
9
- import { info, ok, step, warn } from '../util/logger.js';
9
+ import { info, ok, step } from '../util/logger.js';
10
10
  import { isDir, isSymlink, listDir, pathExists, readTextFile, removePath, writeFileLf, } from '../util/fs.js';
11
11
  import { loadInstallConfig, resolveConfigPath, } from '../phases/install-config.js';
12
12
  import { checkPrerequisites } from '../phases/prereqs.js';
13
13
  import { derivedPaths } from '../phases/provider-detect.js';
14
14
  import { materializeFrameworkVersion } from '../phases/framework-lifecycle.js';
15
- import { CORE_AGENTS, assembleProjectWorkspace, installFramework, } from '../phases/scaffold.js';
15
+ import { assembleProjectWorkspace, installFramework, } from '../phases/scaffold.js';
16
16
  import { frameworkRoot, resolveArtifacts } from '../util/registry.js';
17
17
  const WORKSPACE_PROVIDER_ORDER = [
18
18
  'claude',
@@ -21,12 +21,12 @@ const WORKSPACE_PROVIDER_ORDER = [
21
21
  'kimi',
22
22
  ];
23
23
  /**
24
- * Snapshot the provider/role inventory exposed by one live workspace before a
25
- * global framework version swap. This matters on Windows: directory junctions
26
- * resolve their target when they are created, so a workspace link made through
27
- * `framework/current` must be recreated after `current` moves. Keeping the
28
- * exact visible `sr-*` set also avoids widening or narrowing a project's
29
- * optional role selection during that refresh.
24
+ * Snapshot the providers exposed by one live workspace before a global
25
+ * framework version swap. This matters on Windows: directory junctions resolve
26
+ * their target when they are created, so a workspace link made through
27
+ * `framework/current` must be recreated after `current` moves. Roles are
28
+ * runtime-defined since Core 6.3, so every provider reports an empty selection;
29
+ * the keys are what drive the multi-provider reassembly.
30
30
  */
31
31
  export function snapshotWorkspaceProviderSelections(workspace) {
32
32
  const selections = {};
@@ -36,44 +36,25 @@ export function snapshotWorkspaceProviderSelections(workspace) {
36
36
  if (Array.isArray(manifest.providers))
37
37
  recorded = manifest.providers.filter((value) => typeof value === 'string');
38
38
  }
39
- catch { /* Legacy manifests are recognized by the complete Core role trio. */ }
39
+ catch { /* Legacy manifests: a provider counts only through its managed links. */ }
40
40
  for (const provider of WORKSPACE_PROVIDER_ORDER) {
41
41
  const { providerDir } = derivedPaths(provider);
42
42
  const providerRoot = path.join(workspace, providerDir);
43
43
  if (!pathExists(providerRoot) ||
44
44
  (!workspaceHasManagedProviderLink(providerRoot, provider)
45
- && !(recorded.includes(provider) && hasCopiedCoreArtifacts(providerRoot, provider))
46
- && !hasCopiedCoreRoles(providerRoot, provider))) {
45
+ && !(recorded.includes(provider) && hasCopiedCoreArtifacts(providerRoot, provider)))) {
47
46
  continue;
48
47
  }
49
- const roleRoot = provider === 'codex'
50
- ? path.join(workspace, providerDir, 'skills', 'rails')
51
- : provider === 'kimi'
52
- ? path.join(workspace, providerDir, 'skills')
53
- : path.join(workspace, providerDir, 'agents');
54
- const ids = listDir(roleRoot)
55
- .filter((entry) => provider === 'claude' || provider === 'gemini'
56
- ? path.basename(entry).endsWith('.md')
57
- : isDir(entry))
58
- .map((entry) => {
59
- const name = path.basename(entry);
60
- return name.endsWith('.md') ? name.slice(0, -3) : name;
61
- })
62
- .filter((id) => /^sr-[a-z0-9-]+$/.test(id));
63
- selections[provider] = [...new Set(ids)].sort();
48
+ selections[provider] = [];
64
49
  }
65
50
  return selections;
66
51
  }
52
+ /** A copied (in-repo) install is recognised by its workflow commands or runtime, never by role files. */
67
53
  function hasCopiedCoreArtifacts(providerRoot, provider) {
68
- return hasCopiedCoreRoles(providerRoot, provider) || pathExists(provider === 'codex' ? path.join(providerRoot, 'skills', 'implement', 'SKILL.md')
54
+ return pathExists(provider === 'codex' ? path.join(providerRoot, 'skills', 'implement', 'SKILL.md')
69
55
  : provider === 'kimi' ? path.join(providerRoot, 'specrails', 'run-skill.mjs')
70
56
  : path.join(providerRoot, 'commands', 'specrails', provider === 'gemini' ? 'implement.toml' : 'implement.md'));
71
57
  }
72
- function hasCopiedCoreRoles(providerRoot, provider) {
73
- return [...CORE_AGENTS].every((id) => pathExists(provider === 'codex' ? path.join(providerRoot, 'skills', 'rails', id, 'SKILL.md')
74
- : provider === 'kimi' ? path.join(providerRoot, 'skills', id, 'SKILL.md')
75
- : path.join(providerRoot, 'agents', id + '.md')));
76
- }
77
58
  function workspaceHasManagedProviderLink(providerRoot, provider) {
78
59
  const directoryLinks = provider === 'claude'
79
60
  ? ['commands', 'skills', 'rules']
@@ -85,10 +66,11 @@ function workspaceHasManagedProviderLink(providerRoot, provider) {
85
66
  if (directoryLinks.some((relative) => isSymlink(path.join(providerRoot, relative)))) {
86
67
  return true;
87
68
  }
88
- const granularRoot = provider === 'kimi'
89
- ? path.join(providerRoot, 'skills')
90
- : path.join(providerRoot, 'agents');
91
- return listDir(granularRoot).some((entry) => isSymlink(entry));
69
+ // Kimi links its skills one directory at a time; claude/gemini have no
70
+ // granular region any more (role files are no longer linked per file).
71
+ if (provider !== 'kimi')
72
+ return false;
73
+ return listDir(path.join(providerRoot, 'skills')).some((entry) => isSymlink(entry));
92
74
  }
93
75
  /**
94
76
  * Refresh every provider already represented in the project plus the provider
@@ -110,9 +92,6 @@ function reassembleWorkspaceProviders(input) {
110
92
  version: input.version,
111
93
  codeRoot: input.codeRoot,
112
94
  scriptDir: input.scriptDir,
113
- selectedAgents: provider === input.selectedProvider
114
- ? input.selectedAgents ?? input.previousSelections[provider]
115
- : input.previousSelections[provider],
116
95
  copyStatics: input.copyStatics,
117
96
  });
118
97
  }
@@ -142,10 +121,10 @@ export async function runInit(flags) {
142
121
  }
143
122
  explicitProvider = flags.provider;
144
123
  }
145
- // --from-config: read provider + agents from yaml.
124
+ // --from-config: read the provider from yaml. The legacy `agents` section is
125
+ // validated (and warned about) by `loadInstallConfig`; roles are runtime-defined.
146
126
  const fromConfigFlag = flags['from-config'];
147
127
  let providerHint = explicitProvider;
148
- let selectedAgentsHint;
149
128
  if (fromConfigFlag !== undefined) {
150
129
  const explicitPath = typeof fromConfigFlag === 'string' ? fromConfigFlag : undefined;
151
130
  const resolved = resolveConfigPath(repoRoot, explicitPath);
@@ -155,9 +134,7 @@ export async function runInit(flags) {
155
134
  throw new InstallerError(`--provider '${explicitProvider}' conflicts with provider '${config.provider}' in ${resolved}`, 40);
156
135
  }
157
136
  providerHint = config.provider;
158
- selectedAgentsHint = config.agents.selected;
159
137
  info(`Loaded install config from ${resolved}`);
160
- warnUnknownSelectedAgents(selectedAgentsHint);
161
138
  }
162
139
  else {
163
140
  info(`install-config.yaml not found at ${resolved} — falling back to ${explicitProvider ? `explicit provider '${explicitProvider}'` : 'auto-detection'}`);
@@ -199,7 +176,7 @@ export async function runInit(flags) {
199
176
  // Bundled-framework flow: materialize the provider-INVARIANT framework ONCE
200
177
  // under `<home>/.specrails/framework/<version>/<providerDir>/`, point `current`
201
178
  // at it, then SYMLINK that copy into the workspace and seed the project layer
202
- // (agent-memory, manifest, instruction files, gemini acks). The framework
179
+ // (manifest, instruction files). The framework
203
180
  // source for standalone npx is the package's templates/+commands/ (scriptDir).
204
181
  step('Phase 2 & 3: Installing specrails artifacts');
205
182
  const { providerDir } = derivedPaths(prereqs.provider);
@@ -228,7 +205,6 @@ export async function runInit(flags) {
228
205
  provider: prereqs.provider,
229
206
  providerDir,
230
207
  version,
231
- selectedAgents: selectedAgentsHint,
232
208
  requiredProviders: Object.keys(previousSelections),
233
209
  });
234
210
  reassembleWorkspaceProviders({
@@ -238,7 +214,6 @@ export async function runInit(flags) {
238
214
  codeRoot,
239
215
  scriptDir,
240
216
  selectedProvider: prereqs.provider,
241
- selectedAgents: selectedAgentsHint,
242
217
  previousSelections,
243
218
  // In-repo: COPY the framework statics as real, committable files. Relocated:
244
219
  // symlink from the framework store (O(1) update on a version swap).
@@ -255,7 +230,7 @@ export async function runInit(flags) {
255
230
  if (artifactRoot !== codeRoot)
256
231
  recordSuccessfulInstall(repoRoot, { home: process.env.SPECRAILS_REGISTRY_HOME, providers: [prereqs.provider], coreVersion: version });
257
232
  step('Installation complete');
258
- info('Agents, commands, and rules were placed directly — no follow-up step required.');
233
+ info('Commands and skills were placed directly — no follow-up step required.');
259
234
  // Terminal sentinel for programmatic consumers (specrails-desktop's setup
260
235
  // wizard matches this exact line via regex to mark the "init complete"
261
236
  // checkpoint). The sentinel line below is FROZEN — the downstream setup
@@ -268,22 +243,6 @@ export async function runInit(flags) {
268
243
  });
269
244
  });
270
245
  }
271
- /**
272
- * Warn (once per id) about `agents.selected` entries that have no shipped
273
- * template — typically a pre-v5 install-config.yaml still listing removed
274
- * agents. They are skipped at placement; the warning tells the user why and
275
- * points at the v5 extension path.
276
- */
277
- export function warnUnknownSelectedAgents(selected) {
278
- if (!selected)
279
- return;
280
- for (const id of selected) {
281
- if (!CORE_AGENTS.has(id)) {
282
- warn(`install-config.yaml selects agent '${id}', which specrails-core no longer ships — ` +
283
- `skipping (removed in v5).`);
284
- }
285
- }
286
- }
287
246
  /**
288
247
  * Materialize the framework for `(version, provider)` if absent and (by default)
289
248
  * point `<frameworkDir>/current` at that version. Idempotent — `installFramework`
@@ -304,7 +263,6 @@ export function ensureFramework(input) {
304
263
  provider: input.provider,
305
264
  providerDir: input.providerDir,
306
265
  version: input.version,
307
- selectedAgents: input.selectedAgents,
308
266
  });
309
267
  return;
310
268
  }
@@ -20,7 +20,12 @@ interface InstallModelConfig {
20
20
  interface InstallConfig {
21
21
  version: 1;
22
22
  provider: Provider;
23
- agents: {
23
+ /**
24
+ * Deprecated since Core 6.3: roles are runtime-defined, so the selection has
25
+ * no effect on the installed artifacts. Validated for shape when present and
26
+ * echoed back for consumers that still read it; absent when not supplied.
27
+ */
28
+ agents?: {
24
29
  selected: string[];
25
30
  excluded?: string[];
26
31
  /** Legacy pre-models-section location, accepted for backward compatibility. */
@@ -32,7 +37,8 @@ interface InstallConfig {
32
37
  * Resolve a preset into provider-native identifiers. Kimi currently exposes no
33
38
  * Core-defined cost/capability tier mapping, so every named preset resolves to
34
39
  * its explicit `k3` default. User-supplied exact identifiers are handled later
35
- * and are never interpreted as Claude aliases.
40
+ * and are never interpreted as Claude aliases. No preset carries per-role
41
+ * overrides: Core ships no role ids, so `models.overrides` is Desktop's to fill.
36
42
  */
37
43
  export declare function resolveProviderModelConfig(provider: Provider, preset?: ModelPreset): InstallModelConfig;
38
44
  export declare class InvalidConfigError extends InstallerError {
@@ -2,6 +2,7 @@ import path from 'node:path';
2
2
  import yaml from 'js-yaml';
3
3
  import { FilesystemError, InstallerError } from '../util/errors.js';
4
4
  import { pathExists, readTextFile, writeFileLf } from '../util/fs.js';
5
+ import { warn } from '../util/logger.js';
5
6
  const PROVIDER_DEFAULT_MODELS = {
6
7
  claude: 'sonnet',
7
8
  codex: 'gpt-5.5-mini',
@@ -25,7 +26,8 @@ const SAFE_KIMI_MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._/:-]{0,127}$/;
25
26
  * Resolve a preset into provider-native identifiers. Kimi currently exposes no
26
27
  * Core-defined cost/capability tier mapping, so every named preset resolves to
27
28
  * its explicit `k3` default. User-supplied exact identifiers are handled later
28
- * and are never interpreted as Claude aliases.
29
+ * and are never interpreted as Claude aliases. No preset carries per-role
30
+ * overrides: Core ships no role ids, so `models.overrides` is Desktop's to fill.
29
31
  */
30
32
  export function resolveProviderModelConfig(provider, preset = 'balanced') {
31
33
  if (provider === 'claude') {
@@ -33,11 +35,7 @@ export function resolveProviderModelConfig(provider, preset = 'balanced') {
33
35
  return { preset, defaults: { model: 'haiku' }, overrides: {} };
34
36
  }
35
37
  if (preset === 'max') {
36
- return {
37
- preset,
38
- defaults: { model: 'sonnet' },
39
- overrides: { 'sr-architect': 'opus' },
40
- };
38
+ return { preset, defaults: { model: 'sonnet' }, overrides: {} };
41
39
  }
42
40
  }
43
41
  return {
@@ -109,13 +107,18 @@ export function validateInstallConfig(raw) {
109
107
  // NOTE: a legacy `tier` field (`full`/`quick`) may still be present in pre-v5
110
108
  // configs. It is intentionally ignored (not rejected and not carried forward)
111
109
  // — v5 has no install tiers.
110
+ // The `agents` section is OPTIONAL and deprecated: roles are runtime-defined
111
+ // since Core 6.3. When present it is still validated for shape (safe ids, no
112
+ // overlap, known preset) so a malformed file fails fast, and a non-empty
113
+ // selection logs one warning instead of being rejected — Desktop builds
114
+ // paired with an older Core still write it.
112
115
  const agents = doc.agents;
113
116
  let selectedAgents = [];
114
117
  let excludedAgents;
115
- if (!agents || typeof agents !== 'object') {
116
- errors.push(`missing required 'agents' section with 'selected' list`);
118
+ if (doc.agents !== undefined && (!agents || typeof agents !== 'object' || Array.isArray(agents))) {
119
+ errors.push(`'agents' must be a mapping`);
117
120
  }
118
- else {
121
+ else if (agents) {
119
122
  if (!Array.isArray(agents.selected)) {
120
123
  errors.push(`'agents.selected' must be a list`);
121
124
  }
@@ -182,6 +185,10 @@ export function validateInstallConfig(raw) {
182
185
  if (errors.length > 0) {
183
186
  throw new InvalidConfigError(errors);
184
187
  }
188
+ if (selectedAgents.length > 0) {
189
+ warn(`install-config.yaml 'agents.selected' is ignored since Core 6.3: roles are ` +
190
+ `runtime-defined and no role file is installed (${selectedAgents.join(', ')}).`);
191
+ }
185
192
  const preset = (modelPreset ?? agents?.preset ?? 'balanced');
186
193
  const resolvedModels = resolveProviderModelConfig(provider, preset);
187
194
  if (typeof modelDefaults?.model === 'string') {
@@ -193,18 +200,18 @@ export function validateInstallConfig(raw) {
193
200
  const result = {
194
201
  version: 1,
195
202
  provider,
196
- agents: {
197
- selected: selectedAgents,
198
- },
199
203
  models: resolvedModels,
200
204
  };
201
205
  // NOTE: a legacy `agent_teams` field may still be present in older configs.
202
206
  // It is intentionally ignored (not rejected) for backward compatibility.
203
- if (agents?.preset !== undefined) {
204
- result.agents.preset = agents.preset;
205
- }
206
- if (excludedAgents !== undefined) {
207
- result.agents.excluded = excludedAgents;
207
+ if (agents) {
208
+ result.agents = { selected: selectedAgents };
209
+ if (agents.preset !== undefined) {
210
+ result.agents.preset = agents.preset;
211
+ }
212
+ if (excludedAgents !== undefined) {
213
+ result.agents.excluded = excludedAgents;
214
+ }
208
215
  }
209
216
  return result;
210
217
  }
@@ -1,22 +1,4 @@
1
1
  import type { Provider } from './provider-detect.js';
2
- /**
3
- * The three baseline agents — the COMPLETE set of agents the installer
4
- * ships. The implement pipeline depends on all three. Any additional agent
5
- * comes from a user-authored profile (`custom-*`), never the installer.
6
- *
7
- * Mirrors the required built-in roles in schemas/agent-runtime.schema.json
8
- * (`agents`, with the sr- prefix in installed files); keep both in sync.
9
- */
10
- export declare const CORE_AGENTS: Set<string>;
11
- /**
12
- * Rewrite every literal `Skill("opsx:<id>"[, …])` call in a Claude-authored agent
13
- * body into the Gemini `activate_skill(name="…")` form. Positional skill input
14
- * (e.g. `"<specName>"`) is dropped because `activate_skill` takes only `name` and
15
- * the surrounding persona prose already carries the context. Unknown ids are left
16
- * untouched (better a visible stale ref than a silent `name="undefined"`). This
17
- * runs ONLY on the gemini render path; Claude/Codex keep the `Skill(...)` form.
18
- */
19
- export declare function translateOpsxSkillCallsForGemini(body: string): string;
20
2
  /**
21
3
  * Port shared Claude-authored prose to Kimi's directory-skill contract.
22
4
  *
@@ -28,7 +10,6 @@ export declare function translateOpsxSkillCallsForGemini(body: string): string;
28
10
  * This is render-only; canonical Claude/Codex/Gemini templates stay unchanged.
29
11
  */
30
12
  export declare function translateClaudeTextForKimi(body: string): string;
31
- export declare function geminiAgentLimitMetadata(env?: NodeJS.ProcessEnv): string[];
32
13
  /**
33
14
  * Phase 2 + Phase 3 of init:
34
15
  * - Detect prior installation state (.claude/.codex/openspec already present).
@@ -37,9 +18,11 @@ export declare function geminiAgentLimitMetadata(env?: NodeJS.ProcessEnv): strin
37
18
  * staging dir that placement copies from and `update` diffs against).
38
19
  * - Ensure `.gitignore` excludes the runtime artefacts.
39
20
  *
40
- * Placement (`placeArtefacts`) then copies the staged templates directly into
41
- * the user's live `.claude/agents/` and `.claude/commands/specrails/` dirs so
42
- * the installer finishes in one pass — no follow-up wizard required.
21
+ * Placement (`placeArtefacts`) then renders the staged command templates
22
+ * directly into the user's live provider dir (`.claude/commands/specrails/`,
23
+ * `.codex/skills/`, …) so the installer finishes in one pass — no follow-up
24
+ * wizard required. Roles are never placed: the programmatic runtime defines
25
+ * them (`src/agent-runtime/prompts.ts`).
43
26
  */
44
27
  interface ScaffoldInput {
45
28
  /** Absolute path to the specrails-core package (installed via npx). */
@@ -60,25 +43,6 @@ interface ScaffoldInput {
60
43
  provider: Provider;
61
44
  /** Derived directory name (`.claude` or `.codex`). */
62
45
  providerDir: string;
63
- /** Optional explicit allow-list used by config-driven installs. */
64
- selectedAgents?: string[];
65
- /**
66
- * When false, the static-placement helpers do NOT create the per-workspace
67
- * mutable seeds (agent-memory dirs, gemini headless acknowledgments). Used by
68
- * `installFramework` so the SHARED framework copy stays purely provider-static
69
- * — the project layer is seeded separately by `assembleProjectWorkspace`.
70
- * Defaults to true (legacy in-place behaviour for `scaffoldInstallation`).
71
- */
72
- seedProjectDirs?: boolean;
73
- /**
74
- * When true, place EVERY agent template (the full superset) regardless of
75
- * `selectedAgents`. Used by `installFramework` so
76
- * the SHARED framework store is a superset that ANY project's selection can
77
- * link from — per-project agent filtering then happens at the workspace LINK
78
- * step (`linkAgentFiles`), not at materialization. Defaults to false (legacy
79
- * selection-honouring placement for in-place `scaffoldInstallation`).
80
- */
81
- materializeAllAgents?: boolean;
82
46
  }
83
47
  interface ScaffoldResult {
84
48
  existingSetup: boolean;
@@ -106,8 +70,6 @@ interface InstallFrameworkInput {
106
70
  providerDir: string;
107
71
  /** Framework version (the `<version>/` segment). */
108
72
  version: string;
109
- /** Optional explicit agent allow-list (kept for parity with scaffold). */
110
- selectedAgents?: string[];
111
73
  }
112
74
  interface InstallFrameworkResult {
113
75
  /** `<frameworkDir>/<version>/<providerDir>` — root of the static subtree. */
@@ -130,8 +92,8 @@ export declare function frameworkMaterializationProblem(versionDir: string, vers
130
92
  * `<frameworkDir>/<version>/<providerDir>/` (+ `<version>/setup-templates/`).
131
93
  * Idempotent: when the providerDir already exists with a matching stamp it is a
132
94
  * no-op (the second workspace assemble re-uses the same copy). Writes NO
133
- * per-workspace state (no agent-memory, no acks, no project-named instruction
134
- * files) — those are seeded by `assembleProjectWorkspace`.
95
+ * per-workspace state (no project-named instruction files) — those are seeded
96
+ * by `assembleProjectWorkspace`.
135
97
  */
136
98
  export declare function installFramework(input: InstallFrameworkInput): InstallFrameworkResult;
137
99
  /**
@@ -150,26 +112,18 @@ interface AssembleProjectWorkspaceInput {
150
112
  providerDir: string;
151
113
  /** Framework version (used for the manifest record). */
152
114
  version: string;
153
- /** The user's real repo (drives PROJECT_NAME + gemini ack keying). */
115
+ /** The user's real repo (drives PROJECT_NAME). */
154
116
  codeRoot: string;
155
117
  /** specrails-core package dir (for the manifest hash sources). */
156
118
  scriptDir: string;
157
119
  /**
158
- * Optional agent allow-list. Drives BOTH which framework agents are LINKED
159
- * into the workspace (`linkAgentFiles`) AND which agent-memory dirs are seeded.
160
- * Undefined = the CORE trio only (the lean default). The SHARED framework store
161
- * is always the full superset; this is where per-project filtering happens.
162
- */
163
- selectedAgents?: string[];
164
- /**
165
- * When true, the static provider subtrees (`agents`/`commands`/`skills`/`rules`)
120
+ * When true, the static provider subtrees (`commands`/`skills`/`specrails`)
166
121
  * and the settings file are COPIED as real files from the framework store into
167
122
  * the workspace instead of SYMLINKED. Used by the in-repo standalone install
168
123
  * (`init`/`update` with `artifactRoot === codeRoot`) so the repo gets real,
169
124
  * committable files — a symlink into `$HOME/.specrails/framework` would be
170
125
  * invisible to a standalone user's `claude`/`codex`/`gemini`/`kimi` running in the
171
- * repo. The PROJECT layer (agent-memory, manifest, instruction files) is real
172
- * either way. Defaults to false (relocated workspaces symlink — the desktop /
126
+ * repo. The PROJECT layer (manifest, instruction files) is real either way. Defaults to false (relocated workspaces symlink — the desktop /
173
127
  * `--relocate` path).
174
128
  */
175
129
  copyStatics?: boolean;
@@ -177,34 +131,28 @@ interface AssembleProjectWorkspaceInput {
177
131
  interface AssembleProjectWorkspaceResult {
178
132
  /** Per-linked-subtree mechanism, for diagnostics (copy-fallback loses O(1) swap). */
179
133
  links: Record<string, 'symlink' | 'junction' | 'copy'>;
180
- /** Agent ids whose memory dirs were seeded as real writable dirs. */
181
- seededMemoryAgents: string[];
182
134
  }
183
135
  /**
184
136
  * Assemble a project workspace with NO network and NO re-materialization: (a)
185
- * SYMLINK the static providerDir subtrees from `<frameworkDir>/current/
186
- * <providerDir>/` into `<workspace>/<providerDir>/`, then (b) seed the PROJECT
187
- * layer as real writable files (agent-memory dirs, the manifest, project-named
188
- * instruction/settings files, gemini headless acks re-hashed against the LINKED
189
- * files). `agent-memory/` is NEVER linked.
137
+ * prune the framework-owned `sr-*` role artifacts an older Core linked or
138
+ * copied into the workspace, (b) SYMLINK the static providerDir subtrees from
139
+ * `<frameworkDir>/current/<providerDir>/` into `<workspace>/<providerDir>/`,
140
+ * then (c) seed the PROJECT layer as real writable files (the manifest,
141
+ * project-named instruction/settings files). Reserved `custom-*` files are
142
+ * never touched.
190
143
  */
191
144
  export declare function assembleProjectWorkspace(input: AssembleProjectWorkspaceInput): AssembleProjectWorkspaceResult;
192
145
  /**
193
- * Pre-acknowledge the generated gemini subagents so they load in HEADLESS
194
- * (`gemini -p`) runs. gemini 0.46+ DISCOVERS `.gemini/agents/*.md` but only
195
- * ENABLES a project's custom agents after the interactive "New Agents Discovered
196
- * → Acknowledge and Enable" prompt — which never fires headless, so
197
- * `invoke_agent sr-architect` returns "Subagent not found" and the implement
198
- * orchestrator silently falls back to a generic agent (the specialised personas
199
- * never run, the pipeline degrades). The acknowledgment is a user-global file
200
- * `~/.gemini/acknowledgments/agents.json` shaped
201
- * `{ [projectRoot]: { [agentName]: <sha256-hex of the agent .md file> } }`
202
- * (hash algorithm verified empirically against gemini 0.47 = sha256 of the full
203
- * file). Writing it at install time makes the freshly-generated agents trusted
204
- * with no prompt, for both `gemini` CLI and the desktop's headless spawns. The
205
- * file is MERGED — other projects' and other agents' entries are preserved.
206
- * Best-effort: any failure is swallowed by the caller (agents still work once
207
- * acknowledged interactively).
146
+ * Remove the role artifacts an older Core linked or copied into a workspace:
147
+ * - claude, gemini: `agents/sr-*.md` (symlink or copy-fallback regular file)
148
+ * - codex: `skills/rails/sr-*` and the `rails/` container once empty
149
+ * - kimi: `skills/sr-*`
150
+ *
151
+ * Keyed strictly by NAME, never by content: `custom-*` (the reserved
152
+ * user/Desktop region) and any unknown name are left byte-untouched. A
153
+ * subtree that is itself a symlink into the shared framework store is skipped —
154
+ * the store is Core-owned and rebuilt by `installFramework`, so pruning through
155
+ * the link would only reach into another version's files.
208
156
  */
209
- export declare function writeGeminiAgentAcknowledgments(repoRoot: string, agentIds: string[], agentsBaseDir?: string): void;
157
+ export declare function pruneStaleRoleArtifacts(workspaceProviderDir: string, provider: Provider): void;
210
158
  export {};