feat: backfill Claude skills when useClaudeCli toggle flips on

PUT /api/settings/global now fires an onUseClaudeCliToggled hook on an
actual transition so the UI toggle has immediate effect — serve/daemon/
dashboard wire it to ensureClaudeSkillsForAllProjectsOnStartup so every
registered project picks up .claude/skills/fusion without a restart.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-23 16:13:16 -07:00
parent 51870ed27b
commit e8895c76b8
4 changed files with 92 additions and 25 deletions

View File

@@ -460,6 +460,22 @@ export async function runDaemon(opts: DaemonOptions = {}) {
onProjectRegistered: ({ path }) => {
maybeInstallClaudeSkillForNewProject(path);
},
onUseClaudeCliToggled: (_prev, next) => {
if (!next) return;
void (async () => {
try {
if (!sharedCentralCore) return;
const projects = await sharedCentralCore.listProjects();
ensureClaudeSkillsForAllProjectsOnStartup(
projects.map((p) => ({ id: p.id, name: p.name, path: p.path })),
);
} catch (err) {
console.warn(
`[fusion] Claude skill backfill on toggle failed: ${err instanceof Error ? err.message : String(err)}`,
);
}
})();
},
headless: true,
daemon: { token: daemonToken },
skillsAdapter,

View File

@@ -1000,6 +1000,23 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
onProjectRegistered: ({ path }) => {
maybeInstallClaudeSkillForNewProject(path);
},
onUseClaudeCliToggled: (_prev, next) => {
if (!next) return;
void (async () => {
try {
if (!centralCoreForEngine) return;
const projects = await centralCoreForEngine.listProjects();
ensureClaudeSkillsForAllProjectsOnStartup(
projects.map((p) => ({ id: p.id, name: p.name, path: p.path })),
);
} catch (err) {
logSink.log(
`Claude skill backfill on toggle failed: ${err instanceof Error ? err.message : String(err)}`,
"engine",
);
}
})();
},
skillsAdapter,
https: loadTlsCredentialsFromEnv(),
daemon: dashboardAuthToken ? { token: dashboardAuthToken } : undefined,
@@ -1191,6 +1208,23 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
onProjectRegistered: ({ path }) => {
maybeInstallClaudeSkillForNewProject(path);
},
onUseClaudeCliToggled: (_prev, next) => {
if (!next) return;
void (async () => {
try {
if (!centralCoreForMesh) return;
const projects = await centralCoreForMesh.listProjects();
ensureClaudeSkillsForAllProjectsOnStartup(
projects.map((p) => ({ id: p.id, name: p.name, path: p.path })),
);
} catch (err) {
logSink.log(
`Claude skill backfill on toggle failed: ${err instanceof Error ? err.message : String(err)}`,
"engine",
);
}
})();
},
skillsAdapter,
https: loadTlsCredentialsFromEnv(),
daemon: dashboardAuthToken ? { token: dashboardAuthToken } : undefined,

View File

@@ -621,6 +621,22 @@ export async function runServe(
// is configured. The runner logs its own outcome and swallows errors.
maybeInstallClaudeSkillForNewProject(path);
},
onUseClaudeCliToggled: (_prev, next) => {
if (!next) return; // Toggle-off leaves existing skill symlinks alone.
void (async () => {
try {
if (!sharedCentralCore) return;
const projects = await sharedCentralCore.listProjects();
ensureClaudeSkillsForAllProjectsOnStartup(
projects.map((p) => ({ id: p.id, name: p.name, path: p.path })),
);
} catch (err) {
console.warn(
`[fusion] Claude skill backfill on toggle failed: ${err instanceof Error ? err.message : String(err)}`,
);
}
})();
},
headless: true,
skillsAdapter,
daemon: daemonToken ? { token: daemonToken } : undefined,