@techdivision/opencode-time-tracking 0.3.0 → 0.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techdivision/opencode-time-tracking",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Automatic time tracking plugin for OpenCode - tracks session duration and tool usage to CSV",
5
5
  "main": "src/Plugin.ts",
6
6
  "types": "src/Plugin.ts",
@@ -86,7 +86,7 @@ export function createEventHook(
86
86
  client: OpencodeClient
87
87
  ) {
88
88
  return async ({ event }: { event: Event }): Promise<void> => {
89
- // Track model from assistant messages
89
+ // Track model and agent from assistant messages
90
90
  if (event.type === "message.updated") {
91
91
  const props = event.properties as MessageUpdatedProperties
92
92
  const message = props.info
@@ -94,27 +94,28 @@ export function createEventHook(
94
94
  if (message.role === "assistant") {
95
95
  const assistantMsg = message as AssistantMessage
96
96
 
97
+ // Track model
97
98
  if (assistantMsg.modelID && assistantMsg.providerID) {
98
99
  sessionManager.setModel(assistantMsg.sessionID, {
99
100
  modelID: assistantMsg.modelID,
100
101
  providerID: assistantMsg.providerID,
101
102
  })
102
103
  }
104
+
105
+ // Track agent from mode field
106
+ if (assistantMsg.mode) {
107
+ sessionManager.setAgent(assistantMsg.sessionID, assistantMsg.mode)
108
+ }
103
109
  }
104
110
 
105
111
  return
106
112
  }
107
113
 
108
- // Track token usage and agent from message part events
114
+ // Track token usage from message part events
109
115
  if (event.type === "message.part.updated") {
110
116
  const props = event.properties as MessagePartUpdatedProperties
111
117
  const part = props.part
112
118
 
113
- // Track agent from agent parts (only first agent is stored)
114
- if (part.type === "agent" && part.sessionID && part.name) {
115
- sessionManager.setAgent(part.sessionID, part.name)
116
- }
117
-
118
119
  // Track token usage from step-finish events
119
120
  if (part.type === "step-finish" && part.sessionID && part.tokens) {
120
121
  sessionManager.addTokenUsage(part.sessionID, {
@@ -16,6 +16,42 @@ import "../types/Bun"
16
16
  */
17
17
  const ENV_USER_EMAIL = "OPENCODE_USER_EMAIL"
18
18
 
19
+ /**
20
+ * Loads a value from a .env file in the specified directory.
21
+ *
22
+ * @param directory - The directory containing the .env file
23
+ * @param key - The environment variable key to look for
24
+ * @returns The value if found, or `null` if not found
25
+ *
26
+ * @internal
27
+ */
28
+ async function loadEnvValue(
29
+ directory: string,
30
+ key: string
31
+ ): Promise<string | null> {
32
+ const envPath = `${directory}/.env`
33
+
34
+ try {
35
+ const file = Bun.file(envPath)
36
+
37
+ if (await file.exists()) {
38
+ const content = await file.text()
39
+ // Match KEY=value, handling optional quotes
40
+ const regex = new RegExp(`^${key}=(.*)$`, "m")
41
+ const match = content.match(regex)
42
+
43
+ if (match) {
44
+ // Remove surrounding quotes (single or double) and trim
45
+ return match[1].trim().replace(/^["']|["']$/g, "")
46
+ }
47
+ }
48
+
49
+ return null
50
+ } catch {
51
+ return null
52
+ }
53
+ }
54
+
19
55
  /**
20
56
  * Loads the plugin configuration from the project directory.
21
57
  *
@@ -23,9 +59,10 @@ const ENV_USER_EMAIL = "OPENCODE_USER_EMAIL"
23
59
  * The configuration file is expected at `.opencode/opencode-project.json`
24
60
  * within the project directory, with a `time_tracking` section.
25
61
  *
26
- * The `user_email` is resolved from:
27
- * 1. `OPENCODE_USER_EMAIL` environment variable
28
- * 2. System username (fallback)
62
+ * The `user_email` is resolved from (in order of priority):
63
+ * 1. `OPENCODE_USER_EMAIL` system environment variable
64
+ * 2. `OPENCODE_USER_EMAIL` from project `.env` file
65
+ * 3. System username (fallback)
29
66
  */
30
67
  export class ConfigLoader {
31
68
  /**
@@ -39,7 +76,7 @@ export class ConfigLoader {
39
76
  * const config = await ConfigLoader.load("/path/to/project")
40
77
  * if (config) {
41
78
  * console.log(config.csv_file)
42
- * console.log(config.user_email) // Resolved from ENV or system username
79
+ * console.log(config.user_email) // Resolved from ENV, .env file, or system username
43
80
  * }
44
81
  * ```
45
82
  */
@@ -55,8 +92,13 @@ export class ConfigLoader {
55
92
  if (projectConfig.time_tracking) {
56
93
  const jsonConfig = projectConfig.time_tracking
57
94
 
58
- // Resolve user_email from environment variable or fallback to system username
59
- const userEmail = process.env[ENV_USER_EMAIL] || userInfo().username
95
+ // Resolve user_email with fallback chain:
96
+ // 1. System environment variable
97
+ // 2. Project .env file
98
+ // 3. System username
99
+ const envValue = await loadEnvValue(directory, ENV_USER_EMAIL)
100
+ const userEmail =
101
+ process.env[ENV_USER_EMAIL] || envValue || userInfo().username
60
102
 
61
103
  return {
62
104
  ...jsonConfig,