offworld 0.3.3 → 0.3.5
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 +7 -7
- 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-CrZvPV0A.mjs → src-DAHGD773.mjs} +268 -99
- package/dist/src-DAHGD773.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/src-CrZvPV0A.mjs.map +0 -1
|
@@ -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
299
|
if (!verbose) s.start("Generating reference with AI...");
|
|
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,10 +390,7 @@ 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;
|
|
@@ -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 });
|
|
@@ -1806,6 +1830,27 @@ async function initHandler(options = {}) {
|
|
|
1806
1830
|
|
|
1807
1831
|
//#endregion
|
|
1808
1832
|
//#region src/handlers/project.ts
|
|
1833
|
+
function isInternalDependencyVersion(version) {
|
|
1834
|
+
if (!version) return false;
|
|
1835
|
+
const trimmed = version.trim();
|
|
1836
|
+
if (!trimmed) return false;
|
|
1837
|
+
const isPathReference = trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/") || trimmed.startsWith("~/") || trimmed.startsWith("~\\");
|
|
1838
|
+
return trimmed.startsWith("workspace:") || trimmed.startsWith("file:") || trimmed.startsWith("link:") || trimmed.startsWith("portal:") || isPathReference;
|
|
1839
|
+
}
|
|
1840
|
+
function dedupeMatchesByRepo(matches) {
|
|
1841
|
+
const seenRepos = /* @__PURE__ */ new Set();
|
|
1842
|
+
const deduped = [];
|
|
1843
|
+
for (const match of matches) {
|
|
1844
|
+
if (!match.repo) {
|
|
1845
|
+
deduped.push(match);
|
|
1846
|
+
continue;
|
|
1847
|
+
}
|
|
1848
|
+
if (seenRepos.has(match.repo)) continue;
|
|
1849
|
+
seenRepos.add(match.repo);
|
|
1850
|
+
deduped.push(match);
|
|
1851
|
+
}
|
|
1852
|
+
return deduped;
|
|
1853
|
+
}
|
|
1809
1854
|
function detectProjectRoot() {
|
|
1810
1855
|
let currentDir = process.cwd();
|
|
1811
1856
|
while (currentDir !== homedir()) {
|
|
@@ -1831,6 +1876,20 @@ function showBanner() {
|
|
|
1831
1876
|
for (const line of LOGO_LINES) console.log(`${OLIVE}${line}${RESET}`);
|
|
1832
1877
|
console.log();
|
|
1833
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
|
+
}
|
|
1834
1893
|
async function projectInitHandler(options = {}) {
|
|
1835
1894
|
showBanner();
|
|
1836
1895
|
p.intro("Scan your deps to install reference files and create a clone map for your agents.");
|
|
@@ -1865,11 +1924,7 @@ async function projectInitHandler(options = {}) {
|
|
|
1865
1924
|
p.log.info(`Found ${pc.cyan(dependencies.length)} dependencies`);
|
|
1866
1925
|
if (dependencies.length > 10) p.log.warn("Generating many references at once uses a lot of tokens. Consider selecting just a few.");
|
|
1867
1926
|
p.log.step("Resolving GitHub repositories...");
|
|
1868
|
-
const
|
|
1869
|
-
if (!version) return false;
|
|
1870
|
-
return version.startsWith("workspace:") || version.startsWith("catalog:") || version.startsWith("file:") || version.startsWith("link:");
|
|
1871
|
-
};
|
|
1872
|
-
const resolvedPromises = dependencies.filter((dep) => !isInternalDep(dep.version)).map((dep) => resolveDependencyRepo(dep.name));
|
|
1927
|
+
const resolvedPromises = dependencies.filter((dep) => !isInternalDependencyVersion(dep.version)).map((dep) => resolveDependencyRepo(dep.name, dep.version));
|
|
1873
1928
|
const resolved = await Promise.all(resolvedPromises);
|
|
1874
1929
|
const skipList = options.skip ? options.skip.split(",").map((d) => d.trim()) : [];
|
|
1875
1930
|
const depsList = options.deps ? options.deps.split(",").map((d) => d.trim()) : [];
|
|
@@ -1944,6 +1999,12 @@ async function projectInitHandler(options = {}) {
|
|
|
1944
1999
|
}
|
|
1945
2000
|
selected = Array.isArray(selectedResult) ? selectedResult : [];
|
|
1946
2001
|
}
|
|
2002
|
+
const dedupedSelected = dedupeMatchesByRepo(selected);
|
|
2003
|
+
if (dedupedSelected.length !== selected.length) {
|
|
2004
|
+
const dedupeCount = selected.length - dedupedSelected.length;
|
|
2005
|
+
p.log.info(`Deduped ${dedupeCount} duplicate dependencies that map to the same repository.`);
|
|
2006
|
+
}
|
|
2007
|
+
selected = dedupedSelected;
|
|
1947
2008
|
if (selected.length === 0) {
|
|
1948
2009
|
p.log.warn("No dependencies selected.");
|
|
1949
2010
|
p.outro("");
|
|
@@ -1972,55 +2033,164 @@ async function projectInitHandler(options = {}) {
|
|
|
1972
2033
|
message: "Dry run complete"
|
|
1973
2034
|
};
|
|
1974
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;
|
|
1975
2042
|
const installed = [];
|
|
2043
|
+
const successfulMatches = [];
|
|
1976
2044
|
let failedCount = 0;
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
try {
|
|
1986
|
-
const pullResult = await pullHandler({
|
|
1987
|
-
repo,
|
|
1988
|
-
shallow: true,
|
|
1989
|
-
force: options.generate,
|
|
1990
|
-
verbose: false,
|
|
1991
|
-
quiet: true,
|
|
1992
|
-
skipConfirm: true,
|
|
1993
|
-
onProgress: (msg) => spinner.message(`${prefix}: ${msg}`)
|
|
1994
|
-
});
|
|
1995
|
-
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 {
|
|
1996
2053
|
const referencePath = getReferencePath(repo);
|
|
2054
|
+
successfulMatches.push(match);
|
|
1997
2055
|
installed.push({
|
|
1998
2056
|
dependency: match.dep,
|
|
1999
2057
|
reference: toReferenceFileName(repo),
|
|
2000
2058
|
path: referencePath
|
|
2001
2059
|
});
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
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
|
+
}
|
|
2005
2112
|
spinner.stop(`${prefix} ${pc.red("✗")} ${pc.red("failed")}`);
|
|
2006
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
|
+
};
|
|
2007
2126
|
}
|
|
2008
|
-
}
|
|
2009
|
-
const errMsg = error instanceof Error ? error.message : "Unknown error";
|
|
2010
|
-
spinner.stop(`${prefix} ${pc.red("✗")} ${pc.dim(errMsg)}`);
|
|
2011
|
-
failedCount++;
|
|
2012
|
-
}
|
|
2127
|
+
}), concurrency);
|
|
2013
2128
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2129
|
+
if (generateGroup.length > 0 && options.generate) {
|
|
2130
|
+
p.log.step(`Generating ${pc.yellow(generateGroup.length)} references with AI...`);
|
|
2131
|
+
let openCodeContext;
|
|
2132
|
+
try {
|
|
2133
|
+
if (generateGroup.length > 1) {
|
|
2134
|
+
p.log.info(pc.dim("Starting shared AI server for batch generation..."));
|
|
2135
|
+
openCodeContext = await createOpenCodeContext({ onDebug: () => {} });
|
|
2136
|
+
}
|
|
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();
|
|
2175
|
+
}
|
|
2176
|
+
} else if (generateGroup.length > 0 && !options.generate) p.log.info(`Skipping ${pc.yellow(generateGroup.length)} deps that need generation (use --generate to include them).`);
|
|
2177
|
+
if (successfulMatches.length > 0) {
|
|
2178
|
+
const map = readGlobalMap();
|
|
2179
|
+
const successfulRepos = /* @__PURE__ */ new Map();
|
|
2180
|
+
for (const match of successfulMatches) {
|
|
2181
|
+
if (!match.repo || successfulRepos.has(match.repo)) continue;
|
|
2182
|
+
successfulRepos.set(match.repo, match);
|
|
2183
|
+
}
|
|
2184
|
+
writeProjectMap(projectRoot, Object.fromEntries(Array.from(successfulRepos.values()).map((match) => {
|
|
2185
|
+
const qualifiedName = `github.com:${match.repo}`;
|
|
2186
|
+
const entry = map.repos[qualifiedName];
|
|
2187
|
+
return [qualifiedName, {
|
|
2188
|
+
localPath: entry?.localPath ?? "",
|
|
2189
|
+
reference: toReferenceFileName(match.repo),
|
|
2190
|
+
keywords: entry?.keywords ?? []
|
|
2191
|
+
}];
|
|
2192
|
+
})));
|
|
2193
|
+
} else p.log.warn("No references were installed. Project map was not updated.");
|
|
2024
2194
|
if (installed.length > 0) try {
|
|
2025
2195
|
updateAgentFiles(projectRoot, installed);
|
|
2026
2196
|
} catch (error) {
|
|
@@ -2302,12 +2472,11 @@ async function mapSearchHandler(options) {
|
|
|
2302
2472
|
|
|
2303
2473
|
//#endregion
|
|
2304
2474
|
//#region src/index.ts
|
|
2305
|
-
const version = "0.3.
|
|
2475
|
+
const version = "0.3.5";
|
|
2306
2476
|
const router = os.router({
|
|
2307
2477
|
pull: os.input(z.object({
|
|
2308
2478
|
repo: z.string().describe("repo").meta({ positional: true }),
|
|
2309
2479
|
reference: z.string().optional().describe("Reference name to pull (defaults to owner-repo)").meta({ alias: "r" }),
|
|
2310
|
-
shallow: z.boolean().default(false).describe("Use shallow clone (--depth 1)").meta({ negativeAlias: "full-history" }),
|
|
2311
2480
|
sparse: z.boolean().default(false).describe("Use sparse checkout (only src/, lib/, packages/, docs/)"),
|
|
2312
2481
|
branch: z.string().optional().describe("Branch to clone"),
|
|
2313
2482
|
force: z.boolean().default(false).describe("Force re-generation").meta({ alias: "f" }),
|
|
@@ -2320,7 +2489,6 @@ const router = os.router({
|
|
|
2320
2489
|
await pullHandler({
|
|
2321
2490
|
repo: input.repo,
|
|
2322
2491
|
reference: input.reference,
|
|
2323
|
-
shallow: input.shallow,
|
|
2324
2492
|
sparse: input.sparse,
|
|
2325
2493
|
branch: input.branch,
|
|
2326
2494
|
force: input.force,
|
|
@@ -2401,10 +2569,11 @@ const router = os.router({
|
|
|
2401
2569
|
})).meta({ description: `Set a config value
|
|
2402
2570
|
|
|
2403
2571
|
Valid keys:
|
|
2404
|
-
repoRoot
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
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 }) => {
|
|
2408
2577
|
await configSetHandler({
|
|
2409
2578
|
key: input.key,
|
|
2410
2579
|
value: input.value
|
|
@@ -2412,7 +2581,7 @@ Valid keys:
|
|
|
2412
2581
|
}),
|
|
2413
2582
|
get: os.input(z.object({ key: z.string().describe("key").meta({ positional: true }) })).meta({ description: `Get a config value
|
|
2414
2583
|
|
|
2415
|
-
Valid keys: repoRoot,
|
|
2584
|
+
Valid keys: repoRoot, defaultModel, maxCommitDistance, acceptUnknownDistance, agents` }).handler(async ({ input }) => {
|
|
2416
2585
|
await configGetHandler({ key: input.key });
|
|
2417
2586
|
}),
|
|
2418
2587
|
reset: os.input(z.object({})).meta({ description: "Reset config to defaults" }).handler(async () => {
|
|
@@ -2446,7 +2615,8 @@ Valid keys: repoRoot, defaultShallow, defaultModel, agents` }).handler(async ({
|
|
|
2446
2615
|
skip: z.string().optional().describe("Comma-separated deps to exclude"),
|
|
2447
2616
|
generate: z.boolean().default(false).describe("Generate references for deps without existing ones").meta({ alias: "g" }),
|
|
2448
2617
|
dryRun: z.boolean().default(false).describe("Show what would be done without doing it").meta({ alias: "d" }),
|
|
2449
|
-
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" })
|
|
2450
2620
|
})).meta({
|
|
2451
2621
|
description: "Scan manifest, install references, update AGENTS.md",
|
|
2452
2622
|
default: true
|
|
@@ -2457,7 +2627,8 @@ Valid keys: repoRoot, defaultShallow, defaultModel, agents` }).handler(async ({
|
|
|
2457
2627
|
skip: input.skip,
|
|
2458
2628
|
generate: input.generate,
|
|
2459
2629
|
dryRun: input.dryRun,
|
|
2460
|
-
yes: input.yes
|
|
2630
|
+
yes: input.yes,
|
|
2631
|
+
concurrency: input.concurrency
|
|
2461
2632
|
});
|
|
2462
2633
|
}) }),
|
|
2463
2634
|
map: os.router({
|
|
@@ -2508,14 +2679,12 @@ Valid keys: repoRoot, defaultShallow, defaultModel, agents` }).handler(async ({
|
|
|
2508
2679
|
update: os.input(z.object({
|
|
2509
2680
|
all: z.boolean().default(false).describe("Update all repos"),
|
|
2510
2681
|
pattern: z.string().optional().describe("Filter by pattern"),
|
|
2511
|
-
dryRun: z.boolean().default(false).describe("Show what would be updated").meta({ alias: "d" })
|
|
2512
|
-
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" })
|
|
2513
2683
|
})).meta({ description: "Update repos (git fetch + pull)" }).handler(async ({ input }) => {
|
|
2514
2684
|
await repoUpdateHandler({
|
|
2515
2685
|
all: input.all,
|
|
2516
2686
|
pattern: input.pattern,
|
|
2517
|
-
dryRun: input.dryRun
|
|
2518
|
-
unshallow: input.unshallow
|
|
2687
|
+
dryRun: input.dryRun
|
|
2519
2688
|
});
|
|
2520
2689
|
}),
|
|
2521
2690
|
prune: os.input(z.object({
|
|
@@ -2602,4 +2771,4 @@ function createOwCli() {
|
|
|
2602
2771
|
|
|
2603
2772
|
//#endregion
|
|
2604
2773
|
export { router as n, version as r, createOwCli as t };
|
|
2605
|
-
//# sourceMappingURL=src-
|
|
2774
|
+
//# sourceMappingURL=src-DAHGD773.mjs.map
|