import { readFileSync } from 'node:fs';
import test from 'node:test';
import assert from 'node:assert/strict';
const SETUP_ROUTES = [
'src/app/api/ingest/setup/route.ts',
'src/app/api/ingest/setup/auto/route.ts',
];
test('Claude Code OTel setup preserves tool output sources in shell and PowerShell wrappers', () => {
for (const route of SETUP_ROUTES) {
const source = readFileSync(route, 'utf8');
assert.match(
source,
/OTEL_LOG_TOOL_DETAILS=1[\s\S]*OTEL_LOG_TOOL_CONTENT=1[\s\S]*OTEL_LOG_RAW_API_BODIES/,
`${route} should enable tool content in the shell wrapper`,
);
assert.match(
source,
/\$env:OTEL_LOG_TOOL_DETAILS = "1"[\s\S]*\$env:OTEL_LOG_TOOL_CONTENT = "1"[\s\S]*\$env:OTEL_LOG_RAW_API_BODIES/,
`${route} should enable tool content in the PowerShell wrapper`,
);
assert.match(
source,
/AGENT_INSIGHT_CLAUDE_OTEL_RAW_API_BODIES=file:[^\n]+claude_raw_bodies/,
`${route} should default raw API bodies to file mode`,
);
assert.ok(
source.includes('mkdir -p "$HOME/.agent-insight/claude_raw_bodies"'),
`${route} should create and use the shell raw body directory`,
);
assert.match(
source,
/OTEL_LOG_RAW_API_BODIES="\\?\$\{AGENT_INSIGHT_CLAUDE_OTEL_RAW_API_BODIES:-file:\$HOME\/\.agent-insight\/claude_raw_bodies\}"/,
`${route} should default the shell wrapper to raw body file mode`,
);
assert.ok(
source.includes('$rawBodyDir = Join-Path $env:USERPROFILE ".agent-insight\\\\claude_raw_bodies"') &&
source.includes('AGENT_INSIGHT_CLAUDE_OTEL_RAW_API_BODIES = "file:$rawBodyDir"'),
`${route} should create and use the PowerShell raw body directory`,
);
assert.ok(
source.includes('grep -q "\\\\.agent-insight/claude_otel_env\\\\.sh"'),
`${route} should not let a legacy .skill-insight shell source block the new wrapper`,
);
assert.ok(
!source.includes('grep -q "claude_otel_env.sh"'),
`${route} should not use a broad shell source check`,
);
assert.ok(
source.includes('profileText.Contains(".agent-insight\\\\claude_otel_env.ps1")'),
`${route} should not let a legacy .skill-insight PowerShell source block the new wrapper`,
);
assert.ok(
!source.includes('-match "claude_otel_env.ps1"'),
`${route} should not use a broad PowerShell profile check`,
);
}
});
test('setup scripts install the first-party Hermes plugin without GitHub or venv dependencies', () => {
for (const route of SETUP_ROUTES) {
const source = readFileSync(route, 'utf8');
assert.ok(
source.includes("{ name: 'Hermes', value: 'hermes' }") ||
source.includes("{ name: \\'Hermes\\', value: \\'hermes\\' }"),
`${route} should offer Hermes in the setup framework selector`,
);
assert.ok(source.includes('/api/setup/hermes-plugin'), `${route} should download the first-party plugin`);
assert.ok(source.includes('plugins enable agent_insight_hermes'), `${route} should enable the first-party plugin`);
assert.ok(
!source.includes('plugins disable hermes_otel'),
`${route} should leave independently configured Hermes plugins untouched`,
);
assert.match(
source,
/HERMES_HOME="\\?\$\{HERMES_HOME:-\$HOME\/\.hermes\}"/,
`${route} should center Unix Hermes discovery on HERMES_HOME`,
);
assert.ok(
source.includes('HERMES_PLUGIN_DIR="$HERMES_HOME/plugins/agent_insight_hermes"'),
`${route} should install the Unix plugin under HERMES_HOME`,
);
assert.ok(
source.includes('$hermesPluginDir = Join-Path $hermesHome "plugins\\\\agent_insight_hermes"'),
`${route} should install the PowerShell plugin under HERMES_HOME`,
);
assert.ok(
!source.includes('briancaffey/hermes-otel') &&
!source.includes('opentelemetry-exporter-otlp-proto-http'),
`${route} should not depend on GitHub or the OTel Python runtime`,
);
assert.ok(
source.includes('plugin.yaml') && source.includes('config.json'),
`${route} should write the Hermes manifest and JSON config`,
);
assert.ok(
source.includes('"max_content_chars": 200000') ||
source.includes('max_content_chars = 200000'),
`${route} should configure high-fidelity Hermes content capture`,
);
assert.ok(source.includes('api_request_error'), `${route} should register Hermes API error telemetry`);
assert.ok(
source.includes('hermes-otel-spool'),
`${route} should configure a durable Hermes snapshot spool`,
);
assert.ok(
source.includes('hermes-plugin.log'),
`${route} should configure the Hermes plugin log path`,
);
assert.ok(
source.includes('"api_key":') || source.includes('api_key ='),
`${route} should pass the platform API key to the plugin config`,
);
}
});
test('JiuwenSwarm setup writes a PowerShell-safe extension manifest', () => {
for (const route of SETUP_ROUTES) {
const source = readFileSync(route, 'utf8');
assert.ok(
source.includes('\\\'min_jiuwenswarm_version: "0.2.0"\\\''),
`${route} should quote YAML values with PowerShell single-quoted strings`,
);
assert.ok(
!source.includes('min_jiuwenswarm_version: \\\\"0.2.0\\\\"'),
`${route} should not emit bash-style escaped quotes in PowerShell strings`,
);
}
});
test('OpenCode setup installs the telemetry plugin only under the config directory', () => {
for (const route of SETUP_ROUTES) {
const source = readFileSync(route, 'utf8');
assert.ok(
source.includes('plugins/Witty-Skill-Insight.ts') || source.includes('plugins\\\\Witty-Skill-Insight.ts'),
`${route} should install the OpenCode plugin under the config plugins directory`,
);
assert.ok(
!source.includes('Join-Path $env:APPDATA "opencode"'),
`${route} should not install the Windows OpenCode plugin under AppData`,
);
assert.ok(
source.includes('.config\\\\opencode'),
`${route} should use the user .config OpenCode directory on Windows`,
);
assert.ok(
!source.includes('cp "$OPENCODE_CONFIG_DIR/plugins/Witty-Skill-Insight.ts" "$HOME/.opencode/plugins/Witty-Skill-Insight.ts"'),
`${route} should not copy the OpenCode plugin into ~/.opencode/plugins`,
);
assert.ok(
!source.includes('Copy-Item (Join-Path $opencodeConfigDir "plugins\\\\Witty-Skill-Insight.ts")'),
`${route} should not copy the OpenCode plugin into the legacy PowerShell directory`,
);
}
});
test('OpenCode telemetry plugin stays compatible with the desktop ESM loader', () => {
const source = readFileSync('scripts/opencode_plugin_otel.ts', 'utf8');
assert.match(source, /import fs from ['"]node:fs['"]/);
assert.match(source, /import path from ['"]node:path['"]/);
assert.match(source, /import os from ['"]node:os['"]/);
assert.match(source, /import crypto from ['"]node:crypto['"]/);
assert.match(source, /import \{ spawn \} from ['"]node:child_process['"]/);
assert.doesNotMatch(source, /\brequire\s*\(/);
assert.match(source, /export default async function WittySkillInsightOtelPlugin/);
});