oh-aicoding-tool 0.1.0

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.
Files changed (55) hide show
  1. package/CODEX_LANGFUSE_PLAN.md +62 -0
  2. package/README.md +114 -0
  3. package/bin/cli.js +166 -0
  4. package/bin/langfuse-cli.js +718 -0
  5. package/codex_langfuse_notify.py +591 -0
  6. package/langfuse_hook.py +603 -0
  7. package/opencode-ohai-report/.claude/commands/report-ai-issue.md +60 -0
  8. package/opencode-ohai-report/.opencode/commands/report-ai-issue.md +30 -0
  9. package/opencode-ohai-report/.opencode/plugins/oh-ai-report.ts +569 -0
  10. package/opencode-ohai-report/README.md +45 -0
  11. package/opencode-ohai-report/bin/cli.js +421 -0
  12. package/opencode-ohai-report/docs/opencode-ai-issue-collection-architecture.md +313 -0
  13. package/opencode-ohai-report/docs/opencode-ai-issue-collection-best-practices.md +476 -0
  14. package/opencode-ohai-report/docs/opencode-ai-issue-collection-phase1-summary.md +405 -0
  15. package/opencode-ohai-report/examples/issue_output.json +4 -0
  16. package/opencode-ohai-report/package.json +40 -0
  17. package/opencode-ohai-report/scripts/claude_report_hook.py +257 -0
  18. package/opencode-ohai-report/scripts/create_issue.py +34 -0
  19. package/opencode-ohai-report/scripts/install-claude-plugin.ps1 +254 -0
  20. package/opencode-ohai-report/scripts/install-opencode-plugin.ps1 +264 -0
  21. package/opencode-ohai-report/scripts/install-opencode-plugin.sh +218 -0
  22. package/opencode-ohai-report/scripts/merge-claude-settings.py +99 -0
  23. package/opencode-ohai-report/tools/ohai-report/README.md +151 -0
  24. package/opencode-ohai-report/tools/ohai-report/examples/issue-input.json +26 -0
  25. package/opencode-ohai-report/tools/ohai-report/ohai_report/__init__.py +5 -0
  26. package/opencode-ohai-report/tools/ohai-report/ohai_report/__main__.py +9 -0
  27. package/opencode-ohai-report/tools/ohai-report/ohai_report/cli.py +319 -0
  28. package/opencode-ohai-report/tools/ohai-report/ohai_report/git_context.py +32 -0
  29. package/opencode-ohai-report/tools/ohai-report/ohai_report/gitcode_defaults.py +14 -0
  30. package/opencode-ohai-report/tools/ohai-report/ohai_report/issue_markdown.py +313 -0
  31. package/opencode-ohai-report/tools/ohai-report/ohai_report/metadata.py +360 -0
  32. package/opencode-ohai-report/tools/ohai-report/ohai_report/observability/__init__.py +1 -0
  33. package/opencode-ohai-report/tools/ohai-report/ohai_report/observability/langfuse.py +38 -0
  34. package/opencode-ohai-report/tools/ohai-report/ohai_report/payload.py +64 -0
  35. package/opencode-ohai-report/tools/ohai-report/ohai_report/schema.py +80 -0
  36. package/opencode-ohai-report/tools/ohai-report/ohai_report/sinks/__init__.py +1 -0
  37. package/opencode-ohai-report/tools/ohai-report/ohai_report/sinks/base.py +15 -0
  38. package/opencode-ohai-report/tools/ohai-report/ohai_report/sinks/gitcode.py +405 -0
  39. package/opencode-ohai-report/tools/ohai-report/ohai_report/sinks/local.py +21 -0
  40. package/opencode-ohai-report/tools/ohai-report/ohai_report/sinks/webhook.py +354 -0
  41. package/opencode-ohai-report/tools/ohai-report/ohai_report/webhook_defaults.py +9 -0
  42. package/opencode-ohai-report/tools/ohai-report/ohai_report/workspace.py +61 -0
  43. package/opencode-ohai-report/tools/ohai-report/ohai_report.py +10 -0
  44. package/opencode-ohai-report/tools/ohai-report/schemas/report_issue.schema.json +166 -0
  45. package/package.json +59 -0
  46. package/scripts/codex-langfuse-check.mjs +101 -0
  47. package/scripts/codex-langfuse-setup.mjs +181 -0
  48. package/scripts/langfuse-check.mjs +90 -0
  49. package/scripts/langfuse-setup.mjs +278 -0
  50. package/scripts/opencode-langfuse-check.mjs +94 -0
  51. package/scripts/opencode-langfuse-run.mjs +96 -0
  52. package/scripts/opencode-langfuse-setup.mjs +478 -0
  53. package/scripts/resolve-opencode-cli.mjs +58 -0
  54. package/setup-langfuse.bat +163 -0
  55. package/setup-langfuse.sh +130 -0
@@ -0,0 +1,62 @@
1
+ # Codex Langfuse implementation plan
2
+
3
+ ## Goal
4
+
5
+ Add Codex support without depending on an unstable Codex plugin API. The first
6
+ version uses Codex's `notify` lifecycle command to trigger an exporter after a
7
+ turn, then the exporter reads Codex session JSONL files and sends the new events
8
+ to Langfuse.
9
+
10
+ ## Architecture
11
+
12
+ ```text
13
+ Codex turn ends
14
+ -> ~/.codex/config.toml notify command
15
+ -> ~/.codex/hooks/codex_langfuse_notify.py
16
+ -> incremental read of ~/.codex/sessions/**/*.jsonl
17
+ -> Langfuse trace / generation / tool observations
18
+ -> ~/.codex/langfuse/state.json stores offsets
19
+ ```
20
+
21
+ ## Files
22
+
23
+ - `codex_langfuse_notify.py`: Python notify hook and JSONL exporter.
24
+ - `scripts/codex-langfuse-setup.mjs`: installs the hook, writes Langfuse
25
+ config, creates `~/.codex/langfuse-venv`, updates `~/.codex/config.toml`,
26
+ and installs the Python SDK inside the venv.
27
+ - `scripts/codex-langfuse-check.mjs`: validates hook/config/session/package
28
+ availability.
29
+ - `bin/cli.js`: adds `setup codex` and `check codex`.
30
+ - `package.json`: adds Codex npm scripts and publishes the hook file.
31
+
32
+ ## Commands
33
+
34
+ ```sh
35
+ npm run codex:setup -- --userId=h00613222
36
+ npm run codex:check
37
+ ```
38
+
39
+ or:
40
+
41
+ ```sh
42
+ node bin/cli.js setup codex
43
+ node bin/cli.js check codex
44
+ ```
45
+
46
+ Restart Codex after setup so it reloads `~/.codex/config.toml`.
47
+
48
+ ## Behavior
49
+
50
+ - The exporter reads Langfuse keys from environment variables first, then from
51
+ `~/.codex/langfuse/config.json`.
52
+ - It prefers a session path from the notify payload when Codex provides one.
53
+ If no path is present, it falls back to the latest session JSONL file.
54
+ - It fails open. Errors are logged to
55
+ `~/.codex/langfuse/codex_langfuse_notify.log` and never block Codex.
56
+
57
+ ## Future improvements
58
+
59
+ - If Codex exposes a stable OTEL or plugin trace API, add it as an optional
60
+ second backend.
61
+ - Split shared Langfuse emit logic from the Claude and Codex Python hooks.
62
+ - Add a redaction layer before sending tool output to Langfuse.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # oh-aicoding-tool
2
+
3
+ One npm package for AI coding tooling:
4
+
5
+ - Langfuse tracing setup for Claude Code, OpenCode, and Codex.
6
+ - `oh-ai-report` issue feedback plugin installer for OpenCode and Claude Code.
7
+
8
+ ## Usage
9
+
10
+ Run locally from this project:
11
+
12
+ ```sh
13
+ node bin/cli.js
14
+ node bin/cli.js langfuse
15
+ node bin/cli.js report doctor
16
+ ```
17
+
18
+ After publishing to npm:
19
+
20
+ ```sh
21
+ npx oh-aicoding-tool
22
+ npx oh-aicoding-tool langfuse setup
23
+ npx oh-aicoding-tool report install opencode --email user@company.com
24
+ ```
25
+
26
+ Running `npx oh-aicoding-tool` with no subcommand opens a guided menu. Choose
27
+ Langfuse tracing or the issue report plugin; the report path asks for the
28
+ install target and guides company email configuration before installing.
29
+
30
+ ## Commands
31
+
32
+ ```sh
33
+ oh-aicoding-tool # guided menu
34
+ oh-aicoding-tool langfuse setup
35
+ oh-aicoding-tool langfuse setup claude
36
+ oh-aicoding-tool langfuse setup opencode
37
+ oh-aicoding-tool langfuse setup codex
38
+ oh-aicoding-tool langfuse check
39
+ oh-aicoding-tool langfuse check environment
40
+ oh-aicoding-tool langfuse check claude
41
+ oh-aicoding-tool langfuse check opencode
42
+ oh-aicoding-tool langfuse check codex
43
+ oh-aicoding-tool report install opencode
44
+ oh-aicoding-tool report install claude
45
+ oh-aicoding-tool report install both
46
+ oh-aicoding-tool report doctor
47
+ ```
48
+
49
+ Compatibility binaries are still published:
50
+
51
+ ```sh
52
+ code-tool-langfuse
53
+ opencode-ohai-report
54
+ ohai-report
55
+ ```
56
+
57
+ ## Langfuse
58
+
59
+ The interactive UI supports arrow-key navigation in terminals that support raw input. Use `Up` / `Down` to move, number keys for quick selection, `Enter` to select, and `q` or `Esc` to exit. In multi-select setup screens, use `Space` to toggle targets and `Enter` to continue. It uses a soft color palette and falls back to plain numeric prompts when raw terminal input is unavailable.
60
+
61
+ `Setup Langfuse` supports selecting one or more setup targets:
62
+
63
+ - Claude Code Langfuse
64
+ - OpenCode Langfuse
65
+ - Codex Langfuse
66
+
67
+ During setup, the only required input is `User ID`, which should be your employee number, for example `h00613222`. Other setup values use the built-in defaults.
68
+
69
+ Before setup, the CLI checks required local tools and prints install hints when something is missing:
70
+
71
+ - Node.js / npm
72
+ - Python / pip for Claude Code setup
73
+ - Python / pip for Codex setup
74
+ - OpenCode CLI for OpenCode setup
75
+
76
+ Python package installation uses pip's default package index by default. If you need a custom PyPI mirror, set `CODE_TOOL_PIP_INDEX_URL` or pass `--pipIndexUrl=...`.
77
+
78
+ Use dry-run mode to preview the script calls without modifying files, installing packages, or writing environment variables:
79
+
80
+ ```sh
81
+ oh-aicoding-tool langfuse setup --dry-run
82
+ ```
83
+
84
+ ## Issue Report Plugin
85
+
86
+ The report installer copies the packaged runtime to `~/.config/ohai-report/runtime`, installs OpenCode/Claude command files, and sets `OHAI_REPORT_CLI` to a stable path outside the temporary npx cache.
87
+
88
+ The guided menu prompts for company email and saves it into the corresponding
89
+ tool config so issue reports can carry a stable user identity.
90
+
91
+ ```sh
92
+ oh-aicoding-tool report install opencode --email user@company.com
93
+ oh-aicoding-tool report install claude --email user@company.com
94
+ oh-aicoding-tool report install both --email user@company.com
95
+ oh-aicoding-tool report doctor
96
+ ```
97
+
98
+ ## What It Configures
99
+
100
+ For Claude Code Langfuse setup, the CLI installs `langfuse_hook.py`, creates `~/.claude/langfuse-venv`, installs `langfuse` into that virtual environment, updates `~/.claude/settings.json`, and configures the hook environment. On Ubuntu/Debian, install `python3-venv` first if the setup reports that venv support is missing.
101
+
102
+ For OpenCode Langfuse setup, the CLI installs and patches `opencode-plugin-langfuse`, updates `~/.config/opencode/opencode.json`, and can write `LANGFUSE_*` values to the user environment.
103
+
104
+ For Codex Langfuse setup, the CLI installs `codex_langfuse_notify.py`, creates `~/.codex/langfuse-venv`, installs `langfuse` into that virtual environment, writes Langfuse credentials to `~/.codex/langfuse/config.json`, and updates the top-level `notify` command in `~/.codex/config.toml`. Restart Codex after setup so the notify command is loaded.
105
+
106
+ The Codex integration is implemented as a notify-hook exporter. On each notify event it incrementally reads the matching `~/.codex/sessions/**/*.jsonl` file, converts new user/assistant/tool/token events into Langfuse observations, and then saves its offset in `~/.codex/langfuse/state.json`. If Codex does not provide a session path in the notify payload, the exporter falls back to the latest session JSONL file.
107
+
108
+ ## Notes
109
+
110
+ - Langfuse Base URL, Public Key, and Secret Key have built-in defaults for this internal setup. You can override them with `LANGFUSE_BASEURL`, `LANGFUSE_PUBLIC_KEY`, and `LANGFUSE_SECRET_KEY`.
111
+ - The default secret key is not printed in the terminal UI.
112
+ - The CLI calls the Node setup/check scripts directly, so it avoids PowerShell `npm.ps1` execution-policy failures.
113
+ - The Python hook reads `userId` from hook payload first, then from configured environment variables.
114
+ - The Codex hook fails open: tracing errors are logged to `~/.codex/langfuse/codex_langfuse_notify.log` and do not block Codex.
package/bin/cli.js ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+ import { createInterface } from "node:readline/promises";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+
9
+ function runNodeScript(script, args) {
10
+ const result = spawnSync(process.execPath, [script, ...args], {
11
+ stdio: "inherit",
12
+ windowsHide: true,
13
+ });
14
+ return result.status ?? (result.error ? 1 : 0);
15
+ }
16
+
17
+ function printHelp() {
18
+ console.log("oh-aicoding-tool");
19
+ console.log("");
20
+ console.log("Usage:");
21
+ console.log(" oh-aicoding-tool Open the installer menu");
22
+ console.log(" oh-aicoding-tool langfuse [setup|check] [target]");
23
+ console.log(" oh-aicoding-tool report install opencode [--email user@company.com]");
24
+ console.log(" oh-aicoding-tool report install claude");
25
+ console.log(" oh-aicoding-tool report install both");
26
+ console.log(" oh-aicoding-tool report doctor");
27
+ console.log("");
28
+ console.log("Compatibility bins:");
29
+ console.log(" code-tool-langfuse");
30
+ console.log(" opencode-ohai-report");
31
+ console.log(" ohai-report");
32
+ }
33
+
34
+ function emailIsValid(value) {
35
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value || "").trim());
36
+ }
37
+
38
+ async function askChoice(rl, title, choices) {
39
+ console.log("");
40
+ console.log(title);
41
+ choices.forEach((choice, index) => {
42
+ console.log(` ${index + 1}. ${choice.label}`);
43
+ if (choice.description) console.log(` ${choice.description}`);
44
+ });
45
+ while (true) {
46
+ const answer = (await rl.question("> ")).trim().toLowerCase();
47
+ const index = Number.parseInt(answer, 10) - 1;
48
+ if (Number.isInteger(index) && choices[index]) return choices[index].value;
49
+ const byValue = choices.find((choice) => choice.value === answer);
50
+ if (byValue) return byValue.value;
51
+ console.log("Please choose one of the listed options.");
52
+ }
53
+ }
54
+
55
+ const langfuseCli = path.join(rootDir, "bin", "langfuse-cli.js");
56
+ const reportCli = path.join(rootDir, "opencode-ohai-report", "bin", "cli.js");
57
+
58
+ async function askEmail(rl) {
59
+ const envEmail = process.env.OHAI_INSTALL_USER_EMAIL || "";
60
+ const hint = emailIsValid(envEmail) ? ` [${envEmail}]` : "";
61
+ console.log("");
62
+ console.log("Company email is used as the user identity in issue reports.");
63
+ console.log("Press Enter to skip email setup for now.");
64
+ while (true) {
65
+ const answer = (await rl.question(`Company email${hint}: `)).trim();
66
+ const value = answer || (emailIsValid(envEmail) ? envEmail.trim() : "");
67
+ if (!value) return "";
68
+ if (emailIsValid(value)) return value;
69
+ console.log("Invalid email. Example: name@company.com");
70
+ }
71
+ }
72
+
73
+ async function runReportInstallMenu(rl) {
74
+ const target = await askChoice(rl, "Install issue report plugin for:", [
75
+ { label: "OpenCode", value: "opencode", description: "Install /report-ai-issue and the OpenCode plugin." },
76
+ { label: "Claude Code", value: "claude", description: "Install the command and optional hook integration." },
77
+ { label: "Both", value: "both", description: "Install OpenCode and Claude Code integrations together." },
78
+ { label: "Back", value: "back" },
79
+ ]);
80
+ if (target === "back") return 0;
81
+ const email = await askEmail(rl);
82
+ const args = ["install", target];
83
+ if (email) args.push("--email", email);
84
+ else args.push("--skip-email");
85
+ rl.close();
86
+ return runNodeScript(reportCli, args);
87
+ }
88
+
89
+ async function runMainMenu() {
90
+ if (!process.stdin.isTTY) {
91
+ printHelp();
92
+ return 0;
93
+ }
94
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
95
+ try {
96
+ console.log("");
97
+ console.log("oh-aicoding-tool");
98
+ const action = await askChoice(rl, "What would you like to install or configure?", [
99
+ {
100
+ label: "Langfuse tracing",
101
+ value: "langfuse",
102
+ description: "Configure tracing for Claude Code, OpenCode, or Codex.",
103
+ },
104
+ {
105
+ label: "Issue report plugin",
106
+ value: "report",
107
+ description: "Install oh-ai-report and configure company email.",
108
+ },
109
+ { label: "Check Langfuse configuration", value: "check-langfuse" },
110
+ { label: "Check report plugin", value: "check-report" },
111
+ { label: "Exit", value: "exit" },
112
+ ]);
113
+ if (action === "langfuse") {
114
+ rl.close();
115
+ return runNodeScript(langfuseCli, ["setup"]);
116
+ }
117
+ if (action === "report") return await runReportInstallMenu(rl);
118
+ if (action === "check-langfuse") {
119
+ rl.close();
120
+ return runNodeScript(langfuseCli, ["check"]);
121
+ }
122
+ if (action === "check-report") {
123
+ rl.close();
124
+ return runNodeScript(reportCli, ["doctor"]);
125
+ }
126
+ return 0;
127
+ } finally {
128
+ rl.close();
129
+ }
130
+ }
131
+
132
+ async function main() {
133
+ const argv = process.argv.slice(2);
134
+ const [cmd, ...rest] = argv;
135
+
136
+ if (cmd === "--help" || cmd === "-h" || cmd === "help") {
137
+ printHelp();
138
+ return 0;
139
+ }
140
+
141
+ if (!cmd) return await runMainMenu();
142
+
143
+ if (cmd === "langfuse" || cmd === "trace" || cmd === "tracing") {
144
+ return runNodeScript(langfuseCli, rest);
145
+ }
146
+
147
+ if (cmd === "report" || cmd === "ohai" || cmd === "issue") {
148
+ return runNodeScript(reportCli, rest);
149
+ }
150
+
151
+ if (cmd === "setup" || cmd === "check") {
152
+ return runNodeScript(langfuseCli, argv);
153
+ }
154
+
155
+ console.error(`Unknown command: ${cmd}`);
156
+ console.error("");
157
+ printHelp();
158
+ return 1;
159
+ }
160
+
161
+ main()
162
+ .then((code) => process.exit(code))
163
+ .catch((err) => {
164
+ console.error(err?.message || String(err));
165
+ process.exit(1);
166
+ });