ticket-to-pr 1.2.2 → 1.3.0
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/README.md +34 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +82 -1
- package/dist/index.js +8 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -212,6 +212,8 @@ ticket-to-pr --dry-run --once
|
|
|
212
212
|
|----------------|----------|
|
|
213
213
|
| `init` | Guided setup — validates Notion credentials live, auto-detects build commands, generates starter `CLAUDE.md`, configures projects, writes `.env.local` and `projects.json`. Detects existing config on re-run. |
|
|
214
214
|
| `doctor` | Diagnostic check — verifies environment, Notion connectivity, database schema, tools, and projects |
|
|
215
|
+
| `model` | View current models and available options |
|
|
216
|
+
| `model <review\|execute\|both> <model>` | Set the Claude model for an agent. Accepts aliases (`opus`, `sonnet`, `haiku`) or full model IDs. |
|
|
215
217
|
| *(none)* | Continuous polling every 30s |
|
|
216
218
|
| `--once` | Poll once, wait for agents to finish, exit |
|
|
217
219
|
| `--dry-run` | Poll and log what would happen, don't run agents |
|
|
@@ -320,6 +322,35 @@ Docs: https://www.tickettopr.com
|
|
|
320
322
|
- `gh` missing is a warning (PRs won't auto-create but everything else works), `claude` missing is a hard failure
|
|
321
323
|
- Exits with code 1 if any hard failures, 0 otherwise
|
|
322
324
|
|
|
325
|
+
### `model` — Change AI Models
|
|
326
|
+
|
|
327
|
+
View or change which Claude models the agents use:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
# Show current models and available options
|
|
331
|
+
ticket-to-pr model
|
|
332
|
+
|
|
333
|
+
# Set review model (used for scoring tickets)
|
|
334
|
+
ticket-to-pr model review sonnet
|
|
335
|
+
|
|
336
|
+
# Set execute model (used for writing code)
|
|
337
|
+
ticket-to-pr model execute opus
|
|
338
|
+
|
|
339
|
+
# Set both at once
|
|
340
|
+
ticket-to-pr model both haiku
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Available model aliases:
|
|
344
|
+
|
|
345
|
+
| Alias | Model ID | Best for |
|
|
346
|
+
|-------|----------|----------|
|
|
347
|
+
| `opus` | `claude-opus-4-6` | Best quality (recommended for execute) |
|
|
348
|
+
| `sonnet` | `claude-sonnet-4-6` | Fast and capable (recommended for review) |
|
|
349
|
+
| `sonnet45` | `claude-sonnet-4-5-20250929` | Previous generation Sonnet |
|
|
350
|
+
| `haiku` | `claude-haiku-4-5-20251001` | Fastest, lowest cost |
|
|
351
|
+
|
|
352
|
+
You can also pass a full model ID directly (e.g. `ticket-to-pr model review claude-sonnet-4-5-20250929`). Changes are saved to `.env.local` and take effect on the next poll cycle.
|
|
353
|
+
|
|
323
354
|
### Your First Ticket
|
|
324
355
|
|
|
325
356
|
1. Click **"+ New"** on your Notion board
|
|
@@ -481,6 +512,8 @@ All settings in `config.ts`:
|
|
|
481
512
|
|
|
482
513
|
| Setting | Default | Purpose |
|
|
483
514
|
|---------|---------|---------|
|
|
515
|
+
| `REVIEW_MODEL` | `claude-sonnet-4-6` | Review agent model (change with `ticket-to-pr model review <model>`) |
|
|
516
|
+
| `EXECUTE_MODEL` | `claude-opus-4-6` | Execute agent model (change with `ticket-to-pr model execute <model>`) |
|
|
484
517
|
| `POLL_INTERVAL_MS` | 30000 | How often to check Notion (ms) |
|
|
485
518
|
| `REVIEW_BUDGET_USD` | 2.00 | Max USD per review agent run |
|
|
486
519
|
| `EXECUTE_BUDGET_USD` | 15.00 | Max USD per execute agent run |
|
|
@@ -505,7 +538,7 @@ Project configuration in `projects.json`:
|
|
|
505
538
|
```
|
|
506
539
|
ticket-to-pr/
|
|
507
540
|
index.ts # Poll loop, agent runner, worktree git workflow, graceful shutdown
|
|
508
|
-
cli.ts # init
|
|
541
|
+
cli.ts # init, doctor, and model commands
|
|
509
542
|
config.ts # Budgets, column names, license check, TypeScript types
|
|
510
543
|
projects.json # Your project directories and build commands (git-ignored, copy from example)
|
|
511
544
|
projects.example.json # Template for projects.json
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createInterface } from 'node:readline';
|
|
|
2
2
|
import { execSync } from 'node:child_process';
|
|
3
3
|
import { readFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { mask, shellEscape, writeEnvFile, updateProjectsFile, getDefaultBranch } from './lib/utils.js';
|
|
5
|
+
import { mask, shellEscape, writeEnvFile, updateProjectsFile, getDefaultBranch, parseEnvFile } from './lib/utils.js';
|
|
6
6
|
import { getProjectNames, getProjectDir, getBaseBranch, getBlockedFiles, getSkipPR } from './lib/projects.js';
|
|
7
7
|
import { CONFIG_DIR } from './lib/paths.js';
|
|
8
8
|
// -- Colors --
|
|
@@ -768,3 +768,84 @@ export async function runInit() {
|
|
|
768
768
|
rl.close();
|
|
769
769
|
}
|
|
770
770
|
}
|
|
771
|
+
// -- Model --
|
|
772
|
+
const KNOWN_MODELS = [
|
|
773
|
+
{ alias: 'opus', id: 'claude-opus-4-6', description: 'Best quality, highest cost' },
|
|
774
|
+
{ alias: 'sonnet', id: 'claude-sonnet-4-6', description: 'Fast and capable (recommended for review)' },
|
|
775
|
+
{ alias: 'sonnet45', id: 'claude-sonnet-4-5-20250929', description: 'Previous generation Sonnet' },
|
|
776
|
+
{ alias: 'haiku', id: 'claude-haiku-4-5-20251001', description: 'Fastest, lowest cost' },
|
|
777
|
+
];
|
|
778
|
+
function resolveModel(input) {
|
|
779
|
+
const lower = input.toLowerCase();
|
|
780
|
+
const byAlias = KNOWN_MODELS.find((m) => m.alias === lower);
|
|
781
|
+
if (byAlias)
|
|
782
|
+
return byAlias;
|
|
783
|
+
const byId = KNOWN_MODELS.find((m) => m.id === lower);
|
|
784
|
+
if (byId)
|
|
785
|
+
return byId;
|
|
786
|
+
// Accept any claude- prefixed model ID as a pass-through
|
|
787
|
+
if (input.startsWith('claude-'))
|
|
788
|
+
return { alias: input, id: input };
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
export async function runModel(args) {
|
|
792
|
+
const envPath = join(CONFIG_DIR, '.env.local');
|
|
793
|
+
const envVars = parseEnvFile(envPath);
|
|
794
|
+
const currentReview = envVars.REVIEW_MODEL || 'claude-sonnet-4-6';
|
|
795
|
+
const currentExecute = envVars.EXECUTE_MODEL || 'claude-opus-4-6';
|
|
796
|
+
// No args: show current models and available options
|
|
797
|
+
if (args.length === 0) {
|
|
798
|
+
console.log(`\n${BOLD}Current models:${RESET}`);
|
|
799
|
+
const reviewAlias = KNOWN_MODELS.find((m) => m.id === currentReview)?.alias;
|
|
800
|
+
const executeAlias = KNOWN_MODELS.find((m) => m.id === currentExecute)?.alias;
|
|
801
|
+
console.log(` Review: ${GREEN}${currentReview}${RESET}${reviewAlias ? ` (${reviewAlias})` : ''}`);
|
|
802
|
+
console.log(` Execute: ${GREEN}${currentExecute}${RESET}${executeAlias ? ` (${executeAlias})` : ''}`);
|
|
803
|
+
console.log(`\n${BOLD}Available models:${RESET}`);
|
|
804
|
+
for (const m of KNOWN_MODELS) {
|
|
805
|
+
console.log(` ${BOLD}${m.alias.padEnd(10)}${RESET} ${DIM}${m.id.padEnd(35)}${RESET} ${m.description}`);
|
|
806
|
+
}
|
|
807
|
+
console.log(`\n${BOLD}Usage:${RESET}`);
|
|
808
|
+
console.log(` ticket-to-pr model review sonnet ${DIM}# set review model${RESET}`);
|
|
809
|
+
console.log(` ticket-to-pr model execute opus ${DIM}# set execute model${RESET}`);
|
|
810
|
+
console.log(` ticket-to-pr model both haiku ${DIM}# set both at once${RESET}\n`);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
// Parse: model <agent> <model>
|
|
814
|
+
if (args.length < 2) {
|
|
815
|
+
console.log(`${RED}Usage: ticket-to-pr model <review|execute|both> <model>${RESET}`);
|
|
816
|
+
console.log(`${DIM}Run "ticket-to-pr model" to see available models.${RESET}`);
|
|
817
|
+
process.exitCode = 1;
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
const agent = args[0].toLowerCase();
|
|
821
|
+
const modelInput = args[1];
|
|
822
|
+
if (!['review', 'execute', 'both'].includes(agent)) {
|
|
823
|
+
console.log(`${RED}Unknown agent "${args[0]}". Use: review, execute, or both${RESET}`);
|
|
824
|
+
process.exitCode = 1;
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
const resolved = resolveModel(modelInput);
|
|
828
|
+
if (!resolved) {
|
|
829
|
+
console.log(`${RED}Unknown model "${modelInput}".${RESET}`);
|
|
830
|
+
console.log(`\n${BOLD}Available models:${RESET}`);
|
|
831
|
+
for (const m of KNOWN_MODELS) {
|
|
832
|
+
console.log(` ${BOLD}${m.alias.padEnd(10)}${RESET} ${DIM}${m.id}${RESET}`);
|
|
833
|
+
}
|
|
834
|
+
console.log(`\n${DIM}You can also pass a full model ID like claude-sonnet-4-5-20250929${RESET}`);
|
|
835
|
+
process.exitCode = 1;
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
const updates = {};
|
|
839
|
+
if (agent === 'review' || agent === 'both')
|
|
840
|
+
updates.REVIEW_MODEL = resolved.id;
|
|
841
|
+
if (agent === 'execute' || agent === 'both')
|
|
842
|
+
updates.EXECUTE_MODEL = resolved.id;
|
|
843
|
+
writeEnvFile(envPath, updates);
|
|
844
|
+
if (updates.REVIEW_MODEL) {
|
|
845
|
+
printStatus(true, 'Review model', `${resolved.id}${resolved.alias !== resolved.id ? ` (${resolved.alias})` : ''}`);
|
|
846
|
+
}
|
|
847
|
+
if (updates.EXECUTE_MODEL) {
|
|
848
|
+
printStatus(true, 'Execute model', `${resolved.id}${resolved.alias !== resolved.id ? ` (${resolved.alias})` : ''}`);
|
|
849
|
+
}
|
|
850
|
+
console.log(`${DIM}Saved to .env.local. Takes effect on next poll cycle.${RESET}\n`);
|
|
851
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -13,9 +13,14 @@ loadEnv(join(CONFIG_DIR, '.env.local'));
|
|
|
13
13
|
delete process.env.CLAUDECODE;
|
|
14
14
|
// -- Subcommand routing --
|
|
15
15
|
const subcommand = process.argv[2];
|
|
16
|
-
if (subcommand === 'init' || subcommand === 'doctor') {
|
|
17
|
-
const { runInit, runDoctor } = await import('./cli.js');
|
|
18
|
-
|
|
16
|
+
if (subcommand === 'init' || subcommand === 'doctor' || subcommand === 'model') {
|
|
17
|
+
const { runInit, runDoctor, runModel } = await import('./cli.js');
|
|
18
|
+
if (subcommand === 'model') {
|
|
19
|
+
await runModel(process.argv.slice(3));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
await (subcommand === 'init' ? runInit() : runDoctor());
|
|
23
|
+
}
|
|
19
24
|
process.exit(0);
|
|
20
25
|
}
|
|
21
26
|
// -- CLI flags --
|