@cleocode/core 2026.4.12 → 2026.4.13

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.
@@ -0,0 +1,119 @@
1
+ /**
2
+ * T310-readiness gate: detect conduit.db vs legacy signaldock.db at project tier.
3
+ *
4
+ * T311 backup/restore code paths reference `.cleo/conduit.db` (project tier) and
5
+ * `$XDG_DATA_HOME/cleo/signaldock.db` (global tier) per ADR-037. A project that
6
+ * has not yet been migrated (still has `.cleo/signaldock.db` without a
7
+ * `.cleo/conduit.db`) will confuse T311 export/import commands. This gate runs
8
+ * as a precondition on every T311 CLI verb.
9
+ *
10
+ * @task T342
11
+ * @epic T311
12
+ * @why T311 export/import references .cleo/conduit.db (project tier) and
13
+ * $XDG_DATA_HOME/cleo/signaldock.db (global tier) per ADR-037.
14
+ * If the current project is still on the pre-T310 topology, T311
15
+ * commands must surface a clear error telling the user to run a
16
+ * cleo command first to trigger migration.
17
+ * @what Throws T310MigrationRequiredError with instructions if legacy
18
+ * signaldock.db exists AND conduit.db does not.
19
+ */
20
+
21
+ import { existsSync } from 'node:fs';
22
+ import { join } from 'node:path';
23
+ import { getProjectRoot } from '../paths.js';
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Error class
27
+ // ---------------------------------------------------------------------------
28
+
29
+ /**
30
+ * Thrown by `assertT310Ready` when the current project is still on the
31
+ * pre-T310 topology: `.cleo/signaldock.db` is present but `.cleo/conduit.db`
32
+ * is absent. T311 commands cannot proceed until migration has run.
33
+ *
34
+ * @task T342
35
+ * @epic T311
36
+ */
37
+ export class T310MigrationRequiredError extends Error {
38
+ /**
39
+ * @param projectRoot - Absolute path to the project root that needs migration.
40
+ */
41
+ constructor(public readonly projectRoot: string) {
42
+ super(
43
+ `T310 migration required: .cleo/signaldock.db still exists at ${projectRoot} ` +
44
+ `without a .cleo/conduit.db. Run any cleo command from within the project ` +
45
+ `(e.g. \`cleo version\`) to trigger the automatic T310 migration, then retry.`,
46
+ );
47
+ this.name = 'T310MigrationRequiredError';
48
+ }
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Public API
53
+ // ---------------------------------------------------------------------------
54
+
55
+ /**
56
+ * Asserts the current project is on the post-T310 topology. Does nothing
57
+ * if conduit.db exists (migration already ran) OR if no legacy signaldock.db
58
+ * exists (fresh install — no migration needed).
59
+ *
60
+ * Throws when legacy signaldock.db is present AND conduit.db is absent,
61
+ * which indicates the project has not yet been migrated to the T310 topology
62
+ * expected by T311 backup/restore commands.
63
+ *
64
+ * @param projectRoot - Absolute path to the project root. Defaults to
65
+ * `getProjectRoot()` (walks ancestors for `.cleo/` sentinel).
66
+ * @throws {T310MigrationRequiredError} if legacy signaldock.db exists
67
+ * without conduit.db at the project tier.
68
+ *
69
+ * @task T342
70
+ * @epic T311
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * // Precondition guard at the top of every T311 CLI verb handler:
75
+ * assertT310Ready();
76
+ * ```
77
+ */
78
+ export function assertT310Ready(projectRoot?: string): void {
79
+ const root = projectRoot ?? getProjectRoot();
80
+ const legacyPath = join(root, '.cleo', 'signaldock.db');
81
+ const conduitPath = join(root, '.cleo', 'conduit.db');
82
+
83
+ if (existsSync(legacyPath) && !existsSync(conduitPath)) {
84
+ throw new T310MigrationRequiredError(root);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Returns true if T311 commands can safely run on the current project.
90
+ *
91
+ * This is the non-throwing companion to `assertT310Ready`. Returns false
92
+ * only when the pre-T310 topology is detected (legacy signaldock.db exists
93
+ * without conduit.db). All other states — fresh installs, fully-migrated
94
+ * projects — return true.
95
+ *
96
+ * @param projectRoot - Absolute path to the project root. Defaults to
97
+ * `getProjectRoot()` (walks ancestors for `.cleo/` sentinel).
98
+ * @returns `true` if the project is on the post-T310 topology or is a fresh
99
+ * install; `false` if migration is required.
100
+ *
101
+ * @task T342
102
+ * @epic T311
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * if (!isT310Ready()) {
107
+ * console.error('Run `cleo version` to trigger T310 migration first.');
108
+ * }
109
+ * ```
110
+ */
111
+ export function isT310Ready(projectRoot?: string): boolean {
112
+ try {
113
+ assertT310Ready(projectRoot);
114
+ return true;
115
+ } catch (err) {
116
+ if (err instanceof T310MigrationRequiredError) return false;
117
+ throw err;
118
+ }
119
+ }