@action-llama/action-llama 0.1.4 → 0.2.0

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 (126) hide show
  1. package/README.md +63 -59
  2. package/dist/agents/container-entry.js +36 -9
  3. package/dist/agents/container-entry.js.map +1 -1
  4. package/dist/agents/prompt.d.ts.map +1 -1
  5. package/dist/agents/prompt.js +17 -6
  6. package/dist/agents/prompt.js.map +1 -1
  7. package/dist/cli/commands/new.d.ts.map +1 -1
  8. package/dist/cli/commands/new.js +5 -25
  9. package/dist/cli/commands/new.js.map +1 -1
  10. package/dist/cli/commands/{agent/add.d.ts → setup.d.ts} +1 -2
  11. package/dist/cli/commands/setup.d.ts.map +1 -0
  12. package/dist/cli/commands/setup.js +60 -0
  13. package/dist/cli/commands/setup.js.map +1 -0
  14. package/dist/cli/commands/start.d.ts.map +1 -1
  15. package/dist/cli/commands/start.js +3 -0
  16. package/dist/cli/commands/start.js.map +1 -1
  17. package/dist/cli/commands/status.d.ts.map +1 -1
  18. package/dist/cli/commands/status.js +1 -37
  19. package/dist/cli/commands/status.js.map +1 -1
  20. package/dist/cli/main.js +6 -9
  21. package/dist/cli/main.js.map +1 -1
  22. package/dist/credentials/builtins/anthropic-key.d.ts +4 -0
  23. package/dist/credentials/builtins/anthropic-key.d.ts.map +1 -0
  24. package/dist/credentials/builtins/anthropic-key.js +69 -0
  25. package/dist/credentials/builtins/anthropic-key.js.map +1 -0
  26. package/dist/credentials/builtins/github-token.d.ts +4 -0
  27. package/dist/credentials/builtins/github-token.d.ts.map +1 -0
  28. package/dist/credentials/builtins/github-token.js +19 -0
  29. package/dist/credentials/builtins/github-token.js.map +1 -0
  30. package/dist/credentials/builtins/github-webhook-secret.d.ts +4 -0
  31. package/dist/credentials/builtins/github-webhook-secret.d.ts.map +1 -0
  32. package/dist/credentials/builtins/github-webhook-secret.js +12 -0
  33. package/dist/credentials/builtins/github-webhook-secret.js.map +1 -0
  34. package/dist/credentials/builtins/id-rsa.d.ts +4 -0
  35. package/dist/credentials/builtins/id-rsa.d.ts.map +1 -0
  36. package/dist/credentials/builtins/id-rsa.js +67 -0
  37. package/dist/credentials/builtins/id-rsa.js.map +1 -0
  38. package/dist/credentials/builtins/index.d.ts +3 -0
  39. package/dist/credentials/builtins/index.d.ts.map +1 -0
  40. package/dist/credentials/builtins/index.js +15 -0
  41. package/dist/credentials/builtins/index.js.map +1 -0
  42. package/dist/credentials/builtins/sentry-client-secret.d.ts +4 -0
  43. package/dist/credentials/builtins/sentry-client-secret.d.ts.map +1 -0
  44. package/dist/credentials/builtins/sentry-client-secret.js +12 -0
  45. package/dist/credentials/builtins/sentry-client-secret.js.map +1 -0
  46. package/dist/credentials/builtins/sentry-token.d.ts +4 -0
  47. package/dist/credentials/builtins/sentry-token.d.ts.map +1 -0
  48. package/dist/credentials/builtins/sentry-token.js +72 -0
  49. package/dist/credentials/builtins/sentry-token.js.map +1 -0
  50. package/dist/credentials/prompter.d.ts +14 -0
  51. package/dist/credentials/prompter.d.ts.map +1 -0
  52. package/dist/credentials/prompter.js +65 -0
  53. package/dist/credentials/prompter.js.map +1 -0
  54. package/dist/credentials/registry.d.ts +16 -0
  55. package/dist/credentials/registry.d.ts.map +1 -0
  56. package/dist/credentials/registry.js +25 -0
  57. package/dist/credentials/registry.js.map +1 -0
  58. package/dist/credentials/schema.d.ts +23 -0
  59. package/dist/credentials/schema.d.ts.map +1 -0
  60. package/dist/credentials/schema.js +6 -0
  61. package/dist/credentials/schema.js.map +1 -0
  62. package/dist/scheduler/index.d.ts.map +1 -1
  63. package/dist/scheduler/index.js +14 -4
  64. package/dist/scheduler/index.js.map +1 -1
  65. package/dist/setup/prompts.d.ts +1 -43
  66. package/dist/setup/prompts.d.ts.map +1 -1
  67. package/dist/setup/prompts.js +43 -605
  68. package/dist/setup/prompts.js.map +1 -1
  69. package/dist/setup/scaffold.d.ts +1 -2
  70. package/dist/setup/scaffold.d.ts.map +1 -1
  71. package/dist/setup/scaffold.js +62 -29
  72. package/dist/setup/scaffold.js.map +1 -1
  73. package/dist/shared/config.d.ts +1 -2
  74. package/dist/shared/config.d.ts.map +1 -1
  75. package/dist/shared/config.js +16 -6
  76. package/dist/shared/config.js.map +1 -1
  77. package/dist/shared/credentials.d.ts +2 -0
  78. package/dist/shared/credentials.d.ts.map +1 -1
  79. package/dist/shared/credentials.js +17 -0
  80. package/dist/shared/credentials.js.map +1 -1
  81. package/dist/shared/paths.d.ts +0 -1
  82. package/dist/shared/paths.d.ts.map +1 -1
  83. package/dist/shared/paths.js +0 -3
  84. package/dist/shared/paths.js.map +1 -1
  85. package/dist/webhooks/definitions/github.d.ts +3 -0
  86. package/dist/webhooks/definitions/github.d.ts.map +1 -0
  87. package/dist/webhooks/definitions/github.js +38 -0
  88. package/dist/webhooks/definitions/github.js.map +1 -0
  89. package/dist/webhooks/definitions/registry.d.ts +4 -0
  90. package/dist/webhooks/definitions/registry.d.ts.map +1 -0
  91. package/dist/webhooks/definitions/registry.js +14 -0
  92. package/dist/webhooks/definitions/registry.js.map +1 -0
  93. package/dist/webhooks/definitions/schema.d.ts +19 -0
  94. package/dist/webhooks/definitions/schema.d.ts.map +1 -0
  95. package/dist/webhooks/definitions/schema.js +2 -0
  96. package/dist/webhooks/definitions/schema.js.map +1 -0
  97. package/dist/webhooks/definitions/sentry.d.ts +3 -0
  98. package/dist/webhooks/definitions/sentry.d.ts.map +1 -0
  99. package/dist/webhooks/definitions/sentry.js +22 -0
  100. package/dist/webhooks/definitions/sentry.js.map +1 -0
  101. package/dist/webhooks/providers/sentry.d.ts +9 -0
  102. package/dist/webhooks/providers/sentry.d.ts.map +1 -0
  103. package/dist/webhooks/providers/sentry.js +108 -0
  104. package/dist/webhooks/providers/sentry.js.map +1 -0
  105. package/dist/webhooks/registry.js +2 -2
  106. package/dist/webhooks/registry.js.map +1 -1
  107. package/dist/webhooks/types.d.ts +5 -1
  108. package/dist/webhooks/types.d.ts.map +1 -1
  109. package/package.json +4 -3
  110. package/dist/agents/definitions/dev/AGENTS.md +0 -44
  111. package/dist/agents/definitions/dev/config-definition.json +0 -39
  112. package/dist/agents/definitions/devops/AGENTS.md +0 -33
  113. package/dist/agents/definitions/devops/config-definition.json +0 -37
  114. package/dist/agents/definitions/loader.d.ts +0 -18
  115. package/dist/agents/definitions/loader.d.ts.map +0 -1
  116. package/dist/agents/definitions/loader.js +0 -59
  117. package/dist/agents/definitions/loader.js.map +0 -1
  118. package/dist/agents/definitions/reviewer/AGENTS.md +0 -37
  119. package/dist/agents/definitions/reviewer/config-definition.json +0 -24
  120. package/dist/agents/definitions/schema.d.ts +0 -38
  121. package/dist/agents/definitions/schema.d.ts.map +0 -1
  122. package/dist/agents/definitions/schema.js +0 -97
  123. package/dist/agents/definitions/schema.js.map +0 -1
  124. package/dist/cli/commands/agent/add.d.ts.map +0 -1
  125. package/dist/cli/commands/agent/add.js +0 -86
  126. package/dist/cli/commands/agent/add.js.map +0 -1
@@ -1,381 +1,65 @@
1
- import { input, select, checkbox, confirm } from "@inquirer/prompts";
2
- import { existsSync, readFileSync } from "fs";
3
- import { resolve } from "path";
4
- import { validateGitHubToken, validateSentryToken, validateSentryProjects, validateAnthropicApiKey, validateOAuthTokenFormat } from "./validators.js";
5
- import { loadCredential } from "../shared/credentials.js";
6
- import { CREDENTIALS_DIR } from "../shared/paths.js";
7
- import { listBuiltinDefinitions, loadDefinition } from "../agents/definitions/loader.js";
8
- // --- Shared: configure a single agent from a definition ---
9
- export async function configureAgent(definition, context) {
10
- // Agent name
11
- const name = await input({
12
- message: "Agent name:",
13
- default: definition.name,
14
- validate: (v) => {
15
- const trimmed = v.trim();
16
- if (!trimmed)
17
- return "Name is required";
18
- if (context.existingAgentNames?.includes(trimmed))
19
- return `Agent "${trimmed}" already exists`;
20
- return true;
21
- },
22
- });
23
- // Repos
24
- const repoChoices = context.availableRepos.map((r) => ({
25
- name: r.fullName,
26
- value: r.fullName,
27
- }));
28
- const repos = await checkbox({
29
- message: `Repos for ${name}:`,
30
- choices: repoChoices,
31
- validate: (v) => (v.length > 0 ? true : "Select at least one repo"),
32
- });
33
- // Credentials — handle required and optional
34
- const credentials = [...definition.credentials.required];
35
- let sentryToken;
36
- let sentryOrg;
37
- let sentryProjectSlugs = [];
38
- for (const cred of definition.credentials.optional) {
39
- if (cred === "sentry-token") {
40
- const result = await promptSentryCredential();
41
- sentryToken = result.sentryToken;
42
- sentryOrg = result.sentryOrg;
43
- sentryProjectSlugs = result.sentryProjectSlugs;
44
- if (sentryToken) {
45
- credentials.push("sentry-token");
46
- }
47
- }
48
- }
49
- // Params — prompt for each non-credential param; resolve credential params
50
- const params = {};
51
- for (const [key, paramDef] of Object.entries(definition.params)) {
52
- // Credential-linked params are populated by the credential handler above
53
- if (paramDef.credential === "sentry-token") {
54
- if (key === "sentryOrg" && sentryOrg) {
55
- params[key] = sentryOrg;
56
- }
57
- else if (key === "sentryProjects" && sentryProjectSlugs.length > 0) {
58
- params[key] = sentryProjectSlugs;
59
- }
60
- continue;
61
- }
62
- // Resolve special defaults
63
- let defaultValue = paramDef.default;
64
- if (defaultValue === "$githubUser") {
65
- defaultValue = context.githubUser;
66
- }
67
- if (paramDef.type === "string") {
68
- const value = await input({
69
- message: `${paramDef.description}:`,
70
- default: defaultValue,
71
- ...(paramDef.required ? { validate: (v) => v.trim().length > 0 ? true : `${key} is required` } : {}),
72
- });
73
- if (value.trim()) {
74
- params[key] = value.trim();
75
- }
76
- }
77
- else if (paramDef.type === "string[]") {
78
- const value = await input({
79
- message: `${paramDef.description} (comma-separated):`,
80
- default: defaultValue,
81
- });
82
- if (value.trim()) {
83
- params[key] = value.split(",").map((s) => s.trim()).filter(Boolean);
84
- }
85
- }
86
- }
87
- // Webhook trigger
88
- const useWebhooks = await confirm({
89
- message: `Listen for webhooks? (${definition.webhooks.description})`,
90
- default: true,
91
- });
92
- let webhooks;
93
- if (useWebhooks) {
94
- const filter = buildWebhookFilter(definition, repos, params);
95
- webhooks = { filters: [filter] };
96
- }
97
- // Schedule trigger
98
- const useSchedule = await confirm({
99
- message: "Also run on a schedule (polling)?",
100
- default: !useWebhooks,
101
- });
102
- let schedule;
103
- if (useSchedule) {
104
- schedule = await input({
105
- message: `${name} poll interval (cron):`,
106
- default: definition.defaultSchedule,
107
- });
108
- }
109
- // Select prompt based on trigger mode
110
- const prompt = useWebhooks ? definition.prompts.webhook : definition.prompts.schedule;
111
- // Webhook secret
112
- let githubWebhookSecret;
113
- if (useWebhooks) {
114
- const existingSecret = loadCredential("github-webhook-secret");
115
- if (!existingSecret) {
116
- githubWebhookSecret = (await input({
117
- message: "GitHub webhook secret (set this same value in your GitHub webhook settings):",
118
- validate: (v) => (v.trim().length > 0 ? true : "Secret is required to verify webhook payloads"),
119
- })).trim();
120
- }
1
+ import { select } from "@inquirer/prompts";
2
+ import { validateGitHubToken } from "./validators.js";
3
+ import { writeCredential, writeStructuredCredential } from "../shared/credentials.js";
4
+ import { resolveCredential } from "../credentials/registry.js";
5
+ import { promptCredential } from "../credentials/prompter.js";
6
+ /**
7
+ * Write credential values to disk based on the definition's field count.
8
+ * Single-field: plain text (backward compatible). Multi-field: JSON.
9
+ */
10
+ function writeCredentialValues(def, values) {
11
+ if (Object.keys(values).length === 0)
12
+ return; // e.g. pi_auth
13
+ if (def.fields.length === 1) {
14
+ const fieldName = def.fields[0].name;
15
+ writeCredential(def.filename, values[fieldName]);
121
16
  }
122
- // Build agent config
123
- const config = {
124
- name,
125
- credentials,
126
- model: context.modelConfig,
127
- prompt,
128
- repos,
129
- ...(schedule ? { schedule } : {}),
130
- ...(webhooks ? { webhooks } : {}),
131
- ...(Object.keys(params).length > 0 ? { params } : {}),
132
- };
133
- return {
134
- agent: { name, template: definition.name, config },
135
- secrets: { sentryToken, githubWebhookSecret },
136
- usesWebhooks: useWebhooks,
137
- };
138
- }
139
- // --- Build webhook filter from definition + params ---
140
- function buildWebhookFilter(definition, repos, params) {
141
- const filter = {
142
- source: "github",
143
- repos,
144
- events: definition.webhooks.events,
145
- actions: definition.webhooks.actions,
146
- };
147
- // Inject param values into filter via webhookFilter mappings
148
- for (const [key, paramDef] of Object.entries(definition.params)) {
149
- if (paramDef.webhookFilter && params[key] !== undefined) {
150
- const value = params[key];
151
- const field = paramDef.webhookFilter.field;
152
- if (paramDef.webhookFilter.wrap === "array") {
153
- filter[field] = [value];
154
- }
155
- else {
156
- filter[field] = value;
157
- }
158
- }
17
+ else {
18
+ writeStructuredCredential(def.filename, values);
159
19
  }
160
- return filter;
161
20
  }
162
- // --- Sentry credential handler ---
163
- async function promptSentryCredential() {
164
- let sentryToken;
165
- let sentryOrg;
166
- let sentryProjectSlugs = [];
167
- const existingSentryToken = loadCredential("sentry-token");
168
- if (existingSentryToken) {
169
- const reuse = await confirm({
170
- message: `Found existing Sentry token in ${CREDENTIALS_DIR}/sentry-token. Use it?`,
171
- default: true,
172
- });
173
- if (reuse) {
174
- sentryToken = existingSentryToken;
175
- }
176
- }
177
- if (!sentryToken) {
178
- const useSentry = await confirm({
179
- message: "Configure Sentry integration?",
180
- default: false,
181
- });
182
- if (useSentry) {
183
- sentryToken = (await input({
184
- message: "Sentry auth token:",
185
- validate: (v) => (v.trim().length > 0 ? true : "Token is required"),
186
- })).trim();
187
- }
188
- }
189
- if (sentryToken) {
190
- console.log("Validating Sentry token...");
191
- try {
192
- const { organizations } = await validateSentryToken(sentryToken);
193
- if (organizations.length === 0)
194
- throw new Error("No organizations found");
195
- if (organizations.length === 1) {
196
- sentryOrg = organizations[0].slug;
197
- console.log(`Organization: ${sentryOrg}\n`);
198
- }
199
- else {
200
- sentryOrg = await select({
201
- message: "Select Sentry organization:",
202
- choices: organizations.map((o) => ({ name: `${o.name} (${o.slug})`, value: o.slug })),
203
- });
204
- }
205
- const { projects } = await validateSentryProjects(sentryToken, sentryOrg);
206
- if (projects.length > 0) {
207
- sentryProjectSlugs = await checkbox({
208
- message: "Select Sentry projects to monitor:",
209
- choices: projects.map((p) => ({ name: p.name, value: p.slug })),
210
- });
211
- }
212
- }
213
- catch (err) {
214
- console.log(`Sentry validation failed: ${err.message}. Skipping Sentry.\n`);
215
- sentryToken = undefined;
216
- }
21
+ /**
22
+ * Prompt for a credential and write it to disk.
23
+ * Returns the prompt result (values + optional params), or undefined if skipped.
24
+ */
25
+ async function promptAndStoreCredential(def) {
26
+ const result = await promptCredential(def);
27
+ if (result && Object.keys(result.values).length > 0) {
28
+ writeCredentialValues(def, result.values);
217
29
  }
218
- return { sentryToken, sentryOrg, sentryProjectSlugs };
30
+ return result;
219
31
  }
220
32
  // --- Full interactive setup (new command) ---
221
33
  export async function runSetup() {
222
34
  console.log("\n=== Action Llama — Setup ===\n");
223
- // Step 1: Agent Selection
224
- console.log("--- Step 1: Agents ---\n");
225
- const builtinDefs = listBuiltinDefinitions();
226
- const selectedDefNames = await checkbox({
227
- message: "Which agents do you want to create?",
228
- choices: builtinDefs.map((d) => ({
229
- name: `${d.name} — ${d.label} (${d.description})`,
230
- value: d.name,
231
- })),
232
- validate: (v) => (v.length > 0 ? true : "Select at least one agent"),
233
- });
234
- const selectedDefs = selectedDefNames.map((name) => loadDefinition(name));
235
- // Collect all required/optional credentials from selected definitions
236
- const allOptionalCredentials = new Set();
237
- for (const def of selectedDefs) {
238
- for (const cred of def.credentials.optional) {
239
- allOptionalCredentials.add(cred);
240
- }
241
- }
242
- // Step 2: Credentials
243
- console.log("\n--- Step 2: Credentials ---\n");
35
+ // Step 1: Credentials
36
+ console.log("--- Step 1: Credentials ---\n");
244
37
  // GitHub token (always required)
245
- const existingGithubToken = loadCredential("github-token");
246
- let githubToken;
247
- if (existingGithubToken) {
248
- const reuse = await confirm({
249
- message: `Found existing GitHub token in ${CREDENTIALS_DIR}/github-token. Use it?`,
250
- default: true,
251
- });
252
- if (reuse) {
253
- githubToken = existingGithubToken;
254
- }
255
- else {
256
- githubToken = (await input({
257
- message: "GitHub Personal Access Token (needs repo, workflow scopes):",
258
- validate: (v) => (v.trim().length > 0 ? true : "Token is required"),
259
- })).trim();
260
- }
261
- }
262
- else {
263
- githubToken = (await input({
264
- message: "GitHub Personal Access Token (needs repo, workflow scopes):",
265
- validate: (v) => (v.trim().length > 0 ? true : "Token is required"),
266
- })).trim();
267
- }
38
+ const githubTokenDef = resolveCredential("github-token");
39
+ const githubTokenResult = await promptAndStoreCredential(githubTokenDef);
40
+ if (!githubTokenResult)
41
+ throw new Error("GitHub token is required");
42
+ const githubToken = githubTokenResult.values.token;
268
43
  console.log("Validating GitHub token...");
269
- let githubUser;
270
- let availableRepos;
271
44
  try {
272
45
  const result = await validateGitHubToken(githubToken);
273
- githubUser = result.user;
274
- availableRepos = result.repos;
275
- console.log(`Authenticated as: ${githubUser} (${availableRepos.length} repos found)\n`);
46
+ console.log(`Authenticated as: ${result.user} (${result.repos.length} repos found)\n`);
276
47
  }
277
48
  catch (err) {
278
49
  throw new Error(`GitHub validation failed: ${err.message}`);
279
50
  }
280
51
  // SSH key
281
52
  console.log("--- Git SSH Key ---\n");
282
- const existingSshKey = existsSync(resolve(CREDENTIALS_DIR, "id_rsa"));
283
- let sshKey;
284
- if (existingSshKey) {
285
- const reuse = await confirm({
286
- message: `Found existing SSH key in ${CREDENTIALS_DIR}/id_rsa. Use it?`,
287
- default: true,
288
- });
289
- if (!reuse) {
290
- sshKey = await promptSshKey();
291
- }
292
- }
293
- else {
294
- sshKey = await promptSshKey();
295
- }
296
- // Sentry token (only if any selected definition has it as optional)
297
- let sentryToken;
298
- let sentryOrg;
299
- let sentryProjectSlugs = [];
300
- if (allOptionalCredentials.has("sentry-token")) {
301
- console.log("\n--- Sentry ---\n");
302
- const existingSentryToken = loadCredential("sentry-token");
303
- if (existingSentryToken) {
304
- const reuse = await confirm({
305
- message: `Found existing Sentry token in ${CREDENTIALS_DIR}/sentry-token. Use it?`,
306
- default: true,
307
- });
308
- if (reuse) {
309
- sentryToken = existingSentryToken;
310
- }
311
- }
312
- if (!sentryToken) {
313
- const useSentry = await confirm({
314
- message: "Configure Sentry integration?",
315
- default: false,
316
- });
317
- if (useSentry) {
318
- sentryToken = (await input({
319
- message: "Sentry auth token:",
320
- validate: (v) => (v.trim().length > 0 ? true : "Token is required"),
321
- })).trim();
322
- }
323
- }
324
- if (sentryToken) {
325
- console.log("Validating Sentry token...");
326
- try {
327
- const { organizations } = await validateSentryToken(sentryToken);
328
- if (organizations.length === 0) {
329
- throw new Error("No organizations found");
330
- }
331
- if (organizations.length === 1) {
332
- sentryOrg = organizations[0].slug;
333
- console.log(`Organization: ${sentryOrg}\n`);
334
- }
335
- else {
336
- sentryOrg = await select({
337
- message: "Select Sentry organization:",
338
- choices: organizations.map((o) => ({ name: `${o.name} (${o.slug})`, value: o.slug })),
339
- });
340
- }
341
- const { projects } = await validateSentryProjects(sentryToken, sentryOrg);
342
- if (projects.length > 0) {
343
- sentryProjectSlugs = await checkbox({
344
- message: "Select Sentry projects to monitor:",
345
- choices: projects.map((p) => ({ name: p.name, value: p.slug })),
346
- });
347
- }
348
- }
349
- catch (err) {
350
- console.log(`Sentry validation failed: ${err.message}. Skipping Sentry.\n`);
351
- sentryToken = undefined;
352
- }
353
- }
354
- }
53
+ const sshKeyDef = resolveCredential("id_rsa");
54
+ const sshKeyResult = await promptAndStoreCredential(sshKeyDef);
55
+ const sshKey = sshKeyResult?.values.key;
355
56
  // Anthropic auth
356
57
  console.log("\n--- Anthropic Auth ---\n");
357
- const existingAnthropicKey = loadCredential("anthropic-key");
358
- let authType;
359
- let anthropicKey;
360
- if (existingAnthropicKey) {
361
- const reuse = await confirm({
362
- message: `Found existing Anthropic credential in ${CREDENTIALS_DIR}/anthropic-key. Use it?`,
363
- default: true,
364
- });
365
- if (reuse) {
366
- anthropicKey = existingAnthropicKey;
367
- authType = anthropicKey.includes("sk-ant-oat") ? "oauth_token" : "api_key";
368
- console.log(`Using existing credential (detected type: ${authType}).\n`);
369
- }
370
- else {
371
- ({ authType, anthropicKey } = await promptAnthropicAuth());
372
- }
373
- }
374
- else {
375
- ({ authType, anthropicKey } = await promptAnthropicAuth());
376
- }
377
- // Step 3: LLM defaults
378
- console.log("\n--- Step 3: LLM Defaults ---\n");
58
+ const anthropicDef = resolveCredential("anthropic-key");
59
+ const anthropicResult = await promptAndStoreCredential(anthropicDef);
60
+ const anthropicKey = anthropicResult?.values.token;
61
+ // Step 2: LLM defaults
62
+ console.log("\n--- Step 2: LLM Defaults ---\n");
379
63
  const modelName = await select({
380
64
  message: "Select model:",
381
65
  choices: [
@@ -385,7 +69,7 @@ export async function runSetup() {
385
69
  ],
386
70
  default: "claude-sonnet-4-20250514",
387
71
  });
388
- const thinkingLevel = await select({
72
+ await select({
389
73
  message: "Thinking level:",
390
74
  choices: [
391
75
  { name: "off", value: "off" },
@@ -396,261 +80,15 @@ export async function runSetup() {
396
80
  ],
397
81
  default: "medium",
398
82
  });
399
- const modelConfig = {
400
- provider: "anthropic",
401
- model: modelName,
402
- thinkingLevel,
403
- authType,
404
- };
405
- // Step 4: Configure each agent
406
- console.log("\n--- Step 4: Configure Agents ---\n");
407
- const agents = [];
408
- let anyWebhooks = false;
409
- let firstGithubWebhookSecret;
410
- for (const def of selectedDefs) {
411
- console.log(`\n --- Configure ${def.name} agent ---\n`);
412
- // For init flow, we pre-populate sentry params from the top-level credential gathering
413
- // so we skip the per-agent sentry prompt.
414
- // We accomplish this by building params for credential-linked params here.
415
- const prePopulatedParams = {};
416
- for (const [key, paramDef] of Object.entries(def.params)) {
417
- if (paramDef.credential === "sentry-token") {
418
- if (key === "sentryOrg" && sentryOrg) {
419
- prePopulatedParams[key] = sentryOrg;
420
- }
421
- else if (key === "sentryProjects" && sentryProjectSlugs.length > 0) {
422
- prePopulatedParams[key] = sentryProjectSlugs;
423
- }
424
- }
425
- }
426
- // Use configureAgentInit for init flow (skips credential prompts done globally)
427
- const result = await configureAgentInit(def, {
428
- availableRepos,
429
- githubUser,
430
- modelConfig,
431
- }, prePopulatedParams, sentryToken ? true : false);
432
- if (result.usesWebhooks) {
433
- anyWebhooks = true;
434
- if (!firstGithubWebhookSecret) {
435
- firstGithubWebhookSecret = result.secrets.githubWebhookSecret;
436
- }
437
- }
438
- agents.push(result.agent);
439
- }
440
- // GitHub webhook secret (if any agent uses webhooks and not already prompted)
441
- let githubWebhookSecret = firstGithubWebhookSecret;
442
- if (anyWebhooks && !githubWebhookSecret) {
443
- console.log("\n--- GitHub Webhook Secret ---\n");
444
- const existingSecret = loadCredential("github-webhook-secret");
445
- if (existingSecret) {
446
- const reuse = await confirm({
447
- message: `Found existing webhook secret in ${CREDENTIALS_DIR}/github-webhook-secret. Use it?`,
448
- default: true,
449
- });
450
- if (reuse) {
451
- githubWebhookSecret = existingSecret;
452
- }
453
- }
454
- if (!githubWebhookSecret) {
455
- githubWebhookSecret = (await input({
456
- message: "GitHub webhook secret (set this same value in your GitHub webhook settings):",
457
- validate: (v) => (v.trim().length > 0 ? true : "Secret is required to verify webhook payloads"),
458
- })).trim();
459
- }
460
- }
461
83
  // Build global config
462
84
  const globalConfig = {};
463
- if (githubWebhookSecret) {
464
- globalConfig.webhooks = { githubSecretCredential: "github-webhook-secret" };
465
- }
466
85
  return {
467
86
  globalConfig,
468
- agents,
469
87
  secrets: {
470
88
  githubToken,
471
- sentryToken,
472
89
  anthropicKey,
473
90
  sshKey,
474
- githubWebhookSecret,
475
91
  },
476
92
  };
477
93
  }
478
- /**
479
- * Init-flow variant: configures an agent from a definition, but skips optional
480
- * credential prompts (those were handled globally in runSetup).
481
- */
482
- async function configureAgentInit(definition, context, prePopulatedParams, hasSentryToken) {
483
- const name = await input({
484
- message: `Agent name:`,
485
- default: definition.name,
486
- });
487
- const repoChoices = context.availableRepos.map((r) => ({
488
- name: r.fullName,
489
- value: r.fullName,
490
- }));
491
- const repos = await checkbox({
492
- message: `Repos for ${name}:`,
493
- choices: repoChoices,
494
- validate: (v) => (v.length > 0 ? true : "Select at least one repo"),
495
- });
496
- // Credentials from definition
497
- const credentials = [...definition.credentials.required];
498
- if (hasSentryToken && definition.credentials.optional.includes("sentry-token")) {
499
- credentials.push("sentry-token");
500
- }
501
- // Params — prompt for non-credential params, use pre-populated for credential params
502
- const params = { ...prePopulatedParams };
503
- for (const [key, paramDef] of Object.entries(definition.params)) {
504
- if (paramDef.credential)
505
- continue; // Already handled
506
- let defaultValue = paramDef.default;
507
- if (defaultValue === "$githubUser") {
508
- defaultValue = context.githubUser;
509
- }
510
- if (paramDef.type === "string") {
511
- const value = await input({
512
- message: `${paramDef.description}:`,
513
- default: defaultValue,
514
- ...(paramDef.required ? { validate: (v) => v.trim().length > 0 ? true : `${key} is required` } : {}),
515
- });
516
- if (value.trim()) {
517
- params[key] = value.trim();
518
- }
519
- }
520
- else if (paramDef.type === "string[]") {
521
- const value = await input({
522
- message: `${paramDef.description} (comma-separated):`,
523
- default: defaultValue,
524
- });
525
- if (value.trim()) {
526
- params[key] = value.split(",").map((s) => s.trim()).filter(Boolean);
527
- }
528
- }
529
- }
530
- // Webhook trigger
531
- const useWebhooks = await confirm({
532
- message: `Listen for webhooks? (${definition.webhooks.description})`,
533
- default: true,
534
- });
535
- let webhooks;
536
- if (useWebhooks) {
537
- const filter = buildWebhookFilter(definition, repos, params);
538
- webhooks = { filters: [filter] };
539
- }
540
- // Schedule trigger
541
- const useSchedule = await confirm({
542
- message: `Also run on a schedule (polling)?`,
543
- default: !useWebhooks,
544
- });
545
- let schedule;
546
- if (useSchedule) {
547
- schedule = await input({
548
- message: `${name} poll interval (cron):`,
549
- default: definition.defaultSchedule,
550
- });
551
- }
552
- const prompt = useWebhooks ? definition.prompts.webhook : definition.prompts.schedule;
553
- // Build agent config
554
- const config = {
555
- name,
556
- credentials,
557
- model: context.modelConfig,
558
- prompt,
559
- repos,
560
- ...(schedule ? { schedule } : {}),
561
- ...(webhooks ? { webhooks } : {}),
562
- ...(Object.keys(params).length > 0 ? { params } : {}),
563
- };
564
- return {
565
- agent: { name, template: definition.name, config },
566
- secrets: {},
567
- usesWebhooks: useWebhooks,
568
- };
569
- }
570
- // --- Add agent to existing project ---
571
- export async function runAddAgent(opts) {
572
- console.log("\n=== Action Llama — Add Agent ===\n");
573
- const result = await configureAgent(opts.definition, {
574
- availableRepos: opts.availableRepos,
575
- githubUser: opts.githubUser,
576
- modelConfig: opts.modelConfig,
577
- existingAgentNames: opts.existingAgentNames,
578
- });
579
- return {
580
- agent: result.agent,
581
- secrets: result.secrets,
582
- };
583
- }
584
- // --- SSH key prompt ---
585
- async function promptSshKey() {
586
- const defaultPath = resolve(process.env.HOME || "~", ".ssh", "id_rsa");
587
- const keyPath = await input({
588
- message: `Path to SSH private key for git operations (leave empty to use system default):`,
589
- default: existsSync(defaultPath) ? defaultPath : "",
590
- });
591
- if (!keyPath.trim()) {
592
- console.log("No SSH key configured — git will use your system SSH config.\n");
593
- return undefined;
594
- }
595
- const resolvedPath = resolve(keyPath.trim());
596
- if (!existsSync(resolvedPath)) {
597
- throw new Error(`SSH key not found at ${resolvedPath}`);
598
- }
599
- const content = readFileSync(resolvedPath, "utf-8");
600
- console.log("SSH key loaded.\n");
601
- return content;
602
- }
603
- // --- Anthropic auth prompt ---
604
- async function promptAnthropicAuth() {
605
- const authMethod = await select({
606
- message: "How do you want to authenticate with Anthropic?",
607
- choices: [
608
- { name: "Use existing pi auth (already ran `pi /login` or `claude setup-token`)", value: "pi_auth" },
609
- { name: "Enter an API key (sk-ant-api...)", value: "api_key" },
610
- { name: "Enter an OAuth token (sk-ant-oat...)", value: "oauth_token" },
611
- ],
612
- });
613
- if (authMethod === "pi_auth") {
614
- const { AuthStorage, ModelRegistry } = await import("@mariozechner/pi-coding-agent");
615
- const authStorage = AuthStorage.create();
616
- const registry = new ModelRegistry(authStorage);
617
- const available = await registry.getAvailable();
618
- const hasAnthropic = available.some((m) => m.provider === "anthropic");
619
- if (!hasAnthropic) {
620
- throw new Error("No Anthropic credentials found in pi auth storage (~/.pi/agent/auth.json). " +
621
- "Run `pi /login` first, or choose a different auth method.");
622
- }
623
- console.log("Found existing Anthropic credentials in pi auth storage.\n");
624
- return { authType: "pi_auth", anthropicKey: undefined };
625
- }
626
- else if (authMethod === "api_key") {
627
- let anthropicKey = (await input({
628
- message: "Anthropic API key:",
629
- validate: (v) => (v.trim().length > 0 ? true : "Key is required"),
630
- })).trim();
631
- console.log("Validating API key...");
632
- try {
633
- await validateAnthropicApiKey(anthropicKey);
634
- console.log("API key validated.\n");
635
- }
636
- catch (err) {
637
- throw new Error(`Anthropic validation failed: ${err.message}`);
638
- }
639
- return { authType: "api_key", anthropicKey };
640
- }
641
- else {
642
- let anthropicKey = (await input({
643
- message: "Anthropic OAuth token (from `claude setup-token`):",
644
- validate: (v) => (v.trim().length > 0 ? true : "Token is required"),
645
- })).trim();
646
- try {
647
- validateOAuthTokenFormat(anthropicKey);
648
- console.log("OAuth token format looks valid. It will be verified on first agent run.\n");
649
- }
650
- catch (err) {
651
- throw new Error(err.message);
652
- }
653
- return { authType: "oauth_token", anthropicKey };
654
- }
655
- }
656
94
  //# sourceMappingURL=prompts.js.map