climemo 0.1.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.
Files changed (76) hide show
  1. package/dist/commands/ask.d.ts +2 -0
  2. package/dist/commands/ask.js +37 -0
  3. package/dist/commands/ask.js.map +1 -0
  4. package/dist/commands/compare.d.ts +2 -0
  5. package/dist/commands/compare.js +57 -0
  6. package/dist/commands/compare.js.map +1 -0
  7. package/dist/commands/config.d.ts +2 -0
  8. package/dist/commands/config.js +37 -0
  9. package/dist/commands/config.js.map +1 -0
  10. package/dist/commands/daemon.d.ts +2 -0
  11. package/dist/commands/daemon.js +69 -0
  12. package/dist/commands/daemon.js.map +1 -0
  13. package/dist/commands/invite.d.ts +2 -0
  14. package/dist/commands/invite.js +49 -0
  15. package/dist/commands/invite.js.map +1 -0
  16. package/dist/commands/login.d.ts +2 -0
  17. package/dist/commands/login.js +94 -0
  18. package/dist/commands/login.js.map +1 -0
  19. package/dist/commands/logout.d.ts +2 -0
  20. package/dist/commands/logout.js +9 -0
  21. package/dist/commands/logout.js.map +1 -0
  22. package/dist/commands/members.d.ts +2 -0
  23. package/dist/commands/members.js +98 -0
  24. package/dist/commands/members.js.map +1 -0
  25. package/dist/commands/merge.d.ts +2 -0
  26. package/dist/commands/merge.js +120 -0
  27. package/dist/commands/merge.js.map +1 -0
  28. package/dist/commands/search.d.ts +2 -0
  29. package/dist/commands/search.js +32 -0
  30. package/dist/commands/search.js.map +1 -0
  31. package/dist/commands/sync.d.ts +2 -0
  32. package/dist/commands/sync.js +200 -0
  33. package/dist/commands/sync.js.map +1 -0
  34. package/dist/commands/token.d.ts +2 -0
  35. package/dist/commands/token.js +60 -0
  36. package/dist/commands/token.js.map +1 -0
  37. package/dist/commands/whoami.d.ts +2 -0
  38. package/dist/commands/whoami.js +20 -0
  39. package/dist/commands/whoami.js.map +1 -0
  40. package/dist/daemon/index.d.ts +2 -0
  41. package/dist/daemon/index.js +27 -0
  42. package/dist/daemon/index.js.map +1 -0
  43. package/dist/daemon/install.d.ts +13 -0
  44. package/dist/daemon/install.js +143 -0
  45. package/dist/daemon/install.js.map +1 -0
  46. package/dist/daemon/logger.d.ts +8 -0
  47. package/dist/daemon/logger.js +41 -0
  48. package/dist/daemon/logger.js.map +1 -0
  49. package/dist/daemon/token-refresher.d.ts +14 -0
  50. package/dist/daemon/token-refresher.js +88 -0
  51. package/dist/daemon/token-refresher.js.map +1 -0
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.js +40 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/lib/api.d.ts +13 -0
  56. package/dist/lib/api.js +18 -0
  57. package/dist/lib/api.js.map +1 -0
  58. package/dist/lib/auth.d.ts +24 -0
  59. package/dist/lib/auth.js +72 -0
  60. package/dist/lib/auth.js.map +1 -0
  61. package/dist/lib/config.d.ts +9 -0
  62. package/dist/lib/config.js +36 -0
  63. package/dist/lib/config.js.map +1 -0
  64. package/dist/lib/git.d.ts +24 -0
  65. package/dist/lib/git.js +63 -0
  66. package/dist/lib/git.js.map +1 -0
  67. package/dist/lib/keychain.d.ts +4 -0
  68. package/dist/lib/keychain.js +39 -0
  69. package/dist/lib/keychain.js.map +1 -0
  70. package/dist/lib/project.d.ts +13 -0
  71. package/dist/lib/project.js +133 -0
  72. package/dist/lib/project.js.map +1 -0
  73. package/dist/lib/server.d.ts +9 -0
  74. package/dist/lib/server.js +80 -0
  75. package/dist/lib/server.js.map +1 -0
  76. package/package.json +48 -0
@@ -0,0 +1,4 @@
1
+ export declare function saveTokens(accessToken: string, refreshToken: string): Promise<void>;
2
+ export declare function getAccessToken(): Promise<string | null>;
3
+ export declare function getRefreshToken(): Promise<string | null>;
4
+ export declare function clearTokens(): Promise<void>;
@@ -0,0 +1,39 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ const CREDENTIALS_DIR = join(homedir(), ".climemo");
5
+ const CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json");
6
+ function ensureDir() {
7
+ if (!existsSync(CREDENTIALS_DIR)) {
8
+ mkdirSync(CREDENTIALS_DIR, { recursive: true });
9
+ }
10
+ }
11
+ function readCredentials() {
12
+ if (!existsSync(CREDENTIALS_FILE))
13
+ return {};
14
+ try {
15
+ return JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
16
+ }
17
+ catch {
18
+ return {};
19
+ }
20
+ }
21
+ function writeCredentials(creds) {
22
+ ensureDir();
23
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(creds, null, 2), { mode: 0o600 });
24
+ }
25
+ export async function saveTokens(accessToken, refreshToken) {
26
+ writeCredentials({ access_token: accessToken, refresh_token: refreshToken });
27
+ }
28
+ export async function getAccessToken() {
29
+ return readCredentials().access_token ?? null;
30
+ }
31
+ export async function getRefreshToken() {
32
+ return readCredentials().refresh_token ?? null;
33
+ }
34
+ export async function clearTokens() {
35
+ if (existsSync(CREDENTIALS_FILE)) {
36
+ unlinkSync(CREDENTIALS_FILE);
37
+ }
38
+ }
39
+ //# sourceMappingURL=keychain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../src/lib/keychain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAa,MAAM,IAAI,CAAC;AAC/F,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACpD,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,EAAE,kBAAkB,CAAC,CAAC;AAOnE,SAAS,SAAS;IAChB,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,SAAS,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED,SAAS,eAAe;IACtB,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,EAAE,CAAC;IAC7C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAkB;IAC1C,SAAS,EAAE,CAAC;IACZ,aAAa,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,WAAmB,EAAE,YAAoB;IACxE,gBAAgB,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,OAAO,eAAe,EAAE,CAAC,YAAY,IAAI,IAAI,CAAC;AAChD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,OAAO,eAAe,EAAE,CAAC,aAAa,IAAI,IAAI,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ /** Pattern to add to .gitignore (exported for reference) */
2
+ export declare const CLIMEMO_GITIGNORE_PATTERN = ".climemo";
3
+ /**
4
+ * Resolve the current project from git repo.
5
+ *
6
+ * Priority when multiple projects share the same slug:
7
+ * 1. Project in a workspace where I'm NOT the owner (= team workspace, someone invited me)
8
+ * 2. Project in my own workspace (fallback)
9
+ *
10
+ * This ensures that after being invited to a team, you connect to
11
+ * the team's project instead of your personal copy.
12
+ */
13
+ export declare function resolveProject(token?: string): Promise<string>;
@@ -0,0 +1,133 @@
1
+ import { readFileSync, writeFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { getGitInfo } from "./git.js";
4
+ import { apiRequest } from "./api.js";
5
+ /** Path to the local project anchor file in the git root */
6
+ function getClimemoFilePath(gitRoot) {
7
+ return join(gitRoot, ".climemo");
8
+ }
9
+ /** Read the stored project ID from .climemo, if it exists */
10
+ function readStoredProjectId(gitRoot) {
11
+ try {
12
+ const raw = readFileSync(getClimemoFilePath(gitRoot), "utf-8");
13
+ const data = JSON.parse(raw);
14
+ return typeof data.project_id === "string" ? data.project_id : null;
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ /** Write the project ID to .climemo in the git root */
21
+ function writeStoredProjectId(gitRoot, projectId) {
22
+ try {
23
+ writeFileSync(getClimemoFilePath(gitRoot), JSON.stringify({ project_id: projectId }, null, 2) + "\n", "utf-8");
24
+ }
25
+ catch {
26
+ // Non-fatal: the file may be in a read-only location
27
+ }
28
+ }
29
+ /** Pattern to add to .gitignore (exported for reference) */
30
+ export const CLIMEMO_GITIGNORE_PATTERN = ".climemo";
31
+ /**
32
+ * Resolve the current project from git repo.
33
+ *
34
+ * Priority when multiple projects share the same slug:
35
+ * 1. Project in a workspace where I'm NOT the owner (= team workspace, someone invited me)
36
+ * 2. Project in my own workspace (fallback)
37
+ *
38
+ * This ensures that after being invited to a team, you connect to
39
+ * the team's project instead of your personal copy.
40
+ */
41
+ export async function resolveProject(token) {
42
+ const git = getGitInfo();
43
+ if (!git) {
44
+ throw new Error("Not inside a git repository. Run climemo from a git repo, or use --project to specify a project ID.");
45
+ }
46
+ // Check .climemo file first — this survives repo renames
47
+ const storedProjectId = readStoredProjectId(git.rootPath);
48
+ if (storedProjectId) {
49
+ // Verify the stored project is still accessible
50
+ const checkRes = await apiRequest(`/api/projects/${storedProjectId}`, { token });
51
+ if (checkRes.ok && checkRes.data) {
52
+ return storedProjectId;
53
+ }
54
+ // Project no longer accessible — fall through to slug-based resolution
55
+ }
56
+ // Fetch orgs and projects in parallel
57
+ const [projRes, orgRes] = await Promise.all([
58
+ apiRequest("/api/projects", { token }),
59
+ apiRequest("/api/organizations", { token }),
60
+ ]);
61
+ if (!projRes.ok || !projRes.data) {
62
+ throw new Error("Failed to fetch projects. Are you logged in?");
63
+ }
64
+ const projects = projRes.data.data || [];
65
+ const orgs = orgRes.ok && orgRes.data
66
+ ? orgRes.data.data || []
67
+ : [];
68
+ const ownedOrgIds = new Set(orgs.filter((o) => o.role === "owner").map((o) => o.id));
69
+ // Build a lookup of org names for display
70
+ const orgNameById = new Map(orgs.map((o) => [o.id, o.name]));
71
+ // Find all projects matching this repo's slug
72
+ const matches = projects.filter((p) => p.slug === git.slug);
73
+ if (matches.length > 0) {
74
+ // Separate team projects (not owned) from personal projects (owned)
75
+ const teamProjects = matches.filter((p) => !ownedOrgIds.has(p.organization_id));
76
+ const personalProjects = matches.filter((p) => ownedOrgIds.has(p.organization_id));
77
+ // Multiple team projects match — ambiguous, ask the user to pick
78
+ if (teamProjects.length > 1) {
79
+ const lines = teamProjects.map((p) => {
80
+ const orgName = orgNameById.get(p.organization_id) || p.organization_id;
81
+ return ` - ${p.id} (team: ${orgName})`;
82
+ });
83
+ throw new Error(`Multiple team projects match slug "${git.slug}":\n` +
84
+ lines.join("\n") +
85
+ "\n\n" +
86
+ "Use --project <ID> to specify which project to use.\n" +
87
+ "The choice will be saved in .climemo so you won't need to specify it again.");
88
+ }
89
+ let picked;
90
+ if (teamProjects.length === 1) {
91
+ picked = teamProjects[0];
92
+ // If there's also a personal copy, suggest cleanup
93
+ if (personalProjects.length > 0) {
94
+ const personalProject = personalProjects[0];
95
+ console.error(`\u2139 Using team project (${picked.id}). ` +
96
+ `You also have a personal copy (${personalProject.id}) \u2014 ` +
97
+ `run "climemo merge ${personalProject.id}" to merge and clean up.`);
98
+ }
99
+ }
100
+ else {
101
+ // No team projects — use first personal project
102
+ picked = matches[0];
103
+ }
104
+ writeStoredProjectId(git.rootPath, picked.id);
105
+ return picked.id;
106
+ }
107
+ // No matching project → auto-create in my owned workspace
108
+ const ownedOrg = orgs.find((o) => o.role === "owner");
109
+ const orgId = ownedOrg?.id || orgs[0]?.id;
110
+ if (!orgId) {
111
+ throw new Error("No workspace found. Please log in via web first.");
112
+ }
113
+ const createRes = await apiRequest("/api/projects", {
114
+ method: "POST",
115
+ body: {
116
+ name: git.name,
117
+ slug: git.slug,
118
+ description: git.remoteUrl
119
+ ? `Auto-created from ${git.remoteUrl}`
120
+ : `Auto-created from local repo: ${git.rootPath}`,
121
+ organization_id: orgId,
122
+ },
123
+ token,
124
+ });
125
+ if (!createRes.ok || !createRes.data) {
126
+ throw new Error(`Failed to auto-create project "${git.name}": ${JSON.stringify(createRes.data)}`);
127
+ }
128
+ const newProject = createRes.data.data;
129
+ writeStoredProjectId(git.rootPath, newProject.id);
130
+ console.error(`✓ Project "${git.name}" auto-created (${newProject.id})`);
131
+ return newProject.id;
132
+ }
133
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/lib/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Start a local HTTP server to receive the OAuth callback.
3
+ * Returns the auth code and port used.
4
+ */
5
+ export declare function startCallbackServer(): Promise<{
6
+ code: string;
7
+ port: number;
8
+ close: () => void;
9
+ }>;
@@ -0,0 +1,80 @@
1
+ import { createServer } from "http";
2
+ /**
3
+ * Start a local HTTP server to receive the OAuth callback.
4
+ * Returns the auth code and port used.
5
+ */
6
+ export function startCallbackServer() {
7
+ return new Promise((resolve, reject) => {
8
+ const server = createServer((req, res) => {
9
+ const url = new URL(req.url || "/", `http://localhost`);
10
+ const code = url.searchParams.get("code");
11
+ const error = url.searchParams.get("error");
12
+ if (error) {
13
+ res.writeHead(200, { "Content-Type": "text/html" });
14
+ res.end(getErrorHtml(error));
15
+ reject(new Error(`Auth failed: ${error}`));
16
+ return;
17
+ }
18
+ if (code) {
19
+ res.writeHead(200, { "Content-Type": "text/html" });
20
+ res.end(getSuccessHtml());
21
+ resolve({
22
+ code,
23
+ port: server.address().port,
24
+ close: () => server.close(),
25
+ });
26
+ }
27
+ });
28
+ // Listen on a random available port
29
+ server.listen(0, "127.0.0.1", () => {
30
+ // Auto-timeout after 5 minutes
31
+ setTimeout(() => {
32
+ server.close();
33
+ reject(new Error("Login timed out. Please try again."));
34
+ }, 5 * 60 * 1000);
35
+ });
36
+ server.on("error", reject);
37
+ });
38
+ }
39
+ function getSuccessHtml() {
40
+ return `<!DOCTYPE html>
41
+ <html>
42
+ <head><title>Climemo Login</title>
43
+ <style>
44
+ body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #fafafa; }
45
+ .card { text-align: center; padding: 3rem; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
46
+ h1 { color: #18181b; margin-bottom: 0.5rem; }
47
+ p { color: #71717a; }
48
+ .check { font-size: 3rem; margin-bottom: 1rem; }
49
+ </style>
50
+ </head>
51
+ <body>
52
+ <div class="card">
53
+ <div class="check">✓</div>
54
+ <h1>Login successful!</h1>
55
+ <p>You can close this window and return to the terminal.</p>
56
+ </div>
57
+ </body>
58
+ </html>`;
59
+ }
60
+ function getErrorHtml(error) {
61
+ return `<!DOCTYPE html>
62
+ <html>
63
+ <head><title>Climemo Login Failed</title>
64
+ <style>
65
+ body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #fafafa; }
66
+ .card { text-align: center; padding: 3rem; background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
67
+ h1 { color: #dc2626; margin-bottom: 0.5rem; }
68
+ p { color: #71717a; }
69
+ </style>
70
+ </head>
71
+ <body>
72
+ <div class="card">
73
+ <h1>Login failed</h1>
74
+ <p>${error}</p>
75
+ <p>Please try again with <code>climemo login</code></p>
76
+ </div>
77
+ </body>
78
+ </html>`;
79
+ }
80
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/lib/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA6C,MAAM,MAAM,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAoB,EAAE,GAAmB,EAAE,EAAE;YACxE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;YACxD,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC1C,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAE5C,IAAI,KAAK,EAAE,CAAC;gBACV,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;gBACpD,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC7B,MAAM,CAAC,IAAI,KAAK,CAAC,gBAAgB,KAAK,EAAE,CAAC,CAAC,CAAC;gBAC3C,OAAO;YACT,CAAC;YAED,IAAI,IAAI,EAAE,CAAC;gBACT,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;gBACpD,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;gBAC1B,OAAO,CAAC;oBACN,IAAI;oBACJ,IAAI,EAAG,MAAM,CAAC,OAAO,EAAuB,CAAC,IAAI;oBACjD,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;iBAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,oCAAoC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACjC,+BAA+B;YAC/B,UAAU,CAAC,GAAG,EAAE;gBACd,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC,CAAC;YAC1D,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,cAAc;IACrB,OAAO;;;;;;;;;;;;;;;;;;QAkBD,CAAC;AACT,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO;;;;;;;;;;;;;SAaA,KAAK;;;;QAIN,CAAC;AACT,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "climemo",
3
+ "version": "0.1.0",
4
+ "description": "AI 시대의 프로젝트 지식 관리 도구 - CLI for syncing project knowledge",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/runaidev/climemo"
9
+ },
10
+ "homepage": "https://climemo.com",
11
+ "keywords": [
12
+ "ai",
13
+ "knowledge-management",
14
+ "documentation",
15
+ "cli",
16
+ "claude",
17
+ "cursor",
18
+ "codex",
19
+ "windsurf"
20
+ ],
21
+ "type": "module",
22
+ "bin": {
23
+ "climemo": "./dist/index.js"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "engines": {
29
+ "node": ">=18"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "dev": "tsc --watch",
34
+ "lint": "tsc --noEmit",
35
+ "prepublishOnly": "npm run build"
36
+ },
37
+ "dependencies": {
38
+ "chalk": "^5.4.0",
39
+ "commander": "^13.0.0",
40
+ "open": "^10.1.0",
41
+ "ora": "^8.1.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^22.0.0",
45
+ "tsx": "^4.21.0",
46
+ "typescript": "^5.7.0"
47
+ }
48
+ }