@compilr-dev/cli 0.5.11 → 0.5.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.
Files changed (36) hide show
  1. package/dist/.tsbuildinfo.app +1 -1
  2. package/dist/.tsbuildinfo.data +1 -1
  3. package/dist/.tsbuildinfo.domain +1 -1
  4. package/dist/.tsbuildinfo.foundation +1 -1
  5. package/dist/agent.js +1 -1
  6. package/dist/anchors/index.d.ts +1 -1
  7. package/dist/anchors/index.js +1 -1
  8. package/dist/anchors/project-anchors.d.ts +9 -60
  9. package/dist/anchors/project-anchors.js +31 -173
  10. package/dist/commands-v2/handlers/session.js +2 -2
  11. package/dist/commands-v2/types.d.ts +1 -2
  12. package/dist/compilr-diff-companion.vsix +0 -0
  13. package/dist/multi-agent/capability-loader.js +1 -1
  14. package/dist/multi-agent/checkpointer.d.ts +5 -4
  15. package/dist/multi-agent/checkpointer.js +25 -5
  16. package/dist/multi-agent/custom-agents.d.ts +5 -62
  17. package/dist/multi-agent/custom-agents.js +10 -136
  18. package/dist/multi-agent/delegation-tracker.d.ts +5 -146
  19. package/dist/multi-agent/delegation-tracker.js +7 -218
  20. package/dist/multi-agent/index.d.ts +14 -18
  21. package/dist/multi-agent/index.js +22 -23
  22. package/dist/repl-v2.js +1 -1
  23. package/dist/settings/index.d.ts +3 -14
  24. package/dist/settings/index.js +5 -32
  25. package/dist/settings/mcp-config.d.ts +7 -54
  26. package/dist/settings/mcp-config.js +18 -104
  27. package/dist/tools/delegation-status.js +1 -1
  28. package/dist/tools/platform-adapter.d.ts +2 -12
  29. package/dist/tools/platform-adapter.js +52 -171
  30. package/dist/ui/overlay/impl/background-overlay-v2.d.ts +1 -1
  31. package/dist/ui/overlay/impl/custom-agent-form-overlay-v2.d.ts +1 -1
  32. package/dist/ui/overlay/impl/custom-agent-form-overlay-v2.js +1 -4
  33. package/dist/ui/overlay/impl/delegations-overlay-v2.js +1 -1
  34. package/dist/ui/overlay/impl/team-overlay-v2.d.ts +1 -1
  35. package/dist/ui/overlay/impl/team-overlay-v2.js +1 -1
  36. package/package.json +2 -2
package/dist/repl-v2.js CHANGED
@@ -2746,7 +2746,7 @@ export class ReplV2 {
2746
2746
  formatCompletionMessage(events) {
2747
2747
  const lines = ['[DELEGATION COMPLETIONS]', ''];
2748
2748
  for (const event of events) {
2749
- const delegation = getDelegationTracker().get(event.delegationId);
2749
+ const delegation = getDelegationTracker().getDelegation(event.delegationId);
2750
2750
  const agentLabel = this.team?.get(event.agentId)?.displayName ?? event.agentId;
2751
2751
  const status = event.status === 'completed' ? 'COMPLETED' : 'FAILED';
2752
2752
  lines.push(`## $${event.agentId} (${agentLabel}) — ${status}`);
@@ -4,8 +4,8 @@
4
4
  * Centralized settings management for the CLI.
5
5
  * Persistence in ~/.compilr-dev/settings.json
6
6
  */
7
- export type PermissionMode = 'normal' | 'plan' | 'auto-accept';
8
- export type PermissionLevel = 'always' | 'session' | 'once' | 'deny';
7
+ export type { PermissionRule, PermissionMode, PermissionLevel } from '@compilr-dev/sdk';
8
+ import type { PermissionRule, PermissionMode, PermissionLevel } from '@compilr-dev/sdk';
9
9
  export type NotificationMode = 'auto' | 'bell' | 'disabled';
10
10
  export type ProviderType = 'auto' | 'claude' | 'openai' | 'gemini' | 'ollama' | 'together' | 'groq' | 'fireworks' | 'perplexity' | 'openrouter' | 'custom';
11
11
  export type StartupMode = 'menu' | 'repl';
@@ -14,16 +14,6 @@ export type MascotSetting = 'none' | 'random' | 'neutral' | 'thinking' | 'lookin
14
14
  export type ProjectSessionMode = 'auto' | 'ask' | 'fresh';
15
15
  export type CompactMode = 'active' | 'all' | 'auto';
16
16
  export type Verbosity = 'normal' | 'focused' | 'verbose';
17
- /**
18
- * Permission rule for a tool or pattern.
19
- * Supports wildcards: git_* matches git_commit, git_branch, etc.
20
- */
21
- export interface PermissionRule {
22
- toolName: string;
23
- level: PermissionLevel;
24
- description?: string;
25
- isDefault?: boolean;
26
- }
27
17
  export interface Settings {
28
18
  theme?: string;
29
19
  firstRunComplete: boolean;
@@ -220,7 +210,7 @@ export declare function setRoleTierDefault(roleId: string, tier: 'fast' | 'balan
220
210
  export declare function clearRoleTierDefault(roleId: string): void;
221
211
  /**
222
212
  * Find the permission rule that matches a tool name.
223
- * Supports wildcards: git_* matches git_commit, git_branch, etc.
213
+ * Delegates to SDK's findMatchingRule with the current settings rules.
224
214
  */
225
215
  export declare function findMatchingRule(toolName: string): PermissionRule | null;
226
216
  /**
@@ -231,4 +221,3 @@ export declare function permissionLevelToDisplay(level: PermissionLevel): string
231
221
  * Map display string to permission level
232
222
  */
233
223
  export declare function displayToPermissionLevel(display: string): PermissionLevel;
234
- export {};
@@ -7,6 +7,7 @@
7
7
  import * as fs from 'fs';
8
8
  import * as path from 'path';
9
9
  import * as os from 'os';
10
+ import { DEFAULT_PERMISSION_RULES, findMatchingRule as sdkFindMatchingRule, } from '@compilr-dev/sdk';
10
11
  // =============================================================================
11
12
  // Constants
12
13
  // =============================================================================
@@ -37,19 +38,8 @@ const DEFAULT_SETTINGS = {
37
38
  // Cycle defaults
38
39
  permissionMode: 'normal',
39
40
  notifications: 'auto',
40
- // Default permission rules
41
- permissionRules: [
42
- { toolName: 'bash', level: 'once', description: 'Execute shell commands', isDefault: true },
43
- { toolName: 'write_file', level: 'once', description: 'Write/create files', isDefault: true },
44
- { toolName: 'edit', level: 'once', description: 'Edit file contents', isDefault: true },
45
- { toolName: 'git_commit', level: 'once', description: 'Create git commits', isDefault: true },
46
- { toolName: 'git_branch', level: 'once', description: 'Create/delete branches', isDefault: true },
47
- { toolName: 'run_tests', level: 'once', description: 'Run test suite', isDefault: true },
48
- { toolName: 'run_lint', level: 'once', description: 'Run linter (may auto-fix)', isDefault: true },
49
- { toolName: 'read_file', level: 'always', description: 'Read files', isDefault: true },
50
- { toolName: 'glob', level: 'always', description: 'Find files by pattern', isDefault: true },
51
- { toolName: 'grep', level: 'always', description: 'Search file contents', isDefault: true },
52
- ],
41
+ // Default permission rules (from SDK — shared with desktop)
42
+ permissionRules: [...DEFAULT_PERMISSION_RULES],
53
43
  // Startup default
54
44
  startupMode: 'menu',
55
45
  // Project startup defaults
@@ -517,27 +507,10 @@ export function clearRoleTierDefault(roleId) {
517
507
  }
518
508
  /**
519
509
  * Find the permission rule that matches a tool name.
520
- * Supports wildcards: git_* matches git_commit, git_branch, etc.
510
+ * Delegates to SDK's findMatchingRule with the current settings rules.
521
511
  */
522
512
  export function findMatchingRule(toolName) {
523
- const rules = getPermissionRules();
524
- // First try exact match
525
- const exactMatch = rules.find(r => r.toolName === toolName);
526
- if (exactMatch)
527
- return exactMatch;
528
- // Then try wildcard patterns
529
- for (const rule of rules) {
530
- if (rule.toolName.includes('*')) {
531
- const pattern = rule.toolName
532
- .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special regex chars
533
- .replace(/\*/g, '.*'); // Convert * to .*
534
- const regex = new RegExp(`^${pattern}$`);
535
- if (regex.test(toolName)) {
536
- return rule;
537
- }
538
- }
539
- }
540
- return null;
513
+ return sdkFindMatchingRule(getPermissionRules(), toolName);
541
514
  }
542
515
  /**
543
516
  * Map permission level to display string
@@ -1,49 +1,11 @@
1
1
  /**
2
- * MCP Configuration Loader
2
+ * MCP Configuration Loader — CLI wrapper
3
3
  *
4
- * Loads MCP server configs from:
5
- * 1. Global: ~/.compilr-dev/mcp.json
6
- * 2. Per-project: <projectRoot>/.compilr-dev/mcp.json
7
- *
8
- * Project config overrides global config (by server name).
9
- * Format is compatible with Claude Desktop / Gemini CLI.
10
- */
11
- /**
12
- * Single MCP server entry in the config file.
13
- * If `command` is present → stdio transport.
14
- * If `url` is present → HTTP transport.
15
- */
16
- export interface MCPServerEntry {
17
- command?: string;
18
- args?: string[];
19
- env?: Record<string, string>;
20
- cwd?: string;
21
- url?: string;
22
- headers?: Record<string, string>;
23
- disabled?: boolean;
24
- timeout?: number;
25
- }
26
- /**
27
- * Shape of the mcp.json config file.
4
+ * Types and file I/O come from @compilr-dev/sdk.
5
+ * This module adds CLI-specific path helpers (global + per-project).
28
6
  */
29
- export interface MCPConfigFile {
30
- mcpServers: Record<string, MCPServerEntry>;
31
- }
32
- /**
33
- * Resolved MCP server config ready for MCPManager.
34
- * Uses the MCPServerConfig shape from @compilr-dev/agents.
35
- */
36
- export interface ResolvedMCPServer {
37
- name: string;
38
- transport: 'stdio' | 'http';
39
- command?: string;
40
- args?: string[];
41
- env?: Record<string, string>;
42
- cwd?: string;
43
- url?: string;
44
- headers?: Record<string, string>;
45
- timeout?: number;
46
- }
7
+ import { loadMCPServers } from '@compilr-dev/sdk';
8
+ export type { MCPServerEntry, MCPConfigFile, ResolvedMCPServer } from '@compilr-dev/sdk';
47
9
  /**
48
10
  * Get the global MCP config file path.
49
11
  */
@@ -55,22 +17,13 @@ export declare function getProjectMCPConfigPath(projectRoot: string): string;
55
17
  /**
56
18
  * Load MCP server configs from global and project-level config files.
57
19
  * Project configs override global by server name.
58
- *
59
- * @param projectRoot - Optional project root for per-project config
60
- * @returns Array of resolved MCP server configs
61
20
  */
62
- export declare function loadMCPConfig(projectRoot?: string): ResolvedMCPServer[];
21
+ export declare function loadMCPConfig(projectRoot?: string): ReturnType<typeof loadMCPServers>;
63
22
  /**
64
23
  * Get existing server names from all config files.
65
- * Useful for validating uniqueness when adding a new server.
66
24
  */
67
25
  export declare function getExistingServerNames(projectRoot?: string): Set<string>;
68
26
  /**
69
27
  * Save a new MCP server entry to the config file.
70
- *
71
- * @param name - Server name (unique identifier)
72
- * @param entry - Server configuration entry
73
- * @param scope - 'global' or 'project'
74
- * @param projectRoot - Required when scope is 'project'
75
28
  */
76
- export declare function saveMCPServer(name: string, entry: MCPServerEntry, scope: 'global' | 'project', projectRoot?: string): void;
29
+ export declare function saveMCPServer(name: string, entry: import('@compilr-dev/sdk').MCPServerEntry, scope: 'global' | 'project', projectRoot?: string): void;
@@ -1,18 +1,14 @@
1
1
  /**
2
- * MCP Configuration Loader
2
+ * MCP Configuration Loader — CLI wrapper
3
3
  *
4
- * Loads MCP server configs from:
5
- * 1. Global: ~/.compilr-dev/mcp.json
6
- * 2. Per-project: <projectRoot>/.compilr-dev/mcp.json
7
- *
8
- * Project config overrides global config (by server name).
9
- * Format is compatible with Claude Desktop / Gemini CLI.
4
+ * Types and file I/O come from @compilr-dev/sdk.
5
+ * This module adds CLI-specific path helpers (global + per-project).
10
6
  */
11
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
12
7
  import * as path from 'path';
8
+ import { loadMCPServers, saveMCPServerEntry, getServerNames, } from '@compilr-dev/sdk';
13
9
  import { getDataPath } from './paths.js';
14
10
  // =============================================================================
15
- // Config Loading
11
+ // CLI-specific path helpers
16
12
  // =============================================================================
17
13
  /**
18
14
  * Get the global MCP config file path.
@@ -26,118 +22,36 @@ export function getMCPConfigPath() {
26
22
  export function getProjectMCPConfigPath(projectRoot) {
27
23
  return path.join(projectRoot, '.compilr-dev', 'mcp.json');
28
24
  }
29
- /**
30
- * Read and parse an mcp.json file. Returns empty servers on any error.
31
- */
32
- function readMCPConfigFile(filePath) {
33
- try {
34
- if (!existsSync(filePath)) {
35
- return {};
36
- }
37
- const data = readFileSync(filePath, 'utf-8');
38
- const parsed = JSON.parse(data);
39
- if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {
40
- return parsed.mcpServers;
41
- }
42
- return {};
43
- }
44
- catch {
45
- return {};
46
- }
47
- }
48
- /**
49
- * Convert an MCPServerEntry to a ResolvedMCPServer.
50
- * Returns null if the entry is invalid (neither command nor url).
51
- */
52
- function resolveEntry(name, entry) {
53
- if (entry.disabled) {
54
- return null;
55
- }
56
- if (entry.command) {
57
- return {
58
- name,
59
- transport: 'stdio',
60
- command: entry.command,
61
- args: entry.args,
62
- env: entry.env,
63
- cwd: entry.cwd,
64
- timeout: entry.timeout,
65
- };
66
- }
67
- if (entry.url) {
68
- return {
69
- name,
70
- transport: 'http',
71
- url: entry.url,
72
- headers: entry.headers,
73
- timeout: entry.timeout,
74
- };
75
- }
76
- // Neither command nor url — invalid entry
77
- return null;
78
- }
25
+ // =============================================================================
26
+ // CLI-specific wrappers (delegate to SDK with resolved paths)
27
+ // =============================================================================
79
28
  /**
80
29
  * Load MCP server configs from global and project-level config files.
81
30
  * Project configs override global by server name.
82
- *
83
- * @param projectRoot - Optional project root for per-project config
84
- * @returns Array of resolved MCP server configs
85
31
  */
86
32
  export function loadMCPConfig(projectRoot) {
87
- // 1. Load global config
88
- const globalServers = readMCPConfigFile(getMCPConfigPath());
89
- // 2. Load project config (overrides global by name)
90
- const projectServers = projectRoot
91
- ? readMCPConfigFile(getProjectMCPConfigPath(projectRoot))
92
- : {};
93
- // 3. Merge: project overrides global
94
- const merged = { ...globalServers, ...projectServers };
95
- // 4. Resolve each entry
96
- const resolved = [];
97
- for (const [name, entry] of Object.entries(merged)) {
98
- const server = resolveEntry(name, entry);
99
- if (server) {
100
- resolved.push(server);
101
- }
33
+ const paths = [getMCPConfigPath()];
34
+ if (projectRoot) {
35
+ paths.push(getProjectMCPConfigPath(projectRoot));
102
36
  }
103
- return resolved;
37
+ return loadMCPServers(...paths);
104
38
  }
105
39
  /**
106
40
  * Get existing server names from all config files.
107
- * Useful for validating uniqueness when adding a new server.
108
41
  */
109
42
  export function getExistingServerNames(projectRoot) {
110
- const globalServers = readMCPConfigFile(getMCPConfigPath());
111
- const projectServers = projectRoot
112
- ? readMCPConfigFile(getProjectMCPConfigPath(projectRoot))
113
- : {};
114
- return new Set([...Object.keys(globalServers), ...Object.keys(projectServers)]);
43
+ const paths = [getMCPConfigPath()];
44
+ if (projectRoot) {
45
+ paths.push(getProjectMCPConfigPath(projectRoot));
46
+ }
47
+ return getServerNames(...paths);
115
48
  }
116
49
  /**
117
50
  * Save a new MCP server entry to the config file.
118
- *
119
- * @param name - Server name (unique identifier)
120
- * @param entry - Server configuration entry
121
- * @param scope - 'global' or 'project'
122
- * @param projectRoot - Required when scope is 'project'
123
51
  */
124
52
  export function saveMCPServer(name, entry, scope, projectRoot) {
125
53
  const configPath = scope === 'project' && projectRoot
126
54
  ? getProjectMCPConfigPath(projectRoot)
127
55
  : getMCPConfigPath();
128
- // Read existing config (or empty)
129
- let config = { mcpServers: {} };
130
- try {
131
- const raw = readFileSync(configPath, 'utf-8');
132
- const parsed = JSON.parse(raw);
133
- config = { mcpServers: parsed.mcpServers ?? {} };
134
- }
135
- catch {
136
- // File doesn't exist or invalid — start fresh
137
- }
138
- // Add/overwrite server entry
139
- config.mcpServers[name] = entry;
140
- // Ensure directory exists, write file
141
- mkdirSync(path.dirname(configPath), { recursive: true });
142
- writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
56
+ saveMCPServerEntry(configPath, name, entry);
143
57
  }
@@ -78,7 +78,7 @@ export const delegationStatusTool = defineTool({
78
78
  const tracker = await Promise.resolve(getDelegationTracker());
79
79
  // Single delegation lookup
80
80
  if (input.delegation_id) {
81
- const delegation = tracker.get(input.delegation_id);
81
+ const delegation = tracker.getDelegation(input.delegation_id);
82
82
  if (!delegation) {
83
83
  return {
84
84
  success: false,
@@ -1,19 +1,9 @@
1
1
  /**
2
2
  * Platform Adapter
3
3
  *
4
- * Thin adapter that wraps the CLI's synchronous repositories and services as async
5
- * implementations of the SDK's platform interfaces, wires CLI-specific hooks,
4
+ * Uses the SDK's SQLite repository implementations (shared with Desktop),
5
+ * wires CLI-specific hooks and services (anchors, artifacts, episodes),
6
6
  * and delegates all 32 platform tool definitions to the SDK.
7
- *
8
- * This replaces ~2,900+ lines of duplicated tool logic that previously lived in:
9
- * - project-db.ts (4 tools)
10
- * - workitem-db.ts (9 tools)
11
- * - document-db.ts (4 tools)
12
- * - plan-tools.ts (5 tools)
13
- * - backlog-wrappers.ts (2 tools)
14
- * - anchor-tools.ts (3 tools)
15
- * - artifact-tools.ts (4 tools)
16
- * - recall-work-tool.ts (1 tool)
17
7
  */
18
8
  /**
19
9
  * All 32 platform tools from the SDK:
@@ -1,183 +1,64 @@
1
1
  /**
2
2
  * Platform Adapter
3
3
  *
4
- * Thin adapter that wraps the CLI's synchronous repositories and services as async
5
- * implementations of the SDK's platform interfaces, wires CLI-specific hooks,
4
+ * Uses the SDK's SQLite repository implementations (shared with Desktop),
5
+ * wires CLI-specific hooks and services (anchors, artifacts, episodes),
6
6
  * and delegates all 32 platform tool definitions to the SDK.
7
- *
8
- * This replaces ~2,900+ lines of duplicated tool logic that previously lived in:
9
- * - project-db.ts (4 tools)
10
- * - workitem-db.ts (9 tools)
11
- * - document-db.ts (4 tools)
12
- * - plan-tools.ts (5 tools)
13
- * - backlog-wrappers.ts (2 tools)
14
- * - anchor-tools.ts (3 tools)
15
- * - artifact-tools.ts (4 tools)
16
- * - recall-work-tool.ts (1 tool)
17
7
  */
18
- import { createPlatformTools, } from '@compilr-dev/sdk';
8
+ import { createPlatformTools, createSQLiteRepositories, ProjectAnchorStore, } from '@compilr-dev/sdk';
19
9
  import { createFactoryTools } from '@compilr-dev/factory';
20
- import { projectRepository, workItemRepository, documentRepository, planRepository, } from '../db/repositories/index.js';
10
+ import { getDb } from '../db/index.js';
11
+ import { getDataPath, getSessionsPath } from '../settings/paths.js';
12
+ import { existsSync, rmSync } from 'fs';
13
+ import { join } from 'path';
21
14
  import { getCurrentProject, setCurrentProject } from './project-db.js';
22
15
  import { awardFirstProject, awardWorkItemCompletion } from '../games/coins.js';
23
- import { getActiveSharedContext } from '../multi-agent/activity.js';
24
- import { getGlobalAnchorManager, getAnchorManager, } from '../anchors/index.js';
16
+ import { getActiveSharedContext } from '@compilr-dev/sdk';
17
+ import { setAnchorStore } from '../anchors/index.js';
25
18
  import { getTeamCheckpointer, recordTeamActivity } from '../multi-agent/index.js';
26
19
  import { getGlobalEpisodeStore } from '../episodes/index.js';
27
20
  // =============================================================================
28
- // Async wrappers each method wraps a sync CLI repo call in Promise.resolve()
29
- // =============================================================================
30
- const asyncProjectRepo = {
31
- create: (input) => Promise.resolve(projectRepository.create(input)),
32
- getById: (id) => Promise.resolve(projectRepository.getById(id)),
33
- getByName: (name) => Promise.resolve(projectRepository.getByName(name)),
34
- getByPath: (path) => Promise.resolve(projectRepository.getByPath(path)),
35
- getByDocsPath: (docsPath) => Promise.resolve(projectRepository.getByDocsPath(docsPath)),
36
- findByPath: (searchPath) => Promise.resolve(projectRepository.findByPath(searchPath)),
37
- list: (options) => Promise.resolve(projectRepository.list(options)),
38
- update: (id, input) => Promise.resolve(projectRepository.update(id, input)),
39
- touch: (id) => { projectRepository.touch(id); return Promise.resolve(); },
40
- delete: (id) => Promise.resolve(projectRepository.delete(id)),
41
- archive: (id) => Promise.resolve(projectRepository.archive(id)),
42
- isNameAvailable: (name, excludeId) => Promise.resolve(projectRepository.isNameAvailable(name, excludeId)),
43
- getStatusCounts: () => Promise.resolve(projectRepository.getStatusCounts()),
44
- };
45
- const asyncWorkItemRepo = {
46
- create: (input) => Promise.resolve(workItemRepository.create(input)),
47
- getById: (id) => Promise.resolve(workItemRepository.getById(id)),
48
- getByItemId: (projectId, itemId) => Promise.resolve(workItemRepository.getByItemId(projectId, itemId)),
49
- query: (input) => Promise.resolve(workItemRepository.query(input)),
50
- getNext: (projectId, type) => Promise.resolve(workItemRepository.getNext(projectId, type)),
51
- update: (id, input) => Promise.resolve(workItemRepository.update(id, input)),
52
- delete: (id) => Promise.resolve(workItemRepository.delete(id)),
53
- getByOwner: (projectId, owner, status) => Promise.resolve(workItemRepository.getByOwner(projectId, owner, status)),
54
- getOwnerCounts: (projectId) => Promise.resolve(workItemRepository.getOwnerCounts(projectId)),
55
- getStatusCounts: (projectId) => Promise.resolve(workItemRepository.getStatusCounts(projectId)),
56
- getHistory: (workItemId) => Promise.resolve(workItemRepository.getHistory(workItemId)),
57
- bulkCreate: (projectId, items) => Promise.resolve(workItemRepository.bulkCreate(projectId, items)),
58
- };
59
- const asyncDocumentRepo = {
60
- upsert: (input) => Promise.resolve(documentRepository.upsert(input)),
61
- create: (input) => Promise.resolve(documentRepository.create(input)),
62
- getById: (id) => Promise.resolve(documentRepository.getById(id)),
63
- getByType: (projectId, docType) => Promise.resolve(documentRepository.getByType(projectId, docType)),
64
- listByProject: (projectId) => Promise.resolve(documentRepository.listByProject(projectId)),
65
- update: (id, input) => Promise.resolve(documentRepository.update(id, input)),
66
- delete: (id) => Promise.resolve(documentRepository.delete(id)),
67
- deleteByType: (projectId, docType) => Promise.resolve(documentRepository.deleteByType(projectId, docType)),
68
- getTypeCounts: (projectId) => Promise.resolve(documentRepository.getTypeCounts(projectId)),
69
- };
70
- const asyncPlanRepo = {
71
- create: (input) => Promise.resolve(planRepository.create(input)),
72
- getById: (id) => Promise.resolve(planRepository.getById(id)),
73
- getByName: (projectId, name) => Promise.resolve(planRepository.getByName(projectId, name)),
74
- getWithWorkItem: (id) => Promise.resolve(planRepository.getWithWorkItem(id)),
75
- update: (id, input) => Promise.resolve(planRepository.update(id, input)),
76
- delete: (id) => Promise.resolve(planRepository.delete(id)),
77
- list: (projectId, options) => Promise.resolve(planRepository.list(projectId, options)),
78
- countByStatus: (projectId) => Promise.resolve(planRepository.countByStatus(projectId)),
79
- getInProgress: (projectId) => Promise.resolve(planRepository.getInProgress(projectId)),
80
- hasInProgress: (projectId) => Promise.resolve(planRepository.hasInProgress(projectId)),
81
- linkWorkItem: (planId, workItemId) => Promise.resolve(planRepository.linkWorkItem(planId, workItemId)),
82
- unlinkWorkItem: (planId) => Promise.resolve(planRepository.unlinkWorkItem(planId)),
83
- };
84
- // =============================================================================
85
- // Anchor Service — wraps dual-scope AnchorManager (global + project)
21
+ // RepositoriesSDK's SQLite implementations, backed by CLI's existing DB
86
22
  // =============================================================================
87
- const anchorService = {
88
- add: (input) => {
89
- const activeProject = getCurrentProject();
90
- const priority = input.priority ?? 'info';
91
- let scope = input.scope;
92
- if (!scope) {
93
- scope = activeProject ? 'project' : 'global';
94
- }
95
- if (scope === 'project' && !activeProject) {
96
- throw new Error('Cannot add project-scoped anchor: no active project. Use scope: "global" instead.');
97
- }
98
- const projectId = scope === 'project' && activeProject
99
- ? String(activeProject.id)
100
- : undefined;
101
- const manager = scope === 'global'
102
- ? getGlobalAnchorManager()
103
- : getAnchorManager(projectId ?? '');
104
- const anchor = manager.add({
105
- content: input.content,
106
- priority,
107
- scope: 'persistent',
108
- tags: input.tags,
109
- projectId,
110
- });
111
- return Promise.resolve({
112
- id: anchor.id,
113
- content: anchor.content,
114
- priority: anchor.priority,
115
- scope,
116
- tags: anchor.tags,
117
- });
118
- },
119
- remove: (id) => {
120
- const activeProject = getCurrentProject();
121
- let removed = false;
122
- let scope;
123
- if (activeProject) {
124
- const projectManager = getAnchorManager(String(activeProject.id));
125
- if (projectManager.has(id)) {
126
- removed = projectManager.remove(id);
127
- scope = 'project';
23
+ const db = getDb();
24
+ const repos = createSQLiteRepositories(db, {
25
+ projectDeleteHooks: {
26
+ onDelete: (projectId) => {
27
+ // Clean up session files
28
+ try {
29
+ const sessionsPath = getSessionsPath();
30
+ const projectSessionPath = join(sessionsPath, `project-${String(projectId)}`);
31
+ if (existsSync(projectSessionPath)) {
32
+ rmSync(projectSessionPath, { recursive: true, force: true });
33
+ }
128
34
  }
129
- }
130
- if (!removed) {
131
- const globalManager = getGlobalAnchorManager();
132
- if (globalManager.has(id)) {
133
- removed = globalManager.remove(id);
134
- scope = 'global';
35
+ catch { /* best-effort */ }
36
+ // Clean up anchor files
37
+ try {
38
+ const anchorsPath = join(getSessionsPath(), '..', 'anchors');
39
+ const projectAnchorsPath = join(anchorsPath, `project-${String(projectId)}.json`);
40
+ if (existsSync(projectAnchorsPath)) {
41
+ rmSync(projectAnchorsPath, { force: true });
42
+ }
135
43
  }
136
- }
137
- return Promise.resolve({ removed, scope });
44
+ catch { /* best-effort */ }
45
+ },
138
46
  },
139
- list: (options) => {
140
- const activeProject = getCurrentProject();
141
- const scope = options?.scope ?? 'all';
142
- const anchors = [];
143
- if (scope === 'global' || scope === 'all') {
144
- const globalManager = getGlobalAnchorManager();
145
- for (const anchor of globalManager.getAll({ priority: options?.priority })) {
146
- anchors.push({
147
- id: anchor.id,
148
- content: anchor.content,
149
- priority: anchor.priority,
150
- scope: 'global',
151
- tags: anchor.tags,
152
- });
153
- }
154
- }
155
- if ((scope === 'project' || scope === 'all') && activeProject) {
156
- const projectManager = getAnchorManager(String(activeProject.id));
157
- for (const anchor of projectManager.getAll({ priority: options?.priority })) {
158
- anchors.push({
159
- id: anchor.id,
160
- content: anchor.content,
161
- priority: anchor.priority,
162
- scope: 'project',
163
- tags: anchor.tags,
164
- });
165
- }
166
- }
167
- // Sort by priority (critical first)
168
- const priorityOrder = { critical: 0, safety: 1, info: 2 };
169
- anchors.sort((a, b) => {
170
- const pa = priorityOrder[a.priority] ?? 2;
171
- const pb = priorityOrder[b.priority] ?? 2;
172
- return pa - pb;
173
- });
174
- return Promise.resolve({
175
- count: anchors.length,
176
- activeProjectName: activeProject?.displayName ?? null,
177
- anchors,
178
- });
47
+ });
48
+ // =============================================================================
49
+ // Anchor Service ProjectAnchorStore from SDK (shared with Desktop)
50
+ // =============================================================================
51
+ const anchorStore = new ProjectAnchorStore({
52
+ anchorsDir: join(getDataPath(), 'anchors'),
53
+ getCurrentProjectId: () => getCurrentProject()?.id,
54
+ getProjectName: () => {
55
+ // getCurrentProject() is always in memory — no async needed
56
+ return getCurrentProject()?.displayName ?? null;
179
57
  },
180
- };
58
+ });
59
+ // Register the store so CLI's anchors module delegates to it
60
+ setAnchorStore(anchorStore);
61
+ const anchorService = anchorStore.toService();
181
62
  // =============================================================================
182
63
  // Artifact Service — wraps ArtifactStore from TeamCheckpointer
183
64
  // =============================================================================
@@ -196,7 +77,7 @@ const artifactService = {
196
77
  });
197
78
  if (!updated)
198
79
  throw new Error(`Failed to update artifact "${input.name}"`);
199
- store.save();
80
+ getTeamCheckpointer().saveArtifactStore(currentProject?.id ?? null, store);
200
81
  recordTeamActivity(agentId, 'artifact_updated', `updated artifact "${updated.name}"`);
201
82
  const artifact = {
202
83
  id: updated.id,
@@ -219,7 +100,7 @@ const artifactService = {
219
100
  content: input.content,
220
101
  summary: input.summary,
221
102
  });
222
- store.save();
103
+ getTeamCheckpointer().saveArtifactStore(currentProject?.id ?? null, store);
223
104
  recordTeamActivity(agentId, 'artifact_created', `created artifact "${created.name}"`);
224
105
  const artifact = {
225
106
  id: created.id,
@@ -284,7 +165,7 @@ const artifactService = {
284
165
  const deleted = store.delete(artifact.id);
285
166
  if (!deleted)
286
167
  return Promise.resolve(null);
287
- store.save();
168
+ getTeamCheckpointer().saveArtifactStore(currentProject?.id ?? null, store);
288
169
  const result = {
289
170
  id: artifact.id,
290
171
  name: artifact.name,
@@ -338,10 +219,10 @@ const episodeService = {
338
219
  // PlatformContext with getter/setter for currentProjectId
339
220
  // =============================================================================
340
221
  const context = {
341
- projects: asyncProjectRepo,
342
- workItems: asyncWorkItemRepo,
343
- documents: asyncDocumentRepo,
344
- plans: asyncPlanRepo,
222
+ projects: repos.projects,
223
+ workItems: repos.workItems,
224
+ documents: repos.documents,
225
+ plans: repos.plans,
345
226
  anchors: anchorService,
346
227
  artifacts: artifactService,
347
228
  episodes: episodeService,