arcie 0.1.3 → 0.1.5

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 (156) hide show
  1. package/README.md +34 -6
  2. package/dist/agent/index.d.ts +6 -3
  3. package/dist/agent/index.js +6 -6
  4. package/dist/agent/index.js.map +1 -1
  5. package/dist/auth/index.d.ts +6 -5
  6. package/dist/auth/index.js +8 -17
  7. package/dist/auth/index.js.map +1 -1
  8. package/dist/build-TWNCPXBU.js +82 -0
  9. package/dist/build-TWNCPXBU.js.map +1 -0
  10. package/dist/channels/index.d.ts +8 -5
  11. package/dist/channels/index.js +10 -12
  12. package/dist/channels/index.js.map +1 -1
  13. package/dist/chunk-3CNQ6HQV.js +12 -0
  14. package/dist/chunk-3CNQ6HQV.js.map +1 -0
  15. package/dist/chunk-4L7WVCTU.js +12 -0
  16. package/dist/chunk-4L7WVCTU.js.map +1 -0
  17. package/dist/chunk-4WSILP75.js +1774 -0
  18. package/dist/chunk-4WSILP75.js.map +1 -0
  19. package/dist/chunk-6XETPLIF.js +176 -0
  20. package/dist/chunk-6XETPLIF.js.map +1 -0
  21. package/dist/chunk-CKHBEZCT.js +24 -0
  22. package/dist/chunk-CKHBEZCT.js.map +1 -0
  23. package/dist/chunk-FQ7KDNV5.js +12 -0
  24. package/dist/chunk-FQ7KDNV5.js.map +1 -0
  25. package/dist/chunk-HVBDOHTL.js +148 -0
  26. package/dist/chunk-HVBDOHTL.js.map +1 -0
  27. package/dist/chunk-HWD2Z2CH.js +742 -0
  28. package/dist/chunk-HWD2Z2CH.js.map +1 -0
  29. package/dist/chunk-KVSX4MLK.js +139 -0
  30. package/dist/chunk-KVSX4MLK.js.map +1 -0
  31. package/dist/chunk-L3BTL5FM.js +39 -0
  32. package/dist/chunk-L3BTL5FM.js.map +1 -0
  33. package/dist/chunk-LQC65UHE.js +34 -0
  34. package/dist/chunk-LQC65UHE.js.map +1 -0
  35. package/dist/chunk-N5R3TEEZ.js +15 -0
  36. package/dist/chunk-N5R3TEEZ.js.map +1 -0
  37. package/dist/chunk-PHTJLJOV.js +25 -0
  38. package/dist/chunk-PHTJLJOV.js.map +1 -0
  39. package/dist/chunk-QE46QFID.js +92 -0
  40. package/dist/chunk-QE46QFID.js.map +1 -0
  41. package/dist/chunk-SGWQZZ2A.js +54 -0
  42. package/dist/chunk-SGWQZZ2A.js.map +1 -0
  43. package/dist/chunk-WAD7VLNI.js +51 -0
  44. package/dist/chunk-WAD7VLNI.js.map +1 -0
  45. package/dist/chunk-XOFWQUOO.js +30 -0
  46. package/dist/chunk-XOFWQUOO.js.map +1 -0
  47. package/dist/chunk-ZJNHQXMO.js +20 -0
  48. package/dist/chunk-ZJNHQXMO.js.map +1 -0
  49. package/dist/cli/index.d.ts +7 -2
  50. package/dist/cli/index.js +171 -41
  51. package/dist/cli/index.js.map +1 -1
  52. package/dist/context/index.d.ts +14 -11
  53. package/dist/context/index.js +22 -37
  54. package/dist/context/index.js.map +1 -1
  55. package/dist/dev-ZZDVOWUZ.js +1967 -0
  56. package/dist/dev-ZZDVOWUZ.js.map +1 -0
  57. package/dist/discover/index.d.ts +17 -7
  58. package/dist/discover/index.js +6 -132
  59. package/dist/discover/index.js.map +1 -1
  60. package/dist/hooks/index.d.ts +6 -3
  61. package/dist/hooks/index.js +6 -6
  62. package/dist/hooks/index.js.map +1 -1
  63. package/dist/index-CElLRVTP.d.ts +136 -0
  64. package/dist/index.d.ts +297 -18
  65. package/dist/index.js +160 -13
  66. package/dist/index.js.map +1 -1
  67. package/dist/init-5F7CIVLN.js +822 -0
  68. package/dist/init-5F7CIVLN.js.map +1 -0
  69. package/dist/instructions/index.d.ts +7 -4
  70. package/dist/instructions/index.js +8 -32
  71. package/dist/instructions/index.js.map +1 -1
  72. package/dist/protocol/events.d.ts +47 -39
  73. package/dist/protocol/events.js +46 -45
  74. package/dist/protocol/events.js.map +1 -1
  75. package/dist/runner/index.d.ts +4 -18
  76. package/dist/runner/index.js +11 -277
  77. package/dist/runner/index.js.map +1 -1
  78. package/dist/scaffold-web-chat-OQL5C2GK.js +7 -0
  79. package/dist/scaffold-web-chat-OQL5C2GK.js.map +1 -0
  80. package/dist/schedules/index.d.ts +6 -3
  81. package/dist/schedules/index.js +6 -6
  82. package/dist/schedules/index.js.map +1 -1
  83. package/dist/skills/index.d.ts +7 -4
  84. package/dist/skills/index.js +8 -27
  85. package/dist/skills/index.js.map +1 -1
  86. package/dist/tools/index.d.ts +13 -3
  87. package/dist/tools/index.js +8 -9
  88. package/dist/tools/index.js.map +1 -1
  89. package/dist/types-DwxwdSa2.d.ts +151 -0
  90. package/docs/getting-started.md +2 -2
  91. package/docs/index.md +3 -2
  92. package/docs/project-layout.md +2 -2
  93. package/docs/subagents.md +95 -0
  94. package/package.json +15 -10
  95. package/templates/default/.env.local +12 -0
  96. package/templates/default/agent/agent.ts +2 -2
  97. package/templates/default/package.json +3 -3
  98. package/templates/web-chat/.env.local.example +4 -0
  99. package/templates/web-chat/README.md +51 -0
  100. package/templates/web-chat/app/api/chat/route.ts +43 -0
  101. package/templates/web-chat/app/globals.css +55 -0
  102. package/templates/web-chat/app/layout.tsx +15 -0
  103. package/templates/web-chat/app/page.tsx +9 -0
  104. package/templates/web-chat/components/chat.tsx +196 -0
  105. package/templates/web-chat/components/input-bar.tsx +74 -0
  106. package/templates/web-chat/components/message.tsx +63 -0
  107. package/templates/web-chat/components/tool-call.tsx +79 -0
  108. package/templates/web-chat/components/ui/button.tsx +46 -0
  109. package/templates/web-chat/components/ui/input.tsx +19 -0
  110. package/templates/web-chat/components.json +20 -0
  111. package/templates/web-chat/lib/stream.ts +41 -0
  112. package/templates/web-chat/lib/types.ts +61 -0
  113. package/templates/web-chat/lib/utils.ts +6 -0
  114. package/templates/web-chat/next.config.mjs +4 -0
  115. package/templates/web-chat/package.json +32 -0
  116. package/templates/web-chat/postcss.config.mjs +6 -0
  117. package/templates/web-chat/tailwind.config.ts +59 -0
  118. package/templates/web-chat/tsconfig.json +23 -0
  119. package/dist/agent/index.d.ts.map +0 -1
  120. package/dist/auth/index.d.ts.map +0 -1
  121. package/dist/channels/index.d.ts.map +0 -1
  122. package/dist/cli/banner.d.ts +0 -2
  123. package/dist/cli/banner.d.ts.map +0 -1
  124. package/dist/cli/banner.js +0 -10
  125. package/dist/cli/banner.js.map +0 -1
  126. package/dist/cli/build.d.ts +0 -5
  127. package/dist/cli/build.d.ts.map +0 -1
  128. package/dist/cli/build.js +0 -54
  129. package/dist/cli/build.js.map +0 -1
  130. package/dist/cli/dev.d.ts +0 -5
  131. package/dist/cli/dev.d.ts.map +0 -1
  132. package/dist/cli/dev.js +0 -74
  133. package/dist/cli/dev.js.map +0 -1
  134. package/dist/cli/index.d.ts.map +0 -1
  135. package/dist/cli/init.d.ts +0 -4
  136. package/dist/cli/init.d.ts.map +0 -1
  137. package/dist/cli/init.js +0 -75
  138. package/dist/cli/init.js.map +0 -1
  139. package/dist/context/index.d.ts.map +0 -1
  140. package/dist/discover/index.d.ts.map +0 -1
  141. package/dist/hooks/index.d.ts.map +0 -1
  142. package/dist/index.d.ts.map +0 -1
  143. package/dist/instructions/index.d.ts.map +0 -1
  144. package/dist/loader.d.ts +0 -7
  145. package/dist/loader.d.ts.map +0 -1
  146. package/dist/loader.js +0 -102
  147. package/dist/loader.js.map +0 -1
  148. package/dist/protocol/events.d.ts.map +0 -1
  149. package/dist/runner/index.d.ts.map +0 -1
  150. package/dist/schedules/index.d.ts.map +0 -1
  151. package/dist/skills/index.d.ts.map +0 -1
  152. package/dist/tools/index.d.ts.map +0 -1
  153. package/dist/types.d.ts +0 -118
  154. package/dist/types.d.ts.map +0 -1
  155. package/dist/types.js +0 -2
  156. package/dist/types.js.map +0 -1
@@ -0,0 +1,822 @@
1
+ import {
2
+ renderCliTaggedLine
3
+ } from "./chunk-HVBDOHTL.js";
4
+ import {
5
+ scaffoldWebChat
6
+ } from "./chunk-SGWQZZ2A.js";
7
+ import {
8
+ EMPTY_LINE,
9
+ LiveRegion,
10
+ applyLineEditorKey,
11
+ attachKeyStream,
12
+ createTheme,
13
+ detectUnicode,
14
+ inputTextWidth,
15
+ lineOf,
16
+ maskLine,
17
+ sliceVisible,
18
+ visibleLength,
19
+ visibleLine
20
+ } from "./chunk-HWD2Z2CH.js";
21
+ import "./chunk-N5R3TEEZ.js";
22
+
23
+ // src/cli/init.ts
24
+ import {
25
+ copyFileSync,
26
+ existsSync as existsSync2,
27
+ mkdirSync,
28
+ readdirSync,
29
+ readFileSync as readFileSync2,
30
+ rmSync,
31
+ statSync,
32
+ writeFileSync
33
+ } from "fs";
34
+ import { homedir } from "os";
35
+ import { resolve, join as join2, dirname } from "path";
36
+ import { fileURLToPath } from "url";
37
+ import { exec } from "child_process";
38
+
39
+ // src/cli/model-catalog.ts
40
+ var MODELS = [
41
+ { id: "anthropic/claude-sonnet-5", name: "Claude Sonnet 5", provider: "Anthropic" },
42
+ { id: "anthropic/claude-opus-4.8", name: "Claude Opus 4.8", provider: "Anthropic" },
43
+ { id: "anthropic/claude-haiku-4", name: "Claude Haiku 4", provider: "Anthropic" },
44
+ { id: "openai/gpt-5.5", name: "GPT 5.5", provider: "OpenAI" },
45
+ { id: "openai/gpt-5", name: "GPT 5", provider: "OpenAI" },
46
+ { id: "openai/gpt-4.5", name: "GPT 4.5", provider: "OpenAI" },
47
+ { id: "openai/gpt-4o", name: "GPT 4o", provider: "OpenAI" },
48
+ { id: "openai/o3", name: "o3", provider: "OpenAI" },
49
+ { id: "openai/o4-mini", name: "o4-mini", provider: "OpenAI" },
50
+ { id: "google/gemini-2.5-pro", name: "Gemini 2.5 Pro", provider: "Google" },
51
+ { id: "google/gemini-2.5-flash", name: "Gemini 2.5 Flash", provider: "Google" },
52
+ { id: "google/gemini-2.0-flash", name: "Gemini 2.0 Flash", provider: "Google" },
53
+ { id: "meta/llama-4", name: "Llama 4", provider: "Meta" },
54
+ { id: "meta/llama-3.3-70b", name: "Llama 3.3 70B", provider: "Meta" },
55
+ { id: "meta/llama-3.1-405b", name: "Llama 3.1 405B", provider: "Meta" },
56
+ { id: "mistral/mistral-large-4", name: "Mistral Large 4", provider: "Mistral" },
57
+ { id: "mistral/mistral-small-4", name: "Mistral Small 4", provider: "Mistral" },
58
+ { id: "deepseek/deepseek-v4", name: "DeepSeek V4", provider: "DeepSeek" },
59
+ { id: "deepseek/deepseek-r1", name: "DeepSeek R1", provider: "DeepSeek" },
60
+ { id: "groq/llama-3.3-70b-versatile", name: "Llama 3.3 70B Versatile", provider: "Groq" },
61
+ { id: "groq/llama-3.1-8b-instant", name: "Llama 3.1 8B Instant", provider: "Groq" }
62
+ ];
63
+ async function fetchModelCatalog() {
64
+ await new Promise((r) => setTimeout(r, 600 + Math.random() * 400));
65
+ return MODELS;
66
+ }
67
+
68
+ // src/cli/tui/diagnostics.ts
69
+ import { existsSync, readFileSync } from "fs";
70
+ import { join } from "path";
71
+ var PROVIDER_KEY_MAP = {
72
+ openai: "OPENAI_API_KEY",
73
+ anthropic: "ANTHROPIC_API_KEY",
74
+ groq: "GROQ_API_KEY",
75
+ deepseek: "DEEPSEEK_API_KEY",
76
+ mistral: "MISTRAL_API_KEY",
77
+ google: "GOOGLE_API_KEY",
78
+ meta: "TOGETHER_API_KEY"
79
+ };
80
+ function providerEnvVar(modelId) {
81
+ const provider = modelId.split("/")[0];
82
+ if (!provider) return void 0;
83
+ const envVar = PROVIDER_KEY_MAP[provider];
84
+ return envVar ? { provider, envVar } : void 0;
85
+ }
86
+ function isProviderKeySet(agentDir, envVar) {
87
+ if (process.env[envVar] && process.env[envVar] !== "") return true;
88
+ const envPath = join(agentDir, "..", ".env.local");
89
+ if (!existsSync(envPath)) return false;
90
+ const content = readFileSync(envPath, "utf-8");
91
+ for (const line of content.split("\n")) {
92
+ const trimmed = line.trim();
93
+ if (trimmed.startsWith(`${envVar}=`) && !trimmed.startsWith(`#`)) return true;
94
+ }
95
+ return false;
96
+ }
97
+ function checkProviderKeys(agentDir, modelId) {
98
+ const results = [];
99
+ const info = providerEnvVar(modelId);
100
+ if (info === void 0) return results;
101
+ const { provider, envVar } = info;
102
+ const envPath = join(agentDir, "..", ".env.local");
103
+ let hasKey = false;
104
+ if (existsSync(envPath)) {
105
+ const content = readFileSync(envPath, "utf-8");
106
+ for (const line of content.split("\n")) {
107
+ const trimmed = line.trim();
108
+ if (trimmed.startsWith(`${envVar}=`) && !trimmed.startsWith(`#`)) {
109
+ hasKey = true;
110
+ break;
111
+ }
112
+ }
113
+ }
114
+ const inProcess = process.env[envVar] && process.env[envVar] !== "";
115
+ if (!hasKey && !inProcess) {
116
+ results.push({
117
+ severity: "warn",
118
+ message: `Missing ${envVar} for provider: ${provider}`,
119
+ fix: `Add ${envVar}=your_key_here to .env.local`
120
+ });
121
+ } else {
122
+ results.push({
123
+ severity: "info",
124
+ message: `${envVar} is configured`
125
+ });
126
+ }
127
+ return results;
128
+ }
129
+
130
+ // src/cli/setup/select-input.ts
131
+ function createSelectState(options, init = {}) {
132
+ const query = init.query ?? "";
133
+ const matches = filterOptions(options, query);
134
+ const initial = init.initialValue;
135
+ const selectedIndex = initial === void 0 ? 0 : Math.max(
136
+ 0,
137
+ matches.findIndex((option) => option.value === initial)
138
+ );
139
+ return { options, matches, selectedIndex, query };
140
+ }
141
+ function setQuery(state, query) {
142
+ if (query === state.query) return state;
143
+ const matches = filterOptions(state.options, query);
144
+ const carried = state.matches[state.selectedIndex];
145
+ const carriedIndex = carried === void 0 ? -1 : matches.findIndex((option) => option.value === carried.value);
146
+ return {
147
+ options: state.options,
148
+ matches,
149
+ selectedIndex: carriedIndex >= 0 ? carriedIndex : 0,
150
+ query
151
+ };
152
+ }
153
+ function moveUp(state) {
154
+ const count = state.matches.length;
155
+ if (count === 0) return state;
156
+ const selectedIndex = (state.selectedIndex - 1 + count) % count;
157
+ return { ...state, selectedIndex };
158
+ }
159
+ function moveDown(state) {
160
+ const count = state.matches.length;
161
+ if (count === 0) return state;
162
+ const selectedIndex = (state.selectedIndex + 1) % count;
163
+ return { ...state, selectedIndex };
164
+ }
165
+ function selected(state) {
166
+ return state.matches[state.selectedIndex];
167
+ }
168
+ function filterOptions(options, query) {
169
+ const normalized = query.trim().toLowerCase();
170
+ if (normalized.length === 0) return options;
171
+ return options.filter((option) => optionMatches(option, normalized));
172
+ }
173
+ function optionMatches(option, query) {
174
+ if (option.label.toLowerCase().includes(query)) return true;
175
+ if (option.description !== void 0 && option.description.toLowerCase().includes(query)) {
176
+ return true;
177
+ }
178
+ return false;
179
+ }
180
+
181
+ // src/cli/setup/text-input.ts
182
+ function createTextInputState(options) {
183
+ const line = options.defaultValue ? lineOf(options.defaultValue) : EMPTY_LINE;
184
+ const state = {
185
+ line,
186
+ message: options.message,
187
+ mask: options.mask ?? false
188
+ };
189
+ const result = { ...state };
190
+ if (options.placeholder !== void 0) result.placeholder = options.placeholder;
191
+ if (options.validate !== void 0) result.validate = options.validate;
192
+ return result;
193
+ }
194
+ function applyTextInputKey(state, key) {
195
+ const nextLine = applyLineEditorKey(state.line, key, { multiline: false });
196
+ if (nextLine === void 0) return void 0;
197
+ const next = { ...state, line: nextLine };
198
+ if (state.validationError !== void 0) {
199
+ next.validationError = void 0;
200
+ }
201
+ return next;
202
+ }
203
+ function validateTextInput(state) {
204
+ if (state.validate === void 0) return void 0;
205
+ const error = state.validate(state.line.text);
206
+ if (error === void 0) return void 0;
207
+ return { ...state, validationError: error };
208
+ }
209
+
210
+ // src/cli/setup/setup-panel.ts
211
+ var MAX_VISIBLE_OPTIONS = 10;
212
+ function renderTextPanel(state, theme, width) {
213
+ const c = theme.colors;
214
+ const rows = [renderHeader(state.message, theme, width)];
215
+ const promptGlyph = c.cyan(theme.glyph.prompt);
216
+ const promptWidth = visibleLength(`${theme.glyph.prompt} `);
217
+ const budget = Math.max(1, width - promptWidth - 2);
218
+ const displayLine = state.mask ? maskLine(state.line) : state.line;
219
+ const view = visibleLine(displayLine, budget);
220
+ const under = view.under.length === 0 ? " " : view.under;
221
+ const inputRow = ` ${promptGlyph} ${view.before}${c.inverse(under)}${view.after}`;
222
+ if (state.line.text.length === 0 && state.placeholder !== void 0) {
223
+ const placeholder = c.dim(sliceVisible(state.placeholder, budget));
224
+ rows.push(` ${promptGlyph} ${c.inverse(" ")}${placeholder}`);
225
+ } else {
226
+ rows.push(inputRow);
227
+ }
228
+ if (state.validationError !== void 0) {
229
+ rows.push(` ${c.red(theme.glyph.error)} ${c.red(state.validationError)}`);
230
+ }
231
+ const caretCol = 3 + promptWidth + inputTextWidth(state.mask ? maskLine(state.line).text.slice(0, maskLine(state.line).cursor) : view.before);
232
+ return { rows, caret: { row: 2, col: caretCol } };
233
+ }
234
+ function renderSelectPanel(state, message, theme, width, options = {}) {
235
+ const rows = [renderHeader(message, theme, width)];
236
+ let caretRow = 1;
237
+ let caretCol = 3;
238
+ if (options.searchable === true) {
239
+ const budget = Math.max(1, width - 4);
240
+ const [filterRow, cursorCol] = renderFilterRow(state.query, budget, theme, options.placeholder);
241
+ rows.push(filterRow);
242
+ caretRow = 2;
243
+ caretCol = cursorCol;
244
+ }
245
+ rows.push(...renderOptionRows(state, theme, width));
246
+ return { rows, caret: { row: caretRow, col: caretCol } };
247
+ }
248
+ function renderHeader(message, theme, width) {
249
+ const c = theme.colors;
250
+ const glyph = c.yellow(c.bold(theme.glyph.question));
251
+ const trimmed = message.trim();
252
+ const budget = Math.max(1, width - 2);
253
+ return `${glyph} ${c.bold(sliceVisible(trimmed, budget))}`;
254
+ }
255
+ function renderFilterRow(query, budget, theme, placeholder) {
256
+ const c = theme.colors;
257
+ const prefix = ` ${c.dim("/")} `;
258
+ const prefixWidth = visibleLength(prefix);
259
+ const empty = query.length === 0;
260
+ const visible = empty ? placeholder !== void 0 ? c.dim(sliceVisible(placeholder, budget)) : c.dim(sliceVisible("type to filter", budget)) : sliceVisible(query, budget);
261
+ const row = `${prefix}${empty ? c.inverse(" ") : `${visible}${c.inverse(" ")}`}`;
262
+ const caretCol = 1 + prefixWidth + (empty ? 0 : visibleLength(visible));
263
+ return [row, caretCol];
264
+ }
265
+ function renderOptionRows(state, theme, width) {
266
+ const c = theme.colors;
267
+ if (state.matches.length === 0) {
268
+ return [` ${c.dim("no matches")}`];
269
+ }
270
+ const count = state.matches.length;
271
+ const viewSize = Math.min(count, MAX_VISIBLE_OPTIONS);
272
+ const start = Math.max(
273
+ 0,
274
+ Math.min(state.selectedIndex - Math.floor(viewSize / 2), count - viewSize)
275
+ );
276
+ const end = Math.min(start + viewSize, count);
277
+ const visible = state.matches.slice(start, end);
278
+ const labelWidth = Math.min(
279
+ Math.max(...visible.map((option) => visibleLength(option.label))),
280
+ Math.max(8, Math.floor(width * 0.5))
281
+ );
282
+ return visible.map((option, offset) => {
283
+ const isCursor = start + offset === state.selectedIndex;
284
+ const pointer = isCursor ? c.cyan(theme.glyph.selectedPointer) : " ";
285
+ const label = padLabel(option.label, labelWidth);
286
+ const styledLabel = isCursor ? c.bold(label) : label;
287
+ const description = option.description !== void 0 ? ` ${c.dim(option.description)}` : "";
288
+ return `${pointer} ${styledLabel}${description}`;
289
+ });
290
+ }
291
+ function padLabel(label, width) {
292
+ const visible = visibleLength(label);
293
+ if (visible >= width) return sliceVisible(label, width);
294
+ return `${label}${" ".repeat(width - visible)}`;
295
+ }
296
+
297
+ // src/cli/setup/tui-prompter.ts
298
+ function createTuiPrompter(options = {}) {
299
+ return new TuiPrompter(options);
300
+ }
301
+ var TuiPrompter = class {
302
+ #region;
303
+ #theme;
304
+ #spinnerIntervalMs;
305
+ #detachKeys;
306
+ #spinnerTimer;
307
+ #spinnerFrame = 0;
308
+ #spinnerMessage = "";
309
+ #stopped = false;
310
+ constructor(options) {
311
+ this.#theme = options.theme ?? createTheme({ unicode: detectUnicode() });
312
+ this.#region = new LiveRegion({ output: options.output });
313
+ this.#spinnerIntervalMs = options.spinnerIntervalMs ?? 90;
314
+ }
315
+ get log() {
316
+ return {
317
+ info: (message) => this.#writeTagged("info", message, "info"),
318
+ success: (message) => this.#writeTagged("ok", message, "success"),
319
+ warning: (message) => this.#writeTagged("warn", message, "warning"),
320
+ error: (message) => this.#writeTagged("error", message, "danger")
321
+ };
322
+ }
323
+ section(title, lines) {
324
+ const c = this.#theme.colors;
325
+ const rows = [c.bold(c.cyan(title))];
326
+ for (const line of lines) rows.push(` ${c.dim(line)}`);
327
+ this.#region.commit(rows);
328
+ }
329
+ spinner(message) {
330
+ this.#stopSpinner();
331
+ this.#spinnerMessage = message;
332
+ this.#spinnerFrame = 0;
333
+ this.#paintSpinner();
334
+ this.#spinnerTimer = setInterval(() => {
335
+ this.#spinnerFrame += 1;
336
+ this.#paintSpinner();
337
+ }, this.#spinnerIntervalMs);
338
+ this.#spinnerTimer.unref?.();
339
+ return {
340
+ update: (next) => {
341
+ this.#spinnerMessage = next;
342
+ this.#paintSpinner();
343
+ },
344
+ stop: (outcome = { kind: "silent" }) => {
345
+ this.#stopSpinner();
346
+ this.#region.paint([], { row: 1, col: 1 });
347
+ if (outcome.kind === "silent") return;
348
+ this.#writeTagged(
349
+ outcome.kind === "success" ? "ok" : outcome.kind === "warning" ? "warn" : "error",
350
+ outcome.message ?? this.#spinnerMessage,
351
+ outcome.kind === "success" ? "success" : outcome.kind === "warning" ? "warning" : "danger"
352
+ );
353
+ }
354
+ };
355
+ }
356
+ async text(options) {
357
+ this.#stopSpinner();
358
+ return this.#runPrompt((resolve2) => {
359
+ let state = createTextInputState(options);
360
+ const paint = () => this.#paintFrame(this.#textFrame(state));
361
+ paint();
362
+ return (key) => {
363
+ if (key.type === "ctrl-c" || key.type === "escape") {
364
+ resolve2(void 0);
365
+ return "end";
366
+ }
367
+ if (key.type === "enter") {
368
+ const failed = validateTextInput(state);
369
+ if (failed !== void 0) {
370
+ state = failed;
371
+ paint();
372
+ return "continue";
373
+ }
374
+ resolve2(state.line.text);
375
+ return "end";
376
+ }
377
+ const next = applyTextInputKey(state, key);
378
+ if (next !== void 0) {
379
+ state = next;
380
+ paint();
381
+ }
382
+ return "continue";
383
+ };
384
+ });
385
+ }
386
+ async select(options) {
387
+ return this.#selectImpl(options, false);
388
+ }
389
+ async searchableSelect(options) {
390
+ return this.#selectImpl(options, true);
391
+ }
392
+ async acknowledge(options) {
393
+ this.#stopSpinner();
394
+ await this.#runPrompt((resolve2) => {
395
+ const c = this.#theme.colors;
396
+ const rows = [`${c.yellow(c.bold(this.#theme.glyph.question))} ${c.bold(options.message)}`];
397
+ for (const line of options.lines ?? []) rows.push(` ${c.dim(line)}`);
398
+ rows.push(` ${c.dim("press enter to continue")}`);
399
+ this.#region.paint(rows, { row: rows.length, col: 1 });
400
+ return (key) => {
401
+ if (key.type === "ctrl-c" || key.type === "escape" || key.type === "enter") {
402
+ resolve2();
403
+ return "end";
404
+ }
405
+ return "continue";
406
+ };
407
+ });
408
+ }
409
+ stop() {
410
+ if (this.#stopped) return;
411
+ this.#stopped = true;
412
+ this.#stopSpinner();
413
+ this.#detachKeys?.();
414
+ this.#detachKeys = void 0;
415
+ this.#region.stop();
416
+ }
417
+ async #selectImpl(options, searchable) {
418
+ this.#stopSpinner();
419
+ return this.#runPrompt((resolve2) => {
420
+ let state = createSelectState(options.options, {
421
+ initialValue: options.initialValue
422
+ });
423
+ const placeholder = "placeholder" in options ? options.placeholder : void 0;
424
+ const paint = () => this.#paintFrame(
425
+ renderSelectPanel(state, options.message, this.#theme, this.#region.columns, {
426
+ searchable,
427
+ placeholder
428
+ })
429
+ );
430
+ paint();
431
+ return (key) => {
432
+ if (key.type === "ctrl-c" || key.type === "escape") {
433
+ resolve2(void 0);
434
+ return "end";
435
+ }
436
+ if (key.type === "enter") {
437
+ const chosen = selected(state);
438
+ if (chosen === void 0) return "continue";
439
+ resolve2(chosen.value);
440
+ return "end";
441
+ }
442
+ if (key.type === "up" || key.type === "ctrl-p") {
443
+ state = moveUp(state);
444
+ paint();
445
+ return "continue";
446
+ }
447
+ if (key.type === "down" || key.type === "ctrl-n") {
448
+ state = moveDown(state);
449
+ paint();
450
+ return "continue";
451
+ }
452
+ if (searchable) {
453
+ if (key.type === "backspace") {
454
+ state = setQuery(state, state.query.slice(0, -1));
455
+ paint();
456
+ return "continue";
457
+ }
458
+ if (key.type === "text") {
459
+ state = setQuery(state, `${state.query}${key.value}`);
460
+ paint();
461
+ return "continue";
462
+ }
463
+ const draft = applyLineEditorKey({ text: state.query, cursor: state.query.length }, key, {
464
+ multiline: false
465
+ });
466
+ if (draft !== void 0 && draft.text !== state.query) {
467
+ state = setQuery(state, draft.text);
468
+ paint();
469
+ }
470
+ }
471
+ return "continue";
472
+ };
473
+ });
474
+ }
475
+ #runPrompt(setup) {
476
+ return new Promise((resolve2) => {
477
+ let done = false;
478
+ const detach = attachKeyStream((key) => {
479
+ if (done) return;
480
+ const outcome = onKey(key);
481
+ if (outcome === "end") {
482
+ done = true;
483
+ this.#detachKeys?.();
484
+ this.#detachKeys = void 0;
485
+ this.#region.paint([], { row: 1, col: 1 });
486
+ }
487
+ });
488
+ this.#detachKeys = detach;
489
+ const onKey = setup((value) => resolve2(value));
490
+ });
491
+ }
492
+ #textFrame(state) {
493
+ return renderTextPanel(state, this.#theme, this.#region.columns);
494
+ }
495
+ #paintFrame(frame) {
496
+ this.#region.paint(frame.rows, frame.caret);
497
+ }
498
+ #paintSpinner() {
499
+ const glyph = this.#theme.spinner[this.#spinnerFrame % this.#theme.spinner.length] ?? "-";
500
+ const row = `${this.#theme.colors.cyan(glyph)} ${this.#spinnerMessage}`;
501
+ this.#region.paint([row], { row: 1, col: 1 });
502
+ }
503
+ #stopSpinner() {
504
+ if (this.#spinnerTimer !== void 0) {
505
+ clearInterval(this.#spinnerTimer);
506
+ this.#spinnerTimer = void 0;
507
+ }
508
+ }
509
+ #writeTagged(tag, message, tone) {
510
+ const themeAdapter = {
511
+ accent: this.#theme.colors.cyan,
512
+ color: this.#theme.color,
513
+ danger: this.#theme.colors.red,
514
+ heading: (t) => this.#theme.colors.bold(this.#theme.colors.cyan(t)),
515
+ info: this.#theme.colors.blue,
516
+ label: this.#theme.colors.bold,
517
+ muted: this.#theme.colors.dim,
518
+ plain: (t) => t,
519
+ subagent: this.#theme.colors.orange,
520
+ success: this.#theme.colors.green,
521
+ warning: this.#theme.colors.yellow
522
+ };
523
+ this.#region.commit([
524
+ renderCliTaggedLine(themeAdapter, { message, tag, tone })
525
+ ]);
526
+ }
527
+ };
528
+
529
+ // src/cli/init.ts
530
+ var __dirname = dirname(fileURLToPath(import.meta.url));
531
+ function resolveTemplatesDir() {
532
+ let current = __dirname;
533
+ for (let i = 0; i < 6; i += 1) {
534
+ const candidate = resolve(current, "templates");
535
+ if (existsSync2(candidate)) return candidate;
536
+ const parent = dirname(current);
537
+ if (parent === current) break;
538
+ current = parent;
539
+ }
540
+ return resolve(__dirname, "../../templates");
541
+ }
542
+ var TEMPLATES_DIR = resolveTemplatesDir();
543
+ function copyTemplate(src, dest) {
544
+ for (const entry of collectFiles(src)) {
545
+ const relative = entry.replace(src, "").replace(/^\//, "");
546
+ const destPath = join2(dest, relative);
547
+ if (entry.endsWith(".gitkeep")) {
548
+ const parentDir = destPath.replace("/.gitkeep", "");
549
+ mkdirSync(parentDir, { recursive: true });
550
+ writeFileSync(join2(parentDir, ".gitkeep"), "");
551
+ continue;
552
+ }
553
+ mkdirSync(dirname(destPath), { recursive: true });
554
+ copyFileSync(entry, destPath);
555
+ }
556
+ }
557
+ function collectFiles(dir) {
558
+ const results = [];
559
+ for (const entry of readdirSync(dir)) {
560
+ const full = join2(dir, entry);
561
+ if (statSync(full).isDirectory()) results.push(...collectFiles(full));
562
+ else results.push(full);
563
+ }
564
+ return results;
565
+ }
566
+ function updatePackageJson(dir, name) {
567
+ const pkgPath = join2(dir, "package.json");
568
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
569
+ pkg.name = name;
570
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
571
+ }
572
+ function detectEnvKey() {
573
+ return process.env.CENCORI_API_KEY ?? null;
574
+ }
575
+ function uncommentEnvLine(envPath, key, value) {
576
+ const content = readFileSync2(envPath, "utf-8");
577
+ const lines = content.split("\n");
578
+ const updated = lines.map((line) => {
579
+ const trimmed = line.trim();
580
+ if (trimmed.startsWith(`# ${key}=`) || trimmed.startsWith(`${key}=`)) {
581
+ return `${key}=${value}`;
582
+ }
583
+ return line;
584
+ });
585
+ if (updated.join("\n") === content) updated.push(`${key}=${value}`);
586
+ writeFileSync(envPath, updated.join("\n") + "\n");
587
+ }
588
+ function writeAgentModel(agentDir, model) {
589
+ const agentFile = join2(agentDir, "agent/agent.ts");
590
+ const content = readFileSync2(agentFile, "utf-8");
591
+ const updated = content.replace(/model:\s*"[^"]+"/, `model: "${model}"`);
592
+ writeFileSync(agentFile, updated);
593
+ }
594
+ function runNpmInstall(cwd) {
595
+ return new Promise((resolve2, reject) => {
596
+ const proc = exec("npm install", { cwd });
597
+ proc.once("exit", (code) => code === 0 ? resolve2() : reject(new Error(`npm exit ${code}`)));
598
+ proc.once("error", reject);
599
+ });
600
+ }
601
+ var IGNORED_DIRECTORY_ENTRIES = /* @__PURE__ */ new Set([".DS_Store", ".git", ".gitkeep"]);
602
+ function directoryHasMeaningfulContent(dir) {
603
+ try {
604
+ return readdirSync(dir).some((entry) => !IGNORED_DIRECTORY_ENTRIES.has(entry));
605
+ } catch {
606
+ return false;
607
+ }
608
+ }
609
+ function looksLikeArcieProject(dir) {
610
+ return existsSync2(join2(dir, "agent", "agent.ts"));
611
+ }
612
+ function assertSafeToClear(dir) {
613
+ const absolute = resolve(dir);
614
+ const home = homedir();
615
+ if (absolute === "/" || absolute === home) {
616
+ throw new Error(`Refusing to clear ${absolute}: too dangerous`);
617
+ }
618
+ const parts = absolute.split("/").filter((part) => part.length > 0);
619
+ if (parts.length < 2) {
620
+ throw new Error(`Refusing to clear ${absolute}: not enough path depth`);
621
+ }
622
+ const dangerousFirst = /* @__PURE__ */ new Set(["etc", "var", "usr", "bin", "sbin", "System", "Library"]);
623
+ if (parts.length === 2 && dangerousFirst.has(parts[0])) {
624
+ throw new Error(`Refusing to clear ${absolute}: system path`);
625
+ }
626
+ }
627
+ function clearDirectoryContents(dir) {
628
+ assertSafeToClear(dir);
629
+ for (const entry of readdirSync(dir)) {
630
+ rmSync(join2(dir, entry), { recursive: true, force: true });
631
+ }
632
+ }
633
+ async function initCommand(name, options) {
634
+ const targetDir = name ? resolve(process.cwd(), name) : resolve(process.cwd(), ".");
635
+ const templateDir = resolve(TEMPLATES_DIR, options.template);
636
+ if (!existsSync2(templateDir)) {
637
+ console.error(` Template not found: ${options.template}`);
638
+ process.exit(1);
639
+ }
640
+ const prompter = createTuiPrompter();
641
+ try {
642
+ let mode = "fresh";
643
+ if (existsSync2(targetDir) && directoryHasMeaningfulContent(targetDir)) {
644
+ const decision = await resolveExistingDirectory(prompter, targetDir);
645
+ if (decision === "cancel") return;
646
+ if (decision === "overwrite") {
647
+ try {
648
+ clearDirectoryContents(targetDir);
649
+ prompter.log.warning(`Cleared ${targetDir}`);
650
+ } catch (err) {
651
+ prompter.log.error(err instanceof Error ? err.message : String(err));
652
+ return;
653
+ }
654
+ } else {
655
+ mode = "resume";
656
+ }
657
+ }
658
+ await runInit(prompter, { targetDir, templateDir, name, mode });
659
+ } finally {
660
+ prompter.stop();
661
+ }
662
+ }
663
+ async function maybeScaffoldWebChat(prompter, targetDir) {
664
+ const choice = await prompter.select({
665
+ message: "Add a web chat UI now? (Next.js + shadcn under channels/web)",
666
+ options: [
667
+ { value: "yes", label: "Yes \u2014 scaffold channels/web now" },
668
+ { value: "no", label: "No \u2014 I'll add it later with `arcie channels add web`" }
669
+ ]
670
+ });
671
+ if (choice !== "yes") return;
672
+ try {
673
+ const result = scaffoldWebChat(targetDir);
674
+ if (result.alreadyExisted) {
675
+ prompter.log.info(`channels/web already exists \u2014 left it untouched`);
676
+ return;
677
+ }
678
+ prompter.log.success(`Scaffolded ${result.targetPath}`);
679
+ prompter.section("Next steps", [
680
+ `cd ${result.targetPath.replace(process.cwd() + "/", "")}`,
681
+ "npm install",
682
+ "cp .env.local.example .env.local",
683
+ "npm run dev"
684
+ ]);
685
+ } catch (err) {
686
+ prompter.log.error(err instanceof Error ? err.message : String(err));
687
+ }
688
+ }
689
+ async function maybePromptProviderKey(prompter, targetDir, modelId) {
690
+ if (process.env.CENCORI_API_KEY) return;
691
+ const info = providerEnvVar(modelId);
692
+ if (info === void 0) return;
693
+ if (isProviderKeySet(targetDir, info.envVar)) return;
694
+ const key = await prompter.text({
695
+ message: `Paste your ${info.envVar} for ${info.provider} (optional \u2014 skip with Enter)`,
696
+ mask: true
697
+ });
698
+ if (key === void 0 || key.length === 0) return;
699
+ const envPath = join2(targetDir, ".env.local");
700
+ uncommentEnvLine(envPath, info.envVar, key);
701
+ process.env[info.envVar] = key;
702
+ prompter.log.success(`Wrote ${info.envVar} to .env.local`);
703
+ }
704
+ async function resolveExistingDirectory(prompter, targetDir) {
705
+ const looksArcie = looksLikeArcieProject(targetDir);
706
+ const message = looksArcie ? `${targetDir} already contains an arcie project.` : `${targetDir} is not empty.`;
707
+ const options = looksArcie ? [
708
+ {
709
+ value: "resume",
710
+ label: "Resume \u2014 keep files, reconfigure model + API key"
711
+ },
712
+ {
713
+ value: "overwrite",
714
+ label: "Overwrite \u2014 delete existing files and scaffold fresh"
715
+ },
716
+ { value: "cancel", label: "Cancel" }
717
+ ] : [
718
+ {
719
+ value: "overwrite",
720
+ label: "Overwrite \u2014 delete existing files and scaffold fresh"
721
+ },
722
+ {
723
+ value: "resume",
724
+ label: "Keep existing files and reconfigure"
725
+ },
726
+ { value: "cancel", label: "Cancel" }
727
+ ];
728
+ const choice = await prompter.select({ message, options });
729
+ return choice ?? "cancel";
730
+ }
731
+ async function runInit(prompter, input) {
732
+ const { targetDir, templateDir, name, mode } = input;
733
+ const needsScaffold = mode === "fresh" || !looksLikeArcieProject(targetDir);
734
+ if (needsScaffold) {
735
+ const scaffold = prompter.spinner(`Creating agent in ${targetDir}`);
736
+ copyTemplate(templateDir, targetDir);
737
+ if (name) updatePackageJson(targetDir, name);
738
+ scaffold.stop({ kind: "success", message: `Created agent in ${targetDir}` });
739
+ } else {
740
+ prompter.log.info(`Resuming setup for ${targetDir}`);
741
+ }
742
+ const nodeModules = join2(targetDir, "node_modules");
743
+ if (existsSync2(nodeModules)) {
744
+ prompter.log.info("Dependencies already installed");
745
+ } else {
746
+ const install = prompter.spinner("Installing dependencies");
747
+ try {
748
+ await runNpmInstall(targetDir);
749
+ install.stop({ kind: "success", message: "Installed dependencies" });
750
+ } catch {
751
+ install.stop({ kind: "warning", message: "Dependency install skipped" });
752
+ }
753
+ }
754
+ if (detectEnvKey() === null) {
755
+ const key = await prompter.text({
756
+ message: "Paste your CENCORI_API_KEY (optional \u2014 skip with Enter)",
757
+ mask: true
758
+ });
759
+ if (key !== void 0 && key.length > 0) {
760
+ uncommentEnvLine(join2(targetDir, ".env.local"), "CENCORI_API_KEY", key);
761
+ process.env.CENCORI_API_KEY = key;
762
+ prompter.log.success("Wrote CENCORI_API_KEY to .env.local");
763
+ }
764
+ }
765
+ const catalogSpinner = prompter.spinner("Loading model catalog");
766
+ const catalog = await fetchModelCatalog();
767
+ catalogSpinner.stop();
768
+ const modelId = await prompter.searchableSelect({
769
+ message: "What model would you like to use?",
770
+ placeholder: "type to filter\u2026",
771
+ options: catalog.map((model) => ({
772
+ value: model.id,
773
+ label: model.name,
774
+ description: model.provider
775
+ }))
776
+ });
777
+ if (modelId === void 0) return;
778
+ writeAgentModel(targetDir, modelId);
779
+ prompter.log.success(`Set model to ${modelId}`);
780
+ await maybePromptProviderKey(prompter, targetDir, modelId);
781
+ const diagnostics = checkProviderKeys(targetDir, modelId);
782
+ if (diagnostics.length > 0) {
783
+ prompter.section(
784
+ "Checks",
785
+ diagnostics.map(
786
+ (d) => `${d.severity === "info" ? "\u2713" : "\u26A0"} ${d.message}${d.fix ? ` \u2014 ${d.fix}` : ""}`
787
+ )
788
+ );
789
+ }
790
+ await maybeScaffoldWebChat(prompter, targetDir);
791
+ const action = await prompter.select({
792
+ message: "How would you like to continue?",
793
+ options: [
794
+ { value: "chat", label: `Start arcie dev \u2014 talk to '${name ?? "."}' in your terminal` },
795
+ { value: "server", label: "Start arcie dev server" },
796
+ { value: "code", label: "Open in editor" }
797
+ ]
798
+ });
799
+ if (action === void 0) return;
800
+ switch (action) {
801
+ case "chat": {
802
+ const { devCommand } = await import("./dev-ZZDVOWUZ.js");
803
+ prompter.stop();
804
+ await devCommand({ port: "3000", agentDir: targetDir, input: true });
805
+ break;
806
+ }
807
+ case "server": {
808
+ const { devCommand } = await import("./dev-ZZDVOWUZ.js");
809
+ prompter.stop();
810
+ await devCommand({ port: "3000", agentDir: targetDir, input: false });
811
+ break;
812
+ }
813
+ case "code": {
814
+ prompter.log.info(`cd ${name ?? "."} && code .`);
815
+ break;
816
+ }
817
+ }
818
+ }
819
+ export {
820
+ initCommand
821
+ };
822
+ //# sourceMappingURL=init-5F7CIVLN.js.map