@jvittechs/jai1-cli 0.1.95 → 0.1.97

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 (135) hide show
  1. package/dist/cli.js +684 -122
  2. package/dist/cli.js.map +1 -1
  3. package/dist/web-chat/README.md +418 -0
  4. package/dist/web-chat/app.js +1527 -0
  5. package/dist/web-chat/index.html +324 -0
  6. package/dist/web-chat/node_modules/.bin/0ecdsa-generate-keypair +15 -0
  7. package/dist/web-chat/node_modules/.bin/0gentesthtml +89 -0
  8. package/dist/web-chat/node_modules/.bin/0serve +97 -0
  9. package/dist/web-chat/node_modules/.bin/acorn +4 -0
  10. package/dist/web-chat/node_modules/.bin/autoprefixer +22 -0
  11. package/dist/web-chat/node_modules/.bin/baseline-browser-mapping +2 -0
  12. package/dist/web-chat/node_modules/.bin/browserslist +156 -0
  13. package/dist/web-chat/node_modules/.bin/esbuild +0 -0
  14. package/dist/web-chat/node_modules/.bin/jiti +34 -0
  15. package/dist/web-chat/node_modules/.bin/js-yaml +126 -0
  16. package/dist/web-chat/node_modules/.bin/jsesc +148 -0
  17. package/dist/web-chat/node_modules/.bin/json5 +152 -0
  18. package/dist/web-chat/node_modules/.bin/katex +112 -0
  19. package/dist/web-chat/node_modules/.bin/loose-envify +16 -0
  20. package/dist/web-chat/node_modules/.bin/lz-string +13 -0
  21. package/dist/web-chat/node_modules/.bin/nanoid +55 -0
  22. package/dist/web-chat/node_modules/.bin/parser +15 -0
  23. package/dist/web-chat/node_modules/.bin/prebuild-install +78 -0
  24. package/dist/web-chat/node_modules/.bin/rc +4 -0
  25. package/dist/web-chat/node_modules/.bin/rollup +1912 -0
  26. package/dist/web-chat/node_modules/.bin/semver +174 -0
  27. package/dist/web-chat/node_modules/.bin/tsc +2 -0
  28. package/dist/web-chat/node_modules/.bin/tsserver +2 -0
  29. package/dist/web-chat/node_modules/.bin/update-browserslist-db +42 -0
  30. package/dist/web-chat/node_modules/.bin/uuid +2 -0
  31. package/dist/web-chat/node_modules/.bin/uvu +35 -0
  32. package/dist/web-chat/node_modules/.bin/vite +61 -0
  33. package/dist/web-chat/node_modules/.package-lock.json +15371 -0
  34. package/dist/web-chat/node_modules/.vite/deps/_metadata.json +67 -0
  35. package/dist/web-chat/node_modules/.vite/deps/chunk-DC5AMYBS.js +39 -0
  36. package/dist/web-chat/node_modules/.vite/deps/chunk-DC5AMYBS.js.map +7 -0
  37. package/dist/web-chat/node_modules/.vite/deps/chunk-NUMECXU6.js +21628 -0
  38. package/dist/web-chat/node_modules/.vite/deps/chunk-NUMECXU6.js.map +7 -0
  39. package/dist/web-chat/node_modules/.vite/deps/chunk-RLJ2RCJQ.js +1906 -0
  40. package/dist/web-chat/node_modules/.vite/deps/chunk-RLJ2RCJQ.js.map +7 -0
  41. package/dist/web-chat/node_modules/.vite/deps/chunk-S725DACQ.js +928 -0
  42. package/dist/web-chat/node_modules/.vite/deps/chunk-S725DACQ.js.map +7 -0
  43. package/dist/web-chat/node_modules/.vite/deps/chunk-XJVUMEYI.js +8578 -0
  44. package/dist/web-chat/node_modules/.vite/deps/chunk-XJVUMEYI.js.map +7 -0
  45. package/dist/web-chat/node_modules/.vite/deps/package.json +3 -0
  46. package/dist/web-chat/node_modules/.vite/deps/react-dom.js +7 -0
  47. package/dist/web-chat/node_modules/.vite/deps/react-dom.js.map +7 -0
  48. package/dist/web-chat/node_modules/.vite/deps/react-dom_client.js +39 -0
  49. package/dist/web-chat/node_modules/.vite/deps/react-dom_client.js.map +7 -0
  50. package/dist/web-chat/node_modules/.vite/deps/react-markdown.js +8931 -0
  51. package/dist/web-chat/node_modules/.vite/deps/react-markdown.js.map +7 -0
  52. package/dist/web-chat/node_modules/.vite/deps/react.js +6 -0
  53. package/dist/web-chat/node_modules/.vite/deps/react.js.map +7 -0
  54. package/dist/web-chat/node_modules/.vite/deps/react_jsx-dev-runtime.js +913 -0
  55. package/dist/web-chat/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
  56. package/dist/web-chat/node_modules/.vite/deps/react_jsx-runtime.js +7 -0
  57. package/dist/web-chat/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
  58. package/dist/web-chat/node_modules/.vite/deps/remark-gfm.js +7403 -0
  59. package/dist/web-chat/node_modules/.vite/deps/remark-gfm.js.map +7 -0
  60. package/dist/web-chat/node_modules/@codemirror/autocomplete/.github/workflows/dispatch.yml +16 -0
  61. package/dist/web-chat/node_modules/@codemirror/commands/.github/workflows/dispatch.yml +16 -0
  62. package/dist/web-chat/node_modules/@codemirror/lang-cpp/.github/workflows/dispatch.yml +16 -0
  63. package/dist/web-chat/node_modules/@codemirror/lang-css/.github/workflows/dispatch.yml +16 -0
  64. package/dist/web-chat/node_modules/@codemirror/lang-go/.github/workflows/dispatch.yml +16 -0
  65. package/dist/web-chat/node_modules/@codemirror/lang-html/.github/workflows/dispatch.yml +16 -0
  66. package/dist/web-chat/node_modules/@codemirror/lang-java/.github/workflows/dispatch.yml +16 -0
  67. package/dist/web-chat/node_modules/@codemirror/lang-javascript/.github/workflows/dispatch.yml +16 -0
  68. package/dist/web-chat/node_modules/@codemirror/lang-json/.github/workflows/dispatch.yml +16 -0
  69. package/dist/web-chat/node_modules/@codemirror/lang-less/.github/workflows/dispatch.yml +16 -0
  70. package/dist/web-chat/node_modules/@codemirror/lang-markdown/.github/workflows/dispatch.yml +16 -0
  71. package/dist/web-chat/node_modules/@codemirror/lang-python/.github/workflows/dispatch.yml +16 -0
  72. package/dist/web-chat/node_modules/@codemirror/lang-rust/.github/workflows/dispatch.yml +16 -0
  73. package/dist/web-chat/node_modules/@codemirror/lang-sass/.github/workflows/dispatch.yml +16 -0
  74. package/dist/web-chat/node_modules/@codemirror/lang-sql/.github/workflows/dispatch.yml +16 -0
  75. package/dist/web-chat/node_modules/@codemirror/lang-wast/.github/workflows/dispatch.yml +16 -0
  76. package/dist/web-chat/node_modules/@codemirror/lang-xml/.github/workflows/dispatch.yml +16 -0
  77. package/dist/web-chat/node_modules/@codemirror/lang-yaml/.github/workflows/dispatch.yml +16 -0
  78. package/dist/web-chat/node_modules/@codemirror/language/.github/workflows/dispatch.yml +16 -0
  79. package/dist/web-chat/node_modules/@codemirror/language-data/.github/workflows/dispatch.yml +16 -0
  80. package/dist/web-chat/node_modules/@codemirror/legacy-modes/.github/workflows/dispatch.yml +16 -0
  81. package/dist/web-chat/node_modules/@codemirror/lint/.github/workflows/dispatch.yml +16 -0
  82. package/dist/web-chat/node_modules/@codemirror/search/.github/workflows/dispatch.yml +16 -0
  83. package/dist/web-chat/node_modules/@codemirror/state/.github/workflows/dispatch.yml +16 -0
  84. package/dist/web-chat/node_modules/@codemirror/theme-one-dark/.github/workflows/dispatch.yml +16 -0
  85. package/dist/web-chat/node_modules/@codemirror/view/.github/workflows/dispatch.yml +16 -0
  86. package/dist/web-chat/node_modules/@lezer/html/src/.tern-port +1 -0
  87. package/dist/web-chat/node_modules/@lezer/php/.tern-port +1 -0
  88. package/dist/web-chat/node_modules/@lezer/php/test/.tern-port +1 -0
  89. package/dist/web-chat/node_modules/@lezer/sass/src/.tern-port +1 -0
  90. package/dist/web-chat/node_modules/@lezer/yaml/.tern-port +1 -0
  91. package/dist/web-chat/node_modules/@ungap/structured-clone/.github/workflows/node.js.yml +31 -0
  92. package/dist/web-chat/node_modules/bl/.travis.yml +17 -0
  93. package/dist/web-chat/node_modules/clean-set/.prettierrc +4 -0
  94. package/dist/web-chat/node_modules/clean-set/.travis.yml +7 -0
  95. package/dist/web-chat/node_modules/codemirror/.github/workflows/dispatch.yml +16 -0
  96. package/dist/web-chat/node_modules/entities/src/generated/.eslintrc.json +10 -0
  97. package/dist/web-chat/node_modules/es5-ext/promise/.eslintrc.json +1 -0
  98. package/dist/web-chat/node_modules/es6-iterator/.editorconfig +14 -0
  99. package/dist/web-chat/node_modules/es6-iterator/test/.eslintrc.json +5 -0
  100. package/dist/web-chat/node_modules/es6-symbol/.testignore +1 -0
  101. package/dist/web-chat/node_modules/escape-carriage/.github/workflows/node.js.yml +19 -0
  102. package/dist/web-chat/node_modules/esniff/.prettierignore +2 -0
  103. package/dist/web-chat/node_modules/esniff/.testignore +3 -0
  104. package/dist/web-chat/node_modules/event-emitter/.lint +15 -0
  105. package/dist/web-chat/node_modules/event-emitter/.testignore +1 -0
  106. package/dist/web-chat/node_modules/event-emitter/.travis.yml +16 -0
  107. package/dist/web-chat/node_modules/expand-template/.travis.yml +6 -0
  108. package/dist/web-chat/node_modules/extend/.editorconfig +20 -0
  109. package/dist/web-chat/node_modules/extend/.eslintrc +17 -0
  110. package/dist/web-chat/node_modules/extend/.jscs.json +175 -0
  111. package/dist/web-chat/node_modules/extend/.travis.yml +230 -0
  112. package/dist/web-chat/node_modules/gensync/test/.babelrc +5 -0
  113. package/dist/web-chat/node_modules/github-from-package/.travis.yml +4 -0
  114. package/dist/web-chat/node_modules/intersection-observer/.eslintrc +45 -0
  115. package/dist/web-chat/node_modules/lib0/.github/workflows/node.js.yml +24 -0
  116. package/dist/web-chat/node_modules/lib0/.jsdoc.json +18 -0
  117. package/dist/web-chat/node_modules/lib0/.vscode/launch.json +17 -0
  118. package/dist/web-chat/node_modules/minimist/.eslintrc +29 -0
  119. package/dist/web-chat/node_modules/minimist/.github/FUNDING.yml +12 -0
  120. package/dist/web-chat/node_modules/minimist/.nycrc +14 -0
  121. package/dist/web-chat/node_modules/napi-build-utils/.github/workflows/run-npm-tests.yml +31 -0
  122. package/dist/web-chat/node_modules/next-tick/.editorconfig +16 -0
  123. package/dist/web-chat/node_modules/next-tick/.github/FUNDING.yml +1 -0
  124. package/dist/web-chat/node_modules/next-tick/.lint +16 -0
  125. package/dist/web-chat/node_modules/node-abi/node_modules/.bin/semver +191 -0
  126. package/dist/web-chat/node_modules/pump/.github/FUNDING.yml +2 -0
  127. package/dist/web-chat/node_modules/pump/.travis.yml +5 -0
  128. package/dist/web-chat/node_modules/simple-concat/.travis.yml +3 -0
  129. package/dist/web-chat/node_modules/simple-get/.github/dependabot.yml +15 -0
  130. package/dist/web-chat/node_modules/simple-get/.github/workflows/ci.yml +23 -0
  131. package/dist/web-chat/node_modules/tar-fs/.travis.yml +6 -0
  132. package/dist/web-chat/node_modules/tar-fs/test/fixtures/e/directory/.ignore +0 -0
  133. package/dist/web-chat/node_modules/w3c-keyname/.tern-port +1 -0
  134. package/dist/web-chat/style.css +1882 -0
  135. package/package.json +4 -2
package/dist/cli.js CHANGED
@@ -33,7 +33,7 @@ var NetworkError = class extends Jai1Error {
33
33
  // package.json
34
34
  var package_default = {
35
35
  name: "@jvittechs/jai1-cli",
36
- version: "0.1.95",
36
+ version: "0.1.97",
37
37
  description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Please contact TeamAI for usage instructions.",
38
38
  type: "module",
39
39
  bin: {
@@ -70,7 +70,8 @@ var package_default = {
70
70
  "coding"
71
71
  ],
72
72
  scripts: {
73
- build: "tsup src/cli.ts --dts --format esm --target node22 --out-dir dist --sourcemap",
73
+ build: "tsup src/cli.ts --dts --format esm --target node22 --out-dir dist --sourcemap && npm run copy-web-chat",
74
+ "copy-web-chat": "mkdir -p dist/web-chat && cp -r src/web-chat/* dist/web-chat/",
74
75
  dev: "tsx src/cli.ts --help",
75
76
  lint: "eslint .",
76
77
  test: "vitest run",
@@ -92,6 +93,7 @@ var package_default = {
92
93
  "ink-text-input": "^6.0.0",
93
94
  marked: "^12.0.0",
94
95
  "marked-terminal": "^7.0.0",
96
+ open: "^10.1.0",
95
97
  "p-limit": "^5.0.0",
96
98
  "p-queue": "^7.4.1",
97
99
  "p-retry": "^6.2.0",
@@ -798,6 +800,9 @@ var IDE_MIGRATION_CONFIGS = {
798
800
  workflowsPath: null,
799
801
  commandsPath: "commands",
800
802
  fileExtension: ".mdc",
803
+ // For rules
804
+ workflowsExtension: ".md",
805
+ // For workflows/commands
801
806
  generateFrontmatter: (opts) => {
802
807
  const lines = ["---"];
803
808
  if (opts.description) {
@@ -1000,7 +1005,14 @@ var MigrateIdeService = class {
1000
1005
  const stat = await fs4.stat(filepath);
1001
1006
  if (!stat.isFile()) continue;
1002
1007
  const content = await fs4.readFile(filepath, "utf-8");
1003
- const { data: frontmatter } = matter(content);
1008
+ let frontmatter = {};
1009
+ try {
1010
+ const { data } = matter(content);
1011
+ frontmatter = data;
1012
+ } catch (e) {
1013
+ console.warn(`\u26A0\uFE0F Skipping ${file}: Invalid YAML frontmatter`);
1014
+ continue;
1015
+ }
1004
1016
  const name = path.basename(file, ".md");
1005
1017
  const trigger = this.extractTrigger(frontmatter);
1006
1018
  const alwaysApply = this.isAlwaysTrigger(trigger);
@@ -1105,21 +1117,24 @@ ${reference}
1105
1117
  */
1106
1118
  getTargetPath(config, item) {
1107
1119
  let contentPath = null;
1120
+ let extension = config.fileExtension;
1108
1121
  switch (item.type) {
1109
1122
  case "rules":
1110
1123
  contentPath = config.rulesPath;
1111
1124
  break;
1112
1125
  case "workflows":
1113
1126
  contentPath = config.workflowsPath ?? config.commandsPath;
1127
+ extension = config.workflowsExtension ?? config.fileExtension;
1114
1128
  break;
1115
1129
  case "commands":
1116
1130
  contentPath = config.commandsPath;
1131
+ extension = config.workflowsExtension ?? config.fileExtension;
1117
1132
  break;
1118
1133
  }
1119
1134
  if (!contentPath) {
1120
1135
  return null;
1121
1136
  }
1122
- const filename = `${item.name}${config.fileExtension}`;
1137
+ const filename = `${item.name}${extension}`;
1123
1138
  return path.join(this.projectPath, config.basePath, contentPath, filename);
1124
1139
  }
1125
1140
  // Helper methods
@@ -2349,7 +2364,15 @@ var ContextScannerService = class {
2349
2364
  async parseContextItem(filepath, ide, type) {
2350
2365
  const content = await fs5.readFile(filepath, "utf-8");
2351
2366
  const stat = await fs5.stat(filepath);
2352
- const { data: frontmatter, content: bodyContent } = matter2(content);
2367
+ let frontmatter = {};
2368
+ let bodyContent = content;
2369
+ try {
2370
+ const parsed = matter2(content);
2371
+ frontmatter = parsed.data;
2372
+ bodyContent = parsed.content;
2373
+ } catch (e) {
2374
+ console.warn(`\u26A0\uFE0F Invalid YAML frontmatter in ${filepath}, skipping frontmatter parsing`);
2375
+ }
2353
2376
  const config = IDE_CONFIGS[ide];
2354
2377
  const description = frontmatter[config.frontmatterSchema.descriptionField];
2355
2378
  let globs;
@@ -3366,9 +3389,9 @@ var IdeDetectionService = class {
3366
3389
  /**
3367
3390
  * Check if a path exists
3368
3391
  */
3369
- async pathExists(path8) {
3392
+ async pathExists(path9) {
3370
3393
  try {
3371
- await fs7.access(path8);
3394
+ await fs7.access(path9);
3372
3395
  return true;
3373
3396
  } catch {
3374
3397
  return false;
@@ -4563,15 +4586,19 @@ var LlmProxyService = class {
4563
4586
  }
4564
4587
  const reader = response.body.getReader();
4565
4588
  const decoder = new TextDecoder();
4589
+ let buffer = "";
4566
4590
  try {
4567
4591
  while (true) {
4568
4592
  const { done, value } = await reader.read();
4569
4593
  if (done) break;
4570
- const chunk = decoder.decode(value, { stream: true });
4571
- const lines = chunk.split("\n").filter((line) => line.trim() !== "");
4594
+ buffer += decoder.decode(value, { stream: true });
4595
+ const lines = buffer.split("\n");
4596
+ buffer = lines.pop() || "";
4572
4597
  for (const line of lines) {
4573
- if (line.startsWith("data: ")) {
4574
- const data = line.slice(6);
4598
+ const trimmedLine = line.trim();
4599
+ if (!trimmedLine) continue;
4600
+ if (trimmedLine.startsWith("data: ")) {
4601
+ const data = trimmedLine.slice(6);
4575
4602
  if (data === "[DONE]") {
4576
4603
  return;
4577
4604
  }
@@ -4586,6 +4613,22 @@ var LlmProxyService = class {
4586
4613
  }
4587
4614
  }
4588
4615
  }
4616
+ if (buffer.trim()) {
4617
+ const trimmedLine = buffer.trim();
4618
+ if (trimmedLine.startsWith("data: ")) {
4619
+ const data = trimmedLine.slice(6);
4620
+ if (data !== "[DONE]") {
4621
+ try {
4622
+ const parsed = JSON.parse(data);
4623
+ const content = parsed.choices[0]?.delta?.content;
4624
+ if (content) {
4625
+ yield content;
4626
+ }
4627
+ } catch (e) {
4628
+ }
4629
+ }
4630
+ }
4631
+ }
4589
4632
  } finally {
4590
4633
  reader.releaseLock();
4591
4634
  }
@@ -5068,8 +5111,469 @@ var ChatApp = ({ service, initialModel }) => {
5068
5111
  ), /* @__PURE__ */ React25.createElement(Box16, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Text17, { dimColor: true }, footer)));
5069
5112
  };
5070
5113
 
5114
+ // src/server/web-chat-server.ts
5115
+ import http from "http";
5116
+ import fs8 from "fs";
5117
+ import path4 from "path";
5118
+ import { fileURLToPath } from "url";
5119
+
5120
+ // src/server/session.ts
5121
+ import crypto from "crypto";
5122
+ var SESSION_PREFIX = "wc_";
5123
+ var DEFAULT_TTL_MINUTES = 30;
5124
+ var SessionManager = class {
5125
+ sessions = /* @__PURE__ */ new Map();
5126
+ cleanupInterval = null;
5127
+ constructor() {
5128
+ this.startCleanup();
5129
+ }
5130
+ /**
5131
+ * Generate a new session token
5132
+ */
5133
+ generateToken() {
5134
+ const randomBytes = crypto.randomBytes(16).toString("hex");
5135
+ return `${SESSION_PREFIX}${randomBytes}`;
5136
+ }
5137
+ /**
5138
+ * Create a new session
5139
+ */
5140
+ createSession(config) {
5141
+ const token = this.generateToken();
5142
+ const now = /* @__PURE__ */ new Date();
5143
+ const ttlMinutes = config.ttlMinutes ?? DEFAULT_TTL_MINUTES;
5144
+ const session = {
5145
+ token,
5146
+ accessKey: config.accessKey,
5147
+ apiUrl: config.apiUrl,
5148
+ createdAt: now,
5149
+ expiresAt: new Date(now.getTime() + ttlMinutes * 60 * 1e3),
5150
+ lastActivity: now
5151
+ };
5152
+ this.sessions.set(token, session);
5153
+ return session;
5154
+ }
5155
+ /**
5156
+ * Validate a session token and return session if valid
5157
+ * Also extends the session TTL on successful validation
5158
+ */
5159
+ validateSession(token) {
5160
+ if (!token || !token.startsWith(SESSION_PREFIX)) {
5161
+ return null;
5162
+ }
5163
+ const session = this.sessions.get(token);
5164
+ if (!session) {
5165
+ return null;
5166
+ }
5167
+ if (/* @__PURE__ */ new Date() > session.expiresAt) {
5168
+ this.sessions.delete(token);
5169
+ return null;
5170
+ }
5171
+ session.lastActivity = /* @__PURE__ */ new Date();
5172
+ session.expiresAt = new Date(
5173
+ session.lastActivity.getTime() + DEFAULT_TTL_MINUTES * 60 * 1e3
5174
+ );
5175
+ return session;
5176
+ }
5177
+ /**
5178
+ * Get session without extending TTL
5179
+ */
5180
+ getSession(token) {
5181
+ if (!token || !token.startsWith(SESSION_PREFIX)) {
5182
+ return null;
5183
+ }
5184
+ const session = this.sessions.get(token);
5185
+ if (!session) {
5186
+ return null;
5187
+ }
5188
+ if (/* @__PURE__ */ new Date() > session.expiresAt) {
5189
+ this.sessions.delete(token);
5190
+ return null;
5191
+ }
5192
+ return session;
5193
+ }
5194
+ /**
5195
+ * Invalidate/delete a session
5196
+ */
5197
+ invalidateSession(token) {
5198
+ this.sessions.delete(token);
5199
+ }
5200
+ /**
5201
+ * Get remaining TTL in seconds
5202
+ */
5203
+ getTimeToLive(token) {
5204
+ const session = this.getSession(token);
5205
+ if (!session) {
5206
+ return 0;
5207
+ }
5208
+ const remaining = session.expiresAt.getTime() - Date.now();
5209
+ return Math.max(0, Math.floor(remaining / 1e3));
5210
+ }
5211
+ /**
5212
+ * Start periodic cleanup of expired sessions
5213
+ */
5214
+ startCleanup() {
5215
+ this.cleanupInterval = setInterval(() => {
5216
+ const now = /* @__PURE__ */ new Date();
5217
+ for (const [token, session] of this.sessions.entries()) {
5218
+ if (now > session.expiresAt) {
5219
+ this.sessions.delete(token);
5220
+ }
5221
+ }
5222
+ }, 5 * 60 * 1e3);
5223
+ this.cleanupInterval.unref();
5224
+ }
5225
+ /**
5226
+ * Stop cleanup interval (call when shutting down)
5227
+ */
5228
+ stopCleanup() {
5229
+ if (this.cleanupInterval) {
5230
+ clearInterval(this.cleanupInterval);
5231
+ this.cleanupInterval = null;
5232
+ }
5233
+ }
5234
+ /**
5235
+ * Clear all sessions (for shutdown)
5236
+ */
5237
+ clearAll() {
5238
+ this.sessions.clear();
5239
+ this.stopCleanup();
5240
+ }
5241
+ /**
5242
+ * Get active session count (for debugging)
5243
+ */
5244
+ getActiveSessionCount() {
5245
+ return this.sessions.size;
5246
+ }
5247
+ };
5248
+
5249
+ // src/server/web-chat-server.ts
5250
+ var __filename = fileURLToPath(import.meta.url);
5251
+ var __dirname = path4.dirname(__filename);
5252
+ var MIME_TYPES = {
5253
+ ".html": "text/html",
5254
+ ".css": "text/css",
5255
+ ".js": "application/javascript",
5256
+ ".json": "application/json",
5257
+ ".png": "image/png",
5258
+ ".svg": "image/svg+xml",
5259
+ ".ico": "image/x-icon"
5260
+ };
5261
+ async function findAvailablePort(startPort, endPort) {
5262
+ for (let port = startPort; port <= endPort; port++) {
5263
+ try {
5264
+ await new Promise((resolve4, reject) => {
5265
+ const server = http.createServer();
5266
+ server.listen(port, "127.0.0.1");
5267
+ server.once("listening", () => {
5268
+ server.close(() => resolve4());
5269
+ });
5270
+ server.once("error", reject);
5271
+ });
5272
+ return port;
5273
+ } catch {
5274
+ continue;
5275
+ }
5276
+ }
5277
+ throw new Error(`No available port found in range ${startPort}-${endPort}`);
5278
+ }
5279
+ async function parseJsonBody(req) {
5280
+ return new Promise((resolve4, reject) => {
5281
+ let body = "";
5282
+ req.on("data", (chunk) => {
5283
+ body += chunk.toString();
5284
+ });
5285
+ req.on("end", () => {
5286
+ try {
5287
+ resolve4(body ? JSON.parse(body) : {});
5288
+ } catch (e) {
5289
+ reject(new Error("Invalid JSON body"));
5290
+ }
5291
+ });
5292
+ req.on("error", reject);
5293
+ });
5294
+ }
5295
+ function sendJson(res, statusCode, data) {
5296
+ res.writeHead(statusCode, {
5297
+ "Content-Type": "application/json",
5298
+ "Access-Control-Allow-Origin": "*",
5299
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
5300
+ "Access-Control-Allow-Headers": "Content-Type, X-Session-Token"
5301
+ });
5302
+ res.end(JSON.stringify(data));
5303
+ }
5304
+ function sendError(res, statusCode, code, message) {
5305
+ sendJson(res, statusCode, {
5306
+ success: false,
5307
+ error: { code, message }
5308
+ });
5309
+ }
5310
+ function getSessionToken(req) {
5311
+ const headerToken = req.headers["x-session-token"];
5312
+ if (headerToken && typeof headerToken === "string") {
5313
+ return headerToken;
5314
+ }
5315
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
5316
+ return url.searchParams.get("session");
5317
+ }
5318
+ function createWebChatServer(options) {
5319
+ const { config } = options;
5320
+ const host = options.host ?? "127.0.0.1";
5321
+ let port = options.port ?? 0;
5322
+ const sessionManager = new SessionManager();
5323
+ let server = null;
5324
+ let session = null;
5325
+ const staticDir = path4.join(__dirname, "web-chat");
5326
+ async function serveStaticFile(res, filePath) {
5327
+ const fullPath = path4.join(staticDir, filePath);
5328
+ if (!fullPath.startsWith(staticDir)) {
5329
+ sendError(res, 403, "ERR-WC-006", "Forbidden");
5330
+ return;
5331
+ }
5332
+ try {
5333
+ const stat = await fs8.promises.stat(fullPath);
5334
+ if (!stat.isFile()) {
5335
+ sendError(res, 404, "ERR-WC-007", "Not found");
5336
+ return;
5337
+ }
5338
+ const ext = path4.extname(fullPath);
5339
+ const contentType = MIME_TYPES[ext] || "application/octet-stream";
5340
+ const content = await fs8.promises.readFile(fullPath);
5341
+ res.writeHead(200, { "Content-Type": contentType });
5342
+ res.end(content);
5343
+ } catch (error) {
5344
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
5345
+ sendError(res, 404, "ERR-WC-007", "Not found");
5346
+ } else {
5347
+ sendError(res, 500, "ERR-WC-008", "Internal server error");
5348
+ }
5349
+ }
5350
+ }
5351
+ async function handleApi(req, res, pathname) {
5352
+ if (req.method === "OPTIONS") {
5353
+ res.writeHead(204, {
5354
+ "Access-Control-Allow-Origin": "*",
5355
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
5356
+ "Access-Control-Allow-Headers": "Content-Type, X-Session-Token"
5357
+ });
5358
+ res.end();
5359
+ return;
5360
+ }
5361
+ if (pathname === "/health") {
5362
+ sendJson(res, 200, { status: "ok" });
5363
+ return;
5364
+ }
5365
+ const token = getSessionToken(req);
5366
+ const validSession = token ? sessionManager.validateSession(token) : null;
5367
+ if (!validSession) {
5368
+ sendError(
5369
+ res,
5370
+ 401,
5371
+ "ERR-WC-001",
5372
+ "Session expired. Please run 'jai1 chat --web' again."
5373
+ );
5374
+ return;
5375
+ }
5376
+ const llmService = new LlmProxyService({
5377
+ accessKey: validSession.accessKey,
5378
+ apiUrl: validSession.apiUrl
5379
+ });
5380
+ if (pathname === "/api/session" && req.method === "GET") {
5381
+ try {
5382
+ const models = await llmService.getModelsWithUsage();
5383
+ const allowedModels = models.filter((m) => m.allowed).map((m) => m.id);
5384
+ sendJson(res, 200, {
5385
+ success: true,
5386
+ data: {
5387
+ expiresAt: validSession.expiresAt.toISOString(),
5388
+ models: allowedModels
5389
+ }
5390
+ });
5391
+ } catch (error) {
5392
+ sendError(res, 502, "ERR-WC-004", "Failed to connect to LLM service.");
5393
+ }
5394
+ return;
5395
+ }
5396
+ if (pathname === "/api/models" && req.method === "GET") {
5397
+ try {
5398
+ const models = await llmService.getModelsWithUsage();
5399
+ sendJson(res, 200, {
5400
+ success: true,
5401
+ data: models.filter((m) => m.allowed)
5402
+ });
5403
+ } catch (error) {
5404
+ sendError(res, 502, "ERR-WC-004", "Failed to connect to LLM service.");
5405
+ }
5406
+ return;
5407
+ }
5408
+ if (pathname === "/api/stats" && req.method === "GET") {
5409
+ try {
5410
+ const [limits, usage] = await Promise.all([
5411
+ llmService.getLimits(),
5412
+ llmService.getUsage(1)
5413
+ // Today only
5414
+ ]);
5415
+ const today = (/* @__PURE__ */ new Date()).toLocaleDateString("en-CA", { timeZone: "Asia/Ho_Chi_Minh" });
5416
+ const modelStats = {};
5417
+ const allowedModels = limits.effectiveAllowedModels || [];
5418
+ const rateLimits = limits.effectiveRateLimits || {};
5419
+ allowedModels.forEach((modelId) => {
5420
+ const limit = rateLimits[modelId] || rateLimits[modelId.toLowerCase()] || 0;
5421
+ const usageRecord = (usage.data || []).find(
5422
+ (u) => (u.model === modelId || u.model.toLowerCase() === modelId.toLowerCase()) && u.date === today
5423
+ );
5424
+ const used = usageRecord?.count || 0;
5425
+ modelStats[modelId] = {
5426
+ limit,
5427
+ used,
5428
+ remaining: Math.max(0, limit - used)
5429
+ };
5430
+ });
5431
+ sendJson(res, 200, {
5432
+ success: true,
5433
+ data: {
5434
+ date: today,
5435
+ models: modelStats
5436
+ }
5437
+ });
5438
+ } catch (error) {
5439
+ sendError(res, 502, "ERR-WC-004", "Failed to connect to LLM service.");
5440
+ }
5441
+ return;
5442
+ }
5443
+ if (pathname === "/api/chat" && req.method === "POST") {
5444
+ try {
5445
+ const body = await parseJsonBody(req);
5446
+ if (!body.message || !body.model) {
5447
+ sendError(res, 400, "ERR-WC-009", "Missing message or model");
5448
+ return;
5449
+ }
5450
+ const messages = [];
5451
+ if (body.systemPrompt) {
5452
+ messages.push({ role: "system", content: body.systemPrompt });
5453
+ }
5454
+ messages.push(
5455
+ ...(body.history || []).map((m) => ({
5456
+ role: m.role,
5457
+ content: m.content
5458
+ })),
5459
+ { role: "user", content: body.message }
5460
+ );
5461
+ res.writeHead(200, {
5462
+ "Content-Type": "text/event-stream",
5463
+ "Cache-Control": "no-cache",
5464
+ Connection: "keep-alive",
5465
+ "Access-Control-Allow-Origin": "*"
5466
+ });
5467
+ try {
5468
+ for await (const chunk of llmService.chatStream(
5469
+ messages,
5470
+ body.model
5471
+ )) {
5472
+ res.write(
5473
+ `data: ${JSON.stringify({ type: "chunk", content: chunk })}
5474
+
5475
+ `
5476
+ );
5477
+ }
5478
+ res.write(`data: ${JSON.stringify({ type: "done" })}
5479
+
5480
+ `);
5481
+ } catch (streamError) {
5482
+ const errorMessage = streamError instanceof Error ? streamError.message : "Unknown error";
5483
+ if (errorMessage.includes("not allowed")) {
5484
+ res.write(
5485
+ `data: ${JSON.stringify({ type: "error", code: "ERR-WC-002", message: "You don't have access to this model." })}
5486
+
5487
+ `
5488
+ );
5489
+ } else if (errorMessage.includes("rate limit")) {
5490
+ res.write(
5491
+ `data: ${JSON.stringify({ type: "error", code: "ERR-WC-003", message: "Rate limit exceeded. Please wait." })}
5492
+
5493
+ `
5494
+ );
5495
+ } else {
5496
+ res.write(
5497
+ `data: ${JSON.stringify({ type: "error", code: "ERR-WC-004", message: errorMessage })}
5498
+
5499
+ `
5500
+ );
5501
+ }
5502
+ }
5503
+ res.end();
5504
+ } catch (error) {
5505
+ sendError(res, 500, "ERR-WC-008", "Internal server error");
5506
+ }
5507
+ return;
5508
+ }
5509
+ sendError(res, 404, "ERR-WC-010", "API endpoint not found");
5510
+ }
5511
+ async function handleRequest(req, res) {
5512
+ const url = new URL(req.url || "/", `http://${req.headers.host}`);
5513
+ const pathname = url.pathname;
5514
+ try {
5515
+ if (pathname.startsWith("/api/") || pathname === "/health") {
5516
+ await handleApi(req, res, pathname);
5517
+ return;
5518
+ }
5519
+ let filePath = pathname === "/" ? "/index.html" : pathname;
5520
+ filePath = filePath.replace(/^\//, "");
5521
+ await serveStaticFile(res, filePath);
5522
+ } catch (error) {
5523
+ console.error("[WebChatServer] Error:", error);
5524
+ sendError(res, 500, "ERR-WC-008", "Internal server error");
5525
+ }
5526
+ }
5527
+ return {
5528
+ async start() {
5529
+ if (!port || port === 0) {
5530
+ port = await findAvailablePort(54321, 54399);
5531
+ }
5532
+ session = sessionManager.createSession({
5533
+ accessKey: config.accessKey,
5534
+ apiUrl: config.apiUrl
5535
+ });
5536
+ server = http.createServer(handleRequest);
5537
+ return new Promise((resolve4, reject) => {
5538
+ server.listen(port, host, () => {
5539
+ console.log(`
5540
+ \u{1F310} Web Chat server started at http://${host}:${port}`);
5541
+ console.log(`\u{1F4CB} Session token: ${session.token.slice(0, 12)}...`);
5542
+ console.log(`\u23F0 Session expires in 30 minutes
5543
+ `);
5544
+ resolve4({
5545
+ port,
5546
+ sessionToken: session.token
5547
+ });
5548
+ });
5549
+ server.once("error", (err) => {
5550
+ if (err.code === "EADDRINUSE") {
5551
+ reject(new Error(`Port ${port} is already in use.`));
5552
+ } else {
5553
+ reject(err);
5554
+ }
5555
+ });
5556
+ });
5557
+ },
5558
+ async stop() {
5559
+ if (server) {
5560
+ return new Promise((resolve4) => {
5561
+ server.close(() => {
5562
+ sessionManager.clearAll();
5563
+ console.log("\n\u{1F44B} Web Chat server stopped");
5564
+ resolve4();
5565
+ });
5566
+ });
5567
+ }
5568
+ },
5569
+ getUrl() {
5570
+ return `http://${host}:${port}/?session=${session?.token || ""}`;
5571
+ }
5572
+ };
5573
+ }
5574
+
5071
5575
  // src/commands/chat.ts
5072
- async function handleChatCommand(options) {
5576
+ async function handleTerminalChat(options) {
5073
5577
  const configService = new ConfigService();
5074
5578
  const config = await configService.load();
5075
5579
  if (!config) {
@@ -5090,8 +5594,58 @@ async function handleChatCommand(options) {
5090
5594
  await waitUntilExit();
5091
5595
  clear();
5092
5596
  }
5597
+ async function handleWebChat(options) {
5598
+ const configService = new ConfigService();
5599
+ const config = await configService.load();
5600
+ if (!config) {
5601
+ throw new ValidationError('Not initialized. Run "jai1 auth" first.');
5602
+ }
5603
+ const port = options.port ? parseInt(options.port, 10) : void 0;
5604
+ const server = createWebChatServer({
5605
+ config,
5606
+ port
5607
+ });
5608
+ try {
5609
+ const { port: actualPort, sessionToken } = await server.start();
5610
+ const url = server.getUrl();
5611
+ if (!options.noOpen) {
5612
+ const { default: open } = await import("open");
5613
+ await open(url);
5614
+ console.log("\u{1F310} Browser opened with chat interface");
5615
+ } else {
5616
+ console.log(`
5617
+ \u{1F4CB} Open this URL in your browser:
5618
+ ${url}
5619
+ `);
5620
+ }
5621
+ console.log("Press Ctrl+C to stop the server\n");
5622
+ await new Promise((resolve4) => {
5623
+ process.on("SIGINT", async () => {
5624
+ console.log("\n\n\u{1F6D1} Shutting down...");
5625
+ await server.stop();
5626
+ resolve4();
5627
+ });
5628
+ process.on("SIGTERM", async () => {
5629
+ await server.stop();
5630
+ resolve4();
5631
+ });
5632
+ });
5633
+ } catch (error) {
5634
+ if (error instanceof Error && error.message.includes("Port")) {
5635
+ throw new ValidationError(error.message + " Try specifying a different port with --port.");
5636
+ }
5637
+ throw error;
5638
+ }
5639
+ }
5640
+ async function handleChatCommand(options) {
5641
+ if (options.web) {
5642
+ await handleWebChat(options);
5643
+ } else {
5644
+ await handleTerminalChat(options);
5645
+ }
5646
+ }
5093
5647
  function createChatCommand() {
5094
- const cmd = new Command12("chat").description("Interactive AI chat with Jai1 LLM Proxy").option("--model <model>", "Initial model to use (e.g., gpt-4o, claude-3-opus)").action(async (options) => {
5648
+ const cmd = new Command12("chat").description("Interactive AI chat with Jai1 LLM Proxy").option("--model <model>", "Initial model to use (e.g., gpt-4o, claude-3-opus)").option("--web", "Open chat in web browser instead of terminal").option("--port <port>", "Port for web server (default: auto-find available port)").option("--no-open", "Don't auto-open browser (web mode only)").action(async (options) => {
5095
5649
  await handleChatCommand(options);
5096
5650
  });
5097
5651
  return cmd;
@@ -5239,8 +5793,8 @@ function createStatsCommand() {
5239
5793
  import { Command as Command15 } from "commander";
5240
5794
 
5241
5795
  // src/services/translation.service.ts
5242
- import { promises as fs8 } from "fs";
5243
- import path4 from "path";
5796
+ import { promises as fs9 } from "fs";
5797
+ import path5 from "path";
5244
5798
  import pLimit from "p-limit";
5245
5799
  import pRetry from "p-retry";
5246
5800
  var TRANSLATABLE_EXTS = [".md", ".txt", ".json", ".yaml", ".yml"];
@@ -5258,7 +5812,7 @@ var TranslationService = class {
5258
5812
  */
5259
5813
  async detectInputType(input4) {
5260
5814
  try {
5261
- const stat = await fs8.stat(input4);
5815
+ const stat = await fs9.stat(input4);
5262
5816
  if (stat.isDirectory()) return "folder";
5263
5817
  if (stat.isFile()) return "file";
5264
5818
  } catch {
@@ -5295,13 +5849,13 @@ var TranslationService = class {
5295
5849
  */
5296
5850
  async translateFile(filePath) {
5297
5851
  try {
5298
- const content = await fs8.readFile(filePath, "utf-8");
5299
- const ext = path4.extname(filePath).toLowerCase();
5852
+ const content = await fs9.readFile(filePath, "utf-8");
5853
+ const ext = path5.extname(filePath).toLowerCase();
5300
5854
  const fileType = this.getFileType(ext);
5301
5855
  const translatedContent = await this.translateWithRetry(content, fileType);
5302
5856
  const outputPath = this.generateOutputPath(filePath);
5303
5857
  if (!this.options.dryRun) {
5304
- await fs8.writeFile(outputPath, translatedContent, "utf-8");
5858
+ await fs9.writeFile(outputPath, translatedContent, "utf-8");
5305
5859
  }
5306
5860
  return {
5307
5861
  inputPath: filePath,
@@ -5356,27 +5910,27 @@ var TranslationService = class {
5356
5910
  if (this.options.output) {
5357
5911
  return this.options.output;
5358
5912
  }
5359
- const ext = path4.extname(inputPath);
5360
- const base = path4.basename(inputPath, ext);
5361
- const dir = path4.dirname(inputPath);
5913
+ const ext = path5.extname(inputPath);
5914
+ const base = path5.basename(inputPath, ext);
5915
+ const dir = path5.dirname(inputPath);
5362
5916
  const lang = this.options.to;
5363
- return path4.join(dir, `${base}.${lang}${ext}`);
5917
+ return path5.join(dir, `${base}.${lang}${ext}`);
5364
5918
  }
5365
5919
  /**
5366
5920
  * Discover translatable files in folder recursively
5367
5921
  */
5368
5922
  async discoverFiles(folderPath) {
5369
- const entries = await fs8.readdir(folderPath, { withFileTypes: true });
5923
+ const entries = await fs9.readdir(folderPath, { withFileTypes: true });
5370
5924
  const files = [];
5371
5925
  for (const entry of entries) {
5372
- const fullPath = path4.join(folderPath, entry.name);
5926
+ const fullPath = path5.join(folderPath, entry.name);
5373
5927
  if (entry.isDirectory()) {
5374
5928
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
5375
5929
  const subFiles = await this.discoverFiles(fullPath);
5376
5930
  files.push(...subFiles);
5377
5931
  }
5378
5932
  } else if (entry.isFile()) {
5379
- const ext = path4.extname(entry.name).toLowerCase();
5933
+ const ext = path5.extname(entry.name).toLowerCase();
5380
5934
  if (TRANSLATABLE_EXTS.includes(ext)) {
5381
5935
  if (!this.isTranslatedFile(entry.name)) {
5382
5936
  files.push(fullPath);
@@ -5881,7 +6435,7 @@ import { Command as Command34 } from "commander";
5881
6435
  import { Command as Command21 } from "commander";
5882
6436
 
5883
6437
  // src/services/utils.service.ts
5884
- import crypto from "crypto";
6438
+ import crypto2 from "crypto";
5885
6439
  import bcrypt from "bcryptjs";
5886
6440
  import { readFile } from "fs/promises";
5887
6441
  var UtilsService = class {
@@ -5905,7 +6459,7 @@ var UtilsService = class {
5905
6459
  if (charset.length === 0) {
5906
6460
  throw new Error("At least one character type must be enabled");
5907
6461
  }
5908
- const randomBytes = crypto.randomBytes(length);
6462
+ const randomBytes = crypto2.randomBytes(length);
5909
6463
  let password = "";
5910
6464
  for (let i = 0; i < length; i++) {
5911
6465
  const randomIndex = randomBytes[i] % charset.length;
@@ -5917,7 +6471,7 @@ var UtilsService = class {
5917
6471
  * Generate UUID v4
5918
6472
  */
5919
6473
  generateUuid(options) {
5920
- let uuid = crypto.randomUUID();
6474
+ let uuid = crypto2.randomUUID();
5921
6475
  if (options?.uppercase) {
5922
6476
  uuid = uuid.toUpperCase();
5923
6477
  }
@@ -5933,7 +6487,7 @@ var UtilsService = class {
5933
6487
  if (algorithm === "bcrypt") {
5934
6488
  throw new Error("Use hashBcrypt for bcrypt algorithm");
5935
6489
  }
5936
- const hash = crypto.createHash(algorithm);
6490
+ const hash = crypto2.createHash(algorithm);
5937
6491
  hash.update(input4);
5938
6492
  return hash.digest("hex");
5939
6493
  }
@@ -6021,7 +6575,7 @@ var UtilsService = class {
6021
6575
  const headerB64 = this.base64Encode(JSON.stringify(finalHeader), true);
6022
6576
  const payloadB64 = this.base64Encode(JSON.stringify(payload), true);
6023
6577
  const signatureInput = `${headerB64}.${payloadB64}`;
6024
- const signature = crypto.createHmac("sha256", secret).update(signatureInput).digest("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
6578
+ const signature = crypto2.createHmac("sha256", secret).update(signatureInput).digest("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
6025
6579
  return `${headerB64}.${payloadB64}.${signature}`;
6026
6580
  }
6027
6581
  /**
@@ -7928,8 +8482,8 @@ var HttpView = () => {
7928
8482
  import React37, { useState as useState24 } from "react";
7929
8483
  import { Box as Box27, Text as Text28, useInput as useInput22 } from "ink";
7930
8484
  import TextInput14 from "ink-text-input";
7931
- import * as fs9 from "fs";
7932
- import * as path5 from "path";
8485
+ import * as fs10 from "fs";
8486
+ import * as path6 from "path";
7933
8487
  var MarkdownView = () => {
7934
8488
  const [filePath, setFilePath] = useState24("");
7935
8489
  const [content, setContent] = useState24("");
@@ -7949,12 +8503,12 @@ var MarkdownView = () => {
7949
8503
  }
7950
8504
  try {
7951
8505
  setError("");
7952
- const resolvedPath = path5.resolve(filePath);
7953
- if (!fs9.existsSync(resolvedPath)) {
8506
+ const resolvedPath = path6.resolve(filePath);
8507
+ if (!fs10.existsSync(resolvedPath)) {
7954
8508
  setError(`File not found: ${resolvedPath}`);
7955
8509
  return;
7956
8510
  }
7957
- let fileContent = fs9.readFileSync(resolvedPath, "utf-8");
8511
+ let fileContent = fs10.readFileSync(resolvedPath, "utf-8");
7958
8512
  fileContent = fileContent.replace(/```mermaid\n([\s\S]*?)```/g, (match, code) => {
7959
8513
  return `
7960
8514
  \u{1F3A8} **Mermaid Diagram**
@@ -8235,8 +8789,8 @@ import { Command as Command35 } from "commander";
8235
8789
  import { checkbox as checkbox3, confirm as confirm5 } from "@inquirer/prompts";
8236
8790
 
8237
8791
  // src/services/deps.service.ts
8238
- import { promises as fs10 } from "fs";
8239
- import path6 from "path";
8792
+ import { promises as fs11 } from "fs";
8793
+ import path7 from "path";
8240
8794
  import { execSync } from "child_process";
8241
8795
  import pLimit2 from "p-limit";
8242
8796
  var DepsService = class {
@@ -8245,9 +8799,9 @@ var DepsService = class {
8245
8799
  * Đọc package.json từ thư mục
8246
8800
  */
8247
8801
  async readPackageJson(cwd) {
8248
- const pkgPath = path6.join(cwd, "package.json");
8802
+ const pkgPath = path7.join(cwd, "package.json");
8249
8803
  try {
8250
- const content = await fs10.readFile(pkgPath, "utf-8");
8804
+ const content = await fs11.readFile(pkgPath, "utf-8");
8251
8805
  return JSON.parse(content);
8252
8806
  } catch (error) {
8253
8807
  if (error.code === "ENOENT") {
@@ -8353,7 +8907,7 @@ var DepsService = class {
8353
8907
  ];
8354
8908
  for (const { file, pm } of lockFiles) {
8355
8909
  try {
8356
- await fs10.access(path6.join(cwd, file));
8910
+ await fs11.access(path7.join(cwd, file));
8357
8911
  return pm;
8358
8912
  } catch {
8359
8913
  }
@@ -8637,7 +9191,7 @@ import { Command as Command40 } from "commander";
8637
9191
  import { Command as Command37 } from "commander";
8638
9192
 
8639
9193
  // src/services/starter-kit.service.ts
8640
- import { promises as fs11 } from "fs";
9194
+ import { promises as fs12 } from "fs";
8641
9195
  import { join as join5 } from "path";
8642
9196
  import AdmZip from "adm-zip";
8643
9197
  var StarterKitService = class {
@@ -8686,16 +9240,16 @@ var StarterKitService = class {
8686
9240
  }
8687
9241
  if (onProgress) onProgress(30);
8688
9242
  const tmpDir = join5(process.env.TMPDIR || "/tmp", "jai1-kits");
8689
- await fs11.mkdir(tmpDir, { recursive: true });
9243
+ await fs12.mkdir(tmpDir, { recursive: true });
8690
9244
  const tmpFile = join5(tmpDir, `${slug}.zip`);
8691
9245
  const buffer = await response.arrayBuffer();
8692
- await fs11.writeFile(tmpFile, Buffer.from(buffer));
9246
+ await fs12.writeFile(tmpFile, Buffer.from(buffer));
8693
9247
  if (onProgress) onProgress(60);
8694
9248
  const zip = new AdmZip(tmpFile);
8695
- await fs11.mkdir(targetDir, { recursive: true });
9249
+ await fs12.mkdir(targetDir, { recursive: true });
8696
9250
  zip.extractAllTo(targetDir, true);
8697
9251
  if (onProgress) onProgress(100);
8698
- await fs11.unlink(tmpFile);
9252
+ await fs12.unlink(tmpFile);
8699
9253
  }
8700
9254
  };
8701
9255
 
@@ -8790,7 +9344,7 @@ Post-Init Commands:`);
8790
9344
 
8791
9345
  // src/commands/kit/create.ts
8792
9346
  import { Command as Command39 } from "commander";
8793
- import { promises as fs12 } from "fs";
9347
+ import { promises as fs13 } from "fs";
8794
9348
  import { join as join6 } from "path";
8795
9349
  import { select as select2, input, checkbox as checkbox4 } from "@inquirer/prompts";
8796
9350
  import { execa as execa2 } from "execa";
@@ -8856,7 +9410,7 @@ function createKitCreateCommand() {
8856
9410
  }
8857
9411
  const targetDir = directory || join6(process.cwd(), options.name || slug);
8858
9412
  try {
8859
- await fs12.access(targetDir);
9413
+ await fs13.access(targetDir);
8860
9414
  throw new Error(`Directory already exists: ${targetDir}`);
8861
9415
  } catch (error) {
8862
9416
  if (error.code !== "ENOENT") {
@@ -8974,7 +9528,7 @@ function createKitCreateCommand() {
8974
9528
  async function applyVariableSubstitution(dir, variables) {
8975
9529
  const files = await getAllFiles(dir);
8976
9530
  for (const file of files) {
8977
- let content = await fs12.readFile(file, "utf-8");
9531
+ let content = await fs13.readFile(file, "utf-8");
8978
9532
  let modified = false;
8979
9533
  for (const [key, value] of Object.entries(variables)) {
8980
9534
  const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
@@ -8984,13 +9538,13 @@ async function applyVariableSubstitution(dir, variables) {
8984
9538
  }
8985
9539
  }
8986
9540
  if (modified) {
8987
- await fs12.writeFile(file, content, "utf-8");
9541
+ await fs13.writeFile(file, content, "utf-8");
8988
9542
  }
8989
9543
  }
8990
9544
  }
8991
9545
  async function getAllFiles(dir) {
8992
9546
  const files = [];
8993
- const entries = await fs12.readdir(dir, { withFileTypes: true });
9547
+ const entries = await fs13.readdir(dir, { withFileTypes: true });
8994
9548
  for (const entry of entries) {
8995
9549
  const fullPath = join6(dir, entry.name);
8996
9550
  if (entry.isDirectory()) {
@@ -9077,7 +9631,7 @@ function createRulesListCommand() {
9077
9631
 
9078
9632
  // src/commands/rules/init.ts
9079
9633
  import { Command as Command42 } from "commander";
9080
- import { promises as fs13 } from "fs";
9634
+ import { promises as fs14 } from "fs";
9081
9635
  import { join as join7 } from "path";
9082
9636
  import { select as select3, confirm as confirm6 } from "@inquirer/prompts";
9083
9637
  function createRulesInitCommand() {
@@ -9163,7 +9717,7 @@ function createRulesInitCommand() {
9163
9717
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
9164
9718
  customContext: "09-custom.mdc"
9165
9719
  };
9166
- await fs13.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
9720
+ await fs14.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
9167
9721
  console.log("\u2713 Created jai1-rules.json");
9168
9722
  console.log("\n\u2705 Preset applied successfully!\n");
9169
9723
  console.log("Next steps:");
@@ -9174,10 +9728,10 @@ function createRulesInitCommand() {
9174
9728
  }
9175
9729
  async function applyCursorFormat(bundle) {
9176
9730
  const rulesDir = join7(process.cwd(), ".cursor", "rules");
9177
- await fs13.mkdir(rulesDir, { recursive: true });
9731
+ await fs14.mkdir(rulesDir, { recursive: true });
9178
9732
  for (const [filename, content] of Object.entries(bundle.files)) {
9179
9733
  const filePath = join7(rulesDir, filename);
9180
- await fs13.writeFile(filePath, content, "utf-8");
9734
+ await fs14.writeFile(filePath, content, "utf-8");
9181
9735
  console.log(`\u2713 Created .cursor/rules/${filename}`);
9182
9736
  }
9183
9737
  }
@@ -9204,13 +9758,13 @@ async function applyAgentsMdFormat(bundle) {
9204
9758
  }
9205
9759
  }
9206
9760
  const agentsMd = sections.join("\n");
9207
- await fs13.writeFile("AGENTS.md", agentsMd, "utf-8");
9761
+ await fs14.writeFile("AGENTS.md", agentsMd, "utf-8");
9208
9762
  console.log("\u2713 Created AGENTS.md");
9209
9763
  }
9210
9764
 
9211
9765
  // src/commands/rules/apply.ts
9212
9766
  import { Command as Command43 } from "commander";
9213
- import { promises as fs15 } from "fs";
9767
+ import { promises as fs16 } from "fs";
9214
9768
  import { join as join9 } from "path";
9215
9769
  import { select as select4, confirm as confirm7, checkbox as checkbox5 } from "@inquirer/prompts";
9216
9770
 
@@ -9571,7 +10125,7 @@ Follow all instructions and patterns defined in AGENTS.md above.
9571
10125
  };
9572
10126
 
9573
10127
  // src/services/backup.service.ts
9574
- import { promises as fs14 } from "fs";
10128
+ import { promises as fs15 } from "fs";
9575
10129
  import { join as join8, dirname } from "path";
9576
10130
  var BackupService = class {
9577
10131
  backupDir = ".jai1/backups";
@@ -9602,16 +10156,16 @@ var BackupService = class {
9602
10156
  await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
9603
10157
  hasContent = true;
9604
10158
  } else {
9605
- const stats = await fs14.stat(rulesPath);
10159
+ const stats = await fs15.stat(rulesPath);
9606
10160
  if (stats.isDirectory()) {
9607
- const files = await fs14.readdir(rulesPath);
10161
+ const files = await fs15.readdir(rulesPath);
9608
10162
  for (const file of files) {
9609
10163
  if (file.endsWith(format.fileExtension)) {
9610
10164
  const originalPath = join8(rulesPath, file);
9611
10165
  const relativePath = join8(format.rulesPath, file);
9612
10166
  const destPath = join8(backupPath, ideId, file);
9613
- await fs14.mkdir(dirname(destPath), { recursive: true });
9614
- await fs14.copyFile(originalPath, destPath);
10167
+ await fs15.mkdir(dirname(destPath), { recursive: true });
10168
+ await fs15.copyFile(originalPath, destPath);
9615
10169
  backedUpFiles.push({
9616
10170
  originalPath: relativePath,
9617
10171
  backupPath: join8(ideId, file),
@@ -9635,8 +10189,8 @@ var BackupService = class {
9635
10189
  ides,
9636
10190
  files: backedUpFiles
9637
10191
  };
9638
- await fs14.mkdir(backupPath, { recursive: true });
9639
- await fs14.writeFile(
10192
+ await fs15.mkdir(backupPath, { recursive: true });
10193
+ await fs15.writeFile(
9640
10194
  join8(backupPath, "metadata.json"),
9641
10195
  JSON.stringify(metadata, null, 2),
9642
10196
  "utf-8"
@@ -9654,8 +10208,8 @@ var BackupService = class {
9654
10208
  return;
9655
10209
  }
9656
10210
  const destPath = join8(backupPath, ideId, filename);
9657
- await fs14.mkdir(dirname(destPath), { recursive: true });
9658
- await fs14.copyFile(originalPath, destPath);
10211
+ await fs15.mkdir(dirname(destPath), { recursive: true });
10212
+ await fs15.copyFile(originalPath, destPath);
9659
10213
  backedUpFiles.push({
9660
10214
  originalPath: filename,
9661
10215
  backupPath: join8(ideId, filename),
@@ -9669,15 +10223,15 @@ var BackupService = class {
9669
10223
  */
9670
10224
  async restoreBackup(backupPath) {
9671
10225
  const metadataPath = join8(backupPath, "metadata.json");
9672
- const metadataContent = await fs14.readFile(metadataPath, "utf-8");
10226
+ const metadataContent = await fs15.readFile(metadataPath, "utf-8");
9673
10227
  const metadata = JSON.parse(metadataContent);
9674
10228
  console.log(`
9675
10229
  Restoring backup from ${metadata.timestamp}...`);
9676
10230
  for (const file of metadata.files) {
9677
10231
  const sourcePath = join8(backupPath, file.backupPath);
9678
10232
  const destPath = join8(process.cwd(), file.originalPath);
9679
- await fs14.mkdir(dirname(destPath), { recursive: true });
9680
- await fs14.copyFile(sourcePath, destPath);
10233
+ await fs15.mkdir(dirname(destPath), { recursive: true });
10234
+ await fs15.copyFile(sourcePath, destPath);
9681
10235
  console.log(`\u2713 Restored ${file.originalPath}`);
9682
10236
  }
9683
10237
  console.log("\n\u2705 Backup restored successfully!");
@@ -9692,13 +10246,13 @@ Restoring backup from ${metadata.timestamp}...`);
9692
10246
  if (!exists) {
9693
10247
  return [];
9694
10248
  }
9695
- const entries = await fs14.readdir(backupDirPath, { withFileTypes: true });
10249
+ const entries = await fs15.readdir(backupDirPath, { withFileTypes: true });
9696
10250
  const backups = [];
9697
10251
  for (const entry of entries) {
9698
10252
  if (entry.isDirectory()) {
9699
10253
  const metadataPath = join8(backupDirPath, entry.name, "metadata.json");
9700
10254
  try {
9701
- const metadataContent = await fs14.readFile(metadataPath, "utf-8");
10255
+ const metadataContent = await fs15.readFile(metadataPath, "utf-8");
9702
10256
  const metadata = JSON.parse(metadataContent);
9703
10257
  backups.push(metadata);
9704
10258
  } catch {
@@ -9745,9 +10299,9 @@ Restoring backup from ${metadata.timestamp}...`);
9745
10299
  /**
9746
10300
  * Check if a path exists
9747
10301
  */
9748
- async pathExists(path8) {
10302
+ async pathExists(path9) {
9749
10303
  try {
9750
- await fs14.access(path8);
10304
+ await fs15.access(path9);
9751
10305
  return true;
9752
10306
  } catch {
9753
10307
  return false;
@@ -9756,22 +10310,22 @@ Restoring backup from ${metadata.timestamp}...`);
9756
10310
  /**
9757
10311
  * Recursively delete a directory
9758
10312
  */
9759
- async deleteDirectory(path8) {
10313
+ async deleteDirectory(path9) {
9760
10314
  try {
9761
- const exists = await this.pathExists(path8);
10315
+ const exists = await this.pathExists(path9);
9762
10316
  if (!exists) {
9763
10317
  return;
9764
10318
  }
9765
- const entries = await fs14.readdir(path8, { withFileTypes: true });
10319
+ const entries = await fs15.readdir(path9, { withFileTypes: true });
9766
10320
  for (const entry of entries) {
9767
- const fullPath = join8(path8, entry.name);
10321
+ const fullPath = join8(path9, entry.name);
9768
10322
  if (entry.isDirectory()) {
9769
10323
  await this.deleteDirectory(fullPath);
9770
10324
  } else {
9771
- await fs14.unlink(fullPath);
10325
+ await fs15.unlink(fullPath);
9772
10326
  }
9773
10327
  }
9774
- await fs14.rmdir(path8);
10328
+ await fs15.rmdir(path9);
9775
10329
  } catch (error) {
9776
10330
  }
9777
10331
  }
@@ -9786,7 +10340,7 @@ Restoring backup from ${metadata.timestamp}...`);
9786
10340
  */
9787
10341
  async ensureBackupDir() {
9788
10342
  const backupDirPath = join8(process.cwd(), this.backupDir);
9789
- await fs14.mkdir(backupDirPath, { recursive: true });
10343
+ await fs15.mkdir(backupDirPath, { recursive: true });
9790
10344
  }
9791
10345
  };
9792
10346
 
@@ -9944,19 +10498,19 @@ function createRulesApplyCommand() {
9944
10498
  console.log("\n\u{1F4DD} Applying preset...\n");
9945
10499
  const rulePresetDir = join9(process.cwd(), ".jai1", "rule-preset");
9946
10500
  try {
9947
- await fs15.rm(rulePresetDir, { recursive: true, force: true });
10501
+ await fs16.rm(rulePresetDir, { recursive: true, force: true });
9948
10502
  } catch {
9949
10503
  }
9950
- await fs15.mkdir(rulePresetDir, { recursive: true });
9951
- await fs15.writeFile(
10504
+ await fs16.mkdir(rulePresetDir, { recursive: true });
10505
+ await fs16.writeFile(
9952
10506
  join9(rulePresetDir, "preset.json"),
9953
10507
  JSON.stringify(bundle.preset, null, 2),
9954
10508
  "utf-8"
9955
10509
  );
9956
10510
  for (const [filename, content] of Object.entries(bundle.files)) {
9957
10511
  const filePath = join9(rulePresetDir, filename);
9958
- await fs15.mkdir(join9(filePath, ".."), { recursive: true });
9959
- await fs15.writeFile(filePath, content, "utf-8");
10512
+ await fs16.mkdir(join9(filePath, ".."), { recursive: true });
10513
+ await fs16.writeFile(filePath, content, "utf-8");
9960
10514
  }
9961
10515
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
9962
10516
  const allGeneratedFiles = [];
@@ -9965,8 +10519,8 @@ function createRulesApplyCommand() {
9965
10519
  const files = generatorService.generateForIde(bundle, ideId);
9966
10520
  for (const file of files) {
9967
10521
  const fullPath = join9(process.cwd(), file.path);
9968
- await fs15.mkdir(join9(fullPath, ".."), { recursive: true });
9969
- await fs15.writeFile(fullPath, file.content, "utf-8");
10522
+ await fs16.mkdir(join9(fullPath, ".."), { recursive: true });
10523
+ await fs16.writeFile(fullPath, file.content, "utf-8");
9970
10524
  console.log(`\u2713 [${ideId}] ${file.path}`);
9971
10525
  allGeneratedFiles.push({
9972
10526
  ide: ideId,
@@ -9994,7 +10548,7 @@ function createRulesApplyCommand() {
9994
10548
  };
9995
10549
  try {
9996
10550
  const existingConfigPath = join9(process.cwd(), "jai1-rules.json");
9997
- const existingConfigContent = await fs15.readFile(existingConfigPath, "utf-8");
10551
+ const existingConfigContent = await fs16.readFile(existingConfigPath, "utf-8");
9998
10552
  const existingConfig = JSON.parse(existingConfigContent);
9999
10553
  if (existingConfig.backups && existingConfig.backups.length > 0) {
10000
10554
  projectConfig.backups = [
@@ -10005,7 +10559,7 @@ function createRulesApplyCommand() {
10005
10559
  }
10006
10560
  } catch {
10007
10561
  }
10008
- await fs15.writeFile(
10562
+ await fs16.writeFile(
10009
10563
  join9(process.cwd(), "jai1-rules.json"),
10010
10564
  JSON.stringify(projectConfig, null, 2),
10011
10565
  "utf-8"
@@ -10110,7 +10664,7 @@ function formatTimestamp(timestamp) {
10110
10664
 
10111
10665
  // src/commands/rules/sync.ts
10112
10666
  import { Command as Command45 } from "commander";
10113
- import { promises as fs16 } from "fs";
10667
+ import { promises as fs17 } from "fs";
10114
10668
  import { join as join11 } from "path";
10115
10669
  import { confirm as confirm9 } from "@inquirer/prompts";
10116
10670
  function createRulesSyncCommand() {
@@ -10118,7 +10672,7 @@ function createRulesSyncCommand() {
10118
10672
  const configPath = join11(process.cwd(), "jai1-rules.json");
10119
10673
  let projectConfig;
10120
10674
  try {
10121
- const configContent = await fs16.readFile(configPath, "utf-8");
10675
+ const configContent = await fs17.readFile(configPath, "utf-8");
10122
10676
  projectConfig = JSON.parse(configContent);
10123
10677
  } catch {
10124
10678
  throw new ValidationError(
@@ -10201,11 +10755,11 @@ Detected ${detected.length} active IDE(s):
10201
10755
  const rulePresetDir = join11(process.cwd(), ".jai1", "rule-preset");
10202
10756
  const presetExists = await checkPathExists(rulePresetDir);
10203
10757
  if (presetExists) {
10204
- const files = await fs16.readdir(rulePresetDir);
10758
+ const files = await fs17.readdir(rulePresetDir);
10205
10759
  for (const file of files) {
10206
10760
  if (file.endsWith(".mdc")) {
10207
10761
  const filePath = join11(rulePresetDir, file);
10208
- const content = await fs16.readFile(filePath, "utf-8");
10762
+ const content = await fs17.readFile(filePath, "utf-8");
10209
10763
  bundle.files[file] = content;
10210
10764
  }
10211
10765
  }
@@ -10225,8 +10779,8 @@ Detected ${detected.length} active IDE(s):
10225
10779
  const files = generatorService.generateForIde(bundle, ideId);
10226
10780
  for (const file of files) {
10227
10781
  const fullPath = join11(process.cwd(), file.path);
10228
- await fs16.mkdir(join11(fullPath, ".."), { recursive: true });
10229
- await fs16.writeFile(fullPath, file.content, "utf-8");
10782
+ await fs17.mkdir(join11(fullPath, ".."), { recursive: true });
10783
+ await fs17.writeFile(fullPath, file.content, "utf-8");
10230
10784
  }
10231
10785
  console.log(`\u2713 ${format.name} - ${files.length} files regenerated`);
10232
10786
  } catch (error) {
@@ -10235,7 +10789,7 @@ Detected ${detected.length} active IDE(s):
10235
10789
  }
10236
10790
  if (JSON.stringify(projectConfig.ides) !== JSON.stringify(idesToSync)) {
10237
10791
  projectConfig.ides = idesToSync;
10238
- await fs16.writeFile(
10792
+ await fs17.writeFile(
10239
10793
  join11(process.cwd(), "jai1-rules.json"),
10240
10794
  JSON.stringify(projectConfig, null, 2),
10241
10795
  "utf-8"
@@ -10251,7 +10805,7 @@ Detected ${detected.length} active IDE(s):
10251
10805
  }
10252
10806
  async function checkPathExists(absolutePath) {
10253
10807
  try {
10254
- await fs16.access(absolutePath);
10808
+ await fs17.access(absolutePath);
10255
10809
  return true;
10256
10810
  } catch {
10257
10811
  return false;
@@ -10260,14 +10814,14 @@ async function checkPathExists(absolutePath) {
10260
10814
 
10261
10815
  // src/commands/rules/info.ts
10262
10816
  import { Command as Command46 } from "commander";
10263
- import { promises as fs17 } from "fs";
10817
+ import { promises as fs18 } from "fs";
10264
10818
  import { join as join12 } from "path";
10265
10819
  function createRulesInfoCommand() {
10266
10820
  return new Command46("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
10267
10821
  const configPath = join12(process.cwd(), "jai1-rules.json");
10268
10822
  let projectConfig;
10269
10823
  try {
10270
- const configContent = await fs17.readFile(configPath, "utf-8");
10824
+ const configContent = await fs18.readFile(configPath, "utf-8");
10271
10825
  projectConfig = JSON.parse(configContent);
10272
10826
  } catch {
10273
10827
  throw new ValidationError(
@@ -10284,9 +10838,9 @@ function createRulesInfoCommand() {
10284
10838
  let presetMetadata = null;
10285
10839
  let presetFiles = [];
10286
10840
  try {
10287
- const presetContent = await fs17.readFile(presetJsonPath, "utf-8");
10841
+ const presetContent = await fs18.readFile(presetJsonPath, "utf-8");
10288
10842
  presetMetadata = JSON.parse(presetContent);
10289
- const files = await fs17.readdir(rulePresetDir);
10843
+ const files = await fs18.readdir(rulePresetDir);
10290
10844
  presetFiles = files.filter((f) => f.endsWith(".mdc"));
10291
10845
  } catch {
10292
10846
  }
@@ -10345,9 +10899,9 @@ Available Backups (${projectConfig.backups.length}):`);
10345
10899
  console.log(' \u2022 "jai1 rules apply" - Apply a different preset (replaces current)');
10346
10900
  });
10347
10901
  }
10348
- async function checkPathExists2(path8) {
10902
+ async function checkPathExists2(path9) {
10349
10903
  try {
10350
- await fs17.access(join12(process.cwd(), path8));
10904
+ await fs18.access(join12(process.cwd(), path9));
10351
10905
  return true;
10352
10906
  } catch {
10353
10907
  return false;
@@ -10778,8 +11332,8 @@ var RedmineApiClient = class {
10778
11332
  this.retryConfig = config.defaults.retry;
10779
11333
  this.concurrencyLimit = pLimit3(config.defaults.concurrency);
10780
11334
  }
10781
- async request(path8, options = {}) {
10782
- const url = `${this.baseUrl}${path8}`;
11335
+ async request(path9, options = {}) {
11336
+ const url = `${this.baseUrl}${path9}`;
10783
11337
  const headers = {
10784
11338
  "X-Redmine-API-Key": this.apiAccessToken,
10785
11339
  "Content-Type": "application/json",
@@ -10840,8 +11394,8 @@ var RedmineApiClient = class {
10840
11394
  if (include && include.length > 0) {
10841
11395
  params.append("include", include.join(","));
10842
11396
  }
10843
- const path8 = `/issues/${issueId}.json${params.toString() ? `?${params.toString()}` : ""}`;
10844
- return this.request(path8);
11397
+ const path9 = `/issues/${issueId}.json${params.toString() ? `?${params.toString()}` : ""}`;
11398
+ return this.request(path9);
10845
11399
  }
10846
11400
  async getIssues(projectId, options = {}) {
10847
11401
  const params = new URLSearchParams();
@@ -10861,8 +11415,8 @@ var RedmineApiClient = class {
10861
11415
  if (options.updatedSince) {
10862
11416
  params.append("updated_on", `>=${options.updatedSince}`);
10863
11417
  }
10864
- const path8 = `/issues.json?${params.toString()}`;
10865
- return this.request(path8);
11418
+ const path9 = `/issues.json?${params.toString()}`;
11419
+ return this.request(path9);
10866
11420
  }
10867
11421
  async getAllIssues(projectId, options = {}) {
10868
11422
  const pageSize = options.pageSize || 100;
@@ -11166,7 +11720,15 @@ function parseMarkdownContent(content) {
11166
11720
  rawContent: content
11167
11721
  };
11168
11722
  }
11169
- const { data: frontmatter, content: body } = matter3(content);
11723
+ let frontmatter = {};
11724
+ let body = content;
11725
+ try {
11726
+ const parsed = matter3(content);
11727
+ frontmatter = parsed.data;
11728
+ body = parsed.content;
11729
+ } catch (e) {
11730
+ console.warn(`\u26A0\uFE0F Invalid YAML frontmatter, skipping frontmatter parsing`);
11731
+ }
11170
11732
  const comments = extractCommentsSection(body);
11171
11733
  const mainContent = removeCommentsSection(body);
11172
11734
  return {
@@ -11538,7 +12100,7 @@ async function handleSyncProject(options) {
11538
12100
 
11539
12101
  // src/commands/framework/info.ts
11540
12102
  import { Command as Command53 } from "commander";
11541
- import { promises as fs18 } from "fs";
12103
+ import { promises as fs19 } from "fs";
11542
12104
  import { join as join14 } from "path";
11543
12105
  import { homedir as homedir5 } from "os";
11544
12106
  function createInfoCommand() {
@@ -11590,7 +12152,7 @@ function maskKey3(key) {
11590
12152
  async function getProjectStatus2() {
11591
12153
  const projectJai1 = join14(process.cwd(), ".jai1");
11592
12154
  try {
11593
- await fs18.access(projectJai1);
12155
+ await fs19.access(projectJai1);
11594
12156
  return { exists: true, version: "Synced" };
11595
12157
  } catch {
11596
12158
  return { exists: false };
@@ -11780,8 +12342,8 @@ function createClearBackupsCommand() {
11780
12342
  // src/commands/vscode/index.ts
11781
12343
  import { Command as Command56 } from "commander";
11782
12344
  import { checkbox as checkbox7, confirm as confirm14, select as select7 } from "@inquirer/prompts";
11783
- import fs19 from "fs/promises";
11784
- import path7 from "path";
12345
+ import fs20 from "fs/promises";
12346
+ import path8 from "path";
11785
12347
  import { existsSync as existsSync3 } from "fs";
11786
12348
  var PERFORMANCE_GROUPS2 = {
11787
12349
  telemetry: {
@@ -12001,8 +12563,8 @@ async function selectGroupsToApply2(action) {
12001
12563
  }
12002
12564
  }
12003
12565
  async function applyGroups2(groupKeys, action) {
12004
- const vscodeDir = path7.join(process.cwd(), ".vscode");
12005
- const settingsPath = path7.join(vscodeDir, "settings.json");
12566
+ const vscodeDir = path8.join(process.cwd(), ".vscode");
12567
+ const settingsPath = path8.join(vscodeDir, "settings.json");
12006
12568
  const invalidGroups = groupKeys.filter((key) => !PERFORMANCE_GROUPS2[key]);
12007
12569
  if (invalidGroups.length > 0) {
12008
12570
  console.log(`
@@ -12011,13 +12573,13 @@ async function applyGroups2(groupKeys, action) {
12011
12573
  return;
12012
12574
  }
12013
12575
  if (!existsSync3(vscodeDir)) {
12014
- await fs19.mkdir(vscodeDir, { recursive: true });
12576
+ await fs20.mkdir(vscodeDir, { recursive: true });
12015
12577
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
12016
12578
  }
12017
12579
  let currentSettings = {};
12018
12580
  if (existsSync3(settingsPath)) {
12019
12581
  try {
12020
- const content = await fs19.readFile(settingsPath, "utf-8");
12582
+ const content = await fs20.readFile(settingsPath, "utf-8");
12021
12583
  currentSettings = JSON.parse(content);
12022
12584
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
12023
12585
  } catch {
@@ -12057,14 +12619,14 @@ async function applyGroups2(groupKeys, action) {
12057
12619
  }
12058
12620
  }
12059
12621
  }
12060
- await fs19.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
12622
+ await fs20.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
12061
12623
  console.log(`
12062
12624
  \u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
12063
12625
  console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
12064
12626
  }
12065
12627
  async function resetSettings2(groupKeys) {
12066
- const vscodeDir = path7.join(process.cwd(), ".vscode");
12067
- const settingsPath = path7.join(vscodeDir, "settings.json");
12628
+ const vscodeDir = path8.join(process.cwd(), ".vscode");
12629
+ const settingsPath = path8.join(vscodeDir, "settings.json");
12068
12630
  if (!existsSync3(settingsPath)) {
12069
12631
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
12070
12632
  return;
@@ -12078,7 +12640,7 @@ async function resetSettings2(groupKeys) {
12078
12640
  return;
12079
12641
  }
12080
12642
  if (groupKeys.length === 0) {
12081
- await fs19.unlink(settingsPath);
12643
+ await fs20.unlink(settingsPath);
12082
12644
  console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
12083
12645
  } else {
12084
12646
  await applyGroups2(groupKeys, "disable");