Skip to content

Commit 48b32b7

Browse files
committed
Merge branch 'develop'
2 parents 473c7e3 + 5c165d7 commit 48b32b7

5 files changed

Lines changed: 81 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.7.1] - 2025-03-01
6+
7+
### Fixed
8+
9+
- Fix `agent_defaults` lookup to be tolerant of `@` prefix in agent names
10+
- Add `AgentMatcher` utility for consistent agent name normalization across the codebase
11+
512
## [0.7.0] - 2025-02-04
613

714
### Added

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@techdivision/opencode-time-tracking",
3-
"version": "0.7.0",
3+
"version": "0.7.1",
44
"description": "Automatic time tracking plugin for OpenCode - tracks session duration and tool usage to CSV",
55
"main": "src/Plugin.ts",
66
"types": "src/Plugin.ts",

src/hooks/EventHook.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { MessageWithParts } from "../types/MessageWithParts"
1212
import type { OpencodeClient } from "../types/OpencodeClient"
1313
import type { TimeTrackingConfig } from "../types/TimeTrackingConfig"
1414

15+
import { AgentMatcher } from "../utils/AgentMatcher"
1516
import { DescriptionGenerator } from "../utils/DescriptionGenerator"
1617

1718
/**
@@ -193,9 +194,11 @@ export function createEventHook(
193194
const agentString = session.agent?.name ?? null
194195

195196
// Check if agent should be ignored (tolerant matching: with or without @ prefix)
196-
const normalizedAgent = agentString?.replace(/^@/, "")
197+
const normalizedAgent = agentString
198+
? AgentMatcher.normalize(agentString)
199+
: null
197200
const isIgnoredAgent = config.ignored_agents?.some(
198-
(ignored) => ignored.replace(/^@/, "") === normalizedAgent
201+
(ignored) => AgentMatcher.normalize(ignored) === normalizedAgent
199202
)
200203

201204
if (agentString && isIgnoredAgent) {

src/services/TicketResolver.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import type { ResolvedTicketInfo } from "../types/ResolvedTicketInfo"
66
import type { TimeTrackingConfig } from "../types/TimeTrackingConfig"
77
import type { TicketExtractor } from "./TicketExtractor"
88

9+
import { AgentMatcher } from "../utils/AgentMatcher"
10+
911
/**
1012
* Resolves tickets and account keys using fallback hierarchy.
1113
*
@@ -65,11 +67,13 @@ export class TicketResolver {
6567
}
6668
}
6769

68-
// 2. Try agent default
69-
if (agentName && this.config.agent_defaults?.[agentName]) {
70+
// 2. Try agent default (tolerant matching: with or without @ prefix)
71+
const agentKey = agentName ? this.findAgentKey(agentName) : null
72+
73+
if (agentKey) {
7074
return {
71-
ticket: this.config.agent_defaults[agentName].issue_key,
72-
accountKey: this.resolveAccountKey(agentName),
75+
ticket: this.config.agent_defaults![agentKey].issue_key,
76+
accountKey: this.resolveAccountKey(agentKey),
7377
}
7478
}
7579

@@ -88,16 +92,41 @@ export class TicketResolver {
8892
}
8993
}
9094

95+
/**
96+
* Finds the matching config key for an agent name.
97+
*
98+
* @param agentName - The agent name from the SDK
99+
* @returns The matching config key, or `null` if not found
100+
*
101+
* @remarks
102+
* Normalizes both the agent name and config keys to ensure
103+
* matching works regardless of @ prefix.
104+
*/
105+
private findAgentKey(agentName: string): string | null {
106+
const defaults = this.config.agent_defaults
107+
108+
if (!defaults) {
109+
return null
110+
}
111+
112+
const normalized = AgentMatcher.normalize(agentName)
113+
const key = Object.keys(defaults).find(
114+
(k) => AgentMatcher.normalize(k) === normalized
115+
)
116+
117+
return key ?? null
118+
}
119+
91120
/**
92121
* Resolves account key using fallback hierarchy.
93122
*
94-
* @param agentName - The agent name, or `null`
123+
* @param agentKey - The agent config key, or `null`
95124
* @returns Resolved Tempo account key
96125
*/
97-
private resolveAccountKey(agentName: string | null): string {
126+
private resolveAccountKey(agentKey: string | null): string {
98127
// 1. Agent-specific account_key
99-
if (agentName && this.config.agent_defaults?.[agentName]?.account_key) {
100-
return this.config.agent_defaults[agentName].account_key!
128+
if (agentKey && this.config.agent_defaults?.[agentKey]?.account_key) {
129+
return this.config.agent_defaults[agentKey].account_key!
101130
}
102131

103132
// 2. Global default account_key (required)

src/utils/AgentMatcher.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @fileoverview Utility for normalizing agent names.
3+
*/
4+
5+
/**
6+
* Normalizes agent names to a canonical form.
7+
*
8+
* @remarks
9+
* Agent names can appear with or without the "@" prefix
10+
* depending on the source (SDK, config, user input).
11+
* This class ensures consistent comparison by normalizing
12+
* all agent names to the "@<name>" format.
13+
*/
14+
export class AgentMatcher {
15+
/**
16+
* Normalizes an agent name to canonical form (with @ prefix).
17+
*
18+
* @param agentName - The agent name to normalize
19+
* @returns The normalized agent name with @ prefix
20+
*
21+
* @example
22+
* ```typescript
23+
* AgentMatcher.normalize("developer") // → "@developer"
24+
* AgentMatcher.normalize("@developer") // → "@developer"
25+
* AgentMatcher.normalize("@time-tracking") // → "@time-tracking"
26+
* ```
27+
*/
28+
static normalize(agentName: string): string {
29+
return agentName.startsWith("@") ? agentName : `@${agentName}`
30+
}
31+
}

0 commit comments

Comments
 (0)