@waishnav/devspace 1.0.2 → 1.0.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.
Files changed (115) hide show
  1. package/README.md +43 -7
  2. package/dist/apply-patch.js +402 -0
  3. package/dist/cli.js +256 -4
  4. package/dist/config.js +17 -22
  5. package/dist/db/migrations.js +34 -0
  6. package/dist/db/schema.js +19 -0
  7. package/dist/local-agent-adapters.js +653 -0
  8. package/dist/local-agent-availability.js +119 -0
  9. package/dist/local-agent-path.js +24 -0
  10. package/dist/local-agent-profiles.js +135 -0
  11. package/dist/local-agent-runtime.js +48 -0
  12. package/dist/local-agent-store.js +145 -0
  13. package/dist/local-agent-targets.js +80 -0
  14. package/dist/process-platform.js +49 -0
  15. package/dist/process-sessions.js +337 -0
  16. package/dist/roots.js +5 -2
  17. package/dist/server.js +521 -243
  18. package/dist/skills.js +48 -4
  19. package/dist/ui/.vite/manifest.json +281 -281
  20. package/dist/ui/assets/{angular-html-Dj4LysSE.js → angular-html-CKeFzaPY.js} +1 -1
  21. package/dist/ui/assets/{angular-ts-CxuC_FFh.js → angular-ts-0wcM_Zxf.js} +1 -1
  22. package/dist/ui/assets/{apl-RwuvNFFo.js → apl-C7UdkNb2.js} +1 -1
  23. package/dist/ui/assets/{astro-ncoaUbsO.js → astro-DMqju4MJ.js} +1 -1
  24. package/dist/ui/assets/{blade-Dw8-Lzux.js → blade-DHanNgN5.js} +1 -1
  25. package/dist/ui/assets/{c-Dpbbo_f2.js → c-_iPKYPcx.js} +1 -1
  26. package/dist/ui/assets/{cobol-4ddX3THy.js → cobol-CagdLJsE.js} +1 -1
  27. package/dist/ui/assets/{coffee-Bn-a5KqK.js → coffee-CkNGriPi.js} +1 -1
  28. package/dist/ui/assets/{cpp-DoRrlM_T.js → cpp-CqmKOWIQ.js} +1 -1
  29. package/dist/ui/assets/{crystal-DiZfCxn-.js → crystal-DbAbDEO1.js} +1 -1
  30. package/dist/ui/assets/{css-DiQoSjDH.js → css-xwljL6Kw.js} +1 -1
  31. package/dist/ui/assets/{edge-wEhCUoEt.js → edge-CX3sYz15.js} +1 -1
  32. package/dist/ui/assets/{elixir-C3obhm0H.js → elixir-B3qL9hqd.js} +1 -1
  33. package/dist/ui/assets/{elm-DyTBfh1L.js → elm-CH4vrjBp.js} +1 -1
  34. package/dist/ui/assets/{erb-f4TnXRuY.js → erb-CupHqynN.js} +1 -1
  35. package/dist/ui/assets/{git-rebase-vhZbWNfp.js → git-rebase-CDxsU2LC.js} +1 -1
  36. package/dist/ui/assets/{glimmer-js-BG7puL6Y.js → glimmer-js-emkga3g1.js} +1 -1
  37. package/dist/ui/assets/{glimmer-ts-CNCfLLL1.js → glimmer-ts-D8kQDnUw.js} +1 -1
  38. package/dist/ui/assets/{glsl-DhT76AL5.js → glsl-BtfTaPhi.js} +1 -1
  39. package/dist/ui/assets/{graphql-CJX08w8y.js → graphql-DEus8iSO.js} +1 -1
  40. package/dist/ui/assets/{hack-DY4NGCCL.js → hack-DI8qpBqE.js} +1 -1
  41. package/dist/ui/assets/{haml-DwSkmVcG.js → haml-Br1i1gmi.js} +1 -1
  42. package/dist/ui/assets/{handlebars-CQ_1dEmi.js → handlebars-Ce23mu9x.js} +1 -1
  43. package/dist/ui/assets/{heavy-payload-2rGBiitA.js → heavy-payload-BcZ_nGfF.js} +1 -1
  44. package/dist/ui/assets/{html-B_e3Njif.js → html-RXVmfixf.js} +1 -1
  45. package/dist/ui/assets/{html-derivative-_nHZ_Bk-.js → html-derivative-D1aqeobb.js} +1 -1
  46. package/dist/ui/assets/{http-CfcvPIru.js → http-BebG00CE.js} +1 -1
  47. package/dist/ui/assets/{hurl-9vWDQIMO.js → hurl-kHMuvenL.js} +1 -1
  48. package/dist/ui/assets/{java-C4S5r6L5.js → java-DYc2V5RJ.js} +1 -1
  49. package/dist/ui/assets/{javascript-BRwqbMMC.js → javascript-BcfzTzr3.js} +1 -1
  50. package/dist/ui/assets/{jinja-DHxzMMc5.js → jinja-D2RhihUZ.js} +1 -1
  51. package/dist/ui/assets/{jison-B1DqeWXb.js → jison-BuoFEePN.js} +1 -1
  52. package/dist/ui/assets/{json-DcFTkEdd.js → json-BIDkCuJA.js} +1 -1
  53. package/dist/ui/assets/{jsx-DgfmYa9f.js → jsx-DHCL2HA0.js} +1 -1
  54. package/dist/ui/assets/{julia-3M8SJ8iF.js → julia-CvUQXscF.js} +1 -1
  55. package/dist/ui/assets/{just-UyH7_DNz.js → just-etc9R2qw.js} +1 -1
  56. package/dist/ui/assets/{latex-wUbha6EX.js → latex-BB3zRnxC.js} +1 -1
  57. package/dist/ui/assets/{liquid-DqNTB9kN.js → liquid-CIi5Mvpv.js} +1 -1
  58. package/dist/ui/assets/{lua-DrEQ1QTW.js → lua-Cox7XDiW.js} +1 -1
  59. package/dist/ui/assets/{marko-B3GJw9Md.js → marko-ruBIRV0n.js} +1 -1
  60. package/dist/ui/assets/{mdc-BnThk82m.js → mdc-CcbvB9U5.js} +1 -1
  61. package/dist/ui/assets/{nginx-B5IeSsOX.js → nginx-DtWz6ZYE.js} +1 -1
  62. package/dist/ui/assets/{nim-BJraatnS.js → nim-BV5BR4P7.js} +1 -1
  63. package/dist/ui/assets/{perl-BAhrR4gt.js → perl-BvzoSaJ4.js} +1 -1
  64. package/dist/ui/assets/{php-BKRCk_wX.js → php-C-clrAOy.js} +1 -1
  65. package/dist/ui/assets/{pug-B8gmSV7z.js → pug-E07_GXAr.js} +1 -1
  66. package/dist/ui/assets/{qml-DKrxIjHR.js → qml-dqt33dti.js} +1 -1
  67. package/dist/ui/assets/{r-YASBZvTH.js → r-BcVQxsAw.js} +1 -1
  68. package/dist/ui/assets/{razor-P-XFZqh6.js → razor-BW5T_UZ-.js} +1 -1
  69. package/dist/ui/assets/{regexp-CCpiH6nO.js → regexp-Ed_DEsuU.js} +1 -1
  70. package/dist/ui/assets/{review-payload-BLYwg6t8.js → review-payload-Dsmx33pY.js} +1 -1
  71. package/dist/ui/assets/{rst-CFAzckbn.js → rst-BkcGqLs8.js} +1 -1
  72. package/dist/ui/assets/{ruby-Dyp_Rx-S.js → ruby-Sf_X_YID.js} +1 -1
  73. package/dist/ui/assets/{sas-YkWZIXxT.js → sas-COsombsA.js} +1 -1
  74. package/dist/ui/assets/{scss-Cf89idGl.js → scss-K6Ylfa26.js} +1 -1
  75. package/dist/ui/assets/{shellscript-BFMb52q8.js → shellscript-D2EdqkgV.js} +1 -1
  76. package/dist/ui/assets/{shellsession-DuthW-5v.js → shellsession-jRXgXrnI.js} +1 -1
  77. package/dist/ui/assets/{soy-BohVVlJU.js → soy-BGspKQWW.js} +1 -1
  78. package/dist/ui/assets/{sql-CnK9R2Gt.js → sql-C4lAmYnM.js} +1 -1
  79. package/dist/ui/assets/{stata-D-sV4Vbr.js → stata-DQWvRqxp.js} +1 -1
  80. package/dist/ui/assets/{surrealql-ChAy0gHv.js → surrealql-OmKt9XJ_.js} +1 -1
  81. package/dist/ui/assets/{svelte-3HBVqG-H.js → svelte-D0rUmwDI.js} +1 -1
  82. package/dist/ui/assets/{templ-BixlV4Cp.js → templ-B-U0RMc0.js} +1 -1
  83. package/dist/ui/assets/{tex-DWoJKHvD.js → tex-BIHD-btS.js} +1 -1
  84. package/dist/ui/assets/{ts-tags-DLA8eH13.js → ts-tags-CGuDWfPC.js} +1 -1
  85. package/dist/ui/assets/{tsx-BYWa5JUz.js → tsx-B0-h_HlD.js} +1 -1
  86. package/dist/ui/assets/{twig-CnY65vAo.js → twig-BN2qDJWN.js} +1 -1
  87. package/dist/ui/assets/{typescript-ByInUsro.js → typescript-BRnwPzY3.js} +1 -1
  88. package/dist/ui/assets/{useFileDiffInstance-Dg0pWcJV.js → useFileDiffInstance-5ivdkUYO.js} +3 -3
  89. package/dist/ui/assets/{vue-Qngt2rkz.js → vue-DekNU1qt.js} +1 -1
  90. package/dist/ui/assets/{vue-html-DDue9upr.js → vue-html-Bjlv-uvH.js} +1 -1
  91. package/dist/ui/assets/{vue-vine-DBcAKsA6.js → vue-vine-C2Doytjq.js} +1 -1
  92. package/dist/ui/assets/{workspace-app-DGD9R89D.css → workspace-app-DASlX26z.css} +1 -1
  93. package/dist/ui/assets/{workspace-app-DuzpU4Hf.js → workspace-app-DYi7VJam.js} +5 -5
  94. package/dist/ui/assets/{xml-CRLR5jiL.js → xml-pXbxxgI3.js} +1 -1
  95. package/dist/ui/assets/{xsl-CmcwyXbm.js → xsl-BEAlgDI2.js} +1 -1
  96. package/dist/ui/assets/{yaml-Dpe6Eg-k.js → yaml-C1O-To71.js} +1 -1
  97. package/dist/ui/workspace-app.html +2 -2
  98. package/dist/user-config.js +21 -1
  99. package/dist/workspaces.js +3 -0
  100. package/docs/agent-profile-schema.md +165 -0
  101. package/docs/chatgpt-coding-workflow.md +46 -12
  102. package/docs/configuration.md +47 -12
  103. package/docs/gotchas.md +24 -5
  104. package/docs/setup.md +1 -1
  105. package/examples/agents/claude-implementer.md +30 -0
  106. package/examples/agents/codex-explorer.md +27 -0
  107. package/examples/agents/codex-qa-tester.md +29 -0
  108. package/examples/agents/codex-worker.md +25 -0
  109. package/examples/agents/copilot-reviewer.md +25 -0
  110. package/examples/agents/cursor-agent-worker.md +26 -0
  111. package/examples/agents/opencode-explorer.md +27 -0
  112. package/examples/agents/pi-reviewer.md +27 -0
  113. package/package.json +18 -5
  114. package/scripts/fix-node-pty-permissions.mjs +22 -0
  115. package/skills/subagent-delegation/SKILL.md +132 -0
package/dist/cli.js CHANGED
@@ -1,12 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from "node:module";
3
3
  import { stdin as input, stdout as output } from "node:process";
4
- import { resolve } from "node:path";
4
+ import { spawn } from "node:child_process";
5
+ import { mkdtempSync, writeFileSync } from "node:fs";
6
+ import { readFile } from "node:fs/promises";
7
+ import { tmpdir } from "node:os";
8
+ import { join, resolve } from "node:path";
9
+ import { fileURLToPath } from "node:url";
5
10
  import * as prompts from "@clack/prompts";
6
11
  import { getShellConfig } from "@earendil-works/pi-coding-agent";
7
12
  import { satisfies } from "semver";
8
13
  import { loadConfig } from "./config.js";
9
- import { generateOwnerToken, loadDevspaceFiles, writeDevspaceAuth, writeDevspaceConfig, } from "./user-config.js";
14
+ import { runLocalAgentProvider } from "./local-agent-adapters.js";
15
+ import { isLocalAgentProvider, loadLocalAgentProfiles, } from "./local-agent-profiles.js";
16
+ import { assertLocalAgentProviderAvailable, formatLocalAgentProviderAvailabilitySummary, } from "./local-agent-availability.js";
17
+ import { formatAvailableLocalAgentTargets, parseLocalAgentRunArgs, resolveLocalAgentTarget, } from "./local-agent-targets.js";
18
+ import { createLocalAgentStore } from "./local-agent-store.js";
19
+ import { ensureDevspaceDefaultSkills, generateOwnerToken, loadDevspaceFiles, resolveSubagentsFlag, writeDevspaceAuth, writeDevspaceConfig, } from "./user-config.js";
10
20
  import { expandHomePath } from "./roots.js";
11
21
  const require = createRequire(import.meta.url);
12
22
  const SUPPORTED_NODE_RANGE = ">=20.12 <27";
@@ -28,18 +38,26 @@ async function main(argv) {
28
38
  case "config":
29
39
  runConfigCommand(args);
30
40
  return;
41
+ case "agents":
42
+ await runAgentsCommand(args);
43
+ return;
31
44
  case "help":
32
45
  printHelp();
33
46
  return;
47
+ case "version":
48
+ printVersion();
49
+ return;
34
50
  }
35
51
  }
36
52
  function normalizeCommand(command) {
37
53
  if (!command || command === "serve" || command === "start")
38
54
  return "serve";
39
- if (command === "init" || command === "doctor" || command === "config")
55
+ if (command === "init" || command === "doctor" || command === "config" || command === "agents")
40
56
  return command;
41
57
  if (command === "help" || command === "--help" || command === "-h")
42
58
  return "help";
59
+ if (command === "version" || command === "--version" || command === "-v")
60
+ return "version";
43
61
  throw new Error(`Unknown command: ${command}`);
44
62
  }
45
63
  async function ensureConfigured() {
@@ -108,15 +126,18 @@ async function runInit({ force }) {
108
126
  port,
109
127
  allowedRoots,
110
128
  publicBaseUrl,
129
+ subagents: resolveSubagentsFlag(files.config),
111
130
  };
112
131
  const auth = {
113
132
  ownerToken: files.auth.ownerToken ?? generateOwnerToken(),
114
133
  };
115
134
  const configPath = writeDevspaceConfig(config);
116
135
  const authPath = writeDevspaceAuth(auth);
136
+ const seededSkillPaths = config.subagents ? ensureDevspaceDefaultSkills() : [];
117
137
  const lines = [
118
138
  `Config: ${configPath}`,
119
139
  `Auth: ${authPath}`,
140
+ ...seededSkillPaths.map((path) => `Default skill: ${path}`),
120
141
  `Local MCP URL: http://${config.host}:${config.port}/mcp`,
121
142
  ...(publicBaseUrl ? [`Public MCP URL: ${publicBaseUrl}/mcp`] : []),
122
143
  ];
@@ -149,7 +170,7 @@ async function serve() {
149
170
  }
150
171
  const { createServer } = await import("./server.js");
151
172
  const config = loadConfig();
152
- const { app, close } = createServer(config);
173
+ const { app, close, localAgentProviders } = createServer(config);
153
174
  const httpServer = app.listen(config.port, config.host, () => {
154
175
  console.log(`devspace listening on http://${config.host}:${config.port}/mcp`);
155
176
  console.log(`public base url: ${config.publicBaseUrl}`);
@@ -160,6 +181,9 @@ async function serve() {
160
181
  }
161
182
  console.log("auth: Owner password approval required");
162
183
  console.log(`logging: ${config.logging.level} ${config.logging.format}`);
184
+ if (config.subagents) {
185
+ console.log(`subagent providers: ${formatLocalAgentProviderAvailabilitySummary(localAgentProviders)}`);
186
+ }
163
187
  });
164
188
  const shutdown = () => {
165
189
  httpServer.close(() => {
@@ -226,11 +250,239 @@ function printHelp() {
226
250
  " devspace doctor Show config, runtime, and native dependency status",
227
251
  " devspace config get Print persisted config",
228
252
  " devspace config set publicBaseUrl <url|null>",
253
+ " devspace agents ls List subagent sessions",
254
+ " devspace agents run <profile-or-provider-or-id> [--model <model>] <prompt>",
255
+ " devspace agents show <id>",
256
+ " devspace -v, --version Print the installed version",
229
257
  "",
230
258
  "For temporary tunnels:",
231
259
  " DEVSPACE_PUBLIC_BASE_URL=https://example.trycloudflare.com devspace serve",
232
260
  ].join("\n"));
233
261
  }
262
+ async function runAgentsCommand(args) {
263
+ const [subcommand, ...rest] = args;
264
+ switch (subcommand) {
265
+ case "ls":
266
+ case "list":
267
+ await runAgentsList();
268
+ return;
269
+ case "run":
270
+ await runAgentsRun(rest);
271
+ return;
272
+ case "show":
273
+ await runAgentsShow(rest);
274
+ return;
275
+ case "__worker":
276
+ await runAgentsWorker(rest);
277
+ return;
278
+ case undefined:
279
+ case "help":
280
+ case "--help":
281
+ case "-h":
282
+ printAgentsHelp();
283
+ return;
284
+ default:
285
+ throw new Error(`Unknown agents command: ${subcommand}`);
286
+ }
287
+ }
288
+ async function runAgentsList() {
289
+ const config = loadConfig();
290
+ const store = createLocalAgentStore(config);
291
+ const agents = store.list(resolveCurrentWorkspaceScope());
292
+ if (agents.length === 0) {
293
+ console.log("No subagent sessions found for this workspace.");
294
+ return;
295
+ }
296
+ for (const agent of agents) {
297
+ console.log(formatAgentLine(agent));
298
+ }
299
+ }
300
+ async function runAgentsRun(args) {
301
+ const parsed = parseLocalAgentRunArgs(args);
302
+ const config = loadConfig();
303
+ const workspaceRoot = resolveCurrentWorkspaceRoot();
304
+ const store = createLocalAgentStore(config);
305
+ const existing = store.get(parsed.target);
306
+ if (existing) {
307
+ if (!isLocalAgentProvider(existing.provider)) {
308
+ throw new Error(`Unknown subagent provider for existing session: ${existing.provider}`);
309
+ }
310
+ assertLocalAgentProviderAvailable(existing.provider);
311
+ const promptFile = writeAgentPromptFile(parsed.prompt);
312
+ store.update(existing.id, {
313
+ status: "starting",
314
+ model: parsed.model ?? existing.model,
315
+ thinking: parsed.thinking ?? existing.thinking,
316
+ latestResponse: undefined,
317
+ error: undefined,
318
+ });
319
+ spawnAgentWorker(existing.id, promptFile);
320
+ console.log(formatAgentLine({
321
+ ...existing,
322
+ status: "running",
323
+ model: parsed.model ?? existing.model,
324
+ thinking: parsed.thinking ?? existing.thinking,
325
+ }));
326
+ return;
327
+ }
328
+ const profiles = await loadLocalAgentProfiles(config, workspaceRoot);
329
+ const target = resolveLocalAgentTarget(parsed.target, profiles, parsed.model, parsed.thinking);
330
+ if (!target) {
331
+ throw new Error(`Unknown subagent profile, provider, or id: ${parsed.target}. Available ${formatAvailableLocalAgentTargets(profiles)}`);
332
+ }
333
+ assertLocalAgentProviderAvailable(target.provider);
334
+ const promptFile = writeAgentPromptFile(parsed.prompt);
335
+ const record = store.create({
336
+ workspaceId: process.env.DEVSPACE_WORKSPACE_ID,
337
+ workspaceRoot,
338
+ profileName: target.name,
339
+ provider: target.provider,
340
+ model: target.model,
341
+ thinking: target.thinking,
342
+ });
343
+ spawnAgentWorker(record.id, promptFile);
344
+ console.log(formatAgentLine({ ...record, status: "running" }));
345
+ }
346
+ async function runAgentsShow(args) {
347
+ const [id] = args;
348
+ if (!id)
349
+ throw new Error("Usage: devspace agents show <id>");
350
+ const config = loadConfig();
351
+ const store = createLocalAgentStore(config);
352
+ let record = store.get(id);
353
+ if (!record)
354
+ throw new Error(`Unknown subagent id: ${id}`);
355
+ const deadline = Date.now() + 15_000;
356
+ while ((record.status === "starting" || record.status === "running") && Date.now() < deadline) {
357
+ await sleep(500);
358
+ record = store.get(id) ?? record;
359
+ }
360
+ console.log(formatAgentLine(record));
361
+ if (record.latestResponse) {
362
+ console.log(record.latestResponse);
363
+ return;
364
+ }
365
+ if (record.error) {
366
+ console.log(record.error);
367
+ return;
368
+ }
369
+ if (record.status === "starting" || record.status === "running") {
370
+ console.log(`No final response yet. Call \`devspace agents show ${record.id}\` again later.`);
371
+ }
372
+ }
373
+ async function runAgentsWorker(args) {
374
+ const [id, promptFileFlag, promptFile] = args;
375
+ if (!id || promptFileFlag !== "--prompt-file" || !promptFile) {
376
+ throw new Error("Usage: devspace agents __worker <id> --prompt-file <path>");
377
+ }
378
+ const config = loadConfig();
379
+ const store = createLocalAgentStore(config);
380
+ const record = store.get(id);
381
+ if (!record)
382
+ throw new Error(`Unknown subagent id: ${id}`);
383
+ store.update(record.id, { status: "running", error: undefined });
384
+ try {
385
+ const profiles = await loadLocalAgentProfiles(config, record.workspaceRoot);
386
+ const profile = profiles.find((candidate) => candidate.name === record.profileName);
387
+ const prompt = await readFile(promptFile, "utf8");
388
+ const result = profile
389
+ ? await runLocalAgentProfile(profile, record, prompt)
390
+ : await runRawLocalAgentProvider(record, prompt);
391
+ store.update(record.id, {
392
+ providerSessionId: result.providerSessionId ?? undefined,
393
+ status: "idle",
394
+ latestResponse: result.finalResponse,
395
+ error: undefined,
396
+ });
397
+ }
398
+ catch (error) {
399
+ store.update(record.id, {
400
+ status: "error",
401
+ error: error instanceof Error ? error.message : String(error),
402
+ });
403
+ }
404
+ }
405
+ async function runLocalAgentProfile(profile, record, prompt) {
406
+ const body = profile.body.trim();
407
+ const fullPrompt = body ? `${body}\n\nTask:\n${prompt}` : prompt;
408
+ return runLocalAgentProvider(profile.provider, {
409
+ prompt: fullPrompt,
410
+ workspace: record.workspaceRoot,
411
+ providerSessionId: record.providerSessionId,
412
+ writeMode: "allowed",
413
+ model: record.model ?? profile.model,
414
+ thinking: record.thinking ?? profile.thinking,
415
+ });
416
+ }
417
+ async function runRawLocalAgentProvider(record, prompt) {
418
+ if (record.profileName !== record.provider || !isLocalAgentProvider(record.provider)) {
419
+ throw new Error(`Subagent profile not found: ${record.profileName}`);
420
+ }
421
+ return runLocalAgentProvider(record.provider, {
422
+ prompt,
423
+ workspace: record.workspaceRoot,
424
+ providerSessionId: record.providerSessionId,
425
+ writeMode: "allowed",
426
+ model: record.model,
427
+ thinking: record.thinking,
428
+ });
429
+ }
430
+ function spawnAgentWorker(agentId, promptFile) {
431
+ const child = spawn(process.execPath, [
432
+ ...process.execArgv,
433
+ fileURLToPath(import.meta.url),
434
+ "agents",
435
+ "__worker",
436
+ agentId,
437
+ "--prompt-file",
438
+ promptFile,
439
+ ], {
440
+ detached: true,
441
+ stdio: "ignore",
442
+ env: process.env,
443
+ });
444
+ child.unref();
445
+ }
446
+ function writeAgentPromptFile(prompt) {
447
+ const directory = mkdtempSync(join(tmpdir(), "devspace-agent-prompt-"));
448
+ const filePath = join(directory, "prompt.txt");
449
+ writeFileSync(filePath, prompt, { mode: 0o600 });
450
+ return filePath;
451
+ }
452
+ function resolveCurrentWorkspaceRoot() {
453
+ return resolve(process.env.DEVSPACE_WORKSPACE_ROOT || process.cwd());
454
+ }
455
+ function resolveCurrentWorkspaceScope() {
456
+ return {
457
+ workspaceId: process.env.DEVSPACE_WORKSPACE_ID,
458
+ workspaceRoot: resolveCurrentWorkspaceRoot(),
459
+ };
460
+ }
461
+ function formatAgentLine(agent) {
462
+ const model = agent.model ? ` ${agent.model}` : "";
463
+ const thinking = agent.thinking ? ` thinking=${agent.thinking}` : "";
464
+ return `${agent.id} ${agent.status} ${agent.profileName} ${agent.provider}${model}${thinking}`;
465
+ }
466
+ function sleep(ms) {
467
+ return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
468
+ }
469
+ function printAgentsHelp() {
470
+ console.log([
471
+ "DevSpace agents",
472
+ "",
473
+ "Usage:",
474
+ " devspace agents ls",
475
+ " devspace agents run <profile-or-provider-or-id> [--model <model>] [--thinking <level>] <prompt>",
476
+ " devspace agents show <id>",
477
+ ].join("\n"));
478
+ }
479
+ function printVersion() {
480
+ const packageJson = require("../package.json");
481
+ if (typeof packageJson.version !== "string") {
482
+ throw new Error("Unable to read DevSpace package version.");
483
+ }
484
+ console.log(packageJson.version);
485
+ }
234
486
  function normalizeOptionalPublicBaseUrl(value) {
235
487
  const trimmed = value.trim();
236
488
  if (!trimmed || trimmed === "null" || trimmed === "none")
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { homedir } from "node:os";
2
2
  import { join, resolve } from "node:path";
3
3
  import { expandHomePath } from "./roots.js";
4
- import { loadDevspaceFiles } from "./user-config.js";
4
+ import { devspaceAgentsDir, devspaceSkillsDir, loadDevspaceFiles } from "./user-config.js";
5
5
  const DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS = 60 * 60;
6
6
  const DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
7
7
  function parsePort(value) {
@@ -44,17 +44,16 @@ function normalizeAllowedHosts(rawHosts, derivedHosts) {
44
44
  function parseBoolean(value) {
45
45
  return ["1", "true", "yes", "on"].includes(value?.toLowerCase() ?? "");
46
46
  }
47
- function parseMinimalTools(env) {
48
- if (env.DEVSPACE_TOOL_MODE === "minimal")
49
- return true;
50
- if (env.DEVSPACE_TOOL_MODE === "full")
51
- return false;
52
- if (env.DEVSPACE_TOOL_MODE) {
53
- throw new Error(`Invalid DEVSPACE_TOOL_MODE: ${env.DEVSPACE_TOOL_MODE}`);
47
+ function parseToolMode(env) {
48
+ const mode = env.DEVSPACE_TOOL_MODE;
49
+ if (mode === "minimal" || mode === "full" || mode === "codex")
50
+ return mode;
51
+ if (mode)
52
+ throw new Error(`Invalid DEVSPACE_TOOL_MODE: ${mode}`);
53
+ if (env.DEVSPACE_MINIMAL_TOOLS !== undefined) {
54
+ return parseBoolean(env.DEVSPACE_MINIMAL_TOOLS) ? "minimal" : "full";
54
55
  }
55
- if (env.DEVSPACE_MINIMAL_TOOLS !== undefined)
56
- return parseBoolean(env.DEVSPACE_MINIMAL_TOOLS);
57
- return true;
56
+ return "minimal";
58
57
  }
59
58
  function parseLogLevel(value) {
60
59
  if (!value || value === "info")
@@ -74,8 +73,7 @@ function parsePathList(value) {
74
73
  return (value
75
74
  ?.split(",")
76
75
  .map((entry) => entry.trim())
77
- .filter(Boolean)
78
- .map((entry) => resolve(expandHomePath(entry))) ?? []);
76
+ .filter(Boolean) ?? []);
79
77
  }
80
78
  function parseStringList(value, fallback) {
81
79
  const entries = value
@@ -93,13 +91,6 @@ function parsePositiveInteger(value, fallback, name) {
93
91
  }
94
92
  return parsed;
95
93
  }
96
- function parseToolNaming(value) {
97
- if (!value || value === "short")
98
- return "short";
99
- if (value === "legacy")
100
- return "legacy";
101
- throw new Error(`Invalid DEVSPACE_TOOL_NAMING: ${value}`);
102
- }
103
94
  function parseLoggingConfig(env) {
104
95
  return {
105
96
  level: parseLogLevel(env.DEVSPACE_LOG_LEVEL),
@@ -170,13 +161,17 @@ export function loadConfig(env = process.env) {
170
161
  allowedRoots: parseAllowedRoots(env.DEVSPACE_ALLOWED_ROOTS ?? files.config.allowedRoots),
171
162
  allowedHosts: parseAllowedHosts(env.DEVSPACE_ALLOWED_HOSTS, derivedAllowedHosts),
172
163
  publicBaseUrl,
173
- minimalTools: parseMinimalTools(env),
174
- toolNaming: parseToolNaming(env.DEVSPACE_TOOL_NAMING),
164
+ toolMode: parseToolMode(env),
175
165
  widgets: parseWidgetMode(env.DEVSPACE_WIDGETS),
176
166
  stateDir: resolve(expandHomePath(env.DEVSPACE_STATE_DIR ?? files.config.stateDir ?? defaultStateDir())),
177
167
  worktreeRoot: resolve(expandHomePath(env.DEVSPACE_WORKTREE_ROOT ?? files.config.worktreeRoot ?? defaultWorktreeRoot())),
178
168
  skillsEnabled: env.DEVSPACE_SKILLS === undefined ? true : parseBoolean(env.DEVSPACE_SKILLS),
179
169
  skillPaths: parsePathList(env.DEVSPACE_SKILL_PATHS),
170
+ devspaceSkillsDir: devspaceSkillsDir(env),
171
+ devspaceAgentsDir: devspaceAgentsDir(env),
172
+ subagents: env.DEVSPACE_SUBAGENTS === undefined
173
+ ? files.config.subagents === true
174
+ : parseBoolean(env.DEVSPACE_SUBAGENTS),
180
175
  agentDir: resolve(expandHomePath(env.DEVSPACE_AGENT_DIR ?? files.config.agentDir ?? defaultAgentDir())),
181
176
  logging: parseLoggingConfig(env),
182
177
  };
@@ -9,6 +9,11 @@ const migrations = [
9
9
  name: "oauth-state",
10
10
  up: migrateOAuthState,
11
11
  },
12
+ {
13
+ version: 3,
14
+ name: "local-agent-sessions",
15
+ up: migrateLocalAgentSessions,
16
+ },
12
17
  ];
13
18
  export function migrateDatabase(sqlite) {
14
19
  const migrate = sqlite.transaction(() => {
@@ -115,6 +120,35 @@ function migrateOAuthState(sqlite) {
115
120
  on oauth_refresh_tokens(expires_at);
116
121
  `);
117
122
  }
123
+ function migrateLocalAgentSessions(sqlite) {
124
+ sqlite.exec(`
125
+ create table if not exists local_agent_sessions (
126
+ id text primary key,
127
+ workspace_id text,
128
+ workspace_root text not null,
129
+ profile_name text not null,
130
+ provider text not null,
131
+ model text,
132
+ thinking text,
133
+ provider_session_id text,
134
+ status text not null,
135
+ latest_response text,
136
+ error text,
137
+ created_at text not null,
138
+ updated_at text not null
139
+ );
140
+
141
+ create index if not exists local_agent_sessions_workspace_id_idx
142
+ on local_agent_sessions(workspace_id, updated_at desc);
143
+
144
+ create index if not exists local_agent_sessions_workspace_root_idx
145
+ on local_agent_sessions(workspace_root, updated_at desc);
146
+
147
+ create index if not exists local_agent_sessions_provider_session_id_idx
148
+ on local_agent_sessions(provider_session_id);
149
+ `);
150
+ addColumnIfMissing(sqlite, "local_agent_sessions", "thinking", "text");
151
+ }
118
152
  function addColumnIfMissing(sqlite, table, column, definition) {
119
153
  const columns = sqlite.prepare(`pragma table_info(${table})`).all();
120
154
  if (columns.some((existingColumn) => existingColumn.name === column))
package/dist/db/schema.js CHANGED
@@ -50,3 +50,22 @@ export const oauthRefreshTokens = sqliteTable("oauth_refresh_tokens", {
50
50
  expiresAt: integer("expires_at").notNull(),
51
51
  resource: text("resource"),
52
52
  });
53
+ export const localAgentSessions = sqliteTable("local_agent_sessions", {
54
+ id: text("id").primaryKey(),
55
+ workspaceId: text("workspace_id"),
56
+ workspaceRoot: text("workspace_root").notNull(),
57
+ profileName: text("profile_name").notNull(),
58
+ provider: text("provider").notNull(),
59
+ model: text("model"),
60
+ thinking: text("thinking"),
61
+ providerSessionId: text("provider_session_id"),
62
+ status: text("status").notNull(),
63
+ latestResponse: text("latest_response"),
64
+ error: text("error"),
65
+ createdAt: text("created_at").notNull(),
66
+ updatedAt: text("updated_at").notNull(),
67
+ }, (table) => [
68
+ index("local_agent_sessions_workspace_id_idx").on(table.workspaceId, table.updatedAt),
69
+ index("local_agent_sessions_workspace_root_idx").on(table.workspaceRoot, table.updatedAt),
70
+ index("local_agent_sessions_provider_session_id_idx").on(table.providerSessionId),
71
+ ]);