@open-code-review/cli 1.0.3 → 1.1.1

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/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 Open Code Review Contributors
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
package/README.md CHANGED
@@ -1,51 +1,70 @@
1
1
  # @open-code-review/cli
2
2
 
3
- CLI for Open Code Review - Multi-environment setup and progress tracking.
3
+ The command-line interface for Open Code Review. Handles multi-tool setup and provides real-time progress tracking.
4
4
 
5
- ## Installation
5
+ ## Why Use the CLI?
6
6
 
7
- ```bash
8
- # Via npx (no install)
9
- npx @open-code-review/cli init
7
+ 1. **Multi-tool configuration**: If you use multiple AI assistants (Claude Code, Cursor, Windsurf), the CLI configures all of them with a single command.
10
8
 
11
- # Via pnpm dlx
12
- pnpm dlx @open-code-review/cli init
9
+ 2. **Progress visibility**: AI-powered reviews take time. The `ocr progress` command shows what's happening in real-time.
13
10
 
11
+ ## Installation
12
+
13
+ ```bash
14
14
  # Global install
15
15
  npm install -g @open-code-review/cli
16
- ocr init
16
+
17
+ # Or via pnpm
18
+ pnpm add -g @open-code-review/cli
19
+
20
+ # Or run directly
21
+ npx @open-code-review/cli init
17
22
  ```
18
23
 
19
24
  ## Commands
20
25
 
21
26
  ### `ocr init`
22
27
 
23
- Set up OCR for your AI coding environments.
28
+ Initialize Open Code Review in your project.
24
29
 
25
30
  ```bash
26
- # Interactive mode - select tools via checkbox
31
+ # Interactive mode
27
32
  ocr init
28
33
 
29
- # Non-interactive - specify tools
34
+ # Non-interactive
30
35
  ocr init --tools claude,windsurf,cursor
31
36
 
32
- # Install for all supported tools
37
+ # Configure all detected tools
33
38
  ocr init --tools all
34
-
35
- # Skip AGENTS.md/CLAUDE.md injection
36
- ocr init --no-inject
37
39
  ```
38
40
 
41
+ **What it does:**
42
+
43
+ 1. Creates `.ocr/` directory with skills, commands, and config
44
+ 2. Detects installed AI tools
45
+ 3. Configures each tool appropriately
46
+ 4. Optionally injects OCR instructions into `AGENTS.md` / `CLAUDE.md`
47
+
39
48
  ### `ocr progress`
40
49
 
41
- Watch real-time progress of an active code review session.
50
+ Watch a review session in real-time.
42
51
 
43
52
  ```bash
44
53
  # Auto-detect current session
45
54
  ocr progress
46
55
 
47
- # Specify session
48
- ocr progress --session 2025-01-26-feature-auth
56
+ # Watch a specific session
57
+ ocr progress --session 2026-01-26-feature-auth
58
+ ```
59
+
60
+ Shows: current phase, elapsed time, reviewer status, finding counts, completion percentage.
61
+
62
+ ### `ocr update`
63
+
64
+ Update OCR skills and commands to the latest version.
65
+
66
+ ```bash
67
+ ocr update
49
68
  ```
50
69
 
51
70
  ## Supported AI Tools
@@ -58,7 +77,20 @@ ocr progress --session 2025-01-26-feature-auth
58
77
  | GitHub Copilot | `.github/` |
59
78
  | Cline | `.cline/` |
60
79
  | Continue | `.continue/` |
61
- | And more... | See `ocr init --help` |
80
+
81
+ ## After Installation
82
+
83
+ Use OCR through your AI assistant:
84
+
85
+ ```
86
+ /ocr-review # Start a code review
87
+ /ocr-review against spec.md # Review against a spec file
88
+ /ocr-doctor # Verify setup
89
+ ```
90
+
91
+ For Claude Code / Cursor, use `/ocr:review`, `/ocr:doctor`, etc.
92
+
93
+ See the [main README](https://github.com/spencermarx/open-code-review) for full documentation.
62
94
 
63
95
  ## License
64
96
 
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command as Command4 } from "commander";
6
6
  // packages/cli/src/commands/init.ts
7
7
  import { Command } from "commander";
8
8
  import { checkbox } from "@inquirer/prompts";
9
- import chalk from "chalk";
9
+ import chalk2 from "chalk";
10
10
  import ora from "ora";
11
11
 
12
12
  // packages/cli/src/lib/config.ts
@@ -386,12 +386,40 @@ function injectIntoProjectFiles(targetDir) {
386
386
  return { agentsMd, claudeMd };
387
387
  }
388
388
 
389
- // packages/cli/src/commands/init.ts
390
- var initCommand = new Command("init").description("Set up OCR for AI coding environments").option("-t, --tools <tools>", 'Comma-separated tool IDs or "all"').option("--no-inject", "Skip injecting instructions into AGENTS.md/CLAUDE.md").action(async (options) => {
389
+ // packages/cli/src/lib/banner.ts
390
+ import chalk from "chalk";
391
+ var LOGO_LINES = [
392
+ " \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E",
393
+ " \u2502 \u25C9 \u2500\u2500\u2500 \u25C9 \u2500\u2500\u2500 \u25C9 \u2502",
394
+ " \u2502 \u2502 \u2572 \u2502 \u2571 \u2502 \u2502",
395
+ " \u2502 \u25C9 \u2500\u2500\u2500 O \u2500\u2500\u2500 \u25C9 \u2502",
396
+ " \u2502 \u2502 \u2571 \u2502 \u2572 \u2502 \u2502",
397
+ " \u2502 \u25C9 \u2500\u2500\u2500 \u25C9 \u2500\u2500\u2500 \u25C9 \u2502",
398
+ " \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"
399
+ ];
400
+ var TITLE_LINES = [
401
+ " \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557",
402
+ " \u2551 O P E N C O D E R E V I E W \u2551",
403
+ " \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
404
+ ];
405
+ function printBanner() {
391
406
  console.log();
392
- console.log(chalk.bold.cyan(" Open Code Review"));
393
- console.log(chalk.dim(" AI-powered multi-agent code review"));
407
+ for (const line of LOGO_LINES) {
408
+ const styled = line.replace(/O/g, chalk.bold.white("O")).replace(/◉/g, chalk.cyan("\u25C9"));
409
+ console.log(chalk.blue(styled));
410
+ }
411
+ console.log();
412
+ for (const line of TITLE_LINES) {
413
+ console.log(chalk.blue(line));
414
+ }
415
+ console.log();
416
+ console.log(chalk.dim(" Multi-agent code review for AI coding assistants"));
394
417
  console.log();
418
+ }
419
+
420
+ // packages/cli/src/commands/init.ts
421
+ var initCommand = new Command("init").description("Set up OCR for AI coding environments").option("-t, --tools <tools>", 'Comma-separated tool IDs or "all"').option("--no-inject", "Skip injecting instructions into AGENTS.md/CLAUDE.md").action(async (options) => {
422
+ printBanner();
395
423
  const targetDir = process.cwd();
396
424
  let selectedTools;
397
425
  if (options.tools) {
@@ -400,13 +428,13 @@ var initCommand = new Command("init").description("Set up OCR for AI coding envi
400
428
  selectedTools = toolIds.map((id) => AI_TOOLS.find((t) => t.id === id)).filter((t) => t !== void 0);
401
429
  } catch (error) {
402
430
  console.error(
403
- chalk.red(
431
+ chalk2.red(
404
432
  `Error: ${error instanceof Error ? error.message : "Invalid tools argument"}`
405
433
  )
406
434
  );
407
435
  console.log();
408
436
  console.log(
409
- chalk.dim(`Valid tool IDs: ${AI_TOOLS.map((t) => t.id).join(", ")}`)
437
+ chalk2.dim(`Valid tool IDs: ${AI_TOOLS.map((t) => t.id).join(", ")}`)
410
438
  );
411
439
  process.exit(1);
412
440
  }
@@ -415,7 +443,7 @@ var initCommand = new Command("init").description("Set up OCR for AI coding envi
415
443
  const choices = AI_TOOLS.map((tool) => {
416
444
  const isInstalled = installedTools.some((t) => t.id === tool.id);
417
445
  return {
418
- name: isInstalled ? `${tool.name} ${chalk.dim("(detected)")}` : tool.name,
446
+ name: isInstalled ? `${tool.name} ${chalk2.dim("(detected)")}` : tool.name,
419
447
  value: tool.id,
420
448
  checked: isInstalled
421
449
  };
@@ -427,12 +455,12 @@ var initCommand = new Command("init").description("Set up OCR for AI coding envi
427
455
  pageSize: 15
428
456
  });
429
457
  if (selectedIds.length === 0) {
430
- console.log(chalk.yellow("No tools selected. Exiting."));
458
+ console.log(chalk2.yellow("No tools selected. Exiting."));
431
459
  process.exit(0);
432
460
  }
433
461
  selectedTools = selectedIds.map((id) => AI_TOOLS.find((t) => t.id === id)).filter((t) => t !== void 0);
434
462
  } catch {
435
- console.log(chalk.yellow("\nOperation cancelled."));
463
+ console.log(chalk2.yellow("\nOperation cancelled."));
436
464
  process.exit(0);
437
465
  }
438
466
  }
@@ -448,17 +476,17 @@ var initCommand = new Command("init").description("Set up OCR for AI coding envi
448
476
  const successful = results.filter((r) => r.success);
449
477
  const failed = results.filter((r) => !r.success);
450
478
  if (successful.length > 0) {
451
- console.log(chalk.green("\u2713 OCR installed successfully"));
479
+ console.log(chalk2.green("\u2713 OCR installed successfully"));
452
480
  console.log();
453
481
  for (const result of successful) {
454
- console.log(` ${chalk.green("\u2713")} ${result.tool.name}`);
482
+ console.log(` ${chalk2.green("\u2713")} ${result.tool.name}`);
455
483
  }
456
484
  }
457
485
  if (failed.length > 0) {
458
486
  console.log();
459
- console.log(chalk.red("\u2717 Some installations failed:"));
487
+ console.log(chalk2.red("\u2717 Some installations failed:"));
460
488
  for (const result of failed) {
461
- console.log(` ${chalk.red("\u2717")} ${result.tool.name}: ${result.error}`);
489
+ console.log(` ${chalk2.red("\u2717")} ${result.tool.name}: ${result.error}`);
462
490
  }
463
491
  }
464
492
  if (options.inject && successful.length > 0) {
@@ -469,36 +497,36 @@ var initCommand = new Command("init").description("Set up OCR for AI coding envi
469
497
  const injectResults = injectIntoProjectFiles(targetDir);
470
498
  injectSpinner.stop();
471
499
  if (injectResults.agentsMd || injectResults.claudeMd) {
472
- console.log(chalk.green("\u2713 OCR instructions injected"));
500
+ console.log(chalk2.green("\u2713 OCR instructions injected"));
473
501
  if (injectResults.agentsMd) {
474
- console.log(` ${chalk.green("\u2713")} AGENTS.md`);
502
+ console.log(` ${chalk2.green("\u2713")} AGENTS.md`);
475
503
  }
476
504
  if (injectResults.claudeMd) {
477
- console.log(` ${chalk.green("\u2713")} CLAUDE.md`);
505
+ console.log(` ${chalk2.green("\u2713")} CLAUDE.md`);
478
506
  }
479
507
  }
480
508
  }
481
509
  console.log();
482
- console.log(chalk.bold("Next steps:"));
510
+ console.log(chalk2.bold("Next steps:"));
483
511
  console.log();
484
512
  console.log(
485
- ` ${chalk.cyan("1.")} Review ${chalk.yellow(".ocr/config.yaml")}`
513
+ ` ${chalk2.cyan("1.")} Review ${chalk2.yellow(".ocr/config.yaml")}`
486
514
  );
487
515
  console.log(
488
- chalk.dim(
516
+ chalk2.dim(
489
517
  " Add project context, review rules, and customize discovery settings."
490
518
  )
491
519
  );
492
520
  console.log();
493
521
  console.log(
494
- ` ${chalk.cyan("2.")} Run ${chalk.yellow("/ocr-review")} to start a code review session.`
522
+ ` ${chalk2.cyan("2.")} Run ${chalk2.yellow("/ocr-review")} to start a code review session.`
495
523
  );
496
524
  console.log();
497
525
  });
498
526
 
499
527
  // packages/cli/src/commands/progress.ts
500
528
  import { Command as Command2 } from "commander";
501
- import chalk3 from "chalk";
529
+ import chalk4 from "chalk";
502
530
  import { watch } from "chokidar";
503
531
  import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync } from "node:fs";
504
532
  import { join as join4, basename } from "node:path";
@@ -507,7 +535,7 @@ import logUpdate from "log-update";
507
535
  // packages/cli/src/lib/guards.ts
508
536
  import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "node:fs";
509
537
  import { join as join3 } from "node:path";
510
- import chalk2 from "chalk";
538
+ import chalk3 from "chalk";
511
539
  function checkOcrSetup(targetDir) {
512
540
  const ocrDir = join3(targetDir, ".ocr");
513
541
  const skillsDir = join3(ocrDir, "skills");
@@ -528,22 +556,22 @@ function requireOcrSetup(targetDir) {
528
556
  const status = checkOcrSetup(targetDir);
529
557
  if (!status.valid) {
530
558
  console.log();
531
- console.log(chalk2.red.bold(" \u2717 OCR is not set up in this directory"));
559
+ console.log(chalk3.red.bold(" \u2717 OCR is not set up in this directory"));
532
560
  console.log();
533
561
  if (!existsSync3(status.ocrDir)) {
534
- console.log(chalk2.dim(" The .ocr directory was not found."));
562
+ console.log(chalk3.dim(" The .ocr directory was not found."));
535
563
  } else if (!status.hasSkills) {
536
- console.log(chalk2.dim(" The .ocr/skills directory is missing."));
537
- console.log(chalk2.dim(" OCR may have been partially installed."));
564
+ console.log(chalk3.dim(" The .ocr/skills directory is missing."));
565
+ console.log(chalk3.dim(" OCR may have been partially installed."));
538
566
  }
539
567
  console.log();
540
- console.log(chalk2.dim(" To set up OCR, run:"));
568
+ console.log(chalk3.dim(" To set up OCR, run:"));
541
569
  console.log();
542
- console.log(chalk2.white(" ocr init"));
570
+ console.log(chalk3.white(" ocr init"));
543
571
  console.log();
544
- console.log(chalk2.dim(" Or with npx:"));
572
+ console.log(chalk3.dim(" Or with npx:"));
545
573
  console.log();
546
- console.log(chalk2.white(" npx @open-code-review/cli init"));
574
+ console.log(chalk3.white(" npx @open-code-review/cli init"));
547
575
  console.log();
548
576
  process.exit(1);
549
577
  }
@@ -559,7 +587,20 @@ function ensureSessionsDir(targetDir) {
559
587
 
560
588
  // packages/cli/src/commands/progress.ts
561
589
  var TOTAL_PHASES = 8;
562
- function findLatestSession(sessionsDir) {
590
+ function isSessionActive(sessionPath) {
591
+ const statePath = join4(sessionPath, "state.json");
592
+ if (!existsSync4(statePath)) {
593
+ return true;
594
+ }
595
+ try {
596
+ const stateContent = readFileSync3(statePath, "utf-8");
597
+ const state = JSON.parse(stateContent);
598
+ return state.status !== "closed";
599
+ } catch {
600
+ return true;
601
+ }
602
+ }
603
+ function findLatestActiveSession(sessionsDir) {
563
604
  if (!existsSync4(sessionsDir)) {
564
605
  return null;
565
606
  }
@@ -567,7 +608,13 @@ function findLatestSession(sessionsDir) {
567
608
  const sessionPath = join4(sessionsDir, name);
568
609
  return statSync(sessionPath).isDirectory();
569
610
  }).sort().reverse();
570
- return sessions[0] ?? null;
611
+ for (const session of sessions) {
612
+ const sessionPath = join4(sessionsDir, session);
613
+ if (isSessionActive(sessionPath)) {
614
+ return session;
615
+ }
616
+ }
617
+ return null;
571
618
  }
572
619
  function countFindings(filePath) {
573
620
  if (!existsSync4(filePath)) {
@@ -638,61 +685,56 @@ function parseFromStateJson(session, state, sessionPath, preservedStartTime) {
638
685
  };
639
686
  }
640
687
  function formatDuration(ms) {
641
- const seconds = Math.floor(ms / 1e3);
642
- const minutes = Math.floor(seconds / 60);
643
- const secs = seconds % 60;
644
- return `${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
688
+ const totalSeconds = Math.floor(ms / 1e3);
689
+ const hours = Math.floor(totalSeconds / 3600);
690
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
691
+ const seconds = totalSeconds % 60;
692
+ if (hours > 0) {
693
+ return `${hours}h ${minutes}m ${seconds}s`;
694
+ } else if (minutes > 0) {
695
+ return `${minutes}m ${seconds}s`;
696
+ }
697
+ return `${seconds}s`;
645
698
  }
646
699
  var PHASE_INFO = [
647
- { key: "context", label: "Context Discovery", icon: "\u{1F4CB}" },
648
- { key: "requirements", label: "Requirements Gathering", icon: "\u{1F4DD}" },
649
- { key: "analysis", label: "Tech Lead Analysis", icon: "\u{1F50D}" },
650
- { key: "reviews", label: "Parallel Reviews", icon: "\u{1F465}" },
651
- { key: "aggregation", label: "Aggregate Findings", icon: "\u{1F4CA}" },
652
- { key: "discourse", label: "Reviewer Discourse", icon: "\u{1F4AC}" },
653
- { key: "synthesis", label: "Final Synthesis", icon: "\u2728" },
654
- { key: "complete", label: "Review Complete", icon: "\u{1F389}" }
700
+ { key: "context", label: "Context Discovery" },
701
+ { key: "requirements", label: "Requirements Gathering" },
702
+ { key: "analysis", label: "Tech Lead Analysis" },
703
+ { key: "reviews", label: "Parallel Reviews" },
704
+ { key: "aggregation", label: "Aggregate Findings" },
705
+ { key: "discourse", label: "Reviewer Discourse" },
706
+ { key: "synthesis", label: "Final Synthesis" },
707
+ { key: "complete", label: "Complete" }
655
708
  ];
656
- function getPhaseIcon(phaseKey, isComplete, isCurrent) {
657
- if (isComplete) return chalk3.green("\u2713");
658
- if (isCurrent) return chalk3.yellow("\u25CF");
659
- return chalk3.dim("\u25CB");
709
+ function getPhaseStatus(isComplete, isCurrent) {
710
+ if (isComplete) return chalk4.green("\u2713");
711
+ if (isCurrent) return chalk4.cyan("\u25B8");
712
+ return chalk4.dim("\xB7");
660
713
  }
661
- function renderProgressBar(current, total) {
662
- const width = 20;
714
+ function renderProgressBar(current, total, label) {
715
+ const width = 24;
663
716
  const filled = Math.round(current / total * width);
664
717
  const empty = width - filled;
665
- const bar = chalk3.green("\u2588".repeat(filled)) + chalk3.dim("\u2591".repeat(empty));
718
+ const bar = chalk4.green("\u2501".repeat(filled)) + chalk4.dim("\u2500".repeat(empty));
666
719
  const percent = Math.round(current / total * 100);
667
- return `${bar} ${percent}%`;
720
+ const percentStr = chalk4.bold.white(`${percent}%`);
721
+ return label ? `${bar} ${percentStr} ${chalk4.dim("\xB7")} ${chalk4.cyan(label)}` : `${bar} ${percentStr}`;
668
722
  }
669
723
  function renderProgress(state) {
670
724
  const lines = [];
671
725
  const log = (line = "") => lines.push(line);
672
- const title = "Open Code Review - Live Progress";
673
- const boxWidth = title.length + 4;
674
- const border = "\u2500".repeat(boxWidth);
675
- log(chalk3.bold.cyan(` \u250C${border}\u2510`));
676
- log(
677
- chalk3.bold.cyan(" \u2502") + chalk3.bold(` ${title} `) + chalk3.bold.cyan("\u2502")
678
- );
679
- log(chalk3.bold.cyan(` \u2514${border}\u2518`));
726
+ log();
727
+ log(chalk4.bold.white(" Open Code Review"));
680
728
  log();
681
729
  const elapsed = Date.now() - state.startTime;
682
- log(chalk3.dim(` Session: `) + chalk3.white(state.session));
683
- log(chalk3.dim(` Elapsed: `) + chalk3.white(formatDuration(elapsed)));
730
+ log(
731
+ chalk4.dim(" ") + chalk4.white(state.session) + chalk4.dim(" \xB7 ") + chalk4.white(formatDuration(elapsed))
732
+ );
684
733
  log();
685
734
  const progressPhases = state.complete ? 8 : state.phaseNumber;
686
- log(` ${renderProgressBar(progressPhases, 8)}`);
687
- log();
688
735
  const currentPhase = PHASE_INFO.find((p) => p.key === state.phase);
689
- if (currentPhase && !state.complete) {
690
- log(
691
- chalk3.bold(` ${currentPhase.icon} `) + chalk3.bold.yellow(currentPhase.label) + chalk3.yellow(" in progress...")
692
- );
693
- log();
694
- }
695
- log(chalk3.dim(" \u2500\u2500\u2500 Workflow Phases \u2500\u2500\u2500"));
736
+ const phaseLabel = state.complete ? "Done" : currentPhase?.label;
737
+ log(` ${renderProgressBar(progressPhases, 8, phaseLabel)}`);
696
738
  log();
697
739
  const phaseCompletion = {
698
740
  waiting: false,
@@ -708,26 +750,24 @@ function renderProgress(state) {
708
750
  for (const phase of PHASE_INFO) {
709
751
  const isComplete = phaseCompletion[phase.key];
710
752
  const isCurrent = state.phase === phase.key && !state.complete;
711
- const icon = getPhaseIcon(phase.key, isComplete, isCurrent);
712
- let label = phase.label;
753
+ const status = getPhaseStatus(isComplete, isCurrent);
754
+ let label;
713
755
  if (isCurrent) {
714
- label = chalk3.yellow(label);
756
+ label = chalk4.cyan.bold(phase.label);
715
757
  } else if (isComplete) {
716
- label = chalk3.white(label);
758
+ label = chalk4.white(phase.label);
717
759
  } else {
718
- label = chalk3.dim(label);
760
+ label = chalk4.dim(phase.label);
719
761
  }
720
- log(` ${icon} ${label}`);
762
+ log(` ${status} ${label}`);
721
763
  if (phase.key === "reviews" && state.reviewers.length > 0) {
722
- for (const reviewer of state.reviewers) {
723
- const reviewerIcon = reviewer.status === "complete" ? chalk3.green("\u2713") : chalk3.dim("\u25CB");
724
- const findings = reviewer.findings > 0 ? chalk3.cyan(
725
- ` \u2192 ${reviewer.findings} finding${reviewer.findings > 1 ? "s" : ""}`
726
- ) : "";
727
- log(
728
- chalk3.dim(` \u2514\u2500 `) + `${reviewerIcon} ${chalk3.dim(reviewer.displayName)}${findings}`
729
- );
730
- }
764
+ const reviewerLine = state.reviewers.map((r) => {
765
+ const icon = r.status === "complete" ? chalk4.green("\u2713") : chalk4.dim("\u25CB");
766
+ const name = chalk4.dim(r.displayName);
767
+ const count = r.findings > 0 ? chalk4.cyan(` ${r.findings}`) : chalk4.dim(" 0");
768
+ return `${icon} ${name}${count}`;
769
+ }).join(chalk4.dim(" \u2502 "));
770
+ log(chalk4.dim(" ") + reviewerLine);
731
771
  }
732
772
  }
733
773
  log();
@@ -736,20 +776,16 @@ function renderProgress(state) {
736
776
  (sum, r) => sum + r.findings,
737
777
  0
738
778
  );
739
- log(chalk3.green.bold(" \u2705 Review Complete!"));
740
- if (totalFindings > 0) {
741
- log(
742
- chalk3.dim(
743
- ` ${totalFindings} total finding${totalFindings > 1 ? "s" : ""} identified`
744
- )
745
- );
746
- }
747
- log();
748
779
  log(
749
- chalk3.dim(" Results saved to: ") + chalk3.white(`.ocr/sessions/${state.session}/final.md`)
780
+ chalk4.green.bold(" \u2713 Complete") + chalk4.dim(" \xB7 ") + chalk4.white(
781
+ `${totalFindings} finding${totalFindings !== 1 ? "s" : ""}`
782
+ )
783
+ );
784
+ log(
785
+ chalk4.dim(" ") + chalk4.dim("\u2192 ") + chalk4.white(`.ocr/sessions/${state.session}/final.md`)
750
786
  );
751
787
  } else {
752
- log(chalk3.dim(" Press Ctrl+C to exit"));
788
+ log(chalk4.dim(" Ctrl+C to exit"));
753
789
  }
754
790
  log();
755
791
  logUpdate(lines.join("\n"));
@@ -757,30 +793,19 @@ function renderProgress(state) {
757
793
  function renderWaiting() {
758
794
  const lines = [];
759
795
  const log = (line = "") => lines.push(line);
760
- const title = "Open Code Review - Live Progress";
761
- const boxWidth = title.length + 4;
762
- const border = "\u2500".repeat(boxWidth);
763
- log(chalk3.bold.cyan(` \u250C${border}\u2510`));
764
- log(
765
- chalk3.bold.cyan(" \u2502") + chalk3.bold(` ${title} `) + chalk3.bold.cyan("\u2502")
766
- );
767
- log(chalk3.bold.cyan(` \u2514${border}\u2518`));
768
- log();
769
- log(chalk3.dim(" Session: ") + chalk3.yellow("Waiting for session..."));
770
796
  log();
771
- const bar = chalk3.dim("\u2591".repeat(20));
772
- log(` ${bar} 0%`);
797
+ log(chalk4.bold.white(" Open Code Review"));
773
798
  log();
774
- log(chalk3.yellow(" \u23F3 Waiting for a code review to begin..."));
799
+ log(chalk4.dim(" Waiting for session..."));
775
800
  log();
776
- log(chalk3.dim(" \u2500\u2500\u2500 How to Start \u2500\u2500\u2500"));
801
+ const bar = chalk4.dim("\u2500".repeat(24));
802
+ log(` ${bar} ${chalk4.dim("0%")}`);
777
803
  log();
778
804
  log(
779
- chalk3.dim(" Run ") + chalk3.white("/ocr-review") + chalk3.dim(" in your AI assistant to begin")
805
+ chalk4.dim(" Run ") + chalk4.white("/ocr-review") + chalk4.dim(" to start")
780
806
  );
781
- log(chalk3.dim(" This display will update automatically"));
782
807
  log();
783
- log(chalk3.dim(" Press Ctrl+C to exit"));
808
+ log(chalk4.dim(" Ctrl+C to exit"));
784
809
  log();
785
810
  logUpdate(lines.join("\n"));
786
811
  }
@@ -792,18 +817,18 @@ var progressCommand = new Command2("progress").description("Watch real-time prog
792
817
  if (options.session) {
793
818
  const sessionPath = join4(sessionsDir, options.session);
794
819
  if (!existsSync4(sessionPath)) {
795
- console.log(chalk3.red(`Session not found: ${options.session}`));
820
+ console.log(chalk4.red(`Session not found: ${options.session}`));
796
821
  process.exit(1);
797
822
  }
798
823
  let state = parseSessionState(sessionPath);
799
824
  if (!state) {
800
825
  console.log(
801
- chalk3.red(
826
+ chalk4.red(
802
827
  `Session ${options.session} has no state.json - cannot track progress`
803
828
  )
804
829
  );
805
830
  console.log(
806
- chalk3.dim(
831
+ chalk4.dim(
807
832
  `The orchestrating agent must create state.json for progress tracking.`
808
833
  )
809
834
  );
@@ -838,7 +863,7 @@ var progressCommand = new Command2("progress").description("Watch real-time prog
838
863
  });
839
864
  return;
840
865
  }
841
- let currentSession = findLatestSession(sessionsDir);
866
+ let currentSession = findLatestActiveSession(sessionsDir);
842
867
  let currentSessionPath = currentSession ? join4(sessionsDir, currentSession) : null;
843
868
  let sessionWatcher = null;
844
869
  let preservedStartTime;
@@ -905,7 +930,7 @@ var progressCommand = new Command2("progress").description("Watch real-time prog
905
930
 
906
931
  // packages/cli/src/commands/update.ts
907
932
  import { Command as Command3 } from "commander";
908
- import chalk4 from "chalk";
933
+ import chalk5 from "chalk";
909
934
  import ora2 from "ora";
910
935
  import { existsSync as existsSync5 } from "node:fs";
911
936
  import { join as join5 } from "node:path";
@@ -927,7 +952,7 @@ var updateCommand = new Command3("update").description("Update OCR assets after
927
952
  const targetDir = process.cwd();
928
953
  requireOcrSetup(targetDir);
929
954
  console.log();
930
- console.log(chalk4.bold.cyan(" Open Code Review - Update"));
955
+ console.log(chalk5.bold.cyan(" Open Code Review - Update"));
931
956
  console.log();
932
957
  const configuredTools = detectConfiguredTools(targetDir);
933
958
  const installedTools = detectInstalledTools(targetDir, AI_TOOLS);
@@ -935,8 +960,8 @@ var updateCommand = new Command3("update").description("Update OCR assets after
935
960
  (tool) => configuredTools.some((t) => t.id === tool.id) || installedTools.some((t) => t.id === tool.id)
936
961
  );
937
962
  if (toolsToUpdate.length === 0) {
938
- console.log(chalk4.yellow(" No configured AI tools found."));
939
- console.log(chalk4.dim(" Run `ocr init` to set up OCR first."));
963
+ console.log(chalk5.yellow(" No configured AI tools found."));
964
+ console.log(chalk5.dim(" Run `ocr init` to set up OCR first."));
940
965
  console.log();
941
966
  process.exit(1);
942
967
  }
@@ -945,31 +970,31 @@ var updateCommand = new Command3("update").description("Update OCR assets after
945
970
  const updateSkills = options.skills || !hasSpecificFlag;
946
971
  const updateInject = options.inject || !hasSpecificFlag;
947
972
  if (options.dryRun) {
948
- console.log(chalk4.yellow(" Dry run mode - no files will be modified"));
973
+ console.log(chalk5.yellow(" Dry run mode - no files will be modified"));
949
974
  console.log();
950
975
  }
951
- console.log(chalk4.dim(" Detected tools:"));
976
+ console.log(chalk5.dim(" Detected tools:"));
952
977
  for (const tool of toolsToUpdate) {
953
978
  console.log(` \u2022 ${tool.name}`);
954
979
  }
955
980
  console.log();
956
981
  if (updateCommands || updateSkills) {
957
982
  if (options.dryRun) {
958
- console.log(chalk4.dim(" Would update:"));
959
- console.log(chalk4.dim(" \u2022 .ocr/skills/SKILL.md (main skill)"));
983
+ console.log(chalk5.dim(" Would update:"));
984
+ console.log(chalk5.dim(" \u2022 .ocr/skills/SKILL.md (main skill)"));
960
985
  console.log(
961
- chalk4.dim(" \u2022 .ocr/skills/references/ (workflow, reviewers)")
986
+ chalk5.dim(" \u2022 .ocr/skills/references/ (workflow, reviewers)")
962
987
  );
963
- console.log(chalk4.dim(" \u2022 .ocr/skills/assets/reviewer-template.md"));
988
+ console.log(chalk5.dim(" \u2022 .ocr/skills/assets/reviewer-template.md"));
964
989
  console.log(
965
- chalk4.dim(" \u2022 .ocr/config.yaml (preserved if customized)")
990
+ chalk5.dim(" \u2022 .ocr/config.yaml (preserved if customized)")
966
991
  );
967
992
  for (const tool of toolsToUpdate) {
968
993
  if (tool.commandStrategy === "subdirectory") {
969
- console.log(chalk4.dim(` \u2022 ${tool.commandsDir}/ocr/ (commands)`));
994
+ console.log(chalk5.dim(` \u2022 ${tool.commandsDir}/ocr/ (commands)`));
970
995
  } else {
971
996
  console.log(
972
- chalk4.dim(` \u2022 ${tool.commandsDir}/ocr-*.md (commands)`)
997
+ chalk5.dim(` \u2022 ${tool.commandsDir}/ocr-*.md (commands)`)
973
998
  );
974
999
  }
975
1000
  }
@@ -986,20 +1011,20 @@ var updateCommand = new Command3("update").description("Update OCR assets after
986
1011
  const successful = results.filter((r) => r.success);
987
1012
  const failed = results.filter((r) => !r.success);
988
1013
  if (successful.length > 0) {
989
- console.log(chalk4.green(" \u2713 Commands and skills updated"));
1014
+ console.log(chalk5.green(" \u2713 Commands and skills updated"));
990
1015
  console.log(
991
- chalk4.dim(" Including: SKILL.md, references/, assets/")
1016
+ chalk5.dim(" Including: SKILL.md, references/, assets/")
992
1017
  );
993
1018
  for (const result of successful) {
994
- console.log(` ${chalk4.green("\u2713")} ${result.tool.name}`);
1019
+ console.log(` ${chalk5.green("\u2713")} ${result.tool.name}`);
995
1020
  }
996
1021
  }
997
1022
  if (failed.length > 0) {
998
1023
  console.log();
999
- console.log(chalk4.red(" \u2717 Some updates failed:"));
1024
+ console.log(chalk5.red(" \u2717 Some updates failed:"));
1000
1025
  for (const result of failed) {
1001
1026
  console.log(
1002
- ` ${chalk4.red("\u2717")} ${result.tool.name}: ${result.error}`
1027
+ ` ${chalk5.red("\u2717")} ${result.tool.name}: ${result.error}`
1003
1028
  );
1004
1029
  }
1005
1030
  }
@@ -1008,12 +1033,12 @@ var updateCommand = new Command3("update").description("Update OCR assets after
1008
1033
  }
1009
1034
  if (updateInject) {
1010
1035
  if (options.dryRun) {
1011
- console.log(chalk4.dim(" Would update:"));
1036
+ console.log(chalk5.dim(" Would update:"));
1012
1037
  if (existsSync5(join5(targetDir, "AGENTS.md"))) {
1013
- console.log(chalk4.dim(" \u2022 AGENTS.md (OCR managed block)"));
1038
+ console.log(chalk5.dim(" \u2022 AGENTS.md (OCR managed block)"));
1014
1039
  }
1015
1040
  if (existsSync5(join5(targetDir, "CLAUDE.md"))) {
1016
- console.log(chalk4.dim(" \u2022 CLAUDE.md (OCR managed block)"));
1041
+ console.log(chalk5.dim(" \u2022 CLAUDE.md (OCR managed block)"));
1017
1042
  }
1018
1043
  console.log();
1019
1044
  } else {
@@ -1021,23 +1046,23 @@ var updateCommand = new Command3("update").description("Update OCR assets after
1021
1046
  const injectResults = injectIntoProjectFiles(targetDir);
1022
1047
  spinner.stop();
1023
1048
  if (injectResults.agentsMd || injectResults.claudeMd) {
1024
- console.log(chalk4.green(" \u2713 Instructions updated"));
1049
+ console.log(chalk5.green(" \u2713 Instructions updated"));
1025
1050
  if (injectResults.agentsMd) {
1026
- console.log(` ${chalk4.green("\u2713")} AGENTS.md`);
1051
+ console.log(` ${chalk5.green("\u2713")} AGENTS.md`);
1027
1052
  }
1028
1053
  if (injectResults.claudeMd) {
1029
- console.log(` ${chalk4.green("\u2713")} CLAUDE.md`);
1054
+ console.log(` ${chalk5.green("\u2713")} CLAUDE.md`);
1030
1055
  }
1031
1056
  } else {
1032
- console.log(chalk4.dim(" No instruction files to update"));
1057
+ console.log(chalk5.dim(" No instruction files to update"));
1033
1058
  }
1034
1059
  console.log();
1035
1060
  }
1036
1061
  }
1037
1062
  if (options.dryRun) {
1038
- console.log(chalk4.dim(" Run without --dry-run to apply changes."));
1063
+ console.log(chalk5.dim(" Run without --dry-run to apply changes."));
1039
1064
  } else {
1040
- console.log(chalk4.green(" \u2713 Update complete"));
1065
+ console.log(chalk5.green(" \u2713 Update complete"));
1041
1066
  }
1042
1067
  console.log();
1043
1068
  });
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-code-review/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "description": "CLI for Open Code Review - Multi-environment setup and progress tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  "url": "https://github.com/spencermarx/open-code-review",
23
23
  "directory": "packages/cli"
24
24
  },
25
- "license": "MIT",
25
+ "license": "Apache-2.0",
26
26
  "author": "Spencer Marx",
27
27
  "engines": {
28
28
  "node": ">=20.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-code-review/cli",
3
- "version": "1.0.3",
3
+ "version": "1.1.1",
4
4
  "description": "CLI for Open Code Review - Multi-environment setup and progress tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  "url": "https://github.com/spencermarx/open-code-review",
23
23
  "directory": "packages/cli"
24
24
  },
25
- "license": "MIT",
25
+ "license": "Apache-2.0",
26
26
  "author": "Spencer Marx",
27
27
  "engines": {
28
28
  "node": ">=20.0.0"
@@ -34,7 +34,7 @@
34
34
  "commander": "^13.0.0",
35
35
  "log-update": "^7.0.2",
36
36
  "ora": "^8.1.1",
37
- "@open-code-review/agents": "1.0.3"
37
+ "@open-code-review/agents": "1.1.1"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"