thepopebot 1.2.76-beta.11 → 1.2.76-beta.12
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/bin/cli.js +8 -5
- package/drizzle/0023_needy_ender_wiggin.sql +1 -0
- package/drizzle/meta/0023_snapshot.json +750 -0
- package/drizzle/meta/_journal.json +7 -0
- package/lib/CLAUDE.md +2 -2
- package/lib/actions.js +9 -1
- package/lib/ai/index.js +2 -0
- package/lib/ai/sdk-adapters/claude-code.js +20 -9
- package/lib/ai/system-prompt.js +21 -6
- package/lib/ai/tools.js +10 -3
- package/lib/code/actions.js +23 -4
- package/lib/db/code-workspaces.js +2 -1
- package/lib/db/schema.js +1 -0
- package/lib/tools/create-agent-job.js +3 -0
- package/lib/tools/docker.js +4 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md.template +23 -0
- package/templates/agent-job/SYSTEM.md +8 -1
package/bin/cli.js
CHANGED
|
@@ -86,12 +86,12 @@ function getTemplateFiles(templatesDir) {
|
|
|
86
86
|
return files;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
async function init() {
|
|
89
|
+
async function init(options = {}) {
|
|
90
90
|
let cwd = process.cwd();
|
|
91
91
|
const packageDir = path.join(__dirname, '..');
|
|
92
92
|
const templatesDir = path.join(packageDir, 'templates');
|
|
93
|
-
const noManaged = args.includes('--no-managed');
|
|
94
|
-
const noInstall = args.includes('--no-install');
|
|
93
|
+
const noManaged = options.noManaged ?? args.includes('--no-managed');
|
|
94
|
+
const noInstall = options.noInstall ?? args.includes('--no-install');
|
|
95
95
|
|
|
96
96
|
// Guard: warn if the directory is not empty (unless it's an existing thepopebot project)
|
|
97
97
|
const entries = fs.readdirSync(cwd);
|
|
@@ -646,6 +646,9 @@ const PROTECTED_PATHS = [
|
|
|
646
646
|
'package.json',
|
|
647
647
|
'docker-compose.custom.yml',
|
|
648
648
|
'.claude/',
|
|
649
|
+
'.codex/',
|
|
650
|
+
'.gemini/',
|
|
651
|
+
'.kimi/',
|
|
649
652
|
'.pi/',
|
|
650
653
|
'skills/',
|
|
651
654
|
'node_modules/',
|
|
@@ -739,10 +742,10 @@ async function resetAll() {
|
|
|
739
742
|
|
|
740
743
|
console.log(`\n Moved ${filesToMove.length} file(s) to .backups/${ts}/`);
|
|
741
744
|
|
|
742
|
-
// Run init to rebuild from templates
|
|
745
|
+
// Run init to rebuild from templates (call directly to use the same package version)
|
|
743
746
|
console.log('\n Running init to rebuild project...\n');
|
|
744
747
|
try {
|
|
745
|
-
|
|
748
|
+
await init({ noInstall: true });
|
|
746
749
|
} catch {
|
|
747
750
|
console.error('\n Init failed. Your backup is at .backups/' + ts + '/\n');
|
|
748
751
|
process.exit(1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE `code_workspaces` ADD `scope` text;
|
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "6",
|
|
3
|
+
"dialect": "sqlite",
|
|
4
|
+
"id": "d1566547-b9cf-4d86-9fcf-416f371ebcad",
|
|
5
|
+
"prevId": "65a1b238-c4cb-4ee1-9eca-f82d07858af2",
|
|
6
|
+
"tables": {
|
|
7
|
+
"chats": {
|
|
8
|
+
"name": "chats",
|
|
9
|
+
"columns": {
|
|
10
|
+
"id": {
|
|
11
|
+
"name": "id",
|
|
12
|
+
"type": "text",
|
|
13
|
+
"primaryKey": true,
|
|
14
|
+
"notNull": true,
|
|
15
|
+
"autoincrement": false
|
|
16
|
+
},
|
|
17
|
+
"user_id": {
|
|
18
|
+
"name": "user_id",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"primaryKey": false,
|
|
21
|
+
"notNull": true,
|
|
22
|
+
"autoincrement": false
|
|
23
|
+
},
|
|
24
|
+
"title": {
|
|
25
|
+
"name": "title",
|
|
26
|
+
"type": "text",
|
|
27
|
+
"primaryKey": false,
|
|
28
|
+
"notNull": true,
|
|
29
|
+
"autoincrement": false,
|
|
30
|
+
"default": "'New Chat'"
|
|
31
|
+
},
|
|
32
|
+
"starred": {
|
|
33
|
+
"name": "starred",
|
|
34
|
+
"type": "integer",
|
|
35
|
+
"primaryKey": false,
|
|
36
|
+
"notNull": true,
|
|
37
|
+
"autoincrement": false,
|
|
38
|
+
"default": 0
|
|
39
|
+
},
|
|
40
|
+
"chat_mode": {
|
|
41
|
+
"name": "chat_mode",
|
|
42
|
+
"type": "text",
|
|
43
|
+
"primaryKey": false,
|
|
44
|
+
"notNull": true,
|
|
45
|
+
"autoincrement": false,
|
|
46
|
+
"default": "'agent'"
|
|
47
|
+
},
|
|
48
|
+
"code_workspace_id": {
|
|
49
|
+
"name": "code_workspace_id",
|
|
50
|
+
"type": "text",
|
|
51
|
+
"primaryKey": false,
|
|
52
|
+
"notNull": false,
|
|
53
|
+
"autoincrement": false
|
|
54
|
+
},
|
|
55
|
+
"created_at": {
|
|
56
|
+
"name": "created_at",
|
|
57
|
+
"type": "integer",
|
|
58
|
+
"primaryKey": false,
|
|
59
|
+
"notNull": true,
|
|
60
|
+
"autoincrement": false
|
|
61
|
+
},
|
|
62
|
+
"updated_at": {
|
|
63
|
+
"name": "updated_at",
|
|
64
|
+
"type": "integer",
|
|
65
|
+
"primaryKey": false,
|
|
66
|
+
"notNull": true,
|
|
67
|
+
"autoincrement": false
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"indexes": {},
|
|
71
|
+
"foreignKeys": {},
|
|
72
|
+
"compositePrimaryKeys": {},
|
|
73
|
+
"uniqueConstraints": {},
|
|
74
|
+
"checkConstraints": {}
|
|
75
|
+
},
|
|
76
|
+
"cluster_roles": {
|
|
77
|
+
"name": "cluster_roles",
|
|
78
|
+
"columns": {
|
|
79
|
+
"id": {
|
|
80
|
+
"name": "id",
|
|
81
|
+
"type": "text",
|
|
82
|
+
"primaryKey": true,
|
|
83
|
+
"notNull": true,
|
|
84
|
+
"autoincrement": false
|
|
85
|
+
},
|
|
86
|
+
"cluster_id": {
|
|
87
|
+
"name": "cluster_id",
|
|
88
|
+
"type": "text",
|
|
89
|
+
"primaryKey": false,
|
|
90
|
+
"notNull": true,
|
|
91
|
+
"autoincrement": false
|
|
92
|
+
},
|
|
93
|
+
"role_name": {
|
|
94
|
+
"name": "role_name",
|
|
95
|
+
"type": "text",
|
|
96
|
+
"primaryKey": false,
|
|
97
|
+
"notNull": true,
|
|
98
|
+
"autoincrement": false
|
|
99
|
+
},
|
|
100
|
+
"role": {
|
|
101
|
+
"name": "role",
|
|
102
|
+
"type": "text",
|
|
103
|
+
"primaryKey": false,
|
|
104
|
+
"notNull": true,
|
|
105
|
+
"autoincrement": false,
|
|
106
|
+
"default": "''"
|
|
107
|
+
},
|
|
108
|
+
"prompt": {
|
|
109
|
+
"name": "prompt",
|
|
110
|
+
"type": "text",
|
|
111
|
+
"primaryKey": false,
|
|
112
|
+
"notNull": true,
|
|
113
|
+
"autoincrement": false,
|
|
114
|
+
"default": "'Execute your role.'"
|
|
115
|
+
},
|
|
116
|
+
"trigger_config": {
|
|
117
|
+
"name": "trigger_config",
|
|
118
|
+
"type": "text",
|
|
119
|
+
"primaryKey": false,
|
|
120
|
+
"notNull": false,
|
|
121
|
+
"autoincrement": false
|
|
122
|
+
},
|
|
123
|
+
"max_concurrency": {
|
|
124
|
+
"name": "max_concurrency",
|
|
125
|
+
"type": "integer",
|
|
126
|
+
"primaryKey": false,
|
|
127
|
+
"notNull": true,
|
|
128
|
+
"autoincrement": false,
|
|
129
|
+
"default": 1
|
|
130
|
+
},
|
|
131
|
+
"cleanup_worker_dir": {
|
|
132
|
+
"name": "cleanup_worker_dir",
|
|
133
|
+
"type": "integer",
|
|
134
|
+
"primaryKey": false,
|
|
135
|
+
"notNull": true,
|
|
136
|
+
"autoincrement": false,
|
|
137
|
+
"default": 0
|
|
138
|
+
},
|
|
139
|
+
"plan_mode": {
|
|
140
|
+
"name": "plan_mode",
|
|
141
|
+
"type": "integer",
|
|
142
|
+
"primaryKey": false,
|
|
143
|
+
"notNull": true,
|
|
144
|
+
"autoincrement": false,
|
|
145
|
+
"default": 0
|
|
146
|
+
},
|
|
147
|
+
"folders": {
|
|
148
|
+
"name": "folders",
|
|
149
|
+
"type": "text",
|
|
150
|
+
"primaryKey": false,
|
|
151
|
+
"notNull": false,
|
|
152
|
+
"autoincrement": false
|
|
153
|
+
},
|
|
154
|
+
"sort_order": {
|
|
155
|
+
"name": "sort_order",
|
|
156
|
+
"type": "integer",
|
|
157
|
+
"primaryKey": false,
|
|
158
|
+
"notNull": true,
|
|
159
|
+
"autoincrement": false,
|
|
160
|
+
"default": 0
|
|
161
|
+
},
|
|
162
|
+
"created_at": {
|
|
163
|
+
"name": "created_at",
|
|
164
|
+
"type": "integer",
|
|
165
|
+
"primaryKey": false,
|
|
166
|
+
"notNull": true,
|
|
167
|
+
"autoincrement": false
|
|
168
|
+
},
|
|
169
|
+
"updated_at": {
|
|
170
|
+
"name": "updated_at",
|
|
171
|
+
"type": "integer",
|
|
172
|
+
"primaryKey": false,
|
|
173
|
+
"notNull": true,
|
|
174
|
+
"autoincrement": false
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
"indexes": {},
|
|
178
|
+
"foreignKeys": {},
|
|
179
|
+
"compositePrimaryKeys": {},
|
|
180
|
+
"uniqueConstraints": {},
|
|
181
|
+
"checkConstraints": {}
|
|
182
|
+
},
|
|
183
|
+
"clusters": {
|
|
184
|
+
"name": "clusters",
|
|
185
|
+
"columns": {
|
|
186
|
+
"id": {
|
|
187
|
+
"name": "id",
|
|
188
|
+
"type": "text",
|
|
189
|
+
"primaryKey": true,
|
|
190
|
+
"notNull": true,
|
|
191
|
+
"autoincrement": false
|
|
192
|
+
},
|
|
193
|
+
"user_id": {
|
|
194
|
+
"name": "user_id",
|
|
195
|
+
"type": "text",
|
|
196
|
+
"primaryKey": false,
|
|
197
|
+
"notNull": true,
|
|
198
|
+
"autoincrement": false
|
|
199
|
+
},
|
|
200
|
+
"name": {
|
|
201
|
+
"name": "name",
|
|
202
|
+
"type": "text",
|
|
203
|
+
"primaryKey": false,
|
|
204
|
+
"notNull": true,
|
|
205
|
+
"autoincrement": false,
|
|
206
|
+
"default": "'New Cluster'"
|
|
207
|
+
},
|
|
208
|
+
"system_prompt": {
|
|
209
|
+
"name": "system_prompt",
|
|
210
|
+
"type": "text",
|
|
211
|
+
"primaryKey": false,
|
|
212
|
+
"notNull": true,
|
|
213
|
+
"autoincrement": false,
|
|
214
|
+
"default": "''"
|
|
215
|
+
},
|
|
216
|
+
"folders": {
|
|
217
|
+
"name": "folders",
|
|
218
|
+
"type": "text",
|
|
219
|
+
"primaryKey": false,
|
|
220
|
+
"notNull": false,
|
|
221
|
+
"autoincrement": false
|
|
222
|
+
},
|
|
223
|
+
"enabled": {
|
|
224
|
+
"name": "enabled",
|
|
225
|
+
"type": "integer",
|
|
226
|
+
"primaryKey": false,
|
|
227
|
+
"notNull": true,
|
|
228
|
+
"autoincrement": false,
|
|
229
|
+
"default": 0
|
|
230
|
+
},
|
|
231
|
+
"starred": {
|
|
232
|
+
"name": "starred",
|
|
233
|
+
"type": "integer",
|
|
234
|
+
"primaryKey": false,
|
|
235
|
+
"notNull": true,
|
|
236
|
+
"autoincrement": false,
|
|
237
|
+
"default": 0
|
|
238
|
+
},
|
|
239
|
+
"created_at": {
|
|
240
|
+
"name": "created_at",
|
|
241
|
+
"type": "integer",
|
|
242
|
+
"primaryKey": false,
|
|
243
|
+
"notNull": true,
|
|
244
|
+
"autoincrement": false
|
|
245
|
+
},
|
|
246
|
+
"updated_at": {
|
|
247
|
+
"name": "updated_at",
|
|
248
|
+
"type": "integer",
|
|
249
|
+
"primaryKey": false,
|
|
250
|
+
"notNull": true,
|
|
251
|
+
"autoincrement": false
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"indexes": {},
|
|
255
|
+
"foreignKeys": {},
|
|
256
|
+
"compositePrimaryKeys": {},
|
|
257
|
+
"uniqueConstraints": {},
|
|
258
|
+
"checkConstraints": {}
|
|
259
|
+
},
|
|
260
|
+
"code_workspaces": {
|
|
261
|
+
"name": "code_workspaces",
|
|
262
|
+
"columns": {
|
|
263
|
+
"id": {
|
|
264
|
+
"name": "id",
|
|
265
|
+
"type": "text",
|
|
266
|
+
"primaryKey": true,
|
|
267
|
+
"notNull": true,
|
|
268
|
+
"autoincrement": false
|
|
269
|
+
},
|
|
270
|
+
"user_id": {
|
|
271
|
+
"name": "user_id",
|
|
272
|
+
"type": "text",
|
|
273
|
+
"primaryKey": false,
|
|
274
|
+
"notNull": true,
|
|
275
|
+
"autoincrement": false
|
|
276
|
+
},
|
|
277
|
+
"container_name": {
|
|
278
|
+
"name": "container_name",
|
|
279
|
+
"type": "text",
|
|
280
|
+
"primaryKey": false,
|
|
281
|
+
"notNull": false,
|
|
282
|
+
"autoincrement": false
|
|
283
|
+
},
|
|
284
|
+
"repo": {
|
|
285
|
+
"name": "repo",
|
|
286
|
+
"type": "text",
|
|
287
|
+
"primaryKey": false,
|
|
288
|
+
"notNull": false,
|
|
289
|
+
"autoincrement": false
|
|
290
|
+
},
|
|
291
|
+
"branch": {
|
|
292
|
+
"name": "branch",
|
|
293
|
+
"type": "text",
|
|
294
|
+
"primaryKey": false,
|
|
295
|
+
"notNull": false,
|
|
296
|
+
"autoincrement": false
|
|
297
|
+
},
|
|
298
|
+
"feature_branch": {
|
|
299
|
+
"name": "feature_branch",
|
|
300
|
+
"type": "text",
|
|
301
|
+
"primaryKey": false,
|
|
302
|
+
"notNull": false,
|
|
303
|
+
"autoincrement": false
|
|
304
|
+
},
|
|
305
|
+
"title": {
|
|
306
|
+
"name": "title",
|
|
307
|
+
"type": "text",
|
|
308
|
+
"primaryKey": false,
|
|
309
|
+
"notNull": true,
|
|
310
|
+
"autoincrement": false,
|
|
311
|
+
"default": "'Code Workspace'"
|
|
312
|
+
},
|
|
313
|
+
"last_interactive_commit": {
|
|
314
|
+
"name": "last_interactive_commit",
|
|
315
|
+
"type": "text",
|
|
316
|
+
"primaryKey": false,
|
|
317
|
+
"notNull": false,
|
|
318
|
+
"autoincrement": false
|
|
319
|
+
},
|
|
320
|
+
"coding_agent": {
|
|
321
|
+
"name": "coding_agent",
|
|
322
|
+
"type": "text",
|
|
323
|
+
"primaryKey": false,
|
|
324
|
+
"notNull": false,
|
|
325
|
+
"autoincrement": false
|
|
326
|
+
},
|
|
327
|
+
"scope": {
|
|
328
|
+
"name": "scope",
|
|
329
|
+
"type": "text",
|
|
330
|
+
"primaryKey": false,
|
|
331
|
+
"notNull": false,
|
|
332
|
+
"autoincrement": false
|
|
333
|
+
},
|
|
334
|
+
"starred": {
|
|
335
|
+
"name": "starred",
|
|
336
|
+
"type": "integer",
|
|
337
|
+
"primaryKey": false,
|
|
338
|
+
"notNull": true,
|
|
339
|
+
"autoincrement": false,
|
|
340
|
+
"default": 0
|
|
341
|
+
},
|
|
342
|
+
"has_changes": {
|
|
343
|
+
"name": "has_changes",
|
|
344
|
+
"type": "integer",
|
|
345
|
+
"primaryKey": false,
|
|
346
|
+
"notNull": true,
|
|
347
|
+
"autoincrement": false,
|
|
348
|
+
"default": 0
|
|
349
|
+
},
|
|
350
|
+
"created_at": {
|
|
351
|
+
"name": "created_at",
|
|
352
|
+
"type": "integer",
|
|
353
|
+
"primaryKey": false,
|
|
354
|
+
"notNull": true,
|
|
355
|
+
"autoincrement": false
|
|
356
|
+
},
|
|
357
|
+
"updated_at": {
|
|
358
|
+
"name": "updated_at",
|
|
359
|
+
"type": "integer",
|
|
360
|
+
"primaryKey": false,
|
|
361
|
+
"notNull": true,
|
|
362
|
+
"autoincrement": false
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
"indexes": {
|
|
366
|
+
"code_workspaces_container_name_unique": {
|
|
367
|
+
"name": "code_workspaces_container_name_unique",
|
|
368
|
+
"columns": [
|
|
369
|
+
"container_name"
|
|
370
|
+
],
|
|
371
|
+
"isUnique": true
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
"foreignKeys": {},
|
|
375
|
+
"compositePrimaryKeys": {},
|
|
376
|
+
"uniqueConstraints": {},
|
|
377
|
+
"checkConstraints": {}
|
|
378
|
+
},
|
|
379
|
+
"messages": {
|
|
380
|
+
"name": "messages",
|
|
381
|
+
"columns": {
|
|
382
|
+
"id": {
|
|
383
|
+
"name": "id",
|
|
384
|
+
"type": "text",
|
|
385
|
+
"primaryKey": true,
|
|
386
|
+
"notNull": true,
|
|
387
|
+
"autoincrement": false
|
|
388
|
+
},
|
|
389
|
+
"chat_id": {
|
|
390
|
+
"name": "chat_id",
|
|
391
|
+
"type": "text",
|
|
392
|
+
"primaryKey": false,
|
|
393
|
+
"notNull": true,
|
|
394
|
+
"autoincrement": false
|
|
395
|
+
},
|
|
396
|
+
"role": {
|
|
397
|
+
"name": "role",
|
|
398
|
+
"type": "text",
|
|
399
|
+
"primaryKey": false,
|
|
400
|
+
"notNull": true,
|
|
401
|
+
"autoincrement": false
|
|
402
|
+
},
|
|
403
|
+
"content": {
|
|
404
|
+
"name": "content",
|
|
405
|
+
"type": "text",
|
|
406
|
+
"primaryKey": false,
|
|
407
|
+
"notNull": true,
|
|
408
|
+
"autoincrement": false
|
|
409
|
+
},
|
|
410
|
+
"created_at": {
|
|
411
|
+
"name": "created_at",
|
|
412
|
+
"type": "integer",
|
|
413
|
+
"primaryKey": false,
|
|
414
|
+
"notNull": true,
|
|
415
|
+
"autoincrement": false
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
"indexes": {},
|
|
419
|
+
"foreignKeys": {},
|
|
420
|
+
"compositePrimaryKeys": {},
|
|
421
|
+
"uniqueConstraints": {},
|
|
422
|
+
"checkConstraints": {}
|
|
423
|
+
},
|
|
424
|
+
"notifications": {
|
|
425
|
+
"name": "notifications",
|
|
426
|
+
"columns": {
|
|
427
|
+
"id": {
|
|
428
|
+
"name": "id",
|
|
429
|
+
"type": "text",
|
|
430
|
+
"primaryKey": true,
|
|
431
|
+
"notNull": true,
|
|
432
|
+
"autoincrement": false
|
|
433
|
+
},
|
|
434
|
+
"notification": {
|
|
435
|
+
"name": "notification",
|
|
436
|
+
"type": "text",
|
|
437
|
+
"primaryKey": false,
|
|
438
|
+
"notNull": true,
|
|
439
|
+
"autoincrement": false
|
|
440
|
+
},
|
|
441
|
+
"payload": {
|
|
442
|
+
"name": "payload",
|
|
443
|
+
"type": "text",
|
|
444
|
+
"primaryKey": false,
|
|
445
|
+
"notNull": true,
|
|
446
|
+
"autoincrement": false
|
|
447
|
+
},
|
|
448
|
+
"read": {
|
|
449
|
+
"name": "read",
|
|
450
|
+
"type": "integer",
|
|
451
|
+
"primaryKey": false,
|
|
452
|
+
"notNull": true,
|
|
453
|
+
"autoincrement": false,
|
|
454
|
+
"default": 0
|
|
455
|
+
},
|
|
456
|
+
"created_at": {
|
|
457
|
+
"name": "created_at",
|
|
458
|
+
"type": "integer",
|
|
459
|
+
"primaryKey": false,
|
|
460
|
+
"notNull": true,
|
|
461
|
+
"autoincrement": false
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
"indexes": {},
|
|
465
|
+
"foreignKeys": {},
|
|
466
|
+
"compositePrimaryKeys": {},
|
|
467
|
+
"uniqueConstraints": {},
|
|
468
|
+
"checkConstraints": {}
|
|
469
|
+
},
|
|
470
|
+
"settings": {
|
|
471
|
+
"name": "settings",
|
|
472
|
+
"columns": {
|
|
473
|
+
"id": {
|
|
474
|
+
"name": "id",
|
|
475
|
+
"type": "text",
|
|
476
|
+
"primaryKey": true,
|
|
477
|
+
"notNull": true,
|
|
478
|
+
"autoincrement": false
|
|
479
|
+
},
|
|
480
|
+
"type": {
|
|
481
|
+
"name": "type",
|
|
482
|
+
"type": "text",
|
|
483
|
+
"primaryKey": false,
|
|
484
|
+
"notNull": true,
|
|
485
|
+
"autoincrement": false
|
|
486
|
+
},
|
|
487
|
+
"key": {
|
|
488
|
+
"name": "key",
|
|
489
|
+
"type": "text",
|
|
490
|
+
"primaryKey": false,
|
|
491
|
+
"notNull": true,
|
|
492
|
+
"autoincrement": false
|
|
493
|
+
},
|
|
494
|
+
"value": {
|
|
495
|
+
"name": "value",
|
|
496
|
+
"type": "text",
|
|
497
|
+
"primaryKey": false,
|
|
498
|
+
"notNull": true,
|
|
499
|
+
"autoincrement": false
|
|
500
|
+
},
|
|
501
|
+
"created_by": {
|
|
502
|
+
"name": "created_by",
|
|
503
|
+
"type": "text",
|
|
504
|
+
"primaryKey": false,
|
|
505
|
+
"notNull": false,
|
|
506
|
+
"autoincrement": false
|
|
507
|
+
},
|
|
508
|
+
"last_used_at": {
|
|
509
|
+
"name": "last_used_at",
|
|
510
|
+
"type": "integer",
|
|
511
|
+
"primaryKey": false,
|
|
512
|
+
"notNull": false,
|
|
513
|
+
"autoincrement": false
|
|
514
|
+
},
|
|
515
|
+
"created_at": {
|
|
516
|
+
"name": "created_at",
|
|
517
|
+
"type": "integer",
|
|
518
|
+
"primaryKey": false,
|
|
519
|
+
"notNull": true,
|
|
520
|
+
"autoincrement": false
|
|
521
|
+
},
|
|
522
|
+
"updated_at": {
|
|
523
|
+
"name": "updated_at",
|
|
524
|
+
"type": "integer",
|
|
525
|
+
"primaryKey": false,
|
|
526
|
+
"notNull": true,
|
|
527
|
+
"autoincrement": false
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
"indexes": {},
|
|
531
|
+
"foreignKeys": {},
|
|
532
|
+
"compositePrimaryKeys": {},
|
|
533
|
+
"uniqueConstraints": {},
|
|
534
|
+
"checkConstraints": {}
|
|
535
|
+
},
|
|
536
|
+
"subscriptions": {
|
|
537
|
+
"name": "subscriptions",
|
|
538
|
+
"columns": {
|
|
539
|
+
"id": {
|
|
540
|
+
"name": "id",
|
|
541
|
+
"type": "text",
|
|
542
|
+
"primaryKey": true,
|
|
543
|
+
"notNull": true,
|
|
544
|
+
"autoincrement": false
|
|
545
|
+
},
|
|
546
|
+
"platform": {
|
|
547
|
+
"name": "platform",
|
|
548
|
+
"type": "text",
|
|
549
|
+
"primaryKey": false,
|
|
550
|
+
"notNull": true,
|
|
551
|
+
"autoincrement": false
|
|
552
|
+
},
|
|
553
|
+
"channel_id": {
|
|
554
|
+
"name": "channel_id",
|
|
555
|
+
"type": "text",
|
|
556
|
+
"primaryKey": false,
|
|
557
|
+
"notNull": true,
|
|
558
|
+
"autoincrement": false
|
|
559
|
+
},
|
|
560
|
+
"created_at": {
|
|
561
|
+
"name": "created_at",
|
|
562
|
+
"type": "integer",
|
|
563
|
+
"primaryKey": false,
|
|
564
|
+
"notNull": true,
|
|
565
|
+
"autoincrement": false
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
"indexes": {},
|
|
569
|
+
"foreignKeys": {},
|
|
570
|
+
"compositePrimaryKeys": {},
|
|
571
|
+
"uniqueConstraints": {},
|
|
572
|
+
"checkConstraints": {}
|
|
573
|
+
},
|
|
574
|
+
"user_channels": {
|
|
575
|
+
"name": "user_channels",
|
|
576
|
+
"columns": {
|
|
577
|
+
"id": {
|
|
578
|
+
"name": "id",
|
|
579
|
+
"type": "text",
|
|
580
|
+
"primaryKey": true,
|
|
581
|
+
"notNull": true,
|
|
582
|
+
"autoincrement": false
|
|
583
|
+
},
|
|
584
|
+
"user_id": {
|
|
585
|
+
"name": "user_id",
|
|
586
|
+
"type": "text",
|
|
587
|
+
"primaryKey": false,
|
|
588
|
+
"notNull": true,
|
|
589
|
+
"autoincrement": false
|
|
590
|
+
},
|
|
591
|
+
"channel": {
|
|
592
|
+
"name": "channel",
|
|
593
|
+
"type": "text",
|
|
594
|
+
"primaryKey": false,
|
|
595
|
+
"notNull": true,
|
|
596
|
+
"autoincrement": false
|
|
597
|
+
},
|
|
598
|
+
"channel_chat_id": {
|
|
599
|
+
"name": "channel_chat_id",
|
|
600
|
+
"type": "text",
|
|
601
|
+
"primaryKey": false,
|
|
602
|
+
"notNull": false,
|
|
603
|
+
"autoincrement": false
|
|
604
|
+
},
|
|
605
|
+
"code": {
|
|
606
|
+
"name": "code",
|
|
607
|
+
"type": "text",
|
|
608
|
+
"primaryKey": false,
|
|
609
|
+
"notNull": false,
|
|
610
|
+
"autoincrement": false
|
|
611
|
+
},
|
|
612
|
+
"code_expires_at": {
|
|
613
|
+
"name": "code_expires_at",
|
|
614
|
+
"type": "integer",
|
|
615
|
+
"primaryKey": false,
|
|
616
|
+
"notNull": false,
|
|
617
|
+
"autoincrement": false
|
|
618
|
+
},
|
|
619
|
+
"verified_at": {
|
|
620
|
+
"name": "verified_at",
|
|
621
|
+
"type": "integer",
|
|
622
|
+
"primaryKey": false,
|
|
623
|
+
"notNull": false,
|
|
624
|
+
"autoincrement": false
|
|
625
|
+
},
|
|
626
|
+
"active_thread_id": {
|
|
627
|
+
"name": "active_thread_id",
|
|
628
|
+
"type": "text",
|
|
629
|
+
"primaryKey": false,
|
|
630
|
+
"notNull": false,
|
|
631
|
+
"autoincrement": false
|
|
632
|
+
},
|
|
633
|
+
"created_at": {
|
|
634
|
+
"name": "created_at",
|
|
635
|
+
"type": "integer",
|
|
636
|
+
"primaryKey": false,
|
|
637
|
+
"notNull": true,
|
|
638
|
+
"autoincrement": false
|
|
639
|
+
},
|
|
640
|
+
"updated_at": {
|
|
641
|
+
"name": "updated_at",
|
|
642
|
+
"type": "integer",
|
|
643
|
+
"primaryKey": false,
|
|
644
|
+
"notNull": true,
|
|
645
|
+
"autoincrement": false
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
"indexes": {
|
|
649
|
+
"user_channels_user_channel_unique": {
|
|
650
|
+
"name": "user_channels_user_channel_unique",
|
|
651
|
+
"columns": [
|
|
652
|
+
"user_id",
|
|
653
|
+
"channel"
|
|
654
|
+
],
|
|
655
|
+
"isUnique": true
|
|
656
|
+
},
|
|
657
|
+
"user_channels_channel_chat_id_unique": {
|
|
658
|
+
"name": "user_channels_channel_chat_id_unique",
|
|
659
|
+
"columns": [
|
|
660
|
+
"channel",
|
|
661
|
+
"channel_chat_id"
|
|
662
|
+
],
|
|
663
|
+
"isUnique": true
|
|
664
|
+
},
|
|
665
|
+
"user_channels_code_lookup": {
|
|
666
|
+
"name": "user_channels_code_lookup",
|
|
667
|
+
"columns": [
|
|
668
|
+
"code"
|
|
669
|
+
],
|
|
670
|
+
"isUnique": false
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
"foreignKeys": {},
|
|
674
|
+
"compositePrimaryKeys": {},
|
|
675
|
+
"uniqueConstraints": {},
|
|
676
|
+
"checkConstraints": {}
|
|
677
|
+
},
|
|
678
|
+
"users": {
|
|
679
|
+
"name": "users",
|
|
680
|
+
"columns": {
|
|
681
|
+
"id": {
|
|
682
|
+
"name": "id",
|
|
683
|
+
"type": "text",
|
|
684
|
+
"primaryKey": true,
|
|
685
|
+
"notNull": true,
|
|
686
|
+
"autoincrement": false
|
|
687
|
+
},
|
|
688
|
+
"email": {
|
|
689
|
+
"name": "email",
|
|
690
|
+
"type": "text",
|
|
691
|
+
"primaryKey": false,
|
|
692
|
+
"notNull": true,
|
|
693
|
+
"autoincrement": false
|
|
694
|
+
},
|
|
695
|
+
"password_hash": {
|
|
696
|
+
"name": "password_hash",
|
|
697
|
+
"type": "text",
|
|
698
|
+
"primaryKey": false,
|
|
699
|
+
"notNull": true,
|
|
700
|
+
"autoincrement": false
|
|
701
|
+
},
|
|
702
|
+
"role": {
|
|
703
|
+
"name": "role",
|
|
704
|
+
"type": "text",
|
|
705
|
+
"primaryKey": false,
|
|
706
|
+
"notNull": true,
|
|
707
|
+
"autoincrement": false,
|
|
708
|
+
"default": "'admin'"
|
|
709
|
+
},
|
|
710
|
+
"created_at": {
|
|
711
|
+
"name": "created_at",
|
|
712
|
+
"type": "integer",
|
|
713
|
+
"primaryKey": false,
|
|
714
|
+
"notNull": true,
|
|
715
|
+
"autoincrement": false
|
|
716
|
+
},
|
|
717
|
+
"updated_at": {
|
|
718
|
+
"name": "updated_at",
|
|
719
|
+
"type": "integer",
|
|
720
|
+
"primaryKey": false,
|
|
721
|
+
"notNull": true,
|
|
722
|
+
"autoincrement": false
|
|
723
|
+
}
|
|
724
|
+
},
|
|
725
|
+
"indexes": {
|
|
726
|
+
"users_email_unique": {
|
|
727
|
+
"name": "users_email_unique",
|
|
728
|
+
"columns": [
|
|
729
|
+
"email"
|
|
730
|
+
],
|
|
731
|
+
"isUnique": true
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
"foreignKeys": {},
|
|
735
|
+
"compositePrimaryKeys": {},
|
|
736
|
+
"uniqueConstraints": {},
|
|
737
|
+
"checkConstraints": {}
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
"views": {},
|
|
741
|
+
"enums": {},
|
|
742
|
+
"_meta": {
|
|
743
|
+
"schemas": {},
|
|
744
|
+
"tables": {},
|
|
745
|
+
"columns": {}
|
|
746
|
+
},
|
|
747
|
+
"internal": {
|
|
748
|
+
"indexes": {}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
@@ -162,6 +162,13 @@
|
|
|
162
162
|
"when": 1776193069845,
|
|
163
163
|
"tag": "0022_organic_apocalypse",
|
|
164
164
|
"breakpoints": true
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"idx": 23,
|
|
168
|
+
"version": "6",
|
|
169
|
+
"when": 1776559857722,
|
|
170
|
+
"tag": "0023_needy_ender_wiggin",
|
|
171
|
+
"breakpoints": true
|
|
165
172
|
}
|
|
166
173
|
]
|
|
167
174
|
}
|
package/lib/CLAUDE.md
CHANGED
|
@@ -18,10 +18,10 @@ If the task needs to *think*, use `agent`. If it just needs to *do*, use `comman
|
|
|
18
18
|
|
|
19
19
|
## Cron Jobs
|
|
20
20
|
|
|
21
|
-
Defined in `agent-job/CRONS.json`, loaded by `lib/cron.js` at startup via `node-cron`. Each entry has `name`, `schedule` (cron expression), `type` (`agent`/`command`/`webhook`), and the corresponding action fields (`job`, `command`, or `url`/`method`/`headers`/`vars`). Set `enabled: false` to disable. Agent-type entries support optional `llm_provider` and `
|
|
21
|
+
Defined in `agent-job/CRONS.json`, loaded by `lib/cron.js` at startup via `node-cron`. Each entry has `name`, `schedule` (cron expression), `type` (`agent`/`command`/`webhook`), and the corresponding action fields (`job`, `command`, or `url`/`method`/`headers`/`vars`). Set `enabled: false` to disable. Agent-type entries support optional `llm_provider`, `llm_model`, and `scope` fields. `scope` sets the agent's working directory to a subdirectory (e.g., `"scope": "agents/gary-vee"`) — the system prompt and skills resolve from that scope.
|
|
22
22
|
|
|
23
23
|
## Webhook Triggers
|
|
24
24
|
|
|
25
|
-
Defined in `event-handler/TRIGGERS.json`, loaded by `lib/triggers.js`. Each trigger watches an endpoint path (`watch_path`) and fires an array of actions (fire-and-forget, after auth, before route handler). Actions use the same `type`/`job`/`command`/`url` fields as cron jobs, including optional `llm_provider`/`llm_model` overrides.
|
|
25
|
+
Defined in `event-handler/TRIGGERS.json`, loaded by `lib/triggers.js`. Each trigger watches an endpoint path (`watch_path`) and fires an array of actions (fire-and-forget, after auth, before route handler). Actions use the same `type`/`job`/`command`/`url` fields as cron jobs, including optional `llm_provider`/`llm_model`/`scope` overrides.
|
|
26
26
|
|
|
27
27
|
Template tokens in `job` and `command` strings: `{{body}}`, `{{body.field}}`, `{{query}}`, `{{query.field}}`, `{{headers}}`, `{{headers.field}}`.
|
package/lib/actions.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { exec } from 'child_process';
|
|
2
2
|
import { promisify } from 'util';
|
|
3
3
|
import { createAgentJob } from './tools/create-agent-job.js';
|
|
4
|
+
import { buildCodingAgentSystemPrompt } from './ai/system-prompt.js';
|
|
5
|
+
import { resolveAgentScope } from './ai/scope.js';
|
|
6
|
+
import { PROJECT_ROOT } from './paths.js';
|
|
4
7
|
|
|
5
8
|
const execAsync = promisify(exec);
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Execute a single action
|
|
9
|
-
* @param {Object} action - { type, job, command, url, method, headers, vars } (type: agent|command|webhook)
|
|
12
|
+
* @param {Object} action - { type, job, command, url, method, headers, vars, scope } (type: agent|command|webhook)
|
|
10
13
|
* @param {Object} opts - { cwd, data }
|
|
11
14
|
* @returns {Promise<string>} Result description for logging
|
|
12
15
|
*/
|
|
@@ -37,6 +40,11 @@ async function executeAction(action, opts = {}) {
|
|
|
37
40
|
const options = {};
|
|
38
41
|
if (action.llm_model) options.llmModel = action.llm_model;
|
|
39
42
|
if (action.agent_backend) options.agentBackend = action.agent_backend;
|
|
43
|
+
if (action.scope) {
|
|
44
|
+
options.scope = action.scope;
|
|
45
|
+
const { skillsDir } = resolveAgentScope(PROJECT_ROOT, action.scope);
|
|
46
|
+
options.systemPrompt = buildCodingAgentSystemPrompt('agent', skillsDir, action.scope);
|
|
47
|
+
}
|
|
40
48
|
const result = await createAgentJob(action.job, options);
|
|
41
49
|
return `agent-job ${result.agent_job_id} — ${result.title}`;
|
|
42
50
|
}
|
package/lib/ai/index.js
CHANGED
|
@@ -143,6 +143,7 @@ async function* chatStream(threadId, message, attachments = [], options = {}) {
|
|
|
143
143
|
const workspace = createCodeWorkspace(options.userId || 'unknown', {
|
|
144
144
|
repo: repo,
|
|
145
145
|
branch: branch,
|
|
146
|
+
scope: options.scope || null,
|
|
146
147
|
});
|
|
147
148
|
workspaceId = workspace.id;
|
|
148
149
|
const { generateRandomName } = await import('../utils/random-name.js');
|
|
@@ -215,6 +216,7 @@ async function* chatStream(threadId, message, attachments = [], options = {}) {
|
|
|
215
216
|
const systemPrompt = buildCodingAgentSystemPrompt(
|
|
216
217
|
chatMode,
|
|
217
218
|
chatMode === 'agent' ? skillsDir : null,
|
|
219
|
+
chatMode === 'agent' ? scope : null,
|
|
218
220
|
);
|
|
219
221
|
|
|
220
222
|
// 5. Stream from SDK adapter
|
|
@@ -31,16 +31,20 @@ function encodeCwd(absolutePath) {
|
|
|
31
31
|
* Ensure the interactive container can find SDK-created sessions on the shared volume.
|
|
32
32
|
*
|
|
33
33
|
* The SDK stores sessions at $HOME/.claude/projects/<encoded-cwd>/. The interactive
|
|
34
|
-
* container uses cwd=/home/coding-agent/workspace, but the SDK adapter uses
|
|
35
|
-
* cwd=/app/data/workspaces/workspace-XXX/workspace — different encoded paths.
|
|
34
|
+
* container uses cwd=/home/coding-agent/workspace[/scope], but the SDK adapter uses
|
|
35
|
+
* cwd=/app/data/workspaces/workspace-XXX/workspace[/scope] — different encoded paths.
|
|
36
36
|
*
|
|
37
37
|
* Creates a symlink so the interactive container's encoded path resolves to the
|
|
38
38
|
* SDK adapter's encoded path, both on the same volume.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} wsBaseDir - Volume root (e.g., /app/data/workspaces/workspace-XXX)
|
|
41
|
+
* @param {string} sdkCwd - SDK's working directory (scoped, e.g., .../workspace/agents/gary-vee)
|
|
42
|
+
* @param {string} interactiveCwd - Interactive container's equivalent cwd (e.g., /home/coding-agent/workspace/agents/gary-vee)
|
|
39
43
|
*/
|
|
40
|
-
function ensureSessionSymlink(wsBaseDir,
|
|
44
|
+
function ensureSessionSymlink(wsBaseDir, sdkCwd, interactiveCwd) {
|
|
41
45
|
const projectsDir = path.join(wsBaseDir, '.claude', 'projects');
|
|
42
|
-
const sdkEncoded = encodeCwd(
|
|
43
|
-
const interactiveEncoded = encodeCwd(
|
|
46
|
+
const sdkEncoded = encodeCwd(sdkCwd);
|
|
47
|
+
const interactiveEncoded = encodeCwd(interactiveCwd);
|
|
44
48
|
|
|
45
49
|
// Both point to the same dir — no symlink needed
|
|
46
50
|
if (sdkEncoded === interactiveEncoded) return;
|
|
@@ -65,10 +69,17 @@ function ensureSessionSymlink(wsBaseDir, workspaceDir) {
|
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
export async function* claudeCodeStream({ prompt, workspaceDir, systemPrompt, sessionId, permissionMode, attachments, workspaceId, chatMode }) {
|
|
68
|
-
// Point HOME at the workspace volume
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
// Point HOME at the workspace volume root (always one level above 'workspace/').
|
|
73
|
+
// workspaceDir may be scoped (e.g., .../workspace/agents/gary-vee), so we can't
|
|
74
|
+
// use path.dirname — derive from workspaceId instead.
|
|
75
|
+
const { workspaceDir: getWsDir } = await import('../../tools/docker.js');
|
|
76
|
+
const wsBaseDir = getWsDir(workspaceId);
|
|
77
|
+
|
|
78
|
+
// Build the interactive container's equivalent cwd for session symlink
|
|
79
|
+
const repoRoot = path.join(wsBaseDir, 'workspace');
|
|
80
|
+
const scope = workspaceDir.startsWith(repoRoot) ? workspaceDir.slice(repoRoot.length) : '';
|
|
81
|
+
const interactiveCwd = '/home/coding-agent/workspace' + scope;
|
|
82
|
+
ensureSessionSymlink(wsBaseDir, workspaceDir, interactiveCwd);
|
|
72
83
|
|
|
73
84
|
// Build a local env object with auth credentials from the settings DB.
|
|
74
85
|
// Passed via the SDK's `env` option — no process.env mutation needed.
|
package/lib/ai/system-prompt.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
2
3
|
import { PROJECT_ROOT } from '../paths.js';
|
|
3
4
|
import { render_md } from '../utils/render-md.js';
|
|
4
5
|
|
|
@@ -6,14 +7,28 @@ import { render_md } from '../utils/render-md.js';
|
|
|
6
7
|
* Build the system prompt for a coding agent.
|
|
7
8
|
* @param {'agent'|'code'} mode - Chat mode
|
|
8
9
|
* @param {string|null} [skillsDir] - Skills directory for {{skills}} resolution.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* @param {string|null} [scope] - Agent scope (e.g., 'agents/gary-vee'). When set,
|
|
11
|
+
* looks for SYSTEM.md in the scoped directory first, falls back to agent-job/SYSTEM.md.
|
|
11
12
|
* @returns {string|null} Rendered system prompt, or null if not configured
|
|
12
13
|
*/
|
|
13
|
-
export function buildCodingAgentSystemPrompt(mode, skillsDir) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
export function buildCodingAgentSystemPrompt(mode, skillsDir, scope) {
|
|
15
|
+
let file;
|
|
16
|
+
|
|
17
|
+
if (mode === 'agent') {
|
|
18
|
+
// Check scoped SYSTEM.md first, fall back to agent-job/SYSTEM.md
|
|
19
|
+
if (scope) {
|
|
20
|
+
const scopedFile = path.join(PROJECT_ROOT, scope, 'SYSTEM.md');
|
|
21
|
+
if (existsSync(scopedFile)) {
|
|
22
|
+
file = scopedFile;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!file) {
|
|
26
|
+
file = path.join(PROJECT_ROOT, 'agent-job/SYSTEM.md');
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
file = path.join(PROJECT_ROOT, 'coding-workspace/SYSTEM.md');
|
|
30
|
+
}
|
|
31
|
+
|
|
17
32
|
const rendered = render_md(file, { skillsDir });
|
|
18
33
|
return rendered?.trim() || null;
|
|
19
34
|
}
|
package/lib/ai/tools.js
CHANGED
|
@@ -8,8 +8,15 @@ import { buildCodingAgentSystemPrompt } from './system-prompt.js';
|
|
|
8
8
|
import { resolveAgentScope } from './scope.js';
|
|
9
9
|
|
|
10
10
|
const agentJobTool = tool(
|
|
11
|
-
async ({ prompt }) => {
|
|
12
|
-
const
|
|
11
|
+
async ({ prompt }, runtime) => {
|
|
12
|
+
const scope = runtime?.configurable?.scope || null;
|
|
13
|
+
|
|
14
|
+
// Pre-render system prompt with scoped skills (same as all other entry points)
|
|
15
|
+
const { PROJECT_ROOT } = await import('../paths.js');
|
|
16
|
+
const { skillsDir } = resolveAgentScope(PROJECT_ROOT, scope);
|
|
17
|
+
const systemPrompt = buildCodingAgentSystemPrompt('agent', skillsDir, scope);
|
|
18
|
+
|
|
19
|
+
const result = await createAgentJob(prompt, { scope, systemPrompt });
|
|
13
20
|
return JSON.stringify({
|
|
14
21
|
success: true,
|
|
15
22
|
agent_job_id: result.agent_job_id,
|
|
@@ -72,7 +79,7 @@ const agentChatCodingTool = tool(
|
|
|
72
79
|
workspaceId,
|
|
73
80
|
taskPrompt: prompt,
|
|
74
81
|
mode,
|
|
75
|
-
systemPrompt: buildCodingAgentSystemPrompt('agent', skillsDir),
|
|
82
|
+
systemPrompt: buildCodingAgentSystemPrompt('agent', skillsDir, scope),
|
|
76
83
|
injectSecrets: true,
|
|
77
84
|
scope: scope || undefined,
|
|
78
85
|
});
|
package/lib/code/actions.js
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
touchChatUpdatedAt,
|
|
17
17
|
} from '../db/chats.js';
|
|
18
18
|
import { buildCodingAgentSystemPrompt } from '../ai/system-prompt.js';
|
|
19
|
+
import { resolveAgentScope } from '../ai/scope.js';
|
|
20
|
+
import { workspaceDir as getWorkspaceDir } from '../tools/docker.js';
|
|
19
21
|
import {
|
|
20
22
|
addSession,
|
|
21
23
|
getSession as getTermSession,
|
|
@@ -138,8 +140,15 @@ export async function ensureCodeWorkspaceContainer(id) {
|
|
|
138
140
|
|
|
139
141
|
// Inject agent job secrets when the linked chat is in agent mode
|
|
140
142
|
const chat = getChatByWorkspaceId(id);
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
+
const isAgent = chat?.chatMode === 'agent';
|
|
144
|
+
const injectSecrets = isAgent;
|
|
145
|
+
|
|
146
|
+
// Resolve scope for system prompt skills
|
|
147
|
+
const wsBaseDir = getWorkspaceDir(id);
|
|
148
|
+
const repoDir = (await import('path')).join(wsBaseDir, 'workspace');
|
|
149
|
+
const wsScope = workspace.scope || null;
|
|
150
|
+
const { skillsDir } = resolveAgentScope(repoDir, wsScope);
|
|
151
|
+
const systemPrompt = buildCodingAgentSystemPrompt(isAgent ? 'agent' : 'code', isAgent ? skillsDir : null, isAgent ? wsScope : null);
|
|
143
152
|
|
|
144
153
|
try {
|
|
145
154
|
const { inspectContainer, startContainer, removeContainer, runInteractiveContainer } =
|
|
@@ -158,6 +167,7 @@ export async function ensureCodeWorkspaceContainer(id) {
|
|
|
158
167
|
workspaceId: id,
|
|
159
168
|
injectSecrets,
|
|
160
169
|
systemPrompt,
|
|
170
|
+
scope: workspace.scope || undefined,
|
|
161
171
|
});
|
|
162
172
|
return { status: 'created' };
|
|
163
173
|
}
|
|
@@ -188,6 +198,7 @@ export async function ensureCodeWorkspaceContainer(id) {
|
|
|
188
198
|
workspaceId: id,
|
|
189
199
|
injectSecrets,
|
|
190
200
|
systemPrompt,
|
|
201
|
+
scope: workspace.scope || undefined,
|
|
191
202
|
});
|
|
192
203
|
return { status: 'created' };
|
|
193
204
|
} catch (err) {
|
|
@@ -214,8 +225,15 @@ export async function startInteractiveMode(id, agentOverride) {
|
|
|
214
225
|
|
|
215
226
|
// Inject agent job secrets when the linked chat is in agent mode
|
|
216
227
|
const chat = getChatByWorkspaceId(id);
|
|
217
|
-
const
|
|
218
|
-
const
|
|
228
|
+
const isAgent = chat?.chatMode === 'agent';
|
|
229
|
+
const injectSecrets = isAgent;
|
|
230
|
+
|
|
231
|
+
// Resolve scope for system prompt skills
|
|
232
|
+
const wsBase = getWorkspaceDir(id);
|
|
233
|
+
const repoPath = (await import('path')).join(wsBase, 'workspace');
|
|
234
|
+
const wsScope2 = workspace.scope || null;
|
|
235
|
+
const { skillsDir: scopedSkillsDir } = resolveAgentScope(repoPath, wsScope2);
|
|
236
|
+
const systemPrompt = buildCodingAgentSystemPrompt(isAgent ? 'agent' : 'code', isAgent ? scopedSkillsDir : null, isAgent ? wsScope2 : null);
|
|
219
237
|
|
|
220
238
|
try {
|
|
221
239
|
const { getConfig } = await import('../config.js');
|
|
@@ -234,6 +252,7 @@ export async function startInteractiveMode(id, agentOverride) {
|
|
|
234
252
|
workspaceId: id,
|
|
235
253
|
injectSecrets,
|
|
236
254
|
systemPrompt,
|
|
255
|
+
scope: workspace.scope || undefined,
|
|
237
256
|
});
|
|
238
257
|
|
|
239
258
|
// Persist both the container name and the agent so recovery can use the same image
|
|
@@ -14,7 +14,7 @@ import { codeWorkspaces } from './schema.js';
|
|
|
14
14
|
* @param {string} [options.id] - Optional ID (UUID). Generated if not provided.
|
|
15
15
|
* @returns {object} The created workspace
|
|
16
16
|
*/
|
|
17
|
-
export function createCodeWorkspace(userId, { containerName = null, repo = null, branch = null, title = 'Code Workspace', id = null } = {}) {
|
|
17
|
+
export function createCodeWorkspace(userId, { containerName = null, repo = null, branch = null, scope = null, title = 'Code Workspace', id = null } = {}) {
|
|
18
18
|
const db = getDb();
|
|
19
19
|
const now = Date.now();
|
|
20
20
|
const workspace = {
|
|
@@ -23,6 +23,7 @@ export function createCodeWorkspace(userId, { containerName = null, repo = null,
|
|
|
23
23
|
containerName,
|
|
24
24
|
repo,
|
|
25
25
|
branch,
|
|
26
|
+
scope,
|
|
26
27
|
title,
|
|
27
28
|
createdAt: now,
|
|
28
29
|
updatedAt: now,
|
package/lib/db/schema.js
CHANGED
|
@@ -53,6 +53,7 @@ export const codeWorkspaces = sqliteTable('code_workspaces', {
|
|
|
53
53
|
title: text('title').notNull().default('Code Workspace'),
|
|
54
54
|
lastInteractiveCommit: text('last_interactive_commit'),
|
|
55
55
|
codingAgent: text('coding_agent'),
|
|
56
|
+
scope: text('scope'),
|
|
56
57
|
starred: integer('starred').notNull().default(0),
|
|
57
58
|
hasChanges: integer('has_changes').notNull().default(0),
|
|
58
59
|
createdAt: integer('created_at').notNull(),
|
|
@@ -51,6 +51,8 @@ async function createAgentJob(agentJobDescription, options = {}) {
|
|
|
51
51
|
const config = { title, job: agentJobDescription };
|
|
52
52
|
if (options.llmModel) config.llm_model = options.llmModel;
|
|
53
53
|
if (options.agentBackend) config.agent_backend = options.agentBackend;
|
|
54
|
+
if (options.scope) config.scope = options.scope;
|
|
55
|
+
if (options.systemPrompt) config.system_prompt = options.systemPrompt;
|
|
54
56
|
|
|
55
57
|
const treeEntries = [
|
|
56
58
|
{
|
|
@@ -99,6 +101,7 @@ async function createAgentJob(agentJobDescription, options = {}) {
|
|
|
99
101
|
description: agentJobDescription,
|
|
100
102
|
codingAgent: options.agentBackend,
|
|
101
103
|
llmModel: options.llmModel,
|
|
104
|
+
scope: options.scope,
|
|
102
105
|
}).catch(err => {
|
|
103
106
|
console.error(`[agent-job] Failed to launch container for ${agentJobId}:`, err.message);
|
|
104
107
|
});
|
package/lib/tools/docker.js
CHANGED
|
@@ -880,7 +880,7 @@ async function removeVolume(name) {
|
|
|
880
880
|
* @param {string} [options.llmModel] - Model override
|
|
881
881
|
* @returns {Promise<{containerId: string, containerName: string, volumeName: string}>}
|
|
882
882
|
*/
|
|
883
|
-
async function runAgentJobContainer({ agentJobId, repo, branch, title, description, codingAgent, llmModel }) {
|
|
883
|
+
async function runAgentJobContainer({ agentJobId, repo, branch, title, description, codingAgent, llmModel, scope }) {
|
|
884
884
|
const agent = codingAgent || getConfig('CODING_AGENT') || 'claude-code';
|
|
885
885
|
const version = process.env.THEPOPEBOT_VERSION;
|
|
886
886
|
const image = `stephengpope/thepopebot:coding-agent-${agent}-${version}`;
|
|
@@ -904,6 +904,9 @@ async function runAgentJobContainer({ agentJobId, repo, branch, title, descripti
|
|
|
904
904
|
if (llmModel) {
|
|
905
905
|
env.push(`LLM_MODEL=${llmModel}`);
|
|
906
906
|
}
|
|
907
|
+
if (scope) {
|
|
908
|
+
env.push(`SCOPE=${scope}`);
|
|
909
|
+
}
|
|
907
910
|
|
|
908
911
|
// Auth env vars based on agent type
|
|
909
912
|
const { env: authEnv, backendApi } = buildAgentAuthEnv(agent);
|
package/package.json
CHANGED
|
@@ -31,6 +31,29 @@ Some files are auto-synced by `npx thepopebot init` and will be overwritten on u
|
|
|
31
31
|
- `event-handler/CLAUDE.md`
|
|
32
32
|
- `skills/CLAUDE.md`
|
|
33
33
|
|
|
34
|
+
## Agent Scoping
|
|
35
|
+
|
|
36
|
+
Agents can be scoped to subdirectories within the repository. When a chat is launched with a scope (e.g., `agents/gary-vee`), the coding agent runs with that directory as its working directory.
|
|
37
|
+
|
|
38
|
+
### Directory Structure
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
agents/
|
|
42
|
+
gary-vee/
|
|
43
|
+
CLAUDE.md ← agent-specific context (optional)
|
|
44
|
+
SYSTEM.md ← agent-specific system prompt (optional)
|
|
45
|
+
skills/ ← agent-specific skills (optional, overrides root skills/)
|
|
46
|
+
agent-job-secrets → ../../../skills/agent-job-secrets (symlink)
|
|
47
|
+
custom-skill/
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### How Scoping Works
|
|
51
|
+
|
|
52
|
+
- **Working directory** — The agent's cwd is set to the scoped directory. It still has access to the full repo.
|
|
53
|
+
- **Skills** — If the scoped directory has a `skills/` folder, those skills are used. If not, the root `skills/` folder is used as a fallback. Sub-agent skills can symlink back to root skills they need.
|
|
54
|
+
- **CLAUDE.md** — The coding agent automatically picks up `.claude/` and `CLAUDE.md` files relative to its working directory.
|
|
55
|
+
- **Default scope** — When no scope is selected, the agent runs from the repository root with root-level skills.
|
|
56
|
+
|
|
34
57
|
## Agents
|
|
35
58
|
|
|
36
59
|
(No agents configured yet.)
|
|
@@ -35,9 +35,16 @@ Everything in the workspace is automatically committed and pushed when your job
|
|
|
35
35
|
- `docker-compose.yml`, `.dockerignore`, `.gitignore` — Managed infrastructure files
|
|
36
36
|
- `.env` — Environment secrets
|
|
37
37
|
|
|
38
|
+
## Agent Scoping
|
|
39
|
+
|
|
40
|
+
Agents can be scoped to subdirectories under `agents/`. When scoped, the agent's working directory is set to that subdirectory (e.g., `agents/gary-vee/`). The full repo is still accessible.
|
|
41
|
+
|
|
42
|
+
- **Skills fallback** — If the scoped directory has a `skills/` folder, those are used. Otherwise, the root `skills/` folder applies. Sub-agents can symlink individual skills from root: `skills/agent-job-secrets → ../../../skills/agent-job-secrets`.
|
|
43
|
+
- **No skills folder needed** — If you don't create a `skills/` directory in the agent scope, it inherits all root skills automatically.
|
|
44
|
+
|
|
38
45
|
## Self-Modification
|
|
39
46
|
|
|
40
|
-
**Add an agent** — Create `agents/<name>/` with
|
|
47
|
+
**Add an agent** — Create `agents/<name>/` with an optional `CLAUDE.md`, `SYSTEM.md`, and `skills/` directory. Add a cron entry in `agent-job/CRONS.json` if it runs on a schedule. Update `agents/CLAUDE.md` and root `CLAUDE.md`.
|
|
41
48
|
|
|
42
49
|
**Remove an agent** — Delete the `agents/<name>/` folder, remove its cron entries, update `agents/CLAUDE.md` and root `CLAUDE.md`.
|
|
43
50
|
|