@open-code-review/cli 1.0.3 → 1.1.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/LICENSE +201 -0
- package/README.md +59 -28
- package/dist/index.js +171 -146
- package/dist/package.json +2 -2
- package/package.json +3 -3
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,64 +1,95 @@
|
|
|
1
1
|
# @open-code-review/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The command-line interface for Open Code Review. Handles multi-tool setup and provides real-time progress tracking for review sessions.
|
|
4
|
+
|
|
5
|
+
## Why Use the CLI?
|
|
6
|
+
|
|
7
|
+
The CLI solves two problems:
|
|
8
|
+
|
|
9
|
+
1. **Multi-tool configuration**: If you use multiple AI assistants (Claude Code, Cursor, Windsurf), the CLI configures all of them with a single command—no manual copying of files between tool-specific directories.
|
|
10
|
+
|
|
11
|
+
2. **Progress visibility**: AI-powered reviews take time. The `ocr progress` command shows what's happening in real-time, so you're not staring at a blank screen wondering if anything is working.
|
|
4
12
|
|
|
5
13
|
## Installation
|
|
6
14
|
|
|
7
15
|
```bash
|
|
8
|
-
#
|
|
9
|
-
|
|
16
|
+
# Global install (recommended)
|
|
17
|
+
npm install -g @open-code-review/cli
|
|
10
18
|
|
|
11
|
-
#
|
|
12
|
-
pnpm
|
|
19
|
+
# Or via pnpm
|
|
20
|
+
pnpm add -g @open-code-review/cli
|
|
13
21
|
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
ocr init
|
|
22
|
+
# Or run directly without installing
|
|
23
|
+
npx @open-code-review/cli init
|
|
17
24
|
```
|
|
18
25
|
|
|
19
26
|
## Commands
|
|
20
27
|
|
|
21
28
|
### `ocr init`
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
Initialize Open Code Review in your project.
|
|
24
31
|
|
|
25
32
|
```bash
|
|
26
|
-
# Interactive mode
|
|
33
|
+
# Interactive mode — select which AI tools to configure
|
|
27
34
|
ocr init
|
|
28
35
|
|
|
29
|
-
# Non-interactive
|
|
36
|
+
# Non-interactive — specify tools directly
|
|
30
37
|
ocr init --tools claude,windsurf,cursor
|
|
31
38
|
|
|
32
|
-
#
|
|
39
|
+
# Configure all detected tools
|
|
33
40
|
ocr init --tools all
|
|
34
|
-
|
|
35
|
-
# Skip AGENTS.md/CLAUDE.md injection
|
|
36
|
-
ocr init --no-inject
|
|
37
41
|
```
|
|
38
42
|
|
|
43
|
+
**What it does:**
|
|
44
|
+
|
|
45
|
+
1. Creates `.ocr/` directory with skills, commands, and default config
|
|
46
|
+
2. Detects installed AI tools (Claude Code, Cursor, Windsurf, etc.)
|
|
47
|
+
3. Configures each tool with appropriate symlinks or copies
|
|
48
|
+
4. Optionally injects OCR instructions into `AGENTS.md` / `CLAUDE.md`
|
|
49
|
+
|
|
39
50
|
### `ocr progress`
|
|
40
51
|
|
|
41
|
-
Watch
|
|
52
|
+
Watch a review session in real-time.
|
|
42
53
|
|
|
43
54
|
```bash
|
|
44
|
-
# Auto-detect current session
|
|
55
|
+
# Auto-detect the current session
|
|
45
56
|
ocr progress
|
|
46
57
|
|
|
47
|
-
#
|
|
48
|
-
ocr progress --session
|
|
58
|
+
# Watch a specific session
|
|
59
|
+
ocr progress --session 2026-01-26-feature-auth
|
|
49
60
|
```
|
|
50
61
|
|
|
62
|
+
The progress display shows:
|
|
63
|
+
|
|
64
|
+
- Current phase and elapsed time
|
|
65
|
+
- Individual reviewer status
|
|
66
|
+
- Finding counts as they're discovered
|
|
67
|
+
- Overall completion percentage
|
|
68
|
+
|
|
51
69
|
## Supported AI Tools
|
|
52
70
|
|
|
53
|
-
| Tool | Config Directory |
|
|
54
|
-
|
|
55
|
-
| Claude Code | `.claude/` |
|
|
56
|
-
| Windsurf | `.windsurf/` |
|
|
57
|
-
| Cursor | `.cursor/` |
|
|
58
|
-
| GitHub Copilot | `.github/` |
|
|
59
|
-
| Cline | `.cline/` |
|
|
60
|
-
| Continue | `.continue/` |
|
|
61
|
-
|
|
71
|
+
| Tool | Config Directory | Detection |
|
|
72
|
+
|------|------------------|-----------|
|
|
73
|
+
| Claude Code | `.claude/` | Auto-detected |
|
|
74
|
+
| Windsurf | `.windsurf/` | Auto-detected |
|
|
75
|
+
| Cursor | `.cursor/` | Auto-detected |
|
|
76
|
+
| GitHub Copilot | `.github/` | Auto-detected |
|
|
77
|
+
| Cline | `.cline/` | Auto-detected |
|
|
78
|
+
| Continue | `.continue/` | Auto-detected |
|
|
79
|
+
|
|
80
|
+
The CLI detects which tools you have configured and offers to set up OCR for each.
|
|
81
|
+
|
|
82
|
+
## After Installation
|
|
83
|
+
|
|
84
|
+
Once initialized, use OCR through your AI assistant:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
/ocr-review # Start a code review
|
|
88
|
+
/ocr-doctor # Verify setup
|
|
89
|
+
/ocr-history # View past sessions
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
See the [main README](https://github.com/spencermarx/open-code-review) for full usage documentation.
|
|
62
93
|
|
|
63
94
|
## License
|
|
64
95
|
|
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
|
|
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/
|
|
390
|
-
|
|
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
|
-
|
|
393
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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} ${
|
|
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(
|
|
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(
|
|
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(
|
|
479
|
+
console.log(chalk2.green("\u2713 OCR installed successfully"));
|
|
452
480
|
console.log();
|
|
453
481
|
for (const result of successful) {
|
|
454
|
-
console.log(` ${
|
|
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(
|
|
487
|
+
console.log(chalk2.red("\u2717 Some installations failed:"));
|
|
460
488
|
for (const result of failed) {
|
|
461
|
-
console.log(` ${
|
|
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(
|
|
500
|
+
console.log(chalk2.green("\u2713 OCR instructions injected"));
|
|
473
501
|
if (injectResults.agentsMd) {
|
|
474
|
-
console.log(` ${
|
|
502
|
+
console.log(` ${chalk2.green("\u2713")} AGENTS.md`);
|
|
475
503
|
}
|
|
476
504
|
if (injectResults.claudeMd) {
|
|
477
|
-
console.log(` ${
|
|
505
|
+
console.log(` ${chalk2.green("\u2713")} CLAUDE.md`);
|
|
478
506
|
}
|
|
479
507
|
}
|
|
480
508
|
}
|
|
481
509
|
console.log();
|
|
482
|
-
console.log(
|
|
510
|
+
console.log(chalk2.bold("Next steps:"));
|
|
483
511
|
console.log();
|
|
484
512
|
console.log(
|
|
485
|
-
` ${
|
|
513
|
+
` ${chalk2.cyan("1.")} Review ${chalk2.yellow(".ocr/config.yaml")}`
|
|
486
514
|
);
|
|
487
515
|
console.log(
|
|
488
|
-
|
|
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
|
-
` ${
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
562
|
+
console.log(chalk3.dim(" The .ocr directory was not found."));
|
|
535
563
|
} else if (!status.hasSkills) {
|
|
536
|
-
console.log(
|
|
537
|
-
console.log(
|
|
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(
|
|
568
|
+
console.log(chalk3.dim(" To set up OCR, run:"));
|
|
541
569
|
console.log();
|
|
542
|
-
console.log(
|
|
570
|
+
console.log(chalk3.white(" ocr init"));
|
|
543
571
|
console.log();
|
|
544
|
-
console.log(
|
|
572
|
+
console.log(chalk3.dim(" Or with npx:"));
|
|
545
573
|
console.log();
|
|
546
|
-
console.log(
|
|
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
|
|
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
|
-
|
|
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
|
|
642
|
-
const
|
|
643
|
-
const
|
|
644
|
-
|
|
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"
|
|
648
|
-
{ key: "requirements", label: "Requirements Gathering"
|
|
649
|
-
{ key: "analysis", label: "Tech Lead Analysis"
|
|
650
|
-
{ key: "reviews", label: "Parallel Reviews"
|
|
651
|
-
{ key: "aggregation", label: "Aggregate Findings"
|
|
652
|
-
{ key: "discourse", label: "Reviewer Discourse"
|
|
653
|
-
{ key: "synthesis", label: "Final Synthesis"
|
|
654
|
-
{ key: "complete", label: "
|
|
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
|
|
657
|
-
if (isComplete) return
|
|
658
|
-
if (isCurrent) return
|
|
659
|
-
return
|
|
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 =
|
|
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 =
|
|
718
|
+
const bar = chalk4.green("\u2501".repeat(filled)) + chalk4.dim("\u2500".repeat(empty));
|
|
666
719
|
const percent = Math.round(current / total * 100);
|
|
667
|
-
|
|
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
|
-
|
|
673
|
-
|
|
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(
|
|
683
|
-
|
|
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
|
-
|
|
690
|
-
|
|
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
|
|
712
|
-
let label
|
|
753
|
+
const status = getPhaseStatus(isComplete, isCurrent);
|
|
754
|
+
let label;
|
|
713
755
|
if (isCurrent) {
|
|
714
|
-
label =
|
|
756
|
+
label = chalk4.cyan.bold(phase.label);
|
|
715
757
|
} else if (isComplete) {
|
|
716
|
-
label =
|
|
758
|
+
label = chalk4.white(phase.label);
|
|
717
759
|
} else {
|
|
718
|
-
label =
|
|
760
|
+
label = chalk4.dim(phase.label);
|
|
719
761
|
}
|
|
720
|
-
log(` ${
|
|
762
|
+
log(` ${status} ${label}`);
|
|
721
763
|
if (phase.key === "reviews" && state.reviewers.length > 0) {
|
|
722
|
-
|
|
723
|
-
const
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
772
|
-
log(` ${bar} 0%`);
|
|
797
|
+
log(chalk4.bold.white(" Open Code Review"));
|
|
773
798
|
log();
|
|
774
|
-
log(
|
|
799
|
+
log(chalk4.dim(" Waiting for session..."));
|
|
775
800
|
log();
|
|
776
|
-
|
|
801
|
+
const bar = chalk4.dim("\u2500".repeat(24));
|
|
802
|
+
log(` ${bar} ${chalk4.dim("0%")}`);
|
|
777
803
|
log();
|
|
778
804
|
log(
|
|
779
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
826
|
+
chalk4.red(
|
|
802
827
|
`Session ${options.session} has no state.json - cannot track progress`
|
|
803
828
|
)
|
|
804
829
|
);
|
|
805
830
|
console.log(
|
|
806
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
939
|
-
console.log(
|
|
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(
|
|
973
|
+
console.log(chalk5.yellow(" Dry run mode - no files will be modified"));
|
|
949
974
|
console.log();
|
|
950
975
|
}
|
|
951
|
-
console.log(
|
|
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(
|
|
959
|
-
console.log(
|
|
983
|
+
console.log(chalk5.dim(" Would update:"));
|
|
984
|
+
console.log(chalk5.dim(" \u2022 .ocr/skills/SKILL.md (main skill)"));
|
|
960
985
|
console.log(
|
|
961
|
-
|
|
986
|
+
chalk5.dim(" \u2022 .ocr/skills/references/ (workflow, reviewers)")
|
|
962
987
|
);
|
|
963
|
-
console.log(
|
|
988
|
+
console.log(chalk5.dim(" \u2022 .ocr/skills/assets/reviewer-template.md"));
|
|
964
989
|
console.log(
|
|
965
|
-
|
|
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(
|
|
994
|
+
console.log(chalk5.dim(` \u2022 ${tool.commandsDir}/ocr/ (commands)`));
|
|
970
995
|
} else {
|
|
971
996
|
console.log(
|
|
972
|
-
|
|
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(
|
|
1014
|
+
console.log(chalk5.green(" \u2713 Commands and skills updated"));
|
|
990
1015
|
console.log(
|
|
991
|
-
|
|
1016
|
+
chalk5.dim(" Including: SKILL.md, references/, assets/")
|
|
992
1017
|
);
|
|
993
1018
|
for (const result of successful) {
|
|
994
|
-
console.log(` ${
|
|
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(
|
|
1024
|
+
console.log(chalk5.red(" \u2717 Some updates failed:"));
|
|
1000
1025
|
for (const result of failed) {
|
|
1001
1026
|
console.log(
|
|
1002
|
-
` ${
|
|
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(
|
|
1036
|
+
console.log(chalk5.dim(" Would update:"));
|
|
1012
1037
|
if (existsSync5(join5(targetDir, "AGENTS.md"))) {
|
|
1013
|
-
console.log(
|
|
1038
|
+
console.log(chalk5.dim(" \u2022 AGENTS.md (OCR managed block)"));
|
|
1014
1039
|
}
|
|
1015
1040
|
if (existsSync5(join5(targetDir, "CLAUDE.md"))) {
|
|
1016
|
-
console.log(
|
|
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(
|
|
1049
|
+
console.log(chalk5.green(" \u2713 Instructions updated"));
|
|
1025
1050
|
if (injectResults.agentsMd) {
|
|
1026
|
-
console.log(` ${
|
|
1051
|
+
console.log(` ${chalk5.green("\u2713")} AGENTS.md`);
|
|
1027
1052
|
}
|
|
1028
1053
|
if (injectResults.claudeMd) {
|
|
1029
|
-
console.log(` ${
|
|
1054
|
+
console.log(` ${chalk5.green("\u2713")} CLAUDE.md`);
|
|
1030
1055
|
}
|
|
1031
1056
|
} else {
|
|
1032
|
-
console.log(
|
|
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(
|
|
1063
|
+
console.log(chalk5.dim(" Run without --dry-run to apply changes."));
|
|
1039
1064
|
} else {
|
|
1040
|
-
console.log(
|
|
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.
|
|
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": "
|
|
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
|
+
"version": "1.1.0",
|
|
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": "
|
|
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
|
|
37
|
+
"@open-code-review/agents": "1.1.0"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|