@osovv/vvcode 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -13
- package/dist/vvcode.js +444 -35
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,14 +8,20 @@
|
|
|
8
8
|
bun add -g @osovv/vvcode
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
For local development from this repo:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
bun install
|
|
15
15
|
bun run cli -- --help
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Config layout
|
|
19
|
+
|
|
20
|
+
Global config path:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
~/.config/vvcode/config.jsonc
|
|
24
|
+
```
|
|
19
25
|
|
|
20
26
|
Project config path:
|
|
21
27
|
|
|
@@ -23,30 +29,101 @@ Project config path:
|
|
|
23
29
|
.vvcode/config.jsonc
|
|
24
30
|
```
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
The current behavior is:
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
1. Global config is the source of shared presets and profiles.
|
|
35
|
+
2. Project config is optional for launch if a project root already exists.
|
|
36
|
+
3. `vvcode init` creates a minimal project config scaffold.
|
|
37
|
+
4. `vvcode preset add <name>` copies a preset from global config into the project config.
|
|
38
|
+
5. `vvcode preset remove <name>` removes a copied project preset and prunes unused copied profiles.
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
Initialize a project-local config:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
vvcode init
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
See which presets are available:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
vvcode preset list
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Copy a global preset into the current project:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
vvcode preset add openai
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Launch OpenCode with a preset:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
vvcode opencode --preset openai
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Or use the shorthand form:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
vvcode --preset openai
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Commands
|
|
73
|
+
|
|
74
|
+
Show help:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
vvcode help
|
|
78
|
+
vvcode --help
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Initialize project config:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
vvcode init
|
|
85
|
+
vvcode init --project-root /path/to/project
|
|
30
86
|
```
|
|
31
87
|
|
|
32
|
-
|
|
88
|
+
Launch OpenCode:
|
|
33
89
|
|
|
34
90
|
```bash
|
|
35
91
|
vvcode opencode --preset <name>
|
|
92
|
+
vvcode --preset <name>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
By default `vvcode` starts the shell with:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
opencode
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
You can override the shell command, for example:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
OPENCODE_SHELL_COMMAND='bun x opencode-ai' vvcode --preset openai
|
|
36
105
|
```
|
|
37
106
|
|
|
38
107
|
Options:
|
|
39
108
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
109
|
+
1. `--preset`, `-p` for the preset name.
|
|
110
|
+
2. `--global-config`, `-c` to override the global config file path.
|
|
111
|
+
3. `--project-root`, `-r` to launch from a different project root.
|
|
43
112
|
|
|
44
|
-
|
|
113
|
+
Manage presets:
|
|
45
114
|
|
|
46
115
|
```bash
|
|
47
|
-
vvcode
|
|
116
|
+
vvcode preset list
|
|
117
|
+
vvcode preset add <preset-name>
|
|
118
|
+
vvcode preset remove <preset-name>
|
|
48
119
|
```
|
|
49
120
|
|
|
121
|
+
`preset list` prints one preset per line with its source label:
|
|
122
|
+
|
|
123
|
+
1. `global`
|
|
124
|
+
2. `project`
|
|
125
|
+
3. `project-overrides-global`
|
|
126
|
+
|
|
50
127
|
## Runtime artifacts
|
|
51
128
|
|
|
52
129
|
- `.vvcode/local/runtime/overlays/`
|
|
@@ -60,9 +137,9 @@ vvcode --preset <name>
|
|
|
60
137
|
## Packaging notes
|
|
61
138
|
|
|
62
139
|
- Package name: `@osovv/vvcode`
|
|
63
|
-
- CLI bin: `
|
|
140
|
+
- CLI bin: `vvcode`
|
|
64
141
|
- Published runtime entry: `dist/vvcode.js`
|
|
65
142
|
- Build command: `bun run build`
|
|
66
143
|
- Local dev command: `bun run cli -- --help`
|
|
67
|
-
- `prepack` builds `dist` automatically before publish
|
|
144
|
+
- `prepack` builds `dist` automatically before publish or pack
|
|
68
145
|
- Scoped publish target: `npm publish --access=public`
|
package/dist/vvcode.js
CHANGED
|
@@ -543,11 +543,11 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
|
|
|
543
543
|
|
|
544
544
|
// src/cli/vvcode.ts
|
|
545
545
|
import { homedir } from "os";
|
|
546
|
-
import { join as join3 } from "path";
|
|
546
|
+
import { join as join3, resolve } from "path";
|
|
547
547
|
// package.json
|
|
548
548
|
var package_default = {
|
|
549
549
|
name: "@osovv/vvcode",
|
|
550
|
-
version: "0.
|
|
550
|
+
version: "0.5.1",
|
|
551
551
|
description: "Packaged CLI for launching OpenCode with vvcode runtime semantics.",
|
|
552
552
|
license: "MIT",
|
|
553
553
|
homepage: "https://github.com/osovv/vvcode",
|
|
@@ -565,7 +565,7 @@ var package_default = {
|
|
|
565
565
|
"dist"
|
|
566
566
|
],
|
|
567
567
|
bin: {
|
|
568
|
-
vvcode: "
|
|
568
|
+
vvcode: "bin/vvcode"
|
|
569
569
|
},
|
|
570
570
|
publishConfig: {
|
|
571
571
|
access: "public"
|
|
@@ -602,10 +602,6 @@ var package_default = {
|
|
|
602
602
|
]
|
|
603
603
|
};
|
|
604
604
|
|
|
605
|
-
// src/cli/pivv-opencode.ts
|
|
606
|
-
import { rm } from "fs/promises";
|
|
607
|
-
import { spawn } from "child_process";
|
|
608
|
-
|
|
609
605
|
// src/config/load-pivv-config.ts
|
|
610
606
|
class PivvConfigError extends Error {
|
|
611
607
|
code;
|
|
@@ -628,9 +624,6 @@ async function loadPivvConfig(globalConfigPath, projectRoot) {
|
|
|
628
624
|
for (const sourceCandidate of sourceCandidates) {
|
|
629
625
|
const sourcePath = sourceCandidate.path;
|
|
630
626
|
const sourceExists = await pathExists(sourcePath);
|
|
631
|
-
if (!sourceExists && sourceCandidate.sourceType === "project") {
|
|
632
|
-
throw new PivvConfigError("CONFIG_NOT_FOUND", `Project config not found at ${sourcePath}`);
|
|
633
|
-
}
|
|
634
627
|
if (!sourceExists) {
|
|
635
628
|
continue;
|
|
636
629
|
}
|
|
@@ -674,6 +667,37 @@ async function findProjectRoot(projectRoot) {
|
|
|
674
667
|
}
|
|
675
668
|
throw new PivvConfigError("CONFIG_NOT_FOUND", `Unable to resolve project root from ${projectRoot}`);
|
|
676
669
|
}
|
|
670
|
+
function resolveProjectConfigPath(projectRoot) {
|
|
671
|
+
return joinPath(resolvePath(projectRoot), PROJECT_CONFIG_PATH);
|
|
672
|
+
}
|
|
673
|
+
function createEmptyRawVvcodeConfig() {
|
|
674
|
+
return {
|
|
675
|
+
profiles: {},
|
|
676
|
+
presets: {},
|
|
677
|
+
memory: {},
|
|
678
|
+
overlay: {}
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
async function readOptionalRawVvcodeConfig(configPath) {
|
|
682
|
+
const normalizedPath = resolvePath(configPath);
|
|
683
|
+
if (!await pathExists(normalizedPath)) {
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
return readJsoncConfig(normalizedPath);
|
|
687
|
+
}
|
|
688
|
+
async function writeRawVvcodeConfig(configPath, config) {
|
|
689
|
+
const normalizedPath = resolvePath(configPath);
|
|
690
|
+
const fsPromises = await getFsPromises();
|
|
691
|
+
const stableConfig = {
|
|
692
|
+
profiles: isRecord(config.profiles) ? config.profiles : {},
|
|
693
|
+
presets: isRecord(config.presets) ? config.presets : {},
|
|
694
|
+
memory: isRecord(config.memory) ? config.memory : {},
|
|
695
|
+
overlay: isRecord(config.overlay) ? config.overlay : {}
|
|
696
|
+
};
|
|
697
|
+
await fsPromises.mkdir(getParentPath(normalizedPath), { recursive: true });
|
|
698
|
+
await fsPromises.writeFile(normalizedPath, `${JSON.stringify(stableConfig, null, 2)}
|
|
699
|
+
`, "utf-8");
|
|
700
|
+
}
|
|
677
701
|
function normalizeConfig(rawConfig, projectRoot, configSources) {
|
|
678
702
|
if (!isRecord(rawConfig)) {
|
|
679
703
|
throw new PivvConfigError("CONFIG_INVALID", "Config root must be an object");
|
|
@@ -899,6 +923,10 @@ function normalizePath(path) {
|
|
|
899
923
|
return withSingleSlashes;
|
|
900
924
|
}
|
|
901
925
|
|
|
926
|
+
// src/cli/pivv-opencode.ts
|
|
927
|
+
import { rm } from "fs/promises";
|
|
928
|
+
import { spawn } from "child_process";
|
|
929
|
+
|
|
902
930
|
// src/opencode/build-runtime-overlay.ts
|
|
903
931
|
import { mkdir, writeFile } from "fs/promises";
|
|
904
932
|
import { join } from "path";
|
|
@@ -1331,7 +1359,8 @@ function createTraceWriteError(message, cause) {
|
|
|
1331
1359
|
// src/cli/pivv-opencode.ts
|
|
1332
1360
|
var LAUNCHER_LOG_PREFIX = "[VvcodeLauncher]";
|
|
1333
1361
|
var BLOCK_LOAD_ACTIVE_PRESET = "BLOCK_LOAD_ACTIVE_PRESET";
|
|
1334
|
-
var
|
|
1362
|
+
var DEFAULT_OPENCODE_SHELL_COMMAND = "opencode";
|
|
1363
|
+
var OPENCODE_SHELL_COMMAND_ENV = "OPENCODE_SHELL_COMMAND";
|
|
1335
1364
|
function buildRuntimeEnvironment(runtimeOverlay, baseEnvironment = process.env) {
|
|
1336
1365
|
const runtimeEnvironment = {};
|
|
1337
1366
|
for (const [key, value] of Object.entries(baseEnvironment)) {
|
|
@@ -1340,7 +1369,12 @@ function buildRuntimeEnvironment(runtimeOverlay, baseEnvironment = process.env)
|
|
|
1340
1369
|
}
|
|
1341
1370
|
}
|
|
1342
1371
|
runtimeEnvironment.OPENCODE_CONFIG_DIR = runtimeOverlay.overlayDirPath;
|
|
1343
|
-
runtimeEnvironment.OPENCODE_CONFIG_CONTENT = JSON.stringify(
|
|
1372
|
+
runtimeEnvironment.OPENCODE_CONFIG_CONTENT = JSON.stringify({
|
|
1373
|
+
provider: normalizeProviderConfig(runtimeOverlay.providers),
|
|
1374
|
+
agent: runtimeOverlay.agents,
|
|
1375
|
+
command: normalizeCommandConfig(runtimeOverlay.commands),
|
|
1376
|
+
tools: runtimeOverlay.tools
|
|
1377
|
+
});
|
|
1344
1378
|
return runtimeEnvironment;
|
|
1345
1379
|
}
|
|
1346
1380
|
async function runPivvOpencode(cliArgs) {
|
|
@@ -1355,6 +1389,8 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1355
1389
|
let aliasModelCount = 0;
|
|
1356
1390
|
let providerIds = [];
|
|
1357
1391
|
let lifecycleStage = "config.load";
|
|
1392
|
+
const openCodeShellCommand = resolveOpenCodeShellCommand();
|
|
1393
|
+
const openCodeEntrypoint = openCodeShellCommand.entrypoint;
|
|
1358
1394
|
if (!traceRecorder) {
|
|
1359
1395
|
try {
|
|
1360
1396
|
resolvedProjectRoot = await findProjectRoot(cliArgs.projectRoot);
|
|
@@ -1373,7 +1409,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1373
1409
|
preset: cliArgs.preset,
|
|
1374
1410
|
projectRoot: resolvedProjectRoot,
|
|
1375
1411
|
cwd: resolvedProjectRoot,
|
|
1376
|
-
entrypoint:
|
|
1412
|
+
entrypoint: openCodeEntrypoint
|
|
1377
1413
|
}
|
|
1378
1414
|
});
|
|
1379
1415
|
try {
|
|
@@ -1387,7 +1423,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1387
1423
|
preset: cliArgs.preset,
|
|
1388
1424
|
projectRoot: resolvedProjectRoot,
|
|
1389
1425
|
cwd: resolvedProjectRoot,
|
|
1390
|
-
entrypoint:
|
|
1426
|
+
entrypoint: openCodeEntrypoint
|
|
1391
1427
|
}
|
|
1392
1428
|
});
|
|
1393
1429
|
const projectConfig = await loadPivvConfig(cliArgs.globalConfigPath, cliArgs.projectRoot);
|
|
@@ -1402,7 +1438,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1402
1438
|
preset: cliArgs.preset,
|
|
1403
1439
|
projectRoot: resolvedProjectRoot,
|
|
1404
1440
|
cwd: resolvedProjectRoot,
|
|
1405
|
-
entrypoint:
|
|
1441
|
+
entrypoint: openCodeEntrypoint
|
|
1406
1442
|
}
|
|
1407
1443
|
});
|
|
1408
1444
|
lifecycleStage = "preset.resolve";
|
|
@@ -1416,7 +1452,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1416
1452
|
preset: cliArgs.preset,
|
|
1417
1453
|
coreSlotsResolved: 0,
|
|
1418
1454
|
cwd: resolvedProjectRoot,
|
|
1419
|
-
entrypoint:
|
|
1455
|
+
entrypoint: openCodeEntrypoint
|
|
1420
1456
|
}
|
|
1421
1457
|
});
|
|
1422
1458
|
const resolvedPreset = resolvePreset(cliArgs.preset, projectConfig);
|
|
@@ -1431,7 +1467,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1431
1467
|
profileCount: resolvedPreset.profileCount,
|
|
1432
1468
|
coreSlotsResolved: Object.keys(resolvedPreset.coreProfiles).length,
|
|
1433
1469
|
cwd: resolvedProjectRoot,
|
|
1434
|
-
entrypoint:
|
|
1470
|
+
entrypoint: openCodeEntrypoint
|
|
1435
1471
|
}
|
|
1436
1472
|
});
|
|
1437
1473
|
lifecycleStage = "overlay.build";
|
|
@@ -1446,7 +1482,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1446
1482
|
aliasModelCount,
|
|
1447
1483
|
providerIds,
|
|
1448
1484
|
cwd: resolvedProjectRoot,
|
|
1449
|
-
entrypoint:
|
|
1485
|
+
entrypoint: openCodeEntrypoint
|
|
1450
1486
|
}
|
|
1451
1487
|
});
|
|
1452
1488
|
const runtimeOverlay = await buildRuntimeOverlay(resolvedPreset, projectConfig, baseOpenCodeInputs);
|
|
@@ -1465,7 +1501,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1465
1501
|
aliasModelCount,
|
|
1466
1502
|
providerIds,
|
|
1467
1503
|
cwd: resolvedProjectRoot,
|
|
1468
|
-
entrypoint:
|
|
1504
|
+
entrypoint: openCodeEntrypoint
|
|
1469
1505
|
}
|
|
1470
1506
|
});
|
|
1471
1507
|
lifecycleStage = "launch";
|
|
@@ -1483,7 +1519,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1483
1519
|
aliasModelCount,
|
|
1484
1520
|
providerIds,
|
|
1485
1521
|
cwd: resolvedProjectRoot,
|
|
1486
|
-
entrypoint:
|
|
1522
|
+
entrypoint: openCodeEntrypoint
|
|
1487
1523
|
}
|
|
1488
1524
|
});
|
|
1489
1525
|
const launchResult = await launchOpenCodeProcess({
|
|
@@ -1491,7 +1527,8 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1491
1527
|
projectRoot: resolvedProjectRoot,
|
|
1492
1528
|
runtimeOverlay,
|
|
1493
1529
|
runtimeEnvironment,
|
|
1494
|
-
traceContext
|
|
1530
|
+
traceContext,
|
|
1531
|
+
openCodeShellCommand
|
|
1495
1532
|
});
|
|
1496
1533
|
if (launchResult.exitCode !== 0) {
|
|
1497
1534
|
const cleanupResult = await cleanupRuntimeOverlay(runtimeOverlayPath);
|
|
@@ -1503,7 +1540,8 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1503
1540
|
exitCode: launchResult.exitCode,
|
|
1504
1541
|
cleanupResult,
|
|
1505
1542
|
aliasModelCount,
|
|
1506
|
-
providerIds
|
|
1543
|
+
providerIds,
|
|
1544
|
+
entrypoint: openCodeEntrypoint
|
|
1507
1545
|
});
|
|
1508
1546
|
return failureResult;
|
|
1509
1547
|
}
|
|
@@ -1520,7 +1558,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1520
1558
|
aliasModelCount,
|
|
1521
1559
|
providerIds,
|
|
1522
1560
|
cwd: resolvedProjectRoot,
|
|
1523
|
-
entrypoint:
|
|
1561
|
+
entrypoint: openCodeEntrypoint
|
|
1524
1562
|
}
|
|
1525
1563
|
});
|
|
1526
1564
|
const runFinishTrace = await persistLaunchTrace(traceRecorder, traceContext, {
|
|
@@ -1535,7 +1573,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1535
1573
|
projectRoot: resolvedProjectRoot,
|
|
1536
1574
|
runtimeOverlayPath,
|
|
1537
1575
|
cwd: resolvedProjectRoot,
|
|
1538
|
-
entrypoint:
|
|
1576
|
+
entrypoint: openCodeEntrypoint
|
|
1539
1577
|
}
|
|
1540
1578
|
});
|
|
1541
1579
|
console.info(`${LAUNCHER_LOG_PREFIX}[runPivvOpencode][${BLOCK_LOAD_ACTIVE_PRESET}] launch completed`, {
|
|
@@ -1565,7 +1603,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1565
1603
|
preset: cliArgs.preset,
|
|
1566
1604
|
projectRoot: resolvedProjectRoot,
|
|
1567
1605
|
cwd: resolvedProjectRoot,
|
|
1568
|
-
entrypoint:
|
|
1606
|
+
entrypoint: openCodeEntrypoint
|
|
1569
1607
|
}
|
|
1570
1608
|
});
|
|
1571
1609
|
}
|
|
@@ -1581,7 +1619,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1581
1619
|
projectRoot: resolvedProjectRoot,
|
|
1582
1620
|
coreSlotsResolved: 0,
|
|
1583
1621
|
cwd: resolvedProjectRoot,
|
|
1584
|
-
entrypoint:
|
|
1622
|
+
entrypoint: openCodeEntrypoint
|
|
1585
1623
|
}
|
|
1586
1624
|
});
|
|
1587
1625
|
}
|
|
@@ -1599,7 +1637,7 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1599
1637
|
aliasModelCount,
|
|
1600
1638
|
providerIds,
|
|
1601
1639
|
cwd: resolvedProjectRoot,
|
|
1602
|
-
entrypoint:
|
|
1640
|
+
entrypoint: openCodeEntrypoint
|
|
1603
1641
|
}
|
|
1604
1642
|
});
|
|
1605
1643
|
}
|
|
@@ -1612,7 +1650,8 @@ async function runPivvOpencode(cliArgs) {
|
|
|
1612
1650
|
code: failureCode,
|
|
1613
1651
|
cleanupResult,
|
|
1614
1652
|
aliasModelCount,
|
|
1615
|
-
providerIds
|
|
1653
|
+
providerIds,
|
|
1654
|
+
entrypoint: openCodeEntrypoint
|
|
1616
1655
|
});
|
|
1617
1656
|
}
|
|
1618
1657
|
}
|
|
@@ -1672,7 +1711,7 @@ async function finalizeLaunchFailure(traceRecorder, traceContext, context) {
|
|
|
1672
1711
|
aliasModelCount: context.aliasModelCount,
|
|
1673
1712
|
providerIds: context.providerIds,
|
|
1674
1713
|
cwd: context.projectRoot,
|
|
1675
|
-
entrypoint:
|
|
1714
|
+
entrypoint: context.entrypoint
|
|
1676
1715
|
}
|
|
1677
1716
|
});
|
|
1678
1717
|
const runFailTrace = await persistLaunchTrace(traceRecorder, traceContext, {
|
|
@@ -1689,7 +1728,7 @@ async function finalizeLaunchFailure(traceRecorder, traceContext, context) {
|
|
|
1689
1728
|
code: context.code,
|
|
1690
1729
|
exitCode: context.exitCode,
|
|
1691
1730
|
cwd: context.projectRoot,
|
|
1692
|
-
entrypoint:
|
|
1731
|
+
entrypoint: context.entrypoint
|
|
1693
1732
|
}
|
|
1694
1733
|
});
|
|
1695
1734
|
console.info(`${LAUNCHER_LOG_PREFIX}[runPivvOpencode][${BLOCK_LOAD_ACTIVE_PRESET}] launch failed`, {
|
|
@@ -1711,7 +1750,7 @@ async function finalizeLaunchFailure(traceRecorder, traceContext, context) {
|
|
|
1711
1750
|
}
|
|
1712
1751
|
async function launchOpenCodeProcess(request) {
|
|
1713
1752
|
return await new Promise((resolve, reject) => {
|
|
1714
|
-
const launchedProcess = spawn(
|
|
1753
|
+
const launchedProcess = spawn(request.openCodeShellCommand.executable, request.openCodeShellCommand.args, {
|
|
1715
1754
|
cwd: request.projectRoot,
|
|
1716
1755
|
env: request.runtimeEnvironment,
|
|
1717
1756
|
stdio: "inherit"
|
|
@@ -1726,10 +1765,127 @@ async function launchOpenCodeProcess(request) {
|
|
|
1726
1765
|
});
|
|
1727
1766
|
});
|
|
1728
1767
|
}
|
|
1768
|
+
function resolveOpenCodeShellCommand(baseEnvironment = process.env) {
|
|
1769
|
+
const configuredCommand = baseEnvironment[OPENCODE_SHELL_COMMAND_ENV]?.trim();
|
|
1770
|
+
const entrypoint = configuredCommand && configuredCommand.length > 0 ? configuredCommand : DEFAULT_OPENCODE_SHELL_COMMAND;
|
|
1771
|
+
const tokens = splitShellCommand(entrypoint);
|
|
1772
|
+
if (tokens.length === 0) {
|
|
1773
|
+
return {
|
|
1774
|
+
executable: DEFAULT_OPENCODE_SHELL_COMMAND,
|
|
1775
|
+
args: [],
|
|
1776
|
+
entrypoint: DEFAULT_OPENCODE_SHELL_COMMAND
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
return {
|
|
1780
|
+
executable: tokens[0],
|
|
1781
|
+
args: tokens.slice(1),
|
|
1782
|
+
entrypoint
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
function splitShellCommand(command) {
|
|
1786
|
+
const tokens = [];
|
|
1787
|
+
let current = "";
|
|
1788
|
+
let quote = null;
|
|
1789
|
+
for (let index = 0;index < command.length; index += 1) {
|
|
1790
|
+
const character = command[index];
|
|
1791
|
+
if (quote === null) {
|
|
1792
|
+
if (character === '"' || character === "'") {
|
|
1793
|
+
quote = character === '"' ? "double" : "single";
|
|
1794
|
+
continue;
|
|
1795
|
+
}
|
|
1796
|
+
if (/\s/.test(character)) {
|
|
1797
|
+
if (current.length > 0) {
|
|
1798
|
+
tokens.push(current);
|
|
1799
|
+
current = "";
|
|
1800
|
+
}
|
|
1801
|
+
continue;
|
|
1802
|
+
}
|
|
1803
|
+
} else if (quote === "double" && character === '"' || quote === "single" && character === "'") {
|
|
1804
|
+
quote = null;
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
current += character;
|
|
1808
|
+
}
|
|
1809
|
+
if (current.length > 0) {
|
|
1810
|
+
tokens.push(current);
|
|
1811
|
+
}
|
|
1812
|
+
return tokens;
|
|
1813
|
+
}
|
|
1814
|
+
function normalizeCommandConfig(commands) {
|
|
1815
|
+
const normalizedCommands = {};
|
|
1816
|
+
for (const [commandName, commandConfig] of Object.entries(commands)) {
|
|
1817
|
+
const template = typeof commandConfig.prompt === "string" ? commandConfig.prompt : undefined;
|
|
1818
|
+
if (!template) {
|
|
1819
|
+
continue;
|
|
1820
|
+
}
|
|
1821
|
+
normalizedCommands[commandName] = {
|
|
1822
|
+
template,
|
|
1823
|
+
...typeof commandConfig.description === "string" ? { description: commandConfig.description } : {},
|
|
1824
|
+
...typeof commandConfig.agent === "string" ? { agent: commandConfig.agent } : {},
|
|
1825
|
+
...typeof commandConfig.model === "string" ? { model: commandConfig.model } : {},
|
|
1826
|
+
...typeof commandConfig.subtask === "boolean" ? { subtask: commandConfig.subtask } : {}
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
return normalizedCommands;
|
|
1830
|
+
}
|
|
1831
|
+
function normalizeProviderConfig(providers) {
|
|
1832
|
+
const normalizedProviders = {};
|
|
1833
|
+
for (const [providerId, providerConfig] of Object.entries(providers)) {
|
|
1834
|
+
const normalizedProvider = {};
|
|
1835
|
+
for (const key of ["api", "name", "env", "id", "npm", "whitelist", "blacklist", "options"]) {
|
|
1836
|
+
if (key in providerConfig) {
|
|
1837
|
+
normalizedProvider[key] = providerConfig[key];
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
if (isRecord2(providerConfig.models)) {
|
|
1841
|
+
const normalizedModels = {};
|
|
1842
|
+
for (const [modelId, modelConfig] of Object.entries(providerConfig.models)) {
|
|
1843
|
+
if (!isRecord2(modelConfig)) {
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
const normalizedModel = {};
|
|
1847
|
+
for (const key of [
|
|
1848
|
+
"id",
|
|
1849
|
+
"name",
|
|
1850
|
+
"family",
|
|
1851
|
+
"release_date",
|
|
1852
|
+
"attachment",
|
|
1853
|
+
"reasoning",
|
|
1854
|
+
"temperature",
|
|
1855
|
+
"tool_call",
|
|
1856
|
+
"interleaved",
|
|
1857
|
+
"cost",
|
|
1858
|
+
"limit",
|
|
1859
|
+
"modalities",
|
|
1860
|
+
"experimental",
|
|
1861
|
+
"status",
|
|
1862
|
+
"options",
|
|
1863
|
+
"headers",
|
|
1864
|
+
"variants"
|
|
1865
|
+
]) {
|
|
1866
|
+
if (key in modelConfig) {
|
|
1867
|
+
normalizedModel[key] = modelConfig[key];
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
if (isRecord2(modelConfig.provider)) {
|
|
1871
|
+
normalizedModel.provider = modelConfig.provider;
|
|
1872
|
+
}
|
|
1873
|
+
normalizedModels[modelId] = normalizedModel;
|
|
1874
|
+
}
|
|
1875
|
+
normalizedProvider.models = normalizedModels;
|
|
1876
|
+
}
|
|
1877
|
+
normalizedProviders[providerId] = normalizedProvider;
|
|
1878
|
+
}
|
|
1879
|
+
return normalizedProviders;
|
|
1880
|
+
}
|
|
1881
|
+
function isRecord2(value) {
|
|
1882
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1883
|
+
}
|
|
1729
1884
|
|
|
1730
1885
|
// src/cli/vvcode.ts
|
|
1731
1886
|
var CLI_LOG_PREFIX = "[VvcodeCli]";
|
|
1732
1887
|
var BLOCK_RUN_OPENCODE_COMMAND = "BLOCK_RUN_OPENCODE_COMMAND";
|
|
1888
|
+
var KNOWN_TOP_LEVEL_COMMANDS = new Set(["help", "init", "opencode", "preset"]);
|
|
1733
1889
|
function resolveDefaultGlobalConfigPath() {
|
|
1734
1890
|
return join3(homedir(), ".config", "vvcode", "config.jsonc");
|
|
1735
1891
|
}
|
|
@@ -1738,7 +1894,7 @@ function createVvcodeCommand(dependencyOverrides = {}) {
|
|
|
1738
1894
|
const opencodeArgs = {
|
|
1739
1895
|
preset: {
|
|
1740
1896
|
type: "string",
|
|
1741
|
-
description: "Preset name defined in
|
|
1897
|
+
description: "Preset name defined in global or project vvcode config.",
|
|
1742
1898
|
alias: ["p"],
|
|
1743
1899
|
required: true
|
|
1744
1900
|
},
|
|
@@ -1755,15 +1911,63 @@ function createVvcodeCommand(dependencyOverrides = {}) {
|
|
|
1755
1911
|
default: dependencies.cwd()
|
|
1756
1912
|
}
|
|
1757
1913
|
};
|
|
1914
|
+
const configManagementArgs = {
|
|
1915
|
+
"global-config": {
|
|
1916
|
+
type: "string",
|
|
1917
|
+
description: "Path to the global vvcode config file.",
|
|
1918
|
+
alias: ["c"],
|
|
1919
|
+
default: dependencies.defaultGlobalConfigPath()
|
|
1920
|
+
},
|
|
1921
|
+
"project-root": {
|
|
1922
|
+
type: "string",
|
|
1923
|
+
description: "Project root where .vvcode/config.jsonc should be managed.",
|
|
1924
|
+
alias: ["r"],
|
|
1925
|
+
default: dependencies.cwd()
|
|
1926
|
+
}
|
|
1927
|
+
};
|
|
1758
1928
|
return defineCommand({
|
|
1759
1929
|
meta: {
|
|
1760
1930
|
name: "vvcode",
|
|
1761
1931
|
version: package_default.version,
|
|
1762
1932
|
description: "Packaged CLI for launching OpenCode with vvcode runtime semantics."
|
|
1763
1933
|
},
|
|
1764
|
-
args: opencodeArgs,
|
|
1765
|
-
default: "opencode",
|
|
1766
1934
|
subCommands: {
|
|
1935
|
+
help: defineCommand({
|
|
1936
|
+
meta: {
|
|
1937
|
+
name: "help",
|
|
1938
|
+
description: "Show vvcode command help."
|
|
1939
|
+
},
|
|
1940
|
+
args: {
|
|
1941
|
+
command: {
|
|
1942
|
+
type: "positional",
|
|
1943
|
+
description: "Optional subcommand name to show help for.",
|
|
1944
|
+
required: false
|
|
1945
|
+
}
|
|
1946
|
+
},
|
|
1947
|
+
async run({ args }) {
|
|
1948
|
+
const helpArgs = typeof args.command === "string" && args.command.length > 0 ? [args.command, "--help"] : ["--help"];
|
|
1949
|
+
await runMain(createVvcodeCommand(dependencyOverrides), { rawArgs: helpArgs });
|
|
1950
|
+
}
|
|
1951
|
+
}),
|
|
1952
|
+
init: defineCommand({
|
|
1953
|
+
meta: {
|
|
1954
|
+
name: "init",
|
|
1955
|
+
description: "Create a project-local .vvcode/config.jsonc scaffold."
|
|
1956
|
+
},
|
|
1957
|
+
args: {
|
|
1958
|
+
"project-root": configManagementArgs["project-root"]
|
|
1959
|
+
},
|
|
1960
|
+
async run({ args }) {
|
|
1961
|
+
const configPath = resolveProjectConfigPath(String(args.projectRoot));
|
|
1962
|
+
const existingConfig = await readOptionalRawVvcodeConfig(configPath);
|
|
1963
|
+
if (existingConfig !== null) {
|
|
1964
|
+
dependencies.writeStdout(`vvcode: project config already exists at ${configPath}`);
|
|
1965
|
+
return;
|
|
1966
|
+
}
|
|
1967
|
+
await writeRawVvcodeConfig(configPath, createEmptyRawVvcodeConfig());
|
|
1968
|
+
dependencies.writeStdout(`vvcode: initialized project config at ${configPath}`);
|
|
1969
|
+
}
|
|
1970
|
+
}),
|
|
1767
1971
|
opencode: defineCommand({
|
|
1768
1972
|
meta: {
|
|
1769
1973
|
name: "opencode",
|
|
@@ -1787,13 +1991,151 @@ function createVvcodeCommand(dependencyOverrides = {}) {
|
|
|
1787
1991
|
}
|
|
1788
1992
|
return launchResult;
|
|
1789
1993
|
}
|
|
1994
|
+
}),
|
|
1995
|
+
preset: defineCommand({
|
|
1996
|
+
meta: {
|
|
1997
|
+
name: "preset",
|
|
1998
|
+
description: "Inspect or manage project presets copied from global vvcode config."
|
|
1999
|
+
},
|
|
2000
|
+
default: "list",
|
|
2001
|
+
subCommands: {
|
|
2002
|
+
list: defineCommand({
|
|
2003
|
+
meta: {
|
|
2004
|
+
name: "list",
|
|
2005
|
+
description: "List available presets from global and project config."
|
|
2006
|
+
},
|
|
2007
|
+
args: configManagementArgs,
|
|
2008
|
+
async run({ args }) {
|
|
2009
|
+
const snapshot = await loadProjectConfigSnapshot(String(args.globalConfig), String(args.projectRoot));
|
|
2010
|
+
const presetNames = collectPresetNames(snapshot);
|
|
2011
|
+
if (presetNames.length === 0) {
|
|
2012
|
+
dependencies.writeStdout("vvcode: no presets found in global or project config");
|
|
2013
|
+
return;
|
|
2014
|
+
}
|
|
2015
|
+
for (const presetName of presetNames) {
|
|
2016
|
+
dependencies.writeStdout(`${presetName} ${describePresetSource(snapshot, presetName)}`);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}),
|
|
2020
|
+
add: defineCommand({
|
|
2021
|
+
meta: {
|
|
2022
|
+
name: "add",
|
|
2023
|
+
description: "Copy one preset and its referenced profiles from global config into the project config."
|
|
2024
|
+
},
|
|
2025
|
+
args: {
|
|
2026
|
+
name: {
|
|
2027
|
+
type: "positional",
|
|
2028
|
+
description: "Preset name to copy into the project config.",
|
|
2029
|
+
required: true
|
|
2030
|
+
},
|
|
2031
|
+
...configManagementArgs
|
|
2032
|
+
},
|
|
2033
|
+
async run({ args }) {
|
|
2034
|
+
const presetName = String(args.name);
|
|
2035
|
+
const snapshot = await loadProjectConfigSnapshot(String(args.globalConfig), String(args.projectRoot));
|
|
2036
|
+
const globalPresets = toObjectBucket(snapshot.globalConfig?.presets);
|
|
2037
|
+
const globalProfiles = toObjectBucket(snapshot.globalConfig?.profiles);
|
|
2038
|
+
if (!(presetName in globalPresets) || !isRecord3(globalPresets[presetName])) {
|
|
2039
|
+
throw new Error(`Preset "${presetName}" was not found in global config ${snapshot.globalConfigPath}`);
|
|
2040
|
+
}
|
|
2041
|
+
const projectConfig = snapshot.projectConfig ?? createEmptyRawVvcodeConfig();
|
|
2042
|
+
const projectPresets = toMutableBucket(projectConfig, "presets");
|
|
2043
|
+
const projectProfiles = toMutableBucket(projectConfig, "profiles");
|
|
2044
|
+
if (presetName in projectPresets) {
|
|
2045
|
+
dependencies.writeStdout(`vvcode: preset "${presetName}" already exists in ${snapshot.projectConfigPath}`);
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
const globalPreset = globalPresets[presetName];
|
|
2049
|
+
const copiedProfiles = [];
|
|
2050
|
+
const normalizedPreset = {};
|
|
2051
|
+
for (const [slotName, profileNameValue] of Object.entries(globalPreset)) {
|
|
2052
|
+
if (typeof profileNameValue !== "string" || profileNameValue.length === 0) {
|
|
2053
|
+
throw new Error(`Global preset "${presetName}" slot "${slotName}" must reference a non-empty profile name`);
|
|
2054
|
+
}
|
|
2055
|
+
const profileName = profileNameValue;
|
|
2056
|
+
const globalProfile = globalProfiles[profileName];
|
|
2057
|
+
if (!isRecord3(globalProfile)) {
|
|
2058
|
+
throw new Error(`Global preset "${presetName}" references missing profile "${profileName}"`);
|
|
2059
|
+
}
|
|
2060
|
+
if (profileName in projectProfiles) {
|
|
2061
|
+
if (!areJsonValuesEqual(projectProfiles[profileName], globalProfile)) {
|
|
2062
|
+
throw new Error(`Project config already defines profile "${profileName}" with different settings; remove it or rename the project profile before adding preset "${presetName}"`);
|
|
2063
|
+
}
|
|
2064
|
+
} else {
|
|
2065
|
+
projectProfiles[profileName] = globalProfile;
|
|
2066
|
+
copiedProfiles.push(profileName);
|
|
2067
|
+
}
|
|
2068
|
+
normalizedPreset[slotName] = profileName;
|
|
2069
|
+
}
|
|
2070
|
+
projectPresets[presetName] = normalizedPreset;
|
|
2071
|
+
await writeRawVvcodeConfig(snapshot.projectConfigPath, projectConfig);
|
|
2072
|
+
dependencies.writeStdout(`vvcode: added preset "${presetName}" to ${snapshot.projectConfigPath} (copied ${copiedProfiles.length} profiles)`);
|
|
2073
|
+
}
|
|
2074
|
+
}),
|
|
2075
|
+
remove: defineCommand({
|
|
2076
|
+
meta: {
|
|
2077
|
+
name: "remove",
|
|
2078
|
+
description: "Remove one preset from the project config and prune unreferenced copied profiles."
|
|
2079
|
+
},
|
|
2080
|
+
args: {
|
|
2081
|
+
name: {
|
|
2082
|
+
type: "positional",
|
|
2083
|
+
description: "Preset name to remove from the project config.",
|
|
2084
|
+
required: true
|
|
2085
|
+
},
|
|
2086
|
+
"project-root": configManagementArgs["project-root"]
|
|
2087
|
+
},
|
|
2088
|
+
async run({ args }) {
|
|
2089
|
+
const presetName = String(args.name);
|
|
2090
|
+
const projectRoot = resolve(String(args.projectRoot));
|
|
2091
|
+
const projectConfigPath = resolveProjectConfigPath(projectRoot);
|
|
2092
|
+
const projectConfig = await readOptionalRawVvcodeConfig(projectConfigPath);
|
|
2093
|
+
if (projectConfig === null) {
|
|
2094
|
+
throw new Error(`Project config was not found at ${projectConfigPath}`);
|
|
2095
|
+
}
|
|
2096
|
+
const projectPresets = toMutableBucket(projectConfig, "presets");
|
|
2097
|
+
const projectProfiles = toMutableBucket(projectConfig, "profiles");
|
|
2098
|
+
const removedPreset = projectPresets[presetName];
|
|
2099
|
+
if (!isRecord3(removedPreset)) {
|
|
2100
|
+
throw new Error(`Preset "${presetName}" was not found in project config ${projectConfigPath}`);
|
|
2101
|
+
}
|
|
2102
|
+
delete projectPresets[presetName];
|
|
2103
|
+
const stillReferencedProfiles = new Set;
|
|
2104
|
+
for (const presetValue of Object.values(projectPresets)) {
|
|
2105
|
+
if (!isRecord3(presetValue)) {
|
|
2106
|
+
continue;
|
|
2107
|
+
}
|
|
2108
|
+
for (const profileNameValue of Object.values(presetValue)) {
|
|
2109
|
+
if (typeof profileNameValue === "string" && profileNameValue.length > 0) {
|
|
2110
|
+
stillReferencedProfiles.add(profileNameValue);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
const removedProfiles = [];
|
|
2115
|
+
for (const profileNameValue of Object.values(removedPreset)) {
|
|
2116
|
+
if (typeof profileNameValue !== "string" || profileNameValue.length === 0) {
|
|
2117
|
+
continue;
|
|
2118
|
+
}
|
|
2119
|
+
if (stillReferencedProfiles.has(profileNameValue)) {
|
|
2120
|
+
continue;
|
|
2121
|
+
}
|
|
2122
|
+
if (profileNameValue in projectProfiles) {
|
|
2123
|
+
delete projectProfiles[profileNameValue];
|
|
2124
|
+
removedProfiles.push(profileNameValue);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
await writeRawVvcodeConfig(projectConfigPath, projectConfig);
|
|
2128
|
+
dependencies.writeStdout(`vvcode: removed preset "${presetName}" from ${projectConfigPath} (removed ${removedProfiles.length} profiles)`);
|
|
2129
|
+
}
|
|
2130
|
+
})
|
|
2131
|
+
}
|
|
1790
2132
|
})
|
|
1791
2133
|
}
|
|
1792
2134
|
});
|
|
1793
2135
|
}
|
|
1794
2136
|
async function runVvcodeCli(rawArgs = process.argv.slice(2), dependencyOverrides = {}) {
|
|
1795
2137
|
const vvcodeCommand = createVvcodeCommand(dependencyOverrides);
|
|
1796
|
-
await runMain(vvcodeCommand, { rawArgs });
|
|
2138
|
+
await runMain(vvcodeCommand, { rawArgs: normalizeRawArgs(rawArgs) });
|
|
1797
2139
|
}
|
|
1798
2140
|
function resolveVvcodeCliDependencies(dependencyOverrides) {
|
|
1799
2141
|
return {
|
|
@@ -1801,6 +2143,7 @@ function resolveVvcodeCliDependencies(dependencyOverrides) {
|
|
|
1801
2143
|
cwd: dependencyOverrides.cwd ?? (() => process.cwd()),
|
|
1802
2144
|
defaultGlobalConfigPath: dependencyOverrides.defaultGlobalConfigPath ?? resolveDefaultGlobalConfigPath,
|
|
1803
2145
|
writeStderr: dependencyOverrides.writeStderr ?? ((message) => console.error(message)),
|
|
2146
|
+
writeStdout: dependencyOverrides.writeStdout ?? ((message) => console.log(message)),
|
|
1804
2147
|
setExitCode: dependencyOverrides.setExitCode ?? ((code) => {
|
|
1805
2148
|
process.exitCode = code;
|
|
1806
2149
|
})
|
|
@@ -1820,6 +2163,72 @@ function formatLaunchFailure(launchResult) {
|
|
|
1820
2163
|
}
|
|
1821
2164
|
return failureFields.join(" ");
|
|
1822
2165
|
}
|
|
2166
|
+
function normalizeRawArgs(rawArgs) {
|
|
2167
|
+
if (rawArgs.length === 0) {
|
|
2168
|
+
return ["help"];
|
|
2169
|
+
}
|
|
2170
|
+
if (KNOWN_TOP_LEVEL_COMMANDS.has(rawArgs[0])) {
|
|
2171
|
+
return rawArgs;
|
|
2172
|
+
}
|
|
2173
|
+
if (rawArgs.some((arg) => arg === "-p" || arg === "--preset" || arg.startsWith("--preset="))) {
|
|
2174
|
+
return ["opencode", ...rawArgs];
|
|
2175
|
+
}
|
|
2176
|
+
return rawArgs;
|
|
2177
|
+
}
|
|
2178
|
+
async function loadProjectConfigSnapshot(globalConfigPath, projectRoot) {
|
|
2179
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
2180
|
+
const projectConfigPath = resolveProjectConfigPath(normalizedProjectRoot);
|
|
2181
|
+
const [globalConfig, projectConfig] = await Promise.all([
|
|
2182
|
+
readOptionalRawVvcodeConfig(globalConfigPath),
|
|
2183
|
+
readOptionalRawVvcodeConfig(projectConfigPath)
|
|
2184
|
+
]);
|
|
2185
|
+
return {
|
|
2186
|
+
projectRoot: normalizedProjectRoot,
|
|
2187
|
+
projectConfigPath,
|
|
2188
|
+
globalConfigPath: resolve(globalConfigPath),
|
|
2189
|
+
globalConfig,
|
|
2190
|
+
projectConfig
|
|
2191
|
+
};
|
|
2192
|
+
}
|
|
2193
|
+
function collectPresetNames(snapshot) {
|
|
2194
|
+
const presetNames = new Set;
|
|
2195
|
+
for (const presetName of Object.keys(toObjectBucket(snapshot.globalConfig?.presets))) {
|
|
2196
|
+
presetNames.add(presetName);
|
|
2197
|
+
}
|
|
2198
|
+
for (const presetName of Object.keys(toObjectBucket(snapshot.projectConfig?.presets))) {
|
|
2199
|
+
presetNames.add(presetName);
|
|
2200
|
+
}
|
|
2201
|
+
return [...presetNames].sort((left, right) => left.localeCompare(right));
|
|
2202
|
+
}
|
|
2203
|
+
function describePresetSource(snapshot, presetName) {
|
|
2204
|
+
const hasGlobalPreset = presetName in toObjectBucket(snapshot.globalConfig?.presets);
|
|
2205
|
+
const hasProjectPreset = presetName in toObjectBucket(snapshot.projectConfig?.presets);
|
|
2206
|
+
if (hasProjectPreset && hasGlobalPreset) {
|
|
2207
|
+
return "project-overrides-global";
|
|
2208
|
+
}
|
|
2209
|
+
if (hasProjectPreset) {
|
|
2210
|
+
return "project";
|
|
2211
|
+
}
|
|
2212
|
+
return "global";
|
|
2213
|
+
}
|
|
2214
|
+
function toMutableBucket(config, key) {
|
|
2215
|
+
const bucket = config[key];
|
|
2216
|
+
if (isRecord3(bucket)) {
|
|
2217
|
+
return bucket;
|
|
2218
|
+
}
|
|
2219
|
+
const nextBucket = {};
|
|
2220
|
+
config[key] = nextBucket;
|
|
2221
|
+
return nextBucket;
|
|
2222
|
+
}
|
|
2223
|
+
function toObjectBucket(value) {
|
|
2224
|
+
return isRecord3(value) ? value : {};
|
|
2225
|
+
}
|
|
2226
|
+
function isRecord3(value) {
|
|
2227
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2228
|
+
}
|
|
2229
|
+
function areJsonValuesEqual(left, right) {
|
|
2230
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
2231
|
+
}
|
|
1823
2232
|
|
|
1824
2233
|
// src/cli/vvcode-entry.ts
|
|
1825
2234
|
await runVvcodeCli();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osovv/vvcode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Packaged CLI for launching OpenCode with vvcode runtime semantics.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/osovv/vvcode",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"bin": {
|
|
21
|
-
"vvcode": "
|
|
21
|
+
"vvcode": "bin/vvcode"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|