@proletariat/cli 0.3.31 → 0.3.32

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 (39) hide show
  1. package/dist/commands/diet.d.ts +20 -0
  2. package/dist/commands/diet.js +181 -0
  3. package/dist/commands/mcp-server.js +2 -1
  4. package/dist/commands/priority/add.d.ts +15 -0
  5. package/dist/commands/priority/add.js +70 -0
  6. package/dist/commands/priority/list.d.ts +10 -0
  7. package/dist/commands/priority/list.js +34 -0
  8. package/dist/commands/priority/remove.d.ts +13 -0
  9. package/dist/commands/priority/remove.js +54 -0
  10. package/dist/commands/priority/set.d.ts +14 -0
  11. package/dist/commands/priority/set.js +60 -0
  12. package/dist/commands/pull.d.ts +23 -0
  13. package/dist/commands/pull.js +219 -0
  14. package/dist/commands/roadmap/generate.js +10 -5
  15. package/dist/commands/template/apply.js +5 -4
  16. package/dist/commands/template/create.js +9 -5
  17. package/dist/commands/ticket/create.js +6 -5
  18. package/dist/commands/ticket/edit.js +9 -9
  19. package/dist/commands/ticket/list.d.ts +2 -0
  20. package/dist/commands/ticket/list.js +20 -13
  21. package/dist/commands/ticket/update.js +8 -5
  22. package/dist/commands/work/spawn.d.ts +13 -0
  23. package/dist/commands/work/spawn.js +388 -1
  24. package/dist/lib/mcp/tools/diet.d.ts +6 -0
  25. package/dist/lib/mcp/tools/diet.js +261 -0
  26. package/dist/lib/mcp/tools/index.d.ts +1 -0
  27. package/dist/lib/mcp/tools/index.js +1 -0
  28. package/dist/lib/mcp/tools/template.js +1 -1
  29. package/dist/lib/mcp/tools/ticket.js +48 -3
  30. package/dist/lib/pmo/diet.d.ts +102 -0
  31. package/dist/lib/pmo/diet.js +127 -0
  32. package/dist/lib/pmo/storage/base.d.ts +5 -0
  33. package/dist/lib/pmo/storage/base.js +16 -0
  34. package/dist/lib/pmo/types.d.ts +12 -6
  35. package/dist/lib/pmo/types.js +6 -2
  36. package/dist/lib/pmo/utils.d.ts +40 -0
  37. package/dist/lib/pmo/utils.js +76 -0
  38. package/oclif.manifest.json +2686 -2348
  39. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ import { PMOCommand } from '../lib/pmo/base-command.js';
2
+ export default class Diet extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ set: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ reset: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ protected execute(): Promise<void>;
12
+ /**
13
+ * Build a diet report comparing actual Ready distribution to targets.
14
+ */
15
+ private buildDietReport;
16
+ /**
17
+ * Display the diet report with visual indicators.
18
+ */
19
+ private displayDietReport;
20
+ }
@@ -0,0 +1,181 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { PMOCommand, pmoBaseFlags } from '../lib/pmo/base-command.js';
3
+ import { styles, divider } from '../lib/styles.js';
4
+ import { loadDietConfig, saveDietConfig, parseDietString, formatDietConfig, } from '../lib/pmo/diet.js';
5
+ export default class Diet extends PMOCommand {
6
+ static description = 'Show or configure diet ratios for balanced ticket distribution';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %>',
9
+ '<%= config.bin %> <%= command.id %> --set "ship=4,grow=2.5,support=1.5,bizops=1,strategy=1"',
10
+ '<%= config.bin %> <%= command.id %> --reset',
11
+ ];
12
+ static flags = {
13
+ ...pmoBaseFlags,
14
+ set: Flags.string({
15
+ char: 's',
16
+ description: 'Set diet weights (e.g., "ship=4,grow=2,support=1") - normalized to percentages automatically',
17
+ }),
18
+ reset: Flags.boolean({
19
+ char: 'r',
20
+ description: 'Reset diet to default ratios',
21
+ default: false,
22
+ }),
23
+ };
24
+ async execute() {
25
+ const { flags } = await this.parse(Diet);
26
+ const projectId = await this.requireProject();
27
+ const db = this.storage.getDatabase();
28
+ // Handle --set
29
+ if (flags.set) {
30
+ try {
31
+ const newConfig = parseDietString(flags.set);
32
+ saveDietConfig(db, newConfig);
33
+ this.log(styles.success(`Diet updated: ${formatDietConfig(newConfig)}`));
34
+ }
35
+ catch (error) {
36
+ const message = error instanceof Error ? error.message : String(error);
37
+ this.error(message);
38
+ }
39
+ return;
40
+ }
41
+ // Handle --reset
42
+ if (flags.reset) {
43
+ const { DEFAULT_DIET_CONFIG } = await import('../lib/pmo/diet.js');
44
+ saveDietConfig(db, DEFAULT_DIET_CONFIG);
45
+ this.log(styles.success(`Diet reset to defaults: ${formatDietConfig(DEFAULT_DIET_CONFIG)}`));
46
+ return;
47
+ }
48
+ // Show diet report
49
+ const dietConfig = loadDietConfig(db);
50
+ const report = await this.buildDietReport(projectId, dietConfig);
51
+ this.displayDietReport(report, dietConfig);
52
+ }
53
+ /**
54
+ * Build a diet report comparing actual Ready distribution to targets.
55
+ */
56
+ async buildDietReport(projectId, dietConfig) {
57
+ // Get all ready/unstarted tickets using statusCategory filter
58
+ const readyTickets = await this.storage.listTickets(projectId, { statusCategory: 'unstarted' });
59
+ const totalReady = readyTickets.length;
60
+ // Count by category
61
+ const categoryCounts = new Map();
62
+ let uncategorized = 0;
63
+ for (const ticket of readyTickets) {
64
+ const cat = (ticket.category || '').toLowerCase();
65
+ if (cat) {
66
+ categoryCounts.set(cat, (categoryCounts.get(cat) || 0) + 1);
67
+ }
68
+ else {
69
+ uncategorized++;
70
+ }
71
+ }
72
+ // Build category reports
73
+ const categories = dietConfig.ratios.map(ratio => {
74
+ const count = categoryCounts.get(ratio.category) || 0;
75
+ const actual = totalReady > 0 ? count / totalReady : 0;
76
+ const targetCount = Math.round(totalReady * ratio.target);
77
+ const delta = actual - ratio.target;
78
+ const tolerance = 0.05; // 5% tolerance
79
+ let status = 'ok';
80
+ if (delta > tolerance)
81
+ status = 'over';
82
+ else if (delta < -tolerance)
83
+ status = 'under';
84
+ return {
85
+ category: ratio.category,
86
+ target: ratio.target,
87
+ actual,
88
+ count,
89
+ targetCount,
90
+ delta,
91
+ status,
92
+ };
93
+ });
94
+ // Check for categories not in the diet config
95
+ for (const [cat, count] of categoryCounts) {
96
+ if (!dietConfig.ratios.some(r => r.category === cat)) {
97
+ const actual = totalReady > 0 ? count / totalReady : 0;
98
+ categories.push({
99
+ category: cat,
100
+ target: 0,
101
+ actual,
102
+ count,
103
+ targetCount: 0,
104
+ delta: actual,
105
+ status: actual > 0.05 ? 'over' : 'ok',
106
+ });
107
+ }
108
+ }
109
+ return { categories, totalReady, uncategorized };
110
+ }
111
+ /**
112
+ * Display the diet report with visual indicators.
113
+ */
114
+ displayDietReport(report, dietConfig) {
115
+ this.log(styles.title('\nDiet Report'));
116
+ this.log(divider(65));
117
+ this.log(` Current diet: ${formatDietConfig(dietConfig)}`);
118
+ this.log(` Total in Ready: ${report.totalReady}`);
119
+ if (report.uncategorized > 0) {
120
+ this.log(styles.warning(` Uncategorized: ${report.uncategorized}`));
121
+ }
122
+ this.log(divider(65));
123
+ if (report.totalReady === 0) {
124
+ this.log(styles.muted('\n No tickets in Ready. Run "prlt pull" to populate.\n'));
125
+ return;
126
+ }
127
+ // Header
128
+ this.log('');
129
+ this.log(` ${'Category'.padEnd(14)} ${'Target'.padEnd(8)} ${'Actual'.padEnd(8)} ${'Count'.padEnd(7)} ${'Status'.padEnd(8)} ${'Bar'}`);
130
+ this.log(` ${divider(62).trim()}`);
131
+ // Category rows
132
+ for (const cat of report.categories) {
133
+ const targetPct = `${Math.round(cat.target * 100)}%`;
134
+ const actualPct = `${Math.round(cat.actual * 100)}%`;
135
+ const countStr = `${cat.count}/${cat.targetCount}`;
136
+ let statusIndicator;
137
+ switch (cat.status) {
138
+ case 'over':
139
+ statusIndicator = styles.warning('OVER');
140
+ break;
141
+ case 'under':
142
+ statusIndicator = styles.error('UNDER');
143
+ break;
144
+ default:
145
+ statusIndicator = styles.success('OK');
146
+ }
147
+ // Visual bar (max 20 chars)
148
+ const barWidth = 20;
149
+ const actualBar = Math.round(cat.actual * barWidth);
150
+ const targetBar = Math.round(cat.target * barWidth);
151
+ let bar = '';
152
+ for (let i = 0; i < barWidth; i++) {
153
+ if (i < actualBar && i < targetBar) {
154
+ bar += styles.success('█');
155
+ }
156
+ else if (i < actualBar) {
157
+ bar += styles.warning('█');
158
+ }
159
+ else if (i < targetBar) {
160
+ bar += styles.muted('░');
161
+ }
162
+ else {
163
+ bar += ' ';
164
+ }
165
+ }
166
+ this.log(` ${cat.category.padEnd(14)} ${targetPct.padEnd(8)} ${actualPct.padEnd(8)} ${countStr.padEnd(7)} ${statusIndicator.padEnd(8 + 10)} ${bar}`);
167
+ }
168
+ // Warnings
169
+ const warnings = report.categories.filter(c => c.status !== 'ok');
170
+ if (warnings.length > 0) {
171
+ this.log('');
172
+ this.log(styles.warning(' Warnings:'));
173
+ for (const w of warnings) {
174
+ const direction = w.status === 'over' ? 'over' : 'under';
175
+ const delta = Math.abs(Math.round(w.delta * 100));
176
+ this.log(styles.warning(` ${w.category}: ${delta}% ${direction}-represented`));
177
+ }
178
+ }
179
+ this.log('');
180
+ }
181
+ }
@@ -19,7 +19,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
19
19
  import { execSync } from 'node:child_process';
20
20
  import * as path from 'node:path';
21
21
  import { getPMOContext } from '../lib/pmo/pmo-context.js';
22
- import { registerTicketTools, registerProjectTools, registerBoardTools, registerSpecTools, registerEpicTools, registerWorkTools, registerWorkflowTools, registerStatusTools, registerPhaseTools, registerActionTools, registerRoadmapTools, registerCategoryTools, registerTemplateTools, registerViewTools, registerAgentTools, registerDockerTools, registerRepoTools, registerBranchTools, registerGitHubTools, registerInitTools, registerUtilityTools, } from '../lib/mcp/index.js';
22
+ import { registerTicketTools, registerProjectTools, registerBoardTools, registerSpecTools, registerEpicTools, registerWorkTools, registerWorkflowTools, registerStatusTools, registerPhaseTools, registerActionTools, registerRoadmapTools, registerCategoryTools, registerTemplateTools, registerViewTools, registerDietTools, registerAgentTools, registerDockerTools, registerRepoTools, registerBranchTools, registerGitHubTools, registerInitTools, registerUtilityTools, } from '../lib/mcp/index.js';
23
23
  export default class McpServerCommand extends Command {
24
24
  static description = 'Start MCP server for AI agent integration (exposes all prlt commands as tools)';
25
25
  static hidden = true;
@@ -84,6 +84,7 @@ export default class McpServerCommand extends Command {
84
84
  registerCategoryTools(server, ctx);
85
85
  registerTemplateTools(server, ctx);
86
86
  registerViewTools(server, ctx);
87
+ registerDietTools(server, ctx);
87
88
  registerAgentTools(server, ctx);
88
89
  registerDockerTools(server, ctx);
89
90
  registerRepoTools(server, ctx);
@@ -0,0 +1,15 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class PriorityAdd extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ value: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ position: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ after: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ execute(): Promise<void>;
15
+ }
@@ -0,0 +1,70 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
3
+ import { styles } from '../../lib/styles.js';
4
+ import { getWorkspacePriorities, setWorkspacePriorities } from '../../lib/pmo/utils.js';
5
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
+ export default class PriorityAdd extends PMOCommand {
7
+ static description = 'Add a priority value to the workspace scale';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> Critical',
10
+ '<%= config.bin %> <%= command.id %> "Must Have" --position 0',
11
+ '<%= config.bin %> <%= command.id %> P4 --after P3',
12
+ ];
13
+ static args = {
14
+ value: Args.string({
15
+ description: 'Priority value to add',
16
+ required: true,
17
+ }),
18
+ };
19
+ static flags = {
20
+ ...pmoBaseFlags,
21
+ position: Flags.integer({
22
+ description: 'Position in the priority scale (0 = highest)',
23
+ }),
24
+ after: Flags.string({
25
+ description: 'Insert after this priority value',
26
+ exclusive: ['position'],
27
+ }),
28
+ };
29
+ async execute() {
30
+ const { args, flags } = await this.parse(PriorityAdd);
31
+ const jsonMode = shouldOutputJson(flags);
32
+ const db = this.storage.getDatabase();
33
+ const priorities = getWorkspacePriorities(db);
34
+ // Check for duplicates
35
+ if (priorities.includes(args.value)) {
36
+ if (jsonMode) {
37
+ outputErrorAsJson('DUPLICATE_PRIORITY', `Priority "${args.value}" already exists`, createMetadata('priority add', flags));
38
+ this.exit(1);
39
+ }
40
+ this.error(`Priority "${args.value}" already exists in the scale: ${priorities.join(', ')}`);
41
+ }
42
+ // Determine insertion position
43
+ let insertAt = priorities.length; // Default: append to end (lowest priority)
44
+ if (flags.position !== undefined) {
45
+ insertAt = Math.max(0, Math.min(flags.position, priorities.length));
46
+ }
47
+ else if (flags.after) {
48
+ const afterIndex = priorities.indexOf(flags.after);
49
+ if (afterIndex < 0) {
50
+ if (jsonMode) {
51
+ outputErrorAsJson('PRIORITY_NOT_FOUND', `Priority "${flags.after}" not found`, createMetadata('priority add', flags));
52
+ this.exit(1);
53
+ }
54
+ this.error(`Priority "${flags.after}" not found in the scale: ${priorities.join(', ')}`);
55
+ }
56
+ insertAt = afterIndex + 1;
57
+ }
58
+ // Insert at position
59
+ const newPriorities = [...priorities];
60
+ newPriorities.splice(insertAt, 0, args.value);
61
+ setWorkspacePriorities(db, newPriorities);
62
+ if (jsonMode) {
63
+ this.log(JSON.stringify({ success: true, priorities: newPriorities }, null, 2));
64
+ return;
65
+ }
66
+ this.log(styles.success(`\n✅ Added priority "${args.value}" at position ${insertAt}`));
67
+ this.log(styles.muted(` Scale: ${newPriorities.join(', ')}`));
68
+ this.log('');
69
+ }
70
+ }
@@ -0,0 +1,10 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class PriorityList extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ execute(): Promise<void>;
10
+ }
@@ -0,0 +1,34 @@
1
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
2
+ import { styles } from '../../lib/styles.js';
3
+ import { getWorkspacePriorities } from '../../lib/pmo/utils.js';
4
+ import { isMachineOutput } from '../../lib/prompt-json.js';
5
+ export default class PriorityList extends PMOCommand {
6
+ static description = 'List the workspace priority scale';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %>',
9
+ '<%= config.bin %> <%= command.id %> --json',
10
+ ];
11
+ static flags = {
12
+ ...pmoBaseFlags,
13
+ };
14
+ async execute() {
15
+ const { flags } = await this.parse(PriorityList);
16
+ const db = this.storage.getDatabase();
17
+ const priorities = getWorkspacePriorities(db);
18
+ if (isMachineOutput(flags)) {
19
+ this.log(JSON.stringify({ priorities }, null, 2));
20
+ return;
21
+ }
22
+ this.log(`\n${styles.emphasis('Priority Scale')} (highest to lowest)`);
23
+ this.log('─'.repeat(40));
24
+ for (let i = 0; i < priorities.length; i++) {
25
+ const marker = i === 0 ? '🔴' : i === 1 ? '🟠' : i === 2 ? '🟡' : '🟢';
26
+ this.log(` ${marker} ${priorities[i]}`);
27
+ }
28
+ this.log('');
29
+ this.log(styles.muted('Manage: prlt priority set <values...>'));
30
+ this.log(styles.muted(' prlt priority add <value>'));
31
+ this.log(styles.muted(' prlt priority remove <value>'));
32
+ this.log('');
33
+ }
34
+ }
@@ -0,0 +1,13 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class PriorityRemove extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ value: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ execute(): Promise<void>;
13
+ }
@@ -0,0 +1,54 @@
1
+ import { Args } from '@oclif/core';
2
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
3
+ import { styles } from '../../lib/styles.js';
4
+ import { getWorkspacePriorities, setWorkspacePriorities } from '../../lib/pmo/utils.js';
5
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
+ export default class PriorityRemove extends PMOCommand {
7
+ static description = 'Remove a priority value from the workspace scale';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> P3',
10
+ '<%= config.bin %> <%= command.id %> Low',
11
+ ];
12
+ static args = {
13
+ value: Args.string({
14
+ description: 'Priority value to remove',
15
+ required: true,
16
+ }),
17
+ };
18
+ static flags = {
19
+ ...pmoBaseFlags,
20
+ };
21
+ async execute() {
22
+ const { args, flags } = await this.parse(PriorityRemove);
23
+ const jsonMode = shouldOutputJson(flags);
24
+ const db = this.storage.getDatabase();
25
+ const priorities = getWorkspacePriorities(db);
26
+ // Check that the priority exists
27
+ if (!priorities.includes(args.value)) {
28
+ if (jsonMode) {
29
+ outputErrorAsJson('PRIORITY_NOT_FOUND', `Priority "${args.value}" not found`, createMetadata('priority remove', flags));
30
+ this.exit(1);
31
+ }
32
+ this.error(`Priority "${args.value}" not found in the scale: ${priorities.join(', ')}`);
33
+ }
34
+ // Must keep at least one priority
35
+ if (priorities.length <= 1) {
36
+ if (jsonMode) {
37
+ outputErrorAsJson('MIN_PRIORITIES', 'Cannot remove the last priority. Use "prlt priority set" to replace the scale.', createMetadata('priority remove', flags));
38
+ this.exit(1);
39
+ }
40
+ this.error('Cannot remove the last priority. Use "prlt priority set" to replace the scale.');
41
+ }
42
+ const newPriorities = priorities.filter(p => p !== args.value);
43
+ setWorkspacePriorities(db, newPriorities);
44
+ if (jsonMode) {
45
+ this.log(JSON.stringify({ success: true, priorities: newPriorities, removed: args.value }, null, 2));
46
+ return;
47
+ }
48
+ this.log(styles.success(`\n✅ Removed priority "${args.value}"`));
49
+ this.log(styles.muted(` Scale: ${newPriorities.join(', ')}`));
50
+ this.log(styles.muted('\n Note: Existing tickets with this priority are not affected.'));
51
+ this.log(styles.muted(' You may want to update them manually.'));
52
+ this.log('');
53
+ }
54
+ }
@@ -0,0 +1,14 @@
1
+ import { PMOCommand } from '../../lib/pmo/index.js';
2
+ export default class PrioritySet extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static strict: boolean;
6
+ static args: {
7
+ priorities: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ execute(): Promise<void>;
14
+ }
@@ -0,0 +1,60 @@
1
+ import { Args } from '@oclif/core';
2
+ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
3
+ import { styles } from '../../lib/styles.js';
4
+ import { getWorkspacePriorities, setWorkspacePriorities } from '../../lib/pmo/utils.js';
5
+ import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
6
+ export default class PrioritySet extends PMOCommand {
7
+ static description = 'Set the workspace priority scale (replaces all existing priorities)';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> P0 P1 P2 P3',
10
+ '<%= config.bin %> <%= command.id %> Critical High Medium Low',
11
+ '<%= config.bin %> <%= command.id %> Now Next Later',
12
+ '<%= config.bin %> <%= command.id %> "Must Have" "Should Have" "Nice to Have"',
13
+ ];
14
+ static strict = false;
15
+ static args = {
16
+ priorities: Args.string({
17
+ description: 'Priority values from highest to lowest (space-separated)',
18
+ required: false,
19
+ }),
20
+ };
21
+ static flags = {
22
+ ...pmoBaseFlags,
23
+ };
24
+ async execute() {
25
+ const { flags, argv } = await this.parse(PrioritySet);
26
+ const jsonMode = shouldOutputJson(flags);
27
+ const db = this.storage.getDatabase();
28
+ // argv contains all the non-flag arguments
29
+ const newPriorities = argv.filter(a => a.trim());
30
+ if (newPriorities.length === 0) {
31
+ if (jsonMode) {
32
+ outputErrorAsJson('NO_PRIORITIES', 'At least one priority value is required', createMetadata('priority set', flags));
33
+ this.exit(1);
34
+ }
35
+ this.error('At least one priority value is required.\n\nUsage: prlt priority set <value1> <value2> ...\nExample: prlt priority set P0 P1 P2 P3');
36
+ }
37
+ // Check for duplicates
38
+ const seen = new Set();
39
+ for (const p of newPriorities) {
40
+ if (seen.has(p)) {
41
+ if (jsonMode) {
42
+ outputErrorAsJson('DUPLICATE_PRIORITY', `Duplicate priority value: "${p}"`, createMetadata('priority set', flags));
43
+ this.exit(1);
44
+ }
45
+ this.error(`Duplicate priority value: "${p}". Each priority must be unique.`);
46
+ }
47
+ seen.add(p);
48
+ }
49
+ const oldPriorities = getWorkspacePriorities(db);
50
+ setWorkspacePriorities(db, newPriorities);
51
+ if (jsonMode) {
52
+ this.log(JSON.stringify({ success: true, priorities: newPriorities }, null, 2));
53
+ return;
54
+ }
55
+ this.log(styles.success(`\n✅ Priority scale updated`));
56
+ this.log(styles.muted(` Before: ${oldPriorities.join(', ')}`));
57
+ this.log(styles.muted(` After: ${newPriorities.join(', ')}`));
58
+ this.log('');
59
+ }
60
+ }
@@ -0,0 +1,23 @@
1
+ import { PMOCommand } from '../lib/pmo/base-command.js';
2
+ export default class Pull extends PMOCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ count: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ };
11
+ protected execute(): Promise<void>;
12
+ /**
13
+ * Core pull algorithm with diet enforcement.
14
+ *
15
+ * Pass 1: Walk backlog top-down, pull if category not over ceiling
16
+ * Pass 2: Force-pull from underrepresented categories
17
+ */
18
+ private runPullAlgorithm;
19
+ /**
20
+ * Display pull results.
21
+ */
22
+ private displayPullResults;
23
+ }