crosspad-mcp-server 4.0.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 (80) hide show
  1. package/README.md +187 -0
  2. package/dist/config.d.ts +10 -0
  3. package/dist/config.js +33 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +360 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/tools/architecture.d.ts +16 -0
  9. package/dist/tools/architecture.js +198 -0
  10. package/dist/tools/architecture.js.map +1 -0
  11. package/dist/tools/build-check.d.ts +23 -0
  12. package/dist/tools/build-check.js +162 -0
  13. package/dist/tools/build-check.js.map +1 -0
  14. package/dist/tools/build.d.ts +14 -0
  15. package/dist/tools/build.js +101 -0
  16. package/dist/tools/build.js.map +1 -0
  17. package/dist/tools/diff-core.d.ts +24 -0
  18. package/dist/tools/diff-core.js +88 -0
  19. package/dist/tools/diff-core.js.map +1 -0
  20. package/dist/tools/idf-build.d.ts +10 -0
  21. package/dist/tools/idf-build.js +155 -0
  22. package/dist/tools/idf-build.js.map +1 -0
  23. package/dist/tools/input.d.ts +36 -0
  24. package/dist/tools/input.js +61 -0
  25. package/dist/tools/input.js.map +1 -0
  26. package/dist/tools/log.d.ts +16 -0
  27. package/dist/tools/log.js +49 -0
  28. package/dist/tools/log.js.map +1 -0
  29. package/dist/tools/repos.d.ts +12 -0
  30. package/dist/tools/repos.js +63 -0
  31. package/dist/tools/repos.js.map +1 -0
  32. package/dist/tools/scaffold.d.ts +15 -0
  33. package/dist/tools/scaffold.js +192 -0
  34. package/dist/tools/scaffold.js.map +1 -0
  35. package/dist/tools/screenshot.d.ts +24 -0
  36. package/dist/tools/screenshot.js +80 -0
  37. package/dist/tools/screenshot.js.map +1 -0
  38. package/dist/tools/settings.d.ts +25 -0
  39. package/dist/tools/settings.js +48 -0
  40. package/dist/tools/settings.js.map +1 -0
  41. package/dist/tools/stats.d.ts +18 -0
  42. package/dist/tools/stats.js +31 -0
  43. package/dist/tools/stats.js.map +1 -0
  44. package/dist/tools/symbols.d.ts +20 -0
  45. package/dist/tools/symbols.js +157 -0
  46. package/dist/tools/symbols.js.map +1 -0
  47. package/dist/tools/test.d.ts +24 -0
  48. package/dist/tools/test.js +227 -0
  49. package/dist/tools/test.js.map +1 -0
  50. package/dist/utils/exec.d.ts +58 -0
  51. package/dist/utils/exec.js +292 -0
  52. package/dist/utils/exec.js.map +1 -0
  53. package/dist/utils/git.d.ts +10 -0
  54. package/dist/utils/git.js +29 -0
  55. package/dist/utils/git.js.map +1 -0
  56. package/dist/utils/remote-client.d.ts +17 -0
  57. package/dist/utils/remote-client.js +94 -0
  58. package/dist/utils/remote-client.js.map +1 -0
  59. package/package.json +21 -0
  60. package/server.json +23 -0
  61. package/src/config.ts +45 -0
  62. package/src/index.ts +484 -0
  63. package/src/tools/architecture.ts +260 -0
  64. package/src/tools/build-check.ts +178 -0
  65. package/src/tools/build.ts +130 -0
  66. package/src/tools/diff-core.ts +130 -0
  67. package/src/tools/idf-build.ts +182 -0
  68. package/src/tools/input.ts +80 -0
  69. package/src/tools/log.ts +75 -0
  70. package/src/tools/repos.ts +75 -0
  71. package/src/tools/scaffold.ts +229 -0
  72. package/src/tools/screenshot.ts +100 -0
  73. package/src/tools/settings.ts +68 -0
  74. package/src/tools/stats.ts +38 -0
  75. package/src/tools/symbols.ts +185 -0
  76. package/src/tools/test.ts +264 -0
  77. package/src/utils/exec.ts +376 -0
  78. package/src/utils/git.ts +45 -0
  79. package/src/utils/remote-client.ts +107 -0
  80. package/tsconfig.json +16 -0
@@ -0,0 +1,198 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { REPOS } from "../config.js";
4
+ import { runCommand } from "../utils/exec.js";
5
+ function findInterfaces() {
6
+ const coreInclude = path.join(REPOS["crosspad-core"], "include", "crosspad");
7
+ const results = [];
8
+ function scan(dir) {
9
+ if (!fs.existsSync(dir))
10
+ return;
11
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
12
+ const fullPath = path.join(dir, entry.name);
13
+ if (entry.isDirectory()) {
14
+ scan(fullPath);
15
+ }
16
+ else if (entry.name.startsWith("I") && entry.name.endsWith(".hpp")) {
17
+ // Extract class name from file
18
+ const content = fs.readFileSync(fullPath, "utf-8");
19
+ const match = content.match(/class\s+(I[A-Z]\w+)\b/);
20
+ if (match) {
21
+ results.push({
22
+ name: match[1],
23
+ file: fullPath.replace(/\\/g, "/"),
24
+ });
25
+ }
26
+ }
27
+ }
28
+ }
29
+ scan(coreInclude);
30
+ return results;
31
+ }
32
+ function findImplementations(interfaceName) {
33
+ const results = [];
34
+ const pattern = `class\\s+\\w+.*:\\s*(public\\s+)?.*${interfaceName}`;
35
+ const platformMap = {
36
+ "crosspad-core": "shared",
37
+ "crosspad-gui": "gui",
38
+ "crosspad-pc": "PC",
39
+ "ESP32-S3": "ESP32-S3",
40
+ "2playerCrosspad": "2player",
41
+ };
42
+ for (const [name, repoPath] of Object.entries(REPOS)) {
43
+ if (!fs.existsSync(repoPath))
44
+ continue;
45
+ const result = runCommand(`git grep -n -E "${pattern}" -- "*.hpp" "*.cpp" "*.h"`, repoPath);
46
+ if (!result.success && result.stdout.length === 0)
47
+ continue;
48
+ for (const line of result.stdout.split("\n")) {
49
+ if (!line.trim())
50
+ continue;
51
+ const colonIdx = line.indexOf(":");
52
+ if (colonIdx < 0)
53
+ continue;
54
+ const filePart = line.slice(0, colonIdx);
55
+ const codePart = line.slice(colonIdx + 1);
56
+ // Extract the line number and code
57
+ const lineNumMatch = codePart.match(/^(\d+):(.*)/);
58
+ const code = lineNumMatch ? lineNumMatch[2] : codePart;
59
+ const classMatch = code.match(/class\s+(\w+)/);
60
+ if (classMatch) {
61
+ results.push({
62
+ className: classMatch[1],
63
+ file: path.join(repoPath, filePart).replace(/\\/g, "/"),
64
+ platform: platformMap[name] ?? name,
65
+ });
66
+ }
67
+ }
68
+ }
69
+ return results;
70
+ }
71
+ function queryCapabilities() {
72
+ const capsFile = path.join(REPOS["crosspad-core"], "include", "crosspad", "platform", "PlatformCapabilities.hpp");
73
+ // Parse enum flags
74
+ const flags = [];
75
+ if (fs.existsSync(capsFile)) {
76
+ const content = fs.readFileSync(capsFile, "utf-8");
77
+ const enumMatch = content.match(/enum\s+class\s+Capability[^{]*\{([^}]+)\}/s);
78
+ if (enumMatch) {
79
+ for (const line of enumMatch[1].split("\n")) {
80
+ const flagMatch = line.match(/\b(\w+)\s*=/);
81
+ if (flagMatch && flagMatch[1] !== "None" && flagMatch[1] !== "All") {
82
+ flags.push(flagMatch[1]);
83
+ }
84
+ }
85
+ }
86
+ }
87
+ // Find which platforms set which caps
88
+ const platforms = {};
89
+ const platformMap = {
90
+ "crosspad-pc": "PC",
91
+ "ESP32-S3": "ESP32-S3",
92
+ "2playerCrosspad": "2player",
93
+ };
94
+ for (const [name, repoPath] of Object.entries(REPOS)) {
95
+ if (!platformMap[name])
96
+ continue;
97
+ if (!fs.existsSync(repoPath))
98
+ continue;
99
+ const result = runCommand(`git grep -h "addPlatformCapability\\|setPlatformCapabilities" -- "*.cpp" "*.hpp" "*.h"`, repoPath);
100
+ if (!result.success && result.stdout.length === 0)
101
+ continue;
102
+ const caps = [];
103
+ for (const line of result.stdout.split("\n")) {
104
+ const matches = line.match(/Capability::(\w+)/g);
105
+ if (matches) {
106
+ for (const m of matches) {
107
+ const cap = m.replace("Capability::", "");
108
+ if (!caps.includes(cap))
109
+ caps.push(cap);
110
+ }
111
+ }
112
+ }
113
+ if (caps.length > 0) {
114
+ platforms[platformMap[name]] = caps;
115
+ }
116
+ }
117
+ return { flags, platforms };
118
+ }
119
+ export function crosspadInterfaces(query) {
120
+ const parts = query.trim().split(/\s+/);
121
+ const command = parts[0]?.toLowerCase();
122
+ if (command === "list") {
123
+ return { interfaces: findInterfaces() };
124
+ }
125
+ if (command === "implementations" && parts[1]) {
126
+ const interfaceName = parts[1];
127
+ const interfaces = findInterfaces();
128
+ const defined = interfaces.find((i) => i.name === interfaceName);
129
+ return {
130
+ interface: interfaceName,
131
+ defined_in: defined?.file ?? "not found",
132
+ implementations: findImplementations(interfaceName),
133
+ };
134
+ }
135
+ if (command === "capabilities") {
136
+ const caps = queryCapabilities();
137
+ return { flags: caps.flags, platforms: caps.platforms };
138
+ }
139
+ return {
140
+ error: `Unknown query: "${query}". Use "list", "implementations <InterfaceName>", or "capabilities".`,
141
+ };
142
+ }
143
+ export function crosspadApps(platform) {
144
+ const results = [];
145
+ const targets = [];
146
+ if (platform === "pc" || platform === "all")
147
+ targets.push(["PC", REPOS["crosspad-pc"]]);
148
+ if (platform === "esp32" || platform === "all")
149
+ targets.push(["ESP32-S3", REPOS["ESP32-S3"]]);
150
+ if (platform === "2player" || platform === "all")
151
+ targets.push(["2player", REPOS["2playerCrosspad"]]);
152
+ for (const [platName, repoPath] of targets) {
153
+ if (!fs.existsSync(repoPath))
154
+ continue;
155
+ // Search for REGISTER_APP and _register_*_app patterns
156
+ const result = runCommand(`git grep -n -E "REGISTER_APP\\(|void _register_\\w+_app\\(\\)" -- "*.cpp"`, repoPath);
157
+ if (!result.success && result.stdout.length === 0)
158
+ continue;
159
+ for (const line of result.stdout.split("\n")) {
160
+ if (!line.trim())
161
+ continue;
162
+ const colonIdx = line.indexOf(":");
163
+ if (colonIdx < 0)
164
+ continue;
165
+ const filePart = line.slice(0, colonIdx);
166
+ const rest = line.slice(colonIdx + 1);
167
+ // REGISTER_APP(Name, ...)
168
+ let match = rest.match(/REGISTER_APP\((\w+)/);
169
+ if (match) {
170
+ results.push({
171
+ name: match[1],
172
+ registration_file: filePart,
173
+ platform: platName,
174
+ });
175
+ continue;
176
+ }
177
+ // void _register_Name_app()
178
+ match = rest.match(/_register_(\w+)_app\(/);
179
+ if (match) {
180
+ results.push({
181
+ name: match[1],
182
+ registration_file: filePart,
183
+ platform: platName,
184
+ });
185
+ }
186
+ }
187
+ }
188
+ // Deduplicate by name+platform
189
+ const seen = new Set();
190
+ return results.filter((app) => {
191
+ const key = `${app.platform}:${app.name}`;
192
+ if (seen.has(key))
193
+ return false;
194
+ seen.add(key);
195
+ return true;
196
+ });
197
+ }
198
+ //# sourceMappingURL=architecture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"architecture.js","sourceRoot":"","sources":["../../src/tools/architecture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAe9C,SAAS,cAAc;IACrB,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAoB,EAAE,CAAC;IAEpC,SAAS,IAAI,CAAC,GAAW;QACvB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO;QAChC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjB,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrE,+BAA+B;gBAC/B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;gBACrD,IAAI,KAAK,EAAE,CAAC;oBACV,OAAO,CAAC,IAAI,CAAC;wBACX,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;wBACd,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;qBACnC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,CAAC;IAClB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,mBAAmB,CAAC,aAAqB;IAChD,MAAM,OAAO,GAAyB,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,sCAAsC,aAAa,EAAE,CAAC;IAEtE,MAAM,WAAW,GAA2B;QAC1C,eAAe,EAAE,QAAQ;QACzB,cAAc,EAAE,KAAK;QACrB,aAAa,EAAE,IAAI;QACnB,UAAU,EAAE,UAAU;QACtB,iBAAiB,EAAE,SAAS;KAC7B,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEvC,MAAM,MAAM,GAAG,UAAU,CACvB,mBAAmB,OAAO,4BAA4B,EACtD,QAAQ,CACT,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAE5D,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;gBAAE,SAAS;YAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACnC,IAAI,QAAQ,GAAG,CAAC;gBAAE,SAAS;YAE3B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YAE1C,mCAAmC;YACnC,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YAEvD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YAC/C,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC;oBACX,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;oBACxB,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;oBACvD,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,IAAI;iBACpC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAOD,SAAS,iBAAiB;IACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,KAAK,CAAC,eAAe,CAAC,EACtB,SAAS,EACT,UAAU,EACV,UAAU,EACV,0BAA0B,CAC3B,CAAC;IAEF,mBAAmB;IACnB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC9E,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;gBAC5C,IAAI,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;oBACnE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,MAAM,SAAS,GAA6B,EAAE,CAAC;IAC/C,MAAM,WAAW,GAA2B;QAC1C,aAAa,EAAE,IAAI;QACnB,UAAU,EAAE,UAAU;QACtB,iBAAiB,EAAE,SAAS;KAC7B,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YAAE,SAAS;QACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEvC,MAAM,MAAM,GAAG,UAAU,CACvB,wFAAwF,EACxF,QAAQ,CACT,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAE5D,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;YACjD,IAAI,OAAO,EAAE,CAAC;gBACZ,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;oBACxB,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;oBAC1C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;wBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAAa;IAEb,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IAExC,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,CAAC;IAC1C,CAAC;IAED,IAAI,OAAO,KAAK,iBAAiB,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;QACjE,OAAO;YACL,SAAS,EAAE,aAAa;YACxB,UAAU,EAAE,OAAO,EAAE,IAAI,IAAI,WAAW;YACxC,eAAe,EAAE,mBAAmB,CAAC,aAAa,CAAC;SACpD,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,KAAK,cAAc,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,iBAAiB,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;IAC1D,CAAC;IAED,OAAO;QACL,KAAK,EAAE,mBAAmB,KAAK,sEAAsE;KACtG,CAAC;AACJ,CAAC;AAUD,MAAM,UAAU,YAAY,CAC1B,QAA4C;IAE5C,MAAM,OAAO,GAAc,EAAE,CAAC;IAE9B,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IACxF,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC9F,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAEtG,KAAK,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,OAAO,EAAE,CAAC;QAC3C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEvC,uDAAuD;QACvD,MAAM,MAAM,GAAG,UAAU,CACvB,2EAA2E,EAC3E,QAAQ,CACT,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAE5D,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;gBAAE,SAAS;YAE3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACnC,IAAI,QAAQ,GAAG,CAAC;gBAAE,SAAS;YAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;YACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;YAEtC,0BAA0B;YAC1B,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;YAC9C,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;oBACd,iBAAiB,EAAE,QAAQ;oBAC3B,QAAQ,EAAE,QAAQ;iBACnB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,4BAA4B;YAC5B,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YAC5C,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,IAAI,CAAC;oBACX,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;oBACd,iBAAiB,EAAE,QAAQ;oBAC3B,QAAQ,EAAE,QAAQ;iBACnB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,23 @@
1
+ export interface BuildCheckResult {
2
+ needs_reconfigure: boolean;
3
+ needs_rebuild: boolean;
4
+ exe_exists: boolean;
5
+ exe_age_seconds: number | null;
6
+ reasons: string[];
7
+ submodule_changes: Record<string, {
8
+ pinned: string | null;
9
+ current: string | null;
10
+ changed: boolean;
11
+ }>;
12
+ new_source_files: string[];
13
+ }
14
+ /**
15
+ * Detect whether cmake reconfigure or rebuild is needed.
16
+ * Checks:
17
+ * - Does build/ dir exist?
18
+ * - Does bin/main.exe exist?
19
+ * - Are there new .cpp/.hpp files not in CMakeCache?
20
+ * - Did crosspad-core or crosspad-gui HEAD change vs pinned?
21
+ * - Are there uncommitted changes in source?
22
+ */
23
+ export declare function crosspadBuildCheck(): BuildCheckResult;
@@ -0,0 +1,162 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { CROSSPAD_PC_ROOT, BUILD_DIR, BIN_EXE } from "../config.js";
4
+ import { runCommand } from "../utils/exec.js";
5
+ import { getSubmodulePin } from "../utils/git.js";
6
+ /**
7
+ * Detect whether cmake reconfigure or rebuild is needed.
8
+ * Checks:
9
+ * - Does build/ dir exist?
10
+ * - Does bin/main.exe exist?
11
+ * - Are there new .cpp/.hpp files not in CMakeCache?
12
+ * - Did crosspad-core or crosspad-gui HEAD change vs pinned?
13
+ * - Are there uncommitted changes in source?
14
+ */
15
+ export function crosspadBuildCheck() {
16
+ const reasons = [];
17
+ let needsReconfigure = false;
18
+ let needsRebuild = false;
19
+ // Check build dir
20
+ const buildExists = fs.existsSync(BUILD_DIR);
21
+ if (!buildExists) {
22
+ needsReconfigure = true;
23
+ reasons.push("build/ directory does not exist — need full configure");
24
+ }
25
+ // Check exe
26
+ const exeExists = fs.existsSync(BIN_EXE);
27
+ let exeAgeSeconds = null;
28
+ if (exeExists) {
29
+ const stat = fs.statSync(BIN_EXE);
30
+ exeAgeSeconds = (Date.now() - stat.mtimeMs) / 1000;
31
+ }
32
+ else {
33
+ needsRebuild = true;
34
+ reasons.push(`${path.relative(CROSSPAD_PC_ROOT, BIN_EXE)} not found — need build`);
35
+ }
36
+ // Check for source files newer than exe
37
+ if (exeExists) {
38
+ const exeMtime = fs.statSync(BIN_EXE).mtimeMs;
39
+ const srcDirs = [
40
+ path.join(CROSSPAD_PC_ROOT, "src"),
41
+ path.join(CROSSPAD_PC_ROOT, "crosspad-core", "src"),
42
+ path.join(CROSSPAD_PC_ROOT, "crosspad-core", "include"),
43
+ path.join(CROSSPAD_PC_ROOT, "crosspad-gui", "src"),
44
+ path.join(CROSSPAD_PC_ROOT, "crosspad-gui", "include"),
45
+ ];
46
+ let newerCount = 0;
47
+ for (const dir of srcDirs) {
48
+ if (!fs.existsSync(dir))
49
+ continue;
50
+ newerCount += countFilesNewerThan(dir, exeMtime, [".cpp", ".hpp", ".h", ".c"]);
51
+ if (newerCount > 0)
52
+ break; // One is enough
53
+ }
54
+ if (newerCount > 0) {
55
+ needsRebuild = true;
56
+ reasons.push("Source files are newer than executable");
57
+ }
58
+ }
59
+ // Check for new source files not tracked by CMake (GLOB_RECURSE freshness)
60
+ const newSourceFiles = [];
61
+ if (buildExists) {
62
+ const cacheFile = path.join(BUILD_DIR, "build.ninja");
63
+ if (fs.existsSync(cacheFile)) {
64
+ const ninjaContent = fs.readFileSync(cacheFile, "utf-8");
65
+ // Find .cpp files in src/apps that aren't in build.ninja
66
+ const appsDir = path.join(CROSSPAD_PC_ROOT, "src", "apps");
67
+ if (fs.existsSync(appsDir)) {
68
+ const cppFiles = findFiles(appsDir, [".cpp"]);
69
+ for (const f of cppFiles) {
70
+ const relative = path.relative(CROSSPAD_PC_ROOT, f).replace(/\\/g, "/");
71
+ if (!ninjaContent.includes(relative) && !ninjaContent.includes(path.basename(f))) {
72
+ newSourceFiles.push(relative);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ if (newSourceFiles.length > 0) {
79
+ needsReconfigure = true;
80
+ reasons.push(`${newSourceFiles.length} source file(s) not in build system — need reconfigure`);
81
+ }
82
+ // Submodule changes (dev-mode aware)
83
+ const submoduleChanges = {};
84
+ for (const sub of ["crosspad-core", "crosspad-gui"]) {
85
+ const pinned = getSubmodulePin(CROSSPAD_PC_ROOT, sub);
86
+ const subPath = path.join(CROSSPAD_PC_ROOT, sub);
87
+ let current = null;
88
+ if (fs.existsSync(subPath)) {
89
+ // In dev-mode (junction), get HEAD of the junction target
90
+ const result = runCommand("git rev-parse HEAD", subPath);
91
+ current = result.success ? result.stdout.trim() : null;
92
+ }
93
+ const changed = pinned !== null && current !== null && !current.startsWith(pinned.slice(0, 7));
94
+ submoduleChanges[sub] = { pinned, current, changed };
95
+ if (changed) {
96
+ needsRebuild = true;
97
+ reasons.push(`${sub} HEAD differs from pinned commit`);
98
+ }
99
+ // Check for dirty files in submodule
100
+ if (fs.existsSync(subPath)) {
101
+ const dirty = runCommand("git status --porcelain", subPath);
102
+ if (dirty.success && dirty.stdout.trim().length > 0) {
103
+ const dirtyCount = dirty.stdout.trim().split("\n").length;
104
+ needsRebuild = true;
105
+ reasons.push(`${sub} has ${dirtyCount} dirty file(s)`);
106
+ }
107
+ }
108
+ }
109
+ if (reasons.length === 0) {
110
+ reasons.push("Build appears up to date");
111
+ }
112
+ return {
113
+ needs_reconfigure: needsReconfigure,
114
+ needs_rebuild: needsRebuild || needsReconfigure,
115
+ exe_exists: exeExists,
116
+ exe_age_seconds: exeAgeSeconds !== null ? Math.round(exeAgeSeconds) : null,
117
+ reasons,
118
+ submodule_changes: submoduleChanges,
119
+ new_source_files: newSourceFiles,
120
+ };
121
+ }
122
+ function countFilesNewerThan(dir, thresholdMs, extensions) {
123
+ let count = 0;
124
+ try {
125
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
126
+ const fullPath = path.join(dir, entry.name);
127
+ if (entry.isDirectory()) {
128
+ count += countFilesNewerThan(fullPath, thresholdMs, extensions);
129
+ }
130
+ else if (extensions.some((ext) => entry.name.endsWith(ext))) {
131
+ const stat = fs.statSync(fullPath);
132
+ if (stat.mtimeMs > thresholdMs)
133
+ count++;
134
+ }
135
+ if (count > 0)
136
+ return count; // Early exit
137
+ }
138
+ }
139
+ catch {
140
+ // Ignore permission errors etc.
141
+ }
142
+ return count;
143
+ }
144
+ function findFiles(dir, extensions) {
145
+ const results = [];
146
+ try {
147
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
148
+ const fullPath = path.join(dir, entry.name);
149
+ if (entry.isDirectory()) {
150
+ results.push(...findFiles(fullPath, extensions));
151
+ }
152
+ else if (extensions.some((ext) => entry.name.endsWith(ext))) {
153
+ results.push(fullPath);
154
+ }
155
+ }
156
+ }
157
+ catch {
158
+ // Ignore
159
+ }
160
+ return results;
161
+ }
162
+ //# sourceMappingURL=build-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-check.js","sourceRoot":"","sources":["../../src/tools/build-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { OnLine } from "../utils/exec.js";
2
+ export interface BuildResult {
3
+ success: boolean;
4
+ duration_seconds: number;
5
+ errors: string[];
6
+ warnings_count: number;
7
+ output_path: string;
8
+ }
9
+ export declare function crosspadBuild(mode: "incremental" | "clean" | "reconfigure", onLine?: OnLine): Promise<BuildResult>;
10
+ export interface RunResult {
11
+ pid: number | null;
12
+ exe_path: string;
13
+ }
14
+ export declare function crosspadRun(): RunResult;
@@ -0,0 +1,101 @@
1
+ import fs from "fs";
2
+ import { CROSSPAD_PC_ROOT, BUILD_DIR, BIN_EXE, VCPKG_TOOLCHAIN, IS_WINDOWS } from "../config.js";
3
+ import { runBuild, runBuildStream, spawnDetached } from "../utils/exec.js";
4
+ function parseErrors(output) {
5
+ const errors = [];
6
+ for (const line of output.split("\n")) {
7
+ if (/\berror\b/i.test(line) && !line.includes("error(s)")) {
8
+ errors.push(line.trim());
9
+ }
10
+ }
11
+ return errors.slice(0, 20); // Cap at 20 errors
12
+ }
13
+ function countWarnings(output) {
14
+ let count = 0;
15
+ for (const line of output.split("\n")) {
16
+ if (/\bwarning\b/i.test(line) && !line.includes("warning(s)")) {
17
+ count++;
18
+ }
19
+ }
20
+ return count;
21
+ }
22
+ export async function crosspadBuild(mode, onLine) {
23
+ const startTime = Date.now();
24
+ // Clean: remove build dir
25
+ if (mode === "clean" && fs.existsSync(BUILD_DIR)) {
26
+ onLine?.("stdout", "[crosspad] Cleaning build directory...");
27
+ fs.rmSync(BUILD_DIR, { recursive: true, force: true });
28
+ }
29
+ // Configure if clean or reconfigure
30
+ if (mode === "clean" || mode === "reconfigure") {
31
+ const generator = process.env.CMAKE_GENERATOR || (IS_WINDOWS ? "Ninja" : "");
32
+ const genFlag = generator ? ` -G ${generator}` : "";
33
+ const configCmd = [
34
+ `cmake -B build${genFlag}`,
35
+ `-DCMAKE_TOOLCHAIN_FILE=${VCPKG_TOOLCHAIN}`,
36
+ "-DCMAKE_BUILD_TYPE=Debug",
37
+ ].join(" ");
38
+ onLine?.("stdout", `[crosspad] Configuring: ${mode}...`);
39
+ if (onLine) {
40
+ const configResult = await runBuildStream(configCmd, CROSSPAD_PC_ROOT, onLine, 600_000);
41
+ if (!configResult.success) {
42
+ const combined = configResult.stdout + "\n" + configResult.stderr;
43
+ return {
44
+ success: false,
45
+ duration_seconds: (Date.now() - startTime) / 1000,
46
+ errors: parseErrors(combined),
47
+ warnings_count: countWarnings(combined),
48
+ output_path: BIN_EXE,
49
+ };
50
+ }
51
+ }
52
+ else {
53
+ const configResult = runBuild(configCmd, CROSSPAD_PC_ROOT, 600_000);
54
+ if (!configResult.success) {
55
+ const combined = configResult.stdout + "\n" + configResult.stderr;
56
+ return {
57
+ success: false,
58
+ duration_seconds: (Date.now() - startTime) / 1000,
59
+ errors: parseErrors(combined),
60
+ warnings_count: countWarnings(combined),
61
+ output_path: BIN_EXE,
62
+ };
63
+ }
64
+ }
65
+ }
66
+ // Build
67
+ onLine?.("stdout", "[crosspad] Building...");
68
+ let buildStdout;
69
+ let buildStderr;
70
+ let buildSuccess;
71
+ if (onLine) {
72
+ const buildResult = await runBuildStream("cmake --build build", CROSSPAD_PC_ROOT, onLine, 600_000);
73
+ buildStdout = buildResult.stdout;
74
+ buildStderr = buildResult.stderr;
75
+ buildSuccess = buildResult.success;
76
+ }
77
+ else {
78
+ const buildResult = runBuild("cmake --build build", CROSSPAD_PC_ROOT, 600_000);
79
+ buildStdout = buildResult.stdout;
80
+ buildStderr = buildResult.stderr;
81
+ buildSuccess = buildResult.success;
82
+ }
83
+ const combined = buildStdout + "\n" + buildStderr;
84
+ const result = {
85
+ success: buildSuccess,
86
+ duration_seconds: (Date.now() - startTime) / 1000,
87
+ errors: parseErrors(combined),
88
+ warnings_count: countWarnings(combined),
89
+ output_path: BIN_EXE,
90
+ };
91
+ onLine?.("stdout", `[crosspad] Build ${result.success ? "succeeded" : "FAILED"} in ${result.duration_seconds.toFixed(1)}s`);
92
+ return result;
93
+ }
94
+ export function crosspadRun() {
95
+ if (!fs.existsSync(BIN_EXE)) {
96
+ return { pid: null, exe_path: BIN_EXE };
97
+ }
98
+ const pid = spawnDetached(BIN_EXE, [], CROSSPAD_PC_ROOT);
99
+ return { pid, exe_path: BIN_EXE };
100
+ }
101
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/tools/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface DiffEntry {
2
+ status: string;
3
+ file: string;
4
+ }
5
+ export interface SubmoduleDiff {
6
+ name: string;
7
+ pinned_commit: string | null;
8
+ current_commit: string | null;
9
+ is_dev_mode: boolean;
10
+ ahead_count: number;
11
+ behind_count: number;
12
+ changed_files: DiffEntry[];
13
+ uncommitted_changes: string[];
14
+ commit_log: string[];
15
+ }
16
+ export interface DiffCoreResult {
17
+ submodules: SubmoduleDiff[];
18
+ }
19
+ /**
20
+ * Show what changed in crosspad-core and/or crosspad-gui relative to the
21
+ * pinned submodule commit. Essential for dev-mode workflows where you're
22
+ * editing shared repos but haven't committed/pinned yet.
23
+ */
24
+ export declare function crosspadDiffCore(submodule?: "crosspad-core" | "crosspad-gui" | "both"): DiffCoreResult;
@@ -0,0 +1,88 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { CROSSPAD_PC_ROOT } from "../config.js";
4
+ import { runCommand } from "../utils/exec.js";
5
+ import { getSubmodulePin } from "../utils/git.js";
6
+ /**
7
+ * Show what changed in crosspad-core and/or crosspad-gui relative to the
8
+ * pinned submodule commit. Essential for dev-mode workflows where you're
9
+ * editing shared repos but haven't committed/pinned yet.
10
+ */
11
+ export function crosspadDiffCore(submodule = "both") {
12
+ const targets = submodule === "both"
13
+ ? ["crosspad-core", "crosspad-gui"]
14
+ : [submodule];
15
+ const submodules = [];
16
+ for (const sub of targets) {
17
+ const subPath = path.join(CROSSPAD_PC_ROOT, sub);
18
+ const isDevMode = isJunction(subPath);
19
+ const pinnedCommit = getSubmodulePin(CROSSPAD_PC_ROOT, sub);
20
+ // Get current HEAD
21
+ const headResult = runCommand("git rev-parse HEAD", subPath);
22
+ const currentCommit = headResult.success ? headResult.stdout.trim() : null;
23
+ let aheadCount = 0;
24
+ let behindCount = 0;
25
+ let changedFiles = [];
26
+ let commitLog = [];
27
+ if (pinnedCommit && currentCommit && pinnedCommit !== currentCommit) {
28
+ // Count commits ahead/behind
29
+ const countResult = runCommand(`git rev-list --count --left-right ${pinnedCommit}...HEAD`, subPath);
30
+ if (countResult.success) {
31
+ const parts = countResult.stdout.trim().split(/\s+/);
32
+ behindCount = parseInt(parts[0] || "0", 10);
33
+ aheadCount = parseInt(parts[1] || "0", 10);
34
+ }
35
+ // Get diff stat (files changed between pinned and HEAD)
36
+ const diffResult = runCommand(`git diff --name-status ${pinnedCommit}...HEAD`, subPath);
37
+ if (diffResult.success) {
38
+ changedFiles = diffResult.stdout
39
+ .trim()
40
+ .split("\n")
41
+ .filter((l) => l.length > 0)
42
+ .map((line) => {
43
+ const parts = line.split("\t");
44
+ return { status: parts[0], file: parts.slice(1).join("\t") };
45
+ });
46
+ }
47
+ // Get commit log between pinned and HEAD
48
+ const logResult = runCommand(`git log --oneline ${pinnedCommit}..HEAD`, subPath);
49
+ if (logResult.success) {
50
+ commitLog = logResult.stdout
51
+ .trim()
52
+ .split("\n")
53
+ .filter((l) => l.length > 0)
54
+ .slice(0, 20); // Cap at 20
55
+ }
56
+ }
57
+ // Uncommitted changes (working tree)
58
+ const statusResult = runCommand("git status --porcelain", subPath);
59
+ const uncommittedChanges = statusResult.success
60
+ ? statusResult.stdout
61
+ .trim()
62
+ .split("\n")
63
+ .filter((l) => l.length > 0)
64
+ : [];
65
+ submodules.push({
66
+ name: sub,
67
+ pinned_commit: pinnedCommit,
68
+ current_commit: currentCommit,
69
+ is_dev_mode: isDevMode,
70
+ ahead_count: aheadCount,
71
+ behind_count: behindCount,
72
+ changed_files: changedFiles,
73
+ uncommitted_changes: uncommittedChanges,
74
+ commit_log: commitLog,
75
+ });
76
+ }
77
+ return { submodules };
78
+ }
79
+ function isJunction(p) {
80
+ try {
81
+ const stat = fs.lstatSync(p);
82
+ return stat.isSymbolicLink();
83
+ }
84
+ catch {
85
+ return false;
86
+ }
87
+ }
88
+ //# sourceMappingURL=diff-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-core.js","sourceRoot":"","sources":["../../src/tools/diff-core.ts"],"names":[],"mappings":"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"}