@ramarivera/coding-agent-langfuse 0.1.47 → 0.1.48

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
@@ -97,10 +97,13 @@ global rule:
97
97
 
98
98
  Matched values are emitted on root trace metadata, observation metadata, and
99
99
  top-level OTLP attributes such as `project.tags`, `project.group`, and
100
- `project.owner`. The root span also sets `langfuse.trace.tags`, so Langfuse
101
- custom dashboards can filter/group by tags or metadata. Historical repair runs
102
- use the same OTLP builder as live follow mode, so rerunning with `--force` can
103
- backfill newly added tags for existing sessions.
100
+ `project.owner`. When the session cwd is inside a Git repository, the exporter
101
+ also emits `git.worktree.path`, `git.branch`, and `git.commit` plus matching
102
+ Langfuse metadata fields. The root span also sets `langfuse.trace.tags`, so
103
+ Langfuse custom dashboards can filter/group by tags, metadata, branch, or
104
+ worktree. Historical repair runs use the same OTLP builder as live follow mode,
105
+ so rerunning with `--force` can backfill newly added tags and Git metadata for
106
+ existing sessions.
104
107
 
105
108
  Use a non-default global config path with:
106
109
 
package/dist/backfill.js CHANGED
@@ -35,6 +35,7 @@ const defaultPathTagsConfigPath = join(homedir(), ".config/coding-agent-langfuse
35
35
  const currentHost = hostname();
36
36
  const projectLocalConfigFile = ".langfuse-ca.json";
37
37
  const projectLocalConfigCache = new Map();
38
+ const gitMetadataCache = new Map();
38
39
  const kimiFirepassRates = {
39
40
  input: 2,
40
41
  output: 8,
@@ -244,8 +245,60 @@ function mergeProjectMetadata(cwd, config) {
244
245
  projectFolder: pathTags.projectFolder ?? project.projectFolder,
245
246
  tags: pathTags.tags,
246
247
  metadata: pathTags.metadata,
248
+ git: loadGitMetadata(cwd),
247
249
  };
248
250
  }
251
+ function loadGitMetadata(cwd) {
252
+ const gitCwd = nearestExistingDirectory(cwd);
253
+ if (!gitCwd)
254
+ return {};
255
+ const cached = gitMetadataCache.get(gitCwd);
256
+ if (cached)
257
+ return cached;
258
+ const worktreePath = gitOutput(gitCwd, ["rev-parse", "--show-toplevel"]);
259
+ if (!worktreePath) {
260
+ gitMetadataCache.set(gitCwd, {});
261
+ return {};
262
+ }
263
+ const branch = gitOutput(gitCwd, ["branch", "--show-current"]) ??
264
+ gitOutput(gitCwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
265
+ const commit = gitOutput(gitCwd, ["rev-parse", "--verify", "HEAD"]);
266
+ const metadata = {
267
+ worktreePath,
268
+ branch: branch === "HEAD" ? undefined : branch,
269
+ commit,
270
+ };
271
+ gitMetadataCache.set(gitCwd, metadata);
272
+ return metadata;
273
+ }
274
+ function gitOutput(cwd, args) {
275
+ try {
276
+ const output = execFileSync("git", ["-C", cwd, ...args], {
277
+ encoding: "utf8",
278
+ stdio: ["ignore", "pipe", "ignore"],
279
+ }).trim();
280
+ return output.length > 0 ? output : undefined;
281
+ }
282
+ catch {
283
+ return undefined;
284
+ }
285
+ }
286
+ function nearestExistingDirectory(path) {
287
+ if (!path)
288
+ return undefined;
289
+ let current = path;
290
+ while (true) {
291
+ try {
292
+ return statSync(current).isDirectory() ? current : dirname(current);
293
+ }
294
+ catch {
295
+ const parent = dirname(current);
296
+ if (parent === current)
297
+ return undefined;
298
+ current = parent;
299
+ }
300
+ }
301
+ }
249
302
  function usage() {
250
303
  return `Usage: coding-agent-langfuse-backfill [options]
251
304
 
@@ -1854,6 +1907,9 @@ function toOtlp(events, options = {}) {
1854
1907
  attr("langfuse.trace.metadata.project_folder", firstProject.projectFolder),
1855
1908
  attr("langfuse.trace.metadata.project_tags", nonEmptyArray(firstProject.tags)),
1856
1909
  attr("langfuse.trace.metadata.path_tags", nonEmptyRecord(firstProject.metadata)),
1910
+ attr("langfuse.trace.metadata.git_worktree_path", firstProject.git.worktreePath),
1911
+ attr("langfuse.trace.metadata.git_branch", firstProject.git.branch),
1912
+ attr("langfuse.trace.metadata.git_commit", firstProject.git.commit),
1857
1913
  attr("langfuse.trace.tags", nonEmptyArray(firstProject.tags)),
1858
1914
  attr("langfuse.trace.metadata.import_payload_version", payloadVersion(first)),
1859
1915
  attr("langfuse.trace.metadata.import_state_identity", importIdentity(first)),
@@ -1870,6 +1926,9 @@ function toOtlp(events, options = {}) {
1870
1926
  attr("langfuse.observation.metadata.project_folder", firstProject.projectFolder),
1871
1927
  attr("langfuse.observation.metadata.project_tags", nonEmptyArray(firstProject.tags)),
1872
1928
  attr("langfuse.observation.metadata.path_tags", nonEmptyRecord(firstProject.metadata)),
1929
+ attr("langfuse.observation.metadata.git_worktree_path", firstProject.git.worktreePath),
1930
+ attr("langfuse.observation.metadata.git_branch", firstProject.git.branch),
1931
+ attr("langfuse.observation.metadata.git_commit", firstProject.git.commit),
1873
1932
  attr("langfuse.observation.metadata.import_payload_version", payloadVersion(first)),
1874
1933
  attr("langfuse.observation.metadata.import_state_identity", importIdentity(first)),
1875
1934
  attr("langfuse.observation.metadata.langfuse_id_identity", langfuseIdIdentity(first)),
@@ -1881,6 +1940,9 @@ function toOtlp(events, options = {}) {
1881
1940
  attr("project.tags", nonEmptyArray(firstProject.tags)),
1882
1941
  attr("project.group", firstProject.metadata.project_group),
1883
1942
  attr("project.owner", firstProject.metadata.project_owner),
1943
+ attr("git.worktree.path", firstProject.git.worktreePath),
1944
+ attr("git.branch", firstProject.git.branch),
1945
+ attr("git.commit", firstProject.git.commit),
1884
1946
  ].filter((item) => Boolean(item));
1885
1947
  const rootSpan = {
1886
1948
  traceId: traceId(first),
@@ -1927,6 +1989,9 @@ function toOtlp(events, options = {}) {
1927
1989
  attr("langfuse.observation.metadata.project_folder", eventProject.projectFolder),
1928
1990
  attr("langfuse.observation.metadata.project_tags", nonEmptyArray(eventProject.tags)),
1929
1991
  attr("langfuse.observation.metadata.path_tags", nonEmptyRecord(eventProject.metadata)),
1992
+ attr("langfuse.observation.metadata.git_worktree_path", eventProject.git.worktreePath),
1993
+ attr("langfuse.observation.metadata.git_branch", eventProject.git.branch),
1994
+ attr("langfuse.observation.metadata.git_commit", eventProject.git.commit),
1930
1995
  attr("langfuse.observation.metadata.model", modelName ?? event.model),
1931
1996
  attr("langfuse.observation.metadata.provider", event.provider),
1932
1997
  attr("langfuse.observation.metadata.import_payload_version", payloadVersion(event)),
@@ -1953,6 +2018,9 @@ function toOtlp(events, options = {}) {
1953
2018
  attr("project.tags", nonEmptyArray(eventProject.tags)),
1954
2019
  attr("project.group", eventProject.metadata.project_group),
1955
2020
  attr("project.owner", eventProject.metadata.project_owner),
2021
+ attr("git.worktree.path", eventProject.git.worktreePath),
2022
+ attr("git.branch", eventProject.git.branch),
2023
+ attr("git.commit", eventProject.git.commit),
1956
2024
  attr("role", event.role),
1957
2025
  attr("agent.model", event.model),
1958
2026
  attr("agent.provider", event.provider),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramarivera/coding-agent-langfuse",
3
- "version": "0.1.47",
3
+ "version": "0.1.48",
4
4
  "description": "Universal coding-agent Langfuse backfiller and live OTLP helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",