fchek 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +64 -0
  2. package/bin/fchek.js +107 -0
  3. package/lib/api.js +110 -0
  4. package/lib/audit.js +211 -0
  5. package/lib/bench.js +248 -0
  6. package/lib/config.js +191 -0
  7. package/lib/context.js +356 -0
  8. package/lib/convention.js +526 -0
  9. package/lib/coverage.js +604 -0
  10. package/lib/db.js +135 -0
  11. package/lib/deps-check.js +264 -0
  12. package/lib/deps.js +374 -0
  13. package/lib/docker.js +84 -0
  14. package/lib/doctor.js +149 -0
  15. package/lib/dom.js +226 -0
  16. package/lib/fuzz.js +470 -0
  17. package/lib/git.js +290 -0
  18. package/lib/goto.js +544 -0
  19. package/lib/launch.js +182 -0
  20. package/lib/lint.js +624 -0
  21. package/lib/new_features.test.js +181 -0
  22. package/lib/output.js +46 -0
  23. package/lib/port.js +173 -0
  24. package/lib/process.js +228 -0
  25. package/lib/profile.js +453 -0
  26. package/lib/python.js +41 -0
  27. package/lib/race.js +186 -0
  28. package/lib/registry.js +179 -0
  29. package/lib/repl.js +135 -0
  30. package/lib/run.js +403 -0
  31. package/lib/screenshot.js +152 -0
  32. package/lib/secrets.js +257 -0
  33. package/lib/state.js +219 -0
  34. package/lib/test.js +471 -0
  35. package/lib/vuln.js +253 -0
  36. package/lib/watch.js +240 -0
  37. package/lib/winlog.js +123 -0
  38. package/package.json +27 -0
  39. package/skills/ACTIVATE.md +274 -0
  40. package/skills/README.md +163 -0
  41. package/skills/agent.md +444 -0
  42. package/skills/api.md +47 -0
  43. package/skills/bench.md +117 -0
  44. package/skills/context.md +116 -0
  45. package/skills/convention.md +143 -0
  46. package/skills/coverage.md +99 -0
  47. package/skills/csharp.md +97 -0
  48. package/skills/db.md +66 -0
  49. package/skills/deps-check.md +135 -0
  50. package/skills/deps.md +143 -0
  51. package/skills/docker.md +61 -0
  52. package/skills/dom.md +56 -0
  53. package/skills/fuzz.md +167 -0
  54. package/skills/goto.md +111 -0
  55. package/skills/lint.md +123 -0
  56. package/skills/port.md +57 -0
  57. package/skills/profile.md +91 -0
  58. package/skills/race.md +117 -0
  59. package/skills/repl.md +81 -0
  60. package/skills/rules.md +318 -0
  61. package/skills/run.md +135 -0
  62. package/skills/secrets.md +170 -0
  63. package/skills/security.md +360 -0
  64. package/skills/state.md +261 -0
  65. package/skills/vuln.md +57 -0
  66. package/skills/windows.md +320 -0
package/lib/db.js ADDED
@@ -0,0 +1,135 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { output, ok, fail } = require('./output');
6
+
7
+ const HELP = `
8
+ fchek db schema <db_path>
9
+ fchek db query <db_path> <sql_query>
10
+
11
+ Inspect and query SQLite databases.
12
+ schema: List all tables, columns, types, primary keys, and indexes.
13
+ query: Execute a SELECT or action query and return results.
14
+ `.trim();
15
+
16
+ // Try to load Node.js 22 built-in sqlite module
17
+ let DatabaseSync;
18
+ try {
19
+ DatabaseSync = require('node:sqlite').DatabaseSync;
20
+ } catch (err) {
21
+ // node:sqlite not available (<22.5.0 or older Node)
22
+ }
23
+
24
+ function runSqliteCommand(dbPath, sql) {
25
+ // If sqlite3 CLI tool is installed, we can fall back to it
26
+ const { execSync } = require('child_process');
27
+ try {
28
+ const jsonOut = execSync(`sqlite3 "${dbPath}" -json "${sql}"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
29
+ return JSON.parse(jsonOut.trim() || '[]');
30
+ } catch (err) {
31
+ throw new Error('SQLite query failed. Make sure sqlite3 CLI tool is in your PATH or use Node.js >= 22.5');
32
+ }
33
+ }
34
+
35
+ async function run(args) {
36
+ if (args.includes('--help') || args.includes('-h')) {
37
+ console.log(HELP);
38
+ return;
39
+ }
40
+
41
+ const action = args[0];
42
+ const dbPath = args[1];
43
+
44
+ if (!action || !['schema', 'query'].includes(action)) {
45
+ output(fail('Invalid action. Use "schema" or "query"', 'db'));
46
+ return;
47
+ }
48
+
49
+ if (!dbPath) {
50
+ output(fail('Please specify a database file path', 'db'));
51
+ return;
52
+ }
53
+
54
+ const resolvedPath = path.resolve(dbPath);
55
+ if (!fs.existsSync(resolvedPath)) {
56
+ output(fail(`Database file does not exist: ${dbPath}`, 'db'));
57
+ return;
58
+ }
59
+
60
+ if (action === 'schema') {
61
+ try {
62
+ let tables = [];
63
+ if (DatabaseSync) {
64
+ const db = new DatabaseSync(resolvedPath);
65
+ const tablesRows = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
66
+
67
+ for (const tRow of tablesRows) {
68
+ const tableName = tRow.name;
69
+ const colRows = db.prepare(`PRAGMA table_info("${tableName}")`).all();
70
+ const indexRows = db.prepare(`PRAGMA index_list("${tableName}")`).all();
71
+
72
+ const columns = colRows.map(c => ({
73
+ name: c.name,
74
+ type: c.type,
75
+ notnull: c.notnull === 1,
76
+ dflt_value: c.dflt_value,
77
+ pk: c.pk === 1
78
+ }));
79
+
80
+ tables.push({
81
+ name: tableName,
82
+ columns,
83
+ indexes: indexRows.map(i => i.name)
84
+ });
85
+ }
86
+ db.close();
87
+ } else {
88
+ // Fallback using sqlite3 CLI
89
+ const tablesRows = runSqliteCommand(resolvedPath, "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'");
90
+ for (const tRow of tablesRows) {
91
+ const tableName = tRow.name;
92
+ const colRows = runSqliteCommand(resolvedPath, `PRAGMA table_info("${tableName}")`);
93
+ const indexRows = runSqliteCommand(resolvedPath, `PRAGMA index_list("${tableName}")`);
94
+ tables.push({
95
+ name: tableName,
96
+ columns: colRows.map(c => ({
97
+ name: c.name,
98
+ type: c.type,
99
+ notnull: c.notnull === 1,
100
+ dflt_value: c.dflt_value,
101
+ pk: c.pk === 1
102
+ })),
103
+ indexes: indexRows.map(i => i.name)
104
+ });
105
+ }
106
+ }
107
+ output(ok({ database: dbPath, tables }, 'db'));
108
+ } catch (err) {
109
+ output(fail(`Failed to fetch schema: ${err.message}`, 'db'));
110
+ }
111
+ } else if (action === 'query') {
112
+ const querySql = args.slice(2).join(' ');
113
+ if (!querySql) {
114
+ output(fail('Please specify a SQL query', 'db'));
115
+ return;
116
+ }
117
+
118
+ try {
119
+ let rows = [];
120
+ if (DatabaseSync) {
121
+ const db = new DatabaseSync(resolvedPath);
122
+ const stmt = db.prepare(querySql);
123
+ rows = stmt.all();
124
+ db.close();
125
+ } else {
126
+ rows = runSqliteCommand(resolvedPath, querySql);
127
+ }
128
+ output(ok({ query: querySql, count: rows.length, rows }, 'db'));
129
+ } catch (err) {
130
+ output(fail(`Query execution failed: ${err.message}`, 'db'));
131
+ }
132
+ }
133
+ }
134
+
135
+ module.exports = { run };
@@ -0,0 +1,264 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * deps-check.js — check library API freshness
5
+ *
6
+ * Fetches current package metadata from npm / PyPI / crates.io
7
+ * and compares against what's installed in the project.
8
+ * Closes the "hallucinated API" problem.
9
+ */
10
+
11
+ const { spawnSync, execSync } = require('child_process');
12
+ const https = require('https');
13
+ const path = require('path');
14
+ const fs = require('fs');
15
+ const { output, ok, fail } = require('./output');
16
+
17
+ const HELP = `
18
+ fchek deps-check <lib> [--lang=auto] [--show-changelog]
19
+
20
+ Check if a library's API is current. Fetches live data from package registries.
21
+ Tells you: installed version, latest version, deprecated APIs, what to use instead.
22
+
23
+ Supported registries:
24
+ npm → https://registry.npmjs.org
25
+ PyPI → https://pypi.org/pypi
26
+ crates.io → https://crates.io/api/v1/crates
27
+
28
+ Options:
29
+ --lang=node|python|rust Force language (default: auto-detect from project)
30
+ --show-changelog Include changelog URL if available
31
+
32
+ Examples:
33
+ fchek deps-check express
34
+ fchek deps-check requests --lang=python
35
+ fchek deps-check serde --lang=rust
36
+ `.trim();
37
+
38
+ function fetchJson(url, timeoutMs = 15000) {
39
+ return new Promise((resolve, reject) => {
40
+ const req = https.get(url, {
41
+ headers: { 'User-Agent': 'fchek/1.0', Accept: 'application/json' },
42
+ timeout: timeoutMs,
43
+ }, (res) => {
44
+ let data = '';
45
+ res.on('data', c => { data += c; });
46
+ res.on('end', () => {
47
+ try { resolve(JSON.parse(data)); }
48
+ catch (e) { reject(new Error(`JSON parse failed: ${e.message}`)); }
49
+ });
50
+ });
51
+ req.on('error', (err) => {
52
+ // Provide actionable error messages for common network issues
53
+ if (err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET') {
54
+ reject(new Error(`No connection to registry. Check your internet connection.`));
55
+ } else if (err.code === 'ENOTFOUND') {
56
+ reject(new Error(`DNS lookup failed for registry. Check internet connection or DNS settings.`));
57
+ } else if (err.code === 'ETIMEDOUT') {
58
+ reject(new Error(`Registry request timed out. Check internet connection.`));
59
+ } else {
60
+ reject(err);
61
+ }
62
+ });
63
+ req.on('timeout', () => {
64
+ req.destroy();
65
+ reject(new Error(`Registry request timed out after ${timeoutMs}ms. Check internet connection.`));
66
+ });
67
+ });
68
+ }
69
+
70
+ /** Offline fallback: read installed version from local project files only */
71
+ function offlineFallback(lib, lang, cwd) {
72
+ let installed = null;
73
+
74
+ if (lang === 'node') {
75
+ installed = installedNode(lib, cwd);
76
+ // Also try reading from package.json directly
77
+ if (!installed) {
78
+ try {
79
+ const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
80
+ installed = pkg.dependencies?.[lib] || pkg.devDependencies?.[lib] || null;
81
+ } catch {}
82
+ }
83
+ } else if (lang === 'python') {
84
+ installed = installedPython(lib);
85
+ } else if (lang === 'rust') {
86
+ // Read from Cargo.toml directly
87
+ try {
88
+ const cargo = fs.readFileSync(path.join(cwd, 'Cargo.toml'), 'utf8');
89
+ const m = cargo.match(new RegExp(`${lib}\\s*=\\s*["{]([0-9][^"{}\\n]+)`, 'i'));
90
+ installed = m?.[1]?.trim() || installedRust(lib, cwd);
91
+ } catch { installed = installedRust(lib, cwd); }
92
+ }
93
+
94
+ return {
95
+ lib,
96
+ registry: lang === 'node' ? 'npm' : lang === 'python' ? 'pypi' : 'crates.io',
97
+ offline: true,
98
+ offline_note: 'No internet connection. Showing installed version only. Cannot check for latest/deprecated.',
99
+ installed_version: installed,
100
+ latest_version: null,
101
+ up_to_date: null,
102
+ };
103
+ }
104
+
105
+ function detectLang(cwd) {
106
+ if (fs.existsSync(path.join(cwd, 'package.json'))) return 'node';
107
+ if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) return 'rust';
108
+ if (fs.existsSync(path.join(cwd, 'requirements.txt'))
109
+ || fs.existsSync(path.join(cwd, 'pyproject.toml'))
110
+ || fs.existsSync(path.join(cwd, 'setup.py'))) return 'python';
111
+ return null;
112
+ }
113
+
114
+ function installedNode(lib, cwd) {
115
+ try {
116
+ const p = path.join(cwd, 'node_modules', lib, 'package.json');
117
+ if (fs.existsSync(p)) return JSON.parse(fs.readFileSync(p, 'utf8')).version || null;
118
+ } catch {}
119
+ try {
120
+ const r = spawnSync('npm', ['list', lib, '--depth=0', '--json'], { encoding: 'utf8', cwd, timeout: 8000 });
121
+ return JSON.parse(r.stdout || '{}')?.dependencies?.[lib]?.version || null;
122
+ } catch { return null; }
123
+ }
124
+
125
+ function installedPython(lib) {
126
+ try {
127
+ return execSync(`python3 -c "import importlib.metadata;print(importlib.metadata.version('${lib}'))"`,
128
+ { encoding: 'utf8', timeout: 5000 }).trim() || null;
129
+ } catch {
130
+ try {
131
+ return execSync(`pip show ${lib}`, { encoding: 'utf8', timeout: 5000 })
132
+ .match(/Version:\s*(.+)/)?.[1]?.trim() || null;
133
+ } catch { return null; }
134
+ }
135
+ }
136
+
137
+ function installedRust(lib, cwd) {
138
+ try {
139
+ const r = spawnSync('cargo', ['metadata', '--no-deps', '--format-version=1'],
140
+ { encoding: 'utf8', cwd, timeout: 15000 });
141
+ const data = JSON.parse(r.stdout || '{}');
142
+ for (const pkg of data.packages || []) {
143
+ if (pkg.name === lib) return pkg.version;
144
+ for (const dep of pkg.dependencies || []) {
145
+ if (dep.name === lib) return dep.req?.replace(/[^0-9.]/, '') || null;
146
+ }
147
+ }
148
+ } catch {}
149
+ return null;
150
+ }
151
+
152
+ async function checkNpm(lib, showChangelog) {
153
+ const cwd = process.cwd();
154
+ const installed = installedNode(lib, cwd);
155
+ const data = await fetchJson(`https://registry.npmjs.org/${encodeURIComponent(lib)}`);
156
+ const latest = data['dist-tags']?.latest || null;
157
+ const latestMeta = data.versions?.[latest] || {};
158
+
159
+ const deprecated = [];
160
+ for (const [ver, meta] of Object.entries(data.versions || {})) {
161
+ if (meta.deprecated) deprecated.push({ version: ver, message: meta.deprecated });
162
+ }
163
+
164
+ const installedDeprecated = installed
165
+ ? (data.versions?.[installed]?.deprecated || null)
166
+ : null;
167
+
168
+ return {
169
+ lib, registry: 'npm',
170
+ installed_version: installed,
171
+ latest_version: latest,
172
+ up_to_date: installed === latest,
173
+ installed_deprecated: installedDeprecated,
174
+ deprecated_versions: deprecated.slice(0, 10),
175
+ recent_versions: Object.keys(data.versions || {}).slice(-5).reverse(),
176
+ description: (data.description || '').slice(0, 200),
177
+ homepage: latestMeta.homepage || null,
178
+ changelog: showChangelog ? (latestMeta.repository?.url || null) : null,
179
+ };
180
+ }
181
+
182
+ async function checkPypi(lib, showChangelog) {
183
+ const installed = installedPython(lib);
184
+ const data = await fetchJson(`https://pypi.org/pypi/${encodeURIComponent(lib)}/json`);
185
+ const info = data.info || {};
186
+ const latest = info.version || null;
187
+
188
+ const isDeprecated = (info.classifiers || []).some(c => /deprecated/i.test(c));
189
+ const desc = info.description || '';
190
+ const notices = desc.split('\n')
191
+ .filter(l => /deprecated|removed|use.*instead/i.test(l))
192
+ .slice(0, 5).map(l => l.trim());
193
+
194
+ return {
195
+ lib, registry: 'pypi',
196
+ installed_version: installed,
197
+ latest_version: latest,
198
+ up_to_date: installed === latest,
199
+ is_deprecated: isDeprecated,
200
+ deprecation_notices: notices,
201
+ recent_versions: Object.keys(data.releases || {}).slice(-5).reverse(),
202
+ requires_python: info.requires_python || null,
203
+ description: (info.summary || '').slice(0, 200),
204
+ changelog: showChangelog ? (info.project_urls?.Changelog || null) : null,
205
+ };
206
+ }
207
+
208
+ async function checkCrates(lib, showChangelog) {
209
+ const cwd = process.cwd();
210
+ const installed = installedRust(lib, cwd);
211
+ const data = await fetchJson(`https://crates.io/api/v1/crates/${encodeURIComponent(lib)}`);
212
+ const crate = data.crate || {};
213
+ const versions = (data.versions || []).slice(0, 10);
214
+
215
+ const yanked = versions.filter(v => v.yanked).map(v => ({ version: v.num }));
216
+ const recent = versions.slice(0, 5).map(v => ({
217
+ version: v.num, yanked: v.yanked, date: v.created_at?.slice(0, 10),
218
+ }));
219
+
220
+ return {
221
+ lib, registry: 'crates.io',
222
+ installed_version: installed,
223
+ latest_version: crate.newest_version || null,
224
+ up_to_date: installed === crate.newest_version,
225
+ yanked_versions: yanked,
226
+ recent_versions: recent,
227
+ description: (crate.description || '').slice(0, 200),
228
+ repository: crate.repository || null,
229
+ changelog: showChangelog ? crate.repository : null,
230
+ };
231
+ }
232
+
233
+ async function run(args) {
234
+ if (args.length === 0 || args[0] === '--help') { console.log(HELP); return; }
235
+
236
+ const lib = args[0];
237
+ const langArg = (args.find(a => a.startsWith('--lang=')) || '').replace('--lang=', '') || null;
238
+ const showChangelog = args.includes('--show-changelog');
239
+ const lang = langArg || detectLang(process.cwd());
240
+
241
+ if (!lang) {
242
+ return output(fail('Cannot auto-detect project language. Use --lang=node|python|rust'));
243
+ }
244
+
245
+ try {
246
+ let result;
247
+ if (lang === 'node') result = await checkNpm(lib, showChangelog);
248
+ else if (lang === 'python') result = await checkPypi(lib, showChangelog);
249
+ else if (lang === 'rust') result = await checkCrates(lib, showChangelog);
250
+ else return output(fail(`Unsupported lang: ${lang}. Use node|python|rust`));
251
+ output(ok(result));
252
+ } catch (err) {
253
+ // If it looks like a network error — use offline fallback instead of failing
254
+ const isNetworkError = /connection|DNS|timed out|ENOTFOUND|ECONNREFUSED|ETIMEDOUT/i.test(err.message);
255
+ if (isNetworkError) {
256
+ const fallback = offlineFallback(lib, lang, process.cwd());
257
+ output(ok(fallback));
258
+ } else {
259
+ output(fail(`Registry error: ${err.message}`));
260
+ }
261
+ }
262
+ }
263
+
264
+ module.exports = { run };