@yemi33/minions 0.1.2155 → 0.1.2157
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.
|
@@ -885,8 +885,26 @@ function viewAgentOutput(logPath) {
|
|
|
885
885
|
function openInboxNote(filename) {
|
|
886
886
|
var idx = (inboxData || []).findIndex(function(item) { return item.name === filename; });
|
|
887
887
|
if (idx >= 0) { openModal(idx); return; }
|
|
888
|
-
|
|
889
|
-
|
|
888
|
+
// Not in the live inbox — it's an archived note (the common case for a
|
|
889
|
+
// completed work item's note pill, which the rewriter re-encodes as an
|
|
890
|
+
// 'archive:<name>' token). Fetch it from notes/archive/ and show it in the
|
|
891
|
+
// modal instead of bouncing to the inbox page. Falls back to the inbox page
|
|
892
|
+
// only if the file is genuinely gone (e.g. merged into notes.md).
|
|
893
|
+
fetch('/state/notes/archive/' + encodeURIComponent(filename))
|
|
894
|
+
.then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); })
|
|
895
|
+
.then(function(content) {
|
|
896
|
+
var titleEl = document.getElementById('modal-title');
|
|
897
|
+
var bodyEl = document.getElementById('modal-body');
|
|
898
|
+
if (!titleEl || !bodyEl) return;
|
|
899
|
+
titleEl.textContent = filename + ' (archived)';
|
|
900
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() escapes note content before assembling HTML; filename is set via textContent
|
|
901
|
+
bodyEl.innerHTML = '<div style="font-size:var(--text-md);line-height:1.7;color:var(--muted)">' + renderMd(content) + '</div>';
|
|
902
|
+
document.getElementById('modal').classList.add('open');
|
|
903
|
+
})
|
|
904
|
+
.catch(function() {
|
|
905
|
+
closeModal();
|
|
906
|
+
switchPage('inbox');
|
|
907
|
+
});
|
|
890
908
|
}
|
|
891
909
|
|
|
892
910
|
if (typeof window !== 'undefined') {
|
package/engine/cli.js
CHANGED
|
@@ -580,6 +580,21 @@ const commands = {
|
|
|
580
580
|
}
|
|
581
581
|
} catch (err) { e.log('warn', `cc worker pool force-on migration failed: ${err.message}`); }
|
|
582
582
|
|
|
583
|
+
// Repair work-item note links that broke before the rewriter covered
|
|
584
|
+
// completion-report artifact links (W-mq1j85cj00055a8f). Notes the engine
|
|
585
|
+
// auto-archived back then left the WI pill pointing at the gone
|
|
586
|
+
// notes/inbox/<name> path; this repoints them to the archived/KB location.
|
|
587
|
+
// Idempotent (only touches provably-broken + resolvable links) and routed
|
|
588
|
+
// through mutateWorkItems so SQL + JSON mirror stay consistent — a no-op
|
|
589
|
+
// once healed, so it's safe to run on every boot.
|
|
590
|
+
try {
|
|
591
|
+
const res = require('./note-link-backfill').runNoteLinkBackfill({ config });
|
|
592
|
+
if (res.fixedLinks > 0) {
|
|
593
|
+
e.log('info', `Repointed ${res.fixedLinks} broken note link(s) across ${res.fixedItems} work item(s)`);
|
|
594
|
+
console.log(` Repaired ${res.fixedLinks} broken note link(s) in ${res.fixedItems} work item(s).`);
|
|
595
|
+
}
|
|
596
|
+
} catch (err) { e.log('warn', `note-link backfill failed: ${err.message}`); }
|
|
597
|
+
|
|
583
598
|
// Auto-heal projects missing workSources (cloned-repo / hand-rolled-config
|
|
584
599
|
// footgun): without this block, discoverFromWorkItems / discoverFromPrs
|
|
585
600
|
// bail silently and the engine looks healthy but never dispatches. The
|
|
@@ -1,31 +1,15 @@
|
|
|
1
1
|
// engine/db/migrations/013-backfill-broken-note-links.js
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// and repoints them. Best-effort and self-isolating — wrapped so a failure can
|
|
11
|
-
// never wedge boot or roll back the migration batch.
|
|
3
|
+
// No-op marker. The one-time repair of broken inbox note links was moved to an
|
|
4
|
+
// idempotent engine-boot sweep (engine/cli.js → note-link-backfill.runNoteLink
|
|
5
|
+
// Backfill) because it must go through shared.mutateWorkItems to keep the SQL
|
|
6
|
+
// store and its JSON mirror consistent — a raw in-migration UPDATE left the
|
|
7
|
+
// mirror diverged and could be reverted by the store's resync. This migration
|
|
8
|
+
// stays so schema_version progression is identical across installs that already
|
|
9
|
+
// recorded v13.
|
|
12
10
|
|
|
13
11
|
module.exports = {
|
|
14
12
|
version: 13,
|
|
15
|
-
description: '
|
|
16
|
-
up(
|
|
17
|
-
try {
|
|
18
|
-
const { backfillBrokenNoteLinks } = require('../../note-link-backfill');
|
|
19
|
-
const res = backfillBrokenNoteLinks(db);
|
|
20
|
-
if (res && res.fixedLinks > 0) {
|
|
21
|
-
// eslint-disable-next-line no-console -- migrate.js logs progress to stdout
|
|
22
|
-
console.log(`[db-migrate] v13: repointed ${res.fixedLinks} broken note link(s) across ${res.fixedItems} work item(s) (${res.scanned} scanned)`);
|
|
23
|
-
}
|
|
24
|
-
} catch (e) {
|
|
25
|
-
// One-time cosmetic repair — never block boot. Record the version anyway
|
|
26
|
-
// so it doesn't retry forever against the same un-repairable state.
|
|
27
|
-
// eslint-disable-next-line no-console -- surface the skip without throwing
|
|
28
|
-
console.warn(`[db-migrate] v13: note-link backfill skipped: ${e && e.message ? e.message : e}`);
|
|
29
|
-
}
|
|
30
|
-
},
|
|
13
|
+
description: 'reserved (note-link backfill moved to an idempotent engine-boot sweep)',
|
|
14
|
+
up() { /* intentionally empty — see header */ },
|
|
31
15
|
};
|
|
@@ -18,11 +18,10 @@
|
|
|
18
18
|
// notes.md) are left untouched. Idempotent: a second pass finds nothing because
|
|
19
19
|
// the repaired links no longer reference `notes/inbox/`.
|
|
20
20
|
//
|
|
21
|
-
// Wired as
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// passive mirror here would only risk dropping rows for no benefit.
|
|
21
|
+
// Wired as an idempotent engine-boot sweep (engine/cli.js, alongside the other
|
|
22
|
+
// one-shot boot migrations). It runs through shared.mutateWorkItems — the same
|
|
23
|
+
// write path the live rewriter uses — so the canonical SQL store and its JSON
|
|
24
|
+
// mirror stay consistent, and it's a no-op once every link is healed.
|
|
26
25
|
|
|
27
26
|
'use strict';
|
|
28
27
|
|
|
@@ -149,10 +148,13 @@ function repointItemNoteLinks(item, resolve) {
|
|
|
149
148
|
return fixed;
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
151
|
+
// Repair broken note links across every work-items file (each project's plus
|
|
152
|
+
// the central one), routed through shared.mutateWorkItems so the canonical SQL
|
|
153
|
+
// store AND its JSON mirror stay consistent — the same write path the live
|
|
154
|
+
// rewriter uses. Idempotent: only touches links that are provably broken AND
|
|
155
|
+
// resolvable, so once healed a re-run writes nothing (skipWriteIfUnchanged).
|
|
156
|
+
// Safe to call on every engine boot. Per-file try/catch isolates a bad scope.
|
|
157
|
+
function runNoteLinkBackfill(opts = {}) {
|
|
156
158
|
const shared = require('./shared');
|
|
157
159
|
const minionsDir = opts.minionsDir || shared.MINIONS_DIR;
|
|
158
160
|
const resolve = buildNoteResolver({
|
|
@@ -162,33 +164,34 @@ function backfillBrokenNoteLinks(db, opts = {}) {
|
|
|
162
164
|
kbCategories: shared.KB_CATEGORIES,
|
|
163
165
|
});
|
|
164
166
|
|
|
165
|
-
let
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return { scanned: 0, fixedItems: 0, fixedLinks: 0 };
|
|
167
|
+
let config = opts.config;
|
|
168
|
+
if (!config) {
|
|
169
|
+
try { config = JSON.parse(fs.readFileSync(path.join(minionsDir, 'config.json'), 'utf8')); }
|
|
170
|
+
catch { config = {}; }
|
|
170
171
|
}
|
|
172
|
+
const projects = Array.isArray(config.projects) ? config.projects : [];
|
|
173
|
+
const candidates = [];
|
|
174
|
+
for (const p of projects) {
|
|
175
|
+
if (p && p.name) candidates.push(path.join(minionsDir, 'projects', p.name, 'work-items.json'));
|
|
176
|
+
}
|
|
177
|
+
candidates.push(path.join(minionsDir, 'work-items.json'));
|
|
171
178
|
|
|
172
|
-
const now = opts.now || Date.now();
|
|
173
|
-
const upd = db.prepare('UPDATE work_items SET data = ?, updated_at = ? WHERE scope = ? AND id = ?');
|
|
174
179
|
let scanned = 0, fixedItems = 0, fixedLinks = 0;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
upd.run(JSON.stringify(item), now, scope, item.id);
|
|
187
|
-
fixedItems++;
|
|
188
|
-
fixedLinks += n;
|
|
180
|
+
for (const wiPath of candidates) {
|
|
181
|
+
try {
|
|
182
|
+
shared.mutateWorkItems(wiPath, (items) => {
|
|
183
|
+
if (!Array.isArray(items)) return items;
|
|
184
|
+
for (const item of items) {
|
|
185
|
+
if (!item) continue;
|
|
186
|
+
scanned++;
|
|
187
|
+
try {
|
|
188
|
+
const n = repointItemNoteLinks(item, resolve);
|
|
189
|
+
if (n > 0) { fixedItems++; fixedLinks += n; }
|
|
190
|
+
} catch { /* skip unfixable item */ }
|
|
189
191
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
return items;
|
|
193
|
+
});
|
|
194
|
+
} catch { /* per-file isolation — one bad scope can't block the others */ }
|
|
192
195
|
}
|
|
193
196
|
|
|
194
197
|
return { scanned, fixedItems, fixedLinks };
|
|
@@ -198,5 +201,5 @@ module.exports = {
|
|
|
198
201
|
inboxNameFromRef,
|
|
199
202
|
buildNoteResolver,
|
|
200
203
|
repointItemNoteLinks,
|
|
201
|
-
|
|
204
|
+
runNoteLinkBackfill,
|
|
202
205
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2157",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|