catalyst-core-internal 0.1.4 → 0.1.6
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.
- package/bin/catalyst.js +1 -8
- package/changelog.md +21 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +1 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +1 -12
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +3 -18
- package/dist/native/buildAppAndroid.js +2 -2
- package/dist/native/buildAppIos.js +17 -10
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +2 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +0 -6
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +0 -4
- package/mcp_v2/conversion-tasks.json +326 -0
- package/mcp_v2/knowledge-base.json +1068 -0
- package/mcp_v2/lib/helpers.js +170 -0
- package/mcp_v2/mcp.js +563 -0
- package/mcp_v2/package.json +13 -0
- package/mcp_v2/schema.sql +88 -0
- package/mcp_v2/setup.js +282 -0
- package/mcp_v2/tools/build.js +686 -0
- package/mcp_v2/tools/config.js +453 -0
- package/mcp_v2/tools/conversion.js +799 -0
- package/mcp_v2/tools/debug.js +113 -0
- package/mcp_v2/tools/knowledge.js +219 -0
- package/mcp_v2/tools/sync.js +23 -0
- package/mcp_v2/tools/tasks.js +945 -0
- package/package.json +7 -15
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +0 -7
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +0 -6
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +0 -253
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +0 -139
- package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +0 -43
- package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +0 -28
- package/dist/native/internal-plugins/device-info-plugin/manifest.json +0 -19
- package/dist/native/internalPluginUtils.js +0 -1
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +0 -5
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +0 -6
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +0 -364
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +0 -14
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +0 -160
- package/dist/native/plugin-bridge/PluginBridge.js +0 -1
- package/dist/native/pluginComposerAndroid.js +0 -9
- package/dist/native/pluginComposerIos.js +0 -7
- package/dist/scripts/plugins.js +0 -1
- package/license +0 -10
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
let _db
|
|
4
|
+
|
|
5
|
+
function init(db) {
|
|
6
|
+
_db = db
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function handle_debug_issue({ symptom, layer } = {}) {
|
|
10
|
+
const NOISE = new Set([
|
|
11
|
+
"the",
|
|
12
|
+
"a",
|
|
13
|
+
"an",
|
|
14
|
+
"is",
|
|
15
|
+
"are",
|
|
16
|
+
"was",
|
|
17
|
+
"not",
|
|
18
|
+
"does",
|
|
19
|
+
"do",
|
|
20
|
+
"on",
|
|
21
|
+
"in",
|
|
22
|
+
"at",
|
|
23
|
+
"to",
|
|
24
|
+
"for",
|
|
25
|
+
"of",
|
|
26
|
+
"and",
|
|
27
|
+
"or",
|
|
28
|
+
"with",
|
|
29
|
+
"my",
|
|
30
|
+
"i",
|
|
31
|
+
"its",
|
|
32
|
+
"it",
|
|
33
|
+
"this",
|
|
34
|
+
"that",
|
|
35
|
+
"when",
|
|
36
|
+
"why",
|
|
37
|
+
"how",
|
|
38
|
+
"what",
|
|
39
|
+
"where",
|
|
40
|
+
"after",
|
|
41
|
+
"before",
|
|
42
|
+
"but",
|
|
43
|
+
"can",
|
|
44
|
+
"cant",
|
|
45
|
+
"cannot",
|
|
46
|
+
"will",
|
|
47
|
+
"wont",
|
|
48
|
+
"still",
|
|
49
|
+
"always",
|
|
50
|
+
"never",
|
|
51
|
+
"app",
|
|
52
|
+
"apps",
|
|
53
|
+
])
|
|
54
|
+
const tokens = symptom
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.replace(/[^a-z0-9\s]/g, " ")
|
|
57
|
+
.split(/\s+/)
|
|
58
|
+
.filter((t) => t.length > 2 && !NOISE.has(t))
|
|
59
|
+
|
|
60
|
+
function scoreRow(text) {
|
|
61
|
+
const lower = text.toLowerCase()
|
|
62
|
+
let score = 0
|
|
63
|
+
for (const token of tokens) {
|
|
64
|
+
if (lower.includes(token)) score++
|
|
65
|
+
}
|
|
66
|
+
return score
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const allErrors = layer
|
|
70
|
+
? _db.prepare(`SELECT * FROM known_errors WHERE layer = ?`).all(layer)
|
|
71
|
+
: _db.prepare(`SELECT * FROM known_errors`).all()
|
|
72
|
+
|
|
73
|
+
const scored = allErrors
|
|
74
|
+
.map((r) => ({ ...r, _score: scoreRow(r.symptom + " " + r.cause + " " + r.tags) }))
|
|
75
|
+
.sort((a, b) => b._score - a._score)
|
|
76
|
+
|
|
77
|
+
const matched = scored.filter((r) => r._score > 0).slice(0, 5)
|
|
78
|
+
const fallback = matched.length === 0 ? scored.slice(0, 3) : []
|
|
79
|
+
|
|
80
|
+
const matchedLayers = [...new Set(matched.map((r) => r.layer))]
|
|
81
|
+
const knowledgeLayers = layer ? [layer] : matchedLayers.length ? matchedLayers : null
|
|
82
|
+
|
|
83
|
+
let knowledge = []
|
|
84
|
+
if (knowledgeLayers && knowledgeLayers.length) {
|
|
85
|
+
const placeholders = knowledgeLayers.map(() => "?").join(",")
|
|
86
|
+
knowledge = _db
|
|
87
|
+
.prepare(
|
|
88
|
+
`
|
|
89
|
+
SELECT title, content, layer FROM framework_knowledge
|
|
90
|
+
WHERE source = 'static' AND layer IN (${placeholders})
|
|
91
|
+
ORDER BY layer, id
|
|
92
|
+
LIMIT 8
|
|
93
|
+
`
|
|
94
|
+
)
|
|
95
|
+
.all(...knowledgeLayers)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = { symptom, tokens_used: tokens, layer: layer || "auto" }
|
|
99
|
+
|
|
100
|
+
if (matched.length > 0) {
|
|
101
|
+
result.matched_errors = matched.map(({ _score, ...r }) => ({ ...r, match_score: _score }))
|
|
102
|
+
result.relevant_knowledge = knowledge
|
|
103
|
+
} else {
|
|
104
|
+
result.matched_errors = []
|
|
105
|
+
result.fallback_errors = fallback.map(({ ...r }) => r)
|
|
106
|
+
result.relevant_knowledge = knowledge
|
|
107
|
+
result.note = `No strong keyword matches for "${symptom}". Showing top known_errors entries as fallback. Try rephrasing with more specific terms (e.g. "android build sdkPath", "localhost blocked", "clearWebData cache").`
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return result
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = { init, handle_debug_issue }
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
const https = require("https")
|
|
4
|
+
const { findCatalystRoot } = require("../lib/helpers")
|
|
5
|
+
|
|
6
|
+
let _db
|
|
7
|
+
|
|
8
|
+
// Keywords that signal the LLM wants a complete/live list rather than a concept explanation.
|
|
9
|
+
// When a matched KB entry has github_files + query matches these, we auto-fetch from GitHub.
|
|
10
|
+
const LIST_INTENT_PATTERNS =
|
|
11
|
+
/\b(all|list|available|new|latest|complete|full|enumerate|what hooks|any hooks|what.*available|show.*all)\b/i
|
|
12
|
+
|
|
13
|
+
function init(db) {
|
|
14
|
+
_db = db
|
|
15
|
+
|
|
16
|
+
// Migrate: add github_files column if missing (existing DBs)
|
|
17
|
+
const cols = _db
|
|
18
|
+
.prepare(`PRAGMA table_info(framework_knowledge)`)
|
|
19
|
+
.all()
|
|
20
|
+
.map((c) => c.name)
|
|
21
|
+
if (!cols.includes("github_files")) {
|
|
22
|
+
_db.exec(`ALTER TABLE framework_knowledge ADD COLUMN github_files TEXT`)
|
|
23
|
+
}
|
|
24
|
+
// Drop always_fetch_github if it exists (replaced by intent detection)
|
|
25
|
+
// SQLite can't DROP COLUMN before 3.35 — just leave it, it's ignored
|
|
26
|
+
|
|
27
|
+
// Create FTS5 virtual table if not exists (standalone, not external-content)
|
|
28
|
+
_db.exec(`
|
|
29
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS fk_fts USING fts5(
|
|
30
|
+
title, content, tags, section,
|
|
31
|
+
tokenize='unicode61 remove_diacritics 1'
|
|
32
|
+
);
|
|
33
|
+
`)
|
|
34
|
+
|
|
35
|
+
// Populate FTS index if empty
|
|
36
|
+
const count = _db.prepare(`SELECT count(*) as c FROM fk_fts`).get().c
|
|
37
|
+
if (count === 0) {
|
|
38
|
+
_db.exec(
|
|
39
|
+
`INSERT INTO fk_fts(rowid, title, content, tags, section) SELECT id, title, content, COALESCE(tags,''), section FROM framework_knowledge`
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function fetchRaw(url) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
https
|
|
47
|
+
.get(url, { headers: { "User-Agent": "catalyst-mcp/1.0" } }, (res) => {
|
|
48
|
+
if (res.statusCode === 404) return resolve(null)
|
|
49
|
+
if (res.statusCode !== 200) return reject(new Error(`HTTP ${res.statusCode}`))
|
|
50
|
+
const chunks = []
|
|
51
|
+
res.on("data", (c) => chunks.push(c))
|
|
52
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")))
|
|
53
|
+
})
|
|
54
|
+
.on("error", reject)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Fetch first available file — used for KB-miss GitHub fallback
|
|
59
|
+
async function fetchFromGithub(files, installedVersion) {
|
|
60
|
+
if (!files || files.length === 0) return null
|
|
61
|
+
|
|
62
|
+
const tag = installedVersion || "main"
|
|
63
|
+
const base = `https://raw.githubusercontent.com/tata1mg/catalyst-core/${tag}`
|
|
64
|
+
|
|
65
|
+
for (const file of files) {
|
|
66
|
+
try {
|
|
67
|
+
const content = await fetchRaw(`${base}/${file}`)
|
|
68
|
+
if (content) return { file, tag, content: content.slice(0, 8000) }
|
|
69
|
+
} catch (_err) {
|
|
70
|
+
// Ignore missing/unreachable files and continue to the next candidate.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Retry with main if versioned tag failed
|
|
75
|
+
if (tag !== "main") {
|
|
76
|
+
for (const file of files) {
|
|
77
|
+
try {
|
|
78
|
+
const content = await fetchRaw(
|
|
79
|
+
`https://raw.githubusercontent.com/tata1mg/catalyst-core/main/${file}`
|
|
80
|
+
)
|
|
81
|
+
if (content) return { file, tag: "main", content: content.slice(0, 8000) }
|
|
82
|
+
} catch (_err) {
|
|
83
|
+
// Ignore missing/unreachable files and continue to the next candidate.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Build FTS5 query string from keywords array — each term ORed, quoted for safety
|
|
92
|
+
function buildFtsQuery(keywords) {
|
|
93
|
+
return keywords.map((k) => `"${k.replace(/"/g, "")}"`).join(" OR ")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function stripGithubFiles(rows) {
|
|
97
|
+
return rows.map((row) => {
|
|
98
|
+
const sanitizedRow = { ...row }
|
|
99
|
+
delete sanitizedRow.github_files
|
|
100
|
+
return sanitizedRow
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Narrow down which github_file to fetch based on query keywords.
|
|
105
|
+
// Avoids fetching all files when query is clearly about one specific hook/file.
|
|
106
|
+
function narrowGithubFile(files, query) {
|
|
107
|
+
if (!files || files.length <= 1) return files
|
|
108
|
+
const q = query.toLowerCase()
|
|
109
|
+
// If query mentions a specific file hint, prefer that file
|
|
110
|
+
for (const file of files) {
|
|
111
|
+
const basename = file.split("/").pop().replace(/\.js$/, "").toLowerCase()
|
|
112
|
+
if (q.includes(basename)) return [file]
|
|
113
|
+
}
|
|
114
|
+
// Default: return all (caller fetches first available)
|
|
115
|
+
return files
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function handle_query_knowledge({ query, keywords, section, github_files } = {}) {
|
|
119
|
+
if (!query) return { error: "query is required." }
|
|
120
|
+
|
|
121
|
+
// ── FTS5 search ────────────────────────────────────────────────────────────
|
|
122
|
+
const searchTerms = keywords && keywords.length > 0 ? keywords : [query]
|
|
123
|
+
const ftsQuery = buildFtsQuery(searchTerms)
|
|
124
|
+
|
|
125
|
+
let rows = []
|
|
126
|
+
try {
|
|
127
|
+
// Join back to framework_knowledge to get github_files
|
|
128
|
+
const sql = section
|
|
129
|
+
? `SELECT fk.title, fk.content, fk.tags, fk.section, fk.github_files
|
|
130
|
+
FROM fk_fts
|
|
131
|
+
JOIN framework_knowledge fk ON fk_fts.rowid = fk.id
|
|
132
|
+
WHERE fk_fts MATCH ? AND fk_fts.section = ? ORDER BY rank LIMIT 6`
|
|
133
|
+
: `SELECT fk.title, fk.content, fk.tags, fk.section, fk.github_files
|
|
134
|
+
FROM fk_fts
|
|
135
|
+
JOIN framework_knowledge fk ON fk_fts.rowid = fk.id
|
|
136
|
+
WHERE fk_fts MATCH ? ORDER BY rank LIMIT 6`
|
|
137
|
+
rows = section ? _db.prepare(sql).all(ftsQuery, section) : _db.prepare(sql).all(ftsQuery)
|
|
138
|
+
} catch (_err) {
|
|
139
|
+
// FTS syntax error — fall through to github
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (rows.length > 0) {
|
|
143
|
+
// Find first matched entry that has github_files attached
|
|
144
|
+
const entryWithFiles = rows.find((r) => r.github_files)
|
|
145
|
+
const parsedFiles = entryWithFiles ? JSON.parse(entryWithFiles.github_files) : null
|
|
146
|
+
|
|
147
|
+
// ── Intent detection: does the query want a live/complete list? ──────────
|
|
148
|
+
const isListIntent = LIST_INTENT_PATTERNS.test(query)
|
|
149
|
+
|
|
150
|
+
if (isListIntent && parsedFiles) {
|
|
151
|
+
let installedVersion = null
|
|
152
|
+
try {
|
|
153
|
+
const root = findCatalystRoot()
|
|
154
|
+
installedVersion = root ? root.installedVersion : null
|
|
155
|
+
} catch (_err) {
|
|
156
|
+
// Installed version lookup is best effort.
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Narrow to the most relevant file based on query keywords
|
|
160
|
+
const targetFiles = narrowGithubFile(parsedFiles, query)
|
|
161
|
+
const github = await fetchFromGithub(targetFiles, installedVersion)
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
query,
|
|
165
|
+
source: "knowledge_base+github",
|
|
166
|
+
results: stripGithubFiles(rows),
|
|
167
|
+
github_file: github ? github.file : null,
|
|
168
|
+
github_tag: github ? github.tag : null,
|
|
169
|
+
github_content: github ? github.content : null,
|
|
170
|
+
installed_version: installedVersion,
|
|
171
|
+
note: `${rows.length} KB entries matched. List intent detected — fetched latest source${github ? ` (${github.file}@${github.tag})` : " (GitHub fetch failed)"}.`,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ── Standard KB hit: return results + hint about live source files ───────
|
|
176
|
+
return {
|
|
177
|
+
query,
|
|
178
|
+
source: "knowledge_base",
|
|
179
|
+
results: stripGithubFiles(rows),
|
|
180
|
+
live_source_files: parsedFiles || null, // LLM can re-query with these if it needs fresher data
|
|
181
|
+
note: `${rows.length} entries matched from local knowledge base.${parsedFiles ? " Pass live_source_files as github_files in a follow-up query_knowledge call if you need the latest source." : ""}`,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ── GitHub fallback (KB miss) ──────────────────────────────────────────────
|
|
186
|
+
let installedVersion = null
|
|
187
|
+
try {
|
|
188
|
+
const root = findCatalystRoot()
|
|
189
|
+
installedVersion = root ? root.installedVersion : null
|
|
190
|
+
} catch (_err) {
|
|
191
|
+
// Installed version lookup is best effort.
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const github = await fetchFromGithub(github_files || [], installedVersion)
|
|
195
|
+
|
|
196
|
+
if (github) {
|
|
197
|
+
return {
|
|
198
|
+
query,
|
|
199
|
+
source: "github",
|
|
200
|
+
github_file: github.file,
|
|
201
|
+
github_tag: github.tag,
|
|
202
|
+
installed_version: installedVersion,
|
|
203
|
+
content: github.content,
|
|
204
|
+
note: `No KB match. Fetched source file '${github.file}' from catalyst-core@${github.tag} on GitHub.`,
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ── Hard miss ──────────────────────────────────────────────────────────────
|
|
209
|
+
return {
|
|
210
|
+
query,
|
|
211
|
+
source: "none",
|
|
212
|
+
results: [],
|
|
213
|
+
llm_instruction:
|
|
214
|
+
"No match found in the knowledge base and no source file was provided for GitHub fallback. DO NOT search node_modules, dist, or the filesystem. Tell the user this topic is not fully covered in the knowledge base yet, and suggest they check the catalyst-core source directly at node_modules/catalyst-core/src/.",
|
|
215
|
+
note: `No KB match for "${query}". If you know the relevant source file path, retry with github_files: ["src/path/to/file.js"].`,
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
module.exports = { init, handle_query_knowledge }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
let _db
|
|
4
|
+
|
|
5
|
+
function init(db) {
|
|
6
|
+
_db = db
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function handle_sync_catalyst_docs({ force = false } = {}) {
|
|
10
|
+
const snapshot_count = _db.prepare(`SELECT COUNT(*) as c FROM doc_snapshots`).get()
|
|
11
|
+
const knowledge_count = _db
|
|
12
|
+
.prepare(`SELECT COUNT(*) as c FROM framework_knowledge WHERE source = 'sitemap'`)
|
|
13
|
+
.get()
|
|
14
|
+
return {
|
|
15
|
+
_phase: 6,
|
|
16
|
+
force,
|
|
17
|
+
existing_snapshots: snapshot_count.c,
|
|
18
|
+
sitemap_knowledge_rows: knowledge_count.c,
|
|
19
|
+
message: "Live sync not yet implemented (Phase 6). Run setup.js for initial sync.",
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { init, handle_sync_catalyst_docs }
|