@latent-space-labs/open-auto-doc 0.3.9 → 0.4.1
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/dist/index.js
CHANGED
|
@@ -273,7 +273,8 @@ async function createAndPushDocsRepo(params) {
|
|
|
273
273
|
owner = selected;
|
|
274
274
|
}
|
|
275
275
|
const isOrg = owner !== username;
|
|
276
|
-
const
|
|
276
|
+
const slug = config?.projectName ? config.projectName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") : config?.repos?.[0]?.name;
|
|
277
|
+
const defaultName = slug ? `${slug}-docs` : "my-project-docs";
|
|
277
278
|
const repoName = await p3.text({
|
|
278
279
|
message: "Name for the docs GitHub repo:",
|
|
279
280
|
initialValue: defaultName,
|
|
@@ -1256,6 +1257,258 @@ ${EFFICIENCY_HINTS}`,
|
|
|
1256
1257
|
maxTurns: 25
|
|
1257
1258
|
});
|
|
1258
1259
|
}
|
|
1260
|
+
var configOutputSchema = {
|
|
1261
|
+
type: "object",
|
|
1262
|
+
properties: {
|
|
1263
|
+
configItems: {
|
|
1264
|
+
type: "array",
|
|
1265
|
+
items: {
|
|
1266
|
+
type: "object",
|
|
1267
|
+
properties: {
|
|
1268
|
+
name: { type: "string", description: "Config variable or option name" },
|
|
1269
|
+
source: { type: "string", description: "Where this config is defined (e.g. .env, config.ts, process.env)" },
|
|
1270
|
+
type: { type: "string", description: "Data type (string, number, boolean, etc.)" },
|
|
1271
|
+
defaultValue: { type: "string", description: "Default value if any" },
|
|
1272
|
+
required: { type: "boolean", description: "Whether this config is required" },
|
|
1273
|
+
description: { type: "string", description: "What this config option does" },
|
|
1274
|
+
category: { type: "string", description: "Grouping category (e.g. Database, Auth, Feature Flags)" }
|
|
1275
|
+
},
|
|
1276
|
+
required: ["name", "source", "type", "required", "description"]
|
|
1277
|
+
}
|
|
1278
|
+
},
|
|
1279
|
+
configFiles: {
|
|
1280
|
+
type: "array",
|
|
1281
|
+
items: { type: "string" },
|
|
1282
|
+
description: "List of configuration file paths found in the project"
|
|
1283
|
+
},
|
|
1284
|
+
environmentVariables: {
|
|
1285
|
+
type: "array",
|
|
1286
|
+
items: { type: "string" },
|
|
1287
|
+
description: "List of environment variable names used in the project"
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
required: ["configItems", "configFiles", "environmentVariables"]
|
|
1291
|
+
};
|
|
1292
|
+
async function analyzeConfiguration(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse) {
|
|
1293
|
+
const claudeMdContext = staticAnalysis.claudeMd.map((c) => c.content).join("\n\n");
|
|
1294
|
+
return runAgent({
|
|
1295
|
+
onAgentMessage,
|
|
1296
|
+
onToolUse,
|
|
1297
|
+
systemPrompt: `You are a configuration documentation expert. Your job is to find and document every configuration option, environment variable, and feature flag in a codebase.
|
|
1298
|
+
Your output must be valid JSON matching the provided schema. No markdown, no explanations outside the JSON.`,
|
|
1299
|
+
prompt: `Analyze this codebase and extract all configuration options and environment variables.
|
|
1300
|
+
|
|
1301
|
+
## Architecture Context
|
|
1302
|
+
${architecture.summary}
|
|
1303
|
+
Tech Stack: ${architecture.techStack.join(", ")}
|
|
1304
|
+
${claudeMdContext ? `
|
|
1305
|
+
## CLAUDE.md Context
|
|
1306
|
+
${claudeMdContext}
|
|
1307
|
+
` : ""}
|
|
1308
|
+
|
|
1309
|
+
## Instructions
|
|
1310
|
+
1. Use Glob to find config files: \`.env*\`, \`config/**\`, \`**/config.*\`, \`**/*.config.*\`, \`**/settings.*\`, \`**/.env.example\`
|
|
1311
|
+
2. Use Grep to search for \`process.env\`, \`os.environ\`, \`ConfigService\`, \`@Value\`, \`getenv\`, \`ENV[\`, \`config.\` references
|
|
1312
|
+
3. Use Read to examine config files, .env.example files, and files that reference environment variables
|
|
1313
|
+
|
|
1314
|
+
For each configuration item:
|
|
1315
|
+
- Identify the variable name and where it's defined
|
|
1316
|
+
- Determine its type and default value (if any)
|
|
1317
|
+
- Note whether it's required for the app to function
|
|
1318
|
+
- Write a clear description of what it controls
|
|
1319
|
+
- Assign a category (Database, Auth, API Keys, Feature Flags, Server, Logging, etc.)
|
|
1320
|
+
|
|
1321
|
+
Also list all config files found and all environment variable names.
|
|
1322
|
+
If no configuration is found, return empty arrays.
|
|
1323
|
+
${EFFICIENCY_HINTS}`,
|
|
1324
|
+
cwd: repoPath,
|
|
1325
|
+
apiKey,
|
|
1326
|
+
model,
|
|
1327
|
+
outputSchema: configOutputSchema,
|
|
1328
|
+
maxTurns: 30
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
var businessLogicSchema = {
|
|
1332
|
+
type: "object",
|
|
1333
|
+
properties: {
|
|
1334
|
+
domainConcepts: {
|
|
1335
|
+
type: "array",
|
|
1336
|
+
items: {
|
|
1337
|
+
type: "object",
|
|
1338
|
+
properties: {
|
|
1339
|
+
name: { type: "string", description: "Domain concept name (e.g. User, Order, Subscription)" },
|
|
1340
|
+
description: { type: "string", description: "What this concept represents in the domain" },
|
|
1341
|
+
relatedFiles: { type: "array", items: { type: "string" }, description: "Key files implementing this concept" }
|
|
1342
|
+
},
|
|
1343
|
+
required: ["name", "description", "relatedFiles"]
|
|
1344
|
+
}
|
|
1345
|
+
},
|
|
1346
|
+
businessRules: {
|
|
1347
|
+
type: "array",
|
|
1348
|
+
items: {
|
|
1349
|
+
type: "object",
|
|
1350
|
+
properties: {
|
|
1351
|
+
name: { type: "string", description: "Short name for the business rule" },
|
|
1352
|
+
description: { type: "string", description: "What the rule enforces or ensures" },
|
|
1353
|
+
sourceFiles: { type: "array", items: { type: "string" }, description: "Files containing this rule" },
|
|
1354
|
+
category: { type: "string", description: "Category (Validation, Authorization, Pricing, Workflow, etc.)" }
|
|
1355
|
+
},
|
|
1356
|
+
required: ["name", "description", "sourceFiles"]
|
|
1357
|
+
}
|
|
1358
|
+
},
|
|
1359
|
+
workflows: {
|
|
1360
|
+
type: "array",
|
|
1361
|
+
items: {
|
|
1362
|
+
type: "object",
|
|
1363
|
+
properties: {
|
|
1364
|
+
name: { type: "string", description: "Workflow name" },
|
|
1365
|
+
description: { type: "string", description: "What this workflow accomplishes" },
|
|
1366
|
+
steps: { type: "array", items: { type: "string" }, description: "Ordered steps in the workflow" },
|
|
1367
|
+
diagram: {
|
|
1368
|
+
type: "object",
|
|
1369
|
+
properties: {
|
|
1370
|
+
id: { type: "string" },
|
|
1371
|
+
title: { type: "string" },
|
|
1372
|
+
description: { type: "string" },
|
|
1373
|
+
mermaidSyntax: { type: "string" }
|
|
1374
|
+
},
|
|
1375
|
+
required: ["id", "title", "description", "mermaidSyntax"]
|
|
1376
|
+
}
|
|
1377
|
+
},
|
|
1378
|
+
required: ["name", "description", "steps"]
|
|
1379
|
+
}
|
|
1380
|
+
},
|
|
1381
|
+
keyInvariants: {
|
|
1382
|
+
type: "array",
|
|
1383
|
+
items: { type: "string" },
|
|
1384
|
+
description: "Key invariants or constraints the system maintains"
|
|
1385
|
+
}
|
|
1386
|
+
},
|
|
1387
|
+
required: ["domainConcepts", "businessRules", "workflows", "keyInvariants"]
|
|
1388
|
+
};
|
|
1389
|
+
async function analyzeBusinessLogic(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse) {
|
|
1390
|
+
const claudeMdContext = staticAnalysis.claudeMd.map((c) => c.content).join("\n\n");
|
|
1391
|
+
return runAgent({
|
|
1392
|
+
onAgentMessage,
|
|
1393
|
+
onToolUse,
|
|
1394
|
+
systemPrompt: `You are a domain analysis expert. Your job is to extract the "why" behind the code \u2014 the business rules, domain concepts, workflows, and invariants that shape how the software behaves.
|
|
1395
|
+
Focus on rules and logic, not implementation details. Your output must be valid JSON matching the provided schema. No markdown, no explanations outside the JSON.`,
|
|
1396
|
+
prompt: `Analyze this codebase and extract business logic, domain concepts, and workflows.
|
|
1397
|
+
|
|
1398
|
+
## Architecture Context
|
|
1399
|
+
${architecture.summary}
|
|
1400
|
+
Tech Stack: ${architecture.techStack.join(", ")}
|
|
1401
|
+
Modules: ${architecture.modules.map((m) => `${m.name}: ${m.description}`).join("\n")}
|
|
1402
|
+
${claudeMdContext ? `
|
|
1403
|
+
## CLAUDE.md Context
|
|
1404
|
+
${claudeMdContext}
|
|
1405
|
+
` : ""}
|
|
1406
|
+
|
|
1407
|
+
## Instructions
|
|
1408
|
+
1. Use Glob to find service, domain, and business logic files: \`**/services/**\`, \`**/domain/**\`, \`**/rules/**\`, \`**/validators/**\`, \`**/policies/**\`, \`**/workflows/**\`, \`**/middleware/**\`
|
|
1409
|
+
2. Use Grep to find validation functions, guard clauses, authorization checks, state transitions, business rule comments
|
|
1410
|
+
3. Use Read to examine key files and understand the domain logic
|
|
1411
|
+
|
|
1412
|
+
**Domain Concepts**: Identify the core entities/concepts of the domain (not just data models, but the ideas the software is built around). Explain what each represents.
|
|
1413
|
+
|
|
1414
|
+
**Business Rules**: Find rules encoded in the logic \u2014 validation rules, authorization policies, pricing logic, rate limits, state machine transitions. For each rule, explain what it enforces.
|
|
1415
|
+
|
|
1416
|
+
**Workflows**: Identify multi-step processes (e.g. user registration flow, order processing, deployment pipeline). List the steps and optionally generate a Mermaid \`stateDiagram-v2\` for the most important workflow.
|
|
1417
|
+
|
|
1418
|
+
**Key Invariants**: List constraints the system must always maintain (e.g. "every order must have a valid customer", "API keys must be hashed before storage").
|
|
1419
|
+
|
|
1420
|
+
If this is a library without clear business logic, focus on the design rules and constraints that guide how the library is used.
|
|
1421
|
+
${EFFICIENCY_HINTS}`,
|
|
1422
|
+
cwd: repoPath,
|
|
1423
|
+
apiKey,
|
|
1424
|
+
model,
|
|
1425
|
+
outputSchema: businessLogicSchema,
|
|
1426
|
+
maxTurns: 35
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
var errorHandlingSchema = {
|
|
1430
|
+
type: "object",
|
|
1431
|
+
properties: {
|
|
1432
|
+
errorCodes: {
|
|
1433
|
+
type: "array",
|
|
1434
|
+
items: {
|
|
1435
|
+
type: "object",
|
|
1436
|
+
properties: {
|
|
1437
|
+
code: { type: "string", description: "Error code identifier" },
|
|
1438
|
+
httpStatus: { type: "number", description: "Associated HTTP status code if applicable" },
|
|
1439
|
+
message: { type: "string", description: "Error message shown to users" },
|
|
1440
|
+
description: { type: "string", description: "Detailed explanation of when and why this error occurs" },
|
|
1441
|
+
sourceFile: { type: "string", description: "File where this error is defined" }
|
|
1442
|
+
},
|
|
1443
|
+
required: ["code", "message", "description"]
|
|
1444
|
+
}
|
|
1445
|
+
},
|
|
1446
|
+
commonErrors: {
|
|
1447
|
+
type: "array",
|
|
1448
|
+
items: {
|
|
1449
|
+
type: "object",
|
|
1450
|
+
properties: {
|
|
1451
|
+
error: { type: "string", description: "Error name or symptom" },
|
|
1452
|
+
cause: { type: "string", description: "What causes this error" },
|
|
1453
|
+
solution: { type: "string", description: "How to fix or resolve this error" },
|
|
1454
|
+
category: { type: "string", description: "Category (Setup, Runtime, Configuration, Authentication, etc.)" }
|
|
1455
|
+
},
|
|
1456
|
+
required: ["error", "cause", "solution"]
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1459
|
+
errorClasses: {
|
|
1460
|
+
type: "array",
|
|
1461
|
+
items: { type: "string" },
|
|
1462
|
+
description: "Custom error class names defined in the codebase"
|
|
1463
|
+
},
|
|
1464
|
+
debuggingTips: {
|
|
1465
|
+
type: "array",
|
|
1466
|
+
items: { type: "string" },
|
|
1467
|
+
description: "General debugging tips and techniques for this project"
|
|
1468
|
+
}
|
|
1469
|
+
},
|
|
1470
|
+
required: ["errorCodes", "commonErrors", "errorClasses", "debuggingTips"]
|
|
1471
|
+
};
|
|
1472
|
+
async function analyzeErrorHandling(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse) {
|
|
1473
|
+
const claudeMdContext = staticAnalysis.claudeMd.map((c) => c.content).join("\n\n");
|
|
1474
|
+
return runAgent({
|
|
1475
|
+
onAgentMessage,
|
|
1476
|
+
onToolUse,
|
|
1477
|
+
systemPrompt: `You are an error handling documentation expert. Your job is to find and document all error patterns, error codes, custom error classes, and common failure modes in a codebase. Write troubleshooting guidance that helps developers diagnose and fix issues.
|
|
1478
|
+
Your output must be valid JSON matching the provided schema. No markdown, no explanations outside the JSON.`,
|
|
1479
|
+
prompt: `Analyze this codebase and document its error handling patterns and common errors.
|
|
1480
|
+
|
|
1481
|
+
## Architecture Context
|
|
1482
|
+
${architecture.summary}
|
|
1483
|
+
Tech Stack: ${architecture.techStack.join(", ")}
|
|
1484
|
+
${claudeMdContext ? `
|
|
1485
|
+
## CLAUDE.md Context
|
|
1486
|
+
${claudeMdContext}
|
|
1487
|
+
` : ""}
|
|
1488
|
+
|
|
1489
|
+
## Instructions
|
|
1490
|
+
1. Use Grep to find custom Error classes: \`extends Error\`, \`extends BaseError\`, \`class.*Error\`, \`class.*Exception\`
|
|
1491
|
+
2. Use Grep to find error code constants: \`ERROR_\`, \`ERR_\`, \`error_code\`, HTTP status codes (\`400\`, \`401\`, \`403\`, \`404\`, \`500\`)
|
|
1492
|
+
3. Use Grep to find error handling patterns: \`try.*catch\`, error middleware, \`.catch(\`, \`onError\`, \`handleError\`
|
|
1493
|
+
4. Use Read to examine error handling files, middleware, and error class definitions
|
|
1494
|
+
|
|
1495
|
+
**Error Codes**: Document any defined error codes with their HTTP status (if applicable), user-facing message, and a detailed description of when they occur.
|
|
1496
|
+
|
|
1497
|
+
**Common Errors**: Identify errors developers commonly encounter when setting up, configuring, or using this project. For each, explain the cause and provide a clear solution.
|
|
1498
|
+
|
|
1499
|
+
**Error Classes**: List all custom error/exception classes defined in the project.
|
|
1500
|
+
|
|
1501
|
+
**Debugging Tips**: Write 3-8 practical debugging tips specific to this project (e.g. "Enable verbose logging by setting LOG_LEVEL=debug", "Check the /health endpoint for service status").
|
|
1502
|
+
|
|
1503
|
+
If no error handling patterns are found, return empty arrays with a few general debugging tips.
|
|
1504
|
+
${EFFICIENCY_HINTS}`,
|
|
1505
|
+
cwd: repoPath,
|
|
1506
|
+
apiKey,
|
|
1507
|
+
model,
|
|
1508
|
+
outputSchema: errorHandlingSchema,
|
|
1509
|
+
maxTurns: 30
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1259
1512
|
var guideOutputSchema = {
|
|
1260
1513
|
type: "object",
|
|
1261
1514
|
properties: {
|
|
@@ -1417,6 +1670,25 @@ var SECTION_PATTERNS = {
|
|
|
1417
1670
|
/setup/i,
|
|
1418
1671
|
/contributing/i,
|
|
1419
1672
|
/getting.?started/i
|
|
1673
|
+
],
|
|
1674
|
+
configuration: [
|
|
1675
|
+
/\.env/i,
|
|
1676
|
+
/config\//i,
|
|
1677
|
+
/settings\//i,
|
|
1678
|
+
/\.config\.(ts|js|mjs|cjs)$/
|
|
1679
|
+
],
|
|
1680
|
+
businessLogic: [
|
|
1681
|
+
/services?\//i,
|
|
1682
|
+
/domain\//i,
|
|
1683
|
+
/rules?\//i,
|
|
1684
|
+
/validators?\//i,
|
|
1685
|
+
/policies?\//i,
|
|
1686
|
+
/workflows?\//i
|
|
1687
|
+
],
|
|
1688
|
+
errorHandling: [
|
|
1689
|
+
/errors?\//i,
|
|
1690
|
+
/exceptions?\//i,
|
|
1691
|
+
/middleware\//i
|
|
1420
1692
|
]
|
|
1421
1693
|
};
|
|
1422
1694
|
function computeDiff(repoPath, fromSha) {
|
|
@@ -1448,7 +1720,10 @@ function classifyChanges(entries, staticAnalysis) {
|
|
|
1448
1720
|
"components",
|
|
1449
1721
|
"dataModels",
|
|
1450
1722
|
"features",
|
|
1451
|
-
"gettingStarted"
|
|
1723
|
+
"gettingStarted",
|
|
1724
|
+
"configuration",
|
|
1725
|
+
"businessLogic",
|
|
1726
|
+
"errorHandling"
|
|
1452
1727
|
]),
|
|
1453
1728
|
fullRegenRequired: true
|
|
1454
1729
|
};
|
|
@@ -1510,21 +1785,30 @@ async function analyzeRepository(options) {
|
|
|
1510
1785
|
onProgress?.("architecture", "Analyzing architecture with AI...");
|
|
1511
1786
|
const architecture = await analyzeArchitecture(repoPath, staticAnalysis, apiKey, model, onAgentMessage, onToolUse);
|
|
1512
1787
|
onProgress?.("architecture", `Identified ${architecture.modules.length} modules, ${architecture.diagrams.length} diagrams`);
|
|
1513
|
-
onProgress?.("details", "Analyzing APIs, components, data models,
|
|
1514
|
-
const [apiSettled, compSettled, modelSettled, featuresSettled] = await Promise.allSettled([
|
|
1788
|
+
onProgress?.("details", "Analyzing APIs, components, data models, features, config, business logic, errors...");
|
|
1789
|
+
const [apiSettled, compSettled, modelSettled, featuresSettled, configSettled, bizLogicSettled, errorSettled] = await Promise.allSettled([
|
|
1515
1790
|
analyzeApiEndpoints(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse),
|
|
1516
1791
|
analyzeComponents(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse),
|
|
1517
1792
|
analyzeDataModels(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse),
|
|
1518
|
-
analyzeFeatures(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse)
|
|
1793
|
+
analyzeFeatures(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse),
|
|
1794
|
+
analyzeConfiguration(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse),
|
|
1795
|
+
analyzeBusinessLogic(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse),
|
|
1796
|
+
analyzeErrorHandling(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse)
|
|
1519
1797
|
]);
|
|
1520
1798
|
const apiResult = apiSettled.status === "fulfilled" ? apiSettled.value : null;
|
|
1521
1799
|
const components = compSettled.status === "fulfilled" ? compSettled.value : [];
|
|
1522
1800
|
const modelResult = modelSettled.status === "fulfilled" ? modelSettled.value : null;
|
|
1523
1801
|
const features = featuresSettled.status === "fulfilled" ? featuresSettled.value : null;
|
|
1802
|
+
const configuration = configSettled.status === "fulfilled" ? configSettled.value : null;
|
|
1803
|
+
const businessLogic = bizLogicSettled.status === "fulfilled" ? bizLogicSettled.value : null;
|
|
1804
|
+
const errorHandling = errorSettled.status === "fulfilled" ? errorSettled.value : null;
|
|
1524
1805
|
if (apiSettled.status === "rejected") onProgress?.("details", `API analysis failed (non-fatal): ${apiSettled.reason}`);
|
|
1525
1806
|
if (compSettled.status === "rejected") onProgress?.("details", `Component analysis failed (non-fatal): ${compSettled.reason}`);
|
|
1526
1807
|
if (modelSettled.status === "rejected") onProgress?.("details", `Data model analysis failed (non-fatal): ${modelSettled.reason}`);
|
|
1527
1808
|
if (featuresSettled.status === "rejected") onProgress?.("details", `Features analysis failed (non-fatal): ${featuresSettled.reason}`);
|
|
1809
|
+
if (configSettled.status === "rejected") onProgress?.("details", `Configuration analysis failed (non-fatal): ${configSettled.reason}`);
|
|
1810
|
+
if (bizLogicSettled.status === "rejected") onProgress?.("details", `Business logic analysis failed (non-fatal): ${bizLogicSettled.reason}`);
|
|
1811
|
+
if (errorSettled.status === "rejected") onProgress?.("details", `Error handling analysis failed (non-fatal): ${errorSettled.reason}`);
|
|
1528
1812
|
const apiEndpoints = apiResult?.endpoints ?? [];
|
|
1529
1813
|
const dataModels = modelResult?.models ?? [];
|
|
1530
1814
|
const diagrams = [
|
|
@@ -1532,6 +1816,11 @@ async function analyzeRepository(options) {
|
|
|
1532
1816
|
...apiResult?.diagram ? [apiResult.diagram] : [],
|
|
1533
1817
|
...modelResult?.diagram ? [modelResult.diagram] : []
|
|
1534
1818
|
];
|
|
1819
|
+
if (businessLogic) {
|
|
1820
|
+
for (const workflow of businessLogic.workflows) {
|
|
1821
|
+
if (workflow.diagram) diagrams.push(workflow.diagram);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1535
1824
|
onProgress?.(
|
|
1536
1825
|
"details",
|
|
1537
1826
|
`Found ${apiEndpoints.length} endpoints, ${components.length} components, ${dataModels.length} models, ${diagrams.length} diagrams${features ? `, ${features.features.length} features` : ""}`
|
|
@@ -1557,7 +1846,10 @@ async function analyzeRepository(options) {
|
|
|
1557
1846
|
components,
|
|
1558
1847
|
dataModels,
|
|
1559
1848
|
gettingStarted,
|
|
1560
|
-
diagrams
|
|
1849
|
+
diagrams,
|
|
1850
|
+
configuration,
|
|
1851
|
+
businessLogic,
|
|
1852
|
+
errorHandling
|
|
1561
1853
|
};
|
|
1562
1854
|
}
|
|
1563
1855
|
async function analyzeRepositoryIncremental(options) {
|
|
@@ -1619,8 +1911,12 @@ async function analyzeRepositoryIncremental(options) {
|
|
|
1619
1911
|
let components = previousResult.components;
|
|
1620
1912
|
let dataModels = previousResult.dataModels;
|
|
1621
1913
|
let features = previousResult.features;
|
|
1914
|
+
let configuration = previousResult.configuration;
|
|
1915
|
+
let businessLogic = previousResult.businessLogic;
|
|
1916
|
+
let errorHandling = previousResult.errorHandling;
|
|
1622
1917
|
let apiDiagram;
|
|
1623
1918
|
let modelDiagram;
|
|
1919
|
+
const workflowDiagrams = [];
|
|
1624
1920
|
const prevArchDiagramIds = new Set(previousResult.architecture.diagrams.map((d) => d.id));
|
|
1625
1921
|
const prevDetailDiagrams = previousResult.diagrams.filter((d) => !prevArchDiagramIds.has(d.id));
|
|
1626
1922
|
const promises = [];
|
|
@@ -1670,6 +1966,42 @@ async function analyzeRepositoryIncremental(options) {
|
|
|
1670
1966
|
})
|
|
1671
1967
|
);
|
|
1672
1968
|
}
|
|
1969
|
+
if (affected.has("configuration")) {
|
|
1970
|
+
promises.push(
|
|
1971
|
+
analyzeConfiguration(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse).then(
|
|
1972
|
+
(result) => {
|
|
1973
|
+
configuration = result;
|
|
1974
|
+
}
|
|
1975
|
+
).catch((err) => {
|
|
1976
|
+
onProgress?.("details", `Configuration re-analysis failed (non-fatal): ${err}`);
|
|
1977
|
+
})
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
if (affected.has("businessLogic")) {
|
|
1981
|
+
promises.push(
|
|
1982
|
+
analyzeBusinessLogic(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse).then(
|
|
1983
|
+
(result) => {
|
|
1984
|
+
businessLogic = result;
|
|
1985
|
+
for (const workflow of result.workflows) {
|
|
1986
|
+
if (workflow.diagram) workflowDiagrams.push(workflow.diagram);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
).catch((err) => {
|
|
1990
|
+
onProgress?.("details", `Business logic re-analysis failed (non-fatal): ${err}`);
|
|
1991
|
+
})
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1994
|
+
if (affected.has("errorHandling")) {
|
|
1995
|
+
promises.push(
|
|
1996
|
+
analyzeErrorHandling(repoPath, staticAnalysis, architecture, apiKey, model, onAgentMessage, onToolUse).then(
|
|
1997
|
+
(result) => {
|
|
1998
|
+
errorHandling = result;
|
|
1999
|
+
}
|
|
2000
|
+
).catch((err) => {
|
|
2001
|
+
onProgress?.("details", `Error handling re-analysis failed (non-fatal): ${err}`);
|
|
2002
|
+
})
|
|
2003
|
+
);
|
|
2004
|
+
}
|
|
1673
2005
|
if (promises.length > 0) {
|
|
1674
2006
|
onProgress?.("details", "Re-analyzing affected sections...");
|
|
1675
2007
|
await Promise.all(promises);
|
|
@@ -1687,6 +2019,13 @@ async function analyzeRepositoryIncremental(options) {
|
|
|
1687
2019
|
const prevModelDiag = prevDetailDiagrams.find((d) => d.id.includes("model") || d.id.includes("er"));
|
|
1688
2020
|
if (prevModelDiag) diagrams.push(prevModelDiag);
|
|
1689
2021
|
}
|
|
2022
|
+
if (affected.has("businessLogic")) {
|
|
2023
|
+
diagrams.push(...workflowDiagrams);
|
|
2024
|
+
} else if (businessLogic) {
|
|
2025
|
+
for (const workflow of businessLogic.workflows) {
|
|
2026
|
+
if (workflow.diagram) diagrams.push(workflow.diagram);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
1690
2029
|
let gettingStarted = previousResult.gettingStarted;
|
|
1691
2030
|
if (affected.has("gettingStarted")) {
|
|
1692
2031
|
onProgress?.("synthesis", "Re-writing getting started guide...");
|
|
@@ -1711,7 +2050,10 @@ async function analyzeRepositoryIncremental(options) {
|
|
|
1711
2050
|
components,
|
|
1712
2051
|
dataModels,
|
|
1713
2052
|
gettingStarted,
|
|
1714
|
-
diagrams
|
|
2053
|
+
diagrams,
|
|
2054
|
+
configuration,
|
|
2055
|
+
businessLogic,
|
|
2056
|
+
errorHandling
|
|
1715
2057
|
};
|
|
1716
2058
|
}
|
|
1717
2059
|
var crossRepoSchema = {
|
|
@@ -1883,7 +2225,7 @@ Only edit files inside content/docs/.`,
|
|
|
1883
2225
|
maxTurns: 35
|
|
1884
2226
|
});
|
|
1885
2227
|
}
|
|
1886
|
-
var CACHE_VERSION =
|
|
2228
|
+
var CACHE_VERSION = 3;
|
|
1887
2229
|
function slugify(name) {
|
|
1888
2230
|
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1889
2231
|
}
|
|
@@ -1912,6 +2254,137 @@ function loadCache(cacheDir, repoSlug) {
|
|
|
1912
2254
|
return null;
|
|
1913
2255
|
}
|
|
1914
2256
|
}
|
|
2257
|
+
function computeChangelog(previous, current, fromCommit, toCommit) {
|
|
2258
|
+
const added = [];
|
|
2259
|
+
const removed = [];
|
|
2260
|
+
const modified = [];
|
|
2261
|
+
diffByKey(
|
|
2262
|
+
previous.apiEndpoints,
|
|
2263
|
+
current.apiEndpoints,
|
|
2264
|
+
(e) => `${e.method} ${e.path}`,
|
|
2265
|
+
(e) => e.description,
|
|
2266
|
+
"API Endpoints",
|
|
2267
|
+
added,
|
|
2268
|
+
removed,
|
|
2269
|
+
modified
|
|
2270
|
+
);
|
|
2271
|
+
diffByKey(
|
|
2272
|
+
previous.components,
|
|
2273
|
+
current.components,
|
|
2274
|
+
(c) => c.name,
|
|
2275
|
+
(c) => c.description,
|
|
2276
|
+
"Components",
|
|
2277
|
+
added,
|
|
2278
|
+
removed,
|
|
2279
|
+
modified
|
|
2280
|
+
);
|
|
2281
|
+
diffByKey(
|
|
2282
|
+
previous.dataModels,
|
|
2283
|
+
current.dataModels,
|
|
2284
|
+
(m) => m.name,
|
|
2285
|
+
(m) => m.description,
|
|
2286
|
+
"Data Models",
|
|
2287
|
+
added,
|
|
2288
|
+
removed,
|
|
2289
|
+
modified
|
|
2290
|
+
);
|
|
2291
|
+
if (previous.configuration && current.configuration) {
|
|
2292
|
+
diffByKey(
|
|
2293
|
+
previous.configuration.configItems,
|
|
2294
|
+
current.configuration.configItems,
|
|
2295
|
+
(c) => c.name,
|
|
2296
|
+
(c) => c.description,
|
|
2297
|
+
"Configuration",
|
|
2298
|
+
added,
|
|
2299
|
+
removed,
|
|
2300
|
+
modified
|
|
2301
|
+
);
|
|
2302
|
+
} else if (current.configuration) {
|
|
2303
|
+
for (const item of current.configuration.configItems) {
|
|
2304
|
+
added.push({ name: item.name, description: item.description, section: "Configuration" });
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
if (previous.errorHandling && current.errorHandling) {
|
|
2308
|
+
diffByKey(
|
|
2309
|
+
previous.errorHandling.errorCodes,
|
|
2310
|
+
current.errorHandling.errorCodes,
|
|
2311
|
+
(e) => e.code,
|
|
2312
|
+
(e) => e.description,
|
|
2313
|
+
"Error Codes",
|
|
2314
|
+
added,
|
|
2315
|
+
removed,
|
|
2316
|
+
modified
|
|
2317
|
+
);
|
|
2318
|
+
} else if (current.errorHandling) {
|
|
2319
|
+
for (const item of current.errorHandling.errorCodes) {
|
|
2320
|
+
added.push({ name: item.code, description: item.description, section: "Error Codes" });
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
if (previous.businessLogic && current.businessLogic) {
|
|
2324
|
+
diffByKey(
|
|
2325
|
+
previous.businessLogic.domainConcepts,
|
|
2326
|
+
current.businessLogic.domainConcepts,
|
|
2327
|
+
(c) => c.name,
|
|
2328
|
+
(c) => c.description,
|
|
2329
|
+
"Domain Concepts",
|
|
2330
|
+
added,
|
|
2331
|
+
removed,
|
|
2332
|
+
modified
|
|
2333
|
+
);
|
|
2334
|
+
} else if (current.businessLogic) {
|
|
2335
|
+
for (const item of current.businessLogic.domainConcepts) {
|
|
2336
|
+
added.push({ name: item.name, description: item.description, section: "Domain Concepts" });
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
if (previous.features && current.features) {
|
|
2340
|
+
diffByKey(
|
|
2341
|
+
previous.features.features,
|
|
2342
|
+
current.features.features,
|
|
2343
|
+
(f) => f.name,
|
|
2344
|
+
(f) => f.description,
|
|
2345
|
+
"Features",
|
|
2346
|
+
added,
|
|
2347
|
+
removed,
|
|
2348
|
+
modified
|
|
2349
|
+
);
|
|
2350
|
+
} else if (current.features) {
|
|
2351
|
+
for (const item of current.features.features) {
|
|
2352
|
+
added.push({ name: item.name, description: item.description, section: "Features" });
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
const totalChanges = added.length + removed.length + modified.length;
|
|
2356
|
+
const parts = [];
|
|
2357
|
+
if (added.length > 0) parts.push(`${added.length} added`);
|
|
2358
|
+
if (removed.length > 0) parts.push(`${removed.length} removed`);
|
|
2359
|
+
if (modified.length > 0) parts.push(`${modified.length} modified`);
|
|
2360
|
+
const summary = totalChanges === 0 ? "No documentation changes detected." : `${totalChanges} changes: ${parts.join(", ")}.`;
|
|
2361
|
+
return {
|
|
2362
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2363
|
+
fromCommit,
|
|
2364
|
+
toCommit,
|
|
2365
|
+
added,
|
|
2366
|
+
removed,
|
|
2367
|
+
modified,
|
|
2368
|
+
summary
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
function diffByKey(previous, current, getKey, getDescription, section, added, removed, modified) {
|
|
2372
|
+
const prevMap = new Map(previous.map((item) => [getKey(item), item]));
|
|
2373
|
+
const currMap = new Map(current.map((item) => [getKey(item), item]));
|
|
2374
|
+
for (const [key, item] of currMap) {
|
|
2375
|
+
const prev = prevMap.get(key);
|
|
2376
|
+
if (!prev) {
|
|
2377
|
+
added.push({ name: key, description: getDescription(item), section });
|
|
2378
|
+
} else if (getDescription(prev) !== getDescription(item)) {
|
|
2379
|
+
modified.push({ name: key, description: getDescription(item), section });
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
for (const [key, item] of prevMap) {
|
|
2383
|
+
if (!currMap.has(key)) {
|
|
2384
|
+
removed.push({ name: key, description: getDescription(item), section });
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
1915
2388
|
|
|
1916
2389
|
// src/actions/build-check.ts
|
|
1917
2390
|
function runBuild(docsDir) {
|
|
@@ -2536,7 +3009,7 @@ function loadTemplates() {
|
|
|
2536
3009
|
`Template directory not found. __dirname=${__dirname}, searched: ${templateDirs.join(", ")}`
|
|
2537
3010
|
);
|
|
2538
3011
|
}
|
|
2539
|
-
const templateFiles = ["overview", "features", "getting-started", "architecture", "api-endpoint", "component", "data-model", "diagrams", "cross-repo"];
|
|
3012
|
+
const templateFiles = ["overview", "features", "getting-started", "architecture", "api-endpoint", "component", "data-model", "diagrams", "cross-repo", "configuration", "business-logic", "error-handling", "changelog"];
|
|
2540
3013
|
for (const name of templateFiles) {
|
|
2541
3014
|
const filePath = path23.join(templateDir, `${name}.hbs`);
|
|
2542
3015
|
if (fs23.existsSync(filePath)) {
|
|
@@ -2548,11 +3021,12 @@ function loadTemplates() {
|
|
|
2548
3021
|
}
|
|
2549
3022
|
templatesLoaded = true;
|
|
2550
3023
|
}
|
|
2551
|
-
async function writeContent(contentDir, results, crossRepo) {
|
|
3024
|
+
async function writeContent(contentDir, results, crossRepo, changelogs) {
|
|
2552
3025
|
loadTemplates();
|
|
2553
3026
|
await fs23.ensureDir(contentDir);
|
|
2554
3027
|
if (results.length === 1) {
|
|
2555
|
-
|
|
3028
|
+
const changelog = changelogs?.get(results[0].repoName);
|
|
3029
|
+
await writeRepoContent(contentDir, results[0], changelog);
|
|
2556
3030
|
} else {
|
|
2557
3031
|
await writeMultiRepoIndex(contentDir, results);
|
|
2558
3032
|
if (crossRepo && templates["cross-repo"]) {
|
|
@@ -2563,7 +3037,8 @@ async function writeContent(contentDir, results, crossRepo) {
|
|
|
2563
3037
|
}
|
|
2564
3038
|
for (const result of results) {
|
|
2565
3039
|
const repoDir = path23.join(contentDir, slugify2(result.repoName));
|
|
2566
|
-
|
|
3040
|
+
const changelog = changelogs?.get(result.repoName);
|
|
3041
|
+
await writeRepoContent(repoDir, result, changelog);
|
|
2567
3042
|
}
|
|
2568
3043
|
}
|
|
2569
3044
|
}
|
|
@@ -2595,7 +3070,7 @@ ${repoCards}
|
|
|
2595
3070
|
`;
|
|
2596
3071
|
await fs23.writeFile(path23.join(contentDir, "index.mdx"), escapeMdxOutsideCode(mdx));
|
|
2597
3072
|
}
|
|
2598
|
-
async function writeRepoContent(dir, result) {
|
|
3073
|
+
async function writeRepoContent(dir, result, changelog) {
|
|
2599
3074
|
await fs23.ensureDir(dir);
|
|
2600
3075
|
const safeResult = result;
|
|
2601
3076
|
if (templates["overview"]) {
|
|
@@ -2661,6 +3136,62 @@ async function writeRepoContent(dir, result) {
|
|
|
2661
3136
|
renderTemplate("diagrams", safeResult)
|
|
2662
3137
|
);
|
|
2663
3138
|
}
|
|
3139
|
+
if (safeResult.configuration && safeResult.configuration.configItems.length > 0 && templates["configuration"]) {
|
|
3140
|
+
const configByCategory = {};
|
|
3141
|
+
for (const item of safeResult.configuration.configItems) {
|
|
3142
|
+
const cat = item.category || "General";
|
|
3143
|
+
if (!configByCategory[cat]) configByCategory[cat] = [];
|
|
3144
|
+
configByCategory[cat].push(item);
|
|
3145
|
+
}
|
|
3146
|
+
await fs23.writeFile(
|
|
3147
|
+
path23.join(dir, "configuration.mdx"),
|
|
3148
|
+
renderTemplate("configuration", {
|
|
3149
|
+
...safeResult,
|
|
3150
|
+
configByCategory,
|
|
3151
|
+
configFiles: safeResult.configuration.configFiles,
|
|
3152
|
+
environmentVariables: safeResult.configuration.environmentVariables
|
|
3153
|
+
})
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
3156
|
+
if (safeResult.businessLogic && templates["business-logic"]) {
|
|
3157
|
+
const bl = safeResult.businessLogic;
|
|
3158
|
+
if (bl.domainConcepts.length > 0 || bl.businessRules.length > 0 || bl.workflows.length > 0) {
|
|
3159
|
+
await fs23.writeFile(
|
|
3160
|
+
path23.join(dir, "business-logic.mdx"),
|
|
3161
|
+
renderTemplate("business-logic", {
|
|
3162
|
+
...safeResult,
|
|
3163
|
+
domainConcepts: bl.domainConcepts,
|
|
3164
|
+
businessRules: bl.businessRules,
|
|
3165
|
+
workflows: bl.workflows,
|
|
3166
|
+
keyInvariants: bl.keyInvariants
|
|
3167
|
+
})
|
|
3168
|
+
);
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
if (safeResult.errorHandling && templates["error-handling"]) {
|
|
3172
|
+
const eh = safeResult.errorHandling;
|
|
3173
|
+
if (eh.errorCodes.length > 0 || eh.commonErrors.length > 0) {
|
|
3174
|
+
await fs23.writeFile(
|
|
3175
|
+
path23.join(dir, "error-handling.mdx"),
|
|
3176
|
+
renderTemplate("error-handling", {
|
|
3177
|
+
...safeResult,
|
|
3178
|
+
errorCodes: eh.errorCodes,
|
|
3179
|
+
commonErrors: eh.commonErrors,
|
|
3180
|
+
errorClasses: eh.errorClasses,
|
|
3181
|
+
debuggingTips: eh.debuggingTips
|
|
3182
|
+
})
|
|
3183
|
+
);
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
if (changelog && templates["changelog"]) {
|
|
3187
|
+
const hasChanges = changelog.added.length > 0 || changelog.removed.length > 0 || changelog.modified.length > 0;
|
|
3188
|
+
if (hasChanges) {
|
|
3189
|
+
await fs23.writeFile(
|
|
3190
|
+
path23.join(dir, "changelog.mdx"),
|
|
3191
|
+
renderTemplate("changelog", { ...safeResult, ...changelog })
|
|
3192
|
+
);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
2664
3195
|
}
|
|
2665
3196
|
function renderTemplate(name, data) {
|
|
2666
3197
|
const template = templates[name];
|
|
@@ -2786,9 +3317,10 @@ function sanitizeCodeBlocks(mdx) {
|
|
|
2786
3317
|
function slugify2(name) {
|
|
2787
3318
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
2788
3319
|
}
|
|
2789
|
-
async function writeMeta(contentDir, results, crossRepo) {
|
|
3320
|
+
async function writeMeta(contentDir, results, crossRepo, changelogs) {
|
|
2790
3321
|
if (results.length === 1) {
|
|
2791
|
-
|
|
3322
|
+
const changelog = changelogs?.get(results[0].repoName);
|
|
3323
|
+
await writeRepoMeta(contentDir, results[0], changelog);
|
|
2792
3324
|
} else {
|
|
2793
3325
|
const pages = ["index"];
|
|
2794
3326
|
if (crossRepo) pages.push("cross-repo");
|
|
@@ -2800,20 +3332,25 @@ async function writeMeta(contentDir, results, crossRepo) {
|
|
|
2800
3332
|
);
|
|
2801
3333
|
for (const result of results) {
|
|
2802
3334
|
const repoDir = path33.join(contentDir, slugify22(result.repoName));
|
|
2803
|
-
|
|
3335
|
+
const changelog = changelogs?.get(result.repoName);
|
|
3336
|
+
await writeRepoMeta(repoDir, result, changelog);
|
|
2804
3337
|
}
|
|
2805
3338
|
}
|
|
2806
3339
|
}
|
|
2807
|
-
async function writeRepoMeta(dir, result) {
|
|
3340
|
+
async function writeRepoMeta(dir, result, changelog) {
|
|
2808
3341
|
await fs33.ensureDir(dir);
|
|
2809
3342
|
const pages = ["index"];
|
|
2810
3343
|
if (result.features && result.features.features.length > 0) pages.push("features");
|
|
2811
3344
|
pages.push("getting-started");
|
|
3345
|
+
if (result.configuration && result.configuration.configItems.length > 0) pages.push("configuration");
|
|
3346
|
+
if (result.businessLogic && (result.businessLogic.domainConcepts.length > 0 || result.businessLogic.businessRules.length > 0 || result.businessLogic.workflows.length > 0)) pages.push("business-logic");
|
|
2812
3347
|
pages.push("architecture");
|
|
2813
3348
|
if (result.apiEndpoints.length > 0) pages.push("api");
|
|
2814
3349
|
if (result.components.length > 0) pages.push("components");
|
|
2815
3350
|
if (result.dataModels.length > 0) pages.push("data-models");
|
|
3351
|
+
if (result.errorHandling && (result.errorHandling.errorCodes.length > 0 || result.errorHandling.commonErrors.length > 0)) pages.push("error-handling");
|
|
2816
3352
|
if (result.diagrams && result.diagrams.length > 0) pages.push("diagrams");
|
|
3353
|
+
if (changelog && (changelog.added.length > 0 || changelog.removed.length > 0 || changelog.modified.length > 0)) pages.push("changelog");
|
|
2817
3354
|
const meta = {
|
|
2818
3355
|
title: result.repoName,
|
|
2819
3356
|
pages
|
|
@@ -3068,6 +3605,12 @@ function buildRepoSummary(result) {
|
|
|
3068
3605
|
if (result.dataModels.length > 0) {
|
|
3069
3606
|
parts.push(`${result.dataModels.length} model${result.dataModels.length === 1 ? "" : "s"}`);
|
|
3070
3607
|
}
|
|
3608
|
+
if (result.configuration && result.configuration.configItems.length > 0) {
|
|
3609
|
+
parts.push(`${result.configuration.configItems.length} config${result.configuration.configItems.length === 1 ? "" : "s"}`);
|
|
3610
|
+
}
|
|
3611
|
+
if (result.errorHandling && result.errorHandling.errorCodes.length > 0) {
|
|
3612
|
+
parts.push(`${result.errorHandling.errorCodes.length} error code${result.errorHandling.errorCodes.length === 1 ? "" : "s"}`);
|
|
3613
|
+
}
|
|
3071
3614
|
return parts.length > 0 ? parts.join(", ") : "Analysis complete";
|
|
3072
3615
|
}
|
|
3073
3616
|
|
|
@@ -3094,6 +3637,21 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
|
|
|
3094
3637
|
}
|
|
3095
3638
|
const repos = await pickRepos(token);
|
|
3096
3639
|
p6.log.info(`Selected ${repos.length} ${repos.length === 1 ? "repository" : "repositories"}`);
|
|
3640
|
+
let projectName;
|
|
3641
|
+
if (repos.length > 1) {
|
|
3642
|
+
const nameInput = await p6.text({
|
|
3643
|
+
message: "What would you like to name this project?",
|
|
3644
|
+
placeholder: "My Project",
|
|
3645
|
+
validate: (v) => {
|
|
3646
|
+
if (!v || v.trim().length === 0) return "Project name is required";
|
|
3647
|
+
}
|
|
3648
|
+
});
|
|
3649
|
+
if (p6.isCancel(nameInput)) {
|
|
3650
|
+
p6.cancel("Operation cancelled");
|
|
3651
|
+
process.exit(0);
|
|
3652
|
+
}
|
|
3653
|
+
projectName = nameInput;
|
|
3654
|
+
}
|
|
3097
3655
|
let apiKey = getAnthropicKey();
|
|
3098
3656
|
if (!apiKey) {
|
|
3099
3657
|
const keyInput = await p6.text({
|
|
@@ -3203,7 +3761,9 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
|
|
|
3203
3761
|
}
|
|
3204
3762
|
}
|
|
3205
3763
|
const outputDir = path9.resolve(options.output || "docs-site");
|
|
3206
|
-
|
|
3764
|
+
if (!projectName) {
|
|
3765
|
+
projectName = results.length === 1 ? results[0].repoName : "My Project";
|
|
3766
|
+
}
|
|
3207
3767
|
const genSpinner = p6.spinner();
|
|
3208
3768
|
try {
|
|
3209
3769
|
genSpinner.start("Scaffolding documentation site...");
|
|
@@ -3234,7 +3794,8 @@ Try reinstalling: npm install -g @latent-space-labs/open-auto-doc`
|
|
|
3234
3794
|
cloneUrl: r.cloneUrl,
|
|
3235
3795
|
htmlUrl: r.htmlUrl
|
|
3236
3796
|
})),
|
|
3237
|
-
outputDir
|
|
3797
|
+
outputDir,
|
|
3798
|
+
...projectName !== results[0]?.repoName && { projectName }
|
|
3238
3799
|
};
|
|
3239
3800
|
try {
|
|
3240
3801
|
saveConfig(config);
|
|
@@ -3455,6 +4016,7 @@ async function generateCommand(options) {
|
|
|
3455
4016
|
const total = clones.length;
|
|
3456
4017
|
const progressTable = new ProgressTable({ repos: clones.map((c) => c.info.name) });
|
|
3457
4018
|
progressTable.start();
|
|
4019
|
+
const changelogs = /* @__PURE__ */ new Map();
|
|
3458
4020
|
const analysisPromises = clones.map(async (cloned) => {
|
|
3459
4021
|
const repo = config.repos.find((r) => r.name === cloned.info.name);
|
|
3460
4022
|
const repoName = repo.name;
|
|
@@ -3467,8 +4029,8 @@ async function generateCommand(options) {
|
|
|
3467
4029
|
progressTable.update(repoName, { status: "active", message: "Starting..." });
|
|
3468
4030
|
try {
|
|
3469
4031
|
let result;
|
|
4032
|
+
const cached = loadCache(cacheDir, repo.name);
|
|
3470
4033
|
if (incremental) {
|
|
3471
|
-
const cached = loadCache(cacheDir, repo.name);
|
|
3472
4034
|
if (cached) {
|
|
3473
4035
|
result = await analyzeRepositoryIncremental({
|
|
3474
4036
|
repoPath: cloned.localPath,
|
|
@@ -3503,6 +4065,16 @@ async function generateCommand(options) {
|
|
|
3503
4065
|
onToolUse
|
|
3504
4066
|
});
|
|
3505
4067
|
}
|
|
4068
|
+
if (cached) {
|
|
4069
|
+
try {
|
|
4070
|
+
const headSha = getHeadSha(cloned.localPath);
|
|
4071
|
+
const changelog = computeChangelog(cached.result, result, cached.commitSha, headSha);
|
|
4072
|
+
if (changelog.added.length > 0 || changelog.removed.length > 0 || changelog.modified.length > 0) {
|
|
4073
|
+
changelogs.set(repoName, changelog);
|
|
4074
|
+
}
|
|
4075
|
+
} catch {
|
|
4076
|
+
}
|
|
4077
|
+
}
|
|
3506
4078
|
try {
|
|
3507
4079
|
const headSha = getHeadSha(cloned.localPath);
|
|
3508
4080
|
saveCache(cacheDir, repo.name, headSha, result);
|
|
@@ -3541,8 +4113,8 @@ async function generateCommand(options) {
|
|
|
3541
4113
|
}
|
|
3542
4114
|
}
|
|
3543
4115
|
const contentDir = path10.join(config.outputDir, "content", "docs");
|
|
3544
|
-
await writeContent(contentDir, results, crossRepo);
|
|
3545
|
-
await writeMeta(contentDir, results, crossRepo);
|
|
4116
|
+
await writeContent(contentDir, results, crossRepo, changelogs.size > 0 ? changelogs : void 0);
|
|
4117
|
+
await writeMeta(contentDir, results, crossRepo, changelogs.size > 0 ? changelogs : void 0);
|
|
3546
4118
|
try {
|
|
3547
4119
|
await runBuildCheck({ docsDir: config.outputDir, apiKey, model });
|
|
3548
4120
|
} catch (err) {
|
|
@@ -3682,7 +4254,7 @@ async function logoutCommand() {
|
|
|
3682
4254
|
|
|
3683
4255
|
// src/index.ts
|
|
3684
4256
|
var program = new Command();
|
|
3685
|
-
program.name("open-auto-doc").description("Auto-generate beautiful documentation websites from GitHub repositories using AI").version("0.
|
|
4257
|
+
program.name("open-auto-doc").description("Auto-generate beautiful documentation websites from GitHub repositories using AI").version("0.4.1");
|
|
3686
4258
|
program.command("init", { isDefault: true }).description("Initialize and generate documentation for your repositories").option("-o, --output <dir>", "Output directory", "docs-site").action(initCommand);
|
|
3687
4259
|
program.command("generate").description("Regenerate documentation using existing configuration").option("--incremental", "Only re-analyze changed files (uses cached results)").option("--force", "Force full regeneration (ignore cache)").option("--repo <name>", "Only analyze this repo (uses cache for others)").action(generateCommand);
|
|
3688
4260
|
program.command("deploy").description("Create a GitHub repo for docs and push (connect to Vercel for auto-deploy)").option("-d, --dir <path>", "Docs site directory").action(deployCommand);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Business Logic"
|
|
3
|
+
description: "Domain concepts, business rules, and workflows for {{repoName}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Business Logic & Domain Concepts
|
|
7
|
+
|
|
8
|
+
{{#if domainConcepts.length}}
|
|
9
|
+
## Domain Concepts
|
|
10
|
+
|
|
11
|
+
{{#each domainConcepts}}
|
|
12
|
+
### {{this.name}}
|
|
13
|
+
|
|
14
|
+
{{this.description}}
|
|
15
|
+
|
|
16
|
+
{{#if this.relatedFiles.length}}
|
|
17
|
+
<details>
|
|
18
|
+
<summary>Related files</summary>
|
|
19
|
+
|
|
20
|
+
{{#each this.relatedFiles}}
|
|
21
|
+
- `{{this}}`
|
|
22
|
+
{{/each}}
|
|
23
|
+
|
|
24
|
+
</details>
|
|
25
|
+
{{/if}}
|
|
26
|
+
|
|
27
|
+
{{/each}}
|
|
28
|
+
{{/if}}
|
|
29
|
+
|
|
30
|
+
{{#if businessRules.length}}
|
|
31
|
+
## Business Rules
|
|
32
|
+
|
|
33
|
+
{{#each businessRules}}
|
|
34
|
+
### {{this.name}}
|
|
35
|
+
|
|
36
|
+
{{this.description}}
|
|
37
|
+
|
|
38
|
+
{{#if this.category}}**Category:** {{this.category}}{{/if}}
|
|
39
|
+
|
|
40
|
+
{{#if this.sourceFiles.length}}
|
|
41
|
+
<details>
|
|
42
|
+
<summary>Source files</summary>
|
|
43
|
+
|
|
44
|
+
{{#each this.sourceFiles}}
|
|
45
|
+
- `{{this}}`
|
|
46
|
+
{{/each}}
|
|
47
|
+
|
|
48
|
+
</details>
|
|
49
|
+
{{/if}}
|
|
50
|
+
|
|
51
|
+
{{/each}}
|
|
52
|
+
{{/if}}
|
|
53
|
+
|
|
54
|
+
{{#if workflows.length}}
|
|
55
|
+
## Workflows
|
|
56
|
+
|
|
57
|
+
{{#each workflows}}
|
|
58
|
+
### {{this.name}}
|
|
59
|
+
|
|
60
|
+
{{this.description}}
|
|
61
|
+
|
|
62
|
+
{{#if this.steps.length}}
|
|
63
|
+
{{#each this.steps}}
|
|
64
|
+
{{@index}}. {{this}}
|
|
65
|
+
{{/each}}
|
|
66
|
+
{{/if}}
|
|
67
|
+
|
|
68
|
+
{{#if this.diagram}}
|
|
69
|
+
```mermaid
|
|
70
|
+
{{{this.diagram.mermaidSyntax}}}
|
|
71
|
+
```
|
|
72
|
+
{{/if}}
|
|
73
|
+
|
|
74
|
+
{{/each}}
|
|
75
|
+
{{/if}}
|
|
76
|
+
|
|
77
|
+
{{#if keyInvariants.length}}
|
|
78
|
+
## Key Invariants
|
|
79
|
+
|
|
80
|
+
{{#each keyInvariants}}
|
|
81
|
+
- {{this}}
|
|
82
|
+
{{/each}}
|
|
83
|
+
{{/if}}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "What Changed"
|
|
3
|
+
description: "Documentation changelog for {{repoName}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# What Changed
|
|
7
|
+
|
|
8
|
+
{{{summary}}}
|
|
9
|
+
|
|
10
|
+
**Commit range:** `{{fromCommit}}` → `{{toCommit}}`
|
|
11
|
+
|
|
12
|
+
**Generated:** {{generatedAt}}
|
|
13
|
+
|
|
14
|
+
{{#if added.length}}
|
|
15
|
+
## Added
|
|
16
|
+
|
|
17
|
+
{{#each added}}
|
|
18
|
+
- **{{this.name}}** ({{this.section}}) — {{this.description}}
|
|
19
|
+
{{/each}}
|
|
20
|
+
{{/if}}
|
|
21
|
+
|
|
22
|
+
{{#if removed.length}}
|
|
23
|
+
## Removed
|
|
24
|
+
|
|
25
|
+
{{#each removed}}
|
|
26
|
+
- **{{this.name}}** ({{this.section}}) — {{this.description}}
|
|
27
|
+
{{/each}}
|
|
28
|
+
{{/if}}
|
|
29
|
+
|
|
30
|
+
{{#if modified.length}}
|
|
31
|
+
## Modified
|
|
32
|
+
|
|
33
|
+
{{#each modified}}
|
|
34
|
+
- **{{this.name}}** ({{this.section}}) — {{this.description}}
|
|
35
|
+
{{/each}}
|
|
36
|
+
{{/if}}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Configuration Reference"
|
|
3
|
+
description: "Configuration options and environment variables for {{repoName}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Configuration Reference
|
|
7
|
+
|
|
8
|
+
{{#if configFiles.length}}
|
|
9
|
+
## Configuration Files
|
|
10
|
+
|
|
11
|
+
{{#each configFiles}}
|
|
12
|
+
- `{{this}}`
|
|
13
|
+
{{/each}}
|
|
14
|
+
{{/if}}
|
|
15
|
+
|
|
16
|
+
{{#each configByCategory}}
|
|
17
|
+
## {{@key}}
|
|
18
|
+
|
|
19
|
+
| Name | Source | Type | Default | Required | Description |
|
|
20
|
+
|------|--------|------|---------|----------|-------------|
|
|
21
|
+
{{#each this}}
|
|
22
|
+
| `{{this.name}}` | `{{pipeEscape this.source}}` | `{{pipeEscape this.type}}` | {{#if this.defaultValue}}`{{pipeEscape this.defaultValue}}`{{else}}-{{/if}} | {{#if this.required}}Yes{{else}}No{{/if}} | {{pipeEscape this.description}} |
|
|
23
|
+
{{/each}}
|
|
24
|
+
|
|
25
|
+
{{/each}}
|
|
26
|
+
|
|
27
|
+
{{#if environmentVariables.length}}
|
|
28
|
+
## Environment Variables
|
|
29
|
+
|
|
30
|
+
{{#each environmentVariables}}
|
|
31
|
+
- `{{this}}`
|
|
32
|
+
{{/each}}
|
|
33
|
+
{{/if}}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Error Handling"
|
|
3
|
+
description: "Error codes, troubleshooting, and debugging for {{repoName}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Error Handling & Troubleshooting
|
|
7
|
+
|
|
8
|
+
{{#if errorClasses.length}}
|
|
9
|
+
## Error Classes
|
|
10
|
+
|
|
11
|
+
{{#each errorClasses}}
|
|
12
|
+
- `{{this}}`
|
|
13
|
+
{{/each}}
|
|
14
|
+
{{/if}}
|
|
15
|
+
|
|
16
|
+
{{#if errorCodes.length}}
|
|
17
|
+
## Error Codes
|
|
18
|
+
|
|
19
|
+
| Code | HTTP Status | Message | Description |
|
|
20
|
+
|------|-------------|---------|-------------|
|
|
21
|
+
{{#each errorCodes}}
|
|
22
|
+
| `{{this.code}}` | {{#if this.httpStatus}}{{this.httpStatus}}{{else}}-{{/if}} | {{pipeEscape this.message}} | {{pipeEscape this.description}} |
|
|
23
|
+
{{/each}}
|
|
24
|
+
{{/if}}
|
|
25
|
+
|
|
26
|
+
{{#if commonErrors.length}}
|
|
27
|
+
## Common Errors & Solutions
|
|
28
|
+
|
|
29
|
+
{{#each commonErrors}}
|
|
30
|
+
### {{this.error}}
|
|
31
|
+
|
|
32
|
+
{{#if this.category}}**Category:** {{this.category}}{{/if}}
|
|
33
|
+
|
|
34
|
+
**Cause:** {{this.cause}}
|
|
35
|
+
|
|
36
|
+
**Solution:** {{this.solution}}
|
|
37
|
+
|
|
38
|
+
{{/each}}
|
|
39
|
+
{{/if}}
|
|
40
|
+
|
|
41
|
+
{{#if debuggingTips.length}}
|
|
42
|
+
## Debugging Tips
|
|
43
|
+
|
|
44
|
+
{{#each debuggingTips}}
|
|
45
|
+
- {{this}}
|
|
46
|
+
{{/each}}
|
|
47
|
+
{{/if}}
|