@proletariat/cli 0.3.23 → 0.3.25

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 (235) hide show
  1. package/dist/commands/action/create.js +4 -4
  2. package/dist/commands/action/update.js +3 -3
  3. package/dist/commands/agent/{temp/cleanup.d.ts → cleanup.d.ts} +1 -1
  4. package/dist/commands/agent/{temp/cleanup.js → cleanup.js} +4 -4
  5. package/dist/commands/agent/index.js +8 -8
  6. package/dist/commands/branch/create.js +2 -2
  7. package/dist/commands/epic/activate.js +9 -17
  8. package/dist/commands/epic/archive.js +13 -24
  9. package/dist/commands/epic/create.d.ts +1 -0
  10. package/dist/commands/epic/create.js +46 -8
  11. package/dist/commands/epic/index.js +2 -2
  12. package/dist/commands/epic/move.js +28 -47
  13. package/dist/commands/epic/progress.js +10 -14
  14. package/dist/commands/epic/project.js +42 -59
  15. package/dist/commands/epic/reorder.js +25 -30
  16. package/dist/commands/epic/spec.d.ts +1 -0
  17. package/dist/commands/epic/spec.js +39 -40
  18. package/dist/commands/epic/ticket.d.ts +2 -0
  19. package/dist/commands/epic/ticket.js +63 -37
  20. package/dist/commands/feedback/index.d.ts +10 -0
  21. package/dist/commands/feedback/index.js +60 -0
  22. package/dist/commands/feedback/list.d.ts +12 -0
  23. package/dist/commands/feedback/list.js +126 -0
  24. package/dist/commands/feedback/submit.d.ts +16 -0
  25. package/dist/commands/feedback/submit.js +220 -0
  26. package/dist/commands/{template/phase/delete.d.ts → feedback/view.d.ts} +7 -5
  27. package/dist/commands/feedback/view.js +109 -0
  28. package/dist/commands/gh/index.js +4 -0
  29. package/dist/commands/{epic/link/remove.d.ts → link/create.d.ts} +6 -7
  30. package/dist/commands/link/create.js +141 -0
  31. package/dist/commands/{epic/link/relates.d.ts → link/index.d.ts} +4 -5
  32. package/dist/commands/link/index.js +87 -0
  33. package/dist/commands/{epic/link/duplicates.d.ts → link/list.d.ts} +7 -4
  34. package/dist/commands/link/list.js +182 -0
  35. package/dist/commands/{spec/link → link}/remove.d.ts +4 -5
  36. package/dist/commands/link/remove.js +120 -0
  37. package/dist/commands/mcp-server.d.ts +22 -0
  38. package/dist/commands/mcp-server.js +98 -0
  39. package/dist/commands/phase/create.js +1 -1
  40. package/dist/commands/project/create.d.ts +1 -0
  41. package/dist/commands/project/create.js +38 -4
  42. package/dist/commands/repo/create.d.ts +38 -0
  43. package/dist/commands/repo/create.js +283 -0
  44. package/dist/commands/repo/index.js +7 -0
  45. package/dist/commands/roadmap/add-project.js +9 -22
  46. package/dist/commands/roadmap/create.d.ts +0 -1
  47. package/dist/commands/roadmap/create.js +46 -40
  48. package/dist/commands/roadmap/delete.js +10 -24
  49. package/dist/commands/roadmap/generate.d.ts +1 -0
  50. package/dist/commands/roadmap/generate.js +21 -22
  51. package/dist/commands/roadmap/remove-project.js +14 -34
  52. package/dist/commands/roadmap/reorder.js +19 -26
  53. package/dist/commands/roadmap/update.js +27 -26
  54. package/dist/commands/roadmap/view.js +5 -12
  55. package/dist/commands/session/attach.d.ts +1 -8
  56. package/dist/commands/session/attach.js +93 -59
  57. package/dist/commands/session/list.d.ts +0 -8
  58. package/dist/commands/session/list.js +130 -81
  59. package/dist/commands/spec/create.d.ts +1 -0
  60. package/dist/commands/spec/create.js +44 -3
  61. package/dist/commands/spec/edit.js +63 -33
  62. package/dist/commands/spec/index.js +2 -2
  63. package/dist/commands/{agent/staff → staff}/add.js +10 -10
  64. package/dist/commands/{agent/staff → staff}/index.d.ts +1 -1
  65. package/dist/commands/{agent/staff → staff}/index.js +7 -7
  66. package/dist/commands/{agent/staff → staff}/list.js +3 -3
  67. package/dist/commands/{agent/staff → staff}/remove.d.ts +1 -1
  68. package/dist/commands/{agent/staff → staff}/remove.js +8 -8
  69. package/dist/commands/{template/phase/index.d.ts → support/book.d.ts} +2 -2
  70. package/dist/commands/support/book.js +54 -0
  71. package/dist/commands/{template/ticket/index.d.ts → support/discord.d.ts} +2 -2
  72. package/dist/commands/support/discord.js +54 -0
  73. package/dist/commands/support/docs.d.ts +10 -0
  74. package/dist/commands/support/docs.js +54 -0
  75. package/dist/commands/support/index.d.ts +19 -0
  76. package/dist/commands/support/index.js +81 -0
  77. package/dist/commands/support/issues.d.ts +11 -0
  78. package/dist/commands/support/issues.js +77 -0
  79. package/dist/commands/support/logs.d.ts +18 -0
  80. package/dist/commands/support/logs.js +247 -0
  81. package/dist/commands/{ticket/template → template}/apply.d.ts +8 -6
  82. package/dist/commands/template/apply.js +262 -0
  83. package/dist/commands/{ticket/template → template}/create.d.ts +5 -6
  84. package/dist/commands/template/create.js +238 -0
  85. package/dist/commands/template/index.js +48 -36
  86. package/dist/commands/{ticket/template → template}/save.d.ts +2 -2
  87. package/dist/commands/template/save.js +104 -0
  88. package/dist/commands/{phase/template → template}/update.d.ts +2 -2
  89. package/dist/commands/template/update.js +99 -0
  90. package/dist/commands/{agent/themes → theme}/add-names.d.ts +1 -1
  91. package/dist/commands/{agent/themes → theme}/add-names.js +6 -6
  92. package/dist/commands/{agent/themes → theme}/create.d.ts +1 -1
  93. package/dist/commands/{agent/themes → theme}/create.js +5 -5
  94. package/dist/commands/{agent/themes → theme}/index.d.ts +1 -1
  95. package/dist/commands/{agent/themes → theme}/index.js +10 -10
  96. package/dist/commands/{agent/themes → theme}/list.d.ts +1 -1
  97. package/dist/commands/{agent/themes → theme}/list.js +5 -5
  98. package/dist/commands/{agent/themes → theme}/set.d.ts +1 -1
  99. package/dist/commands/{agent/themes → theme}/set.js +7 -7
  100. package/dist/commands/ticket/create.d.ts +1 -0
  101. package/dist/commands/ticket/create.js +75 -15
  102. package/dist/commands/ticket/edit.js +44 -13
  103. package/dist/commands/ticket/index.js +6 -6
  104. package/dist/commands/ticket/move.d.ts +7 -0
  105. package/dist/commands/ticket/move.js +132 -0
  106. package/dist/commands/work/spawn.d.ts +1 -0
  107. package/dist/commands/work/spawn.js +72 -8
  108. package/dist/commands/work/start.js +6 -0
  109. package/dist/lib/execution/runners.js +21 -17
  110. package/dist/lib/execution/session-utils.d.ts +60 -0
  111. package/dist/lib/execution/session-utils.js +162 -0
  112. package/dist/lib/execution/spawner.d.ts +2 -0
  113. package/dist/lib/execution/spawner.js +42 -0
  114. package/dist/lib/flags/resolver.d.ts +2 -2
  115. package/dist/lib/flags/resolver.js +15 -0
  116. package/dist/lib/init/index.js +18 -0
  117. package/dist/lib/mcp/helpers.d.ts +43 -0
  118. package/dist/lib/mcp/helpers.js +57 -0
  119. package/dist/lib/mcp/index.d.ts +6 -0
  120. package/dist/lib/mcp/index.js +6 -0
  121. package/dist/lib/mcp/tools/action.d.ts +6 -0
  122. package/dist/lib/mcp/tools/action.js +88 -0
  123. package/dist/lib/mcp/tools/board.d.ts +6 -0
  124. package/dist/lib/mcp/tools/board.js +139 -0
  125. package/dist/lib/mcp/tools/category.d.ts +6 -0
  126. package/dist/lib/mcp/tools/category.js +84 -0
  127. package/dist/lib/mcp/tools/cli-passthrough.d.ts +15 -0
  128. package/dist/lib/mcp/tools/cli-passthrough.js +333 -0
  129. package/dist/lib/mcp/tools/epic.d.ts +6 -0
  130. package/dist/lib/mcp/tools/epic.js +178 -0
  131. package/dist/lib/mcp/tools/index.d.ts +18 -0
  132. package/dist/lib/mcp/tools/index.js +19 -0
  133. package/dist/lib/mcp/tools/phase.d.ts +6 -0
  134. package/dist/lib/mcp/tools/phase.js +131 -0
  135. package/dist/lib/mcp/tools/project.d.ts +6 -0
  136. package/dist/lib/mcp/tools/project.js +196 -0
  137. package/dist/lib/mcp/tools/roadmap.d.ts +6 -0
  138. package/dist/lib/mcp/tools/roadmap.js +123 -0
  139. package/dist/lib/mcp/tools/spec.d.ts +6 -0
  140. package/dist/lib/mcp/tools/spec.js +196 -0
  141. package/dist/lib/mcp/tools/status.d.ts +6 -0
  142. package/dist/lib/mcp/tools/status.js +109 -0
  143. package/dist/lib/mcp/tools/template.d.ts +6 -0
  144. package/dist/lib/mcp/tools/template.js +107 -0
  145. package/dist/lib/mcp/tools/ticket.d.ts +6 -0
  146. package/dist/lib/mcp/tools/ticket.js +393 -0
  147. package/dist/lib/mcp/tools/view.d.ts +6 -0
  148. package/dist/lib/mcp/tools/view.js +76 -0
  149. package/dist/lib/mcp/tools/work.d.ts +6 -0
  150. package/dist/lib/mcp/tools/work.js +132 -0
  151. package/dist/lib/mcp/tools/workflow.d.ts +6 -0
  152. package/dist/lib/mcp/tools/workflow.js +95 -0
  153. package/dist/lib/mcp/types.d.ts +17 -0
  154. package/dist/lib/mcp/types.js +4 -0
  155. package/dist/lib/multiline-input.d.ts +63 -0
  156. package/dist/lib/multiline-input.js +360 -0
  157. package/dist/lib/prompt-json.d.ts +57 -6
  158. package/dist/lib/prompt-json.js +45 -0
  159. package/dist/lib/repos/git.d.ts +7 -0
  160. package/dist/lib/repos/git.js +20 -0
  161. package/oclif.manifest.json +3690 -4995
  162. package/package.json +6 -4
  163. package/dist/commands/agent/temp/index.d.ts +0 -14
  164. package/dist/commands/agent/temp/index.js +0 -85
  165. package/dist/commands/agent/temp/list.d.ts +0 -7
  166. package/dist/commands/agent/temp/list.js +0 -108
  167. package/dist/commands/epic/link/block.d.ts +0 -14
  168. package/dist/commands/epic/link/block.js +0 -81
  169. package/dist/commands/epic/link/duplicates.js +0 -68
  170. package/dist/commands/epic/link/index.d.ts +0 -19
  171. package/dist/commands/epic/link/index.js +0 -272
  172. package/dist/commands/epic/link/relates.js +0 -68
  173. package/dist/commands/epic/link/remove.js +0 -93
  174. package/dist/commands/phase/template/apply.d.ts +0 -17
  175. package/dist/commands/phase/template/apply.js +0 -108
  176. package/dist/commands/phase/template/create.d.ts +0 -17
  177. package/dist/commands/phase/template/create.js +0 -104
  178. package/dist/commands/phase/template/delete.d.ts +0 -17
  179. package/dist/commands/phase/template/delete.js +0 -100
  180. package/dist/commands/phase/template/index.d.ts +0 -15
  181. package/dist/commands/phase/template/index.js +0 -130
  182. package/dist/commands/phase/template/list.d.ts +0 -16
  183. package/dist/commands/phase/template/list.js +0 -97
  184. package/dist/commands/phase/template/update.js +0 -89
  185. package/dist/commands/spec/link/depends.d.ts +0 -14
  186. package/dist/commands/spec/link/depends.js +0 -64
  187. package/dist/commands/spec/link/duplicates.d.ts +0 -14
  188. package/dist/commands/spec/link/duplicates.js +0 -63
  189. package/dist/commands/spec/link/index.d.ts +0 -19
  190. package/dist/commands/spec/link/index.js +0 -207
  191. package/dist/commands/spec/link/relates.d.ts +0 -14
  192. package/dist/commands/spec/link/relates.js +0 -63
  193. package/dist/commands/spec/link/remove.js +0 -96
  194. package/dist/commands/template/phase/apply.d.ts +0 -14
  195. package/dist/commands/template/phase/apply.js +0 -43
  196. package/dist/commands/template/phase/create.d.ts +0 -13
  197. package/dist/commands/template/phase/create.js +0 -38
  198. package/dist/commands/template/phase/delete.js +0 -36
  199. package/dist/commands/template/phase/index.js +0 -63
  200. package/dist/commands/template/phase/list.d.ts +0 -11
  201. package/dist/commands/template/phase/list.js +0 -36
  202. package/dist/commands/template/phase/update.d.ts +0 -14
  203. package/dist/commands/template/phase/update.js +0 -43
  204. package/dist/commands/template/ticket/apply.d.ts +0 -17
  205. package/dist/commands/template/ticket/apply.js +0 -60
  206. package/dist/commands/template/ticket/create.d.ts +0 -20
  207. package/dist/commands/template/ticket/create.js +0 -89
  208. package/dist/commands/template/ticket/delete.d.ts +0 -13
  209. package/dist/commands/template/ticket/delete.js +0 -38
  210. package/dist/commands/template/ticket/index.js +0 -63
  211. package/dist/commands/template/ticket/list.d.ts +0 -11
  212. package/dist/commands/template/ticket/list.js +0 -36
  213. package/dist/commands/template/ticket/save.d.ts +0 -15
  214. package/dist/commands/template/ticket/save.js +0 -46
  215. package/dist/commands/ticket/link/block.d.ts +0 -14
  216. package/dist/commands/ticket/link/block.js +0 -96
  217. package/dist/commands/ticket/link/duplicates.d.ts +0 -14
  218. package/dist/commands/ticket/link/duplicates.js +0 -95
  219. package/dist/commands/ticket/link/index.d.ts +0 -19
  220. package/dist/commands/ticket/link/index.js +0 -256
  221. package/dist/commands/ticket/link/relates.d.ts +0 -14
  222. package/dist/commands/ticket/link/relates.js +0 -95
  223. package/dist/commands/ticket/link/remove.d.ts +0 -16
  224. package/dist/commands/ticket/link/remove.js +0 -132
  225. package/dist/commands/ticket/template/apply.js +0 -252
  226. package/dist/commands/ticket/template/create.js +0 -386
  227. package/dist/commands/ticket/template/delete.d.ts +0 -17
  228. package/dist/commands/ticket/template/delete.js +0 -94
  229. package/dist/commands/ticket/template/index.d.ts +0 -15
  230. package/dist/commands/ticket/template/index.js +0 -120
  231. package/dist/commands/ticket/template/list.d.ts +0 -16
  232. package/dist/commands/ticket/template/list.js +0 -112
  233. package/dist/commands/ticket/template/save.js +0 -163
  234. /package/dist/commands/{agent/staff → staff}/add.d.ts +0 -0
  235. /package/dist/commands/{agent/staff → staff}/list.d.ts +0 -0
@@ -0,0 +1,126 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { execSync } from 'node:child_process';
3
+ import chalk from 'chalk';
4
+ import { styles } from '../../lib/styles.js';
5
+ import { isGHInstalled, isGHAuthenticated } from '../../lib/pr/index.js';
6
+ import { isMachineOutput, outputSuccessAsJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
8
+ // Target repository for feedback issues
9
+ const FEEDBACK_REPO = 'chrismcdermut/proletariat';
10
+ // Category to GitHub label mapping
11
+ const CATEGORY_LABELS = {
12
+ bug: 'bug',
13
+ feature: 'enhancement',
14
+ general: 'feedback',
15
+ };
16
+ export default class FeedbackList extends Command {
17
+ static description = 'List recent feedback issues from the repository';
18
+ static examples = [
19
+ '<%= config.bin %> <%= command.id %>',
20
+ '<%= config.bin %> <%= command.id %> --category bug',
21
+ '<%= config.bin %> <%= command.id %> --state closed --limit 10',
22
+ '<%= config.bin %> <%= command.id %> --json',
23
+ ];
24
+ static flags = {
25
+ ...machineOutputFlags,
26
+ category: Flags.string({
27
+ char: 'c',
28
+ description: 'Filter by category (bug, feature, general)',
29
+ options: ['bug', 'feature', 'general'],
30
+ }),
31
+ state: Flags.string({
32
+ char: 's',
33
+ description: 'Filter by state',
34
+ options: ['open', 'closed', 'all'],
35
+ default: 'open',
36
+ }),
37
+ limit: Flags.integer({
38
+ char: 'l',
39
+ description: 'Maximum number of issues to show',
40
+ default: 20,
41
+ }),
42
+ };
43
+ async run() {
44
+ const { flags } = await this.parse(FeedbackList);
45
+ const jsonMode = isMachineOutput(flags);
46
+ // Helper to handle errors in JSON mode
47
+ const handleError = (code, message) => {
48
+ if (jsonMode) {
49
+ outputErrorAsJson(code, message, createMetadata('feedback list', flags));
50
+ }
51
+ this.error(message);
52
+ };
53
+ // Check if gh CLI is installed
54
+ if (!isGHInstalled()) {
55
+ return handleError('GH_NOT_INSTALLED', 'GitHub CLI (gh) is not installed. Install it with: brew install gh');
56
+ }
57
+ // Check if gh is authenticated
58
+ if (!isGHAuthenticated()) {
59
+ return handleError('GH_NOT_AUTHENTICATED', 'GitHub CLI is not authenticated. Run: gh auth login');
60
+ }
61
+ // Build the gh issue list command
62
+ const args = [
63
+ 'issue', 'list',
64
+ '--repo', FEEDBACK_REPO,
65
+ '--limit', String(flags.limit),
66
+ '--json', 'number,title,state,labels,createdAt,author',
67
+ ];
68
+ // Add state filter
69
+ if (flags.state && flags.state !== 'all') {
70
+ args.push('--state', flags.state);
71
+ }
72
+ // Add label filter for category
73
+ if (flags.category) {
74
+ const label = CATEGORY_LABELS[flags.category];
75
+ if (label) {
76
+ args.push('--label', label);
77
+ }
78
+ }
79
+ try {
80
+ const result = execSync(`gh ${args.join(' ')}`, {
81
+ encoding: 'utf-8',
82
+ stdio: ['pipe', 'pipe', 'pipe'],
83
+ }).trim();
84
+ const issues = JSON.parse(result || '[]');
85
+ if (jsonMode) {
86
+ outputSuccessAsJson({
87
+ issues: issues.map(issue => ({
88
+ number: issue.number,
89
+ title: issue.title,
90
+ state: issue.state,
91
+ labels: issue.labels.map(l => l.name),
92
+ createdAt: issue.createdAt,
93
+ author: issue.author.login,
94
+ url: `https://github.com/${FEEDBACK_REPO}/issues/${issue.number}`,
95
+ })),
96
+ count: issues.length,
97
+ repository: FEEDBACK_REPO,
98
+ filters: {
99
+ category: flags.category,
100
+ state: flags.state,
101
+ limit: flags.limit,
102
+ },
103
+ }, createMetadata('feedback list', flags));
104
+ }
105
+ // Display issues in human-friendly format
106
+ if (issues.length === 0) {
107
+ this.log(styles.muted('\nNo issues found matching your criteria.\n'));
108
+ return;
109
+ }
110
+ this.log(`\n${styles.header('Feedback Issues')} ${styles.muted(`(${FEEDBACK_REPO})`)}\n`);
111
+ for (const issue of issues) {
112
+ const stateIcon = issue.state === 'open' ? chalk.green('●') : chalk.red('●');
113
+ const labels = issue.labels.map(l => chalk.cyan(`[${l.name}]`)).join(' ');
114
+ const date = new Date(issue.createdAt).toLocaleDateString();
115
+ this.log(`${stateIcon} ${chalk.bold(`#${issue.number}`)} ${issue.title}`);
116
+ this.log(` ${labels} ${styles.muted(`by ${issue.author.login} on ${date}`)}`);
117
+ this.log('');
118
+ }
119
+ this.log(styles.muted(`Showing ${issues.length} issue(s). Use 'prlt feedback view <number>' to see details.\n`));
120
+ }
121
+ catch (error) {
122
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error listing issues';
123
+ return handleError('ISSUE_LIST_FAILED', `Failed to list issues: ${errorMessage}`);
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class FeedbackSubmit extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ title: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ body: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ category: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ /**
13
+ * Collect diagnostic information to include in the issue
14
+ */
15
+ private collectDiagnostics;
16
+ }
@@ -0,0 +1,220 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { execSync } from 'node:child_process';
3
+ import * as os from 'node:os';
4
+ import inquirer from 'inquirer';
5
+ import chalk from 'chalk';
6
+ import { styles } from '../../lib/styles.js';
7
+ import { isGHInstalled, isGHAuthenticated } from '../../lib/pr/index.js';
8
+ import { isMachineOutput, outputSuccessAsJson, outputErrorAsJson, outputPromptAsJson, buildPromptConfig, createMetadata, } from '../../lib/prompt-json.js';
9
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
10
+ // Target repository for feedback issues
11
+ const FEEDBACK_REPO = 'chrismcdermut/proletariat';
12
+ // Category to GitHub label mapping
13
+ const CATEGORY_LABELS = {
14
+ bug: 'bug',
15
+ feature: 'enhancement',
16
+ general: 'feedback',
17
+ };
18
+ export default class FeedbackSubmit extends Command {
19
+ static description = 'Submit feedback, bug reports, or feature requests to GitHub Issues';
20
+ static examples = [
21
+ '<%= config.bin %> <%= command.id %>',
22
+ '<%= config.bin %> <%= command.id %> --title "Bug in CLI" --body "Description here" --category bug',
23
+ '<%= config.bin %> <%= command.id %> --json',
24
+ ];
25
+ static flags = {
26
+ ...machineOutputFlags,
27
+ title: Flags.string({
28
+ char: 't',
29
+ description: 'Issue title (one-liner) [required for non-interactive]',
30
+ }),
31
+ body: Flags.string({
32
+ char: 'b',
33
+ description: 'Issue description [required for non-interactive]',
34
+ }),
35
+ category: Flags.string({
36
+ char: 'c',
37
+ description: 'Feedback category',
38
+ options: ['bug', 'feature', 'general'],
39
+ }),
40
+ };
41
+ async run() {
42
+ const { flags } = await this.parse(FeedbackSubmit);
43
+ const jsonMode = isMachineOutput(flags);
44
+ // Helper to handle errors in JSON mode
45
+ const handleError = (code, message) => {
46
+ if (jsonMode) {
47
+ outputErrorAsJson(code, message, createMetadata('feedback submit', flags));
48
+ }
49
+ this.error(message);
50
+ };
51
+ // Check if gh CLI is installed
52
+ if (!isGHInstalled()) {
53
+ return handleError('GH_NOT_INSTALLED', 'GitHub CLI (gh) is not installed. Install it with: brew install gh');
54
+ }
55
+ // Check if gh is authenticated
56
+ if (!isGHAuthenticated()) {
57
+ return handleError('GH_NOT_AUTHENTICATED', 'GitHub CLI is not authenticated. Run: gh auth login');
58
+ }
59
+ // Collect category
60
+ let category = flags.category;
61
+ if (!category) {
62
+ const categoryChoices = [
63
+ { name: 'Bug report', value: 'bug' },
64
+ { name: 'Feature request', value: 'feature' },
65
+ { name: 'General feedback', value: 'general' },
66
+ ];
67
+ const categoryMessage = 'What type of feedback would you like to submit?';
68
+ if (jsonMode) {
69
+ outputPromptAsJson(buildPromptConfig('list', 'category', categoryMessage, categoryChoices), createMetadata('feedback submit', flags));
70
+ return;
71
+ }
72
+ const { selectedCategory } = await inquirer.prompt([{
73
+ type: 'list',
74
+ name: 'selectedCategory',
75
+ message: categoryMessage,
76
+ choices: categoryChoices,
77
+ }]);
78
+ category = selectedCategory;
79
+ }
80
+ // Collect title
81
+ let title = flags.title;
82
+ if (!title) {
83
+ const titleMessage = 'Enter a brief title for your feedback:';
84
+ if (jsonMode) {
85
+ outputPromptAsJson(buildPromptConfig('input', 'title', titleMessage), createMetadata('feedback submit', { ...flags, category }));
86
+ return;
87
+ }
88
+ const { inputTitle } = await inquirer.prompt([{
89
+ type: 'input',
90
+ name: 'inputTitle',
91
+ message: titleMessage,
92
+ validate: (input) => {
93
+ if (!input.trim()) {
94
+ return 'Title is required';
95
+ }
96
+ if (input.length > 200) {
97
+ return 'Title must be less than 200 characters';
98
+ }
99
+ return true;
100
+ },
101
+ }]);
102
+ title = inputTitle;
103
+ }
104
+ // Collect body/description
105
+ let body = flags.body;
106
+ if (!body) {
107
+ const bodyMessage = 'Describe your feedback in detail:';
108
+ if (jsonMode) {
109
+ outputPromptAsJson(buildPromptConfig('editor', 'body', bodyMessage), createMetadata('feedback submit', { ...flags, category, title }));
110
+ return;
111
+ }
112
+ const { inputBody } = await inquirer.prompt([{
113
+ type: 'editor',
114
+ name: 'inputBody',
115
+ message: bodyMessage,
116
+ validate: (input) => {
117
+ if (!input.trim()) {
118
+ return 'Description is required';
119
+ }
120
+ return true;
121
+ },
122
+ }]);
123
+ body = inputBody;
124
+ }
125
+ // Build diagnostics section
126
+ const diagnostics = this.collectDiagnostics();
127
+ // Build full issue body with diagnostics
128
+ const fullBody = `${body}\n\n---\n\n## Diagnostics\n\n${diagnostics}`;
129
+ // Get label for category
130
+ const label = CATEGORY_LABELS[category] || 'feedback';
131
+ // Create the GitHub issue
132
+ if (!jsonMode) {
133
+ this.log(styles.muted('\nCreating issue on GitHub...\n'));
134
+ }
135
+ // Escape title for shell
136
+ const escapedTitle = title.replace(/"/g, '\\"');
137
+ let usedLabel = label;
138
+ let result;
139
+ try {
140
+ // Try with label first
141
+ result = execSync(`gh issue create --repo "${FEEDBACK_REPO}" --title "${escapedTitle}" --body-file - --label "${label}"`, {
142
+ encoding: 'utf-8',
143
+ input: fullBody,
144
+ stdio: ['pipe', 'pipe', 'pipe'],
145
+ }).trim();
146
+ }
147
+ catch (labelError) {
148
+ // If label doesn't exist, try without label
149
+ const labelErrorMessage = labelError instanceof Error ? labelError.message : '';
150
+ if (labelErrorMessage.includes('not found') && labelErrorMessage.includes('label')) {
151
+ try {
152
+ usedLabel = null;
153
+ result = execSync(`gh issue create --repo "${FEEDBACK_REPO}" --title "${escapedTitle}" --body-file -`, {
154
+ encoding: 'utf-8',
155
+ input: fullBody,
156
+ stdio: ['pipe', 'pipe', 'pipe'],
157
+ }).trim();
158
+ }
159
+ catch (error) {
160
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error creating issue';
161
+ return handleError('ISSUE_CREATE_FAILED', `Failed to create issue: ${errorMessage}`);
162
+ }
163
+ }
164
+ else {
165
+ const errorMessage = labelError instanceof Error ? labelError.message : 'Unknown error creating issue';
166
+ return handleError('ISSUE_CREATE_FAILED', `Failed to create issue: ${errorMessage}`);
167
+ }
168
+ }
169
+ // Parse issue URL from result
170
+ const issueUrl = result;
171
+ const issueMatch = issueUrl.match(/\/issues\/(\d+)/);
172
+ const issueNumber = issueMatch ? parseInt(issueMatch[1], 10) : undefined;
173
+ if (jsonMode) {
174
+ outputSuccessAsJson({
175
+ issueUrl,
176
+ issueNumber,
177
+ title: title,
178
+ category: category,
179
+ label: usedLabel,
180
+ repository: FEEDBACK_REPO,
181
+ }, createMetadata('feedback submit', flags));
182
+ }
183
+ this.log(chalk.green(' ✓ Issue created successfully!\n'));
184
+ this.log(`${styles.header('Issue URL:')} ${chalk.cyan(issueUrl)}`);
185
+ if (issueNumber) {
186
+ this.log(`${styles.header('Issue #:')} ${issueNumber}`);
187
+ }
188
+ this.log(`${styles.header('Category:')} ${category}`);
189
+ if (usedLabel) {
190
+ this.log(`${styles.header('Label:')} ${usedLabel}\n`);
191
+ }
192
+ else {
193
+ this.log(styles.muted(`(Label '${label}' not found in repository)\n`));
194
+ }
195
+ this.log(styles.muted('Thank you for your feedback!'));
196
+ }
197
+ /**
198
+ * Collect diagnostic information to include in the issue
199
+ */
200
+ collectDiagnostics() {
201
+ const lines = [];
202
+ // CLI version
203
+ lines.push(`- **CLI Version:** ${this.config.version}`);
204
+ // Node version
205
+ lines.push(`- **Node Version:** ${process.version}`);
206
+ // OS info
207
+ lines.push(`- **OS:** ${process.platform}`);
208
+ lines.push(`- **OS Version:** ${os.release()}`);
209
+ // Try to get workspace name if available
210
+ try {
211
+ const cwd = process.cwd();
212
+ const workspaceName = cwd.split('/').pop() || 'unknown';
213
+ lines.push(`- **Working Directory:** ${workspaceName}`);
214
+ }
215
+ catch {
216
+ // Ignore errors getting workspace name
217
+ }
218
+ return lines.join('\n');
219
+ }
220
+ }
@@ -1,13 +1,15 @@
1
1
  import { Command } from '@oclif/core';
2
- export default class TemplatePhaseDelete extends Command {
2
+ export default class FeedbackView extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
- static args: {
6
- id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
- };
8
5
  static flags: {
9
- force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
6
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
7
  };
8
+ static args: {
9
+ number: import("@oclif/core/interfaces").Arg<number, {
10
+ max?: number;
11
+ min?: number;
12
+ }>;
13
+ };
12
14
  run(): Promise<void>;
13
15
  }
@@ -0,0 +1,109 @@
1
+ import { Command, Args } from '@oclif/core';
2
+ import { execSync } from 'node:child_process';
3
+ import chalk from 'chalk';
4
+ import { styles } from '../../lib/styles.js';
5
+ import { isGHInstalled, isGHAuthenticated } from '../../lib/pr/index.js';
6
+ import { isMachineOutput, outputSuccessAsJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
8
+ // Target repository for feedback issues
9
+ const FEEDBACK_REPO = 'chrismcdermut/proletariat';
10
+ export default class FeedbackView extends Command {
11
+ static description = 'View details of a specific feedback issue';
12
+ static examples = [
13
+ '<%= config.bin %> <%= command.id %> 123',
14
+ '<%= config.bin %> <%= command.id %> 123 --json',
15
+ ];
16
+ static flags = {
17
+ ...machineOutputFlags,
18
+ };
19
+ static args = {
20
+ number: Args.integer({
21
+ description: 'Issue number to view',
22
+ required: true,
23
+ }),
24
+ };
25
+ async run() {
26
+ const { args, flags } = await this.parse(FeedbackView);
27
+ const jsonMode = isMachineOutput(flags);
28
+ const issueNumber = args.number;
29
+ // Helper to handle errors in JSON mode
30
+ const handleError = (code, message) => {
31
+ if (jsonMode) {
32
+ outputErrorAsJson(code, message, createMetadata('feedback view', flags));
33
+ }
34
+ this.error(message);
35
+ };
36
+ // Check if gh CLI is installed
37
+ if (!isGHInstalled()) {
38
+ return handleError('GH_NOT_INSTALLED', 'GitHub CLI (gh) is not installed. Install it with: brew install gh');
39
+ }
40
+ // Check if gh is authenticated
41
+ if (!isGHAuthenticated()) {
42
+ return handleError('GH_NOT_AUTHENTICATED', 'GitHub CLI is not authenticated. Run: gh auth login');
43
+ }
44
+ try {
45
+ // Get issue details
46
+ const result = execSync(`gh issue view ${issueNumber} --repo "${FEEDBACK_REPO}" --json number,title,body,state,labels,createdAt,updatedAt,author,comments`, {
47
+ encoding: 'utf-8',
48
+ stdio: ['pipe', 'pipe', 'pipe'],
49
+ }).trim();
50
+ const issue = JSON.parse(result);
51
+ if (jsonMode) {
52
+ outputSuccessAsJson({
53
+ number: issue.number,
54
+ title: issue.title,
55
+ body: issue.body,
56
+ state: issue.state,
57
+ labels: issue.labels.map(l => l.name),
58
+ createdAt: issue.createdAt,
59
+ updatedAt: issue.updatedAt,
60
+ author: issue.author.login,
61
+ comments: issue.comments.map(c => ({
62
+ author: c.author.login,
63
+ body: c.body,
64
+ createdAt: c.createdAt,
65
+ })),
66
+ url: `https://github.com/${FEEDBACK_REPO}/issues/${issue.number}`,
67
+ repository: FEEDBACK_REPO,
68
+ }, createMetadata('feedback view', flags));
69
+ }
70
+ // Display issue in human-friendly format
71
+ const stateIcon = issue.state === 'OPEN' ? chalk.green('OPEN') : chalk.red('CLOSED');
72
+ const labels = issue.labels.map(l => chalk.cyan(`[${l.name}]`)).join(' ');
73
+ const createdDate = new Date(issue.createdAt).toLocaleString();
74
+ const updatedDate = new Date(issue.updatedAt).toLocaleString();
75
+ this.log(`\n${styles.header('Issue #')}${chalk.bold(String(issue.number))} ${stateIcon}\n`);
76
+ this.log(`${styles.header('Title:')} ${issue.title}`);
77
+ this.log(`${styles.header('Author:')} ${issue.author.login}`);
78
+ this.log(`${styles.header('Labels:')} ${labels || styles.muted('none')}`);
79
+ this.log(`${styles.header('Created:')} ${createdDate}`);
80
+ this.log(`${styles.header('Updated:')} ${updatedDate}`);
81
+ this.log(`${styles.header('URL:')} ${chalk.cyan(`https://github.com/${FEEDBACK_REPO}/issues/${issue.number}`)}`);
82
+ if (issue.body) {
83
+ this.log(`\n${styles.header('Description:')}`);
84
+ this.log(styles.muted('─'.repeat(50)));
85
+ this.log(issue.body);
86
+ this.log(styles.muted('─'.repeat(50)));
87
+ }
88
+ if (issue.comments && issue.comments.length > 0) {
89
+ this.log(`\n${styles.header('Comments:')} (${issue.comments.length})`);
90
+ this.log(styles.muted('─'.repeat(50)));
91
+ for (const comment of issue.comments) {
92
+ const commentDate = new Date(comment.createdAt).toLocaleString();
93
+ this.log(`\n${chalk.bold(comment.author.login)} ${styles.muted(`on ${commentDate}`)}`);
94
+ this.log(comment.body);
95
+ }
96
+ this.log(styles.muted('─'.repeat(50)));
97
+ }
98
+ this.log('');
99
+ }
100
+ catch (error) {
101
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error viewing issue';
102
+ // Check if it's a "not found" error
103
+ if (errorMessage.includes('not found') || errorMessage.includes('Could not resolve')) {
104
+ return handleError('ISSUE_NOT_FOUND', `Issue #${issueNumber} not found in ${FEEDBACK_REPO}`);
105
+ }
106
+ return handleError('ISSUE_VIEW_FAILED', `Failed to view issue: ${errorMessage}`);
107
+ }
108
+ }
109
+ }
@@ -32,6 +32,7 @@ export default class GH extends Command {
32
32
  { name: 'Check status', value: 'status', command: 'prlt gh status --json' },
33
33
  { name: 'Login to GitHub', value: 'login', command: 'prlt gh login --json' },
34
34
  { name: 'Show GH_TOKEN setup', value: 'token', command: 'prlt gh token --json' },
35
+ { name: 'Create GitHub repository', value: 'repo-create', command: 'prlt repo create --json' },
35
36
  ],
36
37
  skipAutoCommand: true, // Use our custom commands above
37
38
  });
@@ -48,6 +49,9 @@ export default class GH extends Command {
48
49
  case 'token':
49
50
  await this.config.runCommand('gh token');
50
51
  break;
52
+ case 'repo-create':
53
+ await this.config.runCommand('repo create');
54
+ break;
51
55
  }
52
56
  }
53
57
  }
@@ -1,16 +1,15 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class EpicLinkRemove extends PMOCommand {
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class LinkCreate extends PMOCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
- id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
- target: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
6
+ from: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ to: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
8
8
  };
9
9
  static flags: {
10
- project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
- type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
- all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ type: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
11
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
13
  };
15
14
  execute(): Promise<void>;
16
15
  }