@automagik/genie 4.260331.6 → 4.260331.8
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/dist/genie.js +134 -253
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/genie/.claude-plugin/plugin.json +1 -1
- package/plugins/genie/package.json +1 -1
- package/src/db/migrations/015_agent_archived_state.sql +0 -20
- package/src/db/migrations/018_drop_app_store.sql +4 -0
- package/src/genie.ts +0 -11
- package/src/lib/agent-sync.ts +55 -194
- package/src/lib/export-format.ts +1 -12
- package/src/lib/import-order.ts +1 -11
- package/src/lib/task-service.ts +4 -0
- package/src/term-commands/agent/directory.ts +2 -37
- package/src/term-commands/dir.ts +3 -68
- package/src/term-commands/export.ts +0 -13
- package/src/term-commands/team.ts +14 -11
- package/src/lib/agent-cache.ts +0 -282
- package/src/lib/manifest.ts +0 -342
- package/src/term-commands/install.ts +0 -372
- package/src/term-commands/item-uninstall.ts +0 -118
- package/src/term-commands/item-update.ts +0 -205
- package/src/term-commands/publish.ts +0 -187
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.260331.
|
|
3
|
+
"version": "4.260331.8",
|
|
4
4
|
"description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Namastex Labs"
|
|
@@ -33,26 +33,6 @@ ALTER TABLE agents ADD CONSTRAINT agents_state_check
|
|
|
33
33
|
'done', 'error', 'suspended', 'archived'
|
|
34
34
|
));
|
|
35
35
|
|
|
36
|
-
-- ============================================================================
|
|
37
|
-
-- App store: add 'archived' to approval_status CHECK constraint
|
|
38
|
-
-- ============================================================================
|
|
39
|
-
|
|
40
|
-
DO $$
|
|
41
|
-
BEGIN
|
|
42
|
-
EXECUTE (
|
|
43
|
-
SELECT string_agg('ALTER TABLE app_store DROP CONSTRAINT ' || quote_ident(conname), '; ')
|
|
44
|
-
FROM pg_constraint
|
|
45
|
-
WHERE conrelid = 'app_store'::regclass
|
|
46
|
-
AND contype = 'c'
|
|
47
|
-
AND pg_get_constraintdef(oid) LIKE '%approval_status%'
|
|
48
|
-
);
|
|
49
|
-
EXCEPTION WHEN OTHERS THEN
|
|
50
|
-
NULL;
|
|
51
|
-
END $$;
|
|
52
|
-
|
|
53
|
-
ALTER TABLE app_store ADD CONSTRAINT app_store_approval_status_check
|
|
54
|
-
CHECK (approval_status IN ('local', 'pending', 'approved', 'rejected', 'archived'));
|
|
55
|
-
|
|
56
36
|
-- ============================================================================
|
|
57
37
|
-- Index: fast lookup for non-archived agents in directory listings
|
|
58
38
|
-- ============================================================================
|
package/src/genie.ts
CHANGED
|
@@ -53,16 +53,12 @@ import { registerExportCommands } from './term-commands/export.js';
|
|
|
53
53
|
import * as historyCmd from './term-commands/history.js';
|
|
54
54
|
import { registerImportCommands } from './term-commands/import.js';
|
|
55
55
|
import { registerInitCommands } from './term-commands/init.js';
|
|
56
|
-
import { registerInstallCommand } from './term-commands/install.js';
|
|
57
|
-
import { registerItemUninstallCommand } from './term-commands/item-uninstall.js';
|
|
58
|
-
import { registerItemUpdateCommand } from './term-commands/item-update.js';
|
|
59
56
|
import { type LogOptions, logCommand } from './term-commands/log.js';
|
|
60
57
|
import { registerMetricsCommands } from './term-commands/metrics.js';
|
|
61
58
|
import { registerSendInboxCommands } from './term-commands/msg.js';
|
|
62
59
|
import { registerNotifyCommands } from './term-commands/notify.js';
|
|
63
60
|
import * as orchestrateCmd from './term-commands/orchestrate.js';
|
|
64
61
|
import { registerProjectCommands } from './term-commands/project.js';
|
|
65
|
-
import { registerPublishCommand } from './term-commands/publish.js';
|
|
66
62
|
import {
|
|
67
63
|
type QaCheckOptions,
|
|
68
64
|
type QaOptions,
|
|
@@ -219,13 +215,6 @@ registerImportCommands(program);
|
|
|
219
215
|
registerTemplateCommands(program);
|
|
220
216
|
registerBriefCommands(program);
|
|
221
217
|
|
|
222
|
-
// Item registry commands — install, publish (top-level), item uninstall/update (namespaced)
|
|
223
|
-
registerInstallCommand(program);
|
|
224
|
-
registerPublishCommand(program);
|
|
225
|
-
const itemCmd = program.command('item').description('Item registry management');
|
|
226
|
-
registerItemUninstallCommand(itemCmd);
|
|
227
|
-
registerItemUpdateCommand(itemCmd);
|
|
228
|
-
|
|
229
218
|
// ============================================================================
|
|
230
219
|
// CLI audit hooks — record every command execution to audit_events
|
|
231
220
|
// ============================================================================
|
package/src/lib/agent-sync.ts
CHANGED
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Scans {workspace}/agents/ for directories containing AGENTS.md (the discovery marker).
|
|
5
5
|
* For each found agent, reads the git remote to derive repo_path, then registers or
|
|
6
|
-
* updates the entry in the
|
|
6
|
+
* updates the entry in the agent directory.
|
|
7
7
|
*
|
|
8
8
|
* Handles lifecycle:
|
|
9
|
-
* - New agent dir → register in
|
|
9
|
+
* - New agent dir → register in agent directory
|
|
10
10
|
* - Existing agent with stale data → update
|
|
11
|
-
* -
|
|
12
|
-
* - Missing agent dir → archive (never delete — preserves history)
|
|
11
|
+
* - Missing agent dir → remove
|
|
13
12
|
*
|
|
14
13
|
* Used by:
|
|
15
14
|
* - `genie serve` (startup sync + file watcher)
|
|
@@ -20,13 +19,7 @@
|
|
|
20
19
|
import { execSync } from 'node:child_process';
|
|
21
20
|
import { existsSync, watch as fsWatch, readdirSync, realpathSync } from 'node:fs';
|
|
22
21
|
import { join } from 'node:path';
|
|
23
|
-
import
|
|
24
|
-
getItemFromStore,
|
|
25
|
-
listItemsFromStore,
|
|
26
|
-
regenerateAgentCache,
|
|
27
|
-
registerItemInStore,
|
|
28
|
-
updateItemInStore,
|
|
29
|
-
} from './agent-cache.js';
|
|
22
|
+
import * as directory from './agent-directory.js';
|
|
30
23
|
|
|
31
24
|
// ============================================================================
|
|
32
25
|
// Types
|
|
@@ -128,98 +121,6 @@ function discoverSingleAgent(workspaceRoot: string, agentName: string): AgentInf
|
|
|
128
121
|
};
|
|
129
122
|
}
|
|
130
123
|
|
|
131
|
-
// ============================================================================
|
|
132
|
-
// Archive / Reactivation helpers
|
|
133
|
-
// ============================================================================
|
|
134
|
-
|
|
135
|
-
/** Archive an agent in both agents table and app_store. Never deletes rows. */
|
|
136
|
-
async function archiveAgent(name: string): Promise<boolean> {
|
|
137
|
-
let archived = false;
|
|
138
|
-
try {
|
|
139
|
-
const { getConnection } = await import('./db.js');
|
|
140
|
-
const sql = await getConnection();
|
|
141
|
-
// Archive in agents table (set state='archived')
|
|
142
|
-
const result = await sql`
|
|
143
|
-
UPDATE agents SET state = 'archived', updated_at = now()
|
|
144
|
-
WHERE (custom_name = ${name} OR role = ${name})
|
|
145
|
-
AND (state IS NULL OR state != 'archived')
|
|
146
|
-
`;
|
|
147
|
-
if (result.count > 0) archived = true;
|
|
148
|
-
} catch {
|
|
149
|
-
// DB may not be available
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Archive in app_store (set approval_status='archived')
|
|
153
|
-
try {
|
|
154
|
-
const existing = await getItemFromStore(name).catch(() => null);
|
|
155
|
-
if (existing && existing.approval_status !== 'archived') {
|
|
156
|
-
await updateItemInStore(name, {
|
|
157
|
-
manifest: {
|
|
158
|
-
...(existing.manifest as Record<string, unknown>),
|
|
159
|
-
archived: true,
|
|
160
|
-
archivedAt: new Date().toISOString(),
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
archived = true;
|
|
164
|
-
}
|
|
165
|
-
} catch {
|
|
166
|
-
// Best-effort
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return archived;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** Reactivate an archived agent with full backfill. */
|
|
173
|
-
async function reactivateAgent(agent: AgentInfo): Promise<void> {
|
|
174
|
-
const orgRepo = agent.repoUrl ? extractOrgRepo(agent.repoUrl) : null;
|
|
175
|
-
const repoPath = orgRepo ?? agent.repoUrl ?? agent.dir;
|
|
176
|
-
|
|
177
|
-
// Update app_store entry — clear archived flag, refresh paths
|
|
178
|
-
const existing = await getItemFromStore(agent.name).catch(() => null);
|
|
179
|
-
if (existing) {
|
|
180
|
-
const manifest = { ...(existing.manifest as Record<string, unknown>) };
|
|
181
|
-
manifest.archived = undefined;
|
|
182
|
-
manifest.archivedAt = undefined;
|
|
183
|
-
manifest.repo = repoPath;
|
|
184
|
-
manifest.productRepo = agent.productRepo;
|
|
185
|
-
manifest.source = 'auto-sync';
|
|
186
|
-
await updateItemInStore(agent.name, {
|
|
187
|
-
installPath: agent.dir,
|
|
188
|
-
gitUrl: agent.repoUrl ?? undefined,
|
|
189
|
-
manifest,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Reactivate in agents table
|
|
194
|
-
try {
|
|
195
|
-
const { getConnection } = await import('./db.js');
|
|
196
|
-
const sql = await getConnection();
|
|
197
|
-
await sql`
|
|
198
|
-
UPDATE agents SET state = 'idle', repo_path = ${repoPath}, updated_at = now()
|
|
199
|
-
WHERE (custom_name = ${agent.name} OR role = ${agent.name})
|
|
200
|
-
AND state = 'archived'
|
|
201
|
-
`;
|
|
202
|
-
} catch {
|
|
203
|
-
// DB may not be available
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Trigger session backfill for conversation history recovery
|
|
207
|
-
await triggerSessionBackfill(agent).catch(() => {});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/** Trigger session backfill for a reactivated agent. */
|
|
211
|
-
async function triggerSessionBackfill(_agent: AgentInfo): Promise<void> {
|
|
212
|
-
try {
|
|
213
|
-
const { getConnection, isAvailable } = await import('./db.js');
|
|
214
|
-
if (!(await isAvailable())) return;
|
|
215
|
-
const sql = await getConnection();
|
|
216
|
-
const { startBackfill } = await import('./session-backfill.js');
|
|
217
|
-
await startBackfill(sql);
|
|
218
|
-
} catch {
|
|
219
|
-
// Best-effort — backfill may not be ready
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
124
|
// ============================================================================
|
|
224
125
|
// Sync
|
|
225
126
|
// ============================================================================
|
|
@@ -227,9 +128,8 @@ async function triggerSessionBackfill(_agent: AgentInfo): Promise<void> {
|
|
|
227
128
|
/**
|
|
228
129
|
* Sync all agents from {workspaceRoot}/agents/ into the directory.
|
|
229
130
|
* - New agents → registered
|
|
230
|
-
* - Existing agents with stale
|
|
231
|
-
* -
|
|
232
|
-
* - Agents in store whose dirs are gone → archived
|
|
131
|
+
* - Existing agents with stale data → updated
|
|
132
|
+
* - Agents whose dirs are gone → removed
|
|
233
133
|
*
|
|
234
134
|
* Idempotent — safe to run repeatedly.
|
|
235
135
|
*/
|
|
@@ -249,15 +149,8 @@ export async function syncAgentDirectory(workspaceRoot: string): Promise<SyncRes
|
|
|
249
149
|
}
|
|
250
150
|
}
|
|
251
151
|
|
|
252
|
-
//
|
|
253
|
-
await
|
|
254
|
-
|
|
255
|
-
// Regenerate cache after mutations
|
|
256
|
-
const hasMutations =
|
|
257
|
-
result.registered.length + result.updated.length + result.archived.length + result.reactivated.length;
|
|
258
|
-
if (hasMutations > 0) {
|
|
259
|
-
await regenerateAgentCache().catch(() => {});
|
|
260
|
-
}
|
|
152
|
+
// Remove agents whose dirs no longer exist
|
|
153
|
+
await removeMissingAgents(discoveredNames, result);
|
|
261
154
|
|
|
262
155
|
return result;
|
|
263
156
|
}
|
|
@@ -267,58 +160,30 @@ export function printSyncResult(result: SyncResult): void {
|
|
|
267
160
|
if (result.registered.length > 0) console.log(` Registered: ${result.registered.join(', ')}`);
|
|
268
161
|
if (result.updated.length > 0) console.log(` Updated: ${result.updated.join(', ')}`);
|
|
269
162
|
if (result.reactivated.length > 0) console.log(` Reactivated: ${result.reactivated.join(', ')}`);
|
|
270
|
-
if (result.archived.length > 0) console.log(`
|
|
163
|
+
if (result.archived.length > 0) console.log(` Removed: ${result.archived.join(', ')}`);
|
|
271
164
|
if (result.unchanged.length > 0) console.log(` Unchanged: ${result.unchanged.join(', ')}`);
|
|
272
165
|
for (const err of result.errors) {
|
|
273
166
|
console.error(` Error (${err.name}): ${err.error}`);
|
|
274
167
|
}
|
|
275
168
|
const total = result.registered.length + result.updated.length + result.unchanged.length + result.reactivated.length;
|
|
276
|
-
console.log(`\nSync complete: ${total} active agent(s), ${result.archived.length}
|
|
169
|
+
console.log(`\nSync complete: ${total} active agent(s), ${result.archived.length} removed.`);
|
|
277
170
|
}
|
|
278
171
|
|
|
279
|
-
/**
|
|
280
|
-
async function
|
|
281
|
-
workspaceRoot: string,
|
|
282
|
-
discoveredNames: Set<string>,
|
|
283
|
-
result: SyncResult,
|
|
284
|
-
): Promise<void> {
|
|
172
|
+
/** Remove directory entries whose agent dirs no longer exist on disk. */
|
|
173
|
+
async function removeMissingAgents(discoveredNames: Set<string>, result: SyncResult): Promise<void> {
|
|
285
174
|
try {
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
const manifest = (item.manifest ?? {}) as Record<string, unknown>;
|
|
292
|
-
if (manifest.archived) continue; // already archived
|
|
293
|
-
if (manifest.source !== 'auto-sync') continue; // only archive auto-synced agents
|
|
294
|
-
|
|
295
|
-
// Verify the agent was from this workspace
|
|
296
|
-
if (item.install_path && !item.install_path.startsWith(agentsDir)) continue;
|
|
175
|
+
const entries = await directory.ls();
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
if (discoveredNames.has(entry.name)) continue;
|
|
178
|
+
if (entry.scope === 'built-in') continue;
|
|
179
|
+
if (!entry.dir || !entry.dir.includes('/agents/')) continue; // only remove auto-synced
|
|
297
180
|
|
|
298
|
-
const
|
|
299
|
-
if (
|
|
181
|
+
const removed = await directory.rm(entry.name);
|
|
182
|
+
if (removed) result.archived.push(entry.name);
|
|
300
183
|
}
|
|
301
184
|
} catch {
|
|
302
|
-
// Best-effort
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/** Sync a single agent by name from the workspace (used by file watcher). */
|
|
307
|
-
async function syncSingleAgentByName(workspaceRoot: string, agentName: string): Promise<string> {
|
|
308
|
-
const agent = discoverSingleAgent(workspaceRoot, agentName);
|
|
309
|
-
if (!agent) return 'not-found';
|
|
310
|
-
|
|
311
|
-
const result: SyncResult = { registered: [], updated: [], unchanged: [], archived: [], reactivated: [], errors: [] };
|
|
312
|
-
await syncSingleAgent(agent, result);
|
|
313
|
-
|
|
314
|
-
if (result.registered.length > 0 || result.updated.length > 0 || result.reactivated.length > 0) {
|
|
315
|
-
await regenerateAgentCache().catch(() => {});
|
|
185
|
+
// Best-effort
|
|
316
186
|
}
|
|
317
|
-
|
|
318
|
-
if (result.reactivated.length > 0) return 'reactivated';
|
|
319
|
-
if (result.registered.length > 0) return 'registered';
|
|
320
|
-
if (result.updated.length > 0) return 'updated';
|
|
321
|
-
return 'unchanged';
|
|
322
187
|
}
|
|
323
188
|
|
|
324
189
|
/** Core sync logic for a single agent. */
|
|
@@ -326,39 +191,26 @@ async function syncSingleAgent(agent: AgentInfo, result: SyncResult): Promise<vo
|
|
|
326
191
|
const orgRepo = agent.repoUrl ? extractOrgRepo(agent.repoUrl) : null;
|
|
327
192
|
const repoPath = orgRepo ?? agent.repoUrl ?? agent.dir;
|
|
328
193
|
|
|
329
|
-
const existing = await
|
|
194
|
+
const existing = await directory.get(agent.name);
|
|
330
195
|
|
|
331
196
|
if (!existing) {
|
|
332
|
-
await
|
|
197
|
+
await directory.add({
|
|
333
198
|
name: agent.name,
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
manifest: { promptMode: 'append', repo: repoPath, productRepo: agent.productRepo, source: 'auto-sync' },
|
|
199
|
+
dir: agent.dir,
|
|
200
|
+
repo: repoPath,
|
|
201
|
+
promptMode: 'append',
|
|
338
202
|
});
|
|
339
203
|
result.registered.push(agent.name);
|
|
340
204
|
return;
|
|
341
205
|
}
|
|
342
206
|
|
|
343
|
-
// Check if this is a reactivation (was archived)
|
|
344
|
-
const manifest = (existing.manifest ?? {}) as Record<string, unknown>;
|
|
345
|
-
if (manifest.archived) {
|
|
346
|
-
await reactivateAgent(agent);
|
|
347
|
-
result.reactivated.push(agent.name);
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
207
|
// Check if update needed
|
|
352
|
-
const needsUpdate =
|
|
353
|
-
(manifest.repo as string) !== repoPath ||
|
|
354
|
-
existing.install_path !== agent.dir ||
|
|
355
|
-
(agent.productRepo && manifest.productRepo !== agent.productRepo);
|
|
208
|
+
const needsUpdate = existing.repo !== repoPath || existing.dir !== agent.dir;
|
|
356
209
|
|
|
357
210
|
if (needsUpdate) {
|
|
358
|
-
await
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
manifest: { ...manifest, repo: repoPath, productRepo: agent.productRepo, source: 'auto-sync' },
|
|
211
|
+
await directory.edit(agent.name, {
|
|
212
|
+
dir: agent.dir,
|
|
213
|
+
repo: repoPath,
|
|
362
214
|
});
|
|
363
215
|
result.updated.push(agent.name);
|
|
364
216
|
} else {
|
|
@@ -374,21 +226,17 @@ interface AgentWatcher {
|
|
|
374
226
|
close: () => void;
|
|
375
227
|
}
|
|
376
228
|
|
|
377
|
-
/**
|
|
378
|
-
async function
|
|
379
|
-
const
|
|
380
|
-
if (
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return 'archived';
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return null;
|
|
229
|
+
/** Sync a single agent by name from the workspace (used by file watcher). */
|
|
230
|
+
async function syncSingleAgentByName(workspaceRoot: string, agentName: string): Promise<string> {
|
|
231
|
+
const agent = discoverSingleAgent(workspaceRoot, agentName);
|
|
232
|
+
if (!agent) return 'not-found';
|
|
233
|
+
|
|
234
|
+
const result: SyncResult = { registered: [], updated: [], unchanged: [], archived: [], reactivated: [], errors: [] };
|
|
235
|
+
await syncSingleAgent(agent, result);
|
|
236
|
+
|
|
237
|
+
if (result.registered.length > 0) return 'registered';
|
|
238
|
+
if (result.updated.length > 0) return 'updated';
|
|
239
|
+
return 'unchanged';
|
|
392
240
|
}
|
|
393
241
|
|
|
394
242
|
/**
|
|
@@ -396,8 +244,7 @@ async function processWatchedAgent(workspaceRoot: string, agentsDir: string, nam
|
|
|
396
244
|
* Debounces changes with a 2s window.
|
|
397
245
|
*
|
|
398
246
|
* - New directory with AGENTS.md → auto-register
|
|
399
|
-
* -
|
|
400
|
-
* - Removed directory → archive (never delete)
|
|
247
|
+
* - Removed directory → remove from directory
|
|
401
248
|
*/
|
|
402
249
|
export function watchAgentDirectory(
|
|
403
250
|
workspaceRoot: string,
|
|
@@ -440,3 +287,17 @@ export function watchAgentDirectory(
|
|
|
440
287
|
},
|
|
441
288
|
};
|
|
442
289
|
}
|
|
290
|
+
|
|
291
|
+
/** Process a single watched agent change. */
|
|
292
|
+
async function processWatchedAgent(workspaceRoot: string, agentsDir: string, name: string): Promise<string | null> {
|
|
293
|
+
const agentDir = join(agentsDir, name);
|
|
294
|
+
if (existsSync(agentDir) && existsSync(join(agentDir, 'AGENTS.md'))) {
|
|
295
|
+
const action = await syncSingleAgentByName(workspaceRoot, name);
|
|
296
|
+
return action !== 'unchanged' && action !== 'not-found' ? action : null;
|
|
297
|
+
}
|
|
298
|
+
if (!existsSync(agentDir)) {
|
|
299
|
+
const removed = await directory.rm(name);
|
|
300
|
+
if (removed) return 'removed';
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
package/src/lib/export-format.ts
CHANGED
|
@@ -18,16 +18,7 @@ export interface ExportDocument {
|
|
|
18
18
|
data: Record<string, unknown[]>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export type ExportGroup =
|
|
22
|
-
| 'boards'
|
|
23
|
-
| 'tasks'
|
|
24
|
-
| 'tags'
|
|
25
|
-
| 'projects'
|
|
26
|
-
| 'schedules'
|
|
27
|
-
| 'agents'
|
|
28
|
-
| 'apps'
|
|
29
|
-
| 'comms'
|
|
30
|
-
| 'config';
|
|
21
|
+
export type ExportGroup = 'boards' | 'tasks' | 'tags' | 'projects' | 'schedules' | 'agents' | 'comms' | 'config';
|
|
31
22
|
|
|
32
23
|
/** Tables belonging to each export group */
|
|
33
24
|
export const GROUP_TABLES: Record<ExportGroup, string[]> = {
|
|
@@ -37,7 +28,6 @@ export const GROUP_TABLES: Record<ExportGroup, string[]> = {
|
|
|
37
28
|
projects: ['projects'],
|
|
38
29
|
schedules: ['schedules'],
|
|
39
30
|
agents: ['agents', 'agent_templates', 'agent_checkpoints'],
|
|
40
|
-
apps: ['app_store', 'installed_apps', 'app_versions'],
|
|
41
31
|
comms: ['conversations', 'conversation_members', 'messages', 'mailbox', 'team_chat', 'notification_preferences'],
|
|
42
32
|
config: ['os_config', 'instances', 'warm_pool', 'golden_images'],
|
|
43
33
|
};
|
|
@@ -49,7 +39,6 @@ export const ALL_GROUPS: ExportGroup[] = [
|
|
|
49
39
|
'projects',
|
|
50
40
|
'schedules',
|
|
51
41
|
'agents',
|
|
52
|
-
'apps',
|
|
53
42
|
'comms',
|
|
54
43
|
'config',
|
|
55
44
|
];
|
package/src/lib/import-order.ts
CHANGED
|
@@ -21,23 +21,13 @@ const IMPORT_LEVELS: string[][] = [
|
|
|
21
21
|
'task_types',
|
|
22
22
|
'notification_preferences',
|
|
23
23
|
// Optional (KhalOS)
|
|
24
|
-
'app_store',
|
|
25
24
|
'os_config',
|
|
26
25
|
'golden_images',
|
|
27
26
|
'warm_pool',
|
|
28
27
|
'instances',
|
|
29
28
|
],
|
|
30
29
|
// Level 1: Depend on Level 0
|
|
31
|
-
[
|
|
32
|
-
'triggers',
|
|
33
|
-
'boards',
|
|
34
|
-
'board_templates',
|
|
35
|
-
'agents',
|
|
36
|
-
'conversations',
|
|
37
|
-
// Optional (KhalOS)
|
|
38
|
-
'installed_apps',
|
|
39
|
-
'app_versions',
|
|
40
|
-
],
|
|
30
|
+
['triggers', 'boards', 'board_templates', 'agents', 'conversations'],
|
|
41
31
|
// Level 2: Depend on Level 1
|
|
42
32
|
['tasks', 'runs', 'messages', 'conversation_members', 'mailbox', 'team_chat'],
|
|
43
33
|
// Level 3: Depend on Level 2
|
package/src/lib/task-service.ts
CHANGED
|
@@ -48,6 +48,8 @@ export interface ProjectRow {
|
|
|
48
48
|
name: string;
|
|
49
49
|
repoPath: string | null;
|
|
50
50
|
description: string | null;
|
|
51
|
+
leaderAgent: string | null;
|
|
52
|
+
tmuxSession: string | null;
|
|
51
53
|
status: string;
|
|
52
54
|
archivedAt: string | null;
|
|
53
55
|
createdAt: string;
|
|
@@ -391,6 +393,8 @@ function mapProject(row: Record<string, unknown>): ProjectRow {
|
|
|
391
393
|
name: row.name as string,
|
|
392
394
|
repoPath: str(row.repo_path),
|
|
393
395
|
description: str(row.description),
|
|
396
|
+
leaderAgent: str(row.leader_agent),
|
|
397
|
+
tmuxSession: str(row.tmux_session),
|
|
394
398
|
status: strOrDefault(row.status, 'active'),
|
|
395
399
|
archivedAt: str(row.archived_at),
|
|
396
400
|
createdAt: String(row.created_at),
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Command } from 'commander';
|
|
7
|
-
import { type StoreRow, listItemsFromStore, migrateAgentDirectory } from '../../lib/agent-cache.js';
|
|
8
7
|
import * as directory from '../../lib/agent-directory.js';
|
|
9
8
|
import { printSyncResult, syncAgentDirectory } from '../../lib/agent-sync.js';
|
|
10
9
|
import { ALL_BUILTINS } from '../../lib/builtin-agents.js';
|
|
@@ -82,42 +81,8 @@ function printBuiltinAgentsTable(): void {
|
|
|
82
81
|
console.log('');
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
return roles
|
|
88
|
-
.flatMap((r) => r.split(','))
|
|
89
|
-
.map((r) => r.trim())
|
|
90
|
-
.filter(Boolean);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function listEntries(json?: boolean, includeBuiltins?: boolean, includeArchived?: boolean): Promise<void> {
|
|
94
|
-
await migrateAgentDirectory().catch(() => {});
|
|
95
|
-
|
|
96
|
-
let entries: directory.ScopedDirectoryEntry[];
|
|
97
|
-
try {
|
|
98
|
-
const storeItems = await listItemsFromStore('agent');
|
|
99
|
-
entries = storeItems
|
|
100
|
-
.filter((item: StoreRow) => {
|
|
101
|
-
if (includeArchived) return true;
|
|
102
|
-
const manifest = (item.manifest ?? {}) as Record<string, unknown>;
|
|
103
|
-
return !manifest.archived;
|
|
104
|
-
})
|
|
105
|
-
.map((item: StoreRow) => {
|
|
106
|
-
const manifest = (item.manifest ?? {}) as Record<string, unknown>;
|
|
107
|
-
return {
|
|
108
|
-
name: item.name,
|
|
109
|
-
dir: (item.install_path as string) ?? '',
|
|
110
|
-
repo: (manifest.repo as string) ?? '',
|
|
111
|
-
promptMode: ((manifest.promptMode as string) ?? 'append') as directory.PromptMode,
|
|
112
|
-
model: manifest.model as string | undefined,
|
|
113
|
-
roles: normalizeRoles(manifest.roles as string[] | undefined),
|
|
114
|
-
registeredAt: item.installed_at as string,
|
|
115
|
-
scope: (manifest.archived ? 'archived' : 'global') as directory.DirectoryScope,
|
|
116
|
-
};
|
|
117
|
-
});
|
|
118
|
-
} catch {
|
|
119
|
-
entries = await directory.ls();
|
|
120
|
-
}
|
|
84
|
+
async function listEntries(json?: boolean, includeBuiltins?: boolean, _includeArchived?: boolean): Promise<void> {
|
|
85
|
+
const entries = await directory.ls();
|
|
121
86
|
|
|
122
87
|
if (json) {
|
|
123
88
|
const result: Record<string, unknown>[] = entries.map((e) => ({ ...e, builtin: false }));
|