catalyst-core 0.1.0-canary.9 → 0.2.0-beta.2

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 (148) hide show
  1. package/README.md +1 -1
  2. package/bin/catalyst.js +8 -1
  3. package/changelog.md +6 -0
  4. package/dist/native/androidProject/app/build.gradle.kts +34 -8
  5. package/dist/native/androidProject/app/src/main/AndroidManifest.xml +2 -1
  6. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +1 -1
  7. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +270 -78
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +34 -5
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +149 -1
  10. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeCameraManager.kt +337 -0
  11. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/OfflineCacheService.kt +374 -0
  12. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/TransitionManager.kt +202 -0
  13. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +156 -39
  14. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +123 -0
  15. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +244 -0
  16. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/HoldController.kt +70 -0
  17. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/TorchController.kt +57 -0
  18. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/VideoStreamState.kt +41 -0
  19. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/VideoStreamStateMachine.kt +40 -0
  20. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/ViewfinderMapper.kt +75 -0
  21. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/ZoomController.kt +156 -0
  22. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +7 -0
  23. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +6 -0
  24. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +253 -0
  25. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/FridaDetector.kt +16 -5
  26. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +42 -5
  27. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +7 -3
  28. package/dist/native/androidProject/app/src/main/res/drawable/debug_viewfinder_border.xml +8 -0
  29. package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +37 -0
  30. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CustomWebviewTest.kt +7 -2
  31. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +139 -0
  32. package/dist/native/androidSetup.js +1 -0
  33. package/dist/native/bridge/WebBridge.js +5 -5
  34. package/dist/native/bridge/constants/NativeInterfaces.js +9 -5
  35. package/dist/native/bridge/hooks/index.js +1 -0
  36. package/dist/native/bridge/hooks/useCamera.js +9 -0
  37. package/dist/native/bridge/hooks/useDataProtection.js +2 -0
  38. package/dist/native/bridge/hooks/useDeviceInfo.js +2 -0
  39. package/dist/native/bridge/hooks/useFilePicker.js +2 -0
  40. package/dist/native/bridge/hooks/useGoogleSignIn.js +1 -0
  41. package/dist/native/bridge/hooks/useHapticFeedback.js +1 -0
  42. package/dist/native/bridge/hooks/useIntent.js +1 -0
  43. package/dist/native/bridge/hooks/useNativeTransition.js +43 -0
  44. package/dist/native/bridge/hooks/useNetworkStatus.js +3 -0
  45. package/dist/native/bridge/hooks/useNotification.js +2 -0
  46. package/dist/native/bridge/hooks/useSafeArea.js +2 -0
  47. package/dist/native/bridge/hooks/useVideoStream.js +6 -0
  48. package/dist/native/bridge/hooks/utils.js +1 -0
  49. package/dist/native/bridge/hooks.js +1 -137
  50. package/dist/native/bridge/useBaseHook.js +12 -7
  51. package/dist/native/bridge/utils/NativeBridge.js +41 -6
  52. package/dist/native/buildAndroid/assets.js +52 -0
  53. package/dist/native/buildAndroid/build.js +19 -0
  54. package/dist/native/buildAndroid/config.js +1 -0
  55. package/dist/native/buildAndroid/index.js +1 -0
  56. package/dist/native/buildAppAndroid.js +2 -150
  57. package/dist/native/buildAppIos.js +2 -312
  58. package/dist/native/buildIos/assets.js +8 -0
  59. package/dist/native/buildIos/build.js +40 -0
  60. package/dist/native/buildIos/config.js +71 -0
  61. package/dist/native/buildIos/index.js +6 -0
  62. package/dist/native/buildIos/plugins.js +45 -0
  63. package/dist/native/internalPluginUtils.js +7 -0
  64. package/dist/native/iosnativeWebView/Package.resolved +36 -0
  65. package/dist/native/iosnativeWebView/Sources/Core/Camera/BarcodeDetector.swift +116 -0
  66. package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraPreviewView.swift +58 -0
  67. package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +308 -0
  68. package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +63 -0
  69. package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +194 -0
  70. package/dist/native/iosnativeWebView/Sources/Core/Camera/TorchController.swift +60 -0
  71. package/dist/native/iosnativeWebView/Sources/Core/Camera/VideoStreamState.swift +44 -0
  72. package/dist/native/iosnativeWebView/Sources/Core/Camera/VideoStreamStateMachine.swift +60 -0
  73. package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +130 -0
  74. package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +11 -1
  75. package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +2 -1
  76. package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +5 -0
  77. package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +6 -0
  78. package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +364 -0
  79. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +39 -4
  80. package/dist/native/iosnativeWebView/Sources/Core/Utils/OfflineCacheService.swift +683 -0
  81. package/dist/native/iosnativeWebView/Sources/Core/Utils/OfflineURLSchemeHandler.swift +94 -0
  82. package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +19 -6
  83. package/dist/native/iosnativeWebView/Sources/Core/Utils/SafeAreaUtils.swift +1 -0
  84. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +1 -0
  85. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +4 -1
  86. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +227 -4
  87. package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +169 -0
  88. package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +14 -0
  89. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +53 -8
  90. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +88 -4
  91. package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +30 -11
  92. package/dist/native/iosnativeWebView/iosnativeWebView/Info.plist +11 -17
  93. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +8 -2
  94. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BootTimingUtilityTests.swift +1 -1
  95. package/dist/native/iosnativeWebView/iosnativeWebViewTests/ConfigMappingTests.swift +4 -2
  96. package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +160 -0
  97. package/dist/native/plugin-bridge/PluginBridge.js +1 -0
  98. package/dist/native/pluginComposerAndroid.js +28 -0
  99. package/dist/native/pluginComposerIos.js +16 -0
  100. package/dist/native/renameAndroidProject.js +5 -5
  101. package/dist/native/setupEmulatorIos.js +6 -4
  102. package/dist/native/setupServer.js +2 -3
  103. package/dist/native/utils.js +4 -1
  104. package/dist/offline/catalyst-sw.js +1 -0
  105. package/dist/offline/registerServiceWorker.js +1 -0
  106. package/dist/otel.js +70 -2
  107. package/dist/router/ClientRouter.js +1 -1
  108. package/dist/scripts/build.js +1 -1
  109. package/dist/scripts/cleanCache.js +1 -0
  110. package/dist/scripts/devBuild.js +1 -1
  111. package/dist/scripts/devServe.js +1 -1
  112. package/dist/scripts/generateOfflineManifest.js +1 -0
  113. package/dist/scripts/loadEnvironmentVariables.js +1 -2
  114. package/dist/scripts/plugins.js +1 -0
  115. package/dist/scripts/registerAliases.js +6 -2
  116. package/dist/scripts/scriptUtils.js +2 -1
  117. package/dist/scripts/serve.js +2 -1
  118. package/dist/scripts/start.js +7 -2
  119. package/dist/server/expressServer.js +4 -8
  120. package/dist/server/renderer/document/Body.js +4 -4
  121. package/dist/server/renderer/document/Head.js +2 -2
  122. package/dist/server/renderer/extract.js +31 -27
  123. package/dist/server/renderer/handler.js +47 -24
  124. package/dist/server/renderer/index.js +1 -1
  125. package/dist/server/startServer.js +57 -7
  126. package/dist/server/utils/userAgentUtil.js +9 -3
  127. package/dist/server/utils/validator.js +1 -1
  128. package/dist/webpack/babel-plugins/remove-client.plugin.js +2 -2
  129. package/dist/webpack/babel-plugins/remove-ssr.plugin.js +2 -2
  130. package/dist/webpack/babel.config.client.js +1 -1
  131. package/dist/webpack/babel.config.ssr.js +1 -1
  132. package/dist/webpack/base.babel.js +7 -8
  133. package/dist/webpack/development.client.babel.js +7 -16
  134. package/dist/webpack/production.client.babel.js +1 -1
  135. package/dist/webpack/production.ssr.babel.js +5 -1
  136. package/mcp_v2/knowledge-base.json +206 -100
  137. package/mcp_v2/lib/helpers.js +42 -8
  138. package/mcp_v2/lib/seed.js +88 -0
  139. package/mcp_v2/mcp.js +150 -4
  140. package/mcp_v2/setup.js +30 -193
  141. package/mcp_v2/tools/build.js +1 -1
  142. package/mcp_v2/tools/config.js +16 -4
  143. package/mcp_v2/tools/conversion.js +11 -11
  144. package/mcp_v2/tools/github.js +1117 -0
  145. package/mcp_v2/tools/knowledge.js +16 -3
  146. package/mcp_v2/tools/sync.js +179 -10
  147. package/mcp_v2/tools/tasks.js +13 -4
  148. package/package.json +20 -8
@@ -38,7 +38,8 @@ function versionOlderThan(a, b) {
38
38
  }
39
39
 
40
40
  /**
41
- * Walk up the directory tree looking for a package.json that depends on Catalyst.
41
+ * Walk up the directory tree looking for a package.json that depends on Catalyst,
42
+ * or the catalyst-core package source itself when developing this repo.
42
43
  * Returns { dir, pkg, catalystPackageName, catalystVersion, installedVersion, versionMeta } or null.
43
44
  */
44
45
  function findCatalystRoot() {
@@ -54,6 +55,26 @@ function findCatalystRoot() {
54
55
  if (fs.existsSync(pkgPath)) {
55
56
  try {
56
57
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
58
+ if (pkg.name === "catalyst-core") {
59
+ const sourceVersion = pkg.version || null
60
+ return {
61
+ dir,
62
+ pkg,
63
+ catalystPackageName: "catalyst-core",
64
+ catalystVersion: sourceVersion,
65
+ version: sourceVersion,
66
+ installedVersion: sourceVersion,
67
+ notInstalled: false,
68
+ isSourcePackage: true,
69
+ versionMeta: {
70
+ declaredRef: sourceVersion,
71
+ isGithubRef: false,
72
+ installedVersion: sourceVersion,
73
+ parsed: parseVersion(sourceVersion),
74
+ },
75
+ }
76
+ }
77
+
57
78
  const deps = { ...pkg.dependencies, ...pkg.devDependencies }
58
79
  const declaredRef = deps["catalyst-core"]
59
80
  if (declaredRef) {
@@ -77,6 +98,7 @@ function findCatalystRoot() {
77
98
  pkg,
78
99
  catalystPackageName: "catalyst-core",
79
100
  catalystVersion: declaredRef, // what package.json says (may be github ref)
101
+ version: declaredRef,
80
102
  installedVersion, // what's actually in node_modules e.g. "0.0.3-canary.3"
81
103
  notInstalled: !installed,
82
104
  versionMeta: {
@@ -101,21 +123,33 @@ function findCatalystRoot() {
101
123
  * File-system helpers scoped to a project root.
102
124
  */
103
125
  function makeProjectHelpers(root) {
126
+ function safeProjectPath(rel) {
127
+ if (typeof rel !== "string" || path.isAbsolute(rel)) return null
128
+ const normalized = path.normalize(rel)
129
+ if (normalized === ".." || normalized.startsWith(`..${path.sep}`)) return null
130
+ return `${root}${path.sep}${normalized}`
131
+ }
132
+
104
133
  function fileExists(rel) {
105
- return fs.existsSync(path.join(root, rel))
134
+ const filePath = safeProjectPath(rel)
135
+ return Boolean(filePath && fs.existsSync(filePath))
106
136
  }
107
137
 
108
138
  function readJson(rel) {
139
+ const filePath = safeProjectPath(rel)
140
+ if (!filePath) return null
109
141
  try {
110
- return JSON.parse(fs.readFileSync(path.join(root, rel), "utf8"))
142
+ return JSON.parse(fs.readFileSync(filePath, "utf8"))
111
143
  } catch {
112
144
  return null
113
145
  }
114
146
  }
115
147
 
116
148
  function readText(rel) {
149
+ const filePath = safeProjectPath(rel)
150
+ if (!filePath) return null
117
151
  try {
118
- return fs.readFileSync(path.join(root, rel), "utf8")
152
+ return fs.readFileSync(filePath, "utf8")
119
153
  } catch {
120
154
  return null
121
155
  }
@@ -125,7 +159,6 @@ function makeProjectHelpers(root) {
125
159
  * Walk src/**\/*.{js,jsx,ts,tsx} and return relative paths that match pattern.
126
160
  */
127
161
  function grepSrc(pattern) {
128
- const re = new RegExp(pattern)
129
162
  const matches = []
130
163
  function walk(dir) {
131
164
  let entries
@@ -136,12 +169,13 @@ function makeProjectHelpers(root) {
136
169
  }
137
170
  for (const e of entries) {
138
171
  if (e.name === "node_modules" || e.name === ".git") continue
139
- const full = path.join(dir, e.name)
172
+ const full = `${dir}${path.sep}${e.name}`
140
173
  if (e.isDirectory()) {
141
174
  walk(full)
142
175
  } else if (/\.(js|jsx|ts|tsx)$/.test(e.name)) {
143
176
  try {
144
- if (re.test(fs.readFileSync(full, "utf8"))) {
177
+ const content = fs.readFileSync(full, "utf8")
178
+ if (pattern.test(content)) {
145
179
  matches.push(path.relative(root, full))
146
180
  }
147
181
  } catch {
@@ -150,7 +184,7 @@ function makeProjectHelpers(root) {
150
184
  }
151
185
  }
152
186
  }
153
- walk(path.join(root, "src"))
187
+ walk(`${root}${path.sep}src`)
154
188
  return matches
155
189
  }
156
190
 
@@ -0,0 +1,88 @@
1
+ "use strict"
2
+
3
+ const fs = require("fs")
4
+
5
+ /**
6
+ * Re-seed framework_knowledge (source='static') and known_errors from knowledge-base.json.
7
+ * Caller is responsible for rebuilding fk_fts afterwards (via knowledge.init).
8
+ *
9
+ * @param {import('better-sqlite3').Database} db
10
+ * @param {string} kbPath - absolute path to knowledge-base.json
11
+ * @param {?{ dir: string, pkg: { name?: string }, version: string }} projectInfo
12
+ * When provided, also upserts the project_context row. Pass null at runtime sync
13
+ * when project_context is already populated.
14
+ * @returns {{ knowledgeCount: number, errorCount: number }}
15
+ */
16
+ function seedKnowledgeBase(db, kbPath, projectInfo = null) {
17
+ const kb = JSON.parse(fs.readFileSync(kbPath, "utf8"))
18
+ if (!Array.isArray(kb)) throw new Error("knowledge-base.json must be a JSON array")
19
+
20
+ const deleteKnowledge = db.prepare(`DELETE FROM framework_knowledge WHERE source = 'static'`)
21
+ const deleteErrors = db.prepare(`DELETE FROM known_errors`)
22
+
23
+ const insertKnowledge = db.prepare(`
24
+ INSERT INTO framework_knowledge (section, title, content, layer, source, tags, github_files)
25
+ VALUES (@section, @title, @content, @layer, @source, @tags, @github_files)
26
+ `)
27
+
28
+ const insertError = db.prepare(`
29
+ INSERT INTO known_errors (symptom, cause, fix, layer, tags)
30
+ VALUES (@symptom, @cause, @fix, @layer, @tags)
31
+ `)
32
+
33
+ let knowledgeCount = 0
34
+ let errorCount = 0
35
+
36
+ const seedAll = db.transaction(() => {
37
+ deleteKnowledge.run()
38
+ deleteErrors.run()
39
+ for (const entry of kb) {
40
+ if (entry.section === "known_errors") {
41
+ // known_errors entries encode symptom/cause/fix in content as:
42
+ // "Symptom: ... Cause: ... Fix: ..."
43
+ const content = entry.content || ""
44
+ const symptomMatch = content.match(/Symptom:\s*([^.]+\.?)/i)
45
+ const causeMatch = content.match(/Cause:\s*([^.]+\.?)/i)
46
+ const fixMatch = content.match(/Fix:\s*([\s\S]+)/i)
47
+ insertError.run({
48
+ symptom: symptomMatch ? symptomMatch[1].trim() : entry.title,
49
+ cause: causeMatch ? causeMatch[1].trim() : "",
50
+ fix: fixMatch ? fixMatch[1].trim() : "",
51
+ layer: entry.layer,
52
+ tags: JSON.stringify(entry.tags || []),
53
+ })
54
+ errorCount++
55
+ } else {
56
+ insertKnowledge.run({
57
+ section: entry.section,
58
+ title: entry.title,
59
+ content: entry.content,
60
+ layer: entry.layer,
61
+ source: "static",
62
+ tags: JSON.stringify(entry.tags || []),
63
+ github_files: entry.github_files ? JSON.stringify(entry.github_files) : null,
64
+ })
65
+ knowledgeCount++
66
+ }
67
+ }
68
+ })
69
+
70
+ seedAll()
71
+
72
+ if (projectInfo) {
73
+ db.prepare(
74
+ `
75
+ INSERT OR REPLACE INTO project_context (id, repo_path, package_name, catalyst_version, detected_at)
76
+ VALUES (1, @repo_path, @package_name, @catalyst_version, datetime('now'))
77
+ `
78
+ ).run({
79
+ repo_path: projectInfo.dir,
80
+ package_name: projectInfo.pkg.name || "unknown",
81
+ catalyst_version: projectInfo.version,
82
+ })
83
+ }
84
+
85
+ return { knowledgeCount, errorCount }
86
+ }
87
+
88
+ module.exports = { seedKnowledgeBase }
package/mcp_v2/mcp.js CHANGED
@@ -25,6 +25,7 @@ const build = require("./tools/build")
25
25
  const tasks = require("./tools/tasks")
26
26
  const sync = require("./tools/sync")
27
27
  const knowledge = require("./tools/knowledge")
28
+ const github = require("./tools/github")
28
29
 
29
30
  const MCP_DIR = __dirname
30
31
  const DB_PATH = path.join(MCP_DIR, "context.db")
@@ -87,6 +88,7 @@ build.init(db)
87
88
  tasks.init(db)
88
89
  sync.init(db)
89
90
  knowledge.init(db)
91
+ github.init(projectInfo)
90
92
 
91
93
  // ── Intent classification (internal, not exposed as tool) ─────────────────────
92
94
 
@@ -97,6 +99,9 @@ const INTENT_PATTERNS = {
97
99
  guidance:
98
100
  /\b(what\s+is|what\s+are|how\s+does|how\s+do|explain|show\s+me|tell\s+me|hook|api|usage|example)\b/i,
99
101
  status: /status|done|complet|finish|check.*config|config.*check|what.*(left|remain|todo|next|pending)|how far|progress/i,
102
+ // feedback = wants to raise an issue or discussion on GitHub
103
+ feedback:
104
+ /\b(issue|bug\s+report|report\s+(a\s+)?bug|open\s+(an?\s+)?issue|create\s+(an?\s+)?issue|raise\s+(an?\s+)?issue|discussion|discuss|feature\s+request|proposal|suggest)\b/i,
100
105
  debug: /error|fail|broken|not work|crash|issue|bug|why|wrong/i,
101
106
  build: /build|compile|webpack|bundle|android|ios|platform/i,
102
107
  sync: /sync|update.*doc|fetch.*doc|latest.*doc/i,
@@ -110,6 +115,8 @@ const INTENT_NEXT_ACTION = {
110
115
  debug: "answer_only — provide debug guidance. Do NOT call create_task_plan.",
111
116
  build: "answer_only — explain build flow. Do NOT call create_task_plan.",
112
117
  sync: "answer_only — sync complete. Do NOT call create_task_plan.",
118
+ feedback:
119
+ "answer_only — run the GitHub issue workflow and show the created issue URL or markdown fallback. Do NOT call create_task_plan.",
113
120
  unknown: "answer_only — unclear intent. Return what you found. Do NOT call create_task_plan.",
114
121
  }
115
122
 
@@ -399,9 +406,9 @@ const TOOLS = [
399
406
  },
400
407
  },
401
408
  {
402
- name: "sync_catalyst_docs",
409
+ name: "sync_knowledge_base",
403
410
  description:
404
- "Use when the developer asks: 'sync docs', 'update framework knowledge', 'fetch latest catalyst docs'. Intent: sync. Fetches changelog and template diffs, updates the KB. Maintenance only — no task planning needed after.",
411
+ "Use when the developer asks: 'sync docs', 'update framework knowledge', 'fetch latest catalyst docs'. Intent: sync. Pulls the latest knowledge-base.json from tata1mg/catalyst-core@main and re-seeds the KB. Maintenance only — no task planning needed after.",
405
412
  inputSchema: {
406
413
  type: "object",
407
414
  properties: {
@@ -412,6 +419,144 @@ const TOOLS = [
412
419
  },
413
420
  },
414
421
  },
422
+ {
423
+ name: "create_github_issue",
424
+ description:
425
+ "Use when the developer wants to create, raise, report, preview, or publish a GitHub issue for catalyst-core, or after the LLM discovers that a problem is likely caused by catalyst-core framework behavior and the developer agrees to raise an issue. This single tool first asks the developer to select labels when labels are omitted. After labels are supplied, it gathers project context, renders the issue using the selected/suggested template, searches duplicates using duplicate_search_query when provided, supports preview with dry_run:true, publishes only when dry_run:false is explicitly passed, and falls back to a markdown draft on auth/network/API failure. Default to dry_run:true first; if label_selection_required is returned, ask the developer to select labels before collecting/rendering the rest of the issue. Intent: feedback.",
426
+ inputSchema: {
427
+ type: "object",
428
+ properties: {
429
+ dry_run: {
430
+ type: "boolean",
431
+ description:
432
+ "Defaults to true. When true, returns rendered preview, labels, duplicate candidates, and gathered context without publishing. Pass false only after explicit developer approval.",
433
+ },
434
+ project_path: {
435
+ type: "string",
436
+ description: "Path to the catalyst app root. Defaults to detected project root.",
437
+ },
438
+ title: {
439
+ type: "string",
440
+ description:
441
+ "Short, descriptive issue title. E.g. 'RouterDataProvider fails on nested dynamic routes'.",
442
+ },
443
+ body: {
444
+ type: "string",
445
+ description:
446
+ "Full issue description. Plain text is upgraded into the catalyst-core issue style; already structured markdown is preserved.",
447
+ },
448
+ summary: {
449
+ type: "string",
450
+ description: "Structured issue summary if body is not already composed.",
451
+ },
452
+ issue_template: {
453
+ type: "string",
454
+ enum: ["bug", "enhancement", "documentation", "dependencies", "question"],
455
+ description:
456
+ "Optional template to force. Use bug for broken behavior, enhancement for feature requests, documentation for docs/examples, dependencies for package updates, question for clarification.",
457
+ },
458
+ current_behavior: {
459
+ type: "string",
460
+ description: "What happens today.",
461
+ },
462
+ steps_to_reproduce: {
463
+ type: "array",
464
+ items: { type: "string" },
465
+ description: "Steps to reproduce the issue.",
466
+ },
467
+ expected_behavior: {
468
+ type: "string",
469
+ description: "What should happen.",
470
+ },
471
+ actual_behavior: {
472
+ type: "string",
473
+ description: "What actually happens.",
474
+ },
475
+ error_logs: {
476
+ type: "string",
477
+ description: "Error logs or stack traces.",
478
+ },
479
+ preflight_checklist: {
480
+ type: "array",
481
+ items: { type: "string" },
482
+ description:
483
+ "Optional checklist items for bug reports. Defaults to searched issues, single issue, and included repro/environment/logs.",
484
+ },
485
+ what_i_tried: {
486
+ type: "string",
487
+ description: "Troubleshooting already attempted.",
488
+ },
489
+ additional_information: {
490
+ type: "string",
491
+ description: "Additional context that does not fit the primary sections.",
492
+ },
493
+ related_issues: {
494
+ type: "array",
495
+ items: { type: "string" },
496
+ description: "Related GitHub issues or links.",
497
+ },
498
+ root_cause: {
499
+ type: "string",
500
+ description: "Technical notes or suspected root cause.",
501
+ },
502
+ proposed_fix: {
503
+ type: "string",
504
+ description: "Concrete suggested fix.",
505
+ },
506
+ optional_followups: {
507
+ type: "array",
508
+ items: { type: "string" },
509
+ description: "Follow-up cleanup, docs, or test suggestions.",
510
+ },
511
+ environment: {
512
+ type: "object",
513
+ description:
514
+ "Environment details such as platform, OS, browser, device, Node/npm versions.",
515
+ },
516
+ labels: {
517
+ type: "array",
518
+ items: { type: "string" },
519
+ description:
520
+ "Required after the initial label-selection step. Valid catalyst-core labels: bug, dependencies, documentation, duplicate, enhancement, good first issue, help wanted, invalid, question, wontfix. If omitted, the tool returns label_selection_required and does not gather context, render a full preview, search duplicates, or publish.",
521
+ },
522
+ images: {
523
+ type: "array",
524
+ items: { type: "object" },
525
+ description:
526
+ "Optional image URLs or local image paths. URLs are embedded directly; local paths are preserved for fallback/manual upload.",
527
+ },
528
+ duplicate_search_query: {
529
+ type: "string",
530
+ description:
531
+ "Optional focused search query for duplicate detection, supplied by the LLM from the issue context. Use the same query reviewed in the dry_run:true preview.",
532
+ },
533
+ duplicate_review_confirmed: {
534
+ type: "boolean",
535
+ description:
536
+ "Set true only after a dry_run:true preview or blocked publish returned duplicate candidates, those candidates were shown to the developer, and the developer explicitly confirmed this issue is distinct and should still be published.",
537
+ },
538
+ sensitive_data_confirmed: {
539
+ type: "boolean",
540
+ description:
541
+ "Set true only after a dry_run:true preview or blocked publish returned sensitive_data_review.required_before_publish=true, the config/sensitive-looking data warning and rendered issue preview were shown to the developer, and the developer explicitly confirmed this data may be posted to GitHub.",
542
+ },
543
+ duplicate_review_note: {
544
+ type: "string",
545
+ description:
546
+ "Optional short note explaining why the issue is not a duplicate, after the developer confirms publishing.",
547
+ },
548
+ files: {
549
+ type: "array",
550
+ items: { type: "string" },
551
+ description: "Optional explicit project files to include as context snippets.",
552
+ },
553
+ _query: {
554
+ type: "string",
555
+ description: "Original user query for intent classification.",
556
+ },
557
+ },
558
+ },
559
+ },
415
560
  ]
416
561
 
417
562
  // ── Tool handler dispatch ─────────────────────────────────────────────────────
@@ -427,8 +572,9 @@ const TOOL_HANDLERS = {
427
572
  update_task_step: tasks.handle_update_task_step,
428
573
  get_active_task: tasks.handle_get_active_task,
429
574
  close_task_plan: tasks.handle_close_task_plan,
430
- sync_catalyst_docs: sync.handle_sync_catalyst_docs,
575
+ sync_knowledge_base: sync.handle_sync_knowledge_base,
431
576
  query_knowledge: knowledge.handle_query_knowledge,
577
+ create_github_issue: github.handle_create_github_issue,
432
578
  }
433
579
 
434
580
  // ── MCP JSON-RPC over stdio ───────────────────────────────────────────────────
@@ -496,7 +642,7 @@ rl.on("line", (line) => {
496
642
  text: JSON.stringify({
497
643
  error: "out_of_scope",
498
644
  message:
499
- "This query is outside Catalyst MCP scope. MCP handles: conversion tracking, debugging, config validation, build flow, architecture, task planning, and doc sync.",
645
+ "This query is outside Catalyst MCP scope. MCP handles: conversion tracking, debugging, config validation, build flow, architecture, task planning, doc sync, and GitHub issue creation.",
500
646
  }),
501
647
  },
502
648
  ],
package/mcp_v2/setup.js CHANGED
@@ -8,38 +8,22 @@
8
8
  * 2. Create context.db, run schema.sql
9
9
  * 3. Seed framework_knowledge from knowledge-base.json (static rows)
10
10
  * 4. Seed known_errors from knowledge-base.json known_errors section
11
- * 5. Run sync_catalyst_docs once (dynamic sitemap rows)
11
+ * 5. Build FTS index over static rows
12
+ * 6. Sync knowledge-base.json from tata1mg/catalyst-core@main (best-effort; warns but does not fail)
12
13
  */
13
14
 
14
15
  const fs = require("fs")
15
16
  const path = require("path")
16
17
  const Database = require("better-sqlite3")
17
- const https = require("https")
18
- const crypto = require("crypto")
18
+ const sync = require("./tools/sync")
19
+ const knowledge = require("./tools/knowledge")
20
+ const { seedKnowledgeBase } = require("./lib/seed")
21
+ const { findCatalystRoot } = require("./lib/helpers")
19
22
 
20
23
  const MCP_DIR = __dirname
21
24
  const DB_PATH = path.join(MCP_DIR, "context.db")
22
25
  const SCHEMA_PATH = path.join(MCP_DIR, "schema.sql")
23
26
  const KB_PATH = path.join(MCP_DIR, "knowledge-base.json")
24
- const SITEMAP_URL = "https://catalyst.1mg.com/public_docs/sitemap.xml"
25
-
26
- // ── 1. Find & validate catalyst project ──────────────────────────────────────
27
-
28
- function findCatalystRoot() {
29
- let dir = process.cwd()
30
- while (dir !== path.parse(dir).root) {
31
- const pkgPath = path.join(dir, "package.json")
32
- if (fs.existsSync(pkgPath)) {
33
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
34
- const deps = { ...pkg.dependencies, ...pkg.devDependencies }
35
- if (deps["catalyst-core"]) {
36
- return { dir, pkg, catalystPackageName: "catalyst-core", version: deps["catalyst-core"] }
37
- }
38
- }
39
- dir = path.dirname(dir)
40
- }
41
- return null
42
- }
43
27
 
44
28
  // ── 2. DB init ────────────────────────────────────────────────────────────────
45
29
 
@@ -52,180 +36,15 @@ function initDb() {
52
36
 
53
37
  // ── 3 & 4. Seed from knowledge-base.json ─────────────────────────────────────
54
38
 
55
- function seedKnowledgeBase(db, projectInfo) {
56
- const kb = JSON.parse(fs.readFileSync(KB_PATH, "utf8"))
57
-
58
- // DELETE existing static rows so re-runs reflect updated knowledge-base.json
59
- db.prepare(`DELETE FROM framework_knowledge WHERE source = 'static'`).run()
60
- db.prepare(`DELETE FROM known_errors`).run()
61
-
62
- const insertKnowledge = db.prepare(`
63
- INSERT INTO framework_knowledge (section, title, content, layer, source, tags, github_files)
64
- VALUES (@section, @title, @content, @layer, @source, @tags, @github_files)
65
- `)
66
-
67
- const insertError = db.prepare(`
68
- INSERT INTO known_errors (symptom, cause, fix, layer, tags)
69
- VALUES (@symptom, @cause, @fix, @layer, @tags)
70
- `)
71
-
72
- let knowledgeCount = 0
73
- let errorCount = 0
74
-
75
- const seedAll = db.transaction(() => {
76
- for (const entry of kb) {
77
- if (entry.section === "known_errors") {
78
- // known_errors entries encode symptom/cause/fix in content as:
79
- // "Symptom: ... Cause: ... Fix: ..."
80
- const content = entry.content || ""
81
- const symptomMatch = content.match(/Symptom:\s*([^.]+\.?)/i)
82
- const causeMatch = content.match(/Cause:\s*([^.]+\.?)/i)
83
- const fixMatch = content.match(/Fix:\s*([\s\S]+)/i)
84
- insertError.run({
85
- symptom: symptomMatch ? symptomMatch[1].trim() : entry.title,
86
- cause: causeMatch ? causeMatch[1].trim() : "",
87
- fix: fixMatch ? fixMatch[1].trim() : "",
88
- layer: entry.layer,
89
- tags: JSON.stringify(entry.tags || []),
90
- })
91
- errorCount++
92
- } else {
93
- insertKnowledge.run({
94
- section: entry.section,
95
- title: entry.title,
96
- content: entry.content,
97
- layer: entry.layer,
98
- source: "static",
99
- tags: JSON.stringify(entry.tags || []),
100
- github_files: entry.github_files ? JSON.stringify(entry.github_files) : null,
101
- })
102
- knowledgeCount++
103
- }
104
- }
105
- })
106
-
107
- seedAll()
39
+ function seedFromLocalKb(db, projectInfo) {
40
+ const { knowledgeCount, errorCount } = seedKnowledgeBase(db, KB_PATH, projectInfo)
108
41
  console.log(` ✓ Seeded ${knowledgeCount} knowledge entries`)
109
42
  console.log(` ✓ Seeded ${errorCount} known_errors entries`)
110
-
111
- // Store project context
112
- db.prepare(
113
- `
114
- INSERT OR REPLACE INTO project_context (id, repo_path, package_name, catalyst_version, detected_at)
115
- VALUES (1, @repo_path, @package_name, @catalyst_version, datetime('now'))
116
- `
117
- ).run({
118
- repo_path: projectInfo.dir,
119
- package_name: projectInfo.pkg.name || "unknown",
120
- catalyst_version: projectInfo.version,
121
- })
122
43
  console.log(
123
44
  ` ✓ Project context stored (${projectInfo.pkg.name}, ${projectInfo.catalystPackageName}@${projectInfo.version})`
124
45
  )
125
46
  }
126
47
 
127
- // ── 5. sync_catalyst_docs (initial run) ───────────────────────────────────────
128
-
129
- function fetchUrl(url) {
130
- return new Promise((resolve, reject) => {
131
- const mod = url.startsWith("https") ? https : require("http")
132
- mod.get(url, (res) => {
133
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
134
- return fetchUrl(res.headers.location).then(resolve).catch(reject)
135
- }
136
- let data = ""
137
- res.on("data", (chunk) => (data += chunk))
138
- res.on("end", () => resolve(data))
139
- }).on("error", reject)
140
- })
141
- }
142
-
143
- function parseSitemapUrls(xml) {
144
- const matches = xml.match(/<loc>(.*?)<\/loc>/g) || []
145
- return matches.map((m) => m.replace(/<\/?loc>/g, "").trim())
146
- }
147
-
148
- function stripHtml(html) {
149
- // Very simple strip — good enough for catalyst docs
150
- return html
151
- .replace(/<script[\s\S]*?<\/script>/gi, "")
152
- .replace(/<style[\s\S]*?<\/style>/gi, "")
153
- .replace(/<[^>]+>/g, " ")
154
- .replace(/\s+/g, " ")
155
- .trim()
156
- }
157
-
158
- function contentHash(text) {
159
- return crypto.createHash("sha256").update(text).digest("hex").slice(0, 16)
160
- }
161
-
162
- // Kept for manual/live sync flows; setup does not invoke it by default.
163
- // eslint-disable-next-line no-unused-vars
164
- async function syncCatalystDocs(db) {
165
- console.log("\n Fetching sitemap...")
166
- let xml
167
- try {
168
- xml = await fetchUrl(SITEMAP_URL)
169
- } catch (e) {
170
- console.warn(` ⚠ Could not fetch sitemap (${e.message}). Skipping live docs sync.`)
171
- console.warn(" You can run sync later via the sync_catalyst_docs MCP tool.")
172
- return
173
- }
174
-
175
- const urls = parseSitemapUrls(xml)
176
- console.log(` Found ${urls.length} URLs in sitemap`)
177
-
178
- const insertKnowledge = db.prepare(`
179
- INSERT INTO framework_knowledge (section, title, content, layer, source, tags, url)
180
- VALUES (@section, @title, @content, @layer, 'sitemap', '[]', @url)
181
- `)
182
-
183
- const insertSnapshot = db.prepare(`
184
- INSERT INTO doc_snapshots (url, content_hash, slot) VALUES (@url, @hash, 'curr')
185
- `)
186
-
187
- const insertLink = db.prepare(`
188
- INSERT INTO linkage_map (url, knowledge_id) VALUES (@url, @knowledge_id)
189
- `)
190
-
191
- let synced = 0
192
- let failed = 0
193
-
194
- for (const url of urls) {
195
- try {
196
- const html = await fetchUrl(url)
197
- const text = stripHtml(html)
198
- const hash = contentHash(text)
199
-
200
- // Extract a title from <title> tag if present
201
- const titleMatch = html.match(/<title[^>]*>(.*?)<\/title>/i)
202
- const title = titleMatch ? titleMatch[1].trim() : url.split("/").pop() || url
203
-
204
- // Truncate content to 2000 chars to keep DB lean
205
- const content = text.slice(0, 2000)
206
-
207
- db.transaction(() => {
208
- const row = insertKnowledge.run({
209
- section: "sitemap",
210
- title,
211
- content,
212
- layer: "Component",
213
- url,
214
- })
215
- insertSnapshot.run({ url, hash })
216
- insertLink.run({ url, knowledge_id: row.lastInsertRowid })
217
- })()
218
-
219
- synced++
220
- process.stdout.write(`\r Synced ${synced}/${urls.length} pages...`)
221
- } catch (e) {
222
- failed++
223
- }
224
- }
225
-
226
- console.log(`\n ✓ Synced ${synced} pages (${failed} failed)`)
227
- }
228
-
229
48
  // ── Main ──────────────────────────────────────────────────────────────────────
230
49
 
231
50
  async function main() {
@@ -248,11 +67,29 @@ async function main() {
248
67
 
249
68
  // 3 & 4. Seed static knowledge
250
69
  console.log("\nSeeding knowledge-base.json...")
251
- seedKnowledgeBase(db, projectInfo)
70
+ seedFromLocalKb(db, projectInfo)
252
71
 
253
- // 5. Initial sitemap sync — disabled (slow, use sync_catalyst_docs MCP tool manually)
254
- // console.log('\nRunning initial sync_catalyst_docs...');
255
- // await syncCatalystDocs(db);
72
+ // 5. Build FTS index from the just-seeded static rows.
73
+ // DROP first so re-runs of setup don't leave orphan rowids pointing at deleted rows.
74
+ db.exec(`DROP TABLE IF EXISTS fk_fts`)
75
+ knowledge.init(db)
76
+
77
+ // 6. Initial sync — pulls knowledge-base.json from tata1mg/catalyst-core@main.
78
+ // Failures are warnings, not setup-blockers (network may be unavailable at install time).
79
+ console.log("\nSyncing knowledge-base.json from tata1mg/catalyst-core@main...")
80
+ sync.init(db)
81
+ try {
82
+ const result = await sync.handle_sync_knowledge_base({})
83
+ if (result.error) {
84
+ console.warn(` ⚠ ${result.error}`)
85
+ console.warn(" Run sync_knowledge_base later via your MCP client to retry.")
86
+ } else {
87
+ console.log(` ✓ ${result.message}`)
88
+ }
89
+ } catch (e) {
90
+ console.warn(` ⚠ Sync threw: ${e.message}`)
91
+ console.warn(" Run sync_knowledge_base later via your MCP client to retry.")
92
+ }
256
93
 
257
94
  db.close()
258
95
  console.log("\n✓ Setup complete. MCP is ready.\n")