@compilr-dev/sdk 0.2.2 → 0.2.4
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/index.d.ts +4 -0
- package/dist/index.js +8 -0
- package/dist/mcp-config.d.ts +74 -0
- package/dist/mcp-config.js +124 -0
- package/dist/platform/index.d.ts +2 -0
- package/dist/platform/index.js +2 -0
- package/dist/platform/sqlite/db.d.ts +24 -0
- package/dist/platform/sqlite/db.js +140 -0
- package/dist/platform/sqlite/document-repository.d.ts +19 -0
- package/dist/platform/sqlite/document-repository.js +126 -0
- package/dist/platform/sqlite/index.d.ts +40 -0
- package/dist/platform/sqlite/index.js +41 -0
- package/dist/platform/sqlite/plan-repository.d.ts +24 -0
- package/dist/platform/sqlite/plan-repository.js +205 -0
- package/dist/platform/sqlite/project-repository.d.ts +34 -0
- package/dist/platform/sqlite/project-repository.js +282 -0
- package/dist/platform/sqlite/schema.d.ts +65 -0
- package/dist/platform/sqlite/schema.js +159 -0
- package/dist/platform/sqlite/work-item-repository.d.ts +23 -0
- package/dist/platform/sqlite/work-item-repository.js +350 -0
- package/package.json +11 -4
package/dist/index.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ export type { CapabilityPack, CapabilityTier, LoadedCapability, CapabilityCatalo
|
|
|
54
54
|
export { SystemPromptBuilder, buildSystemPrompt, detectGitRepository, getModuleStats, ALL_MODULES, IDENTITY_MODULE, STYLE_MODULE, TASK_EXECUTION_MODULE, TODO_MANAGEMENT_MODULE, TOOL_USAGE_DIRECT_MODULE, TOOL_USAGE_HINTS_MODULE, PLATFORM_TOOL_HINTS_MODULE, FACTORY_TOOL_HINTS_MODULE, TOOL_USAGE_META_MODULE, DELEGATION_MODULE, GIT_SAFETY_MODULE, SUGGEST_MODULE, IMPORTANT_RULES_MODULE, ENVIRONMENT_MODULE, shouldIncludeModule, getEstimatedTokensForConditions, getTotalEstimatedTokens, } from './system-prompt/index.js';
|
|
55
55
|
export type { SystemPromptContext, BuildResult, SystemPromptModule, ModuleConditions, } from './system-prompt/index.js';
|
|
56
56
|
export type { ProjectType, ProjectStatus, RepoPattern, WorkflowMode, LifecycleState, WorkItemType, WorkItemStatus, WorkItemPriority, GuidedStep, DocumentType, PlanStatus, Project, WorkItem, ProjectDocument, Plan, PlanSummary, PlanWithWorkItem, HistoryEntry, CreateProjectInput, UpdateProjectInput, ProjectListOptions, CreateWorkItemInput, UpdateWorkItemInput, QueryWorkItemsInput, CreateDocumentInput, UpdateDocumentInput, CreatePlanInput, UpdatePlanInput, ListPlansOptions, WorkItemQueryResult, ProjectListResult, BulkCreateItem, IProjectRepository, IWorkItemRepository, IDocumentRepository, IPlanRepository, IAnchorService, IArtifactService, IEpisodeService, AnchorData, ArtifactType, ArtifactData, ArtifactSummaryData, WorkEpisode, ProjectWorkSummary, PlatformContext, PlatformToolsConfig, PlatformHooks, StepCriteria, } from './platform/index.js';
|
|
57
|
+
export { createSQLiteRepositories, SQLiteProjectRepository, SQLiteWorkItemRepository, SQLiteDocumentRepository, SQLitePlanRepository, getDatabase, closeDatabase, closeAllDatabases, databaseExists, SCHEMA_VERSION, SCHEMA_SQL, } from './platform/index.js';
|
|
58
|
+
export type { SQLiteRepositories, CreateSQLiteRepositoriesOptions, ProjectDeleteHooks, ProjectRecord, WorkItemRecord, ProjectDocumentRecord, } from './platform/index.js';
|
|
57
59
|
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, } from './platform/index.js';
|
|
58
60
|
export { STEP_ORDER, GUIDED_STEP_CRITERIA, getNextStep, isValidTransition, getStepCriteria, formatStepDisplay, getStepNumber, } from './platform/index.js';
|
|
59
61
|
export { platformSkills, designSkill, sketchSkill, prdSkill, refineSkill, refineItemSkill, architectureSkill, sessionNotesSkill, buildSkill, scaffoldSkill, } from './skills/index.js';
|
|
@@ -61,5 +63,7 @@ export { defineTool, createSuccessResult, createErrorResult, mergeHooks, createL
|
|
|
61
63
|
export type { Tool, HooksConfig, AgentEvent, Message, LLMProvider, AnchorInput, ToolExecutionResult, AgentRunResult, PermissionHandler, ToolPermission, AgentTypeConfig, GuardrailTriggeredHandler, BeforeLLMHookResult, BeforeToolHook, BeforeToolHookResult, AfterToolHook, AgentState, AgentConfig, SessionInfo, Anchor, AnchorScope, AnchorClearOptions, AnchorPriority, AnchorQueryOptions, FileAccessType, FileAccess, GuardrailResult, GuardrailContext, MCPClient, MCPToolDefinition, } from '@compilr-dev/agents';
|
|
62
64
|
export { DEFAULT_PERMISSION_RULES, findMatchingRule, permissionModeLabel, permissionLevelLabel, } from './permissions.js';
|
|
63
65
|
export type { PermissionRule, PermissionMode, PermissionLevel } from './permissions.js';
|
|
66
|
+
export { readMCPConfigFile, writeMCPConfigFile, resolveServerEntry, loadMCPServers, saveMCPServerEntry, deleteMCPServerEntry, getServerNames, } from './mcp-config.js';
|
|
67
|
+
export type { MCPServerEntry, MCPConfigFile, ResolvedMCPServer } from './mcp-config.js';
|
|
64
68
|
export { readFileTool, writeFileTool, createBashTool, bashTool, bashOutputTool, killShellTool, grepTool, globTool, editTool, todoWriteTool, todoReadTool, createTodoTools, TodoStore, webFetchTool, suggestTool, } from '@compilr-dev/agents';
|
|
65
69
|
export { gitStatusTool, gitDiffTool, gitLogTool, gitCommitTool, gitBranchTool, gitStashTool, gitBlameTool, gitFileHistoryTool, detectProjectTool, findProjectRootTool, runTestsTool, runLintTool, runBuildTool, runFormatTool, findDefinitionTool, findReferencesTool, findTodosTool, checkOutdatedTool, findVulnerabilitiesTool, analyzeTestCoverageTool, getFileStructureTool, getComplexityTool, allCodingTools, unifiedTools, } from '@compilr-dev/agents-coding';
|
package/dist/index.js
CHANGED
|
@@ -113,6 +113,10 @@ ALL_MODULES, IDENTITY_MODULE, STYLE_MODULE, TASK_EXECUTION_MODULE, TODO_MANAGEME
|
|
|
113
113
|
// Module utilities
|
|
114
114
|
shouldIncludeModule, getEstimatedTokensForConditions, getTotalEstimatedTokens, } from './system-prompt/index.js';
|
|
115
115
|
// =============================================================================
|
|
116
|
+
// Platform SQLite Repositories (concrete implementations)
|
|
117
|
+
// =============================================================================
|
|
118
|
+
export { createSQLiteRepositories, SQLiteProjectRepository, SQLiteWorkItemRepository, SQLiteDocumentRepository, SQLitePlanRepository, getDatabase, closeDatabase, closeAllDatabases, databaseExists, SCHEMA_VERSION, SCHEMA_SQL, } from './platform/index.js';
|
|
119
|
+
// =============================================================================
|
|
116
120
|
// Platform Tools (runtime — createPlatformTools factory + individual factories)
|
|
117
121
|
// =============================================================================
|
|
118
122
|
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, } from './platform/index.js';
|
|
@@ -157,6 +161,10 @@ AgentError, ProviderError, ToolError, ToolTimeoutError, MaxIterationsError, Abor
|
|
|
157
161
|
// =============================================================================
|
|
158
162
|
export { DEFAULT_PERMISSION_RULES, findMatchingRule, permissionModeLabel, permissionLevelLabel, } from './permissions.js';
|
|
159
163
|
// =============================================================================
|
|
164
|
+
// Shared MCP Configuration
|
|
165
|
+
// =============================================================================
|
|
166
|
+
export { readMCPConfigFile, writeMCPConfigFile, resolveServerEntry, loadMCPServers, saveMCPServerEntry, deleteMCPServerEntry, getServerNames, } from './mcp-config.js';
|
|
167
|
+
// =============================================================================
|
|
160
168
|
// Individual Tool Re-exports (for consumers that build custom tool registries)
|
|
161
169
|
// =============================================================================
|
|
162
170
|
// Base tools from @compilr-dev/agents
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Configuration — shared types and file I/O for mcp.json
|
|
3
|
+
*
|
|
4
|
+
* Used by both @compilr-dev/cli and compilr-dev-desktop.
|
|
5
|
+
* Config format is compatible with Claude Desktop / Gemini CLI.
|
|
6
|
+
*
|
|
7
|
+
* File structure:
|
|
8
|
+
* { "mcpServers": { "name": { command?, url?, ... } } }
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Single MCP server entry in the config file.
|
|
12
|
+
* If `command` is present → stdio transport.
|
|
13
|
+
* If `url` is present → HTTP transport.
|
|
14
|
+
*/
|
|
15
|
+
export interface MCPServerEntry {
|
|
16
|
+
command?: string;
|
|
17
|
+
args?: string[];
|
|
18
|
+
env?: Record<string, string>;
|
|
19
|
+
cwd?: string;
|
|
20
|
+
url?: string;
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
timeout?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Shape of the mcp.json config file.
|
|
27
|
+
*/
|
|
28
|
+
export interface MCPConfigFile {
|
|
29
|
+
mcpServers: Record<string, MCPServerEntry>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolved MCP server config ready for MCPManager.
|
|
33
|
+
*/
|
|
34
|
+
export interface ResolvedMCPServer {
|
|
35
|
+
name: string;
|
|
36
|
+
transport: 'stdio' | 'http';
|
|
37
|
+
command?: string;
|
|
38
|
+
args?: string[];
|
|
39
|
+
env?: Record<string, string>;
|
|
40
|
+
cwd?: string;
|
|
41
|
+
url?: string;
|
|
42
|
+
headers?: Record<string, string>;
|
|
43
|
+
timeout?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Read and parse an mcp.json file. Returns empty record on any error.
|
|
47
|
+
*/
|
|
48
|
+
export declare function readMCPConfigFile(filePath: string): Record<string, MCPServerEntry>;
|
|
49
|
+
/**
|
|
50
|
+
* Write servers to an mcp.json file (creates directory if needed).
|
|
51
|
+
*/
|
|
52
|
+
export declare function writeMCPConfigFile(filePath: string, servers: Record<string, MCPServerEntry>): void;
|
|
53
|
+
/**
|
|
54
|
+
* Convert an MCPServerEntry to a ResolvedMCPServer.
|
|
55
|
+
* Returns null if the entry is invalid (neither command nor url) or disabled.
|
|
56
|
+
*/
|
|
57
|
+
export declare function resolveServerEntry(name: string, entry: MCPServerEntry): ResolvedMCPServer | null;
|
|
58
|
+
/**
|
|
59
|
+
* Load and resolve MCP servers from one or more config file paths.
|
|
60
|
+
* Later paths override earlier ones (by server name).
|
|
61
|
+
*/
|
|
62
|
+
export declare function loadMCPServers(...configPaths: string[]): ResolvedMCPServer[];
|
|
63
|
+
/**
|
|
64
|
+
* Save a single MCP server entry to a config file (add or overwrite by name).
|
|
65
|
+
*/
|
|
66
|
+
export declare function saveMCPServerEntry(filePath: string, name: string, entry: MCPServerEntry): void;
|
|
67
|
+
/**
|
|
68
|
+
* Delete a single MCP server entry from a config file.
|
|
69
|
+
*/
|
|
70
|
+
export declare function deleteMCPServerEntry(filePath: string, name: string): void;
|
|
71
|
+
/**
|
|
72
|
+
* Get all server names from one or more config files.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getServerNames(...configPaths: string[]): Set<string>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Configuration — shared types and file I/O for mcp.json
|
|
3
|
+
*
|
|
4
|
+
* Used by both @compilr-dev/cli and compilr-dev-desktop.
|
|
5
|
+
* Config format is compatible with Claude Desktop / Gemini CLI.
|
|
6
|
+
*
|
|
7
|
+
* File structure:
|
|
8
|
+
* { "mcpServers": { "name": { command?, url?, ... } } }
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
11
|
+
import { dirname } from 'path';
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// File I/O
|
|
14
|
+
// =============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Read and parse an mcp.json file. Returns empty record on any error.
|
|
17
|
+
*/
|
|
18
|
+
export function readMCPConfigFile(filePath) {
|
|
19
|
+
try {
|
|
20
|
+
if (!existsSync(filePath))
|
|
21
|
+
return {};
|
|
22
|
+
const data = readFileSync(filePath, 'utf-8');
|
|
23
|
+
const parsed = JSON.parse(data);
|
|
24
|
+
if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {
|
|
25
|
+
return parsed.mcpServers;
|
|
26
|
+
}
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Write servers to an mcp.json file (creates directory if needed).
|
|
35
|
+
*/
|
|
36
|
+
export function writeMCPConfigFile(filePath, servers) {
|
|
37
|
+
const dir = dirname(filePath);
|
|
38
|
+
if (!existsSync(dir)) {
|
|
39
|
+
mkdirSync(dir, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
const config = { mcpServers: servers };
|
|
42
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
43
|
+
}
|
|
44
|
+
// =============================================================================
|
|
45
|
+
// Resolution
|
|
46
|
+
// =============================================================================
|
|
47
|
+
/**
|
|
48
|
+
* Convert an MCPServerEntry to a ResolvedMCPServer.
|
|
49
|
+
* Returns null if the entry is invalid (neither command nor url) or disabled.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveServerEntry(name, entry) {
|
|
52
|
+
if (entry.disabled)
|
|
53
|
+
return null;
|
|
54
|
+
if (entry.command) {
|
|
55
|
+
return {
|
|
56
|
+
name,
|
|
57
|
+
transport: 'stdio',
|
|
58
|
+
command: entry.command,
|
|
59
|
+
args: entry.args,
|
|
60
|
+
env: entry.env,
|
|
61
|
+
cwd: entry.cwd,
|
|
62
|
+
timeout: entry.timeout,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (entry.url) {
|
|
66
|
+
return {
|
|
67
|
+
name,
|
|
68
|
+
transport: 'http',
|
|
69
|
+
url: entry.url,
|
|
70
|
+
headers: entry.headers,
|
|
71
|
+
timeout: entry.timeout,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
// =============================================================================
|
|
77
|
+
// High-level helpers
|
|
78
|
+
// =============================================================================
|
|
79
|
+
/**
|
|
80
|
+
* Load and resolve MCP servers from one or more config file paths.
|
|
81
|
+
* Later paths override earlier ones (by server name).
|
|
82
|
+
*/
|
|
83
|
+
export function loadMCPServers(...configPaths) {
|
|
84
|
+
const merged = {};
|
|
85
|
+
for (const p of configPaths) {
|
|
86
|
+
const servers = readMCPConfigFile(p);
|
|
87
|
+
Object.assign(merged, servers);
|
|
88
|
+
}
|
|
89
|
+
const resolved = [];
|
|
90
|
+
for (const [name, entry] of Object.entries(merged)) {
|
|
91
|
+
const server = resolveServerEntry(name, entry);
|
|
92
|
+
if (server)
|
|
93
|
+
resolved.push(server);
|
|
94
|
+
}
|
|
95
|
+
return resolved;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Save a single MCP server entry to a config file (add or overwrite by name).
|
|
99
|
+
*/
|
|
100
|
+
export function saveMCPServerEntry(filePath, name, entry) {
|
|
101
|
+
const servers = readMCPConfigFile(filePath);
|
|
102
|
+
servers[name] = entry;
|
|
103
|
+
writeMCPConfigFile(filePath, servers);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Delete a single MCP server entry from a config file.
|
|
107
|
+
*/
|
|
108
|
+
export function deleteMCPServerEntry(filePath, name) {
|
|
109
|
+
const servers = readMCPConfigFile(filePath);
|
|
110
|
+
const { [name]: _, ...rest } = servers;
|
|
111
|
+
writeMCPConfigFile(filePath, rest);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get all server names from one or more config files.
|
|
115
|
+
*/
|
|
116
|
+
export function getServerNames(...configPaths) {
|
|
117
|
+
const names = new Set();
|
|
118
|
+
for (const p of configPaths) {
|
|
119
|
+
for (const name of Object.keys(readMCPConfigFile(p))) {
|
|
120
|
+
names.add(name);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return names;
|
|
124
|
+
}
|
package/dist/platform/index.d.ts
CHANGED
|
@@ -6,5 +6,7 @@ export type { IProjectRepository, IWorkItemRepository, IDocumentRepository, IPla
|
|
|
6
6
|
export type { IAnchorService, IArtifactService, IEpisodeService, AnchorData, ArtifactType, ArtifactData, ArtifactSummaryData, WorkEpisode, ProjectWorkSummary, } from './services.js';
|
|
7
7
|
export type { PlatformContext, PlatformToolsConfig, PlatformHooks } from './context.js';
|
|
8
8
|
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, } from './tools/index.js';
|
|
9
|
+
export { createSQLiteRepositories, SQLiteProjectRepository, SQLiteWorkItemRepository, SQLiteDocumentRepository, SQLitePlanRepository, getDatabase, closeDatabase, closeAllDatabases, databaseExists, SCHEMA_VERSION, SCHEMA_SQL, } from './sqlite/index.js';
|
|
10
|
+
export type { SQLiteRepositories, CreateSQLiteRepositoriesOptions, ProjectDeleteHooks, ProjectRecord, WorkItemRecord, ProjectDocumentRecord, } from './sqlite/index.js';
|
|
9
11
|
export { STEP_ORDER, GUIDED_STEP_CRITERIA, getNextStep, isValidTransition, getStepCriteria, formatStepDisplay, getStepNumber, } from './workflow.js';
|
|
10
12
|
export type { StepCriteria } from './workflow.js';
|
package/dist/platform/index.js
CHANGED
|
@@ -3,5 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
// Platform tools (runtime)
|
|
5
5
|
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, } from './tools/index.js';
|
|
6
|
+
// SQLite implementations (concrete repositories)
|
|
7
|
+
export { createSQLiteRepositories, SQLiteProjectRepository, SQLiteWorkItemRepository, SQLiteDocumentRepository, SQLitePlanRepository, getDatabase, closeDatabase, closeAllDatabases, databaseExists, SCHEMA_VERSION, SCHEMA_SQL, } from './sqlite/index.js';
|
|
6
8
|
// Workflow (pure step-criteria logic)
|
|
7
9
|
export { STEP_ORDER, GUIDED_STEP_CRITERIA, getNextStep, isValidTransition, getStepCriteria, formatStepDisplay, getStepNumber, } from './workflow.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Connection — Shared SQLite singleton for ~/.compilr-dev/projects.db
|
|
3
|
+
*
|
|
4
|
+
* Uses better-sqlite3 (optional peer dependency).
|
|
5
|
+
* Consumers must install better-sqlite3 to use the SQLite repositories.
|
|
6
|
+
*/
|
|
7
|
+
import type Database from 'better-sqlite3';
|
|
8
|
+
/**
|
|
9
|
+
* Get or create a database connection for the given path.
|
|
10
|
+
* Uses singleton pattern — same path always returns the same instance.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getDatabase(dbPath: string): Database.Database;
|
|
13
|
+
/**
|
|
14
|
+
* Close a specific database connection.
|
|
15
|
+
*/
|
|
16
|
+
export declare function closeDatabase(dbPath: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Close all database connections.
|
|
19
|
+
*/
|
|
20
|
+
export declare function closeAllDatabases(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Check if a database file exists at the given path.
|
|
23
|
+
*/
|
|
24
|
+
export declare function databaseExists(dbPath: string): boolean;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Connection — Shared SQLite singleton for ~/.compilr-dev/projects.db
|
|
3
|
+
*
|
|
4
|
+
* Uses better-sqlite3 (optional peer dependency).
|
|
5
|
+
* Consumers must install better-sqlite3 to use the SQLite repositories.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { SCHEMA_SQL, SCHEMA_VERSION } from './schema.js';
|
|
10
|
+
// Database instance (singleton per path)
|
|
11
|
+
const instances = new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Get or create a database connection for the given path.
|
|
14
|
+
* Uses singleton pattern — same path always returns the same instance.
|
|
15
|
+
*/
|
|
16
|
+
export function getDatabase(dbPath) {
|
|
17
|
+
const existing = instances.get(dbPath);
|
|
18
|
+
if (existing)
|
|
19
|
+
return existing;
|
|
20
|
+
// Dynamic require of better-sqlite3 — it's an optional peer dep
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
22
|
+
const BetterSqlite3 = require('better-sqlite3');
|
|
23
|
+
// Ensure directory exists
|
|
24
|
+
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
25
|
+
const db = new BetterSqlite3(dbPath);
|
|
26
|
+
// Enable foreign keys
|
|
27
|
+
db.pragma('foreign_keys = ON');
|
|
28
|
+
// WAL mode for better concurrency
|
|
29
|
+
db.pragma('journal_mode = WAL');
|
|
30
|
+
// Prevent SQLITE_BUSY under concurrent access
|
|
31
|
+
db.pragma('busy_timeout = 5000');
|
|
32
|
+
// Initialize schema
|
|
33
|
+
initializeSchema(db);
|
|
34
|
+
instances.set(dbPath, db);
|
|
35
|
+
return db;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Close a specific database connection.
|
|
39
|
+
*/
|
|
40
|
+
export function closeDatabase(dbPath) {
|
|
41
|
+
const db = instances.get(dbPath);
|
|
42
|
+
if (db) {
|
|
43
|
+
db.close();
|
|
44
|
+
instances.delete(dbPath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Close all database connections.
|
|
49
|
+
*/
|
|
50
|
+
export function closeAllDatabases() {
|
|
51
|
+
for (const [, db] of instances) {
|
|
52
|
+
db.close();
|
|
53
|
+
}
|
|
54
|
+
instances.clear();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if a database file exists at the given path.
|
|
58
|
+
*/
|
|
59
|
+
export function databaseExists(dbPath) {
|
|
60
|
+
return fs.existsSync(dbPath);
|
|
61
|
+
}
|
|
62
|
+
// =============================================================================
|
|
63
|
+
// Schema initialization & migrations
|
|
64
|
+
// =============================================================================
|
|
65
|
+
function initializeSchema(db) {
|
|
66
|
+
const tableExists = db
|
|
67
|
+
.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'`)
|
|
68
|
+
.get();
|
|
69
|
+
if (!tableExists) {
|
|
70
|
+
db.exec(SCHEMA_SQL);
|
|
71
|
+
db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(SCHEMA_VERSION);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const currentVersion = db.prepare('SELECT MAX(version) as version FROM schema_version').get();
|
|
75
|
+
if (!currentVersion || currentVersion.version < SCHEMA_VERSION) {
|
|
76
|
+
runMigrations(db, currentVersion?.version ?? 0, SCHEMA_VERSION);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function runMigrations(db, fromVersion, toVersion) {
|
|
80
|
+
if (fromVersion < 2 && toVersion >= 2) {
|
|
81
|
+
db.exec(`ALTER TABLE project_documents ADD COLUMN status TEXT;`);
|
|
82
|
+
db.exec(`ALTER TABLE project_documents ADD COLUMN work_item_id INTEGER REFERENCES work_items(id) ON DELETE SET NULL;`);
|
|
83
|
+
db.exec(`
|
|
84
|
+
CREATE INDEX IF NOT EXISTS idx_project_documents_status ON project_documents(status);
|
|
85
|
+
CREATE INDEX IF NOT EXISTS idx_project_documents_work_item ON project_documents(work_item_id);
|
|
86
|
+
`);
|
|
87
|
+
db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(2);
|
|
88
|
+
}
|
|
89
|
+
if (fromVersion < 3 && toVersion >= 3) {
|
|
90
|
+
db.exec(`ALTER TABLE work_items ADD COLUMN owner TEXT;`);
|
|
91
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_work_items_owner ON work_items(owner);`);
|
|
92
|
+
db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(3);
|
|
93
|
+
}
|
|
94
|
+
if (fromVersion < 4 && toVersion >= 4) {
|
|
95
|
+
db.exec(`
|
|
96
|
+
CREATE TABLE IF NOT EXISTS terminal_sessions (
|
|
97
|
+
id TEXT PRIMARY KEY, project_id INTEGER, pid INTEGER NOT NULL,
|
|
98
|
+
tty_path TEXT, label TEXT, started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
99
|
+
last_heartbeat DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
100
|
+
active_agent TEXT DEFAULT 'default', agents_json TEXT DEFAULT '[]',
|
|
101
|
+
status TEXT DEFAULT 'active',
|
|
102
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE SET NULL
|
|
103
|
+
);
|
|
104
|
+
CREATE INDEX IF NOT EXISTS idx_terminal_sessions_project ON terminal_sessions(project_id);
|
|
105
|
+
CREATE INDEX IF NOT EXISTS idx_terminal_sessions_status ON terminal_sessions(status);
|
|
106
|
+
`);
|
|
107
|
+
db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(4);
|
|
108
|
+
}
|
|
109
|
+
if (fromVersion < 5 && toVersion >= 5) {
|
|
110
|
+
db.exec(`
|
|
111
|
+
CREATE TABLE IF NOT EXISTS file_locks (
|
|
112
|
+
path TEXT NOT NULL, project_id INTEGER NOT NULL,
|
|
113
|
+
session_id TEXT NOT NULL, agent_id TEXT NOT NULL,
|
|
114
|
+
locked_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
115
|
+
PRIMARY KEY (path, project_id),
|
|
116
|
+
FOREIGN KEY (session_id) REFERENCES terminal_sessions(id) ON DELETE CASCADE,
|
|
117
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
|
118
|
+
);
|
|
119
|
+
CREATE INDEX IF NOT EXISTS idx_file_locks_session ON file_locks(session_id);
|
|
120
|
+
`);
|
|
121
|
+
db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(5);
|
|
122
|
+
}
|
|
123
|
+
if (fromVersion < 6 && toVersion >= 6) {
|
|
124
|
+
db.exec(`
|
|
125
|
+
CREATE TABLE IF NOT EXISTS session_notifications (
|
|
126
|
+
id TEXT PRIMARY KEY, project_id INTEGER NOT NULL,
|
|
127
|
+
from_session_id TEXT NOT NULL, to_session_id TEXT,
|
|
128
|
+
type TEXT NOT NULL, title TEXT NOT NULL,
|
|
129
|
+
message TEXT, payload_json TEXT,
|
|
130
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, read_at DATETIME,
|
|
131
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
132
|
+
FOREIGN KEY (from_session_id) REFERENCES terminal_sessions(id) ON DELETE CASCADE
|
|
133
|
+
);
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_session_notifications_project ON session_notifications(project_id);
|
|
135
|
+
CREATE INDEX IF NOT EXISTS idx_session_notifications_to_session ON session_notifications(to_session_id);
|
|
136
|
+
CREATE INDEX IF NOT EXISTS idx_session_notifications_unread ON session_notifications(read_at);
|
|
137
|
+
`);
|
|
138
|
+
db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(6);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Document Repository — Concrete implementation of IDocumentRepository.
|
|
3
|
+
*/
|
|
4
|
+
import type Database from 'better-sqlite3';
|
|
5
|
+
import type { IDocumentRepository } from '../repositories.js';
|
|
6
|
+
import type { ProjectDocument, DocumentType, CreateDocumentInput, UpdateDocumentInput } from '../types.js';
|
|
7
|
+
export declare class SQLiteDocumentRepository implements IDocumentRepository {
|
|
8
|
+
private readonly db;
|
|
9
|
+
constructor(db: Database.Database);
|
|
10
|
+
upsert(input: CreateDocumentInput): Promise<ProjectDocument>;
|
|
11
|
+
create(input: CreateDocumentInput): Promise<ProjectDocument>;
|
|
12
|
+
getById(id: number): Promise<ProjectDocument | null>;
|
|
13
|
+
getByType(projectId: number, docType: DocumentType): Promise<ProjectDocument | null>;
|
|
14
|
+
listByProject(projectId: number): Promise<ProjectDocument[]>;
|
|
15
|
+
update(id: number, input: UpdateDocumentInput): Promise<ProjectDocument | null>;
|
|
16
|
+
delete(id: number): Promise<boolean>;
|
|
17
|
+
deleteByType(projectId: number, docType: DocumentType): Promise<boolean>;
|
|
18
|
+
getTypeCounts(projectId: number): Promise<Record<DocumentType, number>>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Document Repository — Concrete implementation of IDocumentRepository.
|
|
3
|
+
*/
|
|
4
|
+
function recordToDocument(record) {
|
|
5
|
+
return {
|
|
6
|
+
id: record.id,
|
|
7
|
+
projectId: record.project_id,
|
|
8
|
+
docType: record.doc_type,
|
|
9
|
+
title: record.title,
|
|
10
|
+
content: record.content,
|
|
11
|
+
createdAt: new Date(record.created_at),
|
|
12
|
+
updatedAt: new Date(record.updated_at),
|
|
13
|
+
status: record.status,
|
|
14
|
+
workItemId: record.work_item_id,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export class SQLiteDocumentRepository {
|
|
18
|
+
db;
|
|
19
|
+
constructor(db) {
|
|
20
|
+
this.db = db;
|
|
21
|
+
}
|
|
22
|
+
upsert(input) {
|
|
23
|
+
const now = new Date().toISOString();
|
|
24
|
+
const existing = this.db
|
|
25
|
+
.prepare('SELECT * FROM project_documents WHERE project_id = ? AND doc_type = ?')
|
|
26
|
+
.get(input.project_id, input.doc_type);
|
|
27
|
+
if (existing) {
|
|
28
|
+
this.db
|
|
29
|
+
.prepare('UPDATE project_documents SET title = ?, content = ?, updated_at = ? WHERE id = ?')
|
|
30
|
+
.run(input.title, input.content, now, existing.id);
|
|
31
|
+
const record = this.db
|
|
32
|
+
.prepare('SELECT * FROM project_documents WHERE id = ?')
|
|
33
|
+
.get(existing.id);
|
|
34
|
+
return Promise.resolve(recordToDocument(record));
|
|
35
|
+
}
|
|
36
|
+
const result = this.db
|
|
37
|
+
.prepare(`INSERT INTO project_documents (project_id, doc_type, title, content, created_at, updated_at)
|
|
38
|
+
VALUES (?, ?, ?, ?, ?, ?)`)
|
|
39
|
+
.run(input.project_id, input.doc_type, input.title, input.content, now, now);
|
|
40
|
+
const record = this.db
|
|
41
|
+
.prepare('SELECT * FROM project_documents WHERE id = ?')
|
|
42
|
+
.get(Number(result.lastInsertRowid));
|
|
43
|
+
return Promise.resolve(recordToDocument(record));
|
|
44
|
+
}
|
|
45
|
+
create(input) {
|
|
46
|
+
const now = new Date().toISOString();
|
|
47
|
+
const result = this.db
|
|
48
|
+
.prepare(`INSERT INTO project_documents (project_id, doc_type, title, content, created_at, updated_at)
|
|
49
|
+
VALUES (?, ?, ?, ?, ?, ?)`)
|
|
50
|
+
.run(input.project_id, input.doc_type, input.title, input.content, now, now);
|
|
51
|
+
const record = this.db
|
|
52
|
+
.prepare('SELECT * FROM project_documents WHERE id = ?')
|
|
53
|
+
.get(Number(result.lastInsertRowid));
|
|
54
|
+
return Promise.resolve(recordToDocument(record));
|
|
55
|
+
}
|
|
56
|
+
getById(id) {
|
|
57
|
+
const record = this.db.prepare('SELECT * FROM project_documents WHERE id = ?').get(id);
|
|
58
|
+
return Promise.resolve(record ? recordToDocument(record) : null);
|
|
59
|
+
}
|
|
60
|
+
getByType(projectId, docType) {
|
|
61
|
+
const record = this.db
|
|
62
|
+
.prepare('SELECT * FROM project_documents WHERE project_id = ? AND doc_type = ?')
|
|
63
|
+
.get(projectId, docType);
|
|
64
|
+
return Promise.resolve(record ? recordToDocument(record) : null);
|
|
65
|
+
}
|
|
66
|
+
listByProject(projectId) {
|
|
67
|
+
const records = this.db
|
|
68
|
+
.prepare(`SELECT * FROM project_documents WHERE project_id = ?
|
|
69
|
+
ORDER BY
|
|
70
|
+
CASE doc_type
|
|
71
|
+
WHEN 'prd' THEN 1 WHEN 'architecture' THEN 2
|
|
72
|
+
WHEN 'design' THEN 3 WHEN 'notes' THEN 4
|
|
73
|
+
WHEN 'plan' THEN 5 ELSE 6
|
|
74
|
+
END, created_at ASC`)
|
|
75
|
+
.all(projectId);
|
|
76
|
+
return Promise.resolve(records.map(recordToDocument));
|
|
77
|
+
}
|
|
78
|
+
update(id, input) {
|
|
79
|
+
const updates = [];
|
|
80
|
+
const params = [];
|
|
81
|
+
if (input.title !== undefined) {
|
|
82
|
+
updates.push('title = ?');
|
|
83
|
+
params.push(input.title);
|
|
84
|
+
}
|
|
85
|
+
if (input.content !== undefined) {
|
|
86
|
+
updates.push('content = ?');
|
|
87
|
+
params.push(input.content);
|
|
88
|
+
}
|
|
89
|
+
if (updates.length === 0)
|
|
90
|
+
return this.getById(id);
|
|
91
|
+
updates.push('updated_at = ?');
|
|
92
|
+
params.push(new Date().toISOString());
|
|
93
|
+
params.push(id);
|
|
94
|
+
this.db
|
|
95
|
+
.prepare(`UPDATE project_documents SET ${updates.join(', ')} WHERE id = ?`)
|
|
96
|
+
.run(...params);
|
|
97
|
+
return this.getById(id);
|
|
98
|
+
}
|
|
99
|
+
delete(id) {
|
|
100
|
+
const result = this.db.prepare('DELETE FROM project_documents WHERE id = ?').run(id);
|
|
101
|
+
return Promise.resolve(result.changes > 0);
|
|
102
|
+
}
|
|
103
|
+
deleteByType(projectId, docType) {
|
|
104
|
+
const result = this.db
|
|
105
|
+
.prepare('DELETE FROM project_documents WHERE project_id = ? AND doc_type = ?')
|
|
106
|
+
.run(projectId, docType);
|
|
107
|
+
return Promise.resolve(result.changes > 0);
|
|
108
|
+
}
|
|
109
|
+
getTypeCounts(projectId) {
|
|
110
|
+
const results = this.db
|
|
111
|
+
.prepare('SELECT doc_type, COUNT(*) as count FROM project_documents WHERE project_id = ? GROUP BY doc_type')
|
|
112
|
+
.all(projectId);
|
|
113
|
+
const counts = {
|
|
114
|
+
prd: 0,
|
|
115
|
+
architecture: 0,
|
|
116
|
+
design: 0,
|
|
117
|
+
notes: 0,
|
|
118
|
+
plan: 0,
|
|
119
|
+
'app-model': 0,
|
|
120
|
+
};
|
|
121
|
+
for (const row of results) {
|
|
122
|
+
counts[row.doc_type] = row.count;
|
|
123
|
+
}
|
|
124
|
+
return Promise.resolve(counts);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Platform Repositories — Concrete implementations of platform interfaces.
|
|
3
|
+
*
|
|
4
|
+
* Requires better-sqlite3 as a peer dependency.
|
|
5
|
+
* Both CLI and Desktop import from this module to avoid code duplication.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createSQLiteRepositories, getDatabase } from '@compilr-dev/sdk/sqlite';
|
|
10
|
+
* // or from the main entry:
|
|
11
|
+
* import { createSQLiteRepositories, getDatabase } from '@compilr-dev/sdk';
|
|
12
|
+
*
|
|
13
|
+
* const db = getDatabase('~/.compilr-dev/projects.db');
|
|
14
|
+
* const repos = createSQLiteRepositories(db);
|
|
15
|
+
* // repos.projects, repos.workItems, repos.documents, repos.plans
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type Database from 'better-sqlite3';
|
|
19
|
+
import { type ProjectDeleteHooks } from './project-repository.js';
|
|
20
|
+
import type { IProjectRepository, IWorkItemRepository, IDocumentRepository, IPlanRepository } from '../repositories.js';
|
|
21
|
+
export interface SQLiteRepositories {
|
|
22
|
+
projects: IProjectRepository;
|
|
23
|
+
workItems: IWorkItemRepository;
|
|
24
|
+
documents: IDocumentRepository;
|
|
25
|
+
plans: IPlanRepository;
|
|
26
|
+
}
|
|
27
|
+
export interface CreateSQLiteRepositoriesOptions {
|
|
28
|
+
projectDeleteHooks?: ProjectDeleteHooks;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create all 4 platform repositories backed by a single SQLite database.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createSQLiteRepositories(db: Database.Database, options?: CreateSQLiteRepositoriesOptions): SQLiteRepositories;
|
|
34
|
+
export { SQLiteProjectRepository, type ProjectDeleteHooks } from './project-repository.js';
|
|
35
|
+
export { SQLiteWorkItemRepository } from './work-item-repository.js';
|
|
36
|
+
export { SQLiteDocumentRepository } from './document-repository.js';
|
|
37
|
+
export { SQLitePlanRepository } from './plan-repository.js';
|
|
38
|
+
export { getDatabase, closeDatabase, closeAllDatabases, databaseExists } from './db.js';
|
|
39
|
+
export { SCHEMA_VERSION, SCHEMA_SQL } from './schema.js';
|
|
40
|
+
export type { ProjectRecord, WorkItemRecord, ProjectDocumentRecord } from './schema.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite Platform Repositories — Concrete implementations of platform interfaces.
|
|
3
|
+
*
|
|
4
|
+
* Requires better-sqlite3 as a peer dependency.
|
|
5
|
+
* Both CLI and Desktop import from this module to avoid code duplication.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createSQLiteRepositories, getDatabase } from '@compilr-dev/sdk/sqlite';
|
|
10
|
+
* // or from the main entry:
|
|
11
|
+
* import { createSQLiteRepositories, getDatabase } from '@compilr-dev/sdk';
|
|
12
|
+
*
|
|
13
|
+
* const db = getDatabase('~/.compilr-dev/projects.db');
|
|
14
|
+
* const repos = createSQLiteRepositories(db);
|
|
15
|
+
* // repos.projects, repos.workItems, repos.documents, repos.plans
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import { SQLiteProjectRepository } from './project-repository.js';
|
|
19
|
+
import { SQLiteWorkItemRepository } from './work-item-repository.js';
|
|
20
|
+
import { SQLiteDocumentRepository } from './document-repository.js';
|
|
21
|
+
import { SQLitePlanRepository } from './plan-repository.js';
|
|
22
|
+
/**
|
|
23
|
+
* Create all 4 platform repositories backed by a single SQLite database.
|
|
24
|
+
*/
|
|
25
|
+
export function createSQLiteRepositories(db, options) {
|
|
26
|
+
return {
|
|
27
|
+
projects: new SQLiteProjectRepository(db, options?.projectDeleteHooks),
|
|
28
|
+
workItems: new SQLiteWorkItemRepository(db),
|
|
29
|
+
documents: new SQLiteDocumentRepository(db),
|
|
30
|
+
plans: new SQLitePlanRepository(db),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// Re-export classes for advanced usage
|
|
34
|
+
export { SQLiteProjectRepository } from './project-repository.js';
|
|
35
|
+
export { SQLiteWorkItemRepository } from './work-item-repository.js';
|
|
36
|
+
export { SQLiteDocumentRepository } from './document-repository.js';
|
|
37
|
+
export { SQLitePlanRepository } from './plan-repository.js';
|
|
38
|
+
// Re-export database utilities
|
|
39
|
+
export { getDatabase, closeDatabase, closeAllDatabases, databaseExists } from './db.js';
|
|
40
|
+
// Re-export schema (for consumers that need direct access)
|
|
41
|
+
export { SCHEMA_VERSION, SCHEMA_SQL } from './schema.js';
|