opencode-time-tracking 0.1.5

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.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview Configuration type for the time tracking plugin.
3
+ */
4
+
5
+ /**
6
+ * Plugin configuration loaded from `.opencode/time-tracking.json`.
7
+ */
8
+ export interface TimeTrackingConfig {
9
+ /**
10
+ * Path to the CSV output file.
11
+ *
12
+ * @remarks
13
+ * Supports three formats:
14
+ * - `~/path` - Expands to home directory
15
+ * - `/absolute/path` - Used as-is
16
+ * - `relative/path` - Relative to project directory
17
+ */
18
+ csv_file: string
19
+
20
+ /** Email address of the user for the worklog */
21
+ user_email: string
22
+
23
+ /** Default Jira account key for time entries */
24
+ default_account_key: string
25
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview Todo item type from OpenCode SDK.
3
+ */
4
+
5
+ /**
6
+ * A todo item from the session's task list.
7
+ */
8
+ export interface Todo {
9
+ /** The content/description of the todo item */
10
+ content?: string
11
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Token usage statistics type.
3
+ */
4
+
5
+ /**
6
+ * Token consumption statistics for a session.
7
+ */
8
+ export interface TokenUsage {
9
+ /** Number of input tokens consumed */
10
+ input: number
11
+
12
+ /** Number of output tokens generated */
13
+ output: number
14
+
15
+ /** Number of reasoning tokens used (for o1-style models) */
16
+ reasoning: number
17
+
18
+ /** Number of tokens read from cache */
19
+ cacheRead: number
20
+
21
+ /** Number of tokens written to cache */
22
+ cacheWrite: number
23
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @fileoverview Input type for tool.execute.after hook.
3
+ */
4
+
5
+ /**
6
+ * Input received by the tool.execute.after hook.
7
+ */
8
+ export interface ToolExecuteAfterInput {
9
+ /** Name of the tool that was executed */
10
+ tool: string
11
+
12
+ /** The OpenCode session identifier */
13
+ sessionID: string
14
+
15
+ /** Unique identifier for this tool call */
16
+ callID: string
17
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @fileoverview Output type for tool.execute.after hook.
3
+ */
4
+
5
+ /**
6
+ * Output data from the tool.execute.after hook.
7
+ */
8
+ export interface ToolExecuteAfterOutput {
9
+ /** Display title for the tool execution */
10
+ title: string
11
+
12
+ /** Tool output content */
13
+ output: string
14
+
15
+ /** Tool-specific metadata (varies by tool type) */
16
+ metadata: unknown
17
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @fileoverview CSV formatting utilities.
3
+ */
4
+
5
+ /**
6
+ * Utility class for CSV formatting operations.
7
+ *
8
+ * @remarks
9
+ * Provides static methods for escaping and formatting values
10
+ * according to CSV standards.
11
+ */
12
+ export class CsvFormatter {
13
+ /**
14
+ * Escapes a string value for CSV output.
15
+ *
16
+ * @param value - The string to escape
17
+ * @returns The escaped string with double quotes doubled
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * CsvFormatter.escape('Say "Hello"') // Returns: Say ""Hello""
22
+ * ```
23
+ */
24
+ static escape(value: string): string {
25
+ return value.replace(/"/g, '""')
26
+ }
27
+
28
+ /**
29
+ * Formats a timestamp as an ISO date string (YYYY-MM-DD).
30
+ *
31
+ * @param timestamp - The Unix timestamp in milliseconds
32
+ * @returns The formatted date string
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * CsvFormatter.formatDate(1704067200000) // Returns: "2024-01-01"
37
+ * ```
38
+ */
39
+ static formatDate(timestamp: number): string {
40
+ return new Date(timestamp).toISOString().split("T")[0]
41
+ }
42
+
43
+ /**
44
+ * Formats a timestamp as a time string (HH:MM:SS).
45
+ *
46
+ * @param timestamp - The Unix timestamp in milliseconds
47
+ * @returns The formatted time string in local time
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * CsvFormatter.formatTime(1704067200000) // Returns: "12:00:00"
52
+ * ```
53
+ */
54
+ static formatTime(timestamp: number): string {
55
+ return new Date(timestamp).toTimeString().split(" ")[0]
56
+ }
57
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * @fileoverview Generates human-readable descriptions from activity data.
3
+ */
4
+
5
+ import type { ActivityData } from "../types/ActivityData"
6
+
7
+ /**
8
+ * Generates human-readable descriptions from tool activities.
9
+ *
10
+ * @remarks
11
+ * Creates descriptions summarizing what tools were used and which files
12
+ * were affected during a session.
13
+ */
14
+ export class DescriptionGenerator {
15
+ /**
16
+ * Generates a human-readable description of activities.
17
+ *
18
+ * @param activities - Array of activity data from the session
19
+ * @returns A formatted description string
20
+ *
21
+ * @remarks
22
+ * Groups activities by type:
23
+ * - File edits (edit + write tools)
24
+ * - File reads
25
+ * - Commands (bash)
26
+ * - Searches (glob + grep)
27
+ *
28
+ * Also includes file names if 5 or fewer files were touched.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const desc = DescriptionGenerator.generate(activities)
33
+ * // Returns: "3 file edit(s), 2 file read(s) - Files: index.ts, utils.ts"
34
+ * ```
35
+ */
36
+ static generate(activities: ActivityData[]): string {
37
+ if (activities.length === 0) {
38
+ return "No activities tracked"
39
+ }
40
+
41
+ const toolCounts = activities.reduce(
42
+ (acc, a) => {
43
+ acc[a.tool] = (acc[a.tool] || 0) + 1
44
+ return acc
45
+ },
46
+ {} as Record<string, number>
47
+ )
48
+
49
+ const filesWorkedOn = new Set<string>()
50
+
51
+ for (const activity of activities) {
52
+ if (activity.file) {
53
+ const fileName = activity.file.split("/").pop() || activity.file
54
+ filesWorkedOn.add(fileName)
55
+ }
56
+ }
57
+
58
+ const mainActivities: string[] = []
59
+
60
+ if (toolCounts.edit || toolCounts.write) {
61
+ mainActivities.push(
62
+ `${(toolCounts.edit || 0) + (toolCounts.write || 0)} file edit(s)`
63
+ )
64
+ }
65
+
66
+ if (toolCounts.read) {
67
+ mainActivities.push(`${toolCounts.read} file read(s)`)
68
+ }
69
+
70
+ if (toolCounts.bash) {
71
+ mainActivities.push(`${toolCounts.bash} command(s)`)
72
+ }
73
+
74
+ if (toolCounts.glob || toolCounts.grep) {
75
+ mainActivities.push(
76
+ `${(toolCounts.glob || 0) + (toolCounts.grep || 0)} search(es)`
77
+ )
78
+ }
79
+
80
+ let description =
81
+ mainActivities.length > 0
82
+ ? mainActivities.join(", ")
83
+ : `${activities.length} tool call(s)`
84
+
85
+ if (filesWorkedOn.size > 0 && filesWorkedOn.size <= 5) {
86
+ description += ` - Files: ${Array.from(filesWorkedOn).join(", ")}`
87
+ } else if (filesWorkedOn.size > 5) {
88
+ description += ` - ${filesWorkedOn.size} files`
89
+ }
90
+
91
+ return description
92
+ }
93
+
94
+ /**
95
+ * Generates a compact tool usage summary.
96
+ *
97
+ * @param activities - Array of activity data from the session
98
+ * @returns A compact summary string showing tool counts
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const summary = DescriptionGenerator.generateToolSummary(activities)
103
+ * // Returns: "read(5x), edit(3x), bash(2x)"
104
+ * ```
105
+ */
106
+ static generateToolSummary(activities: ActivityData[]): string {
107
+ const toolCounts = activities.reduce(
108
+ (acc, a) => {
109
+ acc[a.tool] = (acc[a.tool] || 0) + 1
110
+ return acc
111
+ },
112
+ {} as Record<string, number>
113
+ )
114
+
115
+ return Object.entries(toolCounts)
116
+ .map(([t, c]) => `${t}(${c}x)`)
117
+ .join(", ")
118
+ }
119
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "noEmit": true,
10
+ "types": ["bun-types", "node"]
11
+ },
12
+ "include": ["src/**/*"]
13
+ }