@frenchtoastman/oh-my-groundcontrol 0.0.21 → 0.0.23
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 +24 -0
- package/dist/cli/config-io.d.ts +1 -1
- package/dist/cli/index.js +49 -19
- package/dist/cli/paths.d.ts +3 -3
- package/dist/cli/providers.d.ts +1 -1
- package/dist/cli/types.d.ts +2 -0
- package/dist/index.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,6 +50,30 @@ Verify agent connectivity by running `ping all agents` within the OpenCode termi
|
|
|
50
50
|
|
|
51
51
|
> **Note on Model Customization:** The active model matrix is fully configurable. Edit `~/.config/opencode/oh-my-groundcontrol.json` (or `.jsonc`) to override agent-to-model assignments manually.
|
|
52
52
|
|
|
53
|
+
### Langfuse Observability (Optional)
|
|
54
|
+
|
|
55
|
+
Langfuse tracing is available as an optional, toggleable feature for observability and debugging. When enabled, it injects metadata into LLM requests including:
|
|
56
|
+
|
|
57
|
+
- **Agent tags** (e.g., `agent:explorer`, `task:research`)
|
|
58
|
+
- **Session ID** for request correlation
|
|
59
|
+
- **Trace user ID** (configurable, defaults to `opencode`)
|
|
60
|
+
- **Model and provider information**
|
|
61
|
+
- **Plugin version** for tracking
|
|
62
|
+
|
|
63
|
+
To enable Langfuse tracing, add the following to your configuration:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"langfuse_tracing": {
|
|
68
|
+
"enabled": true,
|
|
69
|
+
"traceUserId": "opencode",
|
|
70
|
+
"customMetadata": {}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The feature is **disabled by default** and must be explicitly enabled in `~/.config/opencode/oh-my-groundcontrol.json`.
|
|
76
|
+
|
|
53
77
|
### For AI Agents / LLMs
|
|
54
78
|
|
|
55
79
|
To instruct an external coding agent to utilize this suite, paste this block into the agent's prompt:
|
package/dist/cli/config-io.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare function parseConfig(path: string): {
|
|
|
16
16
|
*/
|
|
17
17
|
export declare function writeConfig(configPath: string, config: OpenCodeConfig): void;
|
|
18
18
|
export declare function addPluginToOpenCodeConfig(): Promise<ConfigMergeResult>;
|
|
19
|
-
export declare function
|
|
19
|
+
export declare function writeGroundConfig(installConfig: InstallConfig): ConfigMergeResult;
|
|
20
20
|
export declare function disableDefaultAgents(): ConfigMergeResult;
|
|
21
21
|
export declare function canModifyOpenCodeConfig(): boolean;
|
|
22
22
|
export declare function addAntigravityPlugin(): ConfigMergeResult;
|
package/dist/cli/index.js
CHANGED
|
@@ -102,7 +102,7 @@ function getConfigJson() {
|
|
|
102
102
|
function getConfigJsonc() {
|
|
103
103
|
return join(getConfigDir(), "opencode.jsonc");
|
|
104
104
|
}
|
|
105
|
-
function
|
|
105
|
+
function getGroundConfig() {
|
|
106
106
|
return join(getConfigDir(), "oh-my-groundcontrol.json");
|
|
107
107
|
}
|
|
108
108
|
function getExistingConfigPath() {
|
|
@@ -14085,7 +14085,7 @@ function generateAntigravityMixedPreset(config2, existingPreset) {
|
|
|
14085
14085
|
}
|
|
14086
14086
|
return result;
|
|
14087
14087
|
}
|
|
14088
|
-
function
|
|
14088
|
+
function generateGroundConfig(installConfig) {
|
|
14089
14089
|
const config2 = {
|
|
14090
14090
|
preset: "zen-free",
|
|
14091
14091
|
presets: {},
|
|
@@ -14125,6 +14125,13 @@ function generateLiteConfig(installConfig) {
|
|
|
14125
14125
|
main_pane_size: 60
|
|
14126
14126
|
};
|
|
14127
14127
|
}
|
|
14128
|
+
if (installConfig.enableLangfuseTracing) {
|
|
14129
|
+
config2.langfuse_tracing = {
|
|
14130
|
+
enabled: true,
|
|
14131
|
+
traceUserId: "opencode",
|
|
14132
|
+
customMetadata: {}
|
|
14133
|
+
};
|
|
14134
|
+
}
|
|
14128
14135
|
return config2;
|
|
14129
14136
|
}
|
|
14130
14137
|
let activePreset = "zen-free";
|
|
@@ -14182,6 +14189,13 @@ function generateLiteConfig(installConfig) {
|
|
|
14182
14189
|
main_pane_size: 60
|
|
14183
14190
|
};
|
|
14184
14191
|
}
|
|
14192
|
+
if (installConfig.enableLangfuseTracing) {
|
|
14193
|
+
config2.langfuse_tracing = {
|
|
14194
|
+
enabled: true,
|
|
14195
|
+
traceUserId: "opencode",
|
|
14196
|
+
customMetadata: {}
|
|
14197
|
+
};
|
|
14198
|
+
}
|
|
14185
14199
|
return config2;
|
|
14186
14200
|
}
|
|
14187
14201
|
const applyOpenCodeFreeAssignments = (presetAgents, hasExternalProviders) => {
|
|
@@ -14306,6 +14320,13 @@ function generateLiteConfig(installConfig) {
|
|
|
14306
14320
|
main_pane_size: 60
|
|
14307
14321
|
};
|
|
14308
14322
|
}
|
|
14323
|
+
if (installConfig.enableLangfuseTracing) {
|
|
14324
|
+
config2.langfuse_tracing = {
|
|
14325
|
+
enabled: true,
|
|
14326
|
+
traceUserId: "opencode",
|
|
14327
|
+
customMetadata: {}
|
|
14328
|
+
};
|
|
14329
|
+
}
|
|
14309
14330
|
return config2;
|
|
14310
14331
|
}
|
|
14311
14332
|
|
|
@@ -14391,11 +14412,11 @@ async function addPluginToOpenCodeConfig() {
|
|
|
14391
14412
|
};
|
|
14392
14413
|
}
|
|
14393
14414
|
}
|
|
14394
|
-
function
|
|
14395
|
-
const configPath =
|
|
14415
|
+
function writeGroundConfig(installConfig) {
|
|
14416
|
+
const configPath = getGroundConfig();
|
|
14396
14417
|
try {
|
|
14397
14418
|
ensureConfigDir();
|
|
14398
|
-
const config2 =
|
|
14419
|
+
const config2 = generateGroundConfig(installConfig);
|
|
14399
14420
|
const tmpPath = `${configPath}.tmp`;
|
|
14400
14421
|
const bakPath = `${configPath}.bak`;
|
|
14401
14422
|
const content = `${JSON.stringify(config2, null, 2)}
|
|
@@ -14410,7 +14431,7 @@ function writeLiteConfig(installConfig) {
|
|
|
14410
14431
|
return {
|
|
14411
14432
|
success: false,
|
|
14412
14433
|
configPath,
|
|
14413
|
-
error: `Failed to write
|
|
14434
|
+
error: `Failed to write ground config: ${err}`
|
|
14414
14435
|
};
|
|
14415
14436
|
}
|
|
14416
14437
|
}
|
|
@@ -14607,9 +14628,9 @@ function detectCurrentConfig() {
|
|
|
14607
14628
|
result.hasChutes = !!providers?.chutes;
|
|
14608
14629
|
if (providers?.google)
|
|
14609
14630
|
result.hasAntigravity = true;
|
|
14610
|
-
const { config:
|
|
14611
|
-
if (
|
|
14612
|
-
const configObj =
|
|
14631
|
+
const { config: groundConfig } = parseConfig(getGroundConfig());
|
|
14632
|
+
if (groundConfig && typeof groundConfig === "object") {
|
|
14633
|
+
const configObj = groundConfig;
|
|
14613
14634
|
const presetName = configObj.preset;
|
|
14614
14635
|
const presets = configObj.presets;
|
|
14615
14636
|
const agents = presets?.[presetName];
|
|
@@ -16235,8 +16256,8 @@ function handleStepResult(result, successMsg) {
|
|
|
16235
16256
|
return true;
|
|
16236
16257
|
}
|
|
16237
16258
|
function formatConfigSummary(config2) {
|
|
16238
|
-
const
|
|
16239
|
-
const preset =
|
|
16259
|
+
const groundConfig = generateGroundConfig(config2);
|
|
16260
|
+
const preset = groundConfig.preset || "unknown";
|
|
16240
16261
|
const lines = [];
|
|
16241
16262
|
lines.push(`${BOLD}Configuration Summary${RESET}`);
|
|
16242
16263
|
lines.push("");
|
|
@@ -16263,13 +16284,14 @@ function formatConfigSummary(config2) {
|
|
|
16263
16284
|
lines.push(` ${SYMBOLS.check} Chutes Support: ${BLUE}${config2.selectedChutesSecondaryModel}${RESET}`);
|
|
16264
16285
|
}
|
|
16265
16286
|
lines.push(` ${config2.hasTmux ? SYMBOLS.check : `${DIM}\u25CB${RESET}`} Tmux Integration`);
|
|
16287
|
+
lines.push(` ${config2.enableLangfuseTracing ? SYMBOLS.check : `${DIM}\u25CB${RESET}`} Langfuse Tracing`);
|
|
16266
16288
|
return lines.join(`
|
|
16267
16289
|
`);
|
|
16268
16290
|
}
|
|
16269
16291
|
function printAgentModels(config2) {
|
|
16270
|
-
const
|
|
16271
|
-
const presetName =
|
|
16272
|
-
const presets =
|
|
16292
|
+
const groundConfig = generateGroundConfig(config2);
|
|
16293
|
+
const presetName = groundConfig.preset || "unknown";
|
|
16294
|
+
const presets = groundConfig.presets;
|
|
16273
16295
|
const agents = presets?.[presetName];
|
|
16274
16296
|
if (!agents || Object.keys(agents).length === 0)
|
|
16275
16297
|
return;
|
|
@@ -16299,6 +16321,7 @@ function argsToConfig(args) {
|
|
|
16299
16321
|
openRouterApiKey: args.openrouterKey,
|
|
16300
16322
|
balanceProviderUsage: args.balancedSpend === "yes",
|
|
16301
16323
|
hasTmux: args.tmux === "yes",
|
|
16324
|
+
enableLangfuseTracing: args.langfuse === "yes",
|
|
16302
16325
|
installSkills: args.skills === "yes",
|
|
16303
16326
|
installCustomSkills: args.skills === "yes",
|
|
16304
16327
|
setupMode: "quick",
|
|
@@ -16557,6 +16580,8 @@ async function runManualSetupMode(rl, detected, modelsOnly = false) {
|
|
|
16557
16580
|
}
|
|
16558
16581
|
const balancedSpend = await askYesNo(rl, "Do you have subscriptions or pay per API? If yes, we will distribute assignments evenly across selected providers so your subscriptions last longer.", "no");
|
|
16559
16582
|
console.log();
|
|
16583
|
+
const enableLangfuse = await askYesNo(rl, "Enable Langfuse tracing for observability and debugging? This sends metadata (agent tags, session ID, model info) to Langfuse.", "no");
|
|
16584
|
+
console.log();
|
|
16560
16585
|
let skills = "no";
|
|
16561
16586
|
let customSkills = "no";
|
|
16562
16587
|
if (!modelsOnly) {
|
|
@@ -16597,6 +16622,7 @@ async function runManualSetupMode(rl, detected, modelsOnly = false) {
|
|
|
16597
16622
|
openRouterApiKey,
|
|
16598
16623
|
balanceProviderUsage: balancedSpend === "yes",
|
|
16599
16624
|
hasTmux: false,
|
|
16625
|
+
enableLangfuseTracing: enableLangfuse === "yes",
|
|
16600
16626
|
installSkills: skills === "yes",
|
|
16601
16627
|
installCustomSkills: customSkills === "yes",
|
|
16602
16628
|
setupMode: "manual",
|
|
@@ -16620,7 +16646,7 @@ async function runInteractiveMode(detected, modelsOnly = false) {
|
|
|
16620
16646
|
rl.close();
|
|
16621
16647
|
return config2;
|
|
16622
16648
|
}
|
|
16623
|
-
const totalQuestions =
|
|
16649
|
+
const totalQuestions = 12;
|
|
16624
16650
|
const existingAaKey = getEnv("ARTIFICIAL_ANALYSIS_API_KEY");
|
|
16625
16651
|
const existingOpenRouterKey = getEnv("OPENROUTER_API_KEY");
|
|
16626
16652
|
console.log(`${BOLD}Question 1/${totalQuestions}:${RESET}`);
|
|
@@ -16711,6 +16737,9 @@ async function runInteractiveMode(detected, modelsOnly = false) {
|
|
|
16711
16737
|
console.log(`${BOLD}Question 11/${totalQuestions}:${RESET}`);
|
|
16712
16738
|
const balancedSpend = await askYesNo(rl, "Do you have subscriptions or pay per API? If yes, we will distribute assignments evenly across selected providers so your subscriptions last longer.", "no");
|
|
16713
16739
|
console.log();
|
|
16740
|
+
console.log(`${BOLD}Question 12/${totalQuestions}:${RESET}`);
|
|
16741
|
+
const enableLangfuse = await askYesNo(rl, "Enable Langfuse tracing for observability and debugging? This sends metadata (agent tags, session ID, model info) to Langfuse.", "no");
|
|
16742
|
+
console.log();
|
|
16714
16743
|
let skills = "no";
|
|
16715
16744
|
let customSkills = "no";
|
|
16716
16745
|
if (!modelsOnly) {
|
|
@@ -16755,6 +16784,7 @@ async function runInteractiveMode(detected, modelsOnly = false) {
|
|
|
16755
16784
|
openRouterApiKey,
|
|
16756
16785
|
balanceProviderUsage: balancedSpend === "yes",
|
|
16757
16786
|
hasTmux: false,
|
|
16787
|
+
enableLangfuseTracing: enableLangfuse === "yes",
|
|
16758
16788
|
installSkills: skills === "yes",
|
|
16759
16789
|
installCustomSkills: customSkills === "yes",
|
|
16760
16790
|
setupMode: "quick",
|
|
@@ -16910,14 +16940,14 @@ async function runInstall(config2) {
|
|
|
16910
16940
|
}
|
|
16911
16941
|
printStep(step++, totalSteps, "Writing oh-my-groundcontrol configuration...");
|
|
16912
16942
|
if (resolvedConfig.dryRun) {
|
|
16913
|
-
const
|
|
16943
|
+
const groundConfig = generateGroundConfig(resolvedConfig);
|
|
16914
16944
|
printInfo("Dry run mode - configuration that would be written:");
|
|
16915
16945
|
console.log(`
|
|
16916
|
-
${JSON.stringify(
|
|
16946
|
+
${JSON.stringify(groundConfig, null, 2)}
|
|
16917
16947
|
`);
|
|
16918
16948
|
} else {
|
|
16919
|
-
const
|
|
16920
|
-
if (!handleStepResult(
|
|
16949
|
+
const groundResult = writeGroundConfig(resolvedConfig);
|
|
16950
|
+
if (!handleStepResult(groundResult, "Config written"))
|
|
16921
16951
|
return 1;
|
|
16922
16952
|
}
|
|
16923
16953
|
if (!modelsOnly && resolvedConfig.installSkills) {
|
package/dist/cli/paths.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export declare function getConfigDir(): string;
|
|
|
2
2
|
export declare function getOpenCodeConfigPaths(): string[];
|
|
3
3
|
export declare function getConfigJson(): string;
|
|
4
4
|
export declare function getConfigJsonc(): string;
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
5
|
+
export declare function getGroundConfig(): string;
|
|
6
|
+
export declare function getGroundConfigJsonc(): string;
|
|
7
|
+
export declare function getExistingGroundConfigPath(): string;
|
|
8
8
|
export declare function getExistingConfigPath(): string;
|
|
9
9
|
export declare function ensureConfigDir(): void;
|
package/dist/cli/providers.d.ts
CHANGED
|
@@ -201,4 +201,4 @@ export declare const MODEL_MAPPINGS: {
|
|
|
201
201
|
};
|
|
202
202
|
};
|
|
203
203
|
export declare function generateAntigravityMixedPreset(config: InstallConfig, existingPreset?: Record<string, unknown>): Record<string, unknown>;
|
|
204
|
-
export declare function
|
|
204
|
+
export declare function generateGroundConfig(installConfig: InstallConfig): Record<string, unknown>;
|
package/dist/cli/types.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface InstallArgs {
|
|
|
12
12
|
skills?: BooleanArg;
|
|
13
13
|
opencodeFree?: BooleanArg;
|
|
14
14
|
balancedSpend?: BooleanArg;
|
|
15
|
+
langfuse?: BooleanArg;
|
|
15
16
|
opencodeFreeModel?: string;
|
|
16
17
|
aaKey?: string;
|
|
17
18
|
openrouterKey?: string;
|
|
@@ -112,6 +113,7 @@ export interface InstallConfig {
|
|
|
112
113
|
openRouterApiKey?: string;
|
|
113
114
|
balanceProviderUsage?: boolean;
|
|
114
115
|
hasTmux: boolean;
|
|
116
|
+
enableLangfuseTracing?: boolean;
|
|
115
117
|
installSkills: boolean;
|
|
116
118
|
installCustomSkills: boolean;
|
|
117
119
|
setupMode: 'quick' | 'manual';
|
package/dist/index.js
CHANGED
|
@@ -23169,12 +23169,12 @@ function deriveTask(agent) {
|
|
|
23169
23169
|
return AGENT_TASK_MAP[agent] ?? "general";
|
|
23170
23170
|
}
|
|
23171
23171
|
function deriveTags(agent) {
|
|
23172
|
-
const tags = [
|
|
23172
|
+
const tags = [];
|
|
23173
23173
|
if (agent)
|
|
23174
|
-
tags.push(agent);
|
|
23174
|
+
tags.push(`agent:${agent}`);
|
|
23175
23175
|
const task = deriveTask(agent);
|
|
23176
23176
|
if (task)
|
|
23177
|
-
tags.push(task);
|
|
23177
|
+
tags.push(`task:${task}`);
|
|
23178
23178
|
return tags;
|
|
23179
23179
|
}
|
|
23180
23180
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frenchtoastman/oh-my-groundcontrol",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "An OpenCode plugin for multi-agent orchestration for structured planning with NASA-style guardrails.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|