@oomfware/cgr 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/dist/index.mjs +46 -12
- package/package.json +1 -1
- package/src/commands/ask.ts +16 -3
- package/src/lib/git.ts +56 -9
package/README.md
CHANGED
|
@@ -42,10 +42,16 @@ cgr clean --all
|
|
|
42
42
|
## commands
|
|
43
43
|
|
|
44
44
|
```
|
|
45
|
-
cgr ask [-m opus|sonnet|haiku] [-w repo[#branch] ...] <repo>[#branch] <question>
|
|
45
|
+
cgr ask [-m opus|sonnet|haiku] [-s] [-w repo[#branch] ...] <repo>[#branch] <question>
|
|
46
46
|
cgr clean [--all | <repo>]
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
| option | description |
|
|
50
|
+
| --------------- | ------------------------------------------------- |
|
|
51
|
+
| `-m, --model` | model to use: opus, sonnet, haiku (default haiku) |
|
|
52
|
+
| `-s, --shallow` | use shallow clone (depth 1) for faster cloning |
|
|
53
|
+
| `-w, --with` | additional repository to include (repeatable) |
|
|
54
|
+
|
|
49
55
|
| command | description |
|
|
50
56
|
| ------- | ----------------------------------------------------------------- |
|
|
51
57
|
| `ask` | clone/update a repository and ask Claude Code a question about it |
|
|
@@ -82,6 +88,7 @@ You can use `@oomfware/cgr` to ask questions about external repositories.
|
|
|
82
88
|
|
|
83
89
|
options:
|
|
84
90
|
-m, --model <model> model to use: opus, sonnet, haiku (default: haiku)
|
|
91
|
+
-s, --shallow use shallow clone (depth 1) for faster cloning
|
|
85
92
|
-w, --with <repo> additional repository to include, supports #branch (repeatable)
|
|
86
93
|
|
|
87
94
|
Useful repositories:
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { argument, choice, command, constant, message, object, option, or, string } from "@optique/core";
|
|
2
|
+
import { argument, choice, command, constant, flag, message, object, option, or, string } from "@optique/core";
|
|
3
3
|
import { run } from "@optique/run";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { basename, dirname, join, relative } from "node:path";
|
|
@@ -102,14 +102,24 @@ const gitOutput = (args, cwd) => new Promise((resolve, reject) => {
|
|
|
102
102
|
proc.on("error", reject);
|
|
103
103
|
});
|
|
104
104
|
/**
|
|
105
|
+
* checks if a repository is a shallow clone.
|
|
106
|
+
* @param cachePath the local repository path
|
|
107
|
+
* @returns true if the repository is shallow
|
|
108
|
+
*/
|
|
109
|
+
const isShallowRepo = async (cachePath) => {
|
|
110
|
+
return await gitOutput(["rev-parse", "--is-shallow-repository"], cachePath) === "true";
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
105
113
|
* clones a repository to the cache path.
|
|
106
114
|
* @param remote the remote URL
|
|
107
115
|
* @param cachePath the local cache path
|
|
108
116
|
* @param branch optional branch to checkout
|
|
117
|
+
* @param shallow if true, performs a shallow clone with depth 1
|
|
109
118
|
*/
|
|
110
|
-
const cloneRepo = async (remote, cachePath, branch) => {
|
|
119
|
+
const cloneRepo = async (remote, cachePath, branch, shallow) => {
|
|
111
120
|
await mkdir(dirname(cachePath), { recursive: true });
|
|
112
121
|
const args = ["clone"];
|
|
122
|
+
if (shallow) args.push("--depth", "1");
|
|
113
123
|
if (branch) args.push("--branch", branch);
|
|
114
124
|
args.push(remote, cachePath);
|
|
115
125
|
await git(args);
|
|
@@ -119,9 +129,23 @@ const cloneRepo = async (remote, cachePath, branch) => {
|
|
|
119
129
|
* discards any local modifications, staged changes, and untracked files.
|
|
120
130
|
* @param cachePath the local cache path
|
|
121
131
|
* @param branch optional branch to checkout (uses default branch if not specified)
|
|
132
|
+
* @param shallow if true, keeps shallow clone; if false, unshallows if needed
|
|
122
133
|
*/
|
|
123
|
-
const updateRepo = async (cachePath, branch) => {
|
|
124
|
-
await
|
|
134
|
+
const updateRepo = async (cachePath, branch, shallow) => {
|
|
135
|
+
const currentlyShallow = await isShallowRepo(cachePath);
|
|
136
|
+
if (!shallow && currentlyShallow) await git([
|
|
137
|
+
"fetch",
|
|
138
|
+
"--unshallow",
|
|
139
|
+
"origin"
|
|
140
|
+
], cachePath);
|
|
141
|
+
else if (shallow && !currentlyShallow) console.error(" note: repository is already a full clone, use `cgr clean` to re-clone as shallow");
|
|
142
|
+
if (shallow && currentlyShallow) await git([
|
|
143
|
+
"fetch",
|
|
144
|
+
"--depth",
|
|
145
|
+
"1",
|
|
146
|
+
"origin"
|
|
147
|
+
], cachePath);
|
|
148
|
+
else if (!currentlyShallow || !shallow) await git(["fetch", "origin"], cachePath);
|
|
125
149
|
let targetBranch = branch;
|
|
126
150
|
if (!targetBranch) targetBranch = (await gitOutput(["symbolic-ref", "refs/remotes/origin/HEAD"], cachePath)).replace("refs/remotes/origin/", "");
|
|
127
151
|
await git([
|
|
@@ -143,13 +167,12 @@ const updateRepo = async (cachePath, branch) => {
|
|
|
143
167
|
};
|
|
144
168
|
/**
|
|
145
169
|
* ensures a repository is cloned and up-to-date.
|
|
146
|
-
* @param
|
|
147
|
-
* @param cachePath the local cache path
|
|
148
|
-
* @param branch optional branch to checkout
|
|
170
|
+
* @param options repository options
|
|
149
171
|
*/
|
|
150
|
-
const ensureRepo = async (
|
|
151
|
-
|
|
152
|
-
|
|
172
|
+
const ensureRepo = async (options) => {
|
|
173
|
+
const { remote, cachePath, branch, shallow } = options;
|
|
174
|
+
if (existsSync(cachePath)) await updateRepo(cachePath, branch, shallow);
|
|
175
|
+
else await cloneRepo(remote, cachePath, branch, shallow);
|
|
153
176
|
};
|
|
154
177
|
|
|
155
178
|
//#endregion
|
|
@@ -315,6 +338,7 @@ const schema$1 = object({
|
|
|
315
338
|
"sonnet",
|
|
316
339
|
"haiku"
|
|
317
340
|
]), { description: message`model to use for analysis` }), "haiku"),
|
|
341
|
+
shallow: flag("-s", "--shallow", { description: message`use shallow clone (depth 1) to save time and disk space` }),
|
|
318
342
|
branch: optional(option("-b", "--branch", string(), { description: message`branch to checkout (deprecated: use repo#branch instead)` })),
|
|
319
343
|
with: withDefault(multiple(option("-w", "--with", string(), { description: message`additional repository to include` })), []),
|
|
320
344
|
remote: argument(string({ metavar: "REPO" }), { description: message`git remote URL (HTTP/HTTPS/SSH), optionally with #branch` }),
|
|
@@ -420,7 +444,12 @@ const handler$1 = async (args) => {
|
|
|
420
444
|
const remoteUrl = normalizeRemote(mainRepo.remote);
|
|
421
445
|
console.error(`preparing repository: ${mainRepo.parsed.host}/${mainRepo.parsed.path}`);
|
|
422
446
|
try {
|
|
423
|
-
await ensureRepo(
|
|
447
|
+
await ensureRepo({
|
|
448
|
+
remote: remoteUrl,
|
|
449
|
+
cachePath: mainRepo.cachePath,
|
|
450
|
+
branch: mainRepo.branch,
|
|
451
|
+
shallow: args.shallow
|
|
452
|
+
});
|
|
424
453
|
} catch (err) {
|
|
425
454
|
console.error(`error: failed to prepare repository: ${err}`);
|
|
426
455
|
process.exit(1);
|
|
@@ -435,7 +464,12 @@ const handler$1 = async (args) => {
|
|
|
435
464
|
const remoteUrl = normalizeRemote(repo.remote);
|
|
436
465
|
const display = `${repo.parsed.host}/${repo.parsed.path}`;
|
|
437
466
|
console.error(` preparing: ${display}`);
|
|
438
|
-
await ensureRepo(
|
|
467
|
+
await ensureRepo({
|
|
468
|
+
remote: remoteUrl,
|
|
469
|
+
cachePath: repo.cachePath,
|
|
470
|
+
branch: repo.branch,
|
|
471
|
+
shallow: args.shallow
|
|
472
|
+
});
|
|
439
473
|
return repo;
|
|
440
474
|
}));
|
|
441
475
|
const failures = [];
|
package/package.json
CHANGED
package/src/commands/ask.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
|
|
4
|
-
import { argument, choice, constant, type InferValue, message, object, option, string } from '@optique/core';
|
|
4
|
+
import { argument, choice, constant, flag, type InferValue, message, object, option, string } from '@optique/core';
|
|
5
5
|
import { multiple, optional, withDefault } from '@optique/core/modifiers';
|
|
6
6
|
|
|
7
7
|
import { ensureRepo } from '../lib/git.ts';
|
|
@@ -28,6 +28,9 @@ export const schema = object({
|
|
|
28
28
|
}),
|
|
29
29
|
'haiku',
|
|
30
30
|
),
|
|
31
|
+
shallow: flag('-s', '--shallow', {
|
|
32
|
+
description: message`use shallow clone (depth 1) to save time and disk space`,
|
|
33
|
+
}),
|
|
31
34
|
// TODO: deprecated in favor of #branch syntax, remove in future version
|
|
32
35
|
branch: optional(
|
|
33
36
|
option('-b', '--branch', string(), {
|
|
@@ -168,7 +171,12 @@ export const handler = async (args: Args): Promise<void> => {
|
|
|
168
171
|
const remoteUrl = normalizeRemote(mainRepo.remote);
|
|
169
172
|
console.error(`preparing repository: ${mainRepo.parsed.host}/${mainRepo.parsed.path}`);
|
|
170
173
|
try {
|
|
171
|
-
await ensureRepo(
|
|
174
|
+
await ensureRepo({
|
|
175
|
+
remote: remoteUrl,
|
|
176
|
+
cachePath: mainRepo.cachePath,
|
|
177
|
+
branch: mainRepo.branch,
|
|
178
|
+
shallow: args.shallow,
|
|
179
|
+
});
|
|
172
180
|
} catch (err) {
|
|
173
181
|
console.error(`error: failed to prepare repository: ${err}`);
|
|
174
182
|
process.exit(1);
|
|
@@ -192,7 +200,12 @@ export const handler = async (args: Args): Promise<void> => {
|
|
|
192
200
|
const remoteUrl = normalizeRemote(repo.remote);
|
|
193
201
|
const display = `${repo.parsed.host}/${repo.parsed.path}`;
|
|
194
202
|
console.error(` preparing: ${display}`);
|
|
195
|
-
await ensureRepo(
|
|
203
|
+
await ensureRepo({
|
|
204
|
+
remote: remoteUrl,
|
|
205
|
+
cachePath: repo.cachePath,
|
|
206
|
+
branch: repo.branch,
|
|
207
|
+
shallow: args.shallow,
|
|
208
|
+
});
|
|
196
209
|
return repo;
|
|
197
210
|
}),
|
|
198
211
|
);
|
package/src/lib/git.ts
CHANGED
|
@@ -85,15 +85,34 @@ const gitOutput = (args: string[], cwd?: string): Promise<string> =>
|
|
|
85
85
|
proc.on('error', reject);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* checks if a repository is a shallow clone.
|
|
90
|
+
* @param cachePath the local repository path
|
|
91
|
+
* @returns true if the repository is shallow
|
|
92
|
+
*/
|
|
93
|
+
export const isShallowRepo = async (cachePath: string): Promise<boolean> => {
|
|
94
|
+
const result = await gitOutput(['rev-parse', '--is-shallow-repository'], cachePath);
|
|
95
|
+
return result === 'true';
|
|
96
|
+
};
|
|
97
|
+
|
|
88
98
|
/**
|
|
89
99
|
* clones a repository to the cache path.
|
|
90
100
|
* @param remote the remote URL
|
|
91
101
|
* @param cachePath the local cache path
|
|
92
102
|
* @param branch optional branch to checkout
|
|
103
|
+
* @param shallow if true, performs a shallow clone with depth 1
|
|
93
104
|
*/
|
|
94
|
-
export const cloneRepo = async (
|
|
105
|
+
export const cloneRepo = async (
|
|
106
|
+
remote: string,
|
|
107
|
+
cachePath: string,
|
|
108
|
+
branch?: string,
|
|
109
|
+
shallow?: boolean,
|
|
110
|
+
): Promise<void> => {
|
|
95
111
|
await mkdir(dirname(cachePath), { recursive: true });
|
|
96
112
|
const args = ['clone'];
|
|
113
|
+
if (shallow) {
|
|
114
|
+
args.push('--depth', '1');
|
|
115
|
+
}
|
|
97
116
|
if (branch) {
|
|
98
117
|
args.push('--branch', branch);
|
|
99
118
|
}
|
|
@@ -106,9 +125,27 @@ export const cloneRepo = async (remote: string, cachePath: string, branch?: stri
|
|
|
106
125
|
* discards any local modifications, staged changes, and untracked files.
|
|
107
126
|
* @param cachePath the local cache path
|
|
108
127
|
* @param branch optional branch to checkout (uses default branch if not specified)
|
|
128
|
+
* @param shallow if true, keeps shallow clone; if false, unshallows if needed
|
|
109
129
|
*/
|
|
110
|
-
export const updateRepo = async (cachePath: string, branch?: string): Promise<void> => {
|
|
111
|
-
await
|
|
130
|
+
export const updateRepo = async (cachePath: string, branch?: string, shallow?: boolean): Promise<void> => {
|
|
131
|
+
const currentlyShallow = await isShallowRepo(cachePath);
|
|
132
|
+
|
|
133
|
+
// handle shallow state transitions
|
|
134
|
+
if (!shallow && currentlyShallow) {
|
|
135
|
+
// want full history but have shallow - unshallow first
|
|
136
|
+
await git(['fetch', '--unshallow', 'origin'], cachePath);
|
|
137
|
+
} else if (shallow && !currentlyShallow) {
|
|
138
|
+
// want shallow but have full - just use the full clone as-is
|
|
139
|
+
console.error(' note: repository is already a full clone, use `cgr clean` to re-clone as shallow');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// fetch updates (use depth for shallow repos that stay shallow)
|
|
143
|
+
if (shallow && currentlyShallow) {
|
|
144
|
+
await git(['fetch', '--depth', '1', 'origin'], cachePath);
|
|
145
|
+
} else if (!currentlyShallow || !shallow) {
|
|
146
|
+
// either was unshallowed above, or is/was full
|
|
147
|
+
await git(['fetch', 'origin'], cachePath);
|
|
148
|
+
}
|
|
112
149
|
|
|
113
150
|
// determine the branch to use
|
|
114
151
|
let targetBranch = branch;
|
|
@@ -125,16 +162,26 @@ export const updateRepo = async (cachePath: string, branch?: string): Promise<vo
|
|
|
125
162
|
await git(['reset', '--hard', `origin/${targetBranch}`], cachePath);
|
|
126
163
|
};
|
|
127
164
|
|
|
165
|
+
export interface EnsureRepoOptions {
|
|
166
|
+
/** the remote URL */
|
|
167
|
+
remote: string;
|
|
168
|
+
/** the local cache path */
|
|
169
|
+
cachePath: string;
|
|
170
|
+
/** branch to checkout */
|
|
171
|
+
branch: string | undefined;
|
|
172
|
+
/** if true, uses shallow clone with depth 1 */
|
|
173
|
+
shallow: boolean;
|
|
174
|
+
}
|
|
175
|
+
|
|
128
176
|
/**
|
|
129
177
|
* ensures a repository is cloned and up-to-date.
|
|
130
|
-
* @param
|
|
131
|
-
* @param cachePath the local cache path
|
|
132
|
-
* @param branch optional branch to checkout
|
|
178
|
+
* @param options repository options
|
|
133
179
|
*/
|
|
134
|
-
export const ensureRepo = async (
|
|
180
|
+
export const ensureRepo = async (options: EnsureRepoOptions): Promise<void> => {
|
|
181
|
+
const { remote, cachePath, branch, shallow } = options;
|
|
135
182
|
if (existsSync(cachePath)) {
|
|
136
|
-
await updateRepo(cachePath, branch);
|
|
183
|
+
await updateRepo(cachePath, branch, shallow);
|
|
137
184
|
} else {
|
|
138
|
-
await cloneRepo(remote, cachePath, branch);
|
|
185
|
+
await cloneRepo(remote, cachePath, branch, shallow);
|
|
139
186
|
}
|
|
140
187
|
};
|