offworld 0.3.4 → 0.3.6
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 -8
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +2 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-BLm6ei_p.mjs → src-BN5uTbLS.mjs} +223 -86
- package/dist/src-BN5uTbLS.mjs.map +1 -0
- package/package.json +3 -3
- package/dist/src-BLm6ei_p.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -97,7 +97,6 @@ ow list
|
|
|
97
97
|
|
|
98
98
|
```
|
|
99
99
|
--reference, -r Reference filename override
|
|
100
|
-
--shallow Use shallow clone (--depth 1)
|
|
101
100
|
--sparse Sparse checkout (src/, lib/, packages/, docs/)
|
|
102
101
|
--branch <name> Branch to clone
|
|
103
102
|
--force, -f Force regeneration
|
|
@@ -128,7 +127,7 @@ ow list
|
|
|
128
127
|
--all Select all deps
|
|
129
128
|
--deps Comma-separated deps
|
|
130
129
|
--skip Deps to exclude
|
|
131
|
-
--generate, -g
|
|
130
|
+
--generate, -g Force local generation for new deps
|
|
132
131
|
--dry-run, -d Preview only
|
|
133
132
|
--yes, -y Skip confirmations
|
|
134
133
|
```
|
|
@@ -150,12 +149,13 @@ ow list
|
|
|
150
149
|
|
|
151
150
|
## Config Keys
|
|
152
151
|
|
|
153
|
-
| Key
|
|
154
|
-
|
|
|
155
|
-
| `repoRoot`
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
152
|
+
| Key | Type | Description |
|
|
153
|
+
| ----------------------- | ------- | --------------------------------------------------------------------- |
|
|
154
|
+
| `repoRoot` | string | Where to clone repos (default: `~/ow`) |
|
|
155
|
+
| `defaultModel` | string | AI model (e.g., `anthropic/claude-sonnet-4-20250514`) |
|
|
156
|
+
| `maxCommitDistance` | number | Max commit distance to accept remote references (default: `20`) |
|
|
157
|
+
| `acceptUnknownDistance` | boolean | Accept remote refs when commit distance is unknown (default: `false`) |
|
|
158
|
+
| `agents` | list | Comma-separated agent names |
|
|
159
159
|
|
|
160
160
|
## Path Discovery
|
|
161
161
|
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as version, t as createOwCli } from "./src-
|
|
2
|
+
import { r as version, t as createOwCli } from "./src-BN5uTbLS.mjs";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
package/dist/index.d.mts
CHANGED
|
@@ -4,12 +4,11 @@ import { z } from "zod";
|
|
|
4
4
|
import * as trpc_cli_dist_json_js0 from "trpc-cli/dist/json.js";
|
|
5
5
|
|
|
6
6
|
//#region src/index.d.ts
|
|
7
|
-
declare const version = "0.3.
|
|
7
|
+
declare const version = "0.3.6";
|
|
8
8
|
declare const router: {
|
|
9
9
|
pull: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>, z.ZodObject<{
|
|
10
10
|
repo: z.ZodString;
|
|
11
11
|
reference: z.ZodOptional<z.ZodString>;
|
|
12
|
-
shallow: z.ZodDefault<z.ZodBoolean>;
|
|
13
12
|
sparse: z.ZodDefault<z.ZodBoolean>;
|
|
14
13
|
branch: z.ZodOptional<z.ZodString>;
|
|
15
14
|
force: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -71,6 +70,7 @@ declare const router: {
|
|
|
71
70
|
generate: z.ZodDefault<z.ZodBoolean>;
|
|
72
71
|
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
73
72
|
yes: z.ZodDefault<z.ZodBoolean>;
|
|
73
|
+
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
74
74
|
}, z.core.$strip>, _orpc_server0.Schema<void, void>, _orpc_server0.MergedErrorMap<Record<never, never>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>>, Record<never, never>>;
|
|
75
75
|
};
|
|
76
76
|
map: {
|
|
@@ -96,7 +96,6 @@ declare const router: {
|
|
|
96
96
|
all: z.ZodDefault<z.ZodBoolean>;
|
|
97
97
|
pattern: z.ZodOptional<z.ZodString>;
|
|
98
98
|
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
99
|
-
unshallow: z.ZodDefault<z.ZodBoolean>;
|
|
100
99
|
}, z.core.$strip>, _orpc_server0.Schema<void, void>, _orpc_server0.MergedErrorMap<Record<never, never>, _orpc_server0.MergedErrorMap<Record<never, never>, Record<never, never>>>, Record<never, never>>;
|
|
101
100
|
prune: _orpc_server0.Procedure<_orpc_server0.MergedInitialContext<Record<never, never>, _orpc_server0.MergedInitialContext<Record<never, never>, Record<never, never>, Record<never, never>>, Record<never, never>>, Record<never, never>, z.ZodObject<{
|
|
102
101
|
dryRun: z.ZodDefault<z.ZodBoolean>;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;cA+Ba,OAAA;AAAA,cAEA,MAAA
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;cA+Ba,OAAA;AAAA,cAEA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwgBG,WAAA,CAAA;0BAAW,SAAA,CAAA,gBAAA;;+BAnPuC,oBAAA,KAAA,sBAAA,CAAA,WAAA;AAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
7
|
import { NotLoggedInError, Paths, RepoExistsError, TokenExpiredError, cleanShellConfig, clearAuthData, cloneRepo, detectInstallMethod, detectInstalledAgents, discoverRepos, executeUninstall, executeUpgrade, fetchLatestVersion, gcRepos, getAllAgentConfigs, getAuthPath, getAuthStatus, getClonedRepoPath, getCommitDistance, getCommitSha, getConfigPath, getCurrentVersion, getMapEntry, getMetaPath, getMetaRoot, getProvider, getReferencePath, getRepoRoot, getRepoStatus, getShellConfigFiles, getToken, installGlobalSkill, installReference, isRepoCloned, listProviders, listRepos, loadAuthData, loadConfig, matchDependenciesToReferencesWithRemoteCheck, parseDependencies, parseRepoInput, pruneRepos, readGlobalMap, removeRepo, resolveDependencyRepo, resolveReferenceKeywords, saveAuthData, saveConfig, searchMap, toReferenceFileName, toReferenceName, updateAgentFiles, updateAllRepos, updateRepo, validateProviderModel, writeGlobalMap, writeProjectMap } from "@offworld/sdk/internal";
|
|
8
8
|
import { AuthenticationError, CommitExistsError, CommitNotFoundError, GitHubError, InvalidInputError, InvalidReferenceError, LowStarsError, PrivateRepoError, RateLimitError, RepoNotFoundError, checkRemote, checkRemoteByName, pullReference, pullReferenceByName, pushReference } from "@offworld/sdk/sync";
|
|
9
|
-
import { generateReferenceWithAI } from "@offworld/sdk/ai";
|
|
9
|
+
import { createOpenCodeContext, generateReferenceWithAI } from "@offworld/sdk/ai";
|
|
10
10
|
import { ReferenceMetaSchema, WorkOSAuthErrorResponseSchema, WorkOSDeviceAuthResponseSchema, WorkOSTokenResponseSchema } from "@offworld/types";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { AgentSchema, ConfigSchema } from "@offworld/types/schemas";
|
|
@@ -146,7 +146,7 @@ function parseModelFlag$1(model) {
|
|
|
146
146
|
return { model };
|
|
147
147
|
}
|
|
148
148
|
async function pullHandler(options) {
|
|
149
|
-
const { repo,
|
|
149
|
+
const { repo, sparse = false, branch, force = false, verbose = false, allowGenerate = true, skipUpdate = false, quiet = false, skipConfirm = false, onProgress } = options;
|
|
150
150
|
const referenceName = options.reference?.trim() || void 0;
|
|
151
151
|
const { provider, model } = parseModelFlag$1(options.model);
|
|
152
152
|
const config = loadConfig();
|
|
@@ -158,7 +158,7 @@ async function pullHandler(options) {
|
|
|
158
158
|
} : createSpinner({ silent: quiet });
|
|
159
159
|
const log = quiet ? () => {} : (msg) => p.log.info(msg);
|
|
160
160
|
const logSuccess = quiet ? () => {} : (msg) => p.log.success(msg);
|
|
161
|
-
if (verbose && !quiet) p.log.info(`[verbose] Options: repo=${repo}, reference=${referenceName ?? "default"},
|
|
161
|
+
if (verbose && !quiet) p.log.info(`[verbose] Options: repo=${repo}, reference=${referenceName ?? "default"}, branch=${branch || "default"}, force=${force}`);
|
|
162
162
|
try {
|
|
163
163
|
s.start("Parsing repository input...");
|
|
164
164
|
const source = parseRepoInput(repo);
|
|
@@ -167,17 +167,20 @@ async function pullHandler(options) {
|
|
|
167
167
|
let repoPath;
|
|
168
168
|
if (source.type === "remote") {
|
|
169
169
|
const qualifiedName = source.qualifiedName;
|
|
170
|
-
if (isRepoCloned(qualifiedName)) {
|
|
170
|
+
if (isRepoCloned(qualifiedName)) if (skipUpdate) {
|
|
171
|
+
repoPath = getClonedRepoPath(qualifiedName);
|
|
172
|
+
s.stop("Using existing clone");
|
|
173
|
+
} else {
|
|
171
174
|
s.start("Updating repository...");
|
|
172
175
|
const result = await updateRepo(qualifiedName);
|
|
173
176
|
repoPath = getClonedRepoPath(qualifiedName);
|
|
174
177
|
if (result.updated) s.stop(`Updated (${result.previousSha.slice(0, 7)} → ${result.currentSha.slice(0, 7)})`);
|
|
175
178
|
else s.stop("Already up to date");
|
|
176
|
-
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
177
181
|
s.start(`Cloning ${source.fullName}...`);
|
|
178
182
|
try {
|
|
179
183
|
repoPath = await cloneRepo(source, {
|
|
180
|
-
shallow,
|
|
181
184
|
sparse,
|
|
182
185
|
branch,
|
|
183
186
|
config,
|
|
@@ -218,19 +221,23 @@ async function pullHandler(options) {
|
|
|
218
221
|
const remoteSha = remoteCheck.commitSha;
|
|
219
222
|
const remoteShaNorm = remoteSha.slice(0, 7);
|
|
220
223
|
const currentShaNorm = currentSha.slice(0, 7);
|
|
221
|
-
const
|
|
224
|
+
const maxCommitDistance = config.maxCommitDistance ?? 20;
|
|
225
|
+
const acceptUnknownDistance = config.acceptUnknownDistance ?? false;
|
|
222
226
|
const commitDistance = getCommitDistance(repoPath, remoteSha, currentSha);
|
|
223
|
-
|
|
224
|
-
|
|
227
|
+
const isExactMatch = remoteShaNorm === currentShaNorm || commitDistance === 0;
|
|
228
|
+
const isWithinDistance = commitDistance !== null && commitDistance <= maxCommitDistance;
|
|
229
|
+
const hasUnknownDistance = commitDistance === null;
|
|
230
|
+
if (isReferenceOverride || isExactMatch || isWithinDistance || acceptUnknownDistance && hasUnknownDistance) {
|
|
231
|
+
if (!isReferenceOverride) if (isExactMatch) {
|
|
225
232
|
verboseLog(`Remote SHA matches (${remoteShaNorm})`, verbose);
|
|
226
233
|
s.stop("Remote reference found (exact match)");
|
|
227
|
-
} else if (
|
|
228
|
-
verboseLog(`Remote reference is ${commitDistance} commits behind (within ${
|
|
234
|
+
} else if (isWithinDistance) {
|
|
235
|
+
verboseLog(`Remote reference is ${commitDistance} commits behind (within ${maxCommitDistance} threshold)`, verbose);
|
|
229
236
|
s.stop(`Remote reference found (${commitDistance} commits behind)`);
|
|
230
|
-
} else {
|
|
231
|
-
verboseLog("Remote reference found (commit distance unknown)", verbose);
|
|
237
|
+
} else if (hasUnknownDistance) {
|
|
238
|
+
verboseLog(acceptUnknownDistance ? "Remote reference found (distance unknown, accepted)" : "Remote reference found (commit distance unknown)", verbose);
|
|
232
239
|
s.stop("Remote reference found");
|
|
233
|
-
}
|
|
240
|
+
} else s.stop("Remote reference found");
|
|
234
241
|
else s.stop("Remote reference found");
|
|
235
242
|
log(`Preview: ${referenceName ? `https://offworld.sh/${source.fullName}/${encodeURIComponent(referenceName)}` : `https://offworld.sh/${source.fullName}`}`);
|
|
236
243
|
let useRemote = true;
|
|
@@ -266,7 +273,7 @@ async function pullHandler(options) {
|
|
|
266
273
|
}
|
|
267
274
|
} else {
|
|
268
275
|
const distanceInfo = commitDistance !== null ? ` (${commitDistance} commits behind)` : "";
|
|
269
|
-
verboseLog(`Remote reference too outdated${distanceInfo}, threshold is ${
|
|
276
|
+
verboseLog(`Remote reference too outdated${distanceInfo}, threshold is ${maxCommitDistance}`, verbose);
|
|
270
277
|
s.stop(`Remote reference outdated${distanceInfo}`);
|
|
271
278
|
}
|
|
272
279
|
} else s.stop("No remote reference found");
|
|
@@ -277,17 +284,30 @@ async function pullHandler(options) {
|
|
|
277
284
|
}
|
|
278
285
|
}
|
|
279
286
|
if (isReferenceOverride) throw new Error(`Reference not found on offworld.sh: ${referenceName}`);
|
|
287
|
+
if (!allowGenerate && source.type === "remote") {
|
|
288
|
+
const message = "Remote reference unavailable, outdated, or declined; local generation is disabled.";
|
|
289
|
+
s.stop(message);
|
|
290
|
+
return {
|
|
291
|
+
success: false,
|
|
292
|
+
repoPath,
|
|
293
|
+
referenceSource: "local",
|
|
294
|
+
referenceInstalled: false,
|
|
295
|
+
message
|
|
296
|
+
};
|
|
297
|
+
}
|
|
280
298
|
verboseLog(`Starting AI reference generation for: ${repoPath}`, verbose);
|
|
281
|
-
if (!verbose) s.start("Generating reference with
|
|
299
|
+
if (!verbose) s.start("Generating reference with OpenCode...");
|
|
282
300
|
try {
|
|
301
|
+
const onDebug = verbose ? (message) => {
|
|
302
|
+
p.log.info(`[${timestamp()}] [debug] ${message}`);
|
|
303
|
+
} : (msg) => {
|
|
304
|
+
if (!msg.startsWith("[")) s.message(msg);
|
|
305
|
+
};
|
|
283
306
|
const { referenceContent, commitSha: referenceCommitSha } = await generateReferenceWithAI(repoPath, qualifiedName, {
|
|
284
307
|
provider,
|
|
285
308
|
model,
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
} : (msg) => {
|
|
289
|
-
if (!msg.startsWith("[")) s.message(msg);
|
|
290
|
-
}
|
|
309
|
+
openCodeContext: options.openCodeContext,
|
|
310
|
+
onDebug
|
|
291
311
|
});
|
|
292
312
|
const meta = {
|
|
293
313
|
referenceUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -370,14 +390,11 @@ async function generateHandler(options) {
|
|
|
370
390
|
p.log.info(`Using existing clone at ${repoPath}`);
|
|
371
391
|
} else {
|
|
372
392
|
s.start(`Cloning ${source.fullName}...`);
|
|
373
|
-
repoPath = await cloneRepo(source, {
|
|
374
|
-
shallow: config.defaultShallow,
|
|
375
|
-
config
|
|
376
|
-
});
|
|
393
|
+
repoPath = await cloneRepo(source, { config });
|
|
377
394
|
s.stop("Repository cloned");
|
|
378
395
|
}
|
|
379
396
|
} else repoPath = source.path;
|
|
380
|
-
s.start("Generating reference with
|
|
397
|
+
s.start("Generating reference with OpenCode...");
|
|
381
398
|
const qualifiedName = source.qualifiedName;
|
|
382
399
|
const referenceRepoName = source.type === "remote" ? source.fullName : source.name;
|
|
383
400
|
const result = await generateReferenceWithAI(repoPath, referenceRepoName, {
|
|
@@ -466,13 +483,12 @@ async function repoListHandler(options) {
|
|
|
466
483
|
return { repos: items };
|
|
467
484
|
}
|
|
468
485
|
async function repoUpdateHandler(options) {
|
|
469
|
-
const { all = false, pattern, dryRun = false
|
|
486
|
+
const { all = false, pattern, dryRun = false } = options;
|
|
470
487
|
if (!all && !pattern) {
|
|
471
488
|
p.log.error("Specify --all or a pattern to update.");
|
|
472
489
|
return {
|
|
473
490
|
updated: [],
|
|
474
491
|
skipped: [],
|
|
475
|
-
unshallowed: [],
|
|
476
492
|
errors: []
|
|
477
493
|
};
|
|
478
494
|
}
|
|
@@ -483,19 +499,17 @@ async function repoUpdateHandler(options) {
|
|
|
483
499
|
return {
|
|
484
500
|
updated: [],
|
|
485
501
|
skipped: [],
|
|
486
|
-
unshallowed: [],
|
|
487
502
|
errors: []
|
|
488
503
|
};
|
|
489
504
|
}
|
|
490
505
|
let processed = 0;
|
|
491
506
|
const outcomes = [];
|
|
492
507
|
const spinner = p.spinner();
|
|
493
|
-
const action =
|
|
508
|
+
const action = "Updating";
|
|
494
509
|
spinner.start(dryRun ? `Checking ${total} repos...` : `${action} ${total} repos...`);
|
|
495
510
|
const result = await updateAllRepos({
|
|
496
511
|
pattern,
|
|
497
512
|
dryRun,
|
|
498
|
-
unshallow,
|
|
499
513
|
onProgress: (repo, status, message) => {
|
|
500
514
|
if (status === "updating") {
|
|
501
515
|
processed++;
|
|
@@ -509,12 +523,10 @@ async function repoUpdateHandler(options) {
|
|
|
509
523
|
});
|
|
510
524
|
spinner.stop(dryRun ? "Dry run complete" : `${action} complete`);
|
|
511
525
|
for (const { repo, status, message } of outcomes) if (status === "updated") p.log.success(`${repo}${message ? ` (${message})` : ""}`);
|
|
512
|
-
else if (status === "unshallowed") p.log.success(`${repo}: ${message}`);
|
|
513
526
|
else if (status === "error") p.log.error(`${repo}: ${message}`);
|
|
514
527
|
else if (status === "skipped" && message !== "up to date" && message !== "already up to date") p.log.warn(`${repo}: ${message}`);
|
|
515
528
|
const parts = [];
|
|
516
529
|
if (result.updated.length > 0) parts.push(`${result.updated.length} ${dryRun ? "would update" : "updated"}`);
|
|
517
|
-
if (result.unshallowed.length > 0) parts.push(`${result.unshallowed.length} unshallowed`);
|
|
518
530
|
if (result.skipped.length > 0) parts.push(`${result.skipped.length} skipped`);
|
|
519
531
|
if (result.errors.length > 0) parts.push(`${result.errors.length} failed`);
|
|
520
532
|
if (parts.length > 0) p.log.info(`Summary: ${parts.join(", ")}`);
|
|
@@ -1155,8 +1167,9 @@ async function handleReferenceOnlyRemoval(repoName, yes, dryRun) {
|
|
|
1155
1167
|
*/
|
|
1156
1168
|
const VALID_KEYS = [
|
|
1157
1169
|
"repoRoot",
|
|
1158
|
-
"defaultShallow",
|
|
1159
1170
|
"defaultModel",
|
|
1171
|
+
"maxCommitDistance",
|
|
1172
|
+
"acceptUnknownDistance",
|
|
1160
1173
|
"agents"
|
|
1161
1174
|
];
|
|
1162
1175
|
function isValidKey(key) {
|
|
@@ -1201,16 +1214,7 @@ async function configSetHandler(options) {
|
|
|
1201
1214
|
};
|
|
1202
1215
|
}
|
|
1203
1216
|
let parsedValue;
|
|
1204
|
-
if (key === "
|
|
1205
|
-
else if (value === "false" || value === "0") parsedValue = false;
|
|
1206
|
-
else {
|
|
1207
|
-
p.log.error(`Invalid boolean value: ${value}. Use 'true' or 'false'.`);
|
|
1208
|
-
return {
|
|
1209
|
-
success: false,
|
|
1210
|
-
message: `Invalid boolean value: ${value}`
|
|
1211
|
-
};
|
|
1212
|
-
}
|
|
1213
|
-
else if (key === "agents") {
|
|
1217
|
+
if (key === "agents") {
|
|
1214
1218
|
const agentValues = value.split(",").map((a) => a.trim()).filter(Boolean);
|
|
1215
1219
|
const agentsResult = z.array(AgentSchema).safeParse(agentValues);
|
|
1216
1220
|
if (!agentsResult.success) {
|
|
@@ -1223,6 +1227,26 @@ async function configSetHandler(options) {
|
|
|
1223
1227
|
};
|
|
1224
1228
|
}
|
|
1225
1229
|
parsedValue = agentsResult.data;
|
|
1230
|
+
} else if (key === "maxCommitDistance") {
|
|
1231
|
+
const parsed = Number.parseInt(value, 10);
|
|
1232
|
+
if (Number.isNaN(parsed) || parsed < 0) {
|
|
1233
|
+
p.log.error("maxCommitDistance must be a non-negative integer.");
|
|
1234
|
+
return {
|
|
1235
|
+
success: false,
|
|
1236
|
+
message: "Invalid maxCommitDistance value"
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
parsedValue = parsed;
|
|
1240
|
+
} else if (key === "acceptUnknownDistance") {
|
|
1241
|
+
const normalized = value.trim().toLowerCase();
|
|
1242
|
+
if (normalized !== "true" && normalized !== "false") {
|
|
1243
|
+
p.log.error("acceptUnknownDistance must be 'true' or 'false'.");
|
|
1244
|
+
return {
|
|
1245
|
+
success: false,
|
|
1246
|
+
message: "Invalid acceptUnknownDistance value"
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
parsedValue = normalized === "true";
|
|
1226
1250
|
} else parsedValue = value;
|
|
1227
1251
|
try {
|
|
1228
1252
|
saveConfig({ [key]: parsedValue });
|
|
@@ -1852,6 +1876,20 @@ function showBanner() {
|
|
|
1852
1876
|
for (const line of LOGO_LINES) console.log(`${OLIVE}${line}${RESET}`);
|
|
1853
1877
|
console.log();
|
|
1854
1878
|
}
|
|
1879
|
+
async function runWithConcurrency(tasks, concurrency) {
|
|
1880
|
+
if (concurrency < 1) throw new Error("Concurrency must be at least 1.");
|
|
1881
|
+
const results = [];
|
|
1882
|
+
let index = 0;
|
|
1883
|
+
async function worker() {
|
|
1884
|
+
while (index < tasks.length) {
|
|
1885
|
+
const i = index++;
|
|
1886
|
+
results[i] = await tasks[i]();
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
const workers = Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker());
|
|
1890
|
+
await Promise.all(workers);
|
|
1891
|
+
return results;
|
|
1892
|
+
}
|
|
1855
1893
|
async function projectInitHandler(options = {}) {
|
|
1856
1894
|
showBanner();
|
|
1857
1895
|
p.intro("Scan your deps to install reference files and create a clone map for your agents.");
|
|
@@ -1995,28 +2033,23 @@ async function projectInitHandler(options = {}) {
|
|
|
1995
2033
|
message: "Dry run complete"
|
|
1996
2034
|
};
|
|
1997
2035
|
}
|
|
2036
|
+
const requestedConcurrency = options.concurrency ?? 4;
|
|
2037
|
+
const concurrency = Number.isFinite(requestedConcurrency) && requestedConcurrency > 0 ? Math.max(1, Math.trunc(requestedConcurrency)) : 4;
|
|
2038
|
+
const installedGroup = selected.filter((m) => m.status === "installed");
|
|
2039
|
+
const remoteGroup = selected.filter((m) => m.status === "remote");
|
|
2040
|
+
const generateGroup = selected.filter((m) => m.status === "generate");
|
|
2041
|
+
const total = selected.length;
|
|
1998
2042
|
const installed = [];
|
|
1999
2043
|
const successfulMatches = [];
|
|
2000
2044
|
let failedCount = 0;
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
try {
|
|
2010
|
-
const pullResult = await pullHandler({
|
|
2011
|
-
repo,
|
|
2012
|
-
shallow: true,
|
|
2013
|
-
force: options.generate,
|
|
2014
|
-
verbose: false,
|
|
2015
|
-
quiet: true,
|
|
2016
|
-
skipConfirm: true,
|
|
2017
|
-
onProgress: (msg) => spinner.message(`${prefix}: ${msg}`)
|
|
2018
|
-
});
|
|
2019
|
-
if (pullResult.success && pullResult.referenceInstalled) {
|
|
2045
|
+
let counter = 0;
|
|
2046
|
+
if (installedGroup.length > 0) {
|
|
2047
|
+
p.log.step(`Adding ${pc.blue(installedGroup.length)} already-installed references...`);
|
|
2048
|
+
await runWithConcurrency(installedGroup.map((match) => async () => {
|
|
2049
|
+
const i = ++counter;
|
|
2050
|
+
const repo = match.repo;
|
|
2051
|
+
const prefix = `${`[${i}/${total}]`} ${match.dep}`;
|
|
2052
|
+
try {
|
|
2020
2053
|
const referencePath = getReferencePath(repo);
|
|
2021
2054
|
successfulMatches.push(match);
|
|
2022
2055
|
installed.push({
|
|
@@ -2024,16 +2057,121 @@ async function projectInitHandler(options = {}) {
|
|
|
2024
2057
|
reference: toReferenceFileName(repo),
|
|
2025
2058
|
path: referencePath
|
|
2026
2059
|
});
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2060
|
+
p.log.info(`${prefix} ${pc.green("✓")} ${pc.dim("(already installed)")}`);
|
|
2061
|
+
return {
|
|
2062
|
+
match,
|
|
2063
|
+
success: true,
|
|
2064
|
+
source: "installed"
|
|
2065
|
+
};
|
|
2066
|
+
} catch (error) {
|
|
2067
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error";
|
|
2068
|
+
p.log.info(`${prefix} ${pc.red("✗")} ${pc.dim(errMsg)}`);
|
|
2069
|
+
failedCount++;
|
|
2070
|
+
return {
|
|
2071
|
+
match,
|
|
2072
|
+
success: false
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
}), concurrency);
|
|
2076
|
+
}
|
|
2077
|
+
if (remoteGroup.length > 0) {
|
|
2078
|
+
p.log.step(`Downloading ${pc.green(remoteGroup.length)} remote references...`);
|
|
2079
|
+
await runWithConcurrency(remoteGroup.map((match) => async () => {
|
|
2080
|
+
const i = ++counter;
|
|
2081
|
+
const repo = match.repo;
|
|
2082
|
+
const prefix = `${`[${i}/${total}]`} ${match.dep}`;
|
|
2083
|
+
const spinner = p.spinner();
|
|
2084
|
+
spinner.start(`${prefix}: Downloading...`);
|
|
2085
|
+
try {
|
|
2086
|
+
const pullResult = await pullHandler({
|
|
2087
|
+
repo,
|
|
2088
|
+
force: false,
|
|
2089
|
+
verbose: false,
|
|
2090
|
+
allowGenerate: false,
|
|
2091
|
+
quiet: true,
|
|
2092
|
+
skipConfirm: true,
|
|
2093
|
+
skipUpdate: true,
|
|
2094
|
+
onProgress: (msg) => spinner.message(`${prefix}: ${msg}`)
|
|
2095
|
+
});
|
|
2096
|
+
if (pullResult.success && pullResult.referenceInstalled) {
|
|
2097
|
+
const referencePath = getReferencePath(repo);
|
|
2098
|
+
successfulMatches.push(match);
|
|
2099
|
+
installed.push({
|
|
2100
|
+
dependency: match.dep,
|
|
2101
|
+
reference: toReferenceFileName(repo),
|
|
2102
|
+
path: referencePath
|
|
2103
|
+
});
|
|
2104
|
+
const source = pullResult.referenceSource === "remote" ? "downloaded" : pullResult.referenceSource;
|
|
2105
|
+
spinner.stop(`${prefix} ${pc.green("✓")} ${pc.dim(`(${source})`)}`);
|
|
2106
|
+
return {
|
|
2107
|
+
match,
|
|
2108
|
+
success: true,
|
|
2109
|
+
source: "remote"
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2030
2112
|
spinner.stop(`${prefix} ${pc.red("✗")} ${pc.red("failed")}`);
|
|
2031
2113
|
failedCount++;
|
|
2114
|
+
return {
|
|
2115
|
+
match,
|
|
2116
|
+
success: false
|
|
2117
|
+
};
|
|
2118
|
+
} catch (error) {
|
|
2119
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error";
|
|
2120
|
+
spinner.stop(`${prefix} ${pc.red("✗")} ${pc.dim(errMsg)}`);
|
|
2121
|
+
failedCount++;
|
|
2122
|
+
return {
|
|
2123
|
+
match,
|
|
2124
|
+
success: false
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
}), concurrency);
|
|
2128
|
+
}
|
|
2129
|
+
if (generateGroup.length > 0) {
|
|
2130
|
+
p.log.step(`Generating ${pc.yellow(generateGroup.length)} references with OpenCode...`);
|
|
2131
|
+
let openCodeContext;
|
|
2132
|
+
try {
|
|
2133
|
+
if (generateGroup.length > 1) {
|
|
2134
|
+
p.log.info(pc.dim("Starting OpenCode server for batch generation..."));
|
|
2135
|
+
openCodeContext = await createOpenCodeContext({ onDebug: () => {} });
|
|
2032
2136
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2137
|
+
for (const match of generateGroup) {
|
|
2138
|
+
const i = ++counter;
|
|
2139
|
+
const repo = match.repo;
|
|
2140
|
+
const prefix = `${`[${i}/${total}]`} ${match.dep}`;
|
|
2141
|
+
const spinner = p.spinner();
|
|
2142
|
+
spinner.start(`${prefix}: Starting...`);
|
|
2143
|
+
try {
|
|
2144
|
+
const pullResult = await pullHandler({
|
|
2145
|
+
repo,
|
|
2146
|
+
force: options.generate,
|
|
2147
|
+
verbose: false,
|
|
2148
|
+
quiet: true,
|
|
2149
|
+
skipConfirm: true,
|
|
2150
|
+
openCodeContext,
|
|
2151
|
+
onProgress: (msg) => spinner.message(`${prefix}: ${msg}`)
|
|
2152
|
+
});
|
|
2153
|
+
if (pullResult.success && pullResult.referenceInstalled) {
|
|
2154
|
+
const referencePath = getReferencePath(repo);
|
|
2155
|
+
successfulMatches.push(match);
|
|
2156
|
+
installed.push({
|
|
2157
|
+
dependency: match.dep,
|
|
2158
|
+
reference: toReferenceFileName(repo),
|
|
2159
|
+
path: referencePath
|
|
2160
|
+
});
|
|
2161
|
+
const source = pullResult.referenceSource === "remote" ? "downloaded" : "generated";
|
|
2162
|
+
spinner.stop(`${prefix} ${pc.green("✓")} ${pc.dim(`(${source})`)}`);
|
|
2163
|
+
} else {
|
|
2164
|
+
spinner.stop(`${prefix} ${pc.red("✗")} ${pc.red("failed")}`);
|
|
2165
|
+
failedCount++;
|
|
2166
|
+
}
|
|
2167
|
+
} catch (error) {
|
|
2168
|
+
const errMsg = error instanceof Error ? error.message : "Unknown error";
|
|
2169
|
+
spinner.stop(`${prefix} ${pc.red("✗")} ${pc.dim(errMsg)}`);
|
|
2170
|
+
failedCount++;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
} finally {
|
|
2174
|
+
openCodeContext?.close();
|
|
2037
2175
|
}
|
|
2038
2176
|
}
|
|
2039
2177
|
if (successfulMatches.length > 0) {
|
|
@@ -2334,12 +2472,11 @@ async function mapSearchHandler(options) {
|
|
|
2334
2472
|
|
|
2335
2473
|
//#endregion
|
|
2336
2474
|
//#region src/index.ts
|
|
2337
|
-
const version = "0.3.
|
|
2475
|
+
const version = "0.3.6";
|
|
2338
2476
|
const router = os.router({
|
|
2339
2477
|
pull: os.input(z.object({
|
|
2340
2478
|
repo: z.string().describe("repo").meta({ positional: true }),
|
|
2341
2479
|
reference: z.string().optional().describe("Reference name to pull (defaults to owner-repo)").meta({ alias: "r" }),
|
|
2342
|
-
shallow: z.boolean().default(false).describe("Use shallow clone (--depth 1)").meta({ negativeAlias: "full-history" }),
|
|
2343
2480
|
sparse: z.boolean().default(false).describe("Use sparse checkout (only src/, lib/, packages/, docs/)"),
|
|
2344
2481
|
branch: z.string().optional().describe("Branch to clone"),
|
|
2345
2482
|
force: z.boolean().default(false).describe("Force re-generation").meta({ alias: "f" }),
|
|
@@ -2352,7 +2489,6 @@ const router = os.router({
|
|
|
2352
2489
|
await pullHandler({
|
|
2353
2490
|
repo: input.repo,
|
|
2354
2491
|
reference: input.reference,
|
|
2355
|
-
shallow: input.shallow,
|
|
2356
2492
|
sparse: input.sparse,
|
|
2357
2493
|
branch: input.branch,
|
|
2358
2494
|
force: input.force,
|
|
@@ -2433,10 +2569,11 @@ const router = os.router({
|
|
|
2433
2569
|
})).meta({ description: `Set a config value
|
|
2434
2570
|
|
|
2435
2571
|
Valid keys:
|
|
2436
|
-
repoRoot
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2572
|
+
repoRoot (string) Where to clone repos (e.g., ~/ow)
|
|
2573
|
+
defaultModel (string) AI provider/model (e.g., anthropic/claude-sonnet-4-20250514)
|
|
2574
|
+
maxCommitDistance (number) Max commit distance to accept remote references (default: 20)
|
|
2575
|
+
acceptUnknownDistance (boolean) Accept remote refs when distance is unknown (default: false)
|
|
2576
|
+
agents (list) Comma-separated agents (e.g., claude-code,opencode)` }).handler(async ({ input }) => {
|
|
2440
2577
|
await configSetHandler({
|
|
2441
2578
|
key: input.key,
|
|
2442
2579
|
value: input.value
|
|
@@ -2444,7 +2581,7 @@ Valid keys:
|
|
|
2444
2581
|
}),
|
|
2445
2582
|
get: os.input(z.object({ key: z.string().describe("key").meta({ positional: true }) })).meta({ description: `Get a config value
|
|
2446
2583
|
|
|
2447
|
-
Valid keys: repoRoot,
|
|
2584
|
+
Valid keys: repoRoot, defaultModel, maxCommitDistance, acceptUnknownDistance, agents` }).handler(async ({ input }) => {
|
|
2448
2585
|
await configGetHandler({ key: input.key });
|
|
2449
2586
|
}),
|
|
2450
2587
|
reset: os.input(z.object({})).meta({ description: "Reset config to defaults" }).handler(async () => {
|
|
@@ -2476,9 +2613,10 @@ Valid keys: repoRoot, defaultShallow, defaultModel, agents` }).handler(async ({
|
|
|
2476
2613
|
all: z.boolean().default(false).describe("Select all detected dependencies"),
|
|
2477
2614
|
deps: z.string().optional().describe("Comma-separated deps to include (skip selection)"),
|
|
2478
2615
|
skip: z.string().optional().describe("Comma-separated deps to exclude"),
|
|
2479
|
-
generate: z.boolean().default(false).describe("
|
|
2616
|
+
generate: z.boolean().default(false).describe("Force local generation for deps without existing refs").meta({ alias: "g" }),
|
|
2480
2617
|
dryRun: z.boolean().default(false).describe("Show what would be done without doing it").meta({ alias: "d" }),
|
|
2481
|
-
yes: z.boolean().default(false).describe("Skip confirmations").meta({ alias: "y" })
|
|
2618
|
+
yes: z.boolean().default(false).describe("Skip confirmations").meta({ alias: "y" }),
|
|
2619
|
+
concurrency: z.number().int().min(1).default(4).describe("Max parallel installs for remote/installed refs (min: 1)").meta({ alias: "c" })
|
|
2482
2620
|
})).meta({
|
|
2483
2621
|
description: "Scan manifest, install references, update AGENTS.md",
|
|
2484
2622
|
default: true
|
|
@@ -2489,7 +2627,8 @@ Valid keys: repoRoot, defaultShallow, defaultModel, agents` }).handler(async ({
|
|
|
2489
2627
|
skip: input.skip,
|
|
2490
2628
|
generate: input.generate,
|
|
2491
2629
|
dryRun: input.dryRun,
|
|
2492
|
-
yes: input.yes
|
|
2630
|
+
yes: input.yes,
|
|
2631
|
+
concurrency: input.concurrency
|
|
2493
2632
|
});
|
|
2494
2633
|
}) }),
|
|
2495
2634
|
map: os.router({
|
|
@@ -2540,14 +2679,12 @@ Valid keys: repoRoot, defaultShallow, defaultModel, agents` }).handler(async ({
|
|
|
2540
2679
|
update: os.input(z.object({
|
|
2541
2680
|
all: z.boolean().default(false).describe("Update all repos"),
|
|
2542
2681
|
pattern: z.string().optional().describe("Filter by pattern"),
|
|
2543
|
-
dryRun: z.boolean().default(false).describe("Show what would be updated").meta({ alias: "d" })
|
|
2544
|
-
unshallow: z.boolean().default(false).describe("Convert shallow clones to full clones")
|
|
2682
|
+
dryRun: z.boolean().default(false).describe("Show what would be updated").meta({ alias: "d" })
|
|
2545
2683
|
})).meta({ description: "Update repos (git fetch + pull)" }).handler(async ({ input }) => {
|
|
2546
2684
|
await repoUpdateHandler({
|
|
2547
2685
|
all: input.all,
|
|
2548
2686
|
pattern: input.pattern,
|
|
2549
|
-
dryRun: input.dryRun
|
|
2550
|
-
unshallow: input.unshallow
|
|
2687
|
+
dryRun: input.dryRun
|
|
2551
2688
|
});
|
|
2552
2689
|
}),
|
|
2553
2690
|
prune: os.input(z.object({
|
|
@@ -2634,4 +2771,4 @@ function createOwCli() {
|
|
|
2634
2771
|
|
|
2635
2772
|
//#endregion
|
|
2636
2773
|
export { router as n, version as r, createOwCli as t };
|
|
2637
|
-
//# sourceMappingURL=src-
|
|
2774
|
+
//# sourceMappingURL=src-BN5uTbLS.mjs.map
|