@frenchtoastman/oh-my-groundcontrol 0.0.22 → 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 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:
@@ -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 writeLiteConfig(installConfig: InstallConfig): ConfigMergeResult;
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 getLiteConfig() {
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 generateLiteConfig(installConfig) {
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 writeLiteConfig(installConfig) {
14395
- const configPath = getLiteConfig();
14415
+ function writeGroundConfig(installConfig) {
14416
+ const configPath = getGroundConfig();
14396
14417
  try {
14397
14418
  ensureConfigDir();
14398
- const config2 = generateLiteConfig(installConfig);
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 lite config: ${err}`
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: liteConfig } = parseConfig(getLiteConfig());
14611
- if (liteConfig && typeof liteConfig === "object") {
14612
- const configObj = liteConfig;
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 liteConfig = generateLiteConfig(config2);
16239
- const preset = liteConfig.preset || "unknown";
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 liteConfig = generateLiteConfig(config2);
16271
- const presetName = liteConfig.preset || "unknown";
16272
- const presets = liteConfig.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 = 11;
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 liteConfig = generateLiteConfig(resolvedConfig);
16943
+ const groundConfig = generateGroundConfig(resolvedConfig);
16914
16944
  printInfo("Dry run mode - configuration that would be written:");
16915
16945
  console.log(`
16916
- ${JSON.stringify(liteConfig, null, 2)}
16946
+ ${JSON.stringify(groundConfig, null, 2)}
16917
16947
  `);
16918
16948
  } else {
16919
- const liteResult = writeLiteConfig(resolvedConfig);
16920
- if (!handleStepResult(liteResult, "Config written"))
16949
+ const groundResult = writeGroundConfig(resolvedConfig);
16950
+ if (!handleStepResult(groundResult, "Config written"))
16921
16951
  return 1;
16922
16952
  }
16923
16953
  if (!modelsOnly && resolvedConfig.installSkills) {
@@ -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 getLiteConfig(): string;
6
- export declare function getLiteConfigJsonc(): string;
7
- export declare function getExistingLiteConfigPath(): string;
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;
@@ -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 generateLiteConfig(installConfig: InstallConfig): Record<string, unknown>;
204
+ export declare function generateGroundConfig(installConfig: InstallConfig): Record<string, unknown>;
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frenchtoastman/oh-my-groundcontrol",
3
- "version": "0.0.22",
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",