briefed 0.1.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.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/bench/metrics.d.ts +31 -0
- package/dist/bench/metrics.js +122 -0
- package/dist/bench/metrics.js.map +1 -0
- package/dist/bench/runner.d.ts +27 -0
- package/dist/bench/runner.js +184 -0
- package/dist/bench/runner.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +42 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/bench.d.ts +11 -0
- package/dist/commands/bench.js +23 -0
- package/dist/commands/bench.js.map +1 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.js +246 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.js +319 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/stats.d.ts +5 -0
- package/dist/commands/stats.js +87 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/deliver/ci.d.ts +4 -0
- package/dist/deliver/ci.js +62 -0
- package/dist/deliver/ci.js.map +1 -0
- package/dist/deliver/claudemd.d.ts +9 -0
- package/dist/deliver/claudemd.js +51 -0
- package/dist/deliver/claudemd.js.map +1 -0
- package/dist/deliver/cross-tool.d.ts +10 -0
- package/dist/deliver/cross-tool.js +49 -0
- package/dist/deliver/cross-tool.js.map +1 -0
- package/dist/deliver/git-hook.d.ts +10 -0
- package/dist/deliver/git-hook.js +104 -0
- package/dist/deliver/git-hook.js.map +1 -0
- package/dist/deliver/hooks.d.ts +9 -0
- package/dist/deliver/hooks.js +315 -0
- package/dist/deliver/hooks.js.map +1 -0
- package/dist/extract/complexity.d.ts +16 -0
- package/dist/extract/complexity.js +46 -0
- package/dist/extract/complexity.js.map +1 -0
- package/dist/extract/conventions.d.ts +18 -0
- package/dist/extract/conventions.js +143 -0
- package/dist/extract/conventions.js.map +1 -0
- package/dist/extract/depgraph.d.ts +12 -0
- package/dist/extract/depgraph.js +70 -0
- package/dist/extract/depgraph.js.map +1 -0
- package/dist/extract/env.d.ts +17 -0
- package/dist/extract/env.js +142 -0
- package/dist/extract/env.js.map +1 -0
- package/dist/extract/error-patterns.d.ts +15 -0
- package/dist/extract/error-patterns.js +107 -0
- package/dist/extract/error-patterns.js.map +1 -0
- package/dist/extract/frontend.d.ts +30 -0
- package/dist/extract/frontend.js +244 -0
- package/dist/extract/frontend.js.map +1 -0
- package/dist/extract/gotchas.d.ts +12 -0
- package/dist/extract/gotchas.js +145 -0
- package/dist/extract/gotchas.js.map +1 -0
- package/dist/extract/history.d.ts +29 -0
- package/dist/extract/history.js +91 -0
- package/dist/extract/history.js.map +1 -0
- package/dist/extract/infra.d.ts +27 -0
- package/dist/extract/infra.js +226 -0
- package/dist/extract/infra.js.map +1 -0
- package/dist/extract/monorepo.d.ts +16 -0
- package/dist/extract/monorepo.js +135 -0
- package/dist/extract/monorepo.js.map +1 -0
- package/dist/extract/routes.d.ts +16 -0
- package/dist/extract/routes.js +156 -0
- package/dist/extract/routes.js.map +1 -0
- package/dist/extract/scanner.d.ts +18 -0
- package/dist/extract/scanner.js +109 -0
- package/dist/extract/scanner.js.map +1 -0
- package/dist/extract/schema.d.ts +28 -0
- package/dist/extract/schema.js +192 -0
- package/dist/extract/schema.js.map +1 -0
- package/dist/extract/scripts.d.ts +18 -0
- package/dist/extract/scripts.js +104 -0
- package/dist/extract/scripts.js.map +1 -0
- package/dist/extract/security.d.ts +20 -0
- package/dist/extract/security.js +95 -0
- package/dist/extract/security.js.map +1 -0
- package/dist/extract/signatures.d.ts +33 -0
- package/dist/extract/signatures.js +608 -0
- package/dist/extract/signatures.js.map +1 -0
- package/dist/extract/staleness.d.ts +16 -0
- package/dist/extract/staleness.js +108 -0
- package/dist/extract/staleness.js.map +1 -0
- package/dist/extract/tests.d.ts +16 -0
- package/dist/extract/tests.js +175 -0
- package/dist/extract/tests.js.map +1 -0
- package/dist/extract/usage-examples.d.ts +17 -0
- package/dist/extract/usage-examples.js +115 -0
- package/dist/extract/usage-examples.js.map +1 -0
- package/dist/generate/index-file.d.ts +29 -0
- package/dist/generate/index-file.js +168 -0
- package/dist/generate/index-file.js.map +1 -0
- package/dist/generate/rules.d.ts +6 -0
- package/dist/generate/rules.js +94 -0
- package/dist/generate/rules.js.map +1 -0
- package/dist/generate/skeleton.d.ts +14 -0
- package/dist/generate/skeleton.js +145 -0
- package/dist/generate/skeleton.js.map +1 -0
- package/dist/learn/tracker.d.ts +54 -0
- package/dist/learn/tracker.js +129 -0
- package/dist/learn/tracker.js.map +1 -0
- package/dist/utils/detect.d.ts +16 -0
- package/dist/utils/detect.js +188 -0
- package/dist/utils/detect.js.map +1 -0
- package/dist/utils/pagerank.d.ts +19 -0
- package/dist/utils/pagerank.js +52 -0
- package/dist/utils/pagerank.js.map +1 -0
- package/dist/utils/tokens.d.ts +11 -0
- package/dist/utils/tokens.js +27 -0
- package/dist/utils/tokens.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { glob } from "glob";
|
|
4
|
+
/**
|
|
5
|
+
* Extract database schema from ORM definition files.
|
|
6
|
+
* Supports: Prisma, Drizzle, Django models, SQLAlchemy, TypeORM entities.
|
|
7
|
+
*/
|
|
8
|
+
export function extractSchemas(root) {
|
|
9
|
+
const models = [];
|
|
10
|
+
// Prisma
|
|
11
|
+
const prismaFiles = glob.sync("**/schema.prisma", { cwd: root, ignore: ["node_modules/**"] });
|
|
12
|
+
for (const f of prismaFiles) {
|
|
13
|
+
models.push(...parsePrisma(readFileSync(join(root, f), "utf-8"), f));
|
|
14
|
+
}
|
|
15
|
+
// Drizzle
|
|
16
|
+
const drizzleFiles = glob.sync("**/{schema,db}.{ts,js}", { cwd: root, ignore: ["node_modules/**", "dist/**"] });
|
|
17
|
+
for (const f of drizzleFiles) {
|
|
18
|
+
const content = readFileSync(join(root, f), "utf-8");
|
|
19
|
+
if (content.includes("pgTable") || content.includes("mysqlTable") || content.includes("sqliteTable")) {
|
|
20
|
+
models.push(...parseDrizzle(content, f));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// Django models
|
|
24
|
+
const djangoFiles = glob.sync("**/models.py", { cwd: root, ignore: ["venv/**", ".venv/**"] });
|
|
25
|
+
for (const f of djangoFiles) {
|
|
26
|
+
const content = readFileSync(join(root, f), "utf-8");
|
|
27
|
+
if (content.includes("models.Model")) {
|
|
28
|
+
models.push(...parseDjango(content, f));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// TypeORM entities
|
|
32
|
+
const typeormFiles = glob.sync("**/*.entity.{ts,js}", { cwd: root, ignore: ["node_modules/**", "dist/**"] });
|
|
33
|
+
for (const f of typeormFiles) {
|
|
34
|
+
models.push(...parseTypeORM(readFileSync(join(root, f), "utf-8"), f));
|
|
35
|
+
}
|
|
36
|
+
return models;
|
|
37
|
+
}
|
|
38
|
+
function parsePrisma(content, source) {
|
|
39
|
+
const models = [];
|
|
40
|
+
const modelRegex = /model\s+(\w+)\s*\{([\s\S]*?)\n\}/g;
|
|
41
|
+
for (const match of content.matchAll(modelRegex)) {
|
|
42
|
+
const name = match[1];
|
|
43
|
+
const body = match[2];
|
|
44
|
+
const fields = [];
|
|
45
|
+
const relations = [];
|
|
46
|
+
for (const line of body.split("\n")) {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("@@"))
|
|
49
|
+
continue;
|
|
50
|
+
const fieldMatch = trimmed.match(/^(\w+)\s+(\w+)(\[\])?([\?!]?)\s*(.*)/);
|
|
51
|
+
if (fieldMatch) {
|
|
52
|
+
const [, fname, ftype, isArray, optional, rest] = fieldMatch;
|
|
53
|
+
const isRelation = /^[A-Z]/.test(ftype) && ftype !== "String" && ftype !== "Int" &&
|
|
54
|
+
ftype !== "Float" && ftype !== "Boolean" && ftype !== "DateTime" &&
|
|
55
|
+
ftype !== "Json" && ftype !== "Bytes" && ftype !== "BigInt" && ftype !== "Decimal";
|
|
56
|
+
if (isRelation) {
|
|
57
|
+
relations.push({
|
|
58
|
+
field: fname,
|
|
59
|
+
target: ftype,
|
|
60
|
+
type: isArray ? "one-to-many" : "one-to-one",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
fields.push({
|
|
65
|
+
name: fname,
|
|
66
|
+
type: isArray ? `${ftype}[]` : ftype,
|
|
67
|
+
optional: optional === "?",
|
|
68
|
+
unique: rest.includes("@unique"),
|
|
69
|
+
default: rest.match(/@default\((.+?)\)/)?.[1] || null,
|
|
70
|
+
isPk: rest.includes("@id"),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
models.push({ name, fields, relations, source });
|
|
76
|
+
}
|
|
77
|
+
return models;
|
|
78
|
+
}
|
|
79
|
+
function parseDrizzle(content, source) {
|
|
80
|
+
const models = [];
|
|
81
|
+
const tableRegex = /(?:export\s+const\s+)?(\w+)\s*=\s*(?:pgTable|mysqlTable|sqliteTable)\s*\(\s*['"](\w+)['"]\s*,\s*\{([\s\S]*?)\}\s*\)/g;
|
|
82
|
+
for (const match of content.matchAll(tableRegex)) {
|
|
83
|
+
const varName = match[1];
|
|
84
|
+
const tableName = match[2];
|
|
85
|
+
const body = match[3];
|
|
86
|
+
const fields = [];
|
|
87
|
+
const fieldRegex = /(\w+)\s*:\s*(\w+)\s*\(\s*['"]?(\w+)?['"]?\s*\)/g;
|
|
88
|
+
for (const fm of body.matchAll(fieldRegex)) {
|
|
89
|
+
fields.push({
|
|
90
|
+
name: fm[1],
|
|
91
|
+
type: fm[2],
|
|
92
|
+
optional: body.includes(`${fm[1]}`) && body.includes(".default("),
|
|
93
|
+
unique: false,
|
|
94
|
+
default: null,
|
|
95
|
+
isPk: fm[2] === "serial" || body.includes(`${fm[1]}`) && body.includes(".primaryKey()"),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
models.push({ name: tableName, fields, relations: [], source });
|
|
99
|
+
}
|
|
100
|
+
return models;
|
|
101
|
+
}
|
|
102
|
+
function parseDjango(content, source) {
|
|
103
|
+
const models = [];
|
|
104
|
+
const classRegex = /class\s+(\w+)\s*\(.*?models\.Model.*?\)\s*:([\s\S]*?)(?=\nclass\s|\n\S|$)/g;
|
|
105
|
+
for (const match of content.matchAll(classRegex)) {
|
|
106
|
+
const name = match[1];
|
|
107
|
+
const body = match[2];
|
|
108
|
+
const fields = [];
|
|
109
|
+
const relations = [];
|
|
110
|
+
const fieldRegex = /(\w+)\s*=\s*models\.(\w+)\s*\(/g;
|
|
111
|
+
for (const fm of body.matchAll(fieldRegex)) {
|
|
112
|
+
const fieldType = fm[2];
|
|
113
|
+
if (["ForeignKey", "OneToOneField", "ManyToManyField"].includes(fieldType)) {
|
|
114
|
+
const targetMatch = body.slice(fm.index).match(/models\.\w+\(\s*['"]?(\w+)/);
|
|
115
|
+
relations.push({
|
|
116
|
+
field: fm[1],
|
|
117
|
+
target: targetMatch?.[1] || "unknown",
|
|
118
|
+
type: fieldType === "ManyToManyField" ? "many-to-many" : fieldType === "OneToOneField" ? "one-to-one" : "one-to-many",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
fields.push({
|
|
123
|
+
name: fm[1],
|
|
124
|
+
type: fieldType,
|
|
125
|
+
optional: body.includes("null=True") || body.includes("blank=True"),
|
|
126
|
+
unique: body.includes("unique=True"),
|
|
127
|
+
default: null,
|
|
128
|
+
isPk: fieldType === "AutoField" || fm[1] === "id",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
models.push({ name, fields, relations, source });
|
|
133
|
+
}
|
|
134
|
+
return models;
|
|
135
|
+
}
|
|
136
|
+
function parseTypeORM(content, source) {
|
|
137
|
+
const models = [];
|
|
138
|
+
const classMatch = content.match(/class\s+(\w+)/);
|
|
139
|
+
if (!classMatch)
|
|
140
|
+
return models;
|
|
141
|
+
const name = classMatch[1];
|
|
142
|
+
const fields = [];
|
|
143
|
+
const relations = [];
|
|
144
|
+
const colRegex = /@(?:Column|PrimaryGeneratedColumn|PrimaryColumn)\s*\(([^)]*)\)\s*\n\s*(\w+)\s*[!?]?\s*:\s*(\w+)/g;
|
|
145
|
+
for (const m of content.matchAll(colRegex)) {
|
|
146
|
+
fields.push({
|
|
147
|
+
name: m[2],
|
|
148
|
+
type: m[3],
|
|
149
|
+
optional: m[0].includes("nullable: true"),
|
|
150
|
+
unique: m[0].includes("unique: true"),
|
|
151
|
+
default: null,
|
|
152
|
+
isPk: m[0].includes("PrimaryGeneratedColumn") || m[0].includes("PrimaryColumn"),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
const relRegex = /@(?:ManyToOne|OneToMany|OneToOne|ManyToMany)\s*\([^)]*\)\s*\n\s*(\w+)\s*[!?]?\s*:\s*(\w+)/g;
|
|
156
|
+
for (const m of content.matchAll(relRegex)) {
|
|
157
|
+
const relType = m[0].includes("ManyToMany") ? "many-to-many" : m[0].includes("OneToMany") ? "one-to-many" : "one-to-one";
|
|
158
|
+
relations.push({ field: m[1], target: m[2], type: relType });
|
|
159
|
+
}
|
|
160
|
+
models.push({ name, fields, relations, source });
|
|
161
|
+
return models;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Format schemas for skeleton inclusion.
|
|
165
|
+
*/
|
|
166
|
+
export function formatSchemas(models) {
|
|
167
|
+
if (models.length === 0)
|
|
168
|
+
return "";
|
|
169
|
+
const lines = ["Database schema:"];
|
|
170
|
+
for (const m of models) {
|
|
171
|
+
const fieldList = m.fields
|
|
172
|
+
.map((f) => {
|
|
173
|
+
let s = `${f.name}: ${f.type}`;
|
|
174
|
+
if (f.isPk)
|
|
175
|
+
s += " (pk)";
|
|
176
|
+
if (f.unique)
|
|
177
|
+
s += " (unique)";
|
|
178
|
+
if (f.optional)
|
|
179
|
+
s += "?";
|
|
180
|
+
if (f.default)
|
|
181
|
+
s += ` = ${f.default}`;
|
|
182
|
+
return s;
|
|
183
|
+
})
|
|
184
|
+
.join(", ");
|
|
185
|
+
const relList = m.relations
|
|
186
|
+
.map((r) => `${r.field} → ${r.target} (${r.type})`)
|
|
187
|
+
.join(", ");
|
|
188
|
+
lines.push(` ${m.name}: ${fieldList}${relList ? ` | ${relList}` : ""}`);
|
|
189
|
+
}
|
|
190
|
+
return lines.join("\n");
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/extract/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ProjectScripts {
|
|
2
|
+
build: string | null;
|
|
3
|
+
dev: string | null;
|
|
4
|
+
test: string | null;
|
|
5
|
+
lint: string | null;
|
|
6
|
+
start: string | null;
|
|
7
|
+
deploy: string | null;
|
|
8
|
+
other: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Extract build/test/dev commands from package.json, Makefile, etc.
|
|
12
|
+
* Claude needs to know how to run things.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractScripts(root: string): ProjectScripts;
|
|
15
|
+
/**
|
|
16
|
+
* Format scripts for skeleton inclusion.
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatScripts(scripts: ProjectScripts): string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
/**
|
|
4
|
+
* Extract build/test/dev commands from package.json, Makefile, etc.
|
|
5
|
+
* Claude needs to know how to run things.
|
|
6
|
+
*/
|
|
7
|
+
export function extractScripts(root) {
|
|
8
|
+
const scripts = {
|
|
9
|
+
build: null,
|
|
10
|
+
dev: null,
|
|
11
|
+
test: null,
|
|
12
|
+
lint: null,
|
|
13
|
+
start: null,
|
|
14
|
+
deploy: null,
|
|
15
|
+
other: {},
|
|
16
|
+
};
|
|
17
|
+
// package.json
|
|
18
|
+
const pkgPath = join(root, "package.json");
|
|
19
|
+
if (existsSync(pkgPath)) {
|
|
20
|
+
try {
|
|
21
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
22
|
+
const s = pkg.scripts || {};
|
|
23
|
+
scripts.build = s.build || null;
|
|
24
|
+
scripts.dev = s.dev || s.serve || s.start?.includes("--watch") ? (s.dev || s.serve) : null;
|
|
25
|
+
scripts.test = s.test || s["test:unit"] || null;
|
|
26
|
+
scripts.lint = s.lint || s["lint:fix"] || null;
|
|
27
|
+
scripts.start = s.start || null;
|
|
28
|
+
scripts.deploy = s.deploy || s.release || null;
|
|
29
|
+
// Collect other interesting scripts
|
|
30
|
+
for (const [name, cmd] of Object.entries(s)) {
|
|
31
|
+
if (["build", "dev", "test", "lint", "start", "deploy", "serve", "postinstall", "prepare", "precommit"].includes(name))
|
|
32
|
+
continue;
|
|
33
|
+
if (typeof cmd === "string" && name.length < 20) {
|
|
34
|
+
scripts.other[name] = cmd;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch { /* skip */ }
|
|
39
|
+
}
|
|
40
|
+
// Makefile
|
|
41
|
+
const makefilePath = join(root, "Makefile");
|
|
42
|
+
if (existsSync(makefilePath)) {
|
|
43
|
+
try {
|
|
44
|
+
const content = readFileSync(makefilePath, "utf-8");
|
|
45
|
+
const targets = content.matchAll(/^(\w[\w-]*)\s*:/gm);
|
|
46
|
+
for (const match of targets) {
|
|
47
|
+
const target = match[1];
|
|
48
|
+
if (target === "build" && !scripts.build)
|
|
49
|
+
scripts.build = `make build`;
|
|
50
|
+
if (target === "test" && !scripts.test)
|
|
51
|
+
scripts.test = `make test`;
|
|
52
|
+
if (target === "dev" && !scripts.dev)
|
|
53
|
+
scripts.dev = `make dev`;
|
|
54
|
+
if (target === "lint" && !scripts.lint)
|
|
55
|
+
scripts.lint = `make lint`;
|
|
56
|
+
if (target === "deploy" && !scripts.deploy)
|
|
57
|
+
scripts.deploy = `make deploy`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch { /* skip */ }
|
|
61
|
+
}
|
|
62
|
+
// Go
|
|
63
|
+
if (existsSync(join(root, "go.mod"))) {
|
|
64
|
+
if (!scripts.build)
|
|
65
|
+
scripts.build = "go build ./...";
|
|
66
|
+
if (!scripts.test)
|
|
67
|
+
scripts.test = "go test ./...";
|
|
68
|
+
}
|
|
69
|
+
// Rust
|
|
70
|
+
if (existsSync(join(root, "Cargo.toml"))) {
|
|
71
|
+
if (!scripts.build)
|
|
72
|
+
scripts.build = "cargo build";
|
|
73
|
+
if (!scripts.test)
|
|
74
|
+
scripts.test = "cargo test";
|
|
75
|
+
}
|
|
76
|
+
// Python
|
|
77
|
+
if (existsSync(join(root, "pyproject.toml"))) {
|
|
78
|
+
if (!scripts.test)
|
|
79
|
+
scripts.test = "pytest";
|
|
80
|
+
}
|
|
81
|
+
return scripts;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Format scripts for skeleton inclusion.
|
|
85
|
+
*/
|
|
86
|
+
export function formatScripts(scripts) {
|
|
87
|
+
const lines = ["Commands:"];
|
|
88
|
+
if (scripts.build)
|
|
89
|
+
lines.push(` build: ${scripts.build}`);
|
|
90
|
+
if (scripts.dev)
|
|
91
|
+
lines.push(` dev: ${scripts.dev}`);
|
|
92
|
+
if (scripts.test)
|
|
93
|
+
lines.push(` test: ${scripts.test}`);
|
|
94
|
+
if (scripts.lint)
|
|
95
|
+
lines.push(` lint: ${scripts.lint}`);
|
|
96
|
+
if (scripts.start)
|
|
97
|
+
lines.push(` start: ${scripts.start}`);
|
|
98
|
+
if (scripts.deploy)
|
|
99
|
+
lines.push(` deploy: ${scripts.deploy}`);
|
|
100
|
+
if (lines.length === 1)
|
|
101
|
+
return ""; // no scripts found
|
|
102
|
+
return lines.join("\n");
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/extract/scripts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface SecurityWarning {
|
|
2
|
+
file: string;
|
|
3
|
+
line: number;
|
|
4
|
+
type: SecurityIssueType;
|
|
5
|
+
detail: string;
|
|
6
|
+
}
|
|
7
|
+
export type SecurityIssueType = "api_key" | "password" | "secret" | "private_key" | "connection_string" | "token" | "sensitive_file";
|
|
8
|
+
/**
|
|
9
|
+
* Check if a file should be excluded from context output for security reasons.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isSensitiveFile(filePath: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Scan a file for sensitive data patterns.
|
|
14
|
+
* Returns warnings but does NOT include the actual secret values.
|
|
15
|
+
*/
|
|
16
|
+
export declare function scanForSecrets(filePath: string): SecurityWarning[];
|
|
17
|
+
/**
|
|
18
|
+
* Redact sensitive values from text before including in context.
|
|
19
|
+
*/
|
|
20
|
+
export declare function redactSecrets(text: string): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { basename } from "path";
|
|
3
|
+
/** Files that should NEVER be included in context output */
|
|
4
|
+
const SENSITIVE_FILENAMES = new Set([
|
|
5
|
+
".env",
|
|
6
|
+
".env.local",
|
|
7
|
+
".env.production",
|
|
8
|
+
".env.development",
|
|
9
|
+
"credentials.json",
|
|
10
|
+
"service-account.json",
|
|
11
|
+
"secrets.yaml",
|
|
12
|
+
"secrets.yml",
|
|
13
|
+
".npmrc",
|
|
14
|
+
".pypirc",
|
|
15
|
+
"id_rsa",
|
|
16
|
+
"id_ed25519",
|
|
17
|
+
".pem",
|
|
18
|
+
".key",
|
|
19
|
+
]);
|
|
20
|
+
/** Patterns that indicate sensitive values in code */
|
|
21
|
+
const SECRET_PATTERNS = [
|
|
22
|
+
{ regex: /(?:api[_-]?key|apikey)\s*[:=]\s*['"][^'"]{8,}/i, type: "api_key", label: "API key" },
|
|
23
|
+
{ regex: /(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]+/i, type: "password", label: "password" },
|
|
24
|
+
{ regex: /(?:secret|client_secret)\s*[:=]\s*['"][^'"]{8,}/i, type: "secret", label: "secret" },
|
|
25
|
+
{ regex: /-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----/i, type: "private_key", label: "private key" },
|
|
26
|
+
{ regex: /(?:postgres|mysql|mongodb|redis):\/\/\w+:[^@]+@/i, type: "connection_string", label: "connection string with credentials" },
|
|
27
|
+
{ regex: /(?:bearer|token)\s+[a-zA-Z0-9_\-.]{20,}/i, type: "token", label: "auth token" },
|
|
28
|
+
{ regex: /(?:aws_secret_access_key|AWS_SECRET)\s*[:=]\s*['"]?[A-Za-z0-9/+=]{20,}/i, type: "secret", label: "AWS secret key" },
|
|
29
|
+
{ regex: /(?:ghp_|github_pat_)[a-zA-Z0-9]{30,}/i, type: "token", label: "GitHub token" },
|
|
30
|
+
{ regex: /sk-[a-zA-Z0-9]{20,}/i, type: "api_key", label: "OpenAI/Stripe secret key" },
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Check if a file should be excluded from context output for security reasons.
|
|
34
|
+
*/
|
|
35
|
+
export function isSensitiveFile(filePath) {
|
|
36
|
+
const name = basename(filePath);
|
|
37
|
+
if (SENSITIVE_FILENAMES.has(name))
|
|
38
|
+
return true;
|
|
39
|
+
if (name.endsWith(".pem") || name.endsWith(".key") || name.endsWith(".p12"))
|
|
40
|
+
return true;
|
|
41
|
+
if (name.startsWith(".env"))
|
|
42
|
+
return true;
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Scan a file for sensitive data patterns.
|
|
47
|
+
* Returns warnings but does NOT include the actual secret values.
|
|
48
|
+
*/
|
|
49
|
+
export function scanForSecrets(filePath) {
|
|
50
|
+
const warnings = [];
|
|
51
|
+
// Skip binary files
|
|
52
|
+
try {
|
|
53
|
+
const content = readFileSync(filePath, "utf-8");
|
|
54
|
+
const lines = content.split("\n");
|
|
55
|
+
for (let i = 0; i < lines.length; i++) {
|
|
56
|
+
const line = lines[i];
|
|
57
|
+
// Skip comments that reference secrets generically
|
|
58
|
+
if (line.trim().startsWith("//") || line.trim().startsWith("#")) {
|
|
59
|
+
// Still check for actual hardcoded values in comments
|
|
60
|
+
if (!line.includes("=") && !line.includes(":"))
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
64
|
+
if (pattern.regex.test(line)) {
|
|
65
|
+
// Don't flag env var references (process.env.X, os.environ, etc.)
|
|
66
|
+
if (line.includes("process.env") || line.includes("os.environ") ||
|
|
67
|
+
line.includes("${") || line.includes("getenv")) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
warnings.push({
|
|
71
|
+
file: filePath,
|
|
72
|
+
line: i + 1,
|
|
73
|
+
type: pattern.type,
|
|
74
|
+
detail: `Possible ${pattern.label} found — not included in context output`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Can't read file — skip
|
|
82
|
+
}
|
|
83
|
+
return warnings;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Redact sensitive values from text before including in context.
|
|
87
|
+
*/
|
|
88
|
+
export function redactSecrets(text) {
|
|
89
|
+
let result = text;
|
|
90
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
91
|
+
result = result.replace(pattern.regex, `[REDACTED ${pattern.label}]`);
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/extract/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracted symbol from a source file.
|
|
3
|
+
* Represents exports, functions, classes, types — the structural skeleton.
|
|
4
|
+
*/
|
|
5
|
+
export interface Symbol {
|
|
6
|
+
name: string;
|
|
7
|
+
kind: SymbolKind;
|
|
8
|
+
signature: string;
|
|
9
|
+
description: string | null;
|
|
10
|
+
exported: boolean;
|
|
11
|
+
line: number;
|
|
12
|
+
}
|
|
13
|
+
export type SymbolKind = "function" | "class" | "interface" | "type" | "enum" | "variable" | "method" | "component" | "route" | "unknown";
|
|
14
|
+
/**
|
|
15
|
+
* Import reference found in a file.
|
|
16
|
+
*/
|
|
17
|
+
export interface ImportRef {
|
|
18
|
+
source: string;
|
|
19
|
+
names: string[];
|
|
20
|
+
isRelative: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface FileExtraction {
|
|
23
|
+
path: string;
|
|
24
|
+
symbols: Symbol[];
|
|
25
|
+
imports: ImportRef[];
|
|
26
|
+
lineCount: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Extract symbols and imports from a source file.
|
|
30
|
+
* Uses regex-based extraction (fast, no WASM dependency).
|
|
31
|
+
* Covers TypeScript, JavaScript, Python, Go, Rust, Java.
|
|
32
|
+
*/
|
|
33
|
+
export declare function extractFile(filePath: string, rootPath: string): FileExtraction;
|