add header rescan button to apply officer-items changes without restart

New POST /api/rescan re-runs the boot item setup (ensureItemDirs +
ensureToolLoader) and returns live item counts; the header button calls it
and invalidates the item query caches so the UI refetches from disk.

Also fix ensure-tool-loader to write into OFFICER_ITEMS_DIR/extensions (the
runtime read path) instead of the now-unread DATA_PATH/extensions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 01:31:51 +00:00
co-authored by Claude Opus 4.8
parent 86a547b216
commit b9eb78c919
5 changed files with 89 additions and 2 deletions
+39
View File
@@ -0,0 +1,39 @@
import { readdirSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { createRouter } from '../../create-router';
import { ITEM_TYPES, itemsDir, ensureItemDirs, type ItemType } from '../../data-path';
import { ensureToolLoader } from '../../ensure-tool-loader';
// Re-applies the boot-time item setup so changes under OFFICER_ITEMS_DIR take effect without a
// server restart. Item lists are already read fresh from disk on every request; this ensures the
// type dirs exist and the tool-loader extension is current, and reports the live item counts so the
// UI can refetch.
const CANONICAL: Record<ItemType, string> = {
skills: 'SKILL.md',
tools: 'TOOL.md',
tasks: 'TASK.md',
processes: 'PROCESS.md',
extensions: 'index.ts',
};
function countItems(type: ItemType): number {
const dir = itemsDir(type);
if (!existsSync(dir)) return 0;
let count = 0;
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (entry.isDirectory() && existsSync(join(dir, entry.name, CANONICAL[type]))) count++;
}
return count;
}
export const rescanRouter = createRouter();
rescanRouter.post('/', async (ctx) => {
ensureItemDirs();
ensureToolLoader();
const counts = Object.fromEntries(ITEM_TYPES.map((type) => [type, countItems(type)])) as Record<ItemType, number>;
return ctx.json({ ok: true, counts });
});