@proletariat/cli 0.3.34 → 0.3.35
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.
- package/dist/commands/agent/auth.d.ts +3 -1
- package/dist/commands/agent/auth.js +8 -11
- package/dist/commands/agent/index.js +11 -2
- package/dist/commands/agent/staff/add.d.ts +1 -0
- package/dist/commands/agent/staff/add.js +1 -0
- package/dist/commands/agent/staff/index.d.ts +15 -0
- package/dist/commands/agent/staff/index.js +83 -0
- package/dist/commands/agent/staff/list.d.ts +1 -0
- package/dist/commands/agent/staff/list.js +1 -0
- package/dist/commands/agent/staff/remove.d.ts +1 -0
- package/dist/commands/agent/staff/remove.js +1 -0
- package/dist/commands/agent/themes/add-names.d.ts +1 -0
- package/dist/commands/agent/themes/add-names.js +1 -0
- package/dist/commands/agent/themes/create.d.ts +1 -0
- package/dist/commands/agent/themes/create.js +1 -0
- package/dist/commands/agent/themes/index.d.ts +10 -0
- package/dist/commands/agent/themes/index.js +144 -0
- package/dist/commands/agent/themes/list.d.ts +1 -0
- package/dist/commands/agent/themes/list.js +1 -0
- package/dist/commands/agent/themes/set.d.ts +1 -0
- package/dist/commands/agent/themes/set.js +1 -0
- package/dist/commands/agents/themes/add-names.d.ts +1 -0
- package/dist/commands/agents/themes/add-names.js +1 -0
- package/dist/commands/agents/themes/create.d.ts +1 -0
- package/dist/commands/agents/themes/create.js +1 -0
- package/dist/commands/agents/themes/list.d.ts +1 -0
- package/dist/commands/agents/themes/list.js +1 -0
- package/dist/commands/category/list.js +1 -1
- package/dist/commands/label/create.d.ts +20 -0
- package/dist/commands/label/create.js +56 -0
- package/dist/commands/label/delete.d.ts +17 -0
- package/dist/commands/label/delete.js +31 -0
- package/dist/commands/label/group/create.d.ts +20 -0
- package/dist/commands/label/group/create.js +54 -0
- package/dist/commands/label/group/list.d.ts +14 -0
- package/dist/commands/label/group/list.js +51 -0
- package/dist/commands/label/index.d.ts +15 -0
- package/dist/commands/label/index.js +58 -0
- package/dist/commands/label/list.d.ts +16 -0
- package/dist/commands/label/list.js +82 -0
- package/dist/commands/link/list.js +3 -2
- package/dist/commands/mcp-server.js +2 -1
- package/dist/commands/phase/template/apply.d.ts +26 -0
- package/dist/commands/phase/template/apply.js +14 -0
- package/dist/commands/phase/template/create.d.ts +23 -0
- package/dist/commands/phase/template/create.js +14 -0
- package/dist/commands/phase/template/delete.d.ts +18 -0
- package/dist/commands/phase/template/delete.js +61 -0
- package/dist/commands/phase/template/list.d.ts +17 -0
- package/dist/commands/phase/template/list.js +88 -0
- package/dist/commands/phase/template/update.d.ts +1 -0
- package/dist/commands/phase/template/update.js +1 -0
- package/dist/commands/priority/add.js +1 -1
- package/dist/commands/project/update.js +0 -2
- package/dist/commands/roadmap/generate.js +1 -2
- package/dist/commands/session/health.js +1 -1
- package/dist/commands/spec/link/depends.d.ts +18 -0
- package/dist/commands/spec/link/depends.js +86 -0
- package/dist/commands/spec/link/index.d.ts +17 -0
- package/dist/commands/spec/link/index.js +92 -0
- package/dist/commands/spec/link/remove.d.ts +18 -0
- package/dist/commands/spec/link/remove.js +90 -0
- package/dist/commands/support/logs.js +2 -2
- package/dist/commands/template/apply.js +5 -4
- package/dist/commands/template/create.js +1 -1
- package/dist/commands/ticket/link/block.d.ts +15 -0
- package/dist/commands/ticket/link/block.js +95 -0
- package/dist/commands/ticket/link/index.d.ts +14 -0
- package/dist/commands/ticket/link/index.js +96 -0
- package/dist/commands/ticket/list.d.ts +1 -0
- package/dist/commands/ticket/list.js +6 -0
- package/dist/commands/ticket/resolve.js +1 -1
- package/dist/commands/ticket/template/apply.d.ts +26 -0
- package/dist/commands/ticket/template/apply.js +14 -0
- package/dist/commands/ticket/template/delete.d.ts +18 -0
- package/dist/commands/ticket/template/delete.js +61 -0
- package/dist/commands/ticket/template/list.d.ts +17 -0
- package/dist/commands/ticket/template/list.js +77 -0
- package/dist/commands/ticket/template/save.d.ts +17 -0
- package/dist/commands/ticket/template/save.js +97 -0
- package/dist/commands/ticket/view.d.ts +1 -0
- package/dist/commands/ticket/view.js +1 -0
- package/dist/commands/work/ready.js +17 -0
- package/dist/commands/work/resolve.js +1 -1
- package/dist/commands/work/spawn.js +4 -4
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +52 -17
- package/dist/lib/database/index.d.ts +1 -1
- package/dist/lib/database/index.js +20 -0
- package/dist/lib/execution/devcontainer.js +3 -1
- package/dist/lib/execution/runners.d.ts +7 -2
- package/dist/lib/execution/runners.js +18 -10
- package/dist/lib/execution/types.d.ts +1 -0
- package/dist/lib/flags/resolver.js +1 -0
- package/dist/lib/mcp/helpers.d.ts +1 -2
- package/dist/lib/mcp/tools/diet.js +1 -0
- package/dist/lib/mcp/tools/index.d.ts +1 -0
- package/dist/lib/mcp/tools/index.js +1 -0
- package/dist/lib/mcp/tools/label.d.ts +6 -0
- package/dist/lib/mcp/tools/label.js +338 -0
- package/dist/lib/mcp/tools/ticket.js +53 -17
- package/dist/lib/multiline-input.js +6 -18
- package/dist/lib/pmo/base-command.d.ts +0 -1
- package/dist/lib/pmo/base-command.js +0 -1
- package/dist/lib/pmo/schema.d.ts +6 -0
- package/dist/lib/pmo/schema.js +44 -0
- package/dist/lib/pmo/storage/base.d.ts +6 -0
- package/dist/lib/pmo/storage/base.js +116 -2
- package/dist/lib/pmo/storage/index.d.ts +23 -1
- package/dist/lib/pmo/storage/index.js +59 -1
- package/dist/lib/pmo/storage/labels.d.ts +55 -0
- package/dist/lib/pmo/storage/labels.js +346 -0
- package/dist/lib/pmo/storage/tickets.js +17 -0
- package/dist/lib/pmo/storage/types.d.ts +24 -0
- package/dist/lib/pmo/types.d.ts +44 -0
- package/dist/lib/pmo/utils.js +1 -1
- package/oclif.manifest.json +5702 -3660
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PMOCommand } from '../../../lib/pmo/index.js';
|
|
2
|
+
export default class TicketTemplateDelete extends PMOCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
};
|
|
14
|
+
protected getPMOOptions(): {
|
|
15
|
+
promptIfMultiple: boolean;
|
|
16
|
+
};
|
|
17
|
+
execute(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
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 { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../../lib/prompt-json.js';
|
|
5
|
+
export default class TicketTemplateDelete extends PMOCommand {
|
|
6
|
+
static description = 'Delete a ticket template';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> <%= command.id %> my-template --force',
|
|
9
|
+
];
|
|
10
|
+
static args = {
|
|
11
|
+
id: Args.string({
|
|
12
|
+
description: 'Template ID to delete',
|
|
13
|
+
required: true,
|
|
14
|
+
}),
|
|
15
|
+
};
|
|
16
|
+
static flags = {
|
|
17
|
+
...pmoBaseFlags,
|
|
18
|
+
force: Flags.boolean({
|
|
19
|
+
char: 'f',
|
|
20
|
+
description: 'Skip confirmation',
|
|
21
|
+
default: false,
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
getPMOOptions() {
|
|
25
|
+
return { promptIfMultiple: false };
|
|
26
|
+
}
|
|
27
|
+
async execute() {
|
|
28
|
+
const { args, flags } = await this.parse(TicketTemplateDelete);
|
|
29
|
+
const jsonMode = shouldOutputJson(flags);
|
|
30
|
+
const handleError = (code, message) => {
|
|
31
|
+
if (jsonMode) {
|
|
32
|
+
outputErrorAsJson(code, message, createMetadata('ticket template delete', flags));
|
|
33
|
+
}
|
|
34
|
+
this.error(message);
|
|
35
|
+
};
|
|
36
|
+
const template = await this.storage.getTicketTemplate(args.id);
|
|
37
|
+
if (!template) {
|
|
38
|
+
return handleError('NOT_FOUND', `Ticket template not found: ${args.id}`);
|
|
39
|
+
}
|
|
40
|
+
if (template.isBuiltin) {
|
|
41
|
+
return handleError('BUILTIN', `Cannot delete built-in template "${template.name}".`);
|
|
42
|
+
}
|
|
43
|
+
if (!flags.force) {
|
|
44
|
+
const { confirm } = await this.prompt([{
|
|
45
|
+
type: 'list',
|
|
46
|
+
name: 'confirm',
|
|
47
|
+
message: `Delete ticket template "${template.name}"?`,
|
|
48
|
+
choices: [
|
|
49
|
+
{ name: 'No', value: false },
|
|
50
|
+
{ name: 'Yes', value: true },
|
|
51
|
+
],
|
|
52
|
+
}], jsonMode ? { flags, commandName: 'ticket template delete' } : null);
|
|
53
|
+
if (!confirm) {
|
|
54
|
+
this.log(styles.muted('Cancelled.'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
await this.storage.deleteTicketTemplate(args.id);
|
|
59
|
+
this.log(styles.success(`\nDeleted template "${template.name}"`));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PMOCommand } from '../../../lib/pmo/index.js';
|
|
2
|
+
export default class TicketTemplateList extends PMOCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
builtin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
custom: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
protected getPMOOptions(): {
|
|
13
|
+
promptIfMultiple: boolean;
|
|
14
|
+
};
|
|
15
|
+
execute(): Promise<void>;
|
|
16
|
+
private printTemplate;
|
|
17
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
|
|
3
|
+
import { styles } from '../../../lib/styles.js';
|
|
4
|
+
export default class TicketTemplateList extends PMOCommand {
|
|
5
|
+
static description = 'List ticket templates';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %>',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --builtin',
|
|
9
|
+
'<%= config.bin %> <%= command.id %> --json',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
...pmoBaseFlags,
|
|
13
|
+
builtin: Flags.boolean({
|
|
14
|
+
description: 'Show only built-in templates',
|
|
15
|
+
exclusive: ['custom'],
|
|
16
|
+
}),
|
|
17
|
+
custom: Flags.boolean({
|
|
18
|
+
description: 'Show only custom templates',
|
|
19
|
+
exclusive: ['builtin'],
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
getPMOOptions() {
|
|
23
|
+
return { promptIfMultiple: false };
|
|
24
|
+
}
|
|
25
|
+
async execute() {
|
|
26
|
+
const { flags } = await this.parse(TicketTemplateList);
|
|
27
|
+
let builtinFilter;
|
|
28
|
+
if (flags.builtin)
|
|
29
|
+
builtinFilter = { isBuiltin: true };
|
|
30
|
+
if (flags.custom)
|
|
31
|
+
builtinFilter = { isBuiltin: false };
|
|
32
|
+
const templates = await this.storage.listTicketTemplates(builtinFilter);
|
|
33
|
+
if (flags.json) {
|
|
34
|
+
this.log(JSON.stringify(templates, null, 2));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (templates.length === 0) {
|
|
38
|
+
this.log(styles.muted('\nNo ticket templates found.'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.log(`\n${styles.emphasis('Ticket Templates')}`);
|
|
42
|
+
this.log('═'.repeat(60));
|
|
43
|
+
const builtinTemplates = templates.filter(t => t.isBuiltin);
|
|
44
|
+
const customTemplates = templates.filter(t => !t.isBuiltin);
|
|
45
|
+
if (builtinTemplates.length > 0 && !flags.custom) {
|
|
46
|
+
this.log(`\n${styles.emphasis('Built-in Templates')}`);
|
|
47
|
+
this.log('─'.repeat(40));
|
|
48
|
+
for (const template of builtinTemplates) {
|
|
49
|
+
this.printTemplate(template);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (customTemplates.length > 0 && !flags.builtin) {
|
|
53
|
+
this.log(`\n${styles.emphasis('Custom Templates')}`);
|
|
54
|
+
this.log('─'.repeat(40));
|
|
55
|
+
for (const template of customTemplates) {
|
|
56
|
+
this.printTemplate(template);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.log('');
|
|
60
|
+
}
|
|
61
|
+
printTemplate(template) {
|
|
62
|
+
this.log(` ${styles.emphasis(template.name)} ${styles.muted(`(${template.id})`)}`);
|
|
63
|
+
if (template.description) {
|
|
64
|
+
this.log(` ${styles.muted(template.description)}`);
|
|
65
|
+
}
|
|
66
|
+
const details = [];
|
|
67
|
+
if (template.defaultPriority)
|
|
68
|
+
details.push(`Priority: ${template.defaultPriority}`);
|
|
69
|
+
if (template.defaultCategory)
|
|
70
|
+
details.push(`Category: ${template.defaultCategory}`);
|
|
71
|
+
if (template.suggestedSubtasks.length > 0)
|
|
72
|
+
details.push(`Subtasks: ${template.suggestedSubtasks.length}`);
|
|
73
|
+
if (details.length > 0) {
|
|
74
|
+
this.log(` ${styles.muted(details.join(' | '))}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PMOCommand } from '../../../lib/pmo/index.js';
|
|
2
|
+
export default class TicketTemplateSave extends PMOCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
ticket: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
name: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
'template-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
};
|
|
16
|
+
execute(): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Flags, Args } from '@oclif/core';
|
|
2
|
+
import { PMOCommand, pmoBaseFlags } from '../../../lib/pmo/index.js';
|
|
3
|
+
import { styles } from '../../../lib/styles.js';
|
|
4
|
+
import { shouldOutputJson, outputPromptAsJson, outputSuccessAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
|
|
5
|
+
export default class TicketTemplateSave extends PMOCommand {
|
|
6
|
+
static description = 'Create a ticket template from an existing ticket';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> <%= command.id %> TKT-001 "Bug Report Template"',
|
|
9
|
+
'<%= config.bin %> <%= command.id %> TKT-042 "Feature Request" -d "Standard feature request"',
|
|
10
|
+
];
|
|
11
|
+
static args = {
|
|
12
|
+
ticket: Args.string({
|
|
13
|
+
description: 'Ticket ID to create template from',
|
|
14
|
+
required: false,
|
|
15
|
+
}),
|
|
16
|
+
name: Args.string({
|
|
17
|
+
description: 'Template name',
|
|
18
|
+
required: false,
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
static flags = {
|
|
22
|
+
...pmoBaseFlags,
|
|
23
|
+
'template-name': Flags.string({
|
|
24
|
+
char: 'n',
|
|
25
|
+
description: 'Template name (alternative to positional arg)',
|
|
26
|
+
}),
|
|
27
|
+
description: Flags.string({
|
|
28
|
+
char: 'd',
|
|
29
|
+
description: 'Template description',
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
async execute() {
|
|
33
|
+
const { args, flags } = await this.parse(TicketTemplateSave);
|
|
34
|
+
const jsonMode = shouldOutputJson(flags);
|
|
35
|
+
const handleError = (code, message) => {
|
|
36
|
+
if (jsonMode) {
|
|
37
|
+
outputErrorAsJson(code, message, createMetadata('ticket template save', flags));
|
|
38
|
+
}
|
|
39
|
+
this.error(message);
|
|
40
|
+
};
|
|
41
|
+
// Get ticket ID
|
|
42
|
+
let ticketId = args.ticket;
|
|
43
|
+
if (!ticketId) {
|
|
44
|
+
const projectId = await this.requireProject();
|
|
45
|
+
const tickets = await this.storage.listTickets(projectId);
|
|
46
|
+
if (tickets.length === 0) {
|
|
47
|
+
return handleError('NO_TICKETS', 'No tickets found. Create a ticket first.');
|
|
48
|
+
}
|
|
49
|
+
if (jsonMode) {
|
|
50
|
+
const choices = tickets.slice(0, 20).map(t => ({ name: `${t.id} - ${t.title}`, value: t.id }));
|
|
51
|
+
outputPromptAsJson(buildPromptConfig('list', 'ticket', 'Select ticket:', choices), createMetadata('ticket template save', flags));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const { selected } = await this.prompt([{
|
|
55
|
+
type: 'list',
|
|
56
|
+
name: 'selected',
|
|
57
|
+
message: 'Select ticket:',
|
|
58
|
+
choices: tickets.slice(0, 20).map(t => ({ name: `${t.id} - ${t.title}`, value: t.id })),
|
|
59
|
+
}], null);
|
|
60
|
+
ticketId = selected;
|
|
61
|
+
}
|
|
62
|
+
const ticket = await this.storage.getTicket(ticketId);
|
|
63
|
+
if (!ticket) {
|
|
64
|
+
return handleError('TICKET_NOT_FOUND', `Ticket not found: ${ticketId}`);
|
|
65
|
+
}
|
|
66
|
+
// Get template name
|
|
67
|
+
let templateName = flags['template-name'] || args.name;
|
|
68
|
+
if (!templateName) {
|
|
69
|
+
if (jsonMode) {
|
|
70
|
+
outputPromptAsJson(buildPromptConfig('input', 'name', 'Template name:', undefined, ticket.category || ticket.title.split(' ')[0]), createMetadata('ticket template save', flags));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const { name } = await this.prompt([{
|
|
74
|
+
type: 'input',
|
|
75
|
+
name: 'name',
|
|
76
|
+
message: 'Template name:',
|
|
77
|
+
default: ticket.category || ticket.title.split(' ')[0],
|
|
78
|
+
validate: (i) => i.length > 0 || 'Required',
|
|
79
|
+
}], null);
|
|
80
|
+
templateName = name;
|
|
81
|
+
}
|
|
82
|
+
// Get description
|
|
83
|
+
let description = flags.description;
|
|
84
|
+
if (description === undefined && !jsonMode) {
|
|
85
|
+
const { desc } = await this.prompt([{ type: 'input', name: 'desc', message: 'Description (optional):' }], null);
|
|
86
|
+
description = desc || undefined;
|
|
87
|
+
}
|
|
88
|
+
const template = await this.storage.createTicketTemplateFromTicket(ticketId, templateName, description);
|
|
89
|
+
if (flags.json === true) {
|
|
90
|
+
outputSuccessAsJson({ template, sourceTicketId: ticketId }, createMetadata('ticket template save', flags));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
this.log(styles.success(`\nCreated template "${styles.emphasis(template.name)}" from ${ticketId}`));
|
|
94
|
+
this.log(styles.muted(` ID: ${template.id}`));
|
|
95
|
+
this.log(styles.muted(`\nApply with: prlt ticket template apply ${template.id}`));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -3,6 +3,7 @@ import { PMOCommand, pmoBaseFlags } from '../../lib/pmo/index.js';
|
|
|
3
3
|
import { styles } from '../../lib/styles.js';
|
|
4
4
|
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
|
|
5
5
|
export default class TicketView extends PMOCommand {
|
|
6
|
+
static aliases = ['ticket:show'];
|
|
6
7
|
static description = 'View detailed ticket information';
|
|
7
8
|
static examples = [
|
|
8
9
|
'<%= config.bin %> <%= command.id %> TICK-001',
|
|
@@ -254,6 +254,23 @@ export default class WorkReady extends PMOCommand {
|
|
|
254
254
|
}
|
|
255
255
|
try {
|
|
256
256
|
const baseBranch = getDefaultBaseBranch();
|
|
257
|
+
// Check if PR already exists for this branch
|
|
258
|
+
const existingPR = getPRForBranch(currentBranch);
|
|
259
|
+
if (existingPR) {
|
|
260
|
+
if (existingPR.state === 'MERGED') {
|
|
261
|
+
this.log(styles.muted(` PR #${existingPR.number} already merged: ${existingPR.url}`));
|
|
262
|
+
return existingPR.url;
|
|
263
|
+
}
|
|
264
|
+
if (existingPR.state === 'OPEN') {
|
|
265
|
+
// Push any unpushed commits so the existing PR is up to date
|
|
266
|
+
if (hasUnpushedCommits(currentBranch)) {
|
|
267
|
+
this.log(styles.muted(` Pushing unpushed commits to existing PR...`));
|
|
268
|
+
pushBranch(currentBranch);
|
|
269
|
+
}
|
|
270
|
+
this.log(styles.muted(` PR #${existingPR.number} already exists: ${existingPR.url}`));
|
|
271
|
+
return existingPR.url;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
257
274
|
// Push branch if needed
|
|
258
275
|
if (!hasBranchBeenPushed(currentBranch)) {
|
|
259
276
|
this.log(styles.muted(` Pushing branch to origin...`));
|
|
@@ -26,7 +26,7 @@ export default class WorkResolve extends PMOCommand {
|
|
|
26
26
|
}),
|
|
27
27
|
};
|
|
28
28
|
async execute() {
|
|
29
|
-
const {
|
|
29
|
+
const { flags, argv } = await this.parse(WorkResolve);
|
|
30
30
|
const projectId = flags.project;
|
|
31
31
|
const jsonMode = shouldOutputJson(flags);
|
|
32
32
|
const handleError = (code, message) => {
|
|
@@ -491,18 +491,18 @@ export default class WorkSpawn extends PMOCommand {
|
|
|
491
491
|
let candidates = [...allTickets];
|
|
492
492
|
// Apply category filter
|
|
493
493
|
if (flags.category) {
|
|
494
|
-
const categoryList = flags.category.split(',').map(c => c.trim().toLowerCase());
|
|
494
|
+
const categoryList = new Set(flags.category.split(',').map(c => c.trim().toLowerCase()));
|
|
495
495
|
candidates = candidates.filter(t => {
|
|
496
496
|
const ticketCat = (t.category || '').toLowerCase();
|
|
497
|
-
return categoryList.
|
|
497
|
+
return categoryList.has(ticketCat);
|
|
498
498
|
});
|
|
499
499
|
}
|
|
500
500
|
// Apply priority filter
|
|
501
501
|
if (flags.priority) {
|
|
502
|
-
const priorityList = flags.priority.split(',').map(p => p.trim().toUpperCase());
|
|
502
|
+
const priorityList = new Set(flags.priority.split(',').map(p => p.trim().toUpperCase()));
|
|
503
503
|
candidates = candidates.filter(t => {
|
|
504
504
|
const ticketPriority = (t.priority || '').toUpperCase();
|
|
505
|
-
return priorityList.
|
|
505
|
+
return priorityList.has(ticketPriority);
|
|
506
506
|
});
|
|
507
507
|
}
|
|
508
508
|
// Apply epic filter
|
|
@@ -28,6 +28,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
28
28
|
focus: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
29
29
|
clone: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
30
30
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
31
|
+
'use-api-key': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
31
32
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
32
33
|
machine: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
33
34
|
project: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable max-lines -- large command with many execution paths */
|
|
1
2
|
import { Args, Flags } from '@oclif/core';
|
|
2
3
|
import * as fs from 'node:fs';
|
|
3
4
|
import * as path from 'node:path';
|
|
@@ -179,6 +180,11 @@ export default class WorkStart extends PMOCommand {
|
|
|
179
180
|
description: 'Skip confirmation prompt (for non-TTY/scripted execution)',
|
|
180
181
|
default: false,
|
|
181
182
|
}),
|
|
183
|
+
'use-api-key': Flags.boolean({
|
|
184
|
+
description: 'Use ANTHROPIC_API_KEY for Docker containers instead of OAuth credentials',
|
|
185
|
+
default: false,
|
|
186
|
+
hidden: true,
|
|
187
|
+
}),
|
|
182
188
|
};
|
|
183
189
|
async execute() {
|
|
184
190
|
const { args, flags } = await this.parse(WorkStart);
|
|
@@ -1000,8 +1006,10 @@ export default class WorkStart extends PMOCommand {
|
|
|
1000
1006
|
// Default to interactive output mode (streaming UI)
|
|
1001
1007
|
// Can be overridden via --output flag if needed
|
|
1002
1008
|
const outputMode = flags.output || DEFAULT_EXECUTION_CONFIG.outputMode;
|
|
1009
|
+
// Track whether user explicitly chose to use API key instead of OAuth
|
|
1010
|
+
let useApiKey = flags['use-api-key'] || false;
|
|
1003
1011
|
// Check Docker credentials for devcontainer environment
|
|
1004
|
-
if (environment === 'devcontainer') {
|
|
1012
|
+
if (environment === 'devcontainer' && !useApiKey) {
|
|
1005
1013
|
const hasCredentials = dockerCredentialsExist();
|
|
1006
1014
|
if (!hasCredentials) {
|
|
1007
1015
|
// In JSON mode with --yes, continue anyway (agent can run /login)
|
|
@@ -1009,10 +1017,19 @@ export default class WorkStart extends PMOCommand {
|
|
|
1009
1017
|
// Continue without prompting - agent will need to handle auth
|
|
1010
1018
|
}
|
|
1011
1019
|
else {
|
|
1020
|
+
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
|
1012
1021
|
this.log('');
|
|
1013
|
-
this.log(styles.warning('⚠️ No Claude Code credentials found for Docker containers'));
|
|
1014
|
-
this.log(styles.muted(' Agents
|
|
1022
|
+
this.log(styles.warning('⚠️ No Claude Code OAuth credentials found for Docker containers'));
|
|
1023
|
+
this.log(styles.muted(' Agents need credentials to authenticate with Claude.'));
|
|
1015
1024
|
this.log('');
|
|
1025
|
+
// Build choices based on available options
|
|
1026
|
+
const authChoices = [
|
|
1027
|
+
{ name: `🔐 Run ${this.config.bin} agent auth now (recommended — uses Max subscription)`, value: 'auth' },
|
|
1028
|
+
];
|
|
1029
|
+
if (hasApiKey) {
|
|
1030
|
+
authChoices.push({ name: '🔑 Use ANTHROPIC_API_KEY (⚠️ uses API credits, not Max subscription)', value: 'apikey' });
|
|
1031
|
+
}
|
|
1032
|
+
authChoices.push({ name: '💻 Switch to host environment instead', value: 'host' }, { name: '✗ Cancel', value: 'cancel' });
|
|
1016
1033
|
// Use FlagResolver for auth action
|
|
1017
1034
|
const authResolver = new FlagResolver({
|
|
1018
1035
|
commandName: 'work start',
|
|
@@ -1024,12 +1041,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
1024
1041
|
flagName: 'authAction',
|
|
1025
1042
|
type: 'list',
|
|
1026
1043
|
message: 'What would you like to do?',
|
|
1027
|
-
choices: () =>
|
|
1028
|
-
{ name: `🔐 Run ${this.config.bin} agent auth now (one-time setup)`, value: 'auth' },
|
|
1029
|
-
{ name: '💻 Switch to host environment instead', value: 'host' },
|
|
1030
|
-
{ name: '⏩ Continue anyway (must run /login in first agent)', value: 'continue' },
|
|
1031
|
-
{ name: '✗ Cancel', value: 'cancel' },
|
|
1032
|
-
],
|
|
1044
|
+
choices: () => authChoices,
|
|
1033
1045
|
});
|
|
1034
1046
|
const authResult = await authResolver.resolve();
|
|
1035
1047
|
const authAction = authResult.authAction;
|
|
@@ -1042,6 +1054,12 @@ export default class WorkStart extends PMOCommand {
|
|
|
1042
1054
|
environment = 'host';
|
|
1043
1055
|
this.log(styles.muted('Switched to host environment.'));
|
|
1044
1056
|
}
|
|
1057
|
+
else if (authAction === 'apikey') {
|
|
1058
|
+
useApiKey = true;
|
|
1059
|
+
this.log(styles.warning('Using ANTHROPIC_API_KEY — this will consume API credits.'));
|
|
1060
|
+
this.log(styles.muted(`Run "${this.config.bin} agent auth" to set up OAuth and use your Max subscription instead.`));
|
|
1061
|
+
this.log('');
|
|
1062
|
+
}
|
|
1045
1063
|
else if (authAction === 'auth') {
|
|
1046
1064
|
this.log('');
|
|
1047
1065
|
this.log(styles.primary(`Opening ${this.config.bin} agent auth in new tab...`));
|
|
@@ -1094,10 +1112,13 @@ export default class WorkStart extends PMOCommand {
|
|
|
1094
1112
|
}
|
|
1095
1113
|
this.log('');
|
|
1096
1114
|
}
|
|
1097
|
-
// authAction === 'continue' falls through
|
|
1098
1115
|
}
|
|
1099
1116
|
}
|
|
1100
1117
|
}
|
|
1118
|
+
// Pass API key preference to execution context
|
|
1119
|
+
if (useApiKey) {
|
|
1120
|
+
context.useApiKey = true;
|
|
1121
|
+
}
|
|
1101
1122
|
// Prompt for permissions mode (all environments)
|
|
1102
1123
|
// Use FlagResolver to handle both JSON mode and interactive prompts consistently
|
|
1103
1124
|
if (flags['permission-mode']) {
|
|
@@ -1611,23 +1632,30 @@ export default class WorkStart extends PMOCommand {
|
|
|
1611
1632
|
const agentDir = path.join(workspaceInfo.agentsPath, agent.name);
|
|
1612
1633
|
return hasDevcontainerConfig(agentDir) && !flags['run-on-host'];
|
|
1613
1634
|
});
|
|
1635
|
+
// Track whether user explicitly chose to use API key instead of OAuth
|
|
1636
|
+
let batchUseApiKey = false;
|
|
1614
1637
|
if (anyUseDevcontainer) {
|
|
1615
1638
|
const hasCredentials = dockerCredentialsExist();
|
|
1616
1639
|
if (!hasCredentials) {
|
|
1640
|
+
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
|
1617
1641
|
this.log('');
|
|
1618
|
-
this.log(styles.warning('⚠️ No Claude Code credentials found for Docker containers'));
|
|
1619
|
-
this.log(styles.muted(' Agents
|
|
1642
|
+
this.log(styles.warning('⚠️ No Claude Code OAuth credentials found for Docker containers'));
|
|
1643
|
+
this.log(styles.muted(' Agents need credentials to authenticate with Claude.'));
|
|
1620
1644
|
this.log('');
|
|
1645
|
+
// Build choices based on available options
|
|
1646
|
+
const batchAuthChoices = [
|
|
1647
|
+
{ name: `🔐 Run ${this.config.bin} agent auth now (recommended — uses Max subscription)`, value: 'auth', command: `${this.config.bin} agent auth` },
|
|
1648
|
+
];
|
|
1649
|
+
if (hasApiKey) {
|
|
1650
|
+
batchAuthChoices.push({ name: '🔑 Use ANTHROPIC_API_KEY (⚠️ uses API credits, not Max subscription)', value: 'apikey', command: '' });
|
|
1651
|
+
}
|
|
1652
|
+
batchAuthChoices.push({ name: '💻 Run all agents on host instead (--run-on-host)', value: 'host', command: 'prlt work start --all --run-on-host --json' }, { name: '✗ Cancel', value: 'cancel', command: '' });
|
|
1621
1653
|
const { authAction } = await this.prompt([
|
|
1622
1654
|
{
|
|
1623
1655
|
type: 'list',
|
|
1624
1656
|
name: 'authAction',
|
|
1625
1657
|
message: 'What would you like to do?',
|
|
1626
|
-
choices:
|
|
1627
|
-
{ name: `🔐 Run ${this.config.bin} agent auth now (one-time setup)`, value: 'auth', command: `${this.config.bin} agent auth` },
|
|
1628
|
-
{ name: '💻 Run all agents on host instead (--run-on-host)', value: 'host', command: 'prlt work start --all --run-on-host --json' },
|
|
1629
|
-
{ name: '✗ Cancel', value: 'cancel', command: '' },
|
|
1630
|
-
],
|
|
1658
|
+
choices: batchAuthChoices,
|
|
1631
1659
|
},
|
|
1632
1660
|
], batchJsonModeConfig);
|
|
1633
1661
|
if (authAction === 'cancel') {
|
|
@@ -1639,6 +1667,12 @@ export default class WorkStart extends PMOCommand {
|
|
|
1639
1667
|
flags['run-on-host'] = true;
|
|
1640
1668
|
this.log(styles.muted('All agents will run on host.'));
|
|
1641
1669
|
}
|
|
1670
|
+
else if (authAction === 'apikey') {
|
|
1671
|
+
batchUseApiKey = true;
|
|
1672
|
+
this.log(styles.warning('Using ANTHROPIC_API_KEY — this will consume API credits.'));
|
|
1673
|
+
this.log(styles.muted(`Run "${this.config.bin} agent auth" to set up OAuth and use your Max subscription instead.`));
|
|
1674
|
+
this.log('');
|
|
1675
|
+
}
|
|
1642
1676
|
else if (authAction === 'auth') {
|
|
1643
1677
|
this.log('');
|
|
1644
1678
|
this.log(styles.primary(`Opening ${this.config.bin} agent auth in new tab...`));
|
|
@@ -1715,6 +1749,7 @@ export default class WorkStart extends PMOCommand {
|
|
|
1715
1749
|
'--display', flags.display || 'background',
|
|
1716
1750
|
...(flags.executor ? ['--executor', flags.executor] : []),
|
|
1717
1751
|
...(flags['run-on-host'] ? ['--run-on-host'] : []),
|
|
1752
|
+
...(batchUseApiKey ? ['--use-api-key'] : []),
|
|
1718
1753
|
...(flags.force ? ['--force'] : []),
|
|
1719
1754
|
'--permission-mode', batchPermissionMode,
|
|
1720
1755
|
...(flags.clone ? ['--clone'] : []),
|
|
@@ -52,7 +52,7 @@ export interface AgentWorktree {
|
|
|
52
52
|
is_clean: boolean;
|
|
53
53
|
last_checked?: string;
|
|
54
54
|
}
|
|
55
|
-
export declare const CREATE_TABLES_SQL = "\n-- Core workspace metadata\nCREATE TABLE IF NOT EXISTS workspace (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n type TEXT NOT NULL CHECK (type IN ('hq', 'workspace')),\n workspace_name TEXT NOT NULL,\n has_pmo BOOLEAN DEFAULT FALSE,\n active_theme_id TEXT,\n created_at TEXT NOT NULL,\n FOREIGN KEY (active_theme_id) REFERENCES agent_themes(id) ON DELETE SET NULL\n);\n\n-- Repository management\nCREATE TABLE IF NOT EXISTS repositories (\n name TEXT PRIMARY KEY,\n path TEXT NOT NULL,\n type TEXT DEFAULT 'main' CHECK (type IN ('main', 'dependency')),\n source_url TEXT,\n action TEXT CHECK (action IN ('clone', 'move', 'link')),\n added_at TEXT NOT NULL\n);\n\n-- Agent naming themes (optional)\nCREATE TABLE IF NOT EXISTS agent_themes (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n display_name TEXT NOT NULL,\n description TEXT,\n builtin BOOLEAN DEFAULT FALSE,\n created_at TEXT NOT NULL\n);\n\n-- Names available within each theme\nCREATE TABLE IF NOT EXISTS agent_theme_names (\n theme_id TEXT NOT NULL,\n name TEXT NOT NULL,\n PRIMARY KEY (theme_id, name),\n FOREIGN KEY (theme_id) REFERENCES agent_themes(id) ON DELETE CASCADE\n);\n\n-- Agent instances in workspace\nCREATE TABLE IF NOT EXISTS agents (\n name TEXT PRIMARY KEY,\n type TEXT NOT NULL DEFAULT 'persistent' CHECK (type IN ('persistent', 'ephemeral')),\n status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'cleaned')),\n base_name TEXT,\n theme_id TEXT,\n worktree_path TEXT,\n mount_mode TEXT NOT NULL DEFAULT 'worktree' CHECK (mount_mode IN ('worktree', 'clone')),\n created_at TEXT NOT NULL,\n cleaned_at TEXT,\n FOREIGN KEY (theme_id) REFERENCES agent_themes(id) ON DELETE SET NULL\n);\n\n-- Agent-owned worktrees\nCREATE TABLE IF NOT EXISTS agent_worktrees (\n agent_name TEXT NOT NULL,\n repo_name TEXT NOT NULL,\n worktree_path TEXT NOT NULL,\n branch TEXT NOT NULL,\n created_at TEXT NOT NULL,\n PRIMARY KEY (agent_name, repo_name),\n FOREIGN KEY (agent_name) REFERENCES agents(name) ON DELETE CASCADE,\n FOREIGN KEY (repo_name) REFERENCES repositories(name) ON DELETE CASCADE\n);\n\n-- Workspace-level settings (key-value store)\nCREATE TABLE IF NOT EXISTS workspace_settings (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\n-- =============================================================================\n-- Indexes\n-- =============================================================================\n\nCREATE INDEX IF NOT EXISTS idx_worktrees_agent ON agent_worktrees(agent_name);\nCREATE INDEX IF NOT EXISTS idx_worktrees_repo ON agent_worktrees(repo_name);\nCREATE INDEX IF NOT EXISTS idx_theme_names_theme ON agent_theme_names(theme_id);\nCREATE INDEX IF NOT EXISTS idx_agents_theme ON agents(theme_id);\n";
|
|
55
|
+
export declare const CREATE_TABLES_SQL = "\n-- Core workspace metadata\nCREATE TABLE IF NOT EXISTS workspace (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n type TEXT NOT NULL CHECK (type IN ('hq', 'workspace')),\n workspace_name TEXT NOT NULL,\n has_pmo BOOLEAN DEFAULT FALSE,\n active_theme_id TEXT,\n created_at TEXT NOT NULL,\n FOREIGN KEY (active_theme_id) REFERENCES agent_themes(id) ON DELETE SET NULL\n);\n\n-- Repository management\nCREATE TABLE IF NOT EXISTS repositories (\n name TEXT PRIMARY KEY,\n path TEXT NOT NULL,\n type TEXT DEFAULT 'main' CHECK (type IN ('main', 'dependency')),\n source_url TEXT,\n action TEXT CHECK (action IN ('clone', 'move', 'link')),\n added_at TEXT NOT NULL\n);\n\n-- Agent naming themes (optional)\nCREATE TABLE IF NOT EXISTS agent_themes (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n display_name TEXT NOT NULL,\n description TEXT,\n builtin BOOLEAN DEFAULT FALSE,\n created_at TEXT NOT NULL\n);\n\n-- Names available within each theme\nCREATE TABLE IF NOT EXISTS agent_theme_names (\n theme_id TEXT NOT NULL,\n name TEXT NOT NULL,\n PRIMARY KEY (theme_id, name),\n FOREIGN KEY (theme_id) REFERENCES agent_themes(id) ON DELETE CASCADE\n);\n\n-- Agent instances in workspace\nCREATE TABLE IF NOT EXISTS agents (\n name TEXT PRIMARY KEY,\n type TEXT NOT NULL DEFAULT 'persistent' CHECK (type IN ('persistent', 'ephemeral')),\n status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'cleaned')),\n base_name TEXT,\n theme_id TEXT,\n worktree_path TEXT,\n mount_mode TEXT NOT NULL DEFAULT 'worktree' CHECK (mount_mode IN ('worktree', 'clone')),\n created_at TEXT NOT NULL,\n cleaned_at TEXT,\n FOREIGN KEY (theme_id) REFERENCES agent_themes(id) ON DELETE SET NULL\n);\n\n-- Agent-owned worktrees\nCREATE TABLE IF NOT EXISTS agent_worktrees (\n agent_name TEXT NOT NULL,\n repo_name TEXT NOT NULL,\n worktree_path TEXT NOT NULL,\n branch TEXT NOT NULL,\n created_at TEXT NOT NULL,\n last_commit_hash TEXT,\n commits_ahead INTEGER NOT NULL DEFAULT 0,\n is_clean INTEGER NOT NULL DEFAULT 1,\n last_checked TEXT,\n PRIMARY KEY (agent_name, repo_name),\n FOREIGN KEY (agent_name) REFERENCES agents(name) ON DELETE CASCADE,\n FOREIGN KEY (repo_name) REFERENCES repositories(name) ON DELETE CASCADE\n);\n\n-- Workspace-level settings (key-value store)\nCREATE TABLE IF NOT EXISTS workspace_settings (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\n-- =============================================================================\n-- Indexes\n-- =============================================================================\n\nCREATE INDEX IF NOT EXISTS idx_worktrees_agent ON agent_worktrees(agent_name);\nCREATE INDEX IF NOT EXISTS idx_worktrees_repo ON agent_worktrees(repo_name);\nCREATE INDEX IF NOT EXISTS idx_theme_names_theme ON agent_theme_names(theme_id);\nCREATE INDEX IF NOT EXISTS idx_agents_theme ON agents(theme_id);\n";
|
|
56
56
|
/**
|
|
57
57
|
* Get the database path for a workspace
|
|
58
58
|
*/
|
|
@@ -64,6 +64,10 @@ CREATE TABLE IF NOT EXISTS agent_worktrees (
|
|
|
64
64
|
worktree_path TEXT NOT NULL,
|
|
65
65
|
branch TEXT NOT NULL,
|
|
66
66
|
created_at TEXT NOT NULL,
|
|
67
|
+
last_commit_hash TEXT,
|
|
68
|
+
commits_ahead INTEGER NOT NULL DEFAULT 0,
|
|
69
|
+
is_clean INTEGER NOT NULL DEFAULT 1,
|
|
70
|
+
last_checked TEXT,
|
|
67
71
|
PRIMARY KEY (agent_name, repo_name),
|
|
68
72
|
FOREIGN KEY (agent_name) REFERENCES agents(name) ON DELETE CASCADE,
|
|
69
73
|
FOREIGN KEY (repo_name) REFERENCES repositories(name) ON DELETE CASCADE
|
|
@@ -184,6 +188,22 @@ export function openWorkspaceDatabase(workspacePath) {
|
|
|
184
188
|
catch {
|
|
185
189
|
// Ignore migration errors - table might not exist yet
|
|
186
190
|
}
|
|
191
|
+
// Migration: add missing columns to agent_worktrees table (TKT-1014)
|
|
192
|
+
try {
|
|
193
|
+
const worktreesTableExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='agent_worktrees'").get();
|
|
194
|
+
if (worktreesTableExists) {
|
|
195
|
+
const worktreeTableInfo = db.prepare("PRAGMA table_info(agent_worktrees)").all();
|
|
196
|
+
if (!worktreeTableInfo.some(col => col.name === 'commits_ahead')) {
|
|
197
|
+
db.exec("ALTER TABLE agent_worktrees ADD COLUMN last_commit_hash TEXT");
|
|
198
|
+
db.exec("ALTER TABLE agent_worktrees ADD COLUMN commits_ahead INTEGER NOT NULL DEFAULT 0");
|
|
199
|
+
db.exec("ALTER TABLE agent_worktrees ADD COLUMN is_clean INTEGER NOT NULL DEFAULT 1");
|
|
200
|
+
db.exec("ALTER TABLE agent_worktrees ADD COLUMN last_checked TEXT");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Ignore migration errors - table might not exist yet or columns already exist
|
|
206
|
+
}
|
|
187
207
|
// Migration: add mount_mode column to agents table (TKT-686)
|
|
188
208
|
try {
|
|
189
209
|
const agentsTableInfo = db.prepare("PRAGMA table_info(agents)").all();
|
|
@@ -96,7 +96,9 @@ export function generateDevcontainerJson(options, config) {
|
|
|
96
96
|
mounts,
|
|
97
97
|
containerEnv: {
|
|
98
98
|
DEVCONTAINER: 'true',
|
|
99
|
-
|
|
99
|
+
// NOTE: ANTHROPIC_API_KEY is intentionally NOT passed by default.
|
|
100
|
+
// Claude Code prefers API key over OAuth, so passing it would cause agents
|
|
101
|
+
// to burn API credits instead of using Max subscription via OAuth.
|
|
100
102
|
// GH_TOKEN enables gh CLI in container (for PR creation, etc.)
|
|
101
103
|
GH_TOKEN: '${localEnv:GH_TOKEN}',
|
|
102
104
|
GITHUB_TOKEN: '${localEnv:GITHUB_TOKEN}',
|
|
@@ -46,8 +46,13 @@ export declare function configureITermTmuxWindowMode(mode: 'tab' | 'window'): vo
|
|
|
46
46
|
*/
|
|
47
47
|
export declare function credentialsVolumeExists(): boolean;
|
|
48
48
|
/**
|
|
49
|
-
* Check if valid Claude credentials exist in the Docker volume.
|
|
50
|
-
* Returns true if credentials
|
|
49
|
+
* Check if valid Claude OAuth credentials exist in the Docker volume.
|
|
50
|
+
* Returns true if OAuth credentials are stored (even if access token is expired,
|
|
51
|
+
* since Claude Code handles refresh internally using stored refresh tokens).
|
|
52
|
+
*
|
|
53
|
+
* NOTE: This intentionally does NOT check for ANTHROPIC_API_KEY. If the user
|
|
54
|
+
* has an API key but no OAuth credentials, we want to prompt them to set up
|
|
55
|
+
* OAuth (which uses their Max subscription) rather than silently burning API credits.
|
|
51
56
|
*/
|
|
52
57
|
export declare function dockerCredentialsExist(): boolean;
|
|
53
58
|
/**
|