@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.
- package/dist/.tsbuildinfo.app +1 -1
- package/dist/.tsbuildinfo.data +1 -1
- package/dist/.tsbuildinfo.domain +1 -1
- package/dist/.tsbuildinfo.foundation +1 -1
- package/dist/agent.js +1 -1
- package/dist/anchors/index.d.ts +1 -1
- package/dist/anchors/index.js +1 -1
- package/dist/anchors/project-anchors.d.ts +9 -60
- package/dist/anchors/project-anchors.js +31 -173
- package/dist/commands-v2/handlers/session.js +2 -2
- package/dist/commands-v2/types.d.ts +1 -2
- package/dist/compilr-diff-companion.vsix +0 -0
- package/dist/multi-agent/capability-loader.js +1 -1
- package/dist/multi-agent/checkpointer.d.ts +5 -4
- package/dist/multi-agent/checkpointer.js +25 -5
- package/dist/multi-agent/custom-agents.d.ts +5 -62
- package/dist/multi-agent/custom-agents.js +10 -136
- package/dist/multi-agent/delegation-tracker.d.ts +5 -146
- package/dist/multi-agent/delegation-tracker.js +7 -218
- package/dist/multi-agent/index.d.ts +14 -18
- package/dist/multi-agent/index.js +22 -23
- package/dist/repl-v2.js +1 -1
- package/dist/settings/index.d.ts +3 -14
- package/dist/settings/index.js +5 -32
- package/dist/settings/mcp-config.d.ts +7 -54
- package/dist/settings/mcp-config.js +18 -104
- package/dist/tools/delegation-status.js +1 -1
- package/dist/tools/platform-adapter.d.ts +2 -12
- package/dist/tools/platform-adapter.js +52 -171
- package/dist/ui/overlay/impl/background-overlay-v2.d.ts +1 -1
- package/dist/ui/overlay/impl/custom-agent-form-overlay-v2.d.ts +1 -1
- package/dist/ui/overlay/impl/custom-agent-form-overlay-v2.js +1 -4
- package/dist/ui/overlay/impl/delegations-overlay-v2.js +1 -1
- package/dist/ui/overlay/impl/team-overlay-v2.d.ts +1 -1
- package/dist/ui/overlay/impl/team-overlay-v2.js +1 -1
- 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().
|
|
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}`);
|
package/dist/settings/index.d.ts
CHANGED
|
@@ -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
|
|
8
|
-
|
|
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
|
-
*
|
|
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 {};
|
package/dist/settings/index.js
CHANGED
|
@@ -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
|
-
*
|
|
510
|
+
* Delegates to SDK's findMatchingRule with the current settings rules.
|
|
521
511
|
*/
|
|
522
512
|
export function findMatchingRule(toolName) {
|
|
523
|
-
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
-
|
|
30
|
-
|
|
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):
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
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
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return
|
|
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
|
-
|
|
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.
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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 {
|
|
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 '
|
|
24
|
-
import {
|
|
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
|
-
//
|
|
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
|
+
// Repositories — SDK's SQLite implementations, backed by CLI's existing DB
|
|
86
22
|
// =============================================================================
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
44
|
+
catch { /* best-effort */ }
|
|
45
|
+
},
|
|
138
46
|
},
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
342
|
-
workItems:
|
|
343
|
-
documents:
|
|
344
|
-
plans:
|
|
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,
|