diff --git a/dashboard.html b/dashboard.html
index a371b4a..c186ec8 100644
--- a/dashboard.html
+++ b/dashboard.html
@@ -66,8 +66,16 @@
.event-agent { color: var(--purple); min-width: 60px; }
.event-action { color: var(--accent); }
- .kb-list { display: flex; flex-wrap: wrap; gap: 8px; }
- .kb-tag { background: var(--border); color: var(--text); padding: 4px 12px; border-radius: 16px; font-size: 13px; }
+ .kb-list { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
+ .kb-tag { background: var(--border); color: var(--text); padding: 4px 12px; border-radius: 16px; font-size: 13px; cursor: pointer; border: 1px solid transparent; transition: border-color 0.2s; }
+ .kb-tag:hover { border-color: var(--accent); }
+ .kb-tag.active { border-color: var(--accent); background: rgba(88,166,255,0.15); }
+ .kb-viewer { margin-top: 8px; }
+ .kb-content { background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 16px; font-size: 13px; line-height: 1.7; white-space: pre-wrap; font-family: monospace; max-height: 400px; overflow-y: auto; }
+ .kb-content h1, .kb-content h2, .kb-content h3 { font-family: inherit; margin: 8px 0 4px; color: var(--accent); }
+ .kb-toolbar { display: flex; gap: 6px; margin-top: 8px; }
+ .kb-editor { width: 100%; min-height: 250px; background: var(--bg); color: var(--text); border: 1px solid var(--border); padding: 12px; border-radius: 6px; font-size: 13px; font-family: monospace; line-height: 1.6; resize: vertical; }
+ .kb-editor:focus { border-color: var(--accent); outline: none; }
.lock-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; font-size: 13px; border-bottom: 1px solid var(--border); }
.lock-row:last-child { border-bottom: none; }
@@ -184,10 +192,25 @@
-
-
+
+
+
+
+
+
New Knowledge Entry
+
+
+
+
+
+
+
+
+
+
diff --git a/orchestrator.js b/orchestrator.js
index 0749445..9faeb32 100644
--- a/orchestrator.js
+++ b/orchestrator.js
@@ -281,6 +281,14 @@ function readKnowledge(topic) {
return fs.readFileSync(filePath, 'utf8');
}
+function deleteKnowledge(topic, agentId = 'system') {
+ const filePath = path.join(KNOWLEDGE_DIR, `${topic}.md`);
+ if (!fs.existsSync(filePath)) return false;
+ fs.unlinkSync(filePath);
+ logEvent(agentId, 'knowledge_deleted', { topic });
+ return true;
+}
+
function listKnowledge() {
return fs.readdirSync(KNOWLEDGE_DIR)
.filter(f => f.endsWith('.md'))
@@ -375,6 +383,7 @@ module.exports = {
releaseLock,
writeKnowledge,
readKnowledge,
+ deleteKnowledge,
listKnowledge,
status,
statusText
diff --git a/server.js b/server.js
index 665c7ef..9a4c517 100644
--- a/server.js
+++ b/server.js
@@ -71,6 +71,11 @@ const server = http.createServer(async (req, res) => {
return res.end(html);
}
+ if (pathname === '/favicon.ico') {
+ res.writeHead(204);
+ return res.end();
+ }
+
// --- API: Status ---
if (pathname === '/api/status' && method === 'GET') {
return json(res, orc.status());
@@ -204,6 +209,13 @@ const server = http.createServer(async (req, res) => {
return json(res, { ok: true });
}
+ if (kbMatch && method === 'DELETE') {
+ const topic = decodeURIComponent(kbMatch[1]);
+ const deleted = orc.deleteKnowledge(topic, 'dashboard');
+ if (!deleted) return error(res, `Topic "${topic}" not found`, 404);
+ return json(res, { ok: true });
+ }
+
// --- API: Locks ---
if (pathname === '/api/locks' && method === 'GET') {
const state = orc.loadState();
diff --git a/test.js b/test.js
index d07342f..4c3fe00 100644
--- a/test.js
+++ b/test.js
@@ -126,6 +126,11 @@ test('Knowledge base', () => {
const topics = orc.listKnowledge();
assert(topics.includes('test-topic'), 'Topic listed');
+
+ const deleted = orc.deleteKnowledge('test-topic');
+ assert(deleted === true, 'Topic deleted');
+ assert(orc.readKnowledge('test-topic') === null, 'Deleted topic returns null');
+ assert(orc.deleteKnowledge('nonexistent') === false, 'Delete nonexistent returns false');
});
test('Event log', () => {