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,147 @@
1
+ /**
2
+ * Implementation API
3
+ *
4
+ * Functions for interacting with the ProPR backend implementation endpoints.
5
+ * These functions provide a typed interface to trigger issue implementations
6
+ * and poll task status.
7
+ */
8
+ import { createApiClient } from "./index.js";
9
+ /**
10
+ * Triggers implementation for a specific issue/task.
11
+ *
12
+ * This function calls the backend endpoint to start implementing an issue
13
+ * that was created as part of a plan. The backend identifies issues using
14
+ * the combination of draft ID and issue number.
15
+ *
16
+ * @param draftId - The unique identifier of the plan draft containing the issue.
17
+ * @param issueNumber - The GitHub issue number to implement.
18
+ * @param options - Optional configuration for the implementation.
19
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
20
+ * @returns A promise resolving to the implementation response containing task ID for polling.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Implement a single issue with default settings
25
+ * const result = await implementIssue("draft-123", 1);
26
+ * console.log(result.message);
27
+ * if (result.taskId) {
28
+ * // Poll for status using the task ID
29
+ * const status = await getTaskStatus(result.taskId);
30
+ * }
31
+ *
32
+ * // Implement with a specific model
33
+ * const result = await implementIssue("draft-123", 1, {
34
+ * agent_alias: "claude-code",
35
+ * model_name: "claude-sonnet-4-20250514"
36
+ * });
37
+ *
38
+ * // Implement with Epic PR and auto-merge
39
+ * const result = await implementIssue("draft-123", 1, {
40
+ * useEpic: true,
41
+ * autoMerge: true
42
+ * });
43
+ * ```
44
+ */
45
+ export async function implementIssue(draftId, issueNumber, options = {}, client) {
46
+ const apiClient = client ?? (await createApiClient());
47
+ const body = {};
48
+ if (options.agent_alias !== undefined) {
49
+ body.agent_alias = options.agent_alias;
50
+ }
51
+ if (options.model_name !== undefined) {
52
+ body.model_name = options.model_name;
53
+ }
54
+ if (options.models !== undefined) {
55
+ body.models = options.models;
56
+ }
57
+ if (options.useEpic !== undefined) {
58
+ body.useEpic = options.useEpic;
59
+ }
60
+ if (options.autoMerge !== undefined) {
61
+ body.autoMerge = options.autoMerge;
62
+ }
63
+ const endpoint = `/api/planner/drafts/${encodeURIComponent(draftId)}/issues/${encodeURIComponent(String(issueNumber))}/implement`;
64
+ const response = await apiClient.post(endpoint, {
65
+ body,
66
+ });
67
+ return response.data;
68
+ }
69
+ /**
70
+ * Fetches the status of an executing background task.
71
+ *
72
+ * This function retrieves the current state and history of a task,
73
+ * allowing the CLI to poll for progress and completion.
74
+ *
75
+ * @param taskId - The unique identifier of the task to check.
76
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
77
+ * @returns A promise resolving to the parsed task status with convenience fields.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // Get the status of a task
82
+ * const status = await getTaskStatus("task-abc123");
83
+ * console.log(`Current state: ${status.currentState}`);
84
+ *
85
+ * if (status.isCompleted) {
86
+ * console.log("Task completed successfully!");
87
+ * if (status.prNumber) {
88
+ * console.log(`PR created: #${status.prNumber}`);
89
+ * }
90
+ * } else if (status.isFailed) {
91
+ * console.log(`Task failed: ${status.failureReason}`);
92
+ * } else if (status.isInProgress) {
93
+ * console.log("Task is still running...");
94
+ * }
95
+ * ```
96
+ */
97
+ export async function getTaskStatus(taskId, client) {
98
+ const apiClient = client ?? (await createApiClient());
99
+ const endpoint = `/api/task/${encodeURIComponent(taskId)}/history`;
100
+ const response = await apiClient.get(endpoint);
101
+ return parseTaskStatus(response.data);
102
+ }
103
+ /**
104
+ * Parses the raw task status response into a more convenient format.
105
+ *
106
+ * @param response - The raw task status response from the API.
107
+ * @returns A parsed TaskStatus with convenience fields.
108
+ */
109
+ function parseTaskStatus(response) {
110
+ const { taskId, history, taskInfo } = response;
111
+ // Get the latest state from history
112
+ const latestEntry = history.length > 0 ? history[history.length - 1] : null;
113
+ const currentState = latestEntry?.state?.toLowerCase() || "pending";
114
+ // Determine status flags
115
+ const completedStates = ["completed", "failed", "cancelled"];
116
+ const inProgressStates = ["pending", "queued", "processing", "claude_execution", "post_processing"];
117
+ const isCompleted = currentState === "completed";
118
+ const isFailed = currentState === "failed" || currentState === "cancelled";
119
+ const isInProgress = !completedStates.includes(currentState) && inProgressStates.includes(currentState);
120
+ // Extract failure reason
121
+ let failureReason;
122
+ if (isFailed) {
123
+ failureReason = latestEntry?.reason || latestEntry?.message || latestEntry?.metadata?.error;
124
+ }
125
+ // Extract PR information from history
126
+ let prNumber;
127
+ let prUrl;
128
+ for (const entry of history) {
129
+ if (entry.metadata?.pullRequest) {
130
+ prNumber = entry.metadata.pullRequest.number;
131
+ prUrl = entry.metadata.pullRequest.url;
132
+ break;
133
+ }
134
+ }
135
+ return {
136
+ taskId,
137
+ currentState: currentState,
138
+ isInProgress,
139
+ isCompleted,
140
+ isFailed,
141
+ failureReason,
142
+ history,
143
+ taskInfo,
144
+ prNumber,
145
+ prUrl,
146
+ };
147
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * CLI API Module
3
+ *
4
+ * Exports the API client and related types for communicating
5
+ * with the ProPR backend REST API.
6
+ */
7
+ export { ApiClient, createApiClient, createApiClientWithConfig, } from "./client.js";
8
+ export { ApiError, UnauthorizedError, ForbiddenError, NotFoundError, BadRequestError, InternalServerError, NetworkError, TimeoutError, createApiError, } from "./errors.js";
9
+ // Plan Management API
10
+ export { listPlans, createPlan, getPlan, deletePlan, abortPlan, listPlanIssues, generatePlan, finalizePlan, } from "./plans.js";
11
+ // Implementation API
12
+ export { implementIssue, getTaskStatus, } from "./implement.js";
13
+ // Tasks API
14
+ export { listTasks, stopTask, deleteTask, revertTask, } from "./tasks.js";
15
+ // Repository Configuration API
16
+ export { getRepos, addRepo, updateRepo, removeRepo, triggerIndexing, getIndexingStatus, reposApi, } from "./repos.js";
17
+ // Agents Configuration API
18
+ export { listAgents, addAgent, deleteAgent, } from "./agents.js";
19
+ // System Settings API
20
+ export { getSettings, updateSettings, updateSetting, isValidSettingKey, parseSettingValue, settingsApi, VALID_SETTING_KEYS, } from "./settings.js";
21
+ // LLM Logs API
22
+ export { listLlmLogs, } from "./logs.js";
23
+ // Repository To-Dos API
24
+ export { listTodos, getTodo, createTodo, updateTodo, deleteTodo, listCategories, createCategory, updateCategory, deleteCategory, reorderTodos, reorderCategories, } from "./todos.js";
25
+ // System Status API
26
+ export { getSystemStatus, getQueueStats, } from "./system.js";
@@ -0,0 +1,59 @@
1
+ /**
2
+ * LLM Logs API
3
+ *
4
+ * Functions for interacting with the ProPR backend LLM logs endpoints.
5
+ * These functions provide a typed interface to list and filter LLM execution logs.
6
+ */
7
+ import { createApiClient } from "./index.js";
8
+ /**
9
+ * Lists LLM logs from the backend with optional filtering.
10
+ *
11
+ * @param options - Optional filtering and pagination options.
12
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
13
+ * @returns A promise resolving to the list of logs with pagination info.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // List all logs
18
+ * const result = await listLlmLogs();
19
+ * console.log(`Found ${result.pagination.total} logs`);
20
+ *
21
+ * // List logs for a specific model
22
+ * const result = await listLlmLogs({ model: "claude-3-opus" });
23
+ *
24
+ * // List logs with pagination
25
+ * const result = await listLlmLogs({ page: 2, limit: 20 });
26
+ * ```
27
+ */
28
+ export async function listLlmLogs(options = {}, client) {
29
+ const apiClient = client ?? (await createApiClient());
30
+ const params = {};
31
+ if (options.page !== undefined) {
32
+ params.page = String(options.page);
33
+ }
34
+ if (options.limit !== undefined) {
35
+ params.limit = String(options.limit);
36
+ }
37
+ if (options.executionType !== undefined) {
38
+ params.execution_type = options.executionType;
39
+ }
40
+ if (options.model !== undefined) {
41
+ params.model = options.model;
42
+ }
43
+ if (options.success !== undefined) {
44
+ params.success = options.success ? "true" : "false";
45
+ }
46
+ if (options.draftId !== undefined) {
47
+ params.draft_id = options.draftId;
48
+ }
49
+ if (options.agentAlias !== undefined) {
50
+ params.agent_alias = options.agentAlias;
51
+ }
52
+ if (options.workType !== undefined) {
53
+ params.work_type = options.workType;
54
+ }
55
+ const response = await apiClient.get("/api/llm-logs", {
56
+ params,
57
+ });
58
+ return response.data;
59
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Plan Management API
3
+ *
4
+ * Functions for interacting with the ProPR backend plan endpoints.
5
+ * These functions provide a typed interface to list, create, and fetch plans.
6
+ */
7
+ import { createApiClient } from "./index.js";
8
+ /**
9
+ * Fetches plans for a specific repository.
10
+ *
11
+ * @param project - The repository identifier (e.g., "owner/repo").
12
+ * @param options - Optional filtering and pagination options.
13
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
14
+ * @returns A promise resolving to the list of plans with pagination info.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const result = await listPlans("owner/repo");
19
+ * console.log(`Found ${result.total} plans`);
20
+ * for (const plan of result.drafts) {
21
+ * console.log(`- ${plan.name} (${plan.status})`);
22
+ * }
23
+ * ```
24
+ */
25
+ export async function listPlans(project, options = {}, client) {
26
+ const apiClient = client ?? (await createApiClient());
27
+ const params = {
28
+ repository: project,
29
+ page: options.page,
30
+ limit: options.limit,
31
+ search: options.search,
32
+ status: options.status,
33
+ excludeStatuses: options.excludeStatuses,
34
+ };
35
+ const response = await apiClient.get("/api/planner/drafts", {
36
+ params,
37
+ });
38
+ return response.data;
39
+ }
40
+ /**
41
+ * Submits a new plan generation request.
42
+ *
43
+ * @param project - The repository identifier (e.g., "owner/repo").
44
+ * @param prompt - The initial prompt describing the plan.
45
+ * @param options - Optional additional options for plan creation.
46
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
47
+ * @returns A promise resolving to the created plan.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const plan = await createPlan("owner/repo", "Add user authentication feature");
52
+ * console.log(`Created plan: ${plan.draft_id}`);
53
+ * ```
54
+ */
55
+ export async function createPlan(project, prompt, options = {}, client) {
56
+ const apiClient = client ?? (await createApiClient());
57
+ const body = {
58
+ repository: project,
59
+ prompt,
60
+ ...options,
61
+ };
62
+ const response = await apiClient.post("/api/planner/drafts", {
63
+ body,
64
+ });
65
+ return response.data;
66
+ }
67
+ /**
68
+ * Fetches details of a specific plan.
69
+ *
70
+ * @param planId - The unique identifier of the plan (draft_id).
71
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
72
+ * @returns A promise resolving to the plan details.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const plan = await getPlan("abc123-uuid");
77
+ * console.log(`Plan status: ${plan.status}`);
78
+ * console.log(`Plan items: ${plan.plan_json.length}`);
79
+ * ```
80
+ */
81
+ export async function getPlan(planId, client) {
82
+ const apiClient = client ?? (await createApiClient());
83
+ const response = await apiClient.get(`/api/planner/drafts/${encodeURIComponent(planId)}`);
84
+ return response.data;
85
+ }
86
+ /**
87
+ * Deletes a specific plan.
88
+ *
89
+ * @param planId - The unique identifier of the plan (draft_id).
90
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
91
+ * @returns A promise that resolves when the plan is deleted.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * await deletePlan("abc123-uuid");
96
+ * console.log("Plan deleted successfully");
97
+ * ```
98
+ */
99
+ export async function deletePlan(planId, client) {
100
+ const apiClient = client ?? (await createApiClient());
101
+ await apiClient.delete(`/api/planner/drafts/${encodeURIComponent(planId)}`);
102
+ }
103
+ /**
104
+ * Aborts an ongoing plan generation.
105
+ *
106
+ * @param planId - The unique identifier of the plan (draft_id).
107
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
108
+ * @returns A promise resolving to the abort response.
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const result = await abortPlan("abc123-uuid");
113
+ * console.log(result.message); // "Generation aborted"
114
+ * ```
115
+ */
116
+ export async function abortPlan(planId, client) {
117
+ const apiClient = client ?? (await createApiClient());
118
+ const response = await apiClient.post("/api/planner/abort", {
119
+ body: { draftId: planId },
120
+ });
121
+ return response.data;
122
+ }
123
+ /**
124
+ * Finalizes a plan by creating GitHub issues from its plan items.
125
+ *
126
+ * @param draftId - The unique identifier of the plan draft.
127
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
128
+ * @returns A promise resolving to the finalization response.
129
+ */
130
+ export async function finalizePlan(draftId, client) {
131
+ const apiClient = client ?? (await createApiClient());
132
+ const response = await apiClient.post("/api/planner/finalize", { body: { draftId } });
133
+ return response.data;
134
+ }
135
+ /**
136
+ * Triggers plan generation for an existing draft.
137
+ *
138
+ * @param draftId - The unique identifier of the plan draft.
139
+ * @param options - Optional generation configuration.
140
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
141
+ * @returns A promise resolving to the generation response.
142
+ */
143
+ export async function generatePlan(draftId, options = {}, client) {
144
+ const apiClient = client ?? (await createApiClient());
145
+ const body = { draftId, ...options };
146
+ const response = await apiClient.post("/api/planner/generate", { body });
147
+ return response.data;
148
+ }
149
+ /**
150
+ * Lists issues associated with a plan.
151
+ *
152
+ * @param planId - The unique identifier of the plan (draft_id).
153
+ * @param client - Optional ApiClient instance. If not provided, one will be created.
154
+ * @returns A promise resolving to the list of plan issues.
155
+ */
156
+ export async function listPlanIssues(planId, client) {
157
+ const apiClient = client ?? (await createApiClient());
158
+ const response = await apiClient.get(`/api/planner/drafts/${encodeURIComponent(planId)}/issues`);
159
+ return response.data;
160
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * GitHub token relay API (auth path 2).
3
+ *
4
+ * These call the vendor-run relay's enrollment endpoints (propr-routing's
5
+ * /v1/relay-tokens routes), authenticated by the user's GitHub token (the same
6
+ * token `propr login` stores). They are distinct from the ProPR backend API —
7
+ * the relay is a separate service, and the daemon later uses the issued relay
8
+ * token against /v1/installation-token.
9
+ */
10
+ const FETCH_TIMEOUT_MS = 15_000;
11
+ function baseUrl(options) {
12
+ return options.baseUrl.replace(/\/+$/, "");
13
+ }
14
+ async function relayRequest(options, path, method, body, init) {
15
+ let response;
16
+ try {
17
+ const headers = {
18
+ authorization: `Bearer ${options.githubToken}`,
19
+ accept: "application/json",
20
+ };
21
+ if (body !== undefined) {
22
+ headers["content-type"] = "application/json";
23
+ }
24
+ response = await fetch(`${baseUrl(options)}${path}`, {
25
+ method,
26
+ headers,
27
+ body: body === undefined ? undefined : JSON.stringify(body),
28
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
29
+ });
30
+ }
31
+ catch (error) {
32
+ throw new Error(`Cannot reach the relay at ${options.baseUrl}: ${error.message}`);
33
+ }
34
+ if (!response.ok) {
35
+ let code = "";
36
+ try {
37
+ const parsed = (await response.json());
38
+ code = parsed?.error?.code ?? "";
39
+ }
40
+ catch {
41
+ /* non-JSON error body */
42
+ }
43
+ if (response.status === 401) {
44
+ throw new Error("The relay rejected your GitHub token. Run `propr login` to refresh it.");
45
+ }
46
+ if (response.status === 403) {
47
+ throw new Error("You are not authorized for this installation. Confirm the shared GitHub App is installed and you have access to it.");
48
+ }
49
+ if (response.status === 404 && init?.notFoundMessage) {
50
+ throw new Error(init.notFoundMessage);
51
+ }
52
+ throw new Error(`Relay request failed (HTTP ${response.status}${code ? ` ${code}` : ""}).`);
53
+ }
54
+ try {
55
+ return (await response.json());
56
+ }
57
+ catch {
58
+ throw new Error("The relay returned a malformed JSON response.");
59
+ }
60
+ }
61
+ export function enrollRelayToken(options, params) {
62
+ return relayRequest(options, "/relay-tokens", "POST", {
63
+ installation_id: params.installationId,
64
+ label: params.label ?? null,
65
+ });
66
+ }
67
+ export function listRelayTokens(options, installationId) {
68
+ const query = encodeURIComponent(String(installationId));
69
+ return relayRequest(options, `/relay-tokens?installation_id=${query}`, "GET");
70
+ }
71
+ export function revokeRelayToken(options, params) {
72
+ return relayRequest(options, "/relay-tokens/revoke", "POST", { installation_id: params.installationId, token_id: params.tokenId }, { notFoundMessage: "Relay token not found (already revoked or wrong token id)." });
73
+ }