@compilr-dev/sdk 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,367 @@
1
+ /**
2
+ * Project Tools — CRUD operations for projects.
3
+ *
4
+ * 4 tools: project_get, project_create, project_update, project_list
5
+ *
6
+ * Ported from CLI's src/tools/project-db.ts. Key adaptations:
7
+ * - getActiveProject()?.id → ctx.currentProjectId
8
+ * - setCurrentProject() → ctx.currentProjectId = id + hooks?.onProjectResolved()
9
+ * - awardFirstProject() → hooks?.onFirstProject()
10
+ * - process.cwd() → config.cwd ?? process.cwd()
11
+ * - All repo calls are properly await-ed
12
+ */
13
+ import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
14
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
15
+ export function createProjectTools(config) {
16
+ const { context: ctx, hooks } = config;
17
+ // ---------------------------------------------------------------------------
18
+ // project_get
19
+ // ---------------------------------------------------------------------------
20
+ const projectGetTool = defineTool({
21
+ name: 'project_get',
22
+ description: 'Get the active project for the current directory, or get a project by ID/name. ' +
23
+ 'Returns project details including metadata, workflow state, and last context.',
24
+ inputSchema: {
25
+ type: 'object',
26
+ properties: {
27
+ project_id: {
28
+ type: 'number',
29
+ description: 'Project ID to look up (optional - uses active project or cwd detection if not provided)',
30
+ },
31
+ name: {
32
+ type: 'string',
33
+ description: 'Project name to look up (optional)',
34
+ },
35
+ path: {
36
+ type: 'string',
37
+ description: 'Path to check for project (optional - defaults to cwd)',
38
+ },
39
+ },
40
+ required: [],
41
+ },
42
+ execute: async (input) => {
43
+ try {
44
+ let project = null;
45
+ // Priority: ID > name > active > path detection
46
+ if (input.project_id) {
47
+ project = await ctx.projects.getById(input.project_id);
48
+ }
49
+ else if (input.name) {
50
+ project = await ctx.projects.getByName(input.name);
51
+ }
52
+ else if (ctx.currentProjectId) {
53
+ project = await ctx.projects.getById(ctx.currentProjectId);
54
+ }
55
+ else if (input.path) {
56
+ project = await ctx.projects.findByPath(input.path);
57
+ }
58
+ else {
59
+ const cwd = config.cwd ?? process.cwd();
60
+ project = await ctx.projects.findByPath(cwd);
61
+ }
62
+ if (!project) {
63
+ return createSuccessResult({
64
+ success: true,
65
+ project: null,
66
+ message: 'No project found. Use project_create to create a new project, or /projects to select one.',
67
+ });
68
+ }
69
+ // Update current project
70
+ ctx.currentProjectId = project.id;
71
+ hooks?.onProjectResolved?.({
72
+ id: project.id,
73
+ name: project.name,
74
+ displayName: project.displayName,
75
+ path: project.path,
76
+ });
77
+ return createSuccessResult({
78
+ success: true,
79
+ project: {
80
+ id: project.id,
81
+ name: project.name,
82
+ displayName: project.displayName,
83
+ description: project.description,
84
+ type: project.type,
85
+ status: project.status,
86
+ path: project.path,
87
+ docsPath: project.docsPath,
88
+ repoPattern: project.repoPattern,
89
+ language: project.language,
90
+ framework: project.framework,
91
+ packageManager: project.packageManager,
92
+ runtimeVersion: project.runtimeVersion,
93
+ commands: project.commands,
94
+ gitRemote: project.gitRemote,
95
+ gitBranch: project.gitBranch,
96
+ workflowMode: project.workflowMode,
97
+ lifecycleState: project.lifecycleState,
98
+ currentItemId: project.currentItemId,
99
+ lastContext: project.lastContext,
100
+ createdAt: project.createdAt.toISOString(),
101
+ updatedAt: project.updatedAt.toISOString(),
102
+ lastActivityAt: project.lastActivityAt?.toISOString(),
103
+ },
104
+ });
105
+ }
106
+ catch (error) {
107
+ return createErrorResult(`Failed to get project: ${error instanceof Error ? error.message : String(error)}`);
108
+ }
109
+ },
110
+ });
111
+ // ---------------------------------------------------------------------------
112
+ // project_create
113
+ // ---------------------------------------------------------------------------
114
+ const projectCreateTool = defineTool({
115
+ name: 'project_create',
116
+ description: 'Create a new tracked project. This promotes a directory to a tracked project with workflow support.',
117
+ inputSchema: {
118
+ type: 'object',
119
+ properties: {
120
+ name: {
121
+ type: 'string',
122
+ description: 'Filesystem-safe project name (e.g., "my-api-project")',
123
+ },
124
+ display_name: {
125
+ type: 'string',
126
+ description: 'Human-friendly display name (e.g., "My API Project")',
127
+ },
128
+ description: {
129
+ type: 'string',
130
+ description: 'Project description',
131
+ },
132
+ path: {
133
+ type: 'string',
134
+ description: 'Project path (defaults to cwd)',
135
+ },
136
+ type: {
137
+ type: 'string',
138
+ enum: ['general', 'web', 'cli', 'library', 'api'],
139
+ description: 'Project type',
140
+ },
141
+ workflow_mode: {
142
+ type: 'string',
143
+ enum: ['flexible', 'guided'],
144
+ description: 'Workflow mode: flexible (default) or guided',
145
+ },
146
+ language: {
147
+ type: 'string',
148
+ description: 'Programming language (e.g., "typescript", "python")',
149
+ },
150
+ framework: {
151
+ type: 'string',
152
+ description: 'Framework (e.g., "react", "express")',
153
+ },
154
+ package_manager: {
155
+ type: 'string',
156
+ description: 'Package manager (e.g., "npm", "yarn", "pnpm")',
157
+ },
158
+ },
159
+ required: ['name', 'display_name'],
160
+ },
161
+ execute: async (input) => {
162
+ try {
163
+ // Check if project with this name already exists
164
+ const existing = await ctx.projects.getByName(input.name);
165
+ if (existing) {
166
+ return createErrorResult(`Project with name "${input.name}" already exists`);
167
+ }
168
+ // Check if a project exists at this path
169
+ const projectPath = input.path ?? config.cwd ?? process.cwd();
170
+ const existingByPath = await ctx.projects.findByPath(projectPath);
171
+ if (existingByPath) {
172
+ return createErrorResult(`A project already exists at path "${projectPath}": ${existingByPath.name}`);
173
+ }
174
+ const createInput = {
175
+ name: input.name,
176
+ display_name: input.display_name,
177
+ description: input.description,
178
+ path: projectPath,
179
+ type: input.type ? input.type : 'general',
180
+ workflow_mode: input.workflow_mode ? input.workflow_mode : 'flexible',
181
+ language: input.language,
182
+ framework: input.framework,
183
+ package_manager: input.package_manager,
184
+ };
185
+ const project = await ctx.projects.create(createInput);
186
+ // Award coin for first project
187
+ const listResult = await ctx.projects.list();
188
+ if (listResult.total === 1) {
189
+ hooks?.onFirstProject?.({ id: project.id, name: project.name });
190
+ }
191
+ // Set as current project
192
+ ctx.currentProjectId = project.id;
193
+ hooks?.onProjectResolved?.({
194
+ id: project.id,
195
+ name: project.name,
196
+ displayName: project.displayName,
197
+ path: project.path,
198
+ });
199
+ return createSuccessResult({
200
+ success: true,
201
+ message: `Project "${project.displayName}" created successfully`,
202
+ project: {
203
+ id: project.id,
204
+ name: project.name,
205
+ displayName: project.displayName,
206
+ type: project.type,
207
+ workflowMode: project.workflowMode,
208
+ path: project.path,
209
+ },
210
+ });
211
+ }
212
+ catch (error) {
213
+ return createErrorResult(`Failed to create project: ${error instanceof Error ? error.message : String(error)}`);
214
+ }
215
+ },
216
+ });
217
+ // ---------------------------------------------------------------------------
218
+ // project_update
219
+ // ---------------------------------------------------------------------------
220
+ const projectUpdateTool = defineTool({
221
+ name: 'project_update',
222
+ description: 'Update an existing project. Can update status, description, workflow mode, lifecycle state, and other fields.',
223
+ inputSchema: {
224
+ type: 'object',
225
+ properties: {
226
+ project_id: {
227
+ type: 'number',
228
+ description: 'Project ID to update (uses active project if not provided)',
229
+ },
230
+ status: {
231
+ type: 'string',
232
+ enum: ['active', 'paused', 'completed', 'archived'],
233
+ description: 'Project status',
234
+ },
235
+ description: {
236
+ type: 'string',
237
+ description: 'Project description',
238
+ },
239
+ workflow_mode: {
240
+ type: 'string',
241
+ enum: ['flexible', 'guided'],
242
+ description: 'Workflow mode',
243
+ },
244
+ lifecycle_state: {
245
+ type: 'string',
246
+ enum: ['setup', 'active', 'maintenance', 'complete'],
247
+ description: 'Project lifecycle state',
248
+ },
249
+ current_item_id: {
250
+ type: 'string',
251
+ description: 'Currently focused work item ID (e.g., "REQ-003")',
252
+ },
253
+ git_remote: {
254
+ type: 'string',
255
+ description: 'Git remote URL',
256
+ },
257
+ git_branch: {
258
+ type: 'string',
259
+ description: 'Default git branch',
260
+ },
261
+ last_context: {
262
+ type: 'object',
263
+ description: 'Context for session resumption',
264
+ },
265
+ },
266
+ required: [],
267
+ },
268
+ execute: async (input) => {
269
+ try {
270
+ const projectId = input.project_id ?? ctx.currentProjectId;
271
+ if (!projectId) {
272
+ return createErrorResult('No project specified and no active project. Use project_get or /projects to select a project.');
273
+ }
274
+ const updateInput = {};
275
+ if (input.status)
276
+ updateInput.status = input.status;
277
+ if (input.description !== undefined)
278
+ updateInput.description = input.description;
279
+ if (input.workflow_mode)
280
+ updateInput.workflow_mode = input.workflow_mode;
281
+ if (input.lifecycle_state)
282
+ updateInput.lifecycle_state = input.lifecycle_state;
283
+ if (input.current_item_id !== undefined)
284
+ updateInput.current_item_id = input.current_item_id;
285
+ if (input.git_remote !== undefined)
286
+ updateInput.git_remote = input.git_remote;
287
+ if (input.git_branch !== undefined)
288
+ updateInput.git_branch = input.git_branch;
289
+ if (input.last_context)
290
+ updateInput.last_context = input.last_context;
291
+ const project = await ctx.projects.update(projectId, updateInput);
292
+ if (!project) {
293
+ return createErrorResult(`Project with ID ${String(projectId)} not found`);
294
+ }
295
+ return createSuccessResult({
296
+ success: true,
297
+ message: `Project "${project.displayName}" updated`,
298
+ project: {
299
+ id: project.id,
300
+ name: project.name,
301
+ displayName: project.displayName,
302
+ status: project.status,
303
+ workflowMode: project.workflowMode,
304
+ lifecycleState: project.lifecycleState,
305
+ currentItemId: project.currentItemId,
306
+ gitRemote: project.gitRemote,
307
+ gitBranch: project.gitBranch,
308
+ },
309
+ });
310
+ }
311
+ catch (error) {
312
+ return createErrorResult(`Failed to update project: ${error instanceof Error ? error.message : String(error)}`);
313
+ }
314
+ },
315
+ });
316
+ // ---------------------------------------------------------------------------
317
+ // project_list
318
+ // ---------------------------------------------------------------------------
319
+ const projectListTool = defineTool({
320
+ name: 'project_list',
321
+ description: 'List all tracked projects, optionally filtered by status.',
322
+ inputSchema: {
323
+ type: 'object',
324
+ properties: {
325
+ status: {
326
+ type: 'string',
327
+ enum: ['active', 'paused', 'completed', 'archived', 'all'],
328
+ description: 'Filter by status (default: all)',
329
+ },
330
+ limit: {
331
+ type: 'number',
332
+ description: 'Maximum number of projects to return (default: 20)',
333
+ },
334
+ },
335
+ required: [],
336
+ },
337
+ execute: async (input) => {
338
+ try {
339
+ const result = await ctx.projects.list({
340
+ status: input.status,
341
+ limit: input.limit ?? 20,
342
+ });
343
+ const projects = result.projects.map((p) => ({
344
+ id: p.id,
345
+ name: p.name,
346
+ displayName: p.displayName,
347
+ type: p.type,
348
+ status: p.status,
349
+ path: p.path,
350
+ workflowMode: p.workflowMode,
351
+ lifecycleState: p.lifecycleState,
352
+ lastActivityAt: p.lastActivityAt?.toISOString(),
353
+ }));
354
+ return createSuccessResult({
355
+ success: true,
356
+ projects,
357
+ total: result.total,
358
+ currentProjectId: ctx.currentProjectId,
359
+ });
360
+ }
361
+ catch (error) {
362
+ return createErrorResult(`Failed to list projects: ${error instanceof Error ? error.message : String(error)}`);
363
+ }
364
+ },
365
+ });
366
+ return [projectGetTool, projectCreateTool, projectUpdateTool, projectListTool];
367
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Work Item Tools — CRUD + workflow operations for work items.
3
+ *
4
+ * 9 tools: workitem_query, workitem_add, workitem_update, workitem_next,
5
+ * workitem_delete, workitem_status_counts, workitem_advance_step,
6
+ * workitem_claim, workitem_handoff
7
+ *
8
+ * Ported from CLI's src/tools/workitem-db.ts. Key adaptations:
9
+ * - getActiveProject()?.id → ctx.currentProjectId
10
+ * - getActiveSharedContext() "self" resolution → hooks?.resolveOwner?.('self')
11
+ * - awardWorkItemCompletion() → hooks?.onWorkItemCompleted?.(item)
12
+ * - workitem_advance_step imports from ../workflow.js (SDK's module)
13
+ */
14
+ import type { PlatformToolsConfig } from '../context.js';
15
+ export declare function createWorkItemTools(config: PlatformToolsConfig): (import("@compilr-dev/agents").Tool<{
16
+ project_id?: number;
17
+ status?: string;
18
+ type?: string;
19
+ priority?: string;
20
+ owner?: string;
21
+ search?: string;
22
+ limit?: number;
23
+ offset?: number;
24
+ }> | import("@compilr-dev/agents").Tool<{
25
+ project_id?: number;
26
+ type: string;
27
+ title: string;
28
+ description?: string;
29
+ priority?: string;
30
+ estimated_effort?: string;
31
+ owner?: string;
32
+ }> | import("@compilr-dev/agents").Tool<{
33
+ item_id: string;
34
+ project_id?: number;
35
+ status?: string;
36
+ priority?: string;
37
+ owner?: string;
38
+ guided_step?: string;
39
+ title?: string;
40
+ description?: string;
41
+ commit_hash?: string;
42
+ }> | import("@compilr-dev/agents").Tool<{
43
+ item_id: string;
44
+ reason: string;
45
+ force?: boolean;
46
+ project_id?: number;
47
+ }> | import("@compilr-dev/agents").Tool<{
48
+ item_id: string;
49
+ agent_id: string;
50
+ project_id?: number;
51
+ }> | import("@compilr-dev/agents").Tool<{
52
+ item_id: string;
53
+ to_agent_id: string;
54
+ notes?: string;
55
+ project_id?: number;
56
+ }>)[];