dx-devkit 0.0.0-stage → 1.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 (172) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/LICENSE +21 -0
  3. package/README.md +475 -2
  4. package/bin/dx-mcp.js +2 -0
  5. package/dist/command.d.ts +9 -0
  6. package/dist/command.d.ts.map +1 -0
  7. package/dist/command.js +2 -0
  8. package/dist/command.js.map +1 -0
  9. package/dist/commands/ai.d.ts +7 -0
  10. package/dist/commands/ai.d.ts.map +1 -0
  11. package/dist/commands/ai.js +148 -0
  12. package/dist/commands/ai.js.map +1 -0
  13. package/dist/commands/clean.d.ts +3 -0
  14. package/dist/commands/clean.d.ts.map +1 -0
  15. package/dist/commands/clean.js +72 -0
  16. package/dist/commands/clean.js.map +1 -0
  17. package/dist/commands/deps.d.ts +3 -0
  18. package/dist/commands/deps.d.ts.map +1 -0
  19. package/dist/commands/deps.js +98 -0
  20. package/dist/commands/deps.js.map +1 -0
  21. package/dist/commands/go.d.ts +3 -0
  22. package/dist/commands/go.d.ts.map +1 -0
  23. package/dist/commands/go.js +202 -0
  24. package/dist/commands/go.js.map +1 -0
  25. package/dist/commands/log.d.ts +3 -0
  26. package/dist/commands/log.d.ts.map +1 -0
  27. package/dist/commands/log.js +51 -0
  28. package/dist/commands/log.js.map +1 -0
  29. package/dist/commands/new.d.ts +3 -0
  30. package/dist/commands/new.d.ts.map +1 -0
  31. package/dist/commands/new.js +117 -0
  32. package/dist/commands/new.js.map +1 -0
  33. package/dist/commands/port.d.ts +4 -0
  34. package/dist/commands/port.d.ts.map +1 -0
  35. package/dist/commands/port.js +106 -0
  36. package/dist/commands/port.js.map +1 -0
  37. package/dist/commands/projects.d.ts +5 -0
  38. package/dist/commands/projects.d.ts.map +1 -0
  39. package/dist/commands/projects.js +46 -0
  40. package/dist/commands/projects.js.map +1 -0
  41. package/dist/commands/stack.d.ts +34 -0
  42. package/dist/commands/stack.d.ts.map +1 -0
  43. package/dist/commands/stack.js +140 -0
  44. package/dist/commands/stack.js.map +1 -0
  45. package/dist/commands/summary.d.ts +3 -0
  46. package/dist/commands/summary.d.ts.map +1 -0
  47. package/dist/commands/summary.js +88 -0
  48. package/dist/commands/summary.js.map +1 -0
  49. package/dist/commands/system.d.ts +5 -0
  50. package/dist/commands/system.d.ts.map +1 -0
  51. package/dist/commands/system.js +104 -0
  52. package/dist/commands/system.js.map +1 -0
  53. package/dist/commands/ui.d.ts +3 -0
  54. package/dist/commands/ui.d.ts.map +1 -0
  55. package/dist/commands/ui.js +96 -0
  56. package/dist/commands/ui.js.map +1 -0
  57. package/dist/index.d.ts +3 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +103 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/ui.d.ts +27 -0
  62. package/dist/ui.d.ts.map +1 -0
  63. package/dist/ui.js +104 -0
  64. package/dist/ui.js.map +1 -0
  65. package/node_modules/@dx/core/dist/config.d.ts +46 -0
  66. package/node_modules/@dx/core/dist/config.d.ts.map +1 -0
  67. package/node_modules/@dx/core/dist/config.js +131 -0
  68. package/node_modules/@dx/core/dist/config.js.map +1 -0
  69. package/node_modules/@dx/core/dist/fuzzy.d.ts +15 -0
  70. package/node_modules/@dx/core/dist/fuzzy.d.ts.map +1 -0
  71. package/node_modules/@dx/core/dist/fuzzy.js +63 -0
  72. package/node_modules/@dx/core/dist/fuzzy.js.map +1 -0
  73. package/node_modules/@dx/core/dist/index.d.ts +17 -0
  74. package/node_modules/@dx/core/dist/index.d.ts.map +1 -0
  75. package/node_modules/@dx/core/dist/index.js +19 -0
  76. package/node_modules/@dx/core/dist/index.js.map +1 -0
  77. package/node_modules/@dx/core/dist/runs.d.ts +19 -0
  78. package/node_modules/@dx/core/dist/runs.d.ts.map +1 -0
  79. package/node_modules/@dx/core/dist/runs.js +41 -0
  80. package/node_modules/@dx/core/dist/runs.js.map +1 -0
  81. package/node_modules/@dx/core/dist/tools/aiSetup.d.ts +58 -0
  82. package/node_modules/@dx/core/dist/tools/aiSetup.d.ts.map +1 -0
  83. package/node_modules/@dx/core/dist/tools/aiSetup.js +243 -0
  84. package/node_modules/@dx/core/dist/tools/aiSetup.js.map +1 -0
  85. package/node_modules/@dx/core/dist/tools/cleanup.d.ts +61 -0
  86. package/node_modules/@dx/core/dist/tools/cleanup.d.ts.map +1 -0
  87. package/node_modules/@dx/core/dist/tools/cleanup.js +171 -0
  88. package/node_modules/@dx/core/dist/tools/cleanup.js.map +1 -0
  89. package/node_modules/@dx/core/dist/tools/deps.d.ts +57 -0
  90. package/node_modules/@dx/core/dist/tools/deps.d.ts.map +1 -0
  91. package/node_modules/@dx/core/dist/tools/deps.js +151 -0
  92. package/node_modules/@dx/core/dist/tools/deps.js.map +1 -0
  93. package/node_modules/@dx/core/dist/tools/devErrors.d.ts +74 -0
  94. package/node_modules/@dx/core/dist/tools/devErrors.d.ts.map +1 -0
  95. package/node_modules/@dx/core/dist/tools/devErrors.js +261 -0
  96. package/node_modules/@dx/core/dist/tools/devErrors.js.map +1 -0
  97. package/node_modules/@dx/core/dist/tools/doctor.d.ts +12 -0
  98. package/node_modules/@dx/core/dist/tools/doctor.d.ts.map +1 -0
  99. package/node_modules/@dx/core/dist/tools/doctor.js +176 -0
  100. package/node_modules/@dx/core/dist/tools/doctor.js.map +1 -0
  101. package/node_modules/@dx/core/dist/tools/github.d.ts +31 -0
  102. package/node_modules/@dx/core/dist/tools/github.d.ts.map +1 -0
  103. package/node_modules/@dx/core/dist/tools/github.js +80 -0
  104. package/node_modules/@dx/core/dist/tools/github.js.map +1 -0
  105. package/node_modules/@dx/core/dist/tools/journal.d.ts +23 -0
  106. package/node_modules/@dx/core/dist/tools/journal.d.ts.map +1 -0
  107. package/node_modules/@dx/core/dist/tools/journal.js +80 -0
  108. package/node_modules/@dx/core/dist/tools/journal.js.map +1 -0
  109. package/node_modules/@dx/core/dist/tools/launch.d.ts +24 -0
  110. package/node_modules/@dx/core/dist/tools/launch.d.ts.map +1 -0
  111. package/node_modules/@dx/core/dist/tools/launch.js +137 -0
  112. package/node_modules/@dx/core/dist/tools/launch.js.map +1 -0
  113. package/node_modules/@dx/core/dist/tools/ports.d.ts +42 -0
  114. package/node_modules/@dx/core/dist/tools/ports.d.ts.map +1 -0
  115. package/node_modules/@dx/core/dist/tools/ports.js +220 -0
  116. package/node_modules/@dx/core/dist/tools/ports.js.map +1 -0
  117. package/node_modules/@dx/core/dist/tools/projects.d.ts +58 -0
  118. package/node_modules/@dx/core/dist/tools/projects.d.ts.map +1 -0
  119. package/node_modules/@dx/core/dist/tools/projects.js +208 -0
  120. package/node_modules/@dx/core/dist/tools/projects.js.map +1 -0
  121. package/node_modules/@dx/core/dist/tools/scaffold.d.ts +40 -0
  122. package/node_modules/@dx/core/dist/tools/scaffold.d.ts.map +1 -0
  123. package/node_modules/@dx/core/dist/tools/scaffold.js +188 -0
  124. package/node_modules/@dx/core/dist/tools/scaffold.js.map +1 -0
  125. package/node_modules/@dx/core/dist/tools/stacks.d.ts +119 -0
  126. package/node_modules/@dx/core/dist/tools/stacks.d.ts.map +1 -0
  127. package/node_modules/@dx/core/dist/tools/stacks.js +386 -0
  128. package/node_modules/@dx/core/dist/tools/stacks.js.map +1 -0
  129. package/node_modules/@dx/core/dist/tools/summary.d.ts +87 -0
  130. package/node_modules/@dx/core/dist/tools/summary.d.ts.map +1 -0
  131. package/node_modules/@dx/core/dist/tools/summary.js +331 -0
  132. package/node_modules/@dx/core/dist/tools/summary.js.map +1 -0
  133. package/node_modules/@dx/core/package.json +20 -0
  134. package/node_modules/@dx/mcp/dist/index.d.ts +3 -0
  135. package/node_modules/@dx/mcp/dist/index.d.ts.map +1 -0
  136. package/node_modules/@dx/mcp/dist/index.js +540 -0
  137. package/node_modules/@dx/mcp/dist/index.js.map +1 -0
  138. package/node_modules/@dx/mcp/package.json +8 -0
  139. package/node_modules/@dx/web/dist/api.d.ts +13 -0
  140. package/node_modules/@dx/web/dist/api.d.ts.map +1 -0
  141. package/node_modules/@dx/web/dist/api.js +351 -0
  142. package/node_modules/@dx/web/dist/api.js.map +1 -0
  143. package/node_modules/@dx/web/dist/background.d.ts +2 -0
  144. package/node_modules/@dx/web/dist/background.d.ts.map +1 -0
  145. package/node_modules/@dx/web/dist/background.js +24 -0
  146. package/node_modules/@dx/web/dist/background.js.map +1 -0
  147. package/node_modules/@dx/web/dist/check.d.ts +39 -0
  148. package/node_modules/@dx/web/dist/check.d.ts.map +1 -0
  149. package/node_modules/@dx/web/dist/check.js +97 -0
  150. package/node_modules/@dx/web/dist/check.js.map +1 -0
  151. package/node_modules/@dx/web/dist/dev.d.ts +109 -0
  152. package/node_modules/@dx/web/dist/dev.d.ts.map +1 -0
  153. package/node_modules/@dx/web/dist/dev.js +645 -0
  154. package/node_modules/@dx/web/dist/dev.js.map +1 -0
  155. package/node_modules/@dx/web/dist/link.d.ts +45 -0
  156. package/node_modules/@dx/web/dist/link.d.ts.map +1 -0
  157. package/node_modules/@dx/web/dist/link.js +150 -0
  158. package/node_modules/@dx/web/dist/link.js.map +1 -0
  159. package/node_modules/@dx/web/dist/proxy.d.ts +21 -0
  160. package/node_modules/@dx/web/dist/proxy.d.ts.map +1 -0
  161. package/node_modules/@dx/web/dist/proxy.js +140 -0
  162. package/node_modules/@dx/web/dist/proxy.js.map +1 -0
  163. package/node_modules/@dx/web/dist/server.d.ts +33 -0
  164. package/node_modules/@dx/web/dist/server.d.ts.map +1 -0
  165. package/node_modules/@dx/web/dist/server.js +184 -0
  166. package/node_modules/@dx/web/dist/server.js.map +1 -0
  167. package/node_modules/@dx/web/package.json +26 -0
  168. package/node_modules/@dx/web/public/app.js +1553 -0
  169. package/node_modules/@dx/web/public/icon.svg +1 -0
  170. package/node_modules/@dx/web/public/index.html +47 -0
  171. package/node_modules/@dx/web/public/styles.css +1124 -0
  172. package/package.json +52 -4
@@ -0,0 +1,176 @@
1
+ import { execFile } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import { promisify } from "node:util";
4
+ import { configPath, dxHome, dxPath, ensureDxHome, loadConfig } from "../config.js";
5
+ import { listAiTools } from "./aiSetup.js";
6
+ import { listListeningPorts } from "./ports.js";
7
+ import { scanProjects } from "./projects.js";
8
+ import { detectStack } from "./stacks.js";
9
+ const run = promisify(execFile);
10
+ /** Checks that everything dx relies on works on this machine. */
11
+ export async function runDoctor() {
12
+ const checks = [];
13
+ const major = Number(process.versions.node.split(".")[0]);
14
+ checks.push({
15
+ name: "Node.js",
16
+ status: major >= 22 ? "ok" : "fail",
17
+ detail: major >= 22 ? `v${process.versions.node}` : `v${process.versions.node}; dx and Raycast need Node 22 or newer`,
18
+ });
19
+ try {
20
+ ensureDxHome();
21
+ fs.accessSync(dxHome(), fs.constants.W_OK);
22
+ checks.push({ name: "dx folder", status: "ok", detail: dxHome() });
23
+ }
24
+ catch (err) {
25
+ checks.push({ name: "dx folder", status: "fail", detail: `${dxHome()} isn't writable: ${err.message}` });
26
+ return checks;
27
+ }
28
+ let config;
29
+ try {
30
+ config = loadConfig();
31
+ checks.push({ name: "Config", status: "ok", detail: configPath() });
32
+ }
33
+ catch (err) {
34
+ checks.push({ name: "Config", status: "fail", detail: err.message });
35
+ return checks;
36
+ }
37
+ for (const root of config.projectsRoots) {
38
+ const exists = fs.existsSync(root);
39
+ checks.push({
40
+ name: "Projects root",
41
+ status: exists ? "ok" : "fail",
42
+ detail: exists ? root : `${root} doesn't exist`,
43
+ fix: exists ? undefined : `dx config add-root <your code folder>, or remove it from "projectsRoots" in ${configPath()}`,
44
+ });
45
+ }
46
+ const projects = scanProjects(config);
47
+ checks.push({
48
+ name: "Projects found",
49
+ status: projects.length > 0 ? "ok" : "warn",
50
+ detail: projects.length > 0 ? `${projects.length}` : "none in your projects folders",
51
+ fix: projects.length > 0 ? undefined : "dx config add-root <the folder that holds your projects>",
52
+ });
53
+ const editor = await commandExists(config.editor);
54
+ checks.push({
55
+ name: "Editor",
56
+ status: editor ? "ok" : "warn",
57
+ detail: editor ? `${config.editor} → ${editor}` : `"${config.editor}" isn't on PATH`,
58
+ fix: editor ? undefined : "dx config editor <command>, e.g. code, cursor or zed",
59
+ });
60
+ try {
61
+ const ports = await listListeningPorts();
62
+ checks.push({ name: "Port listing", status: "ok", detail: `${ports.length} listening port(s) visible` });
63
+ }
64
+ catch (err) {
65
+ checks.push({ name: "Port listing", status: "fail", detail: err.message, fix: lsofFix() });
66
+ }
67
+ if (process.platform === "linux") {
68
+ const clip = (await commandExists("wl-copy")) ?? (await commandExists("xclip"));
69
+ checks.push({
70
+ name: "Clipboard",
71
+ status: clip ? "ok" : "warn",
72
+ detail: clip ? clip : "neither wl-copy nor xclip is installed; dx summary --copy won't work",
73
+ fix: clip ? undefined : linuxInstall(process.env.WAYLAND_DISPLAY ? "wl-clipboard" : "xclip"),
74
+ });
75
+ }
76
+ // Docker matters only when a project's stack has a database in docker-compose.
77
+ const needDocker = projects.filter((p) => detectStack(p)?.services.some((s) => s.kind === "docker"));
78
+ if (needDocker.length) {
79
+ const docker = await commandExists("docker");
80
+ checks.push({
81
+ name: "Docker",
82
+ status: docker ? "ok" : "warn",
83
+ detail: docker ? docker : `not installed; ${needDocker.map((p) => p.name).slice(0, 3).join(", ")} start a database with it`,
84
+ fix: docker ? undefined : "Install Docker Desktop (https://docs.docker.com/get-docker/), or on Linux the docker package, then start it",
85
+ });
86
+ }
87
+ // Local domains (shop.localhost) run with the dashboard, on port 80 when they can get it.
88
+ const dashboard = readDashboardFile();
89
+ if (dashboard) {
90
+ const p = dashboard.localDomainsPort;
91
+ if (p === undefined) {
92
+ checks.push({ name: "Local domains", status: "warn", detail: "off: no port was free", fix: "dx ui --stop, then open the dashboard again" });
93
+ }
94
+ else if (p === 80) {
95
+ checks.push({ name: "Local domains", status: "ok", detail: "apps are at name.localhost" });
96
+ }
97
+ else {
98
+ let holder = "";
99
+ try {
100
+ holder = (await listListeningPorts(80)).map((e) => e.processName ?? `PID ${e.pid}`).join(", ");
101
+ }
102
+ catch {
103
+ // can't tell who
104
+ }
105
+ checks.push({
106
+ name: "Local domains",
107
+ status: "warn",
108
+ detail: `on port ${p}, so addresses look like shop.localhost:${p}${holder ? ` (port 80 is used by ${holder})` : ""}`,
109
+ fix: process.platform === "linux" && !holder
110
+ ? "sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80 && echo net.ipv4.ip_unprivileged_port_start=80 | sudo tee /etc/sysctl.d/50-dx-localhost.conf, then dx ui --stop"
111
+ : holder
112
+ ? `Stop ${holder} if you don't need it, then dx ui --stop`
113
+ : undefined,
114
+ });
115
+ }
116
+ }
117
+ const ai = await listAiTools();
118
+ const installed = ai.filter((t) => t.installed);
119
+ const missing = installed.filter((t) => !t.connected || !t.current);
120
+ checks.push({
121
+ name: "AI tools",
122
+ status: installed.length === 0 ? "ok" : missing.length ? "warn" : "ok",
123
+ detail: installed.length === 0
124
+ ? "none found (Claude Code, Claude Desktop, Cursor, Windsurf, VS Code)"
125
+ : missing.length
126
+ ? `not connected to ${missing.map((t) => t.name).join(", ")}`
127
+ : `connected to ${installed.map((t) => t.name).join(", ")}`,
128
+ fix: missing.length ? "dx setup" : undefined,
129
+ });
130
+ if (process.platform === "win32") {
131
+ const wt = await commandExists("wt");
132
+ checks.push({
133
+ name: "Windows Terminal",
134
+ status: wt ? "ok" : "warn",
135
+ detail: wt ? wt : "not found; new terminal windows will open in plain cmd.exe",
136
+ });
137
+ }
138
+ return checks;
139
+ }
140
+ /** The running dashboard's ~/.dx/dashboard.json, if there is one. */
141
+ function readDashboardFile() {
142
+ try {
143
+ const info = JSON.parse(fs.readFileSync(dxPath("dashboard.json"), "utf8"));
144
+ process.kill(info.pid, 0); // throws when that process is gone
145
+ return info;
146
+ }
147
+ catch {
148
+ return undefined;
149
+ }
150
+ }
151
+ function linuxInstall(pkg) {
152
+ return `sudo apt install -y ${pkg} (Fedora: sudo dnf install -y ${pkg}; Arch: sudo pacman -S ${pkg})`;
153
+ }
154
+ function lsofFix() {
155
+ if (process.platform === "linux")
156
+ return linuxInstall("lsof");
157
+ if (process.platform === "darwin")
158
+ return "lsof ships with macOS; check that /usr/sbin is on your PATH";
159
+ return undefined;
160
+ }
161
+ /** Returns the resolved path of a command, or undefined if it can't be found. */
162
+ export async function commandExists(command) {
163
+ // `"C:\Program Files\X\x.exe" --flag` → the quoted part; `code -n` → `code`
164
+ const trimmed = command.trim();
165
+ const unquoted = trimmed.startsWith('"') ? trimmed.slice(1, trimmed.indexOf('"', 1)) : trimmed.split(/\s+/)[0];
166
+ if (/[\\/]/.test(unquoted))
167
+ return fs.existsSync(unquoted) ? unquoted : undefined;
168
+ try {
169
+ const { stdout } = await run(process.platform === "win32" ? "where" : "which", [unquoted], { windowsHide: true });
170
+ return stdout.split(/\r?\n/).find(Boolean)?.trim();
171
+ }
172
+ catch {
173
+ return undefined;
174
+ }
175
+ }
176
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/tools/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** Pull requests you merged or reviewed on GitHub, for the weekly summary. */
2
+ export interface PullRequest {
3
+ title: string;
4
+ /** "owner/repo" */
5
+ repo: string;
6
+ number: number;
7
+ url: string;
8
+ /** YYYY-MM-DD the PR was merged (merged list) or last updated (reviewed list). */
9
+ date: string;
10
+ }
11
+ export interface GithubActivity {
12
+ user: string;
13
+ merged: PullRequest[];
14
+ reviewed: PullRequest[];
15
+ /** True when a token was used, so private repos are included. */
16
+ authenticated: boolean;
17
+ }
18
+ /**
19
+ * A token for the GitHub API, if one is available: GITHUB_TOKEN or GH_TOKEN, or the GitHub CLI's
20
+ * login when `gh` is installed. Without one, only public repos can be searched.
21
+ */
22
+ export declare function githubToken(): Promise<string | undefined>;
23
+ /** "https://github.com/me/repo.git" or "git@github.com:me/repo.git" → "me"; anything else → undefined. */
24
+ export declare function githubOwner(remoteUrl: string): string | undefined;
25
+ /** The owner that appears most often among the given remote URLs. */
26
+ export declare function mostCommonOwner(remoteUrls: string[]): string | undefined;
27
+ /** Searches GitHub for PRs `user` merged, and PRs by others that `user` reviewed, since `since` (YYYY-MM-DD). */
28
+ export declare function fetchGithubActivity(user: string, since: string, token?: string): Promise<GithubActivity>;
29
+ /** Reads a GitHub /search/issues response. Oldest first. */
30
+ export declare function parseSearchResults(json: unknown, dateField: "merged" | "updated"): PullRequest[];
31
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/tools/github.ts"],"names":[],"mappings":"AAKA,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,iEAAiE;IACjE,aAAa,EAAE,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAS/D;AAED,0GAA0G;AAC1G,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGjE;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAOxE;AAED,iHAAiH;AACjH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAM9G;AAmBD,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,EAAE,CAoBhG"}
@@ -0,0 +1,80 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ const run = promisify(execFile);
4
+ /**
5
+ * A token for the GitHub API, if one is available: GITHUB_TOKEN or GH_TOKEN, or the GitHub CLI's
6
+ * login when `gh` is installed. Without one, only public repos can be searched.
7
+ */
8
+ export async function githubToken() {
9
+ const env = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
10
+ if (env)
11
+ return env.trim();
12
+ try {
13
+ const { stdout } = await run("gh", ["auth", "token"], { windowsHide: true, timeout: 5000 });
14
+ return stdout.trim() || undefined;
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ }
20
+ /** "https://github.com/me/repo.git" or "git@github.com:me/repo.git" → "me"; anything else → undefined. */
21
+ export function githubOwner(remoteUrl) {
22
+ const m = /github\.com[:/]([^/\s]+)\/[^/\s]+?(?:\.git)?\/?$/i.exec(remoteUrl.trim());
23
+ return m?.[1];
24
+ }
25
+ /** The owner that appears most often among the given remote URLs. */
26
+ export function mostCommonOwner(remoteUrls) {
27
+ const counts = new Map();
28
+ for (const url of remoteUrls) {
29
+ const owner = githubOwner(url);
30
+ if (owner)
31
+ counts.set(owner, (counts.get(owner) ?? 0) + 1);
32
+ }
33
+ return [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
34
+ }
35
+ /** Searches GitHub for PRs `user` merged, and PRs by others that `user` reviewed, since `since` (YYYY-MM-DD). */
36
+ export async function fetchGithubActivity(user, since, token) {
37
+ const [merged, reviewed] = await Promise.all([
38
+ searchPullRequests(`is:pr author:${user} is:merged merged:>=${since}`, token, "merged"),
39
+ searchPullRequests(`is:pr reviewed-by:${user} -author:${user} updated:>=${since}`, token, "updated"),
40
+ ]);
41
+ return { user, merged, reviewed, authenticated: Boolean(token) };
42
+ }
43
+ async function searchPullRequests(query, token, dateField) {
44
+ const url = `https://api.github.com/search/issues?q=${encodeURIComponent(query)}&per_page=50&sort=updated`;
45
+ const headers = {
46
+ Accept: "application/vnd.github+json",
47
+ "User-Agent": "dx-toolkit",
48
+ "X-GitHub-Api-Version": "2022-11-28",
49
+ };
50
+ if (token)
51
+ headers.Authorization = `Bearer ${token}`;
52
+ const res = await fetch(url, { headers, signal: AbortSignal.timeout(15_000) });
53
+ if (!res.ok) {
54
+ const body = (await res.json().catch(() => ({})));
55
+ const hint = res.status === 401 ? " (the GitHub token was rejected)" : res.status === 403 ? " (rate limited; set GITHUB_TOKEN)" : "";
56
+ throw new Error(`GitHub search failed: ${res.status} ${body.message ?? res.statusText}${hint}`);
57
+ }
58
+ return parseSearchResults(await res.json(), dateField);
59
+ }
60
+ /** Reads a GitHub /search/issues response. Oldest first. */
61
+ export function parseSearchResults(json, dateField) {
62
+ const items = json?.items;
63
+ if (!Array.isArray(items))
64
+ return [];
65
+ const out = [];
66
+ for (const raw of items) {
67
+ const item = raw;
68
+ if (!item.html_url || typeof item.number !== "number")
69
+ continue;
70
+ const when = dateField === "merged" ? item.pull_request?.merged_at ?? item.closed_at : item.updated_at;
71
+ const repo = /repos\/([^/]+\/[^/]+)$/.exec(item.repository_url ?? "")?.[1] ?? "";
72
+ out.push({ title: (item.title ?? "").trim(), repo, number: item.number, url: item.html_url, date: when ? localDay(when) : "" });
73
+ }
74
+ return out.sort((a, b) => a.date.localeCompare(b.date));
75
+ }
76
+ function localDay(iso) {
77
+ const d = new Date(iso);
78
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
79
+ }
80
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/tools/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * A plain markdown work journal at ~/.dx/journal.md:
3
+ *
4
+ * ## 2026-11-03
5
+ * - 09:14 fixed seat map overflow
6
+ *
7
+ * Plain text on purpose: you can open and edit it by hand, and the weekly summary reads it.
8
+ */
9
+ export interface JournalEntry {
10
+ date: string;
11
+ time: string;
12
+ text: string;
13
+ }
14
+ export declare function journalPath(): string;
15
+ export declare function addJournalEntry(text: string, when?: Date): JournalEntry;
16
+ export declare function readJournal(options?: {
17
+ sinceDays?: number;
18
+ now?: Date;
19
+ }): JournalEntry[];
20
+ export declare function parseJournal(text: string): JournalEntry[];
21
+ export declare function localDate(d: Date): string;
22
+ export declare function localTime(d: Date): string;
23
+ //# sourceMappingURL=journal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/tools/journal.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAMD,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,OAAa,GAAG,YAAY,CAqB7E;AAED,wBAAgB,WAAW,CAAC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,IAAI,CAAA;CAAO,GAAG,YAAY,EAAE,CAS5F;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CAczD;AAWD,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAEzC;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAEzC"}
@@ -0,0 +1,80 @@
1
+ import fs from "node:fs";
2
+ import { dxPath, ensureDxHome, stripBom } from "../config.js";
3
+ const JOURNAL_FILE = "journal.md";
4
+ const HEADING = /^##\s+(\d{4}-\d{2}-\d{2})\s*$/;
5
+ const ENTRY = /^-\s+(?:(\d{2}:\d{2})\s+)?(.*)$/;
6
+ export function journalPath() {
7
+ return dxPath(JOURNAL_FILE);
8
+ }
9
+ export function addJournalEntry(text, when = new Date()) {
10
+ const clean = text.replace(/\s+/g, " ").trim();
11
+ if (!clean)
12
+ throw new Error("Nothing to log: the entry is empty.");
13
+ ensureDxHome();
14
+ const file = journalPath();
15
+ const date = localDate(when);
16
+ const time = localTime(when);
17
+ const existing = fs.existsSync(file) ? stripBom(fs.readFileSync(file, "utf8")) : "";
18
+ let chunk = "";
19
+ if (lastHeading(existing) !== date) {
20
+ const needsGap = existing.length > 0;
21
+ const endsWithNewline = existing.endsWith("\n");
22
+ chunk += `${needsGap && !endsWithNewline ? "\n" : ""}${needsGap ? "\n" : ""}## ${date}\n`;
23
+ }
24
+ else if (existing.length > 0 && !existing.endsWith("\n")) {
25
+ chunk += "\n";
26
+ }
27
+ chunk += `- ${time} ${clean}\n`;
28
+ if (!existing)
29
+ chunk = `# Work journal\n\n${chunk}`;
30
+ fs.appendFileSync(file, chunk, "utf8");
31
+ return { date, time, text: clean };
32
+ }
33
+ export function readJournal(options = {}) {
34
+ const file = journalPath();
35
+ if (!fs.existsSync(file))
36
+ return [];
37
+ const entries = parseJournal(stripBom(fs.readFileSync(file, "utf8")));
38
+ if (options.sinceDays === undefined)
39
+ return entries;
40
+ const now = options.now ?? new Date();
41
+ const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - (options.sinceDays - 1));
42
+ const startKey = localDate(start);
43
+ return entries.filter((e) => e.date >= startKey);
44
+ }
45
+ export function parseJournal(text) {
46
+ const entries = [];
47
+ let current;
48
+ for (const line of text.split(/\r?\n/)) {
49
+ const heading = HEADING.exec(line);
50
+ if (heading) {
51
+ current = heading[1];
52
+ continue;
53
+ }
54
+ if (!current)
55
+ continue;
56
+ const entry = ENTRY.exec(line);
57
+ if (entry && entry[2].trim())
58
+ entries.push({ date: current, time: entry[1] ?? "", text: entry[2].trim() });
59
+ }
60
+ return entries;
61
+ }
62
+ function lastHeading(text) {
63
+ const lines = text.split(/\r?\n/);
64
+ for (let i = lines.length - 1; i >= 0; i--) {
65
+ const m = HEADING.exec(lines[i]);
66
+ if (m)
67
+ return m[1];
68
+ }
69
+ return undefined;
70
+ }
71
+ export function localDate(d) {
72
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
73
+ }
74
+ export function localTime(d) {
75
+ return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
76
+ }
77
+ function pad(n) {
78
+ return String(n).padStart(2, "0");
79
+ }
80
+ //# sourceMappingURL=journal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/tools/journal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Opens a folder in the editor. `editor` is a command such as "code" or "cursor" (both are .cmd
3
+ * files on Windows, so they go through the shell) or a full path in quotes.
4
+ */
5
+ export declare function openInEditor(dir: string, editor?: string): Promise<void>;
6
+ /**
7
+ * Opens a file in the editor at a line. VS Code and its relatives (Cursor, Windsurf, VSCodium)
8
+ * jump to the line with `-g file:line:column`; other editors open the file.
9
+ */
10
+ export declare function openFileInEditor(file: string, line?: number, column?: number, editor?: string): Promise<void>;
11
+ /** Opens a URL in the default browser. */
12
+ export declare function openUrl(url: string): Promise<void>;
13
+ /** Opens the folder in File Explorer / Finder / the Linux file manager. */
14
+ export declare function revealInFileManager(dir: string): Promise<void>;
15
+ /**
16
+ * Opens a new terminal window in `dir`, optionally running `command` in it and keeping the window
17
+ * open afterwards. Uses Windows Terminal when installed, plain cmd.exe otherwise.
18
+ */
19
+ export declare function openTerminal(dir: string, command?: string, title?: string): Promise<void>;
20
+ /** True if something accepts TCP connections on localhost:port (IPv4 or IPv6). */
21
+ export declare function isPortOpen(port: number, timeoutMs?: number): Promise<boolean>;
22
+ /** Waits until localhost:port accepts connections. Resolves false on timeout or abort. */
23
+ export declare function waitForPort(port: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
24
+ //# sourceMappingURL=launch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.d.ts","sourceRoot":"","sources":["../../src/tools/launch.ts"],"names":[],"mappings":"AAyBA;;;GAGG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAM9E;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAUnH;AAED,0CAA0C;AAC1C,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKxD;AAED,2EAA2E;AAC3E,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpE;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B/F;AAMD,kFAAkF;AAClF,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhF;AAED,0FAA0F;AAC1F,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CAQlB"}
@@ -0,0 +1,137 @@
1
+ import { spawn } from "node:child_process";
2
+ import net from "node:net";
3
+ /** Starts a program without tying it to this process: dx can exit and the program keeps running. */
4
+ function detached(command, args, options = {}) {
5
+ return new Promise((resolve, reject) => {
6
+ const opts = { detached: true, stdio: "ignore", windowsHide: false, ...options };
7
+ // With shell: true the whole command line is in `command`; passing an args array as well is deprecated.
8
+ const child = opts.shell ? spawn(command, opts) : spawn(command, args, opts);
9
+ child.once("error", reject);
10
+ child.once("spawn", () => {
11
+ child.unref();
12
+ resolve();
13
+ });
14
+ });
15
+ }
16
+ const isWin = process.platform === "win32";
17
+ const isMac = process.platform === "darwin";
18
+ /** Quotes one argument for cmd.exe. */
19
+ function winQuote(arg) {
20
+ return /[\s"&|<>^()]/.test(arg) ? `"${arg.replace(/"/g, '""')}"` : arg;
21
+ }
22
+ /**
23
+ * Opens a folder in the editor. `editor` is a command such as "code" or "cursor" (both are .cmd
24
+ * files on Windows, so they go through the shell) or a full path in quotes.
25
+ */
26
+ export async function openInEditor(dir, editor = "code") {
27
+ if (isWin) {
28
+ await detached(`${editor} ${winQuote(dir)}`, [], { shell: true, windowsHide: true });
29
+ return;
30
+ }
31
+ await detached(editor, [dir], { shell: false });
32
+ }
33
+ /**
34
+ * Opens a file in the editor at a line. VS Code and its relatives (Cursor, Windsurf, VSCodium)
35
+ * jump to the line with `-g file:line:column`; other editors open the file.
36
+ */
37
+ export async function openFileInEditor(file, line, column, editor = "code") {
38
+ const name = editor.replace(/^"|"$/g, "").split(/[\\/]/).pop()?.toLowerCase() ?? "";
39
+ const goto = /^(code|code-insiders|cursor|windsurf|codium|vscodium)(\.cmd|\.exe)?$/.test(name.split(/\s+/)[0]) && line;
40
+ const target = goto ? `${file}:${line}${column ? `:${column}` : ""}` : file;
41
+ const args = goto ? ["-g", target] : [target];
42
+ if (isWin) {
43
+ await detached(`${editor} ${args.map(winQuote).join(" ")}`, [], { shell: true, windowsHide: true });
44
+ return;
45
+ }
46
+ await detached(editor, args, { shell: false });
47
+ }
48
+ /** Opens a URL in the default browser. */
49
+ export async function openUrl(url) {
50
+ if (!/^https?:\/\//i.test(url))
51
+ throw new Error(`Only http(s) URLs can be opened: ${url}`);
52
+ if (isWin)
53
+ return detached("rundll32.exe", ["url.dll,FileProtocolHandler", url]);
54
+ if (isMac)
55
+ return detached("open", [url]);
56
+ return detached("xdg-open", [url]);
57
+ }
58
+ /** Opens the folder in File Explorer / Finder / the Linux file manager. */
59
+ export async function revealInFileManager(dir) {
60
+ if (isWin)
61
+ return detached("explorer.exe", [dir]);
62
+ if (isMac)
63
+ return detached("open", [dir]);
64
+ return detached("xdg-open", [dir]);
65
+ }
66
+ /**
67
+ * Opens a new terminal window in `dir`, optionally running `command` in it and keeping the window
68
+ * open afterwards. Uses Windows Terminal when installed, plain cmd.exe otherwise.
69
+ */
70
+ export async function openTerminal(dir, command, title) {
71
+ if (isWin) {
72
+ const wtArgs = ["-w", "new", "-d", dir];
73
+ if (title)
74
+ wtArgs.push("--title", title);
75
+ if (command)
76
+ wtArgs.push("cmd", "/k", command);
77
+ try {
78
+ await detached("wt.exe", wtArgs);
79
+ return;
80
+ }
81
+ catch {
82
+ // Windows Terminal isn't installed: fall back to a classic console window.
83
+ // `start "title" /D dir cmd /k "command"`; the quotes keep && inside the new window's command.
84
+ const args = ["/c", "start", `"${(title ?? "dx").replace(/"/g, "")}"`, "/D", winQuote(dir), "cmd", "/k"];
85
+ if (command)
86
+ args.push(`"${command}"`);
87
+ await detached("cmd.exe", args, { shell: false, windowsVerbatimArguments: true, windowsHide: true });
88
+ return;
89
+ }
90
+ }
91
+ if (isMac) {
92
+ const script = command
93
+ ? `tell application "Terminal" to do script "cd ${escapeAppleScript(dir)} && ${escapeAppleScript(command)}"`
94
+ : `tell application "Terminal" to do script "cd ${escapeAppleScript(dir)}"`;
95
+ await detached("osascript", ["-e", script, "-e", 'tell application "Terminal" to activate']);
96
+ return;
97
+ }
98
+ const shellCmd = command ? `cd '${dir}' && ${command}; exec $SHELL` : `cd '${dir}'; exec $SHELL`;
99
+ await detached("x-terminal-emulator", ["-e", "sh", "-c", shellCmd]);
100
+ }
101
+ function escapeAppleScript(s) {
102
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
103
+ }
104
+ /** True if something accepts TCP connections on localhost:port (IPv4 or IPv6). */
105
+ export async function isPortOpen(port, timeoutMs = 400) {
106
+ for (const host of ["127.0.0.1", "::1"]) {
107
+ if (await tryConnect(host, port, timeoutMs))
108
+ return true;
109
+ }
110
+ return false;
111
+ }
112
+ /** Waits until localhost:port accepts connections. Resolves false on timeout or abort. */
113
+ export async function waitForPort(port, timeoutMs, signal) {
114
+ const deadline = Date.now() + timeoutMs;
115
+ while (Date.now() < deadline) {
116
+ if (signal?.aborted)
117
+ return false;
118
+ if (await isPortOpen(port))
119
+ return true;
120
+ await new Promise((r) => setTimeout(r, 750));
121
+ }
122
+ return false;
123
+ }
124
+ function tryConnect(host, port, timeoutMs) {
125
+ return new Promise((resolve) => {
126
+ const socket = net.connect({ host, port });
127
+ const done = (ok) => {
128
+ socket.removeAllListeners();
129
+ socket.destroy();
130
+ resolve(ok);
131
+ };
132
+ socket.setTimeout(timeoutMs, () => done(false));
133
+ socket.once("connect", () => done(true));
134
+ socket.once("error", () => done(false));
135
+ });
136
+ }
137
+ //# sourceMappingURL=launch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch.js","sourceRoot":"","sources":["../../src/tools/launch.ts"],"names":[],"mappings":"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"}