neural-loom 0.2.60 → 0.2.61

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 (185) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +3 -0
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/cache/.previewinfo +1 -1
  5. package/.next/cache/.rscinfo +1 -1
  6. package/.next/cache/.tsbuildinfo +1 -1
  7. package/.next/diagnostics/route-bundle-stats.json +2 -2
  8. package/.next/fallback-build-manifest.json +3 -3
  9. package/.next/prerender-manifest.json +3 -3
  10. package/.next/routes-manifest.json +18 -0
  11. package/.next/server/app/_global-error.html +1 -1
  12. package/.next/server/app/_global-error.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  16. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  17. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/.next/server/app/_not-found.html +1 -1
  19. package/.next/server/app/_not-found.rsc +1 -1
  20. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  21. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  23. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/.next/server/app/api/configs/route/app-paths-manifest.json +3 -0
  27. package/.next/server/app/api/configs/route/build-manifest.json +9 -0
  28. package/.next/server/app/api/configs/route/server-reference-manifest.json +4 -0
  29. package/.next/server/app/api/configs/route.js +6 -0
  30. package/.next/server/app/api/configs/route.js.map +5 -0
  31. package/.next/server/app/api/configs/route.js.nft.json +1 -0
  32. package/.next/server/app/api/configs/route_client-reference-manifest.js +3 -0
  33. package/.next/server/app/api/context/aider/route.js.nft.json +1 -1
  34. package/.next/server/app/api/context/claude/route.js.nft.json +1 -1
  35. package/.next/server/app/api/context/route.js.nft.json +1 -1
  36. package/.next/server/app/api/context/ssh/route.js.nft.json +1 -1
  37. package/.next/server/app/api/recents/route.js.nft.json +1 -1
  38. package/.next/server/app/api/sessions/inject/route.js +2 -2
  39. package/.next/server/app/api/sessions/inject/route.js.nft.json +1 -1
  40. package/.next/server/app/api/sessions/input/route.js +2 -2
  41. package/.next/server/app/api/sessions/input/route.js.nft.json +1 -1
  42. package/.next/server/app/api/sessions/launch/route.js +2 -2
  43. package/.next/server/app/api/sessions/launch/route.js.nft.json +1 -1
  44. package/.next/server/app/api/sessions/route.js +2 -2
  45. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  46. package/.next/server/app/api/sessions/scope/route.js +2 -2
  47. package/.next/server/app/api/sessions/scope/route.js.nft.json +1 -1
  48. package/.next/server/app/api/sessions/start/route.js +2 -2
  49. package/.next/server/app/api/sessions/start/route.js.nft.json +1 -1
  50. package/.next/server/app/api/sessions/stats/route.js +2 -2
  51. package/.next/server/app/api/sessions/stats/route.js.nft.json +1 -1
  52. package/.next/server/app/api/sessions/stop/route.js +2 -2
  53. package/.next/server/app/api/sessions/stop/route.js.nft.json +1 -1
  54. package/.next/server/app/api/sessions/upload-image/route/app-paths-manifest.json +3 -0
  55. package/.next/server/app/api/sessions/upload-image/route/build-manifest.json +9 -0
  56. package/.next/server/app/api/sessions/upload-image/route/server-reference-manifest.json +4 -0
  57. package/.next/server/app/api/sessions/upload-image/route.js +7 -0
  58. package/.next/server/app/api/sessions/upload-image/route.js.map +5 -0
  59. package/.next/server/app/api/sessions/upload-image/route.js.nft.json +1 -0
  60. package/.next/server/app/api/sessions/upload-image/route_client-reference-manifest.js +3 -0
  61. package/.next/server/app/api/sql/connections/route.js.nft.json +1 -1
  62. package/.next/server/app/api/sql/mcp/route.js.nft.json +1 -1
  63. package/.next/server/app/api/sql/query/route.js.nft.json +1 -1
  64. package/.next/server/app/api/sql/schema/route.js.nft.json +1 -1
  65. package/.next/server/app/api/telemetry/route/app-paths-manifest.json +3 -0
  66. package/.next/server/app/api/telemetry/route/build-manifest.json +9 -0
  67. package/.next/server/app/api/telemetry/route/server-reference-manifest.json +4 -0
  68. package/.next/server/app/api/telemetry/route.js +6 -0
  69. package/.next/server/app/api/telemetry/route.js.map +5 -0
  70. package/.next/server/app/api/telemetry/route.js.nft.json +1 -0
  71. package/.next/server/app/api/telemetry/route_client-reference-manifest.js +3 -0
  72. package/.next/server/app/index.html +1 -1
  73. package/.next/server/app/index.rsc +3 -3
  74. package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
  75. package/.next/server/app/index.segments/_full.segment.rsc +3 -3
  76. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  77. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  78. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  79. package/.next/server/app/page/react-loadable-manifest.json +5 -5
  80. package/.next/server/app/page_client-reference-manifest.js +1 -1
  81. package/.next/server/app-paths-manifest.json +3 -0
  82. package/.next/server/chunks/[root-of-the-server]__0.ptwor._.js +1 -1
  83. package/.next/server/chunks/[root-of-the-server]__0.ptwor._.js.map +1 -1
  84. package/.next/server/chunks/[root-of-the-server]__00-sqou._.js +15 -0
  85. package/.next/server/chunks/{[root-of-the-server]__0thsozo._.js.map → [root-of-the-server]__00-sqou._.js.map} +1 -1
  86. package/.next/server/chunks/[root-of-the-server]__00l5btc._.js +1 -1
  87. package/.next/server/chunks/[root-of-the-server]__00l5btc._.js.map +1 -1
  88. package/.next/server/chunks/[root-of-the-server]__01621nz._.js +3 -0
  89. package/.next/server/chunks/[root-of-the-server]__01621nz._.js.map +1 -0
  90. package/.next/server/chunks/[root-of-the-server]__06men8g._.js +1 -1
  91. package/.next/server/chunks/[root-of-the-server]__06men8g._.js.map +1 -1
  92. package/.next/server/chunks/[root-of-the-server]__08r0s-s._.js +3 -0
  93. package/.next/server/chunks/[root-of-the-server]__08r0s-s._.js.map +1 -0
  94. package/.next/server/chunks/[root-of-the-server]__097vtn1._.js +15 -0
  95. package/.next/server/chunks/{[root-of-the-server]__0em6a~~._.js.map → [root-of-the-server]__097vtn1._.js.map} +1 -1
  96. package/.next/server/chunks/[root-of-the-server]__0_v2.a8._.js +15 -0
  97. package/.next/server/chunks/[root-of-the-server]__0_v2.a8._.js.map +1 -0
  98. package/.next/server/chunks/[root-of-the-server]__0co.g5k._.js +1 -1
  99. package/.next/server/chunks/[root-of-the-server]__0co.g5k._.js.map +1 -1
  100. package/.next/server/chunks/[root-of-the-server]__0e03ite._.js +15 -0
  101. package/.next/server/chunks/{[root-of-the-server]__10wwca8._.js.map → [root-of-the-server]__0e03ite._.js.map} +1 -1
  102. package/.next/server/chunks/[root-of-the-server]__0fxtx7z._.js +15 -0
  103. package/.next/server/chunks/[root-of-the-server]__0fxtx7z._.js.map +1 -0
  104. package/.next/server/chunks/[root-of-the-server]__0h.~hv6._.js +15 -0
  105. package/.next/server/chunks/{[root-of-the-server]__0s0is_9._.js.map → [root-of-the-server]__0h.~hv6._.js.map} +1 -1
  106. package/.next/server/chunks/[root-of-the-server]__0li1h1u._.js +33 -0
  107. package/.next/server/chunks/[root-of-the-server]__0li1h1u._.js.map +1 -0
  108. package/.next/server/chunks/[root-of-the-server]__0s1axek._.js +15 -0
  109. package/.next/server/chunks/{[root-of-the-server]__0i~01cv._.js.map → [root-of-the-server]__0s1axek._.js.map} +1 -1
  110. package/.next/server/chunks/[root-of-the-server]__0tq0maq._.js +15 -0
  111. package/.next/server/chunks/{[root-of-the-server]__0_o7_jj._.js.map → [root-of-the-server]__0tq0maq._.js.map} +1 -1
  112. package/.next/server/chunks/[root-of-the-server]__0viil6b._.js +15 -0
  113. package/.next/server/chunks/{[root-of-the-server]__07zx04e._.js.map → [root-of-the-server]__0viil6b._.js.map} +1 -1
  114. package/.next/server/chunks/_next-internal_server_app_api_configs_route_actions_087k_4j.js +3 -0
  115. package/.next/server/chunks/_next-internal_server_app_api_configs_route_actions_087k_4j.js.map +1 -0
  116. package/.next/server/chunks/_next-internal_server_app_api_sessions_upload-image_route_actions_09tfn3a.js +3 -0
  117. package/.next/server/chunks/_next-internal_server_app_api_sessions_upload-image_route_actions_09tfn3a.js.map +1 -0
  118. package/.next/server/chunks/_next-internal_server_app_api_telemetry_route_actions_0.yo5qr.js +3 -0
  119. package/.next/server/chunks/_next-internal_server_app_api_telemetry_route_actions_0.yo5qr.js.map +1 -0
  120. package/.next/server/chunks/ssr/_0t7oqy6._.js +1 -1
  121. package/.next/server/chunks/ssr/_0t7oqy6._.js.map +1 -1
  122. package/.next/server/middleware-build-manifest.js +3 -3
  123. package/.next/server/pages/404.html +1 -1
  124. package/.next/server/pages/500.html +1 -1
  125. package/.next/server/server-reference-manifest.js +1 -1
  126. package/.next/server/server-reference-manifest.json +1 -1
  127. package/.next/static/chunks/07lj0adi0kt5p.css +1 -0
  128. package/.next/static/chunks/0xhaghhxllgzs.js +1 -0
  129. package/.next/static/chunks/128c-kly7grxg.js +4 -0
  130. package/.next/static/chunks/{0y~7jjqbr14.5.js → 15_baizhaiq8c.js} +10 -7
  131. package/.next/static/chunks/15sigqif_v.mu.js +1 -0
  132. package/.next/static/chunks/17ud2wmsyqknc.js +11 -0
  133. package/.next/trace +1 -1
  134. package/.next/trace-build +1 -1
  135. package/.next/types/routes.d.ts +4 -1
  136. package/.next/types/validator.ts +27 -0
  137. package/bin/cli.js +4 -4
  138. package/bin/fix-pwa-ubuntu.sh +50 -0
  139. package/package.json +1 -1
  140. package/src/app/api/configs/route.ts +40 -0
  141. package/src/app/api/sessions/launch/route.ts +3 -3
  142. package/src/app/api/sessions/route.ts +2 -2
  143. package/src/app/api/sessions/start/route.ts +2 -2
  144. package/src/app/api/sessions/upload-image/route.ts +57 -0
  145. package/src/app/api/sql/connections/route.ts +9 -6
  146. package/src/app/api/sql/mcp/route.ts +3 -2
  147. package/src/app/api/sql/query/route.ts +3 -2
  148. package/src/app/api/sql/schema/route.ts +3 -2
  149. package/src/app/api/telemetry/route.ts +18 -0
  150. package/src/app/components/AiderWizard.tsx +46 -2
  151. package/src/app/components/ClaudeWizard.tsx +30 -0
  152. package/src/app/components/IdeLayout.tsx +566 -70
  153. package/src/app/components/TerminalConsole.tsx +54 -0
  154. package/src/app/page.module.css +8 -2
  155. package/src/app/page.tsx +534 -401
  156. package/src/lib/agents/AgentRunner.ts +88 -5
  157. package/src/lib/agents/AiderRunner.ts +4 -18
  158. package/src/lib/agents/ClaudeRunner.ts +16 -26
  159. package/src/lib/agents/DockerRunner.ts +16 -26
  160. package/src/lib/agents/MockRunner.ts +4 -16
  161. package/src/lib/agents/SSHRunner.ts +16 -26
  162. package/src/lib/agents/SessionConfig.ts +79 -0
  163. package/src/lib/agents/SessionManager.ts +16 -16
  164. package/src/lib/agents/SessionPersister.ts +13 -9
  165. package/src/lib/agents/ShellRunner.ts +14 -21
  166. package/src/lib/agents/WebSocketServer.ts +2 -2
  167. package/.next/server/chunks/[root-of-the-server]__01.zh3b._.js +0 -15
  168. package/.next/server/chunks/[root-of-the-server]__01.zh3b._.js.map +0 -1
  169. package/.next/server/chunks/[root-of-the-server]__07zx04e._.js +0 -15
  170. package/.next/server/chunks/[root-of-the-server]__0_o7_jj._.js +0 -15
  171. package/.next/server/chunks/[root-of-the-server]__0d0pykb._.js +0 -33
  172. package/.next/server/chunks/[root-of-the-server]__0d0pykb._.js.map +0 -1
  173. package/.next/server/chunks/[root-of-the-server]__0em6a~~._.js +0 -15
  174. package/.next/server/chunks/[root-of-the-server]__0i~01cv._.js +0 -15
  175. package/.next/server/chunks/[root-of-the-server]__0s0is_9._.js +0 -15
  176. package/.next/server/chunks/[root-of-the-server]__0thsozo._.js +0 -15
  177. package/.next/server/chunks/[root-of-the-server]__10wwca8._.js +0 -15
  178. package/.next/static/chunks/076_b43_kienv.js +0 -1
  179. package/.next/static/chunks/07r_au7dcyu8l.js +0 -11
  180. package/.next/static/chunks/0hoyc~vbuqe0t.css +0 -1
  181. package/.next/static/chunks/0k2co3uj4w81_.js +0 -1
  182. package/.next/static/chunks/0sqeixl35qoac.js +0 -1
  183. /package/.next/static/{PdRX_M_W5hqhNmHxYt-oH → b48zoI5QUg8m7v1asTiCM}/_buildManifest.js +0 -0
  184. /package/.next/static/{PdRX_M_W5hqhNmHxYt-oH → b48zoI5QUg8m7v1asTiCM}/_clientMiddlewareManifest.js +0 -0
  185. /package/.next/static/{PdRX_M_W5hqhNmHxYt-oH → b48zoI5QUg8m7v1asTiCM}/_ssgManifest.js +0 -0
@@ -0,0 +1,57 @@
1
+ import { NextResponse } from "next/server";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { SessionManager } from "@/lib/agents/SessionManager";
5
+
6
+ export async function POST(request: Request) {
7
+ try {
8
+ const { sessionId, base64Data, fileName } = await request.json();
9
+
10
+ if (!sessionId) {
11
+ return NextResponse.json({ error: "Missing parameter 'sessionId'" }, { status: 400 });
12
+ }
13
+ if (!base64Data) {
14
+ return NextResponse.json({ error: "Missing parameter 'base64Data'" }, { status: 400 });
15
+ }
16
+
17
+ const manager = SessionManager.getInstance();
18
+ const session = manager.getSession(sessionId);
19
+ if (!session) {
20
+ return NextResponse.json({ error: "Session not found" }, { status: 404 });
21
+ }
22
+
23
+ const root = session.workspaceRoot || process.env.ORIGINAL_CWD || process.cwd();
24
+
25
+ // Create pasted-images folder inside workspace root under .ai/neural-loom
26
+ const destDir = path.join(root, ".ai", "neural-loom", "pasted-images");
27
+ if (!fs.existsSync(destDir)) {
28
+ fs.mkdirSync(destDir, { recursive: true });
29
+ }
30
+
31
+ // Parse base64 data URL
32
+ let buffer: Buffer;
33
+ let ext = "png";
34
+ const matches = base64Data.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
35
+
36
+ if (matches && matches.length === 3) {
37
+ buffer = Buffer.from(matches[2], 'base64');
38
+ const mimeType = matches[1];
39
+ ext = mimeType.split("/")[1] || "png";
40
+ } else {
41
+ buffer = Buffer.from(base64Data, 'base64');
42
+ }
43
+
44
+ const name = fileName || `image_${Date.now()}.${ext}`;
45
+ const destPath = path.join(destDir, name);
46
+
47
+ fs.writeFileSync(destPath, buffer);
48
+
49
+ // Return relative path from workspace root
50
+ const relativePath = `./.ai/neural-loom/pasted-images/${name}`;
51
+
52
+ return NextResponse.json({ success: true, path: relativePath });
53
+ } catch (error) {
54
+ const msg = error instanceof Error ? error.message : String(error);
55
+ return NextResponse.json({ error: msg }, { status: 500 });
56
+ }
57
+ }
@@ -7,8 +7,9 @@ export async function GET(request: Request) {
7
7
  const root = searchParams.get("root") || undefined;
8
8
  const data = await ConnectionManager.getProfiles(root);
9
9
  return NextResponse.json({ success: true, ...data });
10
- } catch (error: any) {
11
- return NextResponse.json({ error: error.message || String(error) }, { status: 500 });
10
+ } catch (error) {
11
+ const msg = error instanceof Error ? error.message : String(error);
12
+ return NextResponse.json({ error: msg }, { status: 500 });
12
13
  }
13
14
  }
14
15
 
@@ -48,13 +49,15 @@ export async function POST(request: Request) {
48
49
  await adapter.listTables(); // Quick verify
49
50
  await adapter.disconnect();
50
51
  return NextResponse.json({ success: true, message: "Connection test successful!" });
51
- } catch (err: any) {
52
- return NextResponse.json({ success: false, error: err.message || String(err) });
52
+ } catch (err) {
53
+ const msg = err instanceof Error ? err.message : String(err);
54
+ return NextResponse.json({ success: false, error: msg });
53
55
  }
54
56
  }
55
57
 
56
58
  return NextResponse.json({ error: "Invalid action" }, { status: 400 });
57
- } catch (error: any) {
58
- return NextResponse.json({ error: error.message || String(error) }, { status: 500 });
59
+ } catch (error) {
60
+ const msg = error instanceof Error ? error.message : String(error);
61
+ return NextResponse.json({ error: msg }, { status: 500 });
59
62
  }
60
63
  }
@@ -14,13 +14,14 @@ export async function POST(request: Request) {
14
14
  }
15
15
 
16
16
  return NextResponse.json(response);
17
- } catch (error: any) {
17
+ } catch (error) {
18
+ const msg = error instanceof Error ? error.message : String(error);
18
19
  return NextResponse.json({
19
20
  jsonrpc: "2.0",
20
21
  id: null,
21
22
  error: {
22
23
  code: -32603,
23
- message: "Internal server error: " + (error.message || String(error))
24
+ message: "Internal server error: " + msg
24
25
  }
25
26
  }, { status: 500 });
26
27
  }
@@ -50,7 +50,8 @@ export async function POST(request: Request) {
50
50
  affectedRows: result.affectedRows,
51
51
  durationMs
52
52
  });
53
- } catch (error: any) {
54
- return NextResponse.json({ error: error.message || String(error) }, { status: 500 });
53
+ } catch (error) {
54
+ const msg = error instanceof Error ? error.message : String(error);
55
+ return NextResponse.json({ error: msg }, { status: 500 });
55
56
  }
56
57
  }
@@ -36,7 +36,8 @@ export async function GET(request: Request) {
36
36
  }
37
37
 
38
38
  return NextResponse.json({ error: "Invalid action. Must be 'list_tables', 'describe_table', or 'summary'" }, { status: 400 });
39
- } catch (error: any) {
40
- return NextResponse.json({ error: error.message || String(error) }, { status: 500 });
39
+ } catch (error) {
40
+ const msg = error instanceof Error ? error.message : String(error);
41
+ return NextResponse.json({ error: msg }, { status: 500 });
41
42
  }
42
43
  }
@@ -0,0 +1,18 @@
1
+ import { NextResponse } from "next/server";
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ export async function GET() {
6
+ try {
7
+ const historyFile = path.join(process.cwd(), ".ai", "neural-loom", "token-history.json");
8
+ if (!fs.existsSync(historyFile)) {
9
+ return NextResponse.json({ success: true, history: [] });
10
+ }
11
+ const raw = fs.readFileSync(historyFile, "utf8");
12
+ const history = JSON.parse(raw) || [];
13
+ return NextResponse.json({ success: true, history });
14
+ } catch (error) {
15
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
16
+ return NextResponse.json({ error: errorMessage }, { status: 500 });
17
+ }
18
+ }
@@ -4,6 +4,7 @@ import { useEffect, useState } from "react";
4
4
  import DirectoryBrowserModal from "./DirectoryBrowserModal";
5
5
 
6
6
  interface AiderConfig {
7
+ name: string;
7
8
  openaiApiKey: string;
8
9
  anthropicApiKey: string;
9
10
  geminiApiKey: string;
@@ -16,12 +17,13 @@ interface AiderConfig {
16
17
 
17
18
  interface AiderWizardProps {
18
19
  onClose: () => void;
19
- onConfirmLaunch: () => void;
20
+ onConfirmLaunch: (name: string, workspaceRoot: string, scopedDirs: string[]) => void;
20
21
  isDocker: boolean;
21
22
  }
22
23
 
23
24
  export default function AiderWizard({ onClose, onConfirmLaunch, isDocker }: AiderWizardProps) {
24
25
  const [config, setConfig] = useState<AiderConfig>({
26
+ name: isDocker ? "Aider Docker" : "Aider Local",
25
27
  openaiApiKey: "",
26
28
  anthropicApiKey: "",
27
29
  geminiApiKey: "",
@@ -33,6 +35,7 @@ export default function AiderWizard({ onClose, onConfirmLaunch, isDocker }: Aide
33
35
  });
34
36
 
35
37
  const [newScopePath, setNewScopePath] = useState("");
38
+ const [saveAsTemplate, setSaveAsTemplate] = useState(false);
36
39
  const [isLoading, setIsLoading] = useState(true);
37
40
  const [isSaving, setIsSaving] = useState(false);
38
41
  const [errorMessage, setErrorMessage] = useState("");
@@ -49,6 +52,7 @@ export default function AiderWizard({ onClose, onConfirmLaunch, isDocker }: Aide
49
52
  const data = await res.json();
50
53
  if (data.success && data.config) {
51
54
  setConfig({
55
+ name: data.config.name || (isDocker ? "Aider Docker" : "Aider Local"),
52
56
  openaiApiKey: data.config.openaiApiKey || "",
53
57
  anthropicApiKey: data.config.anthropicApiKey || "",
54
58
  geminiApiKey: data.config.geminiApiKey || "",
@@ -90,8 +94,24 @@ export default function AiderWizard({ onClose, onConfirmLaunch, isDocker }: Aide
90
94
  });
91
95
 
92
96
  if (res.ok) {
97
+ if (saveAsTemplate) {
98
+ await fetch("/api/configs", {
99
+ method: "POST",
100
+ headers: { "Content-Type": "application/json" },
101
+ body: JSON.stringify({
102
+ action: "save",
103
+ config: {
104
+ name: config.name,
105
+ type: isDocker ? "aider-docker" : "aider",
106
+ workspaceRoot: config.workspaceRoot,
107
+ scopedDirs: config.scopedDirs,
108
+ extraFlags: config.extraFlags
109
+ }
110
+ })
111
+ });
112
+ }
93
113
  setIsSaving(false);
94
- onConfirmLaunch();
114
+ onConfirmLaunch(config.name, config.workspaceRoot, config.scopedDirs);
95
115
  } else {
96
116
  const data = await res.json();
97
117
  setErrorMessage(data.error || "Failed to save settings.");
@@ -133,6 +153,17 @@ export default function AiderWizard({ onClose, onConfirmLaunch, isDocker }: Aide
133
153
  Configure model and environment variables for the Aider session. API keys are saved locally in `.ai/neural-loom/aider.json` and are ignored by git.
134
154
  </p>
135
155
 
156
+ <div style={formGroupStyle}>
157
+ <label style={labelStyle}>Session Display Name</label>
158
+ <input
159
+ type="text"
160
+ value={config.name}
161
+ onChange={(e) => setConfig({ ...config, name: e.target.value })}
162
+ placeholder={isDocker ? "Aider Docker" : "Aider Local"}
163
+ style={inputStyle}
164
+ />
165
+ </div>
166
+
136
167
  <div style={formGroupStyle}>
137
168
  <label style={labelStyle}>Anthropic API Key</label>
138
169
  <input
@@ -355,6 +386,19 @@ export default function AiderWizard({ onClose, onConfirmLaunch, isDocker }: Aide
355
386
  </label>
356
387
  </div>
357
388
 
389
+ <div style={{ ...formGroupStyle, flexDirection: "row", alignItems: "center", gap: "0.5rem", marginTop: "1rem", marginBottom: "0.5rem" }}>
390
+ <input
391
+ type="checkbox"
392
+ id="saveAsTemplate"
393
+ checked={saveAsTemplate}
394
+ onChange={(e) => setSaveAsTemplate(e.target.checked)}
395
+ style={{ width: "16px", height: "16px", cursor: "pointer", accentColor: "var(--accent)" }}
396
+ />
397
+ <label htmlFor="saveAsTemplate" style={{ fontSize: "0.8rem", color: "var(--foreground)", cursor: "pointer", userSelect: "none" }}>
398
+ Save configuration template
399
+ </label>
400
+ </div>
401
+
358
402
  {errorMessage && (
359
403
  <div style={errorBoxStyle}>
360
404
  ✗ {errorMessage}
@@ -25,6 +25,7 @@ export default function ClaudeWizard({ onClose, onConfirmLaunch, isDocker }: Cla
25
25
  });
26
26
 
27
27
  const [newScopePath, setNewScopePath] = useState("");
28
+ const [saveAsTemplate, setSaveAsTemplate] = useState(false);
28
29
  const [isLoading, setIsLoading] = useState(true);
29
30
  const [isSaving, setIsSaving] = useState(false);
30
31
  const [errorMessage, setErrorMessage] = useState("");
@@ -78,6 +79,22 @@ export default function ClaudeWizard({ onClose, onConfirmLaunch, isDocker }: Cla
78
79
  });
79
80
 
80
81
  if (res.ok) {
82
+ if (saveAsTemplate) {
83
+ await fetch("/api/configs", {
84
+ method: "POST",
85
+ headers: { "Content-Type": "application/json" },
86
+ body: JSON.stringify({
87
+ action: "save",
88
+ config: {
89
+ name: config.name,
90
+ type: isDocker ? "claude-docker" : "claude",
91
+ workspaceRoot: config.workspaceRoot,
92
+ scopedDirs: config.scopedDirs,
93
+ extraFlags: config.extraFlags
94
+ }
95
+ })
96
+ });
97
+ }
81
98
  setIsSaving(false);
82
99
  onConfirmLaunch(config.name, config.workspaceRoot, config.scopedDirs);
83
100
  } else {
@@ -289,6 +306,19 @@ export default function ClaudeWizard({ onClose, onConfirmLaunch, isDocker }: Cla
289
306
  )}
290
307
  </div>
291
308
 
309
+ <div style={{ ...formGroupStyle, flexDirection: "row", alignItems: "center", gap: "0.5rem", marginTop: "1rem", marginBottom: "0.5rem" }}>
310
+ <input
311
+ type="checkbox"
312
+ id="saveAsTemplate"
313
+ checked={saveAsTemplate}
314
+ onChange={(e) => setSaveAsTemplate(e.target.checked)}
315
+ style={{ width: "16px", height: "16px", cursor: "pointer", accentColor: "var(--accent)" }}
316
+ />
317
+ <label htmlFor="saveAsTemplate" style={{ fontSize: "0.8rem", color: "var(--foreground)", cursor: "pointer", userSelect: "none" }}>
318
+ Save configuration template
319
+ </label>
320
+ </div>
321
+
292
322
  {errorMessage && (
293
323
  <div style={errorBoxStyle}>
294
324
  ✗ {errorMessage}