@knpkv/jira-clockify 0.2.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.
- package/LICENSE +21 -0
- package/README.md +121 -0
- package/dist/package.json +47 -0
- package/dist/src/bin.js +28 -0
- package/dist/src/bin.js.map +1 -0
- package/dist/src/cli/auth.js +169 -0
- package/dist/src/cli/auth.js.map +1 -0
- package/dist/src/cli/config.js +107 -0
- package/dist/src/cli/config.js.map +1 -0
- package/dist/src/cli/fuzzySelect.js +149 -0
- package/dist/src/cli/fuzzySelect.js.map +1 -0
- package/dist/src/cli/layers.js +59 -0
- package/dist/src/cli/layers.js.map +1 -0
- package/dist/src/cli/list.js +27 -0
- package/dist/src/cli/list.js.map +1 -0
- package/dist/src/cli/setup.js +134 -0
- package/dist/src/cli/setup.js.map +1 -0
- package/dist/src/cli/timer/discard.js +33 -0
- package/dist/src/cli/timer/discard.js.map +1 -0
- package/dist/src/cli/timer/edit.js +139 -0
- package/dist/src/cli/timer/edit.js.map +1 -0
- package/dist/src/cli/timer/index.js +12 -0
- package/dist/src/cli/timer/index.js.map +1 -0
- package/dist/src/cli/timer/log.js +96 -0
- package/dist/src/cli/timer/log.js.map +1 -0
- package/dist/src/cli/timer/start.js +156 -0
- package/dist/src/cli/timer/start.js.map +1 -0
- package/dist/src/cli/timer/status.js +80 -0
- package/dist/src/cli/timer/status.js.map +1 -0
- package/dist/src/cli/timer/stop.js +96 -0
- package/dist/src/cli/timer/stop.js.map +1 -0
- package/dist/src/cli/timer.js +7 -0
- package/dist/src/cli/timer.js.map +1 -0
- package/dist/src/main.js +24 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/services/ClockifyAuth.js +77 -0
- package/dist/src/services/ClockifyAuth.js.map +1 -0
- package/dist/src/services/ConfigService.js +66 -0
- package/dist/src/services/ConfigService.js.map +1 -0
- package/dist/src/services/StateWriter.js +69 -0
- package/dist/src/services/StateWriter.js.map +1 -0
- package/dist/src/services/TicketService.js +106 -0
- package/dist/src/services/TicketService.js.map +1 -0
- package/dist/src/services/TimerService.js +271 -0
- package/dist/src/services/TimerService.js.map +1 -0
- package/dist/src/tui/App.js +204 -0
- package/dist/src/tui/App.js.map +1 -0
- package/dist/src/tui/atoms/runtime.js +9 -0
- package/dist/src/tui/atoms/runtime.js.map +1 -0
- package/dist/src/tui/atoms/tickets.js +17 -0
- package/dist/src/tui/atoms/tickets.js.map +1 -0
- package/dist/src/tui/atoms/timer.js +31 -0
- package/dist/src/tui/atoms/timer.js.map +1 -0
- package/dist/src/tui/atoms/ui.js +10 -0
- package/dist/src/tui/atoms/ui.js.map +1 -0
- package/dist/src/tui/components/BigTimer.js +60 -0
- package/dist/src/tui/components/BigTimer.js.map +1 -0
- package/dist/src/tui/components/Footer.js +16 -0
- package/dist/src/tui/components/Footer.js.map +1 -0
- package/dist/src/tui/components/Header.js +16 -0
- package/dist/src/tui/components/Header.js.map +1 -0
- package/dist/src/tui/components/PopupInput.js +30 -0
- package/dist/src/tui/components/PopupInput.js.map +1 -0
- package/dist/src/tui/components/PopupMessage.js +47 -0
- package/dist/src/tui/components/PopupMessage.js.map +1 -0
- package/dist/src/tui/components/TicketList.js +40 -0
- package/dist/src/tui/components/TicketList.js.map +1 -0
- package/dist/src/tui/components/TicketRow.js +48 -0
- package/dist/src/tui/components/TicketRow.js.map +1 -0
- package/dist/src/tui/components/TimerDisplay.js +30 -0
- package/dist/src/tui/components/TimerDisplay.js.map +1 -0
- package/dist/src/tui/components/index.js +12 -0
- package/dist/src/tui/components/index.js.map +1 -0
- package/dist/src/tui/context/theme.js +15 -0
- package/dist/src/tui/context/theme.js.map +1 -0
- package/dist/src/tui/hooks/useElapsedTimer.js +35 -0
- package/dist/src/tui/hooks/useElapsedTimer.js.map +1 -0
- package/dist/src/tui/hooks/useTerminalSize.js +32 -0
- package/dist/src/tui/hooks/useTerminalSize.js.map +1 -0
- package/dist/src/utils/time.js +23 -0
- package/dist/src/utils/time.js.map +1 -0
- package/dist/test/TimerService.test.js +355 -0
- package/dist/test/TimerService.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/nvim/lua/jcf/branch.lua +26 -0
- package/nvim/lua/jcf/float.lua +87 -0
- package/nvim/lua/jcf/init.lua +70 -0
- package/nvim/lua/jcf/state.lua +56 -0
- package/nvim/lua/jcf/statusline.lua +31 -0
- package/nvim/lua/jcf/telescope.lua +50 -0
- package/nvim/plugin/jcf.vim +2 -0
- package/package.json +47 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timer `log` command — add activity manually.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { Args, Command, Options } from "@effect/cli";
|
|
7
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
8
|
+
import * as HttpClientRequest from "@effect/platform/HttpClientRequest";
|
|
9
|
+
import { ClockifyApiClient } from "@knpkv/clockify-api-client";
|
|
10
|
+
import { JiraApiClient, toEffect } from "@knpkv/jira-api-client";
|
|
11
|
+
import { JiraAuth } from "@knpkv/jira-cli/JiraAuth";
|
|
12
|
+
import { Console, Effect, Option, Redacted } from "effect";
|
|
13
|
+
import { ClockifyAuth } from "../../services/ClockifyAuth.js";
|
|
14
|
+
export const log = Command.make("log", {
|
|
15
|
+
key: Args.text({ name: "key" }),
|
|
16
|
+
time: Options.text("time").pipe(Options.withAlias("t"), Options.withDescription("Duration (e.g. 1h30m, 2h, 45m)")),
|
|
17
|
+
date: Options.text("date").pipe(Options.withAlias("d"), Options.withDescription("Date (YYYY-MM-DD, default today)"), Options.optional),
|
|
18
|
+
comment: Options.text("comment").pipe(Options.withAlias("c"), Options.withDescription("Worklog comment"), Options.optional)
|
|
19
|
+
}, ({ comment, date, key, time }) => Effect.gen(function* () {
|
|
20
|
+
// Parse duration string like "1h30m", "2h", "45m"
|
|
21
|
+
const durationMatch = time.match(/(?:(\d+)h)?(?:(\d+)m)?/);
|
|
22
|
+
if (!durationMatch || (!durationMatch[1] && !durationMatch[2])) {
|
|
23
|
+
yield* Console.log("Invalid duration. Use format: 1h30m, 2h, 45m");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const hours = parseInt(durationMatch[1] ?? "0", 10);
|
|
27
|
+
const minutes = parseInt(durationMatch[2] ?? "0", 10);
|
|
28
|
+
const totalSeconds = Math.max(60, hours * 3600 + minutes * 60);
|
|
29
|
+
// Parse date
|
|
30
|
+
const dateStr = Option.isSome(date) ? date.value : new Date().toISOString().slice(0, 10);
|
|
31
|
+
const started = new Date(`${dateStr}T09:00:00.000Z`);
|
|
32
|
+
if (isNaN(started.getTime())) {
|
|
33
|
+
yield* Console.log("Invalid date. Use format: YYYY-MM-DD");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Validate ticket exists
|
|
37
|
+
const jira = yield* JiraApiClient;
|
|
38
|
+
const issue = yield* toEffect(jira.v3.client.GET("/rest/api/3/issue/{issueIdOrKey}", {
|
|
39
|
+
params: {
|
|
40
|
+
path: { issueIdOrKey: key },
|
|
41
|
+
query: { fields: ["summary", "issuetype", "labels"] }
|
|
42
|
+
}
|
|
43
|
+
})).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
44
|
+
if (!issue) {
|
|
45
|
+
yield* Console.log(`Ticket ${key} not found in Jira.`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const fields = issue.fields;
|
|
49
|
+
const summary = typeof fields?.["summary"] === "string" ? fields["summary"] : key;
|
|
50
|
+
yield* Console.log(`Logging: ${key} — ${summary}`);
|
|
51
|
+
yield* Console.log(` Duration: ${hours}h ${minutes}m (${totalSeconds}s)`);
|
|
52
|
+
yield* Console.log(` Date: ${dateStr}`);
|
|
53
|
+
// Create Clockify entry (completed)
|
|
54
|
+
const clockifyAuth = yield* ClockifyAuth;
|
|
55
|
+
const clockifyClient = yield* ClockifyApiClient;
|
|
56
|
+
const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
57
|
+
let clockifyOk = false;
|
|
58
|
+
if (auth) {
|
|
59
|
+
const end = new Date(started.getTime() + totalSeconds * 1000);
|
|
60
|
+
yield* clockifyClient.createTimeEntry(auth.workspaceId, {
|
|
61
|
+
description: `[${key}] ${summary}`,
|
|
62
|
+
start: started.toISOString(),
|
|
63
|
+
end: end.toISOString()
|
|
64
|
+
}).pipe(Effect.tap(() => Effect.sync(() => {
|
|
65
|
+
clockifyOk = true;
|
|
66
|
+
})), Effect.catchAll(() => Effect.void));
|
|
67
|
+
}
|
|
68
|
+
// Create Jira worklog via raw HTTP
|
|
69
|
+
const jiraAuthSvc = yield* JiraAuth;
|
|
70
|
+
const accessToken = yield* jiraAuthSvc.getAccessToken().pipe(Effect.map((t) => Redacted.value(t)), Effect.catchAll(() => Effect.succeed("")));
|
|
71
|
+
const cloudId = yield* jiraAuthSvc.getCloudId().pipe(Effect.catchAll(() => Effect.succeed("")));
|
|
72
|
+
let jiraOk = false;
|
|
73
|
+
if (accessToken && cloudId) {
|
|
74
|
+
const httpClient = yield* HttpClient.HttpClient;
|
|
75
|
+
const commentText = Option.isSome(comment) ? comment.value : undefined;
|
|
76
|
+
const response = yield* httpClient.execute(HttpClientRequest.post(`https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${key}/worklog`).pipe(HttpClientRequest.setHeader("Authorization", `Bearer ${accessToken}`), HttpClientRequest.setHeader("Content-Type", "application/json"), HttpClientRequest.bodyUnsafeJson({
|
|
77
|
+
started: started.toISOString().replace("Z", "+0000"),
|
|
78
|
+
timeSpentSeconds: totalSeconds,
|
|
79
|
+
...(commentText ?
|
|
80
|
+
{
|
|
81
|
+
comment: {
|
|
82
|
+
type: "doc",
|
|
83
|
+
version: 1,
|
|
84
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: commentText }] }]
|
|
85
|
+
}
|
|
86
|
+
} :
|
|
87
|
+
{})
|
|
88
|
+
}))).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
89
|
+
if (response && response.status >= 200 && response.status < 300) {
|
|
90
|
+
jiraOk = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
yield* Console.log(` Clockify: ${clockifyOk ? "✓" : "✗"}`);
|
|
94
|
+
yield* Console.log(` Jira worklog: ${jiraOk ? "✓" : "✗"}`);
|
|
95
|
+
}));
|
|
96
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../../../src/cli/timer/log.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timer `start` command.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { Args, Command, Options, Prompt } from "@effect/cli";
|
|
7
|
+
import { ClockifyApiClient } from "@knpkv/clockify-api-client";
|
|
8
|
+
import { JiraApiClient, toEffect } from "@knpkv/jira-api-client";
|
|
9
|
+
import { Console, Effect, Option, SubscriptionRef } from "effect";
|
|
10
|
+
import { ClockifyAuth } from "../../services/ClockifyAuth.js";
|
|
11
|
+
import { ConfigService } from "../../services/ConfigService.js";
|
|
12
|
+
import { TicketService } from "../../services/TicketService.js";
|
|
13
|
+
import { TimerService } from "../../services/TimerService.js";
|
|
14
|
+
import { fuzzySelect } from "../fuzzySelect.js";
|
|
15
|
+
export const start = Command.make("start", {
|
|
16
|
+
key: Args.text({ name: "key" }).pipe(Args.optional),
|
|
17
|
+
project: Options.text("project").pipe(Options.withAlias("p"), Options.withDescription("Clockify project ID"), Options.optional),
|
|
18
|
+
billable: Options.boolean("billable").pipe(Options.withAlias("b"), Options.withDescription("Mark as billable"), Options.optional),
|
|
19
|
+
saveDefaults: Options.boolean("save-defaults").pipe(Options.withDescription("Save project/billable as defaults"), Options.withDefault(false))
|
|
20
|
+
}, ({ billable, key, project, saveDefaults }) => Effect.gen(function* () {
|
|
21
|
+
const timer = yield* TimerService;
|
|
22
|
+
const ticketService = yield* TicketService;
|
|
23
|
+
const cfg = yield* ConfigService;
|
|
24
|
+
const clockifyAuth = yield* ClockifyAuth;
|
|
25
|
+
const clockifyClient = yield* ClockifyApiClient;
|
|
26
|
+
// Check for running timer
|
|
27
|
+
yield* timer.detectRunning;
|
|
28
|
+
const currentState = yield* SubscriptionRef.get(timer.state);
|
|
29
|
+
if (currentState.active && currentState.ticketKey) {
|
|
30
|
+
const elapsed = currentState.startedAt
|
|
31
|
+
? Math.floor((Date.now() - currentState.startedAt.getTime()) / 1000)
|
|
32
|
+
: 0;
|
|
33
|
+
const h = Math.floor(elapsed / 3600);
|
|
34
|
+
const m = Math.floor((elapsed % 3600) / 60);
|
|
35
|
+
const s = elapsed % 60;
|
|
36
|
+
yield* Console.log(`Timer running: ${currentState.ticketKey} — ${currentState.summary ?? ""} (${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")})`);
|
|
37
|
+
const action = yield* Prompt.select({
|
|
38
|
+
message: "What to do?",
|
|
39
|
+
choices: [
|
|
40
|
+
{ title: "Stop current and start new", value: "replace" },
|
|
41
|
+
{ title: "Keep current timer", value: "keep" }
|
|
42
|
+
]
|
|
43
|
+
});
|
|
44
|
+
if (action === "keep")
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Refresh tickets for selection
|
|
48
|
+
yield* ticketService.refresh;
|
|
49
|
+
yield* Effect.sleep("600 millis");
|
|
50
|
+
const { tickets: allTickets } = yield* SubscriptionRef.get(ticketService.state);
|
|
51
|
+
let ticket;
|
|
52
|
+
if (key._tag === "None") {
|
|
53
|
+
if (allTickets.length === 0) {
|
|
54
|
+
yield* Console.log("No tickets found. Usage: jcf start PROJ-123");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const selectedKey = yield* fuzzySelect({
|
|
58
|
+
message: "Select ticket (/ to filter):",
|
|
59
|
+
choices: allTickets.map((t) => ({
|
|
60
|
+
title: `${t.key.padEnd(12)} ${t.summary.slice(0, 45).padEnd(45)} [${t.status}]`,
|
|
61
|
+
value: t.key
|
|
62
|
+
}))
|
|
63
|
+
}).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
64
|
+
if (!selectedKey)
|
|
65
|
+
return;
|
|
66
|
+
const found = allTickets.find((t) => t.key === selectedKey);
|
|
67
|
+
if (!found)
|
|
68
|
+
return;
|
|
69
|
+
ticket = found;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Key provided — fetch from Jira to validate and get title
|
|
73
|
+
const jira = yield* JiraApiClient;
|
|
74
|
+
const issue = yield* toEffect(jira.v3.client.GET("/rest/api/3/issue/{issueIdOrKey}", {
|
|
75
|
+
params: {
|
|
76
|
+
path: { issueIdOrKey: key.value },
|
|
77
|
+
query: { fields: ["summary", "status", "priority", "assignee", "issuetype", "labels"] }
|
|
78
|
+
}
|
|
79
|
+
})).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
80
|
+
if (!issue) {
|
|
81
|
+
yield* Console.log(`Ticket ${key.value} not found in Jira.`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const fields = issue.fields;
|
|
85
|
+
const nested = (k, n) => {
|
|
86
|
+
const v = fields?.[k];
|
|
87
|
+
return v && typeof v === "object" && n in v ? v[n] : null;
|
|
88
|
+
};
|
|
89
|
+
ticket = {
|
|
90
|
+
key: issue.key ?? key.value,
|
|
91
|
+
summary: (typeof fields?.["summary"] === "string" ? fields["summary"] : null) ?? key.value,
|
|
92
|
+
status: (typeof nested("status", "name") === "string" ? nested("status", "name") : null) ??
|
|
93
|
+
"Unknown",
|
|
94
|
+
priority: typeof nested("priority", "name") === "string" ? nested("priority", "name") : null,
|
|
95
|
+
assignee: typeof nested("assignee", "displayName") === "string"
|
|
96
|
+
? nested("assignee", "displayName")
|
|
97
|
+
: null,
|
|
98
|
+
type: (typeof nested("issuetype", "name") === "string" ? nested("issuetype", "name") : null) ??
|
|
99
|
+
"Task",
|
|
100
|
+
labels: Array.isArray(fields?.["labels"]) ? fields["labels"] : [],
|
|
101
|
+
updated: (typeof fields?.["updated"] === "string" ? fields["updated"] : null) ?? new Date().toISOString()
|
|
102
|
+
};
|
|
103
|
+
yield* Console.log(`${ticket.key}: ${ticket.summary} [${ticket.status}]`);
|
|
104
|
+
}
|
|
105
|
+
// Resolve projectId
|
|
106
|
+
let projectId = Option.isSome(project) ? project.value : undefined;
|
|
107
|
+
if (!projectId) {
|
|
108
|
+
const config = yield* cfg.get;
|
|
109
|
+
if (config.defaultProjectId) {
|
|
110
|
+
projectId = config.defaultProjectId;
|
|
111
|
+
yield* Console.log(`Using default project: ${config.defaultProjectName ?? config.defaultProjectId}`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// Prompt: list projects
|
|
115
|
+
const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
116
|
+
if (auth) {
|
|
117
|
+
const projects = yield* clockifyClient.getProjects(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
118
|
+
if (projects.length > 0) {
|
|
119
|
+
const selected = yield* Prompt.select({
|
|
120
|
+
message: "Select Clockify project:",
|
|
121
|
+
choices: [
|
|
122
|
+
...projects.map((p) => ({ title: p.name, value: p.id })),
|
|
123
|
+
{ title: "(skip)", value: "" }
|
|
124
|
+
]
|
|
125
|
+
});
|
|
126
|
+
if (selected)
|
|
127
|
+
projectId = selected;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Resolve billable
|
|
133
|
+
let billableVal = Option.isSome(billable) ? billable.value : undefined;
|
|
134
|
+
if (billableVal === undefined) {
|
|
135
|
+
const config = yield* cfg.get;
|
|
136
|
+
billableVal = config.defaultBillable;
|
|
137
|
+
yield* Console.log(`Billable: ${billableVal ? "yes" : "no"} (default)`);
|
|
138
|
+
}
|
|
139
|
+
// Save defaults if requested
|
|
140
|
+
if (saveDefaults && (projectId || billableVal !== undefined)) {
|
|
141
|
+
const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
142
|
+
let projectName = null;
|
|
143
|
+
if (projectId && auth) {
|
|
144
|
+
const projects = yield* clockifyClient.getProjects(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
145
|
+
projectName = projects.find((p) => p.id === projectId)?.name ?? null;
|
|
146
|
+
}
|
|
147
|
+
yield* cfg.set({
|
|
148
|
+
...(projectId ? { defaultProjectId: projectId, defaultProjectName: projectName } : {}),
|
|
149
|
+
...(billableVal !== undefined ? { defaultBillable: billableVal } : {})
|
|
150
|
+
});
|
|
151
|
+
yield* Console.log("Defaults saved to ~/.jcf/config.json");
|
|
152
|
+
}
|
|
153
|
+
yield* timer.start(ticket, { projectId, billable: billableVal }).pipe(Effect.catchAll((e) => Console.log(`Error: ${e.message}`)));
|
|
154
|
+
yield* Console.log(`Timer started: ${ticket.key} — ${ticket.summary}`);
|
|
155
|
+
}));
|
|
156
|
+
//# sourceMappingURL=start.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.js","sourceRoot":"","sources":["../../../../src/cli/timer/start.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timer `status` command.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from "@effect/cli";
|
|
7
|
+
import { ClockifyApiClient } from "@knpkv/clockify-api-client";
|
|
8
|
+
import { Console, Effect } from "effect";
|
|
9
|
+
import { ClockifyAuth } from "../../services/ClockifyAuth.js";
|
|
10
|
+
import { StateWriter } from "../../services/StateWriter.js";
|
|
11
|
+
export const statusCmd = Command.make("status", {}, () => Effect.gen(function* () {
|
|
12
|
+
const stateWriter = yield* StateWriter;
|
|
13
|
+
const state = yield* stateWriter.read;
|
|
14
|
+
// Verify against Clockify — timer may have been stopped externally
|
|
15
|
+
if (state.active) {
|
|
16
|
+
const clockifyAuth = yield* ClockifyAuth;
|
|
17
|
+
const clockifyClient = yield* ClockifyApiClient;
|
|
18
|
+
const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
19
|
+
if (auth) {
|
|
20
|
+
let apiReachable = false;
|
|
21
|
+
const running = yield* clockifyClient.getRunningTimer(auth.workspaceId, auth.userId).pipe(Effect.tap(() => Effect.sync(() => {
|
|
22
|
+
apiReachable = true;
|
|
23
|
+
})), Effect.catchAll(() => Effect.succeed(null)));
|
|
24
|
+
// Only clear if API was reachable and confirmed no running timer
|
|
25
|
+
if (apiReachable && !running) {
|
|
26
|
+
yield* stateWriter.clear;
|
|
27
|
+
yield* Console.log("Timer was stopped externally. State cleared.");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// If timer still running, update state file (refreshes mtime for Lua statusline)
|
|
31
|
+
if (running) {
|
|
32
|
+
yield* stateWriter.write({
|
|
33
|
+
...state,
|
|
34
|
+
elapsed: state.startedAt_unix ? Math.floor(Date.now() / 1000) - state.startedAt_unix : 0
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!state.active) {
|
|
40
|
+
yield* Console.log("No active timer");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const elapsed = state.startedAt_unix
|
|
44
|
+
? Math.floor(Date.now() / 1000) - state.startedAt_unix
|
|
45
|
+
: 0;
|
|
46
|
+
const h = Math.floor(elapsed / 3600);
|
|
47
|
+
const m = Math.floor((elapsed % 3600) / 60);
|
|
48
|
+
const s = elapsed % 60;
|
|
49
|
+
yield* Console.log(`● ${state.ticketKey} ${state.summary ?? ""}`);
|
|
50
|
+
yield* Console.log(` Time: ${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`);
|
|
51
|
+
yield* Console.log(` Started: ${state.startedAt ?? "?"}`);
|
|
52
|
+
// Show project/billable from Clockify entry
|
|
53
|
+
const clockifyAuth = yield* ClockifyAuth;
|
|
54
|
+
const clockifyClient = yield* ClockifyApiClient;
|
|
55
|
+
const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
56
|
+
if (auth && state.clockifyEntryId) {
|
|
57
|
+
const entry = yield* clockifyClient.getTimeEntry(auth.workspaceId, state.clockifyEntryId).pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
58
|
+
if (entry) {
|
|
59
|
+
let projectName = "none";
|
|
60
|
+
if (entry.projectId) {
|
|
61
|
+
const projects = yield* clockifyClient.getProjects(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
62
|
+
projectName = projects.find((p) => p.id === entry.projectId)?.name ?? entry.projectId;
|
|
63
|
+
}
|
|
64
|
+
yield* Console.log(` Project: ${projectName}`);
|
|
65
|
+
yield* Console.log(` Billable: ${entry.billable ? "yes" : "no"}`);
|
|
66
|
+
// Show tags
|
|
67
|
+
if (entry.tagIds && entry.tagIds.length > 0) {
|
|
68
|
+
const allTags = yield* clockifyClient.getTags(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
69
|
+
const tagNames = entry.tagIds
|
|
70
|
+
.map((id) => allTags.find((t) => t.id === id)?.name ?? id)
|
|
71
|
+
.join(", ");
|
|
72
|
+
yield* Console.log(` Tags: ${tagNames}`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
yield* Console.log(" Tags: none");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}));
|
|
80
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/cli/timer/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timer `stop` command.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { Command, Options, Prompt } from "@effect/cli";
|
|
7
|
+
import { ClockifyApiClient } from "@knpkv/clockify-api-client";
|
|
8
|
+
import { Console, Duration, Effect, Option, SubscriptionRef } from "effect";
|
|
9
|
+
import { ClockifyAuth } from "../../services/ClockifyAuth.js";
|
|
10
|
+
import { ConfigService } from "../../services/ConfigService.js";
|
|
11
|
+
import { TimerService } from "../../services/TimerService.js";
|
|
12
|
+
export const stop = Command.make("stop", {
|
|
13
|
+
project: Options.text("project").pipe(Options.withAlias("p"), Options.withDescription("Clockify project ID"), Options.optional),
|
|
14
|
+
billable: Options.boolean("billable").pipe(Options.withAlias("b"), Options.withDescription("Mark as billable"), Options.optional)
|
|
15
|
+
}, ({ billable, project }) => Effect.gen(function* () {
|
|
16
|
+
const timer = yield* TimerService;
|
|
17
|
+
yield* timer.detectRunning;
|
|
18
|
+
// Check if project/billable need to be set
|
|
19
|
+
const currentTimer = yield* SubscriptionRef.get(timer.state);
|
|
20
|
+
const current = { projectId: currentTimer.projectId, billable: currentTimer.billable };
|
|
21
|
+
let stopProjectId = Option.isSome(project) ? project.value : undefined;
|
|
22
|
+
let stopBillable = Option.isSome(billable) ? billable.value : undefined;
|
|
23
|
+
// Prompt for project if not set on start and not provided via flag
|
|
24
|
+
if (!current.projectId && !stopProjectId) {
|
|
25
|
+
const clockifyAuth = yield* ClockifyAuth;
|
|
26
|
+
const clockifyClient = yield* ClockifyApiClient;
|
|
27
|
+
const auth = yield* clockifyAuth.getConfig.pipe(Effect.catchAll(() => Effect.succeed(null)));
|
|
28
|
+
if (auth) {
|
|
29
|
+
const projects = yield* clockifyClient.getProjects(auth.workspaceId).pipe(Effect.catchAll(() => Effect.succeed([])));
|
|
30
|
+
if (projects.length > 0) {
|
|
31
|
+
const selected = yield* Prompt.select({
|
|
32
|
+
message: "No project set. Select Clockify project:",
|
|
33
|
+
choices: [
|
|
34
|
+
...projects.map((p) => ({ title: p.name, value: p.id })),
|
|
35
|
+
{ title: "(skip)", value: "" }
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
if (selected) {
|
|
39
|
+
stopProjectId = selected;
|
|
40
|
+
const selectedName = projects.find((p) => p.id === selected)?.name ?? selected;
|
|
41
|
+
const saveDefault = yield* Prompt.select({
|
|
42
|
+
message: `Save "${selectedName}" as default project?`,
|
|
43
|
+
choices: [
|
|
44
|
+
{ title: "Yes", value: true },
|
|
45
|
+
{ title: "No", value: false }
|
|
46
|
+
]
|
|
47
|
+
});
|
|
48
|
+
if (saveDefault) {
|
|
49
|
+
const cfg = yield* ConfigService;
|
|
50
|
+
yield* cfg.set({ defaultProjectId: stopProjectId, defaultProjectName: selectedName });
|
|
51
|
+
yield* Console.log("Default project saved.");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Prompt for billable if not set
|
|
58
|
+
if (current.billable === null && stopBillable === undefined) {
|
|
59
|
+
stopBillable = yield* Prompt.select({
|
|
60
|
+
message: "Billable?",
|
|
61
|
+
choices: [
|
|
62
|
+
{ title: "Yes", value: true },
|
|
63
|
+
{ title: "No", value: false }
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
const saveDefault = yield* Prompt.select({
|
|
67
|
+
message: `Save billable=${stopBillable ? "yes" : "no"} as default?`,
|
|
68
|
+
choices: [
|
|
69
|
+
{ title: "Yes", value: true },
|
|
70
|
+
{ title: "No", value: false }
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
if (saveDefault) {
|
|
74
|
+
const cfg = yield* ConfigService;
|
|
75
|
+
yield* cfg.set({ defaultBillable: stopBillable });
|
|
76
|
+
yield* Console.log("Default billable saved.");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Optional comment for Jira worklog
|
|
80
|
+
const comment = yield* Prompt.text({ message: "Comment (empty to skip):" });
|
|
81
|
+
const result = yield* timer.stop({
|
|
82
|
+
projectId: stopProjectId,
|
|
83
|
+
billable: stopBillable,
|
|
84
|
+
comment: comment.trim() || undefined
|
|
85
|
+
}).pipe(Effect.catchAll((e) => Console.log(`Error: ${e.message}`).pipe(Effect.flatMap(() => Effect.succeed(null)))));
|
|
86
|
+
if (result) {
|
|
87
|
+
const secs = Duration.toSeconds(result.duration);
|
|
88
|
+
const h = Math.floor(secs / 3600);
|
|
89
|
+
const m = Math.floor((secs % 3600) / 60);
|
|
90
|
+
const s = Math.floor(secs % 60);
|
|
91
|
+
yield* Console.log(`Timer stopped: ${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`);
|
|
92
|
+
yield* Console.log(` Clockify: ${result.clockifyLogged ? "✓" : "✗"}`);
|
|
93
|
+
yield* Console.log(` Jira worklog: ${result.jiraWorklogLogged ? "✓" : "skipped"}`);
|
|
94
|
+
}
|
|
95
|
+
}));
|
|
96
|
+
//# sourceMappingURL=stop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop.js","sourceRoot":"","sources":["../../../../src/cli/timer/stop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timer.js","sourceRoot":"","sources":["../../../src/cli/timer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/src/main.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@opentui/react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Bun-only TUI entry point — renders the React app via `@opentui/react`.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { createCliRenderer } from "@opentui/core";
|
|
8
|
+
import { createRoot } from "@opentui/react";
|
|
9
|
+
import { Deferred, Effect } from "effect";
|
|
10
|
+
import { App } from "./tui/App.js";
|
|
11
|
+
const program = Effect.gen(function* () {
|
|
12
|
+
const exitSignal = yield* Deferred.make();
|
|
13
|
+
const renderer = yield* Effect.acquireRelease(Effect.promise(() => createCliRenderer({
|
|
14
|
+
exitOnCtrlC: false
|
|
15
|
+
})), (renderer) => Effect.sync(() => renderer.destroy()));
|
|
16
|
+
const onQuit = () => {
|
|
17
|
+
Effect.runSync(Deferred.succeed(exitSignal, void 0));
|
|
18
|
+
};
|
|
19
|
+
const root = createRoot(renderer);
|
|
20
|
+
root.render(_jsx(App, { onQuit: onQuit }));
|
|
21
|
+
yield* Deferred.await(exitSignal);
|
|
22
|
+
}).pipe(Effect.scoped);
|
|
23
|
+
export default program;
|
|
24
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAElC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAQ,CAAA;IAE/C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAC3C,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAClB,iBAAiB,CAAC;QAChB,WAAW,EAAE,KAAK;KACnB,CAAC,CACH,EACD,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CACpD,CAAA;IAED,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;IACtD,CAAC,CAAA;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAA;IACjC,IAAI,CAAC,MAAM,CAAC,KAAC,GAAG,IAAC,MAAM,EAAE,MAAM,GAAI,CAAC,CAAA;IAEpC,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;AACnC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;AAEtB,eAAe,OAAO,CAAA"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based Clockify API key storage with schema-validated reads.
|
|
3
|
+
*
|
|
4
|
+
* **Mental model**
|
|
5
|
+
*
|
|
6
|
+
* - **Credential file**: Reads/writes `~/.jcf/clockify.json` containing API key, workspace ID,
|
|
7
|
+
* user ID, and base URL. The API key is wrapped in `Redacted` on read.
|
|
8
|
+
* - **Schema-gated**: JSON is parsed then validated via `Schema.decodeUnknown(StoredAuth)` —
|
|
9
|
+
* corrupt data yields {@link ClockifyAuthMissingError} instead of a crash.
|
|
10
|
+
* - **Config-based home dir**: Uses Effect `Config.string("HOME")` for testable env access.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
import * as FileSystem from "@effect/platform/FileSystem";
|
|
15
|
+
import * as Path from "@effect/platform/Path";
|
|
16
|
+
import * as Context from "effect/Context";
|
|
17
|
+
import * as Data from "effect/Data";
|
|
18
|
+
import * as Effect from "effect/Effect";
|
|
19
|
+
import * as Layer from "effect/Layer";
|
|
20
|
+
import * as Redacted from "effect/Redacted";
|
|
21
|
+
import * as Schema from "effect/Schema";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
export class ClockifyAuthMissingError extends Data.TaggedError("ClockifyAuthMissingError") {
|
|
24
|
+
}
|
|
25
|
+
const StoredAuth = Schema.Struct({
|
|
26
|
+
apiKey: Schema.String,
|
|
27
|
+
workspaceId: Schema.String,
|
|
28
|
+
userId: Schema.String,
|
|
29
|
+
baseUrl: Schema.optional(Schema.String)
|
|
30
|
+
});
|
|
31
|
+
export class ClockifyAuth extends Context.Tag("jcf/ClockifyAuth")() {
|
|
32
|
+
}
|
|
33
|
+
const AUTH_DIR = ".jcf";
|
|
34
|
+
const AUTH_FILE = "clockify.json";
|
|
35
|
+
const DEFAULT_BASE_URL = "https://api.clockify.me/api";
|
|
36
|
+
export const layer = Layer.effect(ClockifyAuth, Effect.gen(function* () {
|
|
37
|
+
const fs = yield* FileSystem.FileSystem;
|
|
38
|
+
const path = yield* Path.Path;
|
|
39
|
+
const home = yield* Effect.sync(() => homedir());
|
|
40
|
+
const dir = path.join(home, AUTH_DIR);
|
|
41
|
+
const filePath = path.join(dir, AUTH_FILE);
|
|
42
|
+
const ensureDir = Effect.gen(function* () {
|
|
43
|
+
const exists = yield* fs.exists(dir);
|
|
44
|
+
if (!exists)
|
|
45
|
+
yield* fs.makeDirectory(dir, { recursive: true });
|
|
46
|
+
});
|
|
47
|
+
const decodeStoredAuth = Schema.decodeUnknown(StoredAuth);
|
|
48
|
+
return {
|
|
49
|
+
getConfig: Effect.gen(function* () {
|
|
50
|
+
const exists = yield* fs.exists(filePath);
|
|
51
|
+
if (!exists) {
|
|
52
|
+
return yield* Effect.fail(new ClockifyAuthMissingError({
|
|
53
|
+
message: "Clockify not configured. Run: jcf auth clockify setup"
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
const content = yield* fs.readFileString(filePath).pipe(Effect.mapError(() => new ClockifyAuthMissingError({ message: "Failed to read Clockify auth file" })));
|
|
57
|
+
const json = yield* Effect.try({
|
|
58
|
+
try: () => JSON.parse(content),
|
|
59
|
+
catch: () => new ClockifyAuthMissingError({ message: "Invalid JSON in Clockify auth file" })
|
|
60
|
+
});
|
|
61
|
+
const stored = yield* decodeStoredAuth(json).pipe(Effect.mapError(() => new ClockifyAuthMissingError({ message: "Invalid Clockify auth file schema" })));
|
|
62
|
+
return {
|
|
63
|
+
apiKey: Redacted.make(stored.apiKey),
|
|
64
|
+
workspaceId: stored.workspaceId,
|
|
65
|
+
userId: stored.userId,
|
|
66
|
+
baseUrl: stored.baseUrl ?? DEFAULT_BASE_URL
|
|
67
|
+
};
|
|
68
|
+
}).pipe(Effect.catchTag("SystemError", () => Effect.fail(new ClockifyAuthMissingError({ message: "Failed to read Clockify auth" }))), Effect.catchTag("BadArgument", () => Effect.fail(new ClockifyAuthMissingError({ message: "Invalid Clockify auth file" })))),
|
|
69
|
+
save: (auth) => Effect.gen(function* () {
|
|
70
|
+
yield* ensureDir;
|
|
71
|
+
yield* fs.writeFileString(filePath, JSON.stringify(auth, null, 2));
|
|
72
|
+
yield* Effect.tryPromise(() => import("node:fs/promises").then((f) => f.chmod(filePath, 0o600))).pipe(Effect.catchAll(() => Effect.void));
|
|
73
|
+
}).pipe(Effect.catchAll(() => Effect.void)),
|
|
74
|
+
isConfigured: fs.exists(filePath).pipe(Effect.catchAll(() => Effect.succeed(false)))
|
|
75
|
+
};
|
|
76
|
+
}));
|
|
77
|
+
//# sourceMappingURL=ClockifyAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClockifyAuth.js","sourceRoot":"","sources":["../../../src/services/ClockifyAuth.ts"],"names":[],"mappings":"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"}
|