@usemeno/meno-cli 0.1.0 → 0.1.1

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,61 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ const SVG_EMBED = `<?xml version="1.0" encoding="UTF-8"?>
5
+ <svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.44 45.4">
6
+ <g id="MenoLogo">
7
+ <path d="M43.78,28.72l-3.22-14.3-13.31,13.4c-2.15-1.65-3.92-3.4-5.43-5.75L42.15.78c.61-.64,3.06-1.02,3.67-.61s1.52,1.79,1.66,2.64l4.05,25.48-7.75.43Z"/>
8
+ <path d="M19.61,20.03l-6.25-5.12-2.48,13.76-8.11-.12L7.13,1.25c.22-1.13,4.18-1.52,5.05-.57l12.62,13.69-5.2,5.66Z"/>
9
+ <path d="M55.44,38.35c0,3.89-3.15,7.04-7.04,7.04-2.53,0-4.77-1.35-6.01-3.37H.36c-.4-1.61-.44-3.89-.23-6.03l41.85-.55c1.1-2.45,3.57-4.14,6.42-4.14,3.9,0,7.04,3.15,7.04,7.04Z"/>
10
+ </g>
11
+ </svg>`;
12
+
13
+ function fallbackAscii() {
14
+ return [
15
+ " ____ __ ",
16
+ " /\\ / __ \\/ /___ _ _ ",
17
+ " / \\ _ __ / / / / / __ \\ | | |",
18
+ " / /\\ \\ | '_ \\ / /_/ / / /_/ / |_| |",
19
+ " / ____ \\| | | |\\____/_/\\____/\\__,_|",
20
+ " /_/ \\_\\_| |_| ",
21
+ "",
22
+ " M E N O (logo) ",
23
+ ].join('\n');
24
+ }
25
+
26
+ export async function printLogoAscii(width = 64) {
27
+ try {
28
+ const sharpImport = await import("sharp");
29
+ const sharp: any = sharpImport.default ?? sharpImport;
30
+
31
+ const svgBuffer = Buffer.from(SVG_EMBED, "utf8");
32
+ const img = sharp(svgBuffer).resize({ width }).flatten({ background: { r: 255, g: 255, b: 255 } }).grayscale();
33
+ const { data, info } = await img.raw().toBuffer({ resolveWithObject: true });
34
+
35
+ const chars = " .,:;i1tfLCG08@";
36
+ const rows: string[] = [];
37
+ const aspect = 0.5;
38
+ const rowStep = Math.max(1, Math.round(1 / aspect));
39
+
40
+ for (let y = 0; y < info.height; y += rowStep) {
41
+ let line = "";
42
+ for (let x = 0; x < info.width; x++) {
43
+ const idx = y * info.width + x;
44
+ const val = data[idx];
45
+ const charIdx = Math.floor((1 - val / 255) * (chars.length - 1));
46
+ line += chars[charIdx];
47
+ }
48
+ rows.push(line);
49
+ }
50
+
51
+ console.log(rows.join("\n"));
52
+ return;
53
+ } catch (err) {
54
+ console.log(fallbackAscii());
55
+ return;
56
+ }
57
+ }
58
+
59
+ if (require.main === module) {
60
+ printLogoAscii().catch(() => {});
61
+ }
package/src/utils/api.ts CHANGED
@@ -111,3 +111,82 @@ export interface Stats {
111
111
  entriesCount: number;
112
112
  currency: string;
113
113
  }
114
+
115
+ export interface Task {
116
+ id: string;
117
+ title: string;
118
+ description?: string;
119
+ projectId: string;
120
+ status: "Backlog" | "Todo" | "InProgress" | "Review" | "Done";
121
+ estimatedHours?: number;
122
+ priority?: string;
123
+ project: {
124
+ id: string;
125
+ name: string;
126
+ hourlyRate: number;
127
+ client?: {
128
+ name: string;
129
+ company?: string;
130
+ };
131
+ };
132
+ }
133
+
134
+ export interface CliStartResponse {
135
+ success: boolean;
136
+ message: string;
137
+ timer?: {
138
+ id: string;
139
+ taskId: string;
140
+ startTime: string;
141
+ };
142
+ task?: Task;
143
+ }
144
+
145
+ export interface CliStopResponse {
146
+ success: boolean;
147
+ message: string;
148
+ entry?: {
149
+ id: string;
150
+ duration: number;
151
+ amount: number;
152
+ description: string;
153
+ };
154
+ evidence?: {
155
+ id: string;
156
+ commitHash: string;
157
+ repoUrl: string;
158
+ };
159
+ }
160
+
161
+ export interface CliLogResponse {
162
+ success: boolean;
163
+ message: string;
164
+ entry?: TimeEntry;
165
+ task?: Task;
166
+ }
167
+
168
+ // Fetch all tasks from Kanban board
169
+ export async function getTasks(): Promise<Task[]> {
170
+ const response = await apiRequest<{ tasks: Task[] }>("/api/kanban/tasks");
171
+ return response.tasks || [];
172
+ }
173
+
174
+ // CLI actions: start, stop, log
175
+ export async function cliAction(
176
+ action: "start" | "stop" | "log",
177
+ payload: {
178
+ taskId?: string;
179
+ description?: string;
180
+ commitHash?: string;
181
+ repoUrl?: string;
182
+ done?: boolean;
183
+ }
184
+ ): Promise<CliStartResponse | CliStopResponse | CliLogResponse> {
185
+ return await apiRequest("/api/cli", {
186
+ method: "POST",
187
+ body: JSON.stringify({
188
+ action,
189
+ ...payload,
190
+ }),
191
+ });
192
+ }
@@ -0,0 +1,62 @@
1
+ import { execSync } from "child_process";
2
+
3
+ export interface GitInfo {
4
+ commitHash: string;
5
+ repoUrl: string;
6
+ }
7
+
8
+ /**
9
+ * Get current git commit hash and repository URL
10
+ * Returns null if not in a git repository or if git commands fail
11
+ */
12
+ export function getGitInfo(): GitInfo | null {
13
+ try {
14
+ // Get commit hash
15
+ const commitHash = execSync("git rev-parse HEAD", {
16
+ encoding: "utf8",
17
+ stdio: ["pipe", "pipe", "ignore"], // Suppress stderr
18
+ }).trim();
19
+
20
+ // Get remote URL
21
+ let repoUrl = execSync("git config --get remote.origin.url", {
22
+ encoding: "utf8",
23
+ stdio: ["pipe", "pipe", "ignore"],
24
+ }).trim();
25
+
26
+ // Convert SSH URLs to HTTPS format
27
+ if (repoUrl.startsWith("git@github.com:")) {
28
+ repoUrl = repoUrl
29
+ .replace("git@github.com:", "https://github.com/")
30
+ .replace(/\.git$/, "");
31
+ } else if (repoUrl.endsWith(".git")) {
32
+ repoUrl = repoUrl.replace(/\.git$/, "");
33
+ }
34
+
35
+ if (!commitHash || !repoUrl) {
36
+ return null;
37
+ }
38
+
39
+ return {
40
+ commitHash,
41
+ repoUrl,
42
+ };
43
+ } catch (error) {
44
+ // Not in a git repository or git not available
45
+ return null;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Check if current directory is inside a git repository
51
+ */
52
+ export function isGitRepository(): boolean {
53
+ try {
54
+ execSync("git rev-parse --is-inside-work-tree", {
55
+ encoding: "utf8",
56
+ stdio: ["pipe", "pipe", "ignore"],
57
+ });
58
+ return true;
59
+ } catch (error) {
60
+ return false;
61
+ }
62
+ }