propr-cli 0.8.3

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 (64) hide show
  1. package/README.md +549 -0
  2. package/dist/api/agentTank.js +27 -0
  3. package/dist/api/agents.js +201 -0
  4. package/dist/api/client.js +284 -0
  5. package/dist/api/errors.js +145 -0
  6. package/dist/api/implement.js +147 -0
  7. package/dist/api/index.js +26 -0
  8. package/dist/api/logs.js +59 -0
  9. package/dist/api/plans.js +160 -0
  10. package/dist/api/relay.js +73 -0
  11. package/dist/api/repos.js +243 -0
  12. package/dist/api/settings.js +219 -0
  13. package/dist/api/system.js +53 -0
  14. package/dist/api/tasks.js +140 -0
  15. package/dist/api/todos.js +77 -0
  16. package/dist/api/types.js +6 -0
  17. package/dist/assets/.env.example +183 -0
  18. package/dist/assets/env.example.txt +198 -0
  19. package/dist/commands/agentCommands.js +405 -0
  20. package/dist/commands/checkCommands.js +384 -0
  21. package/dist/commands/implementCommands.js +178 -0
  22. package/dist/commands/index.js +22 -0
  23. package/dist/commands/initCommands.js +167 -0
  24. package/dist/commands/initStack.js +193 -0
  25. package/dist/commands/logCommands.js +170 -0
  26. package/dist/commands/planCommands.js +552 -0
  27. package/dist/commands/relayCommands.js +149 -0
  28. package/dist/commands/repoCommands.js +526 -0
  29. package/dist/commands/settingCommands.js +237 -0
  30. package/dist/commands/stackCommands.js +86 -0
  31. package/dist/commands/startCommand.js +36 -0
  32. package/dist/commands/systemCommands.js +221 -0
  33. package/dist/commands/tankCommands.js +55 -0
  34. package/dist/commands/taskCommands.js +554 -0
  35. package/dist/commands/todoCommands.js +620 -0
  36. package/dist/commands/uiDocsCommands.js +69 -0
  37. package/dist/config/ConfigManager.js +360 -0
  38. package/dist/config/index.js +8 -0
  39. package/dist/config/types.js +16 -0
  40. package/dist/index.js +276 -0
  41. package/dist/orchestrator/format.js +31 -0
  42. package/dist/orchestrator/index.js +102 -0
  43. package/dist/orchestrator/manifest.json +16 -0
  44. package/dist/orchestrator/orchestrator.mjs +798 -0
  45. package/dist/orchestrator/types.js +10 -0
  46. package/dist/tui/StartApp.js +175 -0
  47. package/dist/tui/app.js +9 -0
  48. package/dist/tui/render.js +87 -0
  49. package/dist/utils/envFile.js +65 -0
  50. package/dist/utils/index.js +8 -0
  51. package/dist/utils/io.js +186 -0
  52. package/dist/utils/parseState.js +14 -0
  53. package/dist/utils/resolveProject.js +50 -0
  54. package/dist/vendor/shared/demoMode.js +6 -0
  55. package/dist/vendor/shared/events.js +30 -0
  56. package/dist/vendor/shared/githubAuthMode.js +35 -0
  57. package/dist/vendor/shared/index.js +15 -0
  58. package/dist/vendor/shared/labelUtils.js +32 -0
  59. package/dist/vendor/shared/modelDefinitions.js +146 -0
  60. package/dist/vendor/shared/reviewPrompt.js +18 -0
  61. package/dist/vendor/shared/usageTypes.js +13 -0
  62. package/dist/vendor/shared/userWhitelist.js +30 -0
  63. package/dist/vendor/shared/validateRelayUrl.js +21 -0
  64. package/package.json +31 -0
@@ -0,0 +1,405 @@
1
+ /**
2
+ * Agent Management Commands
3
+ *
4
+ * CLI commands for managing AI agent configurations.
5
+ * Provides the `agent` command group with `list`, `add`, and `delete` subcommands.
6
+ */
7
+ import { Command } from "commander";
8
+ import { listAgents, addAgent, deleteAgent, setAgentEnabled, AGENT_TYPES, } from "../api/agents.js";
9
+ import { ApiError, NetworkError, NotFoundError, UnauthorizedError } from "../api/errors.js";
10
+ import { printOutput, readJsonInput, validateJsonFields, JsonInputError, } from "../utils/index.js";
11
+ const AGENT_TYPE_LIST = AGENT_TYPES.join(", ");
12
+ /**
13
+ * Formats an agent type for display.
14
+ */
15
+ function formatType(type) {
16
+ const typeMap = {
17
+ claude: "Claude",
18
+ codex: "Codex",
19
+ antigravity: "Antigravity",
20
+ opencode: "OpenCode",
21
+ vibe: "Mistral Vibe",
22
+ };
23
+ return typeMap[type?.toLowerCase()] || type;
24
+ }
25
+ /**
26
+ * Truncates a string to a maximum length.
27
+ */
28
+ function truncate(str, maxLen) {
29
+ if (!str)
30
+ return "";
31
+ if (str.length <= maxLen)
32
+ return str;
33
+ return str.substring(0, maxLen - 3) + "...";
34
+ }
35
+ /**
36
+ * Displays a table of agents with clean formatting.
37
+ */
38
+ function displayAgentsTable(agents) {
39
+ const aliasWidth = Math.max("Alias".length, ...agents.map((a) => a.alias.length));
40
+ const typeWidth = Math.max("Type".length, ...agents.map((a) => formatType(a.type).length));
41
+ const enabledWidth = "Enabled".length;
42
+ const defaultModelWidth = Math.max("Default Model".length, ...agents.map((a) => truncate(a.defaultModel, 30).length));
43
+ const modelsWidth = Math.max("Supported Models".length, ...agents.map((a) => truncate(a.supportedModels.join(", "), 40).length));
44
+ const header = [
45
+ "Alias".padEnd(aliasWidth),
46
+ "Type".padEnd(typeWidth),
47
+ "Enabled".padEnd(enabledWidth),
48
+ "Default Model".padEnd(defaultModelWidth),
49
+ "Supported Models".padEnd(modelsWidth),
50
+ ].join(" ");
51
+ console.log(header);
52
+ console.log("-".repeat(header.length));
53
+ for (const agent of agents) {
54
+ const row = [
55
+ agent.alias.padEnd(aliasWidth),
56
+ formatType(agent.type).padEnd(typeWidth),
57
+ (agent.enabled ? "Yes" : "No").padEnd(enabledWidth),
58
+ truncate(agent.defaultModel, 30).padEnd(defaultModelWidth),
59
+ truncate(agent.supportedModels.join(", "), 40).padEnd(modelsWidth),
60
+ ].join(" ");
61
+ console.log(row);
62
+ }
63
+ }
64
+ /**
65
+ * Validates that the agent type is valid.
66
+ */
67
+ function isValidAgentType(type) {
68
+ return AGENT_TYPES.includes(type.toLowerCase());
69
+ }
70
+ /**
71
+ * Prompts the user for confirmation.
72
+ */
73
+ async function confirm(message) {
74
+ const readline = await import("readline");
75
+ const rl = readline.createInterface({
76
+ input: process.stdin,
77
+ output: process.stdout,
78
+ });
79
+ return new Promise((resolve) => {
80
+ rl.question(`${message} (y/N): `, (answer) => {
81
+ rl.close();
82
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
83
+ });
84
+ });
85
+ }
86
+ /**
87
+ * Creates the `agent` command group.
88
+ */
89
+ export function createAgentCommand() {
90
+ const agent = new Command("agent")
91
+ .description("Manage AI agent configurations")
92
+ .addHelpText("after", `
93
+ Examples:
94
+ $ propr agent list # List all agents
95
+ $ propr agent add my-claude -t claude -m ... # Add an agent
96
+ $ propr agent delete my-agent # Delete an agent
97
+ `);
98
+ // agent list
99
+ agent
100
+ .command("list")
101
+ .description("List all configured AI agents with their models and status")
102
+ .option("-j, --json", "Output as JSON for programmatic use")
103
+ .addHelpText("after", `
104
+ Examples:
105
+ $ propr agent list
106
+ $ propr agent list --json
107
+ `)
108
+ .action(async (options) => {
109
+ try {
110
+ const result = await listAgents();
111
+ if (printOutput(result, options.json ?? false)) {
112
+ return;
113
+ }
114
+ console.log("Fetching agents...");
115
+ if (result.agents.length === 0) {
116
+ console.log("");
117
+ console.log("No agents configured.");
118
+ console.log("");
119
+ console.log("To add an agent, use:");
120
+ console.log(" propr agent add <alias> --type <type> --model <models>");
121
+ console.log("");
122
+ console.log("Example:");
123
+ console.log(" propr agent add claude-prod --type claude --model claude-sonnet-4-20250514,claude-opus-4-20250514");
124
+ return;
125
+ }
126
+ console.log("");
127
+ displayAgentsTable(result.agents);
128
+ console.log("");
129
+ console.log(`Total: ${result.agents.length} agent(s)`);
130
+ }
131
+ catch (error) {
132
+ if (error instanceof UnauthorizedError) {
133
+ console.error("Error: Unauthorized. Please run 'propr login' first.");
134
+ }
135
+ else if (error instanceof NetworkError) {
136
+ console.error("Error: cannot reach the ProPR backend. Start the stack first: propr start");
137
+ }
138
+ else {
139
+ console.error(`Error listing agents: ${error.message}`);
140
+ }
141
+ process.exit(1);
142
+ }
143
+ });
144
+ // agent add
145
+ agent
146
+ .command("add [alias]")
147
+ .description("Add a new AI agent configuration for code implementation")
148
+ .option("-t, --type <type>", `Agent type (${AGENT_TYPE_LIST})`)
149
+ .option("-m, --model <models>", "Comma-separated list of supported models")
150
+ .option("-d, --default-model <model>", "Default model to use (defaults to first model)")
151
+ .option("--docker-image <image>", "Docker image for the agent")
152
+ .option("--config-path <path>", "Host path to mount for configuration")
153
+ .option("--disabled", "Create the agent in disabled state")
154
+ .option("-f, --file <path>", "Load agent configuration from JSON file (use '-' for stdin)")
155
+ .option("-j, --json", "Output result as JSON")
156
+ .addHelpText("after", `
157
+ Argument:
158
+ alias Unique identifier for the agent (required unless using --file)
159
+
160
+ Agent Types:
161
+ claude Anthropic Claude models
162
+ codex OpenAI Codex models
163
+ antigravity Antigravity models
164
+ opencode OpenCode models
165
+ vibe Mistral Vibe models
166
+
167
+ JSON File Format:
168
+ {
169
+ "alias": "my-agent",
170
+ "type": "claude",
171
+ "models": ["claude-sonnet-4-20250514", "claude-opus-4-20250514"],
172
+ "defaultModel": "claude-sonnet-4-20250514",
173
+ "dockerImage": "optional-image",
174
+ "configPath": "/optional/path",
175
+ "enabled": true
176
+ }
177
+
178
+ Examples:
179
+ $ propr agent add my-claude -t claude -m claude-sonnet-4-20250514
180
+ $ propr agent add opencode -t opencode -m opencode-minimax-m3-free
181
+ $ propr agent add prod-agent -t claude -m claude-sonnet-4-20250514,claude-opus-4-20250514 -d claude-sonnet-4-20250514
182
+ $ propr agent add test-agent -t antigravity -m antigravity-gemini-3-pro-preview --disabled
183
+ $ propr agent add --file agent-config.json
184
+ $ cat config.json | propr agent add --file -
185
+ `)
186
+ .action(async (aliasArg, options) => {
187
+ try {
188
+ let alias;
189
+ let type;
190
+ let models;
191
+ let defaultModel;
192
+ let dockerImage;
193
+ let configPath;
194
+ let enabled;
195
+ if (options.file) {
196
+ try {
197
+ const jsonConfig = await readJsonInput(options.file);
198
+ validateJsonFields(jsonConfig, ["alias", "type", "models"]);
199
+ alias = jsonConfig.alias;
200
+ type = jsonConfig.type.toLowerCase();
201
+ models = jsonConfig.models;
202
+ defaultModel = jsonConfig.defaultModel;
203
+ dockerImage = jsonConfig.dockerImage;
204
+ configPath = jsonConfig.configPath;
205
+ enabled = jsonConfig.enabled !== false;
206
+ }
207
+ catch (error) {
208
+ if (error instanceof JsonInputError) {
209
+ console.error(`Error: ${error.message}`);
210
+ }
211
+ else {
212
+ console.error(`Error reading JSON file: ${error.message}`);
213
+ }
214
+ process.exit(1);
215
+ }
216
+ }
217
+ else {
218
+ if (!aliasArg) {
219
+ console.error("Error: Alias is required. Provide it as an argument or use --file.");
220
+ process.exit(1);
221
+ }
222
+ if (!options.type) {
223
+ console.error("Error: --type is required when not using --file");
224
+ process.exit(1);
225
+ }
226
+ if (!options.model) {
227
+ console.error("Error: --model is required when not using --file");
228
+ process.exit(1);
229
+ }
230
+ alias = aliasArg;
231
+ type = options.type.toLowerCase();
232
+ models = options.model
233
+ .split(",")
234
+ .map((m) => m.trim())
235
+ .filter((m) => m.length > 0);
236
+ defaultModel = options.defaultModel;
237
+ dockerImage = options.dockerImage;
238
+ configPath = options.configPath;
239
+ enabled = !options.disabled;
240
+ }
241
+ if (!isValidAgentType(type)) {
242
+ console.error(`Error: Invalid agent type '${type}'. Must be one of: ${AGENT_TYPE_LIST}`);
243
+ process.exit(1);
244
+ }
245
+ if (models.length === 0) {
246
+ console.error("Error: At least one model must be specified");
247
+ process.exit(1);
248
+ }
249
+ if (defaultModel && !models.includes(defaultModel)) {
250
+ console.error(`Error: Default model '${defaultModel}' is not in the list of supported models`);
251
+ process.exit(1);
252
+ }
253
+ if (!options.json) {
254
+ console.log(`Adding agent '${alias}'...`);
255
+ }
256
+ const result = await addAgent({
257
+ alias,
258
+ type,
259
+ models,
260
+ defaultModel,
261
+ dockerImage,
262
+ configPath,
263
+ enabled,
264
+ });
265
+ if (result.success) {
266
+ if (printOutput(result, options.json ?? false)) {
267
+ return;
268
+ }
269
+ console.log("");
270
+ console.log(`Agent '${alias}' added successfully!`);
271
+ console.log("");
272
+ console.log("Configuration:");
273
+ console.log(` Type: ${formatType(type)}`);
274
+ console.log(` Enabled: ${enabled ? "Yes" : "No"}`);
275
+ console.log(` Models: ${models.join(", ")}`);
276
+ console.log(` Default Model: ${defaultModel || models[0]}`);
277
+ console.log("");
278
+ console.log(`Total agents configured: ${result.agents.length}`);
279
+ }
280
+ else {
281
+ console.error("Failed to add agent");
282
+ process.exit(1);
283
+ }
284
+ }
285
+ catch (error) {
286
+ if (error instanceof UnauthorizedError) {
287
+ console.error("Error: Unauthorized. Please run 'propr login' first.");
288
+ }
289
+ else if (error instanceof NetworkError) {
290
+ console.error("Error: cannot reach the ProPR backend. Start the stack first: propr start");
291
+ }
292
+ else if (error instanceof ApiError && error.message.includes("already exists")) {
293
+ console.error(`Error: ${error.message}`);
294
+ }
295
+ else {
296
+ console.error(`Error adding agent: ${error.message}`);
297
+ }
298
+ process.exit(1);
299
+ }
300
+ });
301
+ // agent enable / disable
302
+ const applyEnabled = async (alias, enabled) => {
303
+ try {
304
+ const result = await setAgentEnabled(alias, enabled);
305
+ if (result.success) {
306
+ console.log(`Agent '${alias}' ${enabled ? "enabled" : "disabled"}.`);
307
+ }
308
+ else {
309
+ console.error(`Failed to ${enabled ? "enable" : "disable"} agent '${alias}'`);
310
+ process.exit(1);
311
+ }
312
+ }
313
+ catch (error) {
314
+ if (error instanceof NetworkError) {
315
+ console.error("Error: cannot reach the ProPR backend. Start the stack first: propr start");
316
+ }
317
+ else if (error instanceof NotFoundError || (error instanceof Error && error.message.includes("not found"))) {
318
+ console.error(`Error: Agent '${alias}' not found`);
319
+ }
320
+ else if (error instanceof UnauthorizedError) {
321
+ console.error("Error: Unauthorized. Please run 'propr login' first.");
322
+ }
323
+ else if (error instanceof ApiError) {
324
+ console.error(`Error updating agent: ${error.message}`);
325
+ }
326
+ else {
327
+ console.error(`Error updating agent: ${error.message}`);
328
+ }
329
+ process.exit(1);
330
+ }
331
+ };
332
+ agent
333
+ .command("enable <alias>")
334
+ .description("Enable an agent (requires the stack to be running)")
335
+ .addHelpText("after", `
336
+ Example:
337
+ $ propr agent enable claude-prod
338
+ `)
339
+ .action(async (alias) => {
340
+ await applyEnabled(alias, true);
341
+ });
342
+ agent
343
+ .command("disable <alias>")
344
+ .description("Disable an agent (requires the stack to be running)")
345
+ .addHelpText("after", `
346
+ Example:
347
+ $ propr agent disable claude-prod
348
+ `)
349
+ .action(async (alias) => {
350
+ await applyEnabled(alias, false);
351
+ });
352
+ // agent delete
353
+ agent
354
+ .command("delete <alias>")
355
+ .description("Delete an AI agent configuration permanently")
356
+ .option("-f, --force", "Skip confirmation prompt")
357
+ .addHelpText("after", `
358
+ Argument:
359
+ alias The alias of the agent to delete
360
+
361
+ Examples:
362
+ $ propr agent delete my-agent # With confirmation
363
+ $ propr agent delete my-agent --force # Skip confirmation
364
+ `)
365
+ .action(async (alias, options) => {
366
+ try {
367
+ if (!options.force) {
368
+ console.log(`About to delete agent: ${alias}`);
369
+ console.log("");
370
+ const confirmed = await confirm("Are you sure you want to delete this agent?");
371
+ if (!confirmed) {
372
+ console.log("Deletion cancelled.");
373
+ return;
374
+ }
375
+ }
376
+ console.log(`Deleting agent '${alias}'...`);
377
+ const result = await deleteAgent(alias);
378
+ if (result.success) {
379
+ console.log("");
380
+ console.log(`Agent '${alias}' deleted successfully!`);
381
+ console.log(`Remaining agents: ${result.agents.length}`);
382
+ }
383
+ else {
384
+ console.error("Failed to delete agent");
385
+ process.exit(1);
386
+ }
387
+ }
388
+ catch (error) {
389
+ if (error instanceof UnauthorizedError) {
390
+ console.error("Error: Unauthorized. Please run 'propr login' first.");
391
+ }
392
+ else if (error instanceof NotFoundError) {
393
+ console.error(`Error: Agent '${alias}' not found`);
394
+ }
395
+ else if (error instanceof NetworkError) {
396
+ console.error("Error: cannot reach the ProPR backend. Start the stack first: propr start");
397
+ }
398
+ else {
399
+ console.error(`Error deleting agent: ${error.message}`);
400
+ }
401
+ process.exit(1);
402
+ }
403
+ });
404
+ return agent;
405
+ }