@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,11 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class SupportIssues extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ browser: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ };
9
+ run(): Promise<void>;
10
+ private openUrl;
11
+ }
@@ -0,0 +1,77 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { execSync } from 'node:child_process';
3
+ import { styles } from '../../lib/styles.js';
4
+ import { isMachineOutput, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
5
+ import { machineOutputFlags } from '../../lib/pmo/index.js';
6
+ import { SUPPORT_URLS } from './index.js';
7
+ export default class SupportIssues extends Command {
8
+ static description = 'Browse GitHub Issues';
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %>',
11
+ '<%= config.bin %> <%= command.id %> --browser',
12
+ '<%= config.bin %> <%= command.id %> --json',
13
+ ];
14
+ static flags = {
15
+ ...machineOutputFlags,
16
+ browser: Flags.boolean({
17
+ description: 'Open issues in browser (default behavior)',
18
+ default: true,
19
+ allowNo: true,
20
+ }),
21
+ };
22
+ async run() {
23
+ const { flags } = await this.parse(SupportIssues);
24
+ const url = SUPPORT_URLS.issues;
25
+ // In JSON mode, return the URL
26
+ if (isMachineOutput(flags)) {
27
+ outputSuccessAsJson({
28
+ action: 'issues',
29
+ url,
30
+ message: 'Browse GitHub Issues',
31
+ }, createMetadata('support issues', flags));
32
+ return;
33
+ }
34
+ // Open in browser (default)
35
+ if (flags.browser) {
36
+ this.openUrl(url);
37
+ this.log(styles.success('Opening GitHub Issues...'));
38
+ this.log(styles.muted(`URL: ${url}`));
39
+ }
40
+ else {
41
+ // List issues via gh CLI when --no-browser is set
42
+ try {
43
+ const output = execSync('gh issue list --repo chrismcdermut/proletariat', {
44
+ encoding: 'utf-8',
45
+ stdio: ['ignore', 'pipe', 'pipe'],
46
+ });
47
+ this.log(styles.header('Open Issues'));
48
+ this.log(output.trim() || styles.muted('No open issues found.'));
49
+ }
50
+ catch {
51
+ this.log(styles.warning('Could not list issues via gh CLI.'));
52
+ this.log(styles.info(`Please visit: ${url}`));
53
+ }
54
+ }
55
+ }
56
+ openUrl(url) {
57
+ const platform = process.platform;
58
+ try {
59
+ if (platform === 'darwin') {
60
+ execSync(`open "${url}"`);
61
+ }
62
+ else if (platform === 'linux') {
63
+ execSync(`xdg-open "${url}"`);
64
+ }
65
+ else if (platform === 'win32') {
66
+ execSync(`start "" "${url}"`);
67
+ }
68
+ else {
69
+ throw new Error(`Unsupported platform: ${platform}`);
70
+ }
71
+ }
72
+ catch {
73
+ this.log(styles.warning('Could not open browser automatically.'));
74
+ this.log(styles.info(`Please visit: ${url}`));
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,18 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class SupportLogs extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ clipboard: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ protected execute(): Promise<void>;
11
+ private collectDiagnostics;
12
+ private checkGh;
13
+ private checkDocker;
14
+ private checkTmux;
15
+ private getWorkspaceInfo;
16
+ private formatDiagnostics;
17
+ private copyToClipboard;
18
+ }
@@ -0,0 +1,247 @@
1
+ import { Flags } from '@oclif/core';
2
+ import * as os from 'node:os';
3
+ import { execSync } from 'node:child_process';
4
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
5
+ import { styles } from '../../lib/styles.js';
6
+ import { isMachineOutput, outputSuccessAsJson, createMetadata, } from '../../lib/prompt-json.js';
7
+ export default class SupportLogs extends PMOCommand {
8
+ static description = 'Collect diagnostic info for troubleshooting';
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %>',
11
+ '<%= config.bin %> <%= command.id %> --clipboard',
12
+ '<%= config.bin %> <%= command.id %> --json',
13
+ ];
14
+ static flags = {
15
+ ...pmoBaseFlags,
16
+ clipboard: Flags.boolean({
17
+ description: 'Copy diagnostics to clipboard',
18
+ default: false,
19
+ }),
20
+ };
21
+ async execute() {
22
+ const { flags } = await this.parse(SupportLogs);
23
+ const diagnostics = await this.collectDiagnostics();
24
+ // In JSON mode, return structured object
25
+ if (isMachineOutput(flags)) {
26
+ outputSuccessAsJson(diagnostics, createMetadata('support logs', flags));
27
+ return;
28
+ }
29
+ // Format as readable text
30
+ const text = this.formatDiagnostics(diagnostics);
31
+ // Copy to clipboard if requested
32
+ if (flags.clipboard) {
33
+ const copied = this.copyToClipboard(text);
34
+ if (copied) {
35
+ this.log(styles.success('Diagnostics copied to clipboard!'));
36
+ this.log('');
37
+ }
38
+ }
39
+ // Display diagnostics
40
+ this.log(text);
41
+ }
42
+ async collectDiagnostics() {
43
+ const diagnostics = {
44
+ prlt: {
45
+ version: this.config.version,
46
+ },
47
+ node: {
48
+ version: process.version,
49
+ },
50
+ os: {
51
+ platform: process.platform,
52
+ release: os.release(),
53
+ arch: os.arch(),
54
+ },
55
+ shell: process.env.SHELL,
56
+ tools: {
57
+ gh: this.checkGh(),
58
+ docker: this.checkDocker(),
59
+ tmux: this.checkTmux(),
60
+ },
61
+ };
62
+ // Add workspace info if available
63
+ try {
64
+ const workspaceInfo = await this.getWorkspaceInfo();
65
+ if (workspaceInfo) {
66
+ diagnostics.workspace = workspaceInfo;
67
+ }
68
+ }
69
+ catch {
70
+ // PMO may not be initialized
71
+ }
72
+ return diagnostics;
73
+ }
74
+ checkGh() {
75
+ try {
76
+ const versionOutput = execSync('gh --version', {
77
+ encoding: 'utf-8',
78
+ stdio: ['ignore', 'pipe', 'ignore'],
79
+ });
80
+ const version = versionOutput.split('\n')[0]?.replace('gh version ', '').trim();
81
+ let authenticated = false;
82
+ try {
83
+ execSync('gh auth status', { stdio: 'ignore' });
84
+ authenticated = true;
85
+ }
86
+ catch {
87
+ authenticated = false;
88
+ }
89
+ return { installed: true, version, authenticated };
90
+ }
91
+ catch {
92
+ return { installed: false };
93
+ }
94
+ }
95
+ checkDocker() {
96
+ try {
97
+ execSync('docker --version', { stdio: 'ignore' });
98
+ let running = false;
99
+ try {
100
+ execSync('docker info', { stdio: 'ignore' });
101
+ running = true;
102
+ }
103
+ catch {
104
+ running = false;
105
+ }
106
+ return { installed: true, running };
107
+ }
108
+ catch {
109
+ return { installed: false };
110
+ }
111
+ }
112
+ checkTmux() {
113
+ try {
114
+ const versionOutput = execSync('tmux -V', {
115
+ encoding: 'utf-8',
116
+ stdio: ['ignore', 'pipe', 'ignore'],
117
+ });
118
+ const version = versionOutput.trim();
119
+ return { installed: true, version };
120
+ }
121
+ catch {
122
+ return { installed: false };
123
+ }
124
+ }
125
+ async getWorkspaceInfo() {
126
+ try {
127
+ const projects = await this.storage.listProjects();
128
+ let totalTickets = 0;
129
+ for (const project of projects) {
130
+ // eslint-disable-next-line no-await-in-loop
131
+ const tickets = await this.storage.listTickets(project.id);
132
+ totalTickets += tickets.length;
133
+ }
134
+ // Get agents count by checking for agents directory
135
+ let agentCount = 0;
136
+ try {
137
+ const { getWorkspaceInfo } = await import('../../lib/agents/commands.js');
138
+ const wsInfo = getWorkspaceInfo();
139
+ agentCount = wsInfo.agents?.length || 0;
140
+ }
141
+ catch {
142
+ // Agents module may not be available
143
+ }
144
+ return {
145
+ path: this.pmoPath,
146
+ name: projects[0]?.name,
147
+ repoCount: projects.length,
148
+ agentCount,
149
+ ticketCount: totalTickets,
150
+ };
151
+ }
152
+ catch {
153
+ return undefined;
154
+ }
155
+ }
156
+ formatDiagnostics(d) {
157
+ const lines = [];
158
+ lines.push(styles.title('Diagnostic Information'));
159
+ lines.push('');
160
+ // System info
161
+ lines.push(styles.header('System'));
162
+ lines.push(` prlt version: ${d.prlt.version}`);
163
+ lines.push(` Node version: ${d.node.version}`);
164
+ lines.push(` OS: ${d.os.platform} ${d.os.release} (${d.os.arch})`);
165
+ lines.push(` Shell: ${d.shell || 'unknown'}`);
166
+ lines.push('');
167
+ // Tools
168
+ lines.push(styles.header('Tools'));
169
+ // gh
170
+ if (d.tools.gh.installed) {
171
+ const authStatus = d.tools.gh.authenticated ? styles.success('authenticated') : styles.warning('not authenticated');
172
+ lines.push(` gh CLI: ${styles.success('installed')} (${d.tools.gh.version}) - ${authStatus}`);
173
+ }
174
+ else {
175
+ lines.push(` gh CLI: ${styles.warning('not installed')}`);
176
+ }
177
+ // docker
178
+ if (d.tools.docker.installed) {
179
+ const runStatus = d.tools.docker.running ? styles.success('running') : styles.warning('not running');
180
+ lines.push(` Docker: ${styles.success('installed')} - ${runStatus}`);
181
+ }
182
+ else {
183
+ lines.push(` Docker: ${styles.warning('not installed')}`);
184
+ }
185
+ // tmux
186
+ if (d.tools.tmux.installed) {
187
+ lines.push(` tmux: ${styles.success('installed')} (${d.tools.tmux.version})`);
188
+ }
189
+ else {
190
+ lines.push(` tmux: ${styles.muted('not installed')}`);
191
+ }
192
+ lines.push('');
193
+ // Workspace
194
+ if (d.workspace) {
195
+ lines.push(styles.header('Workspace'));
196
+ lines.push(` Path: ${d.workspace.path}`);
197
+ if (d.workspace.name) {
198
+ lines.push(` Name: ${d.workspace.name}`);
199
+ }
200
+ lines.push(` Projects: ${d.workspace.repoCount ?? 0}`);
201
+ lines.push(` Agents: ${d.workspace.agentCount ?? 0}`);
202
+ lines.push(` Tickets: ${d.workspace.ticketCount ?? 0}`);
203
+ }
204
+ else {
205
+ lines.push(styles.header('Workspace'));
206
+ lines.push(styles.muted(' No workspace initialized'));
207
+ }
208
+ return lines.join('\n');
209
+ }
210
+ copyToClipboard(text) {
211
+ const platform = process.platform;
212
+ // Strip ANSI codes for clipboard
213
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional ANSI escape code stripping
214
+ const plainText = text.replace(/\x1b\[[0-9;]*m/g, '');
215
+ try {
216
+ if (platform === 'darwin') {
217
+ execSync('pbcopy', { input: plainText, encoding: 'utf-8' });
218
+ return true;
219
+ }
220
+ else if (platform === 'linux') {
221
+ // Try xclip first, then xsel
222
+ try {
223
+ execSync('xclip -selection clipboard', { input: plainText, encoding: 'utf-8' });
224
+ return true;
225
+ }
226
+ catch {
227
+ try {
228
+ execSync('xsel --clipboard --input', { input: plainText, encoding: 'utf-8' });
229
+ return true;
230
+ }
231
+ catch {
232
+ this.log(styles.warning('Install xclip or xsel to enable clipboard support.'));
233
+ return false;
234
+ }
235
+ }
236
+ }
237
+ else if (platform === 'win32') {
238
+ execSync('clip', { input: plainText, encoding: 'utf-8' });
239
+ return true;
240
+ }
241
+ }
242
+ catch {
243
+ this.log(styles.warning('Could not copy to clipboard.'));
244
+ }
245
+ return false;
246
+ }
247
+ }
@@ -1,25 +1,27 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class TicketTemplateApply extends PMOCommand {
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class TemplateApply extends PMOCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
6
  template: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
+ type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  title: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  column: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  priority: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  category: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  assignee: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  owner: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
- status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
- labels: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
16
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ epic: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ 'no-subtasks': import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
19
  interactive: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
21
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
- 'no-subtasks': import("@oclif/core/interfaces").BooleanFlag<boolean>;
21
- epic: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
22
  project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
23
  };
24
24
  execute(): Promise<void>;
25
+ private applyTicketTemplate;
26
+ private applyPhaseTemplate;
25
27
  }
@@ -0,0 +1,262 @@
1
+ import { Flags, Args } from '@oclif/core';
2
+ import inquirer from 'inquirer';
3
+ import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
4
+ import { PRIORITIES, PRIORITY_LABELS } from '../../lib/pmo/types.js';
5
+ import { styles } from '../../lib/styles.js';
6
+ import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildFormPromptConfig, buildPromptConfig, } from '../../lib/prompt-json.js';
7
+ export default class TemplateApply extends PMOCommand {
8
+ static description = 'Apply a template';
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %> --type ticket bug-report',
11
+ '<%= config.bin %> <%= command.id %> --type ticket bug-report --title "Login fails"',
12
+ '<%= config.bin %> <%= command.id %> --type phase agile',
13
+ '<%= config.bin %> <%= command.id %> --type phase default --force',
14
+ ];
15
+ static args = {
16
+ template: Args.string({
17
+ description: 'Template ID to apply',
18
+ required: false,
19
+ }),
20
+ };
21
+ static flags = {
22
+ ...pmoBaseFlags,
23
+ type: Flags.string({
24
+ char: 't',
25
+ description: 'Template type',
26
+ options: ['ticket', 'phase'],
27
+ }),
28
+ // Ticket-specific flags
29
+ title: Flags.string({
30
+ description: 'Ticket title (ticket only)',
31
+ }),
32
+ column: Flags.string({
33
+ char: 'c',
34
+ description: 'Column to place ticket (ticket only)',
35
+ }),
36
+ priority: Flags.string({
37
+ char: 'p',
38
+ description: 'Priority override (ticket only)',
39
+ options: [...PRIORITIES],
40
+ }),
41
+ category: Flags.string({
42
+ description: 'Category override (ticket only)',
43
+ }),
44
+ assignee: Flags.string({
45
+ char: 'a',
46
+ description: 'Assignee (ticket only)',
47
+ }),
48
+ owner: Flags.string({
49
+ char: 'o',
50
+ description: 'Owner (ticket only)',
51
+ }),
52
+ description: Flags.string({
53
+ char: 'd',
54
+ description: 'Description override (ticket only)',
55
+ }),
56
+ epic: Flags.string({
57
+ char: 'e',
58
+ description: 'Link to epic (ticket only)',
59
+ }),
60
+ 'no-subtasks': Flags.boolean({
61
+ description: 'Skip creating subtasks (ticket only)',
62
+ default: false,
63
+ }),
64
+ interactive: Flags.boolean({
65
+ char: 'i',
66
+ description: 'Interactive mode (ticket only)',
67
+ default: false,
68
+ }),
69
+ // Phase-specific flags
70
+ force: Flags.boolean({
71
+ char: 'f',
72
+ description: 'Skip confirmation (phase only)',
73
+ default: false,
74
+ }),
75
+ json: Flags.boolean({
76
+ char: 'm',
77
+ aliases: ['machine'],
78
+ description: 'Output as JSON for AI agents/scripts',
79
+ default: false,
80
+ }),
81
+ };
82
+ async execute() {
83
+ const { args, flags } = await this.parse(TemplateApply);
84
+ const jsonMode = shouldOutputJson(flags);
85
+ // Determine template type
86
+ let templateType = flags.type;
87
+ if (!templateType) {
88
+ if (jsonMode) {
89
+ outputPromptAsJson(buildPromptConfig('list', 'type', 'What type of template?', [
90
+ { name: 'Ticket template', value: 'ticket' },
91
+ { name: 'Phase template', value: 'phase' },
92
+ ]), createMetadata('template apply', flags));
93
+ return;
94
+ }
95
+ const { selectedType } = await inquirer.prompt([{
96
+ type: 'list',
97
+ name: 'selectedType',
98
+ message: 'What type of template?',
99
+ choices: [
100
+ { name: 'Ticket template', value: 'ticket' },
101
+ { name: 'Phase template', value: 'phase' },
102
+ ],
103
+ }]);
104
+ templateType = selectedType;
105
+ }
106
+ if (templateType === 'ticket') {
107
+ await this.applyTicketTemplate(args.template, flags, jsonMode);
108
+ }
109
+ else {
110
+ await this.applyPhaseTemplate(args.template, flags, jsonMode);
111
+ }
112
+ }
113
+ async applyTicketTemplate(templateId, flags, jsonMode) {
114
+ const projectId = await this.requireProject();
115
+ const board = await this.storage.getBoard(projectId);
116
+ const columns = board.columns.map(col => col.name);
117
+ const handleError = (code, message) => {
118
+ if (jsonMode) {
119
+ outputErrorAsJson(code, message, createMetadata('template apply', flags));
120
+ this.exit(1);
121
+ }
122
+ this.error(message);
123
+ };
124
+ // Get template
125
+ if (!templateId) {
126
+ const templates = await this.storage.listTicketTemplates();
127
+ if (templates.length === 0) {
128
+ return handleError('NO_TEMPLATES', 'No ticket templates. Create with: prlt template create --type ticket');
129
+ }
130
+ const { selected } = await inquirer.prompt([{
131
+ type: 'list',
132
+ name: 'selected',
133
+ message: 'Select template:',
134
+ choices: templates.map(t => ({ name: `${t.name}${t.description ? ` - ${t.description}` : ''}`, value: t.id })),
135
+ }]);
136
+ templateId = selected;
137
+ }
138
+ const template = await this.storage.getTicketTemplate(templateId);
139
+ if (!template) {
140
+ return handleError('TEMPLATE_NOT_FOUND', `Template not found: ${templateId}`);
141
+ }
142
+ // Determine ticket data
143
+ let title = flags.title || template.titlePattern || '';
144
+ let column = flags.column || columns[0];
145
+ let priority = flags.priority || template.defaultPriority;
146
+ let category = flags.category || template.defaultCategory;
147
+ let assignee = flags.assignee || template.defaultAssignee;
148
+ let owner = flags.owner || template.defaultOwner;
149
+ let description = flags.description || template.descriptionTemplate;
150
+ const labels = template.defaultLabels;
151
+ // Interactive mode
152
+ if (flags.interactive || !title) {
153
+ const fields = [
154
+ { type: 'input', name: 'title', message: 'Title:', default: title || undefined },
155
+ { type: 'list', name: 'column', message: 'Column:', choices: columns.map(c => ({ name: c, value: c })), default: column },
156
+ { type: 'list', name: 'priority', message: 'Priority:', choices: [{ name: 'None', value: '' }, ...PRIORITIES.map(p => ({ name: PRIORITY_LABELS[p], value: p }))], default: priority },
157
+ ];
158
+ if (jsonMode) {
159
+ outputPromptAsJson(buildFormPromptConfig(fields), createMetadata('template apply', flags));
160
+ return;
161
+ }
162
+ const answers = await inquirer.prompt(fields.map(f => ({
163
+ ...f,
164
+ validate: f.name === 'title' ? ((i) => i.length > 0 || 'Required') : undefined,
165
+ })));
166
+ title = answers.title;
167
+ column = answers.column;
168
+ priority = answers.priority || undefined;
169
+ }
170
+ // Validate epic if provided
171
+ if (flags.epic) {
172
+ const epic = await this.storage.getEpic(flags.epic);
173
+ if (!epic)
174
+ this.error(`Epic not found: ${flags.epic}`);
175
+ }
176
+ // Create ticket
177
+ const ticket = await this.storage.createTicket(projectId, {
178
+ title,
179
+ statusName: column,
180
+ priority,
181
+ category,
182
+ assignee,
183
+ owner,
184
+ labels,
185
+ description,
186
+ epicId: flags.epic,
187
+ });
188
+ // Add subtasks
189
+ if (!flags['no-subtasks'] && template.suggestedSubtasks.length > 0) {
190
+ for (const subtask of template.suggestedSubtasks) {
191
+ await this.storage.addSubtask(ticket.id, subtask.title);
192
+ }
193
+ }
194
+ await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
195
+ this.log(styles.success(`\nCreated ticket ${styles.emphasis(ticket.id)} from template "${template.name}"`));
196
+ this.log(styles.muted(` Title: ${ticket.title}`));
197
+ this.log(styles.muted(` Status: ${ticket.statusName}`));
198
+ if (priority)
199
+ this.log(styles.muted(` Priority: ${priority}`));
200
+ if (template.suggestedSubtasks.length > 0 && !flags['no-subtasks']) {
201
+ this.log(styles.muted(` Subtasks: ${template.suggestedSubtasks.length} created`));
202
+ }
203
+ }
204
+ async applyPhaseTemplate(templateId, flags, jsonMode) {
205
+ const handleError = (code, message) => {
206
+ if (jsonMode) {
207
+ outputErrorAsJson(code, message, createMetadata('template apply', flags));
208
+ this.exit(1);
209
+ }
210
+ this.error(message);
211
+ };
212
+ // Get template
213
+ if (!templateId) {
214
+ const templates = await this.storage.listPhaseTemplates();
215
+ if (templates.length === 0) {
216
+ return handleError('NO_TEMPLATES', 'No phase templates. Create with: prlt template create --type phase');
217
+ }
218
+ const { selected } = await inquirer.prompt([{
219
+ type: 'list',
220
+ name: 'selected',
221
+ message: 'Select phase template:',
222
+ choices: templates.map(t => ({ name: `${t.name}${t.description ? ` - ${t.description}` : ''}`, value: t.id })),
223
+ }]);
224
+ templateId = selected;
225
+ }
226
+ const template = await this.storage.getPhaseTemplate(templateId);
227
+ if (!template) {
228
+ return handleError('TEMPLATE_NOT_FOUND', `Template not found: ${templateId}`);
229
+ }
230
+ // Check existing phases
231
+ const existingPhases = await this.storage.listPhases();
232
+ if (existingPhases.length > 0 && !flags.force) {
233
+ if (jsonMode) {
234
+ outputPromptAsJson(buildPromptConfig('list', 'confirmed', `Replace ${existingPhases.length} existing phase(s)?`, [
235
+ { name: 'No', value: 'false' },
236
+ { name: 'Yes', value: 'true' },
237
+ ]), createMetadata('template apply', flags));
238
+ return;
239
+ }
240
+ this.log(styles.warning(`\nThis will REPLACE ${existingPhases.length} existing phase(s).`));
241
+ const { confirm } = await inquirer.prompt([{
242
+ type: 'list',
243
+ name: 'confirm',
244
+ message: `Apply template "${template.name}"?`,
245
+ choices: [
246
+ { name: 'No', value: false },
247
+ { name: 'Yes', value: true },
248
+ ],
249
+ }]);
250
+ if (!confirm) {
251
+ this.log(styles.muted('Cancelled'));
252
+ return;
253
+ }
254
+ }
255
+ const phases = await this.storage.applyPhaseTemplate(templateId);
256
+ this.log(styles.success(`\nApplied phase template "${styles.emphasis(template.name)}"`));
257
+ this.log(styles.muted(`Created ${phases.length} phases:`));
258
+ for (const phase of phases) {
259
+ this.log(styles.muted(` • ${phase.name} [${phase.category}]`));
260
+ }
261
+ }
262
+ }
@@ -1,11 +1,12 @@
1
- import { PMOCommand } from '../../../lib/pmo/index.js';
2
- export default class TicketTemplateCreate extends PMOCommand {
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class TemplateCreate extends PMOCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static args: {
6
6
  name: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
7
  };
8
8
  static flags: {
9
+ type: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  'title-pattern': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  'description-template': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -21,8 +22,6 @@ export default class TicketTemplateCreate extends PMOCommand {
21
22
  promptIfMultiple: boolean;
22
23
  };
23
24
  execute(): Promise<void>;
24
- /**
25
- * Check if any non-default flags were provided (indicating non-interactive intent)
26
- */
27
- private hasNonDefaultFlags;
25
+ private createTicketTemplate;
26
+ private createPhaseTemplate;
28
27
  }