clementine-agent 1.0.98 → 1.0.99

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.
@@ -5,10 +5,19 @@
5
5
  * Each exports a `migration` object conforming to VaultMigration.
6
6
  * State is tracked in ~/.clementine/.vault-migrations.json.
7
7
  */
8
- import type { VaultMigrationSummary } from './types.js';
8
+ import type { MigrationContext, VaultMigrationSummary } from './types.js';
9
9
  /**
10
- * Run all pending vault migrations against the user's vault.
11
- * Idempotent safe to call multiple times.
10
+ * Run all pending migrations. Each is dispatched based on its shape:
11
+ * - VaultMigration (no `kind` field) apply(vaultDir)
12
+ * - Migration (has `kind` field) → apply(MigrationContext)
13
+ *
14
+ * Idempotent — safe to call multiple times. State is tracked in
15
+ * `~/.clementine/.vault-migrations.json` (kept this filename for back-compat).
16
+ */
17
+ export declare function runMigrations(ctx: MigrationContext, backupDir?: string): Promise<VaultMigrationSummary>;
18
+ /**
19
+ * Back-compat wrapper: existing call sites pass just vaultDir. Builds a
20
+ * default MigrationContext from BASE_DIR and PKG_DIR.
12
21
  */
13
22
  export declare function runVaultMigrations(vaultDir: string, backupDir?: string): Promise<VaultMigrationSummary>;
14
23
  //# sourceMappingURL=runner.d.ts.map
@@ -8,7 +8,7 @@
8
8
  import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, copyFileSync } from 'node:fs';
9
9
  import path from 'node:path';
10
10
  import pino from 'pino';
11
- import { VAULT_MIGRATIONS_STATE } from '../config.js';
11
+ import { BASE_DIR, PKG_DIR, VAULT_MIGRATIONS_STATE } from '../config.js';
12
12
  const logger = pino({ name: 'clementine.vault-migrations' });
13
13
  /** Load the migration state file. Returns empty state if missing or corrupt. */
14
14
  function loadState() {
@@ -41,6 +41,7 @@ function backupFile(filePath, backupDir) {
41
41
  /**
42
42
  * Discover all migration modules in the compiled dist/vault-migrations/ directory.
43
43
  * Returns them sorted by filename (numeric prefix ensures correct order).
44
+ * Accepts both VaultMigration (legacy) and Migration (multi-target) shapes.
44
45
  */
45
46
  async function discoverMigrations() {
46
47
  const migrations = [];
@@ -64,16 +65,24 @@ async function discoverMigrations() {
64
65
  }
65
66
  }
66
67
  catch (err) {
67
- logger.warn({ file, err }, 'Failed to load vault migration');
68
+ logger.warn({ file, err }, 'Failed to load migration');
68
69
  }
69
70
  }
70
71
  return migrations;
71
72
  }
73
+ /** Discriminator — true if migration uses the new MigrationContext shape. */
74
+ function isContextMigration(m) {
75
+ return typeof m.kind === 'string';
76
+ }
72
77
  /**
73
- * Run all pending vault migrations against the user's vault.
74
- * Idempotent safe to call multiple times.
78
+ * Run all pending migrations. Each is dispatched based on its shape:
79
+ * - VaultMigration (no `kind` field) apply(vaultDir)
80
+ * - Migration (has `kind` field) → apply(MigrationContext)
81
+ *
82
+ * Idempotent — safe to call multiple times. State is tracked in
83
+ * `~/.clementine/.vault-migrations.json` (kept this filename for back-compat).
75
84
  */
76
- export async function runVaultMigrations(vaultDir, backupDir) {
85
+ export async function runMigrations(ctx, backupDir) {
77
86
  const summary = {
78
87
  applied: [],
79
88
  skipped: [],
@@ -87,16 +96,14 @@ export async function runVaultMigrations(vaultDir, backupDir) {
87
96
  if (migrations.length === 0)
88
97
  return summary;
89
98
  for (const migration of migrations) {
90
- // Skip if already recorded as applied
91
99
  if (appliedIds.has(migration.id)) {
92
100
  summary.alreadyRun.push(migration.id);
93
101
  continue;
94
102
  }
95
103
  try {
96
- // Back up target files before migration
97
- if (backupDir) {
98
- // Best-effort backup of the vault's 00-System dir (most common target)
99
- const systemDir = path.join(vaultDir, '00-System');
104
+ // Best-effort backup of the vault's 00-System dir for vault-style migrations
105
+ if (backupDir && !isContextMigration(migration)) {
106
+ const systemDir = path.join(ctx.vaultDir, '00-System');
100
107
  if (existsSync(systemDir)) {
101
108
  const systemFiles = readdirSync(systemDir).filter(f => f.endsWith('.md'));
102
109
  for (const f of systemFiles) {
@@ -104,36 +111,36 @@ export async function runVaultMigrations(vaultDir, backupDir) {
104
111
  }
105
112
  }
106
113
  }
107
- const result = migration.apply(vaultDir);
108
- if (result.applied) {
114
+ const result = isContextMigration(migration)
115
+ ? await migration.apply(ctx)
116
+ : migration.apply(ctx.vaultDir);
117
+ const r = result;
118
+ if (r.applied) {
109
119
  summary.applied.push(migration.id);
110
- state.applied.push({
111
- id: migration.id,
112
- appliedAt: new Date().toISOString(),
113
- result: 'applied',
114
- });
115
- logger.info({ id: migration.id, details: result.details }, 'Vault migration applied');
120
+ state.applied.push({ id: migration.id, appliedAt: new Date().toISOString(), result: 'applied' });
121
+ logger.info({ id: migration.id, details: r.details }, 'Migration applied');
116
122
  }
117
- else if (result.skipped) {
123
+ else if (r.skipped) {
118
124
  summary.skipped.push(migration.id);
119
- // Record as applied so we don't re-check every update
120
- state.applied.push({
121
- id: migration.id,
122
- appliedAt: new Date().toISOString(),
123
- result: 'skipped',
124
- });
125
- logger.info({ id: migration.id, details: result.details }, 'Vault migration skipped (already present)');
125
+ state.applied.push({ id: migration.id, appliedAt: new Date().toISOString(), result: 'skipped' });
126
+ logger.info({ id: migration.id, details: r.details }, 'Migration skipped (already present)');
126
127
  }
127
128
  }
128
129
  catch (err) {
129
130
  const errMsg = String(err).slice(0, 200);
130
131
  summary.failed.push(migration.id);
131
132
  summary.errors.push({ id: migration.id, error: errMsg });
132
- // Do NOT record as applied — will retry on next update
133
- logger.warn({ id: migration.id, err }, 'Vault migration failed');
133
+ logger.warn({ id: migration.id, err }, 'Migration failed');
134
134
  }
135
135
  }
136
136
  saveState(state);
137
137
  return summary;
138
138
  }
139
+ /**
140
+ * Back-compat wrapper: existing call sites pass just vaultDir. Builds a
141
+ * default MigrationContext from BASE_DIR and PKG_DIR.
142
+ */
143
+ export async function runVaultMigrations(vaultDir, backupDir) {
144
+ return runMigrations({ vaultDir, baseDir: BASE_DIR, pkgDir: PKG_DIR }, backupDir);
145
+ }
139
146
  //# sourceMappingURL=runner.js.map
@@ -1,10 +1,27 @@
1
1
  /**
2
- * Vault migration system — types and interfaces.
2
+ * Migration system — types and interfaces.
3
3
  *
4
- * Vault migrations ship structural changes to user vault files (SOUL.md,
5
- * AGENTS.md, etc.) alongside code updates. Each migration is idempotent
6
- * and runs once during `clementine update`.
4
+ * Migrations ship structural changes to user data files (SOUL.md, AGENTS.md,
5
+ * advisor rules, prompt overrides, clementine.json, etc.) alongside code
6
+ * updates. Each migration is idempotent and runs once during `clementine update`.
7
+ *
8
+ * Two shapes coexist for back-compat:
9
+ * - VaultMigration: takes vaultDir only (the original shape, used by 0001-0003)
10
+ * - Migration: takes a MigrationContext { vaultDir, baseDir, pkgDir } so a
11
+ * migration can touch advisor-rules/, prompt-overrides/, clementine.json,
12
+ * etc. — anything under ~/.clementine/, not just the vault.
13
+ *
14
+ * Discriminator: `kind` field. Vault-style migrations omit it.
7
15
  */
16
+ export interface MigrationContext {
17
+ /** ~/.clementine/vault */
18
+ vaultDir: string;
19
+ /** ~/.clementine/ (data home — config, state, cache, advisor-rules, etc.) */
20
+ baseDir: string;
21
+ /** Package install root (where dist/ lives). For migrations that ship templates. */
22
+ pkgDir: string;
23
+ }
24
+ /** Original vault-only migration shape. Existing 0001-0003 use this. */
8
25
  export interface VaultMigration {
9
26
  /** Unique ID matching the filename (e.g., "0001-add-execution-framework"). */
10
27
  id: string;
@@ -13,6 +30,15 @@ export interface VaultMigration {
13
30
  /** Apply the migration. Must be idempotent — safe to re-run. */
14
31
  apply: (vaultDir: string) => MigrationResult;
15
32
  }
33
+ /** Multi-target migration shape. Use for any data outside vault/. */
34
+ export interface Migration {
35
+ /** Discriminator. Vault-only migrations omit this and use VaultMigration. */
36
+ kind: 'vault' | 'config' | 'advisor-rules' | 'prompt-overrides' | 'multi';
37
+ id: string;
38
+ description: string;
39
+ apply: (ctx: MigrationContext) => MigrationResult | Promise<MigrationResult>;
40
+ }
41
+ export type AnyMigration = VaultMigration | Migration;
16
42
  export interface MigrationResult {
17
43
  /** True if changes were written to disk. */
18
44
  applied: boolean;
@@ -39,4 +65,6 @@ export interface VaultMigrationSummary {
39
65
  error: string;
40
66
  }>;
41
67
  }
68
+ /** Alias — same shape, generalized name now that migrations aren't vault-only. */
69
+ export type MigrationSummary = VaultMigrationSummary;
42
70
  //# sourceMappingURL=types.d.ts.map
@@ -1,9 +1,17 @@
1
1
  /**
2
- * Vault migration system — types and interfaces.
2
+ * Migration system — types and interfaces.
3
3
  *
4
- * Vault migrations ship structural changes to user vault files (SOUL.md,
5
- * AGENTS.md, etc.) alongside code updates. Each migration is idempotent
6
- * and runs once during `clementine update`.
4
+ * Migrations ship structural changes to user data files (SOUL.md, AGENTS.md,
5
+ * advisor rules, prompt overrides, clementine.json, etc.) alongside code
6
+ * updates. Each migration is idempotent and runs once during `clementine update`.
7
+ *
8
+ * Two shapes coexist for back-compat:
9
+ * - VaultMigration: takes vaultDir only (the original shape, used by 0001-0003)
10
+ * - Migration: takes a MigrationContext { vaultDir, baseDir, pkgDir } so a
11
+ * migration can touch advisor-rules/, prompt-overrides/, clementine.json,
12
+ * etc. — anything under ~/.clementine/, not just the vault.
13
+ *
14
+ * Discriminator: `kind` field. Vault-style migrations omit it.
7
15
  */
8
16
  export {};
9
17
  //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.0.98",
3
+ "version": "1.0.99",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",