sourcebook 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 +111 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +17 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.js +91 -0
- package/dist/generators/claude.d.ts +11 -0
- package/dist/generators/claude.js +191 -0
- package/dist/generators/copilot.d.ts +12 -0
- package/dist/generators/copilot.js +119 -0
- package/dist/generators/cursor.d.ts +17 -0
- package/dist/generators/cursor.js +123 -0
- package/dist/scanner/build.d.ts +2 -0
- package/dist/scanner/build.js +56 -0
- package/dist/scanner/frameworks.d.ts +2 -0
- package/dist/scanner/frameworks.js +230 -0
- package/dist/scanner/git.d.ts +17 -0
- package/dist/scanner/git.js +317 -0
- package/dist/scanner/graph.d.ts +17 -0
- package/dist/scanner/graph.js +251 -0
- package/dist/scanner/index.d.ts +2 -0
- package/dist/scanner/index.js +87 -0
- package/dist/scanner/patterns.d.ts +6 -0
- package/dist/scanner/patterns.js +203 -0
- package/dist/scanner/structure.d.ts +2 -0
- package/dist/scanner/structure.js +148 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.js +1 -0
- package/dist/utils/output.d.ts +1 -0
- package/dist/utils/output.js +10 -0
- package/package.json +53 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Detect code patterns and conventions that are non-obvious.
|
|
5
|
+
* This is the core intelligence layer -- finding things agents miss.
|
|
6
|
+
*/
|
|
7
|
+
export async function detectPatterns(dir, files, frameworks) {
|
|
8
|
+
const findings = [];
|
|
9
|
+
// Only analyze source files
|
|
10
|
+
const sourceFiles = files.filter((f) => f.endsWith(".ts") ||
|
|
11
|
+
f.endsWith(".tsx") ||
|
|
12
|
+
f.endsWith(".js") ||
|
|
13
|
+
f.endsWith(".jsx"));
|
|
14
|
+
// Sample files for pattern detection (don't read everything)
|
|
15
|
+
const sampled = sampleFiles(sourceFiles, 50);
|
|
16
|
+
const fileContents = new Map();
|
|
17
|
+
for (const file of sampled) {
|
|
18
|
+
try {
|
|
19
|
+
const content = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
20
|
+
fileContents.set(file, content);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// skip unreadable files
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// --- Barrel exports detection ---
|
|
27
|
+
findings.push(...detectBarrelExports(files, fileContents));
|
|
28
|
+
// --- Import style detection ---
|
|
29
|
+
findings.push(...detectImportPatterns(fileContents));
|
|
30
|
+
// --- Environment variable patterns ---
|
|
31
|
+
findings.push(...detectEnvPatterns(dir, files, fileContents));
|
|
32
|
+
// --- Error handling patterns ---
|
|
33
|
+
findings.push(...detectErrorHandling(fileContents));
|
|
34
|
+
// --- Export patterns ---
|
|
35
|
+
findings.push(...detectExportPatterns(fileContents));
|
|
36
|
+
// Filter out discoverable findings
|
|
37
|
+
return findings.filter((f) => !f.discoverable);
|
|
38
|
+
}
|
|
39
|
+
function sampleFiles(files, maxCount) {
|
|
40
|
+
if (files.length <= maxCount)
|
|
41
|
+
return files;
|
|
42
|
+
// Prioritize: entry points, configs, then random sample
|
|
43
|
+
const priority = files.filter((f) => f.includes("index.") ||
|
|
44
|
+
f.includes("config.") ||
|
|
45
|
+
f.includes("app.") ||
|
|
46
|
+
f.includes("layout.") ||
|
|
47
|
+
f.includes("middleware."));
|
|
48
|
+
const rest = files.filter((f) => !priority.includes(f));
|
|
49
|
+
const shuffled = rest.sort(() => Math.random() - 0.5);
|
|
50
|
+
return [...priority, ...shuffled].slice(0, maxCount);
|
|
51
|
+
}
|
|
52
|
+
function detectBarrelExports(files, contents) {
|
|
53
|
+
const indexFiles = files.filter((f) => path.basename(f).startsWith("index.") && !f.includes("node_modules"));
|
|
54
|
+
if (indexFiles.length < 3)
|
|
55
|
+
return [];
|
|
56
|
+
// Check if index files are barrel exports (re-exporting)
|
|
57
|
+
let barrelCount = 0;
|
|
58
|
+
for (const indexFile of indexFiles) {
|
|
59
|
+
const content = contents.get(indexFile);
|
|
60
|
+
if (content && /export\s+\{.*\}\s+from|export\s+\*\s+from/.test(content)) {
|
|
61
|
+
barrelCount++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (barrelCount >= 3) {
|
|
65
|
+
return [
|
|
66
|
+
{
|
|
67
|
+
category: "Import conventions",
|
|
68
|
+
description: "Project uses barrel exports (index.ts files that re-export). Import from the directory, not from deep paths.",
|
|
69
|
+
evidence: `${barrelCount} barrel export files found`,
|
|
70
|
+
confidence: "high",
|
|
71
|
+
discoverable: false,
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
function detectImportPatterns(contents) {
|
|
78
|
+
const findings = [];
|
|
79
|
+
let aliasImports = 0;
|
|
80
|
+
let relativeImports = 0;
|
|
81
|
+
let absoluteImports = 0;
|
|
82
|
+
for (const [, content] of contents) {
|
|
83
|
+
const imports = content.match(/from\s+['"]([^'"]+)['"]/g) || [];
|
|
84
|
+
for (const imp of imports) {
|
|
85
|
+
const importPath = imp.match(/['"]([^'"]+)['"]/)?.[1] || "";
|
|
86
|
+
if (importPath.startsWith("@/") || importPath.startsWith("~/")) {
|
|
87
|
+
aliasImports++;
|
|
88
|
+
}
|
|
89
|
+
else if (importPath.startsWith(".")) {
|
|
90
|
+
relativeImports++;
|
|
91
|
+
}
|
|
92
|
+
else if (!importPath.startsWith("@") && !importPath.includes("/")) {
|
|
93
|
+
absoluteImports++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const total = aliasImports + relativeImports;
|
|
98
|
+
if (total > 10 && aliasImports > relativeImports * 2) {
|
|
99
|
+
findings.push({
|
|
100
|
+
category: "Import conventions",
|
|
101
|
+
description: "Project strongly prefers path alias imports (@/ or ~/) over relative imports. Use aliases for cross-directory imports.",
|
|
102
|
+
evidence: `${aliasImports} alias imports vs ${relativeImports} relative imports in sampled files`,
|
|
103
|
+
confidence: "high",
|
|
104
|
+
discoverable: false,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return findings;
|
|
108
|
+
}
|
|
109
|
+
function detectEnvPatterns(dir, files, contents) {
|
|
110
|
+
const findings = [];
|
|
111
|
+
// Check for .env.example or .env.local
|
|
112
|
+
const hasEnvExample = files.includes(".env.example") || files.includes(".env.sample");
|
|
113
|
+
const hasEnvLocal = files.includes(".env.local");
|
|
114
|
+
const hasEnv = files.includes(".env");
|
|
115
|
+
if (hasEnvExample) {
|
|
116
|
+
findings.push({
|
|
117
|
+
category: "Environment",
|
|
118
|
+
description: "Environment variables are documented in .env.example. Copy it to .env.local before running the project.",
|
|
119
|
+
confidence: "high",
|
|
120
|
+
discoverable: false,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// Detect which env vars are used in code
|
|
124
|
+
const envVars = new Set();
|
|
125
|
+
for (const [, content] of contents) {
|
|
126
|
+
const matches = content.matchAll(/process\.env\.(\w+)|import\.meta\.env\.(\w+)/g);
|
|
127
|
+
for (const match of matches) {
|
|
128
|
+
envVars.add(match[1] || match[2]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (envVars.size > 0) {
|
|
132
|
+
// Check for NEXT_PUBLIC_ prefix pattern
|
|
133
|
+
const publicVars = [...envVars].filter((v) => v.startsWith("NEXT_PUBLIC_") || v.startsWith("VITE_") || v.startsWith("EXPO_PUBLIC_"));
|
|
134
|
+
const privateVars = [...envVars].filter((v) => !v.startsWith("NEXT_PUBLIC_") &&
|
|
135
|
+
!v.startsWith("VITE_") &&
|
|
136
|
+
!v.startsWith("EXPO_PUBLIC_") &&
|
|
137
|
+
!v.startsWith("NODE_ENV"));
|
|
138
|
+
if (publicVars.length > 0 && privateVars.length > 0) {
|
|
139
|
+
findings.push({
|
|
140
|
+
category: "Environment",
|
|
141
|
+
description: `${envVars.size} env vars detected. Public (browser-exposed): ${publicVars.slice(0, 3).join(", ")}${publicVars.length > 3 ? "..." : ""}. Private (server-only): ${privateVars.slice(0, 3).join(", ")}${privateVars.length > 3 ? "..." : ""}.`,
|
|
142
|
+
rationale: "Agents sometimes expose private env vars to the client by using the wrong prefix.",
|
|
143
|
+
confidence: "medium",
|
|
144
|
+
discoverable: false,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return findings;
|
|
149
|
+
}
|
|
150
|
+
function detectErrorHandling(contents) {
|
|
151
|
+
const findings = [];
|
|
152
|
+
let tryCatchCount = 0;
|
|
153
|
+
let errorBoundaryCount = 0;
|
|
154
|
+
let customErrorClasses = 0;
|
|
155
|
+
for (const [, content] of contents) {
|
|
156
|
+
tryCatchCount += (content.match(/try\s*\{/g) || []).length;
|
|
157
|
+
if (content.includes("ErrorBoundary"))
|
|
158
|
+
errorBoundaryCount++;
|
|
159
|
+
if (/class\s+\w+Error\s+extends\s+(Error|BaseError)/.test(content)) {
|
|
160
|
+
customErrorClasses++;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (customErrorClasses >= 2) {
|
|
164
|
+
findings.push({
|
|
165
|
+
category: "Error handling",
|
|
166
|
+
description: "Project uses custom error classes. Throw specific error types instead of generic Error.",
|
|
167
|
+
confidence: "medium",
|
|
168
|
+
discoverable: false,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return findings;
|
|
172
|
+
}
|
|
173
|
+
function detectExportPatterns(contents) {
|
|
174
|
+
const findings = [];
|
|
175
|
+
let defaultExports = 0;
|
|
176
|
+
let namedExports = 0;
|
|
177
|
+
for (const [, content] of contents) {
|
|
178
|
+
defaultExports += (content.match(/export\s+default\s/g) || []).length;
|
|
179
|
+
namedExports += (content.match(/export\s+(const|function|class|type|interface)\s/g) || []).length;
|
|
180
|
+
}
|
|
181
|
+
const total = defaultExports + namedExports;
|
|
182
|
+
if (total > 10) {
|
|
183
|
+
if (namedExports > defaultExports * 3) {
|
|
184
|
+
findings.push({
|
|
185
|
+
category: "Export conventions",
|
|
186
|
+
description: "Project strongly prefers named exports over default exports. Use `export function` / `export const`, not `export default`.",
|
|
187
|
+
evidence: `${namedExports} named vs ${defaultExports} default exports in sampled files`,
|
|
188
|
+
confidence: "high",
|
|
189
|
+
discoverable: false,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
else if (defaultExports > namedExports * 2) {
|
|
193
|
+
findings.push({
|
|
194
|
+
category: "Export conventions",
|
|
195
|
+
description: "Project prefers default exports. Use `export default` for main module exports.",
|
|
196
|
+
evidence: `${defaultExports} default vs ${namedExports} named exports in sampled files`,
|
|
197
|
+
confidence: "medium",
|
|
198
|
+
discoverable: false,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return findings;
|
|
203
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function detectProjectStructure(dir, files) {
|
|
3
|
+
const findings = [];
|
|
4
|
+
const directories = {};
|
|
5
|
+
const entryPoints = [];
|
|
6
|
+
// Detect top-level directory purposes
|
|
7
|
+
const topDirs = new Set();
|
|
8
|
+
for (const file of files) {
|
|
9
|
+
const parts = file.split(path.sep);
|
|
10
|
+
if (parts.length > 1)
|
|
11
|
+
topDirs.add(parts[0]);
|
|
12
|
+
}
|
|
13
|
+
// Common directory purpose detection
|
|
14
|
+
const dirPurposes = {
|
|
15
|
+
src: "Source code",
|
|
16
|
+
lib: "Library code",
|
|
17
|
+
app: "Application routes / pages",
|
|
18
|
+
pages: "Page routes",
|
|
19
|
+
components: "UI components",
|
|
20
|
+
hooks: "Custom React hooks",
|
|
21
|
+
utils: "Utility functions",
|
|
22
|
+
helpers: "Helper functions",
|
|
23
|
+
services: "Service layer / API clients",
|
|
24
|
+
api: "API routes or endpoints",
|
|
25
|
+
server: "Server-side code",
|
|
26
|
+
public: "Static assets",
|
|
27
|
+
assets: "Static assets",
|
|
28
|
+
styles: "Stylesheets",
|
|
29
|
+
types: "TypeScript type definitions",
|
|
30
|
+
config: "Configuration files",
|
|
31
|
+
scripts: "Build / utility scripts",
|
|
32
|
+
test: "Tests",
|
|
33
|
+
tests: "Tests",
|
|
34
|
+
__tests__: "Tests",
|
|
35
|
+
e2e: "End-to-end tests",
|
|
36
|
+
docs: "Documentation",
|
|
37
|
+
migrations: "Database migrations",
|
|
38
|
+
supabase: "Supabase configuration and migrations",
|
|
39
|
+
prisma: "Prisma schema and migrations",
|
|
40
|
+
context: "Context / state management",
|
|
41
|
+
store: "State store",
|
|
42
|
+
features: "Feature modules",
|
|
43
|
+
modules: "Feature modules",
|
|
44
|
+
middleware: "Middleware",
|
|
45
|
+
};
|
|
46
|
+
for (const d of topDirs) {
|
|
47
|
+
if (dirPurposes[d]) {
|
|
48
|
+
directories[d] = dirPurposes[d];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Detect layout pattern
|
|
52
|
+
const hasFeatureDirs = topDirs.has("features") || topDirs.has("modules");
|
|
53
|
+
const hasLayerDirs = topDirs.has("components") &&
|
|
54
|
+
(topDirs.has("services") || topDirs.has("utils"));
|
|
55
|
+
if (hasFeatureDirs) {
|
|
56
|
+
findings.push({
|
|
57
|
+
category: "Project structure",
|
|
58
|
+
description: "Feature-based architecture. Group new code by feature, not by type (don't put components in a global components/ folder).",
|
|
59
|
+
rationale: "Agents default to layer-based grouping. Feature-based projects break when you scatter feature code across type folders.",
|
|
60
|
+
confidence: "high",
|
|
61
|
+
discoverable: false,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// Detect src/ vs root layout
|
|
65
|
+
const hasSrc = topDirs.has("src");
|
|
66
|
+
if (hasSrc) {
|
|
67
|
+
// Check if there's code at both root and src/ (messy)
|
|
68
|
+
const rootCode = files.some((f) => !f.includes(path.sep) &&
|
|
69
|
+
(f.endsWith(".ts") || f.endsWith(".js")) &&
|
|
70
|
+
f !== "next.config.js" &&
|
|
71
|
+
f !== "next.config.ts" &&
|
|
72
|
+
f !== "vite.config.ts" &&
|
|
73
|
+
f !== "tailwind.config.ts" &&
|
|
74
|
+
f !== "postcss.config.js" &&
|
|
75
|
+
f !== "vitest.config.ts");
|
|
76
|
+
if (rootCode) {
|
|
77
|
+
findings.push({
|
|
78
|
+
category: "Project structure",
|
|
79
|
+
description: "Source code lives in src/ but some code files exist at the root. Keep application code in src/.",
|
|
80
|
+
confidence: "medium",
|
|
81
|
+
discoverable: false,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Detect entry points
|
|
86
|
+
const commonEntries = [
|
|
87
|
+
"src/index.ts",
|
|
88
|
+
"src/index.tsx",
|
|
89
|
+
"src/main.ts",
|
|
90
|
+
"src/main.tsx",
|
|
91
|
+
"src/app.ts",
|
|
92
|
+
"src/app.tsx",
|
|
93
|
+
"index.ts",
|
|
94
|
+
"index.tsx",
|
|
95
|
+
"src/cli.ts",
|
|
96
|
+
"app/layout.tsx",
|
|
97
|
+
"app/page.tsx",
|
|
98
|
+
"pages/index.tsx",
|
|
99
|
+
"pages/_app.tsx",
|
|
100
|
+
"src/server.ts",
|
|
101
|
+
"server.ts",
|
|
102
|
+
];
|
|
103
|
+
for (const entry of commonEntries) {
|
|
104
|
+
if (files.includes(entry))
|
|
105
|
+
entryPoints.push(entry);
|
|
106
|
+
}
|
|
107
|
+
// Detect monorepo
|
|
108
|
+
const hasWorkspaces = files.includes("pnpm-workspace.yaml");
|
|
109
|
+
const hasLernaJson = files.includes("lerna.json");
|
|
110
|
+
const hasPackagesDir = topDirs.has("packages") || topDirs.has("apps");
|
|
111
|
+
if (hasWorkspaces || hasLernaJson || hasPackagesDir) {
|
|
112
|
+
findings.push({
|
|
113
|
+
category: "Project structure",
|
|
114
|
+
description: "This is a monorepo. Changes may affect multiple packages. Check workspace dependencies before modifying shared code.",
|
|
115
|
+
confidence: "high",
|
|
116
|
+
discoverable: false,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// Detect test co-location vs separate test dir
|
|
120
|
+
const colocatedTests = files.some((f) => f.includes(".test.") || f.includes(".spec."));
|
|
121
|
+
const separateTestDir = topDirs.has("test") || topDirs.has("tests") || topDirs.has("__tests__");
|
|
122
|
+
if (colocatedTests && !separateTestDir) {
|
|
123
|
+
findings.push({
|
|
124
|
+
category: "Testing",
|
|
125
|
+
description: "Tests are co-located with source files (*.test.ts next to *.ts). Keep this pattern -- don't create a separate test/ directory.",
|
|
126
|
+
confidence: "high",
|
|
127
|
+
discoverable: false,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else if (separateTestDir && !colocatedTests) {
|
|
131
|
+
findings.push({
|
|
132
|
+
category: "Testing",
|
|
133
|
+
description: "Tests live in a separate test/ directory, mirroring src/ structure. New tests go there, not next to source files.",
|
|
134
|
+
confidence: "high",
|
|
135
|
+
discoverable: false,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
layout: hasFeatureDirs
|
|
140
|
+
? "feature-based"
|
|
141
|
+
: hasLayerDirs
|
|
142
|
+
? "layer-based"
|
|
143
|
+
: undefined,
|
|
144
|
+
entryPoints,
|
|
145
|
+
directories,
|
|
146
|
+
findings,
|
|
147
|
+
};
|
|
148
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface Finding {
|
|
2
|
+
/** Category of the finding */
|
|
3
|
+
category: string;
|
|
4
|
+
/** Human-readable description of the finding */
|
|
5
|
+
description: string;
|
|
6
|
+
/** Why this matters -- context an agent needs */
|
|
7
|
+
rationale?: string;
|
|
8
|
+
/** File path or pattern that evidences this finding */
|
|
9
|
+
evidence?: string;
|
|
10
|
+
/** How confident we are this is a real convention, not coincidence */
|
|
11
|
+
confidence: "high" | "medium" | "low";
|
|
12
|
+
/** Is this discoverable by an agent reading the code? If yes, we filter it out */
|
|
13
|
+
discoverable: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface FrameworkDetection {
|
|
16
|
+
name: string;
|
|
17
|
+
version?: string;
|
|
18
|
+
findings: Finding[];
|
|
19
|
+
}
|
|
20
|
+
export interface StructureAnalysis {
|
|
21
|
+
/** Detected project layout pattern (e.g., "feature-based", "layer-based") */
|
|
22
|
+
layout?: string;
|
|
23
|
+
/** Entry points */
|
|
24
|
+
entryPoints: string[];
|
|
25
|
+
/** Key directories and their purposes */
|
|
26
|
+
directories: Record<string, string>;
|
|
27
|
+
/** Findings about structure that aren't obvious */
|
|
28
|
+
findings: Finding[];
|
|
29
|
+
}
|
|
30
|
+
export interface BuildCommands {
|
|
31
|
+
dev?: string;
|
|
32
|
+
build?: string;
|
|
33
|
+
test?: string;
|
|
34
|
+
lint?: string;
|
|
35
|
+
start?: string;
|
|
36
|
+
[key: string]: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
export interface ProjectScan {
|
|
39
|
+
dir: string;
|
|
40
|
+
files: string[];
|
|
41
|
+
languages: string[];
|
|
42
|
+
frameworks: string[];
|
|
43
|
+
commands: BuildCommands;
|
|
44
|
+
structure: StructureAnalysis;
|
|
45
|
+
findings: Finding[];
|
|
46
|
+
/** Files ranked by PageRank importance in the import graph */
|
|
47
|
+
rankedFiles?: {
|
|
48
|
+
file: string;
|
|
49
|
+
score: number;
|
|
50
|
+
}[];
|
|
51
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function writeOutput(dir: string, filename: string, content: string): Promise<void>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function writeOutput(dir, filename, content) {
|
|
4
|
+
const filePath = path.join(dir, filename);
|
|
5
|
+
const parentDir = path.dirname(filePath);
|
|
6
|
+
if (!fs.existsSync(parentDir)) {
|
|
7
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
10
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sourcebook",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Extract the conventions, constraints, and architectural truths your AI coding agents keep missing.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sourcebook": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx src/cli.ts",
|
|
12
|
+
"start": "node dist/cli.js",
|
|
13
|
+
"test": "vitest"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"claude",
|
|
18
|
+
"cursor",
|
|
19
|
+
"copilot",
|
|
20
|
+
"context",
|
|
21
|
+
"developer-tools",
|
|
22
|
+
"cli",
|
|
23
|
+
"code-analysis",
|
|
24
|
+
"llm",
|
|
25
|
+
"agents"
|
|
26
|
+
],
|
|
27
|
+
"author": "maroond",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"homepage": "https://sourcebook.run",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/maroondlabs/sourcebook.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/maroondlabs/sourcebook/issues"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"commander": "^13.0.0",
|
|
44
|
+
"glob": "^11.0.0",
|
|
45
|
+
"chalk": "^5.4.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"typescript": "^5.7.0",
|
|
49
|
+
"tsx": "^4.19.0",
|
|
50
|
+
"vitest": "^3.0.0",
|
|
51
|
+
"@types/node": "^22.0.0"
|
|
52
|
+
}
|
|
53
|
+
}
|