ndomo 0.1.0 → 0.2.0
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/.env.example +4 -0
- package/README.es.md +29 -23
- package/README.md +64 -24
- package/bun.lock +447 -0
- package/docs/configuration.md +4 -4
- package/docs/installation.md +53 -34
- package/docs/installer.md +164 -0
- package/docs/integrations.md +1 -1
- package/docs/web-ui.md +124 -0
- package/package.json +43 -4
- package/scripts/install.sh +28 -0
- package/scripts/smoke-install.sh +47 -0
- package/scripts/smoke-web.sh +335 -0
- package/src/cli/__tests__/install.test.ts +733 -0
- package/src/cli/index.ts +8 -0
- package/src/cli/install.ts +1292 -0
- package/src/config/__tests__/schema.test.ts +223 -0
- package/src/config/schema.ts +129 -16
- package/src/http/__tests__/auth.test.ts +10 -10
- package/src/http/__tests__/spa.test.ts +296 -0
- package/src/http/auth.ts +8 -1
- package/src/http/server.ts +71 -2
- package/.bun-version +0 -1
- package/.dockerignore +0 -79
- package/.editorconfig +0 -18
- package/.github/CODEOWNERS +0 -8
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -62
- package/.github/ISSUE_TEMPLATE/config.yml +0 -2
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -34
- package/.github/dependabot.yml +0 -36
- package/.github/pull_request_template.md +0 -24
- package/.github/release.yml +0 -30
- package/.github/workflows/gitleaks.yml +0 -28
- package/.github/workflows/release-please.yml +0 -27
- package/.github/workflows/smoke.yml +0 -29
- package/.husky/commit-msg +0 -1
- package/CHANGELOG.md +0 -114
- package/Dockerfile +0 -32
- package/bin/ndomo-analyses.ts +0 -4
- package/bin/ndomo-status.ts +0 -4
- package/biome.json +0 -57
- package/commitlint.config.js +0 -3
- package/opencode.json +0 -5
- package/release-please-config.json +0 -11
- package/scripts/dev-bust-cache.sh +0 -164
- package/scripts/smoke-e2e.ts +0 -704
- package/scripts/smoke-hot.ts +0 -417
- package/scripts/smoke-v4.ts +0 -256
- package/scripts/smoke-v5.ts +0 -397
- package/scripts/uninstall.sh +0 -224
- package/src/index.ts +0 -37
- package/src/lib.ts +0 -65
- package/src/mem/scoped.ts +0 -65
- package/src/orchestrator/background.test.ts +0 -268
- package/src/orchestrator/background.ts +0 -293
- package/src/orchestrator/memory-hook.ts +0 -182
- package/src/orchestrator/reconciler.ts +0 -123
- package/src/orchestrator/scheduler.test.ts +0 -300
- package/src/orchestrator/scheduler.ts +0 -243
- package/src/plugin.test.ts +0 -2574
- package/src/plugin.ts +0 -1690
- package/src/worktrees/manager.ts +0 -236
- package/src/worktrees/state.ts +0 -87
- package/tests/integration/ranger-flow.test.ts +0 -257
- package/tsconfig.json +0 -31
package/scripts/smoke-v5.ts
DELETED
|
@@ -1,397 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ndomo v5 migration smoke test.
|
|
3
|
-
*
|
|
4
|
-
* Verifies soft delete + auto-archive functionality:
|
|
5
|
-
* 1. Create plan + 2 tasks + 1 session, mark completed → auto-archive fires
|
|
6
|
-
* 2. listPlans() (default) excludes archived plan
|
|
7
|
-
* 3. listPlans({ includeArchived: true }) includes archived plan
|
|
8
|
-
* 4. searchPlans() (default) excludes archived plan
|
|
9
|
-
* 5. Markdown file exists, >500 bytes, contains title/tasks/sessions
|
|
10
|
-
* 6. plan_tasks.archived_at set for both tasks
|
|
11
|
-
* 7. sessions.archived_at set for the session
|
|
12
|
-
* 8. Re-archive throws "already archived"
|
|
13
|
-
* 9. searchPlans returns results ordered by rank (FTS5 relevance)
|
|
14
|
-
* 10. nextTaskForAgent skips archived tasks by default
|
|
15
|
-
* 11. findPlansByCategory excludes archived by default
|
|
16
|
-
* 12. plan_progress view excludes archived tasks from counts
|
|
17
|
-
*
|
|
18
|
-
* Usage: bun run scripts/smoke-v5.ts
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { Database } from "bun:sqlite";
|
|
22
|
-
import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
23
|
-
import { tmpdir } from "node:os";
|
|
24
|
-
import { join } from "node:path";
|
|
25
|
-
import { runMigrations } from "../src/db/migrations.ts";
|
|
26
|
-
import { archivePlan } from "../src/db/plan-archive.ts";
|
|
27
|
-
import {
|
|
28
|
-
createPlan,
|
|
29
|
-
findPlansByCategory,
|
|
30
|
-
getPlanProgress,
|
|
31
|
-
listPlans,
|
|
32
|
-
searchPlans,
|
|
33
|
-
} from "../src/db/plans.ts";
|
|
34
|
-
import { startSession } from "../src/db/sessions.ts";
|
|
35
|
-
import { createTasksBatch, listTasksByPlan, nextTaskForAgent } from "../src/db/tasks.ts";
|
|
36
|
-
import type { Plan } from "../src/db/types.ts";
|
|
37
|
-
|
|
38
|
-
let pass = 0;
|
|
39
|
-
let fail = 0;
|
|
40
|
-
|
|
41
|
-
function assert(cond: boolean, msg: string): void {
|
|
42
|
-
if (cond) {
|
|
43
|
-
pass++;
|
|
44
|
-
console.log(` ✓ ${msg}`);
|
|
45
|
-
} else {
|
|
46
|
-
fail++;
|
|
47
|
-
console.error(` ✗ ${msg}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function assertThrows(fn: () => void, expectedMsg: string, msg: string): void {
|
|
52
|
-
try {
|
|
53
|
-
fn();
|
|
54
|
-
fail++;
|
|
55
|
-
console.error(` ✗ ${msg} (expected error, got none)`);
|
|
56
|
-
} catch (err) {
|
|
57
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
58
|
-
if (errMsg.includes(expectedMsg)) {
|
|
59
|
-
pass++;
|
|
60
|
-
console.log(` ✓ ${msg}`);
|
|
61
|
-
} else {
|
|
62
|
-
fail++;
|
|
63
|
-
console.error(` ✗ ${msg} (expected '${expectedMsg}', got '${errMsg}')`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const PLAN_DEFAULTS = {
|
|
69
|
-
title: "Test",
|
|
70
|
-
status: "draft" as const,
|
|
71
|
-
priority: 3,
|
|
72
|
-
overview: "test",
|
|
73
|
-
complexity: 3,
|
|
74
|
-
createdBy: "smoke",
|
|
75
|
-
updatedBy: "smoke",
|
|
76
|
-
metadata: {},
|
|
77
|
-
approvedAt: null,
|
|
78
|
-
completedAt: null,
|
|
79
|
-
sessionId: null,
|
|
80
|
-
approach: "Test approach for smoke validation",
|
|
81
|
-
sourceSessionId: null,
|
|
82
|
-
sourceMessageId: null,
|
|
83
|
-
category: null as Plan["category"],
|
|
84
|
-
archivedAt: null,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const TASK_DEFAULTS = {
|
|
88
|
-
agent: "smoke",
|
|
89
|
-
files: [] as string[],
|
|
90
|
-
complexity: 3,
|
|
91
|
-
createdBy: "smoke",
|
|
92
|
-
updatedBy: "smoke",
|
|
93
|
-
sourceSessionId: null,
|
|
94
|
-
sourceMessageId: null,
|
|
95
|
-
reviewedBy: null,
|
|
96
|
-
tokensUsed: null,
|
|
97
|
-
durationMs: null,
|
|
98
|
-
artifacts: [] as string[],
|
|
99
|
-
dependencies: [] as string[],
|
|
100
|
-
metadata: {},
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
// Use in-memory DB + temp dir for isolated smoke test
|
|
104
|
-
const db = new Database(":memory:");
|
|
105
|
-
db.exec("PRAGMA journal_mode = WAL");
|
|
106
|
-
db.exec("PRAGMA foreign_keys = ON");
|
|
107
|
-
|
|
108
|
-
const testMemDir = join(tmpdir(), `ndomo-smoke-v5-${Date.now()}`);
|
|
109
|
-
mkdirSync(testMemDir, { recursive: true });
|
|
110
|
-
|
|
111
|
-
console.log("ndomo v5 smoke test\n");
|
|
112
|
-
|
|
113
|
-
// ── Run migrations ──────────────────────────────────────────────────────────
|
|
114
|
-
console.log("Running migrations v1→v5...");
|
|
115
|
-
runMigrations(db);
|
|
116
|
-
const ver = db.query("SELECT MAX(version) as v FROM schema_version").get() as { v: number };
|
|
117
|
-
assert(ver.v === 5, "schema_version = 5");
|
|
118
|
-
|
|
119
|
-
// Verify archived_at columns exist
|
|
120
|
-
const planCols = db.query("PRAGMA table_info(plans)").all() as Array<{ name: string }>;
|
|
121
|
-
assert(
|
|
122
|
-
planCols.some((c) => c.name === "archived_at"),
|
|
123
|
-
"plans.archived_at column exists",
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// ── Test 1: Create plan + tasks + session, archive via function ─────────────
|
|
127
|
-
console.log("\nTest 1: Create plan + 2 tasks + 1 session, archive");
|
|
128
|
-
|
|
129
|
-
const planOverrides = {
|
|
130
|
-
id: "archive-test",
|
|
131
|
-
slug: "archive-test-plan",
|
|
132
|
-
title: "Archive Test Plan",
|
|
133
|
-
overview: "A plan for testing the archive flow with tasks and sessions",
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
createPlan(db, {
|
|
137
|
-
...PLAN_DEFAULTS,
|
|
138
|
-
...planOverrides,
|
|
139
|
-
status: "draft" as const,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
createTasksBatch(db, "archive-test", [
|
|
143
|
-
{ ...TASK_DEFAULTS, description: "Implement feature A", orderIndex: 0 },
|
|
144
|
-
{ ...TASK_DEFAULTS, description: "Write tests for A", orderIndex: 1 },
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
startSession(db, {
|
|
148
|
-
id: "session-archive-test",
|
|
149
|
-
goal: "Build feature A",
|
|
150
|
-
planId: "archive-test",
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
// Archive the plan
|
|
154
|
-
const result = archivePlan(db, "archive-test", { memDir: testMemDir });
|
|
155
|
-
assert(result.planId === "archive-test", `archivePlan returned planId=${result.planId}`);
|
|
156
|
-
assert(result.tasksCount === 2, `archivePlan tasksCount=${result.tasksCount}`);
|
|
157
|
-
assert(result.sessionsCount === 1, `archivePlan sessionsCount=${result.sessionsCount}`);
|
|
158
|
-
assert(result.filePath.startsWith(testMemDir), `filePath in testMemDir: ${result.filePath}`);
|
|
159
|
-
|
|
160
|
-
// ── Test 2: listPlans() default excludes archived ───────────────────────────
|
|
161
|
-
console.log("\nTest 2: listPlans() excludes archived by default");
|
|
162
|
-
|
|
163
|
-
// Create another active plan to ensure listPlans still returns it
|
|
164
|
-
createPlan(db, {
|
|
165
|
-
...PLAN_DEFAULTS,
|
|
166
|
-
id: "active-plan",
|
|
167
|
-
slug: "active-plan",
|
|
168
|
-
title: "Active Plan",
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
const activePlans = listPlans(db);
|
|
172
|
-
assert(
|
|
173
|
-
activePlans.every((p) => p.archivedAt === null),
|
|
174
|
-
"listPlans() returns only active plans (archived_at IS NULL)",
|
|
175
|
-
);
|
|
176
|
-
assert(
|
|
177
|
-
!activePlans.some((p) => p.id === "archive-test"),
|
|
178
|
-
"listPlans() does NOT include archived plan",
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
// ── Test 3: listPlans({ includeArchived: true }) includes archived ──────────
|
|
182
|
-
console.log("\nTest 3: listPlans({ includeArchived: true }) includes archived");
|
|
183
|
-
|
|
184
|
-
const allPlans = listPlans(db, { includeArchived: true });
|
|
185
|
-
assert(
|
|
186
|
-
allPlans.some((p) => p.id === "archive-test"),
|
|
187
|
-
"listPlans({ includeArchived: true }) includes archived plan",
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
// ── Test 4: searchPlans() excludes archived ─────────────────────────────────
|
|
191
|
-
console.log("\nTest 4: searchPlans() excludes archived by default");
|
|
192
|
-
|
|
193
|
-
// FTS search for a word from the archived plan's title
|
|
194
|
-
const searchResults = searchPlans(db, "archive");
|
|
195
|
-
assert(
|
|
196
|
-
!searchResults.some((p) => p.id === "archive-test"),
|
|
197
|
-
"searchPlans() does NOT find archived plan",
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
// ── Test 5: Markdown file validation ────────────────────────────────────────
|
|
201
|
-
console.log("\nTest 5: Markdown file exists and is valid");
|
|
202
|
-
|
|
203
|
-
const mdFile = result.filePath;
|
|
204
|
-
assert(existsSync(mdFile), `markdown file exists: ${mdFile}`);
|
|
205
|
-
|
|
206
|
-
const mdContent = readFileSync(mdFile, "utf-8");
|
|
207
|
-
assert(mdContent.length > 500, `markdown > 500 bytes: ${mdContent.length} bytes`);
|
|
208
|
-
assert(
|
|
209
|
-
mdContent.includes("# Plan: Archive Test Plan"),
|
|
210
|
-
"markdown contains '# Plan: Archive Test Plan'",
|
|
211
|
-
);
|
|
212
|
-
assert(mdContent.includes("Implement feature A"), "markdown contains task 1 description");
|
|
213
|
-
assert(mdContent.includes("Write tests for A"), "markdown contains task 2 description");
|
|
214
|
-
assert(mdContent.includes("session-"), "markdown contains session ID prefix");
|
|
215
|
-
|
|
216
|
-
// ── Test 6: plan_tasks.archived_at set ──────────────────────────────────────
|
|
217
|
-
console.log("\nTest 6: plan_tasks.archived_at is set");
|
|
218
|
-
|
|
219
|
-
const archivedTasks = listTasksByPlan(db, "archive-test", { includeArchived: true });
|
|
220
|
-
assert(
|
|
221
|
-
archivedTasks.length === 2 && archivedTasks.every((t) => t.archivedAt !== null),
|
|
222
|
-
"both tasks have archived_at set",
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
// ── Test 7: sessions.archived_at set ────────────────────────────────────────
|
|
226
|
-
console.log("\nTest 7: sessions.archived_at is set");
|
|
227
|
-
|
|
228
|
-
const archivedSession = db
|
|
229
|
-
.query("SELECT archived_at FROM sessions WHERE id = ?")
|
|
230
|
-
.get("session-archive-test") as { archived_at: number | null } | null;
|
|
231
|
-
assert(
|
|
232
|
-
archivedSession !== null && archivedSession.archived_at !== null,
|
|
233
|
-
"session.archived_at is set",
|
|
234
|
-
);
|
|
235
|
-
|
|
236
|
-
// ── Test 8: Re-archive throws "already archived" ────────────────────────────
|
|
237
|
-
console.log("\nTest 8: Re-archive throws 'already archived'");
|
|
238
|
-
|
|
239
|
-
assertThrows(
|
|
240
|
-
() => archivePlan(db, "archive-test", { memDir: testMemDir }),
|
|
241
|
-
"already archived",
|
|
242
|
-
"re-archive throws 'ndomo: plan already archived'",
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
// ── Test 9: searchPlans returns results ordered by rank ─────────────────────
|
|
246
|
-
console.log("\nTest 9: searchPlans returns results ordered by rank (FTS5 relevance)");
|
|
247
|
-
|
|
248
|
-
// Create 3 plans with different match-strength keywords
|
|
249
|
-
createPlan(db, {
|
|
250
|
-
...PLAN_DEFAULTS,
|
|
251
|
-
id: "rank-1",
|
|
252
|
-
slug: "rank-test-1",
|
|
253
|
-
title: "Database migration tool",
|
|
254
|
-
overview: "A tool for running database migration automatically",
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
createPlan(db, {
|
|
258
|
-
...PLAN_DEFAULTS,
|
|
259
|
-
id: "rank-2",
|
|
260
|
-
slug: "rank-test-2",
|
|
261
|
-
title: "Migration helper",
|
|
262
|
-
overview: "A helper utility for migration tasks in the database layer",
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
createPlan(db, {
|
|
266
|
-
...PLAN_DEFAULTS,
|
|
267
|
-
id: "rank-3",
|
|
268
|
-
slug: "rank-test-3",
|
|
269
|
-
title: "Simple utility",
|
|
270
|
-
overview: "A basic utility for common tasks",
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
// Search for "database" — rank-1 and rank-2 should match, rank-1 has it in title
|
|
274
|
-
const rankResults = searchPlans(db, "database", 10);
|
|
275
|
-
assert(rankResults.length >= 2, `searchPlans found ${rankResults.length} results for "database"`);
|
|
276
|
-
if (rankResults[0] && rankResults.length >= 2) {
|
|
277
|
-
// rank-1 has "Database" in title (higher rank), should be first
|
|
278
|
-
assert(
|
|
279
|
-
rankResults[0].id === "rank-1",
|
|
280
|
-
`searchPlans rank: rank-1 is first (got ${rankResults[0].id})`,
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// ── Test 10: nextTaskForAgent skips archived tasks ──────────────────────────
|
|
285
|
-
console.log("\nTest 10: nextTaskForAgent skips archived tasks by default");
|
|
286
|
-
|
|
287
|
-
// Create a plan with tasks, then archive it
|
|
288
|
-
createPlan(db, {
|
|
289
|
-
...PLAN_DEFAULTS,
|
|
290
|
-
id: "agent-archived-plan",
|
|
291
|
-
slug: "agent-archived-plan",
|
|
292
|
-
title: "Agent Archived Plan",
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
createTasksBatch(db, "agent-archived-plan", [
|
|
296
|
-
{ ...TASK_DEFAULTS, description: "Task for archived plan", orderIndex: 0, agent: "test-agent" },
|
|
297
|
-
]);
|
|
298
|
-
|
|
299
|
-
// Archive the plan (this archives tasks too)
|
|
300
|
-
archivePlan(db, "agent-archived-plan", { memDir: testMemDir });
|
|
301
|
-
|
|
302
|
-
// nextTaskForAgent should NOT return archived task
|
|
303
|
-
const archivedTask = nextTaskForAgent(db, "test-agent");
|
|
304
|
-
assert(
|
|
305
|
-
archivedTask === null,
|
|
306
|
-
"nextTaskForAgent returns null for archived task (default includeArchived=false)",
|
|
307
|
-
);
|
|
308
|
-
|
|
309
|
-
// With includeArchived: true, should return the task
|
|
310
|
-
const archivedTaskIncluded = nextTaskForAgent(db, "test-agent", { includeArchived: true });
|
|
311
|
-
assert(
|
|
312
|
-
archivedTaskIncluded !== null && archivedTaskIncluded.planId === "agent-archived-plan",
|
|
313
|
-
"nextTaskForAgent with includeArchived=true returns archived task",
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
// ── Test 11: findPlansByCategory excludes archived by default ───────────────
|
|
317
|
-
console.log("\nTest 11: findPlansByCategory excludes archived by default");
|
|
318
|
-
|
|
319
|
-
// Create a plan with category, then archive it
|
|
320
|
-
createPlan(db, {
|
|
321
|
-
...PLAN_DEFAULTS,
|
|
322
|
-
id: "cat-archived-plan",
|
|
323
|
-
slug: "cat-archived-plan",
|
|
324
|
-
title: "Category Archived Plan",
|
|
325
|
-
category: "feature",
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
archivePlan(db, "cat-archived-plan", { memDir: testMemDir });
|
|
329
|
-
|
|
330
|
-
// Create an active plan with same category
|
|
331
|
-
createPlan(db, {
|
|
332
|
-
...PLAN_DEFAULTS,
|
|
333
|
-
id: "cat-active-plan",
|
|
334
|
-
slug: "cat-active-plan",
|
|
335
|
-
title: "Category Active Plan",
|
|
336
|
-
category: "feature",
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
const catResults = findPlansByCategory(db, "feature");
|
|
340
|
-
assert(
|
|
341
|
-
!catResults.some((p) => p.id === "cat-archived-plan"),
|
|
342
|
-
"findPlansByCategory default excludes archived plan",
|
|
343
|
-
);
|
|
344
|
-
assert(
|
|
345
|
-
catResults.some((p) => p.id === "cat-active-plan"),
|
|
346
|
-
"findPlansByCategory default includes active plan",
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
const catResultsAll = findPlansByCategory(db, "feature", 20, { includeArchived: true });
|
|
350
|
-
assert(
|
|
351
|
-
catResultsAll.some((p) => p.id === "cat-archived-plan"),
|
|
352
|
-
"findPlansByCategory with includeArchived=true includes archived plan",
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
// ── Test 12: plan_progress view excludes archived tasks ─────────────────────
|
|
356
|
-
console.log("\nTest 12: plan_progress view excludes archived tasks from counts");
|
|
357
|
-
|
|
358
|
-
// Create a plan with tasks, check progress, archive, check again
|
|
359
|
-
createPlan(db, {
|
|
360
|
-
...PLAN_DEFAULTS,
|
|
361
|
-
id: "progress-test",
|
|
362
|
-
slug: "progress-test",
|
|
363
|
-
title: "Progress Test Plan",
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
createTasksBatch(db, "progress-test", [
|
|
367
|
-
{ ...TASK_DEFAULTS, description: "Progress task 1", orderIndex: 0 },
|
|
368
|
-
{ ...TASK_DEFAULTS, description: "Progress task 2", orderIndex: 1 },
|
|
369
|
-
]);
|
|
370
|
-
|
|
371
|
-
// Check progress before archive — should show 2 tasks
|
|
372
|
-
const progressBefore = getPlanProgress(db, "progress-test");
|
|
373
|
-
const progressBeforeFirst = progressBefore[0];
|
|
374
|
-
assert(
|
|
375
|
-
progressBefore.length === 1 && progressBeforeFirst?.totalTasks === 2,
|
|
376
|
-
`plan_progress before archive: totalTasks=${progressBeforeFirst?.totalTasks ?? 0}`,
|
|
377
|
-
);
|
|
378
|
-
|
|
379
|
-
// Archive the plan (archives tasks too)
|
|
380
|
-
archivePlan(db, "progress-test", { memDir: testMemDir });
|
|
381
|
-
|
|
382
|
-
// Check progress after archive — should show 0 tasks (archived excluded)
|
|
383
|
-
const progressAfter = getPlanProgress(db, "progress-test");
|
|
384
|
-
const progressAfterFirst = progressAfter[0];
|
|
385
|
-
assert(
|
|
386
|
-
progressAfter.length === 1 && progressAfterFirst?.totalTasks === 0,
|
|
387
|
-
`plan_progress after archive: totalTasks=${progressAfterFirst?.totalTasks ?? 0} (archived excluded)`,
|
|
388
|
-
);
|
|
389
|
-
|
|
390
|
-
// ── Cleanup ─────────────────────────────────────────────────────────────────
|
|
391
|
-
rmSync(testMemDir, { recursive: true, force: true });
|
|
392
|
-
db.close();
|
|
393
|
-
|
|
394
|
-
// ── Summary ─────────────────────────────────────────────────────────────────
|
|
395
|
-
console.log(`\n${"─".repeat(50)}`);
|
|
396
|
-
console.log(`Results: ${pass} passed, ${fail} failed`);
|
|
397
|
-
process.exit(fail > 0 ? 1 : 0);
|
package/scripts/uninstall.sh
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# ndomo uninstaller — removes agents, skills, and config from ~/.config/opencode/
|
|
3
|
-
# Usage: ./uninstall.sh [--keep-data]
|
|
4
|
-
|
|
5
|
-
set -euo pipefail
|
|
6
|
-
|
|
7
|
-
# ── Colors ────────────────────────────────────────────────────────────────────
|
|
8
|
-
RED='\033[0;31m'
|
|
9
|
-
GREEN='\033[0;32m'
|
|
10
|
-
YELLOW='\033[1;33m'
|
|
11
|
-
BLUE='\033[0;34m'
|
|
12
|
-
BOLD='\033[1m'
|
|
13
|
-
NC='\033[0m' # No Color
|
|
14
|
-
|
|
15
|
-
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
16
|
-
info() { printf "${BLUE}[info]${NC} %s\n" "$*"; }
|
|
17
|
-
ok() { printf "${GREEN}[ok]${NC} %s\n" "$*"; }
|
|
18
|
-
warn() { printf "${YELLOW}[warn]${NC} %s\n" "$*"; }
|
|
19
|
-
err() { printf "${RED}[error]${NC} %s\n" "$*" >&2; }
|
|
20
|
-
|
|
21
|
-
# ── Parse flags ───────────────────────────────────────────────────────────────
|
|
22
|
-
KEEP_DATA=false
|
|
23
|
-
|
|
24
|
-
for arg in "$@"; do
|
|
25
|
-
case "$arg" in
|
|
26
|
-
--keep-data) KEEP_DATA=true ;;
|
|
27
|
-
--help|-h)
|
|
28
|
-
echo "Usage: ./uninstall.sh [--keep-data]"
|
|
29
|
-
echo ""
|
|
30
|
-
echo "Options:"
|
|
31
|
-
echo " --keep-data Skip removal of .slim/ directory and opencode-mem data"
|
|
32
|
-
exit 0
|
|
33
|
-
;;
|
|
34
|
-
*)
|
|
35
|
-
err "Unknown option: $arg (try --help)"
|
|
36
|
-
exit 1
|
|
37
|
-
;;
|
|
38
|
-
esac
|
|
39
|
-
done
|
|
40
|
-
|
|
41
|
-
# ── Paths ─────────────────────────────────────────────────────────────────────
|
|
42
|
-
CONFIG_DIR="${HOME}/.config/opencode"
|
|
43
|
-
|
|
44
|
-
# ── Agent files to remove ─────────────────────────────────────────────────────
|
|
45
|
-
AGENTS=(
|
|
46
|
-
"foreman.md"
|
|
47
|
-
"scout.md"
|
|
48
|
-
"scribe.md"
|
|
49
|
-
"painter.md"
|
|
50
|
-
"smith.md"
|
|
51
|
-
"sage.md"
|
|
52
|
-
"guild.md"
|
|
53
|
-
"go-smith.md"
|
|
54
|
-
"js-smith.md"
|
|
55
|
-
"python-smith.md"
|
|
56
|
-
"vue-smith.md"
|
|
57
|
-
"zig-smith.md"
|
|
58
|
-
"rust-smith.md"
|
|
59
|
-
"inspector.md"
|
|
60
|
-
"chronicler.md"
|
|
61
|
-
"ci-smith.md"
|
|
62
|
-
"craftsman.md"
|
|
63
|
-
"deploy-smith.md"
|
|
64
|
-
"ops-scout.md"
|
|
65
|
-
"ranger.md"
|
|
66
|
-
"release-smith.md"
|
|
67
|
-
"warden.md"
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
# ── Skill directories to remove ───────────────────────────────────────────────
|
|
71
|
-
SKILLS=(
|
|
72
|
-
"caveman"
|
|
73
|
-
"bash-scripting"
|
|
74
|
-
"cavecrew"
|
|
75
|
-
"deepwork"
|
|
76
|
-
"reflect"
|
|
77
|
-
"worktrees"
|
|
78
|
-
"dcp-integration"
|
|
79
|
-
"mem-recall"
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
# ── Config files to remove ────────────────────────────────────────────────────
|
|
83
|
-
CONFIGS=(
|
|
84
|
-
"ndomo.json"
|
|
85
|
-
"ndomo.schema.json"
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
echo ""
|
|
89
|
-
printf "${YELLOW}${BOLD}ndomo uninstaller${NC}\n"
|
|
90
|
-
echo ""
|
|
91
|
-
|
|
92
|
-
# ── Step 1: Remove agent files ───────────────────────────────────────────────
|
|
93
|
-
info "Removing agent files..."
|
|
94
|
-
AGENT_DIR="${CONFIG_DIR}/agent"
|
|
95
|
-
AGENT_REMOVED=0
|
|
96
|
-
for name in "${AGENTS[@]}"; do
|
|
97
|
-
target="${AGENT_DIR}/${name}"
|
|
98
|
-
if [[ -f "$target" ]]; then
|
|
99
|
-
rm -f "$target"
|
|
100
|
-
ok "Removed agent/${name}"
|
|
101
|
-
AGENT_REMOVED=$((AGENT_REMOVED + 1))
|
|
102
|
-
fi
|
|
103
|
-
done
|
|
104
|
-
if [[ $AGENT_REMOVED -eq 0 ]]; then
|
|
105
|
-
info "No ndomo agent files found"
|
|
106
|
-
else
|
|
107
|
-
ok "Removed ${AGENT_REMOVED} agent file(s)"
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
# ── Step 2: Remove skill directories ─────────────────────────────────────────
|
|
111
|
-
info "Removing skill directories..."
|
|
112
|
-
SKILL_DIR="${CONFIG_DIR}/skills"
|
|
113
|
-
SKILL_REMOVED=0
|
|
114
|
-
for name in "${SKILLS[@]}"; do
|
|
115
|
-
target="${SKILL_DIR}/${name}"
|
|
116
|
-
if [[ -d "$target" ]]; then
|
|
117
|
-
rm -rf "$target"
|
|
118
|
-
ok "Removed skills/${name}/"
|
|
119
|
-
SKILL_REMOVED=$((SKILL_REMOVED + 1))
|
|
120
|
-
fi
|
|
121
|
-
done
|
|
122
|
-
if [[ $SKILL_REMOVED -eq 0 ]]; then
|
|
123
|
-
info "No ndomo skill directories found"
|
|
124
|
-
else
|
|
125
|
-
ok "Removed ${SKILL_REMOVED} skill directory(ies)"
|
|
126
|
-
fi
|
|
127
|
-
|
|
128
|
-
# ── Step 3: Remove config files ──────────────────────────────────────────────
|
|
129
|
-
info "Removing config files..."
|
|
130
|
-
CONFIG_REMOVED=0
|
|
131
|
-
for name in "${CONFIGS[@]}"; do
|
|
132
|
-
target="${CONFIG_DIR}/${name}"
|
|
133
|
-
if [[ -f "$target" ]]; then
|
|
134
|
-
rm -f "$target"
|
|
135
|
-
ok "Removed ${name}"
|
|
136
|
-
CONFIG_REMOVED=$((CONFIG_REMOVED + 1))
|
|
137
|
-
fi
|
|
138
|
-
done
|
|
139
|
-
if [[ $CONFIG_REMOVED -eq 0 ]]; then
|
|
140
|
-
info "No ndomo config files found"
|
|
141
|
-
else
|
|
142
|
-
ok "Removed ${CONFIG_REMOVED} config file(s)"
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# ── Step 4: Data removal (.slim/, mem data) ──────────────────────────────────
|
|
146
|
-
SLIM_DIR="${HOME}/.slim"
|
|
147
|
-
MEM_DIR="${HOME}/.ndomo/mem"
|
|
148
|
-
|
|
149
|
-
if [[ "$KEEP_DATA" == true ]]; then
|
|
150
|
-
info "Keeping data (--keep-data specified)"
|
|
151
|
-
else
|
|
152
|
-
HAS_DATA=false
|
|
153
|
-
[[ -d "$SLIM_DIR" ]] && HAS_DATA=true
|
|
154
|
-
[[ -d "$MEM_DIR" ]] && HAS_DATA=true
|
|
155
|
-
|
|
156
|
-
if [[ "$HAS_DATA" == true ]]; then
|
|
157
|
-
echo ""
|
|
158
|
-
printf "${YELLOW}${BOLD}Data directories found:${NC}\n"
|
|
159
|
-
[[ -d "$SLIM_DIR" ]] && printf " - ${SLIM_DIR}\n"
|
|
160
|
-
[[ -d "$MEM_DIR" ]] && printf " - ${MEM_DIR}\n"
|
|
161
|
-
echo ""
|
|
162
|
-
printf "${YELLOW}These may contain session data and memories.${NC}\n"
|
|
163
|
-
printf "${YELLOW}This action cannot be undone.${NC}\n"
|
|
164
|
-
echo ""
|
|
165
|
-
read -rp "Remove data directories? [y/N] " confirm
|
|
166
|
-
case "$confirm" in
|
|
167
|
-
[yY]|[yY][eE][sS])
|
|
168
|
-
if [[ -d "$SLIM_DIR" ]]; then
|
|
169
|
-
rm -rf "$SLIM_DIR"
|
|
170
|
-
ok "Removed ${SLIM_DIR}"
|
|
171
|
-
fi
|
|
172
|
-
if [[ -d "$MEM_DIR" ]]; then
|
|
173
|
-
rm -rf "$MEM_DIR"
|
|
174
|
-
ok "Removed ${MEM_DIR}"
|
|
175
|
-
fi
|
|
176
|
-
;;
|
|
177
|
-
*)
|
|
178
|
-
info "Skipping data removal"
|
|
179
|
-
;;
|
|
180
|
-
esac
|
|
181
|
-
else
|
|
182
|
-
info "No data directories found to clean"
|
|
183
|
-
fi
|
|
184
|
-
fi
|
|
185
|
-
|
|
186
|
-
# ── Step 5: Remove ndomo package from node_modules/ ─────────────────────────
|
|
187
|
-
info "Removing ndomo package from OpenCode config..."
|
|
188
|
-
if [[ -L "$CONFIG_DIR/node_modules/ndomo" ]] || [[ -e "$CONFIG_DIR/node_modules/ndomo" ]]; then
|
|
189
|
-
rm -rf "$CONFIG_DIR/node_modules/ndomo"
|
|
190
|
-
ok "Removed ndomo from $CONFIG_DIR/node_modules/"
|
|
191
|
-
else
|
|
192
|
-
info "No ndomo in $CONFIG_DIR/node_modules/ found"
|
|
193
|
-
fi
|
|
194
|
-
|
|
195
|
-
if [[ -f "$CONFIG_DIR/package.json" ]] && command -v jq &>/dev/null; then
|
|
196
|
-
jq 'del(.dependencies.ndomo)' "$CONFIG_DIR/package.json" > "$CONFIG_DIR/package.json.tmp" && mv "$CONFIG_DIR/package.json.tmp" "$CONFIG_DIR/package.json"
|
|
197
|
-
ok "Removed ndomo entry from $CONFIG_DIR/package.json"
|
|
198
|
-
fi
|
|
199
|
-
|
|
200
|
-
# ── Step 6: Remove custom tools symlink ─────────────────────────────────────
|
|
201
|
-
TOOLS_DIR="${CONFIG_DIR}/tools"
|
|
202
|
-
if [[ -L "$TOOLS_DIR" ]]; then
|
|
203
|
-
rm "$TOOLS_DIR"
|
|
204
|
-
ok "Removed custom tools symlink"
|
|
205
|
-
elif [[ -d "$TOOLS_DIR" ]]; then
|
|
206
|
-
info "Custom tools directory at $TOOLS_DIR is not a symlink — skipping"
|
|
207
|
-
else
|
|
208
|
-
info "No custom tools symlink at $TOOLS_DIR"
|
|
209
|
-
fi
|
|
210
|
-
|
|
211
|
-
# ── Summary ──────────────────────────────────────────────────────────────────
|
|
212
|
-
echo ""
|
|
213
|
-
printf "${GREEN}${BOLD}════════════════════════════════════════${NC}\n"
|
|
214
|
-
printf "${GREEN}${BOLD} ndomo uninstalled${NC}\n"
|
|
215
|
-
printf "${GREEN}${BOLD}════════════════════════════════════════${NC}\n"
|
|
216
|
-
echo ""
|
|
217
|
-
printf "${BOLD}Removed:${NC}\n"
|
|
218
|
-
printf " Agents: %d file(s)\n" "$AGENT_REMOVED"
|
|
219
|
-
printf " Skills: %d directory(ies)\n" "$SKILL_REMOVED"
|
|
220
|
-
printf " Config: %d file(s)\n" "$CONFIG_REMOVED"
|
|
221
|
-
echo ""
|
|
222
|
-
printf "${BOLD}Note:${NC} This does NOT uninstall opencode-mem or @tarquinen/opencode-dcp.\n"
|
|
223
|
-
printf " Those are separate plugins managed by opencode.\n"
|
|
224
|
-
echo ""
|
package/src/index.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ndomo — OpenCode multi-agent plugin.
|
|
3
|
-
*
|
|
4
|
-
* Entry point. Import as a plugin in opencode.json:
|
|
5
|
-
* ```jsonc
|
|
6
|
-
* { "plugin": ["ndomo"] }
|
|
7
|
-
* ```
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export type {
|
|
11
|
-
BackgroundTask,
|
|
12
|
-
DispatchOptions,
|
|
13
|
-
IntegrityReport,
|
|
14
|
-
MemoryEntry,
|
|
15
|
-
ReconciliationReport,
|
|
16
|
-
RoutingDecision,
|
|
17
|
-
TaskRequest,
|
|
18
|
-
TaskResult,
|
|
19
|
-
Worktree,
|
|
20
|
-
WorktreeState,
|
|
21
|
-
} from "./lib.ts";
|
|
22
|
-
export {
|
|
23
|
-
BackgroundDispatcher,
|
|
24
|
-
canRunParallel,
|
|
25
|
-
cavemanCompress,
|
|
26
|
-
createWorktree,
|
|
27
|
-
getProjectTag,
|
|
28
|
-
listActive,
|
|
29
|
-
memorySearchOptions,
|
|
30
|
-
prepareForMemory,
|
|
31
|
-
reconcileResults,
|
|
32
|
-
removeWorktree,
|
|
33
|
-
routeTask,
|
|
34
|
-
shouldStoreMemory,
|
|
35
|
-
verifyIntegrity,
|
|
36
|
-
} from "./lib.ts";
|
|
37
|
-
export { NdomoPlugin, type NdomoPluginOptions } from "./plugin.ts";
|