openthrottle 0.1.11 → 0.1.12

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.
@@ -0,0 +1,125 @@
1
+ // =============================================================================
2
+ // openthrottle config — View and edit .openthrottle.yml settings.
3
+ //
4
+ // Usage:
5
+ // openthrottle config Show current config
6
+ // openthrottle config <key> <value> Set a value (dot-notation)
7
+ //
8
+ // Examples:
9
+ // openthrottle config phases.plan.model opus
10
+ // openthrottle config phases.build.timeout 3600
11
+ // openthrottle config limits.task_timeout 7200
12
+ // openthrottle config review.enabled false
13
+ // =============================================================================
14
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
15
+ import { join } from 'node:path';
16
+ import { parse, stringify } from 'yaml';
17
+ const CONFIG_FILE = '.openthrottle.yml';
18
+ function configPath() {
19
+ return join(process.cwd(), CONFIG_FILE);
20
+ }
21
+ function loadYaml() {
22
+ const path = configPath();
23
+ if (!existsSync(path)) {
24
+ console.error(`No ${CONFIG_FILE} found. Run "openthrottle init" first.`);
25
+ process.exit(1);
26
+ }
27
+ return parse(readFileSync(path, 'utf8'));
28
+ }
29
+ function saveYaml(doc) {
30
+ const header = [
31
+ '# openthrottle.yml — project config for Open Throttle (Daytona runtime)',
32
+ '# Edit directly or use: openthrottle config <key> <value>',
33
+ '',
34
+ ].join('\n');
35
+ writeFileSync(configPath(), header + stringify(doc));
36
+ }
37
+ /** Get a nested value using dot notation: "phases.plan.model" */
38
+ function getDeep(obj, path) {
39
+ const parts = path.split('.');
40
+ let cur = obj;
41
+ for (const part of parts) {
42
+ if (cur == null || typeof cur !== 'object')
43
+ return undefined;
44
+ cur = cur[part];
45
+ }
46
+ return cur;
47
+ }
48
+ /** Set a nested value using dot notation, creating intermediate objects. */
49
+ function setDeep(obj, path, value) {
50
+ const parts = path.split('.');
51
+ let cur = obj;
52
+ for (let i = 0; i < parts.length - 1; i++) {
53
+ if (cur[parts[i]] == null || typeof cur[parts[i]] !== 'object') {
54
+ cur[parts[i]] = {};
55
+ }
56
+ cur = cur[parts[i]];
57
+ }
58
+ cur[parts[parts.length - 1]] = value;
59
+ }
60
+ /** Coerce string values to appropriate types. */
61
+ function coerce(value) {
62
+ if (value === 'true')
63
+ return true;
64
+ if (value === 'false')
65
+ return false;
66
+ const num = Number(value);
67
+ if (!isNaN(num) && value.trim() !== '')
68
+ return num;
69
+ return value;
70
+ }
71
+ function printConfig(doc, prefix = '', indent = 0) {
72
+ const pad = ' '.repeat(indent);
73
+ for (const [key, val] of Object.entries(doc)) {
74
+ const fullKey = prefix ? `${prefix}.${key}` : key;
75
+ if (val != null && typeof val === 'object' && !Array.isArray(val)) {
76
+ console.log(`${pad}${key}:`);
77
+ printConfig(val, fullKey, indent + 1);
78
+ }
79
+ else if (Array.isArray(val)) {
80
+ console.log(`${pad}${key}:`);
81
+ for (const item of val) {
82
+ if (typeof item === 'object') {
83
+ console.log(`${pad} -`);
84
+ printConfig(item, fullKey, indent + 2);
85
+ }
86
+ else {
87
+ console.log(`${pad} - ${item}`);
88
+ }
89
+ }
90
+ }
91
+ else {
92
+ console.log(`${pad}${key}: ${val}`);
93
+ }
94
+ }
95
+ }
96
+ export default function configCmd(args) {
97
+ const doc = loadYaml();
98
+ // No args → show config
99
+ if (args.length === 0) {
100
+ printConfig(doc);
101
+ return;
102
+ }
103
+ // One arg → show specific key
104
+ if (args.length === 1) {
105
+ const val = getDeep(doc, args[0]);
106
+ if (val === undefined) {
107
+ console.error(`Key not found: ${args[0]}`);
108
+ process.exit(1);
109
+ }
110
+ if (typeof val === 'object') {
111
+ printConfig(val);
112
+ }
113
+ else {
114
+ console.log(val);
115
+ }
116
+ return;
117
+ }
118
+ // Two args → set key
119
+ const [key, ...rest] = args;
120
+ const rawValue = rest.join(' ');
121
+ const value = coerce(rawValue);
122
+ setDeep(doc, key, value);
123
+ saveYaml(doc);
124
+ console.log(` ${key}: ${value}`);
125
+ }
package/dist/index.js CHANGED
@@ -127,6 +127,9 @@ function cmdShip(args) {
127
127
  'prd-queued', 'prd-running', 'prd-complete', 'prd-failed',
128
128
  'needs-review', 'reviewing',
129
129
  'bug-queued', 'bug-running', 'bug-complete', 'bug-failed',
130
+ 'bug-confirmed', 'bug-unconfirmed',
131
+ 'triage-queued', 'triage-running', 'triage-complete',
132
+ 'skip-plan',
130
133
  ];
131
134
  for (const label of labels) {
132
135
  try {
@@ -278,6 +281,7 @@ const HELP = `Usage: openthrottle <command>
278
281
  Commands:
279
282
  init Set up Open Throttle in your project
280
283
  ship <file.md> [--base <branch>] Create a GitHub issue to trigger a sandbox
284
+ config [key] [value] View or edit .openthrottle.yml settings
281
285
  status Show running, queued, and completed tasks
282
286
  logs Show recent GitHub Actions workflow runs
283
287
 
@@ -301,6 +305,11 @@ async function main() {
301
305
  await init();
302
306
  return;
303
307
  }
308
+ if (command === 'config') {
309
+ const { default: configCmd } = await import('./config-cmd.js');
310
+ configCmd(args.slice(1));
311
+ return;
312
+ }
304
313
  preflight();
305
314
  switch (command) {
306
315
  case 'ship':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openthrottle",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "CLI for Open Throttle — ship prompts to Daytona sandboxes.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,8 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "build": "tsc",
15
+ "test": "echo 'No tests'",
16
+ "lint": "echo 'No linter configured'",
15
17
  "prepublishOnly": "npm run build"
16
18
  },
17
19
  "dependencies": {
@@ -37,7 +37,7 @@ permissions:
37
37
 
38
38
  jobs:
39
39
  run-task:
40
- if: ${{ contains(fromJSON('["prd-queued","bug-queued","needs-review","needs-investigation"]'), github.event.label.name) || (github.event.review.state == 'changes_requested') }}
40
+ if: ${{ contains(fromJSON('["prd-queued","bug-queued","needs-review","needs-investigation","triage-queued"]'), github.event.label.name) || (github.event.review.state == 'changes_requested') }}
41
41
  runs-on: ubuntu-latest
42
42
  steps:
43
43
  - uses: actions/checkout@v6
@@ -70,6 +70,8 @@ jobs:
70
70
  TASK_TYPE="review"
71
71
  elif [[ "$EVENT_LABEL" == "needs-investigation" ]]; then
72
72
  TASK_TYPE="investigation"
73
+ elif [[ "$EVENT_LABEL" == "triage-queued" ]]; then
74
+ TASK_TYPE="triage"
73
75
  elif [[ "$EVENT_REVIEW_STATE" == "changes_requested" ]]; then
74
76
  TASK_TYPE="review-fix"
75
77
  fi