forgesmith 0.2.0 → 0.3.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/CHANGELOG.md +11 -0
- package/dist/index.cjs +83 -0
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.mjs +83 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 — Ask-Driven Asset Generation
|
|
4
|
+
|
|
5
|
+
- `generateAskDrivenAsset(blueprint, question, opts, provider)` — generate any content format from a natural-language question about your codebase architecture
|
|
6
|
+
- Formats: `markdown` | `blog` | `social` | `email` | `slack` | `slide`
|
|
7
|
+
- Tones: `professional` | `casual` | `technical` | `executive`
|
|
8
|
+
- Grounded in Blueprint dependency graph (top files by usage, edge sample, category breakdown)
|
|
9
|
+
- Format-aware token budgets (512 for social/slack, 3072 for blog/markdown, 2048 for slides)
|
|
10
|
+
- Use-cases: blog posts, tweets, investor-update bullets, Slack status, slide decks
|
|
11
|
+
- New exported types: `AskDrivenAssetOpts`, `AskDrivenAssetFormat`
|
|
12
|
+
- 52 unit tests, all green (+8 new)
|
|
13
|
+
|
|
3
14
|
## 0.2.0 — Architecture Narrative Generators
|
|
4
15
|
|
|
5
16
|
- `generateArchitectureWalkthrough(blueprint, opts, provider)` — long-form narrative explaining structure, key files, and important relationships. Tones: `technical` | `casual` | `onboarding`
|
package/dist/index.cjs
CHANGED
|
@@ -383,7 +383,90 @@ async function readBlueprintData(targetPath) {
|
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
+
// src/generators/askDrivenAsset.ts
|
|
387
|
+
var NO_DATA_MSG5 = "No Blueprint data available. Run prism scan first.";
|
|
388
|
+
var FORMAT_GUIDES = {
|
|
389
|
+
markdown: { name: "Markdown document", structure: "Use headers, bullet lists, and code blocks where appropriate.", maxTokens: 3072 },
|
|
390
|
+
blog: { name: "blog post", structure: "Write with an engaging intro, clear sections, a conclusion, and a call-to-action.", maxTokens: 3072 },
|
|
391
|
+
social: { name: "social media post", structure: "Write concise, punchy content suitable for Twitter/LinkedIn. Max 280 characters for Twitter mode.", maxTokens: 512 },
|
|
392
|
+
email: { name: "email", structure: "Use Subject:, greeting, body paragraphs, and a sign-off.", maxTokens: 1024 },
|
|
393
|
+
slack: { name: "Slack message", structure: "Keep it conversational, use *bold* for emphasis, bullet points for lists. Max 3 paragraphs.", maxTokens: 512 },
|
|
394
|
+
slide: { name: "presentation outline", structure: "Structure as slide titles with 3-5 bullet points each. Include a title slide and summary slide.", maxTokens: 2048 }
|
|
395
|
+
};
|
|
396
|
+
function buildSystemPrompt6(format, tone) {
|
|
397
|
+
const toneMap = {
|
|
398
|
+
professional: "You are a professional technical writer and developer advocate.",
|
|
399
|
+
casual: "You are a friendly engineering blogger who writes in an approachable, conversational style.",
|
|
400
|
+
technical: "You are a senior software engineer writing precise, implementation-focused content.",
|
|
401
|
+
executive: "You are a VP of Engineering writing high-level, business-value-focused content for leadership."
|
|
402
|
+
};
|
|
403
|
+
const guide = FORMAT_GUIDES[format];
|
|
404
|
+
return `${toneMap[tone] ?? toneMap.professional} Generate a ${guide.name} based on the user's question and the provided codebase architecture context. ${guide.structure} Write only the requested content \u2014 no preamble, no meta-commentary.`;
|
|
405
|
+
}
|
|
406
|
+
function buildUserPrompt6(blueprint, question, opts) {
|
|
407
|
+
const format = opts.format ?? "markdown";
|
|
408
|
+
const tone = opts.tone ?? "professional";
|
|
409
|
+
const length = opts.length ?? "medium";
|
|
410
|
+
const guide = FORMAT_GUIDES[format];
|
|
411
|
+
const lengthGuide = {
|
|
412
|
+
short: "Keep it concise \u2014 1-2 paragraphs or equivalent.",
|
|
413
|
+
medium: "Medium length \u2014 3-5 paragraphs or equivalent.",
|
|
414
|
+
long: "Detailed and comprehensive \u2014 cover the topic thoroughly."
|
|
415
|
+
}[length];
|
|
416
|
+
const topFiles = blueprint.files.slice().sort((a, b) => (b.importedByCount ?? 0) - (a.importedByCount ?? 0)).slice(0, 12);
|
|
417
|
+
const lines = [
|
|
418
|
+
`## User's Question`,
|
|
419
|
+
question,
|
|
420
|
+
``,
|
|
421
|
+
`## Output Requirements`,
|
|
422
|
+
`- Format: ${guide.name}`,
|
|
423
|
+
`- Tone: ${tone}`,
|
|
424
|
+
`- Length: ${length} (${lengthGuide})`,
|
|
425
|
+
``,
|
|
426
|
+
`## Codebase Architecture Context`,
|
|
427
|
+
`Target: ${blueprint.targetPath}`,
|
|
428
|
+
`Total files: ${blueprint.stats.totalFiles} | Dependency edges: ${blueprint.stats.runtimeEdges}`,
|
|
429
|
+
`Categories: app=${blueprint.categories.app ?? 0}, components=${blueprint.categories.component ?? 0}, lib=${blueprint.categories.lib ?? 0}, hooks=${blueprint.categories.hook ?? 0}`,
|
|
430
|
+
``,
|
|
431
|
+
`Key files (by usage):`,
|
|
432
|
+
...topFiles.map((f) => `- ${f.path} [${f.category ?? "?"}] \u2014 imported by ${f.importedByCount ?? 0} files, ${f.lineCount ?? "?"} lines`)
|
|
433
|
+
];
|
|
434
|
+
if (blueprint.edges.length > 0) {
|
|
435
|
+
const edgeSample = blueprint.edges.slice(0, 15);
|
|
436
|
+
lines.push(``, `Dependency edges (sample):`, ...edgeSample.map((e) => `- ${e.from} \u2192 ${e.to}`));
|
|
437
|
+
}
|
|
438
|
+
lines.push(``, `Answer the user's question using the architecture context above. Generate the ${guide.name} now.`);
|
|
439
|
+
return lines.join("\n");
|
|
440
|
+
}
|
|
441
|
+
async function generateAskDrivenAsset(blueprint, question, opts, provider) {
|
|
442
|
+
if (!blueprint) {
|
|
443
|
+
return {
|
|
444
|
+
text: NO_DATA_MSG5,
|
|
445
|
+
metadata: { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), usedTokens: 0, generator: "forgesmith" }
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
if (!question || !question.trim()) {
|
|
449
|
+
return {
|
|
450
|
+
text: "No question provided. Please ask something about your codebase.",
|
|
451
|
+
metadata: { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), usedTokens: 0, generator: "forgesmith" }
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
const format = opts.format ?? "markdown";
|
|
455
|
+
const tone = opts.tone ?? "professional";
|
|
456
|
+
const maxTokens = FORMAT_GUIDES[format].maxTokens;
|
|
457
|
+
const response = await provider.complete({
|
|
458
|
+
systemPrompt: buildSystemPrompt6(format, tone),
|
|
459
|
+
messages: [{ role: "user", content: buildUserPrompt6(blueprint, question, opts) }],
|
|
460
|
+
maxTokens
|
|
461
|
+
});
|
|
462
|
+
return {
|
|
463
|
+
text: response.content,
|
|
464
|
+
metadata: { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), usedTokens: response.usedTokens, generator: "forgesmith" }
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
386
468
|
exports.generateArchitectureWalkthrough = generateArchitectureWalkthrough;
|
|
469
|
+
exports.generateAskDrivenAsset = generateAskDrivenAsset;
|
|
387
470
|
exports.generateChangesSince = generateChangesSince;
|
|
388
471
|
exports.generateOnboardingDoc = generateOnboardingDoc;
|
|
389
472
|
exports.generateRefactoringReport = generateRefactoringReport;
|
package/dist/index.d.cts
CHANGED
|
@@ -79,6 +79,12 @@ interface RefactoringReportOpts {
|
|
|
79
79
|
length?: "short" | "medium" | "long";
|
|
80
80
|
format?: "markdown" | "plain";
|
|
81
81
|
}
|
|
82
|
+
type AskDrivenAssetFormat = "markdown" | "blog" | "social" | "email" | "slack" | "slide";
|
|
83
|
+
interface AskDrivenAssetOpts {
|
|
84
|
+
format?: AskDrivenAssetFormat;
|
|
85
|
+
tone?: "professional" | "casual" | "technical" | "executive";
|
|
86
|
+
length?: "short" | "medium" | "long";
|
|
87
|
+
}
|
|
82
88
|
interface GenerationResult {
|
|
83
89
|
text: string;
|
|
84
90
|
metadata: {
|
|
@@ -119,4 +125,6 @@ declare function generateRefactoringReport(blueprint: BlueprintData | null, opts
|
|
|
119
125
|
declare function readPrismDirectory(prismPath: string): Promise<PrismData>;
|
|
120
126
|
declare function readBlueprintData(targetPath: string): Promise<BlueprintData | null>;
|
|
121
127
|
|
|
122
|
-
|
|
128
|
+
declare function generateAskDrivenAsset(blueprint: BlueprintData | null, question: string, opts: AskDrivenAssetOpts, provider: LlmProvider): Promise<GenerationResult>;
|
|
129
|
+
|
|
130
|
+
export { type ArchitectureWalkthroughOpts, type AskDrivenAssetFormat, type AskDrivenAssetOpts, type BlueprintData, type BlueprintEdge, type BlueprintFile, type ChangesSinceOpts, type GenerationResult, type LlmMessage, type LlmProvider, type LlmRequest, type LlmResponse, type OnboardingDocOpts, type PrismData, type PrismInsight, type PrismRecommendation, type PrismSession, type RefactoringReportOpts, type ReleaseNotesOpts, generateArchitectureWalkthrough, generateAskDrivenAsset, generateChangesSince, generateOnboardingDoc, generateRefactoringReport, generateReleaseNotes, readBlueprintData, readPrismDirectory };
|
package/dist/index.d.ts
CHANGED
|
@@ -79,6 +79,12 @@ interface RefactoringReportOpts {
|
|
|
79
79
|
length?: "short" | "medium" | "long";
|
|
80
80
|
format?: "markdown" | "plain";
|
|
81
81
|
}
|
|
82
|
+
type AskDrivenAssetFormat = "markdown" | "blog" | "social" | "email" | "slack" | "slide";
|
|
83
|
+
interface AskDrivenAssetOpts {
|
|
84
|
+
format?: AskDrivenAssetFormat;
|
|
85
|
+
tone?: "professional" | "casual" | "technical" | "executive";
|
|
86
|
+
length?: "short" | "medium" | "long";
|
|
87
|
+
}
|
|
82
88
|
interface GenerationResult {
|
|
83
89
|
text: string;
|
|
84
90
|
metadata: {
|
|
@@ -119,4 +125,6 @@ declare function generateRefactoringReport(blueprint: BlueprintData | null, opts
|
|
|
119
125
|
declare function readPrismDirectory(prismPath: string): Promise<PrismData>;
|
|
120
126
|
declare function readBlueprintData(targetPath: string): Promise<BlueprintData | null>;
|
|
121
127
|
|
|
122
|
-
|
|
128
|
+
declare function generateAskDrivenAsset(blueprint: BlueprintData | null, question: string, opts: AskDrivenAssetOpts, provider: LlmProvider): Promise<GenerationResult>;
|
|
129
|
+
|
|
130
|
+
export { type ArchitectureWalkthroughOpts, type AskDrivenAssetFormat, type AskDrivenAssetOpts, type BlueprintData, type BlueprintEdge, type BlueprintFile, type ChangesSinceOpts, type GenerationResult, type LlmMessage, type LlmProvider, type LlmRequest, type LlmResponse, type OnboardingDocOpts, type PrismData, type PrismInsight, type PrismRecommendation, type PrismSession, type RefactoringReportOpts, type ReleaseNotesOpts, generateArchitectureWalkthrough, generateAskDrivenAsset, generateChangesSince, generateOnboardingDoc, generateRefactoringReport, generateReleaseNotes, readBlueprintData, readPrismDirectory };
|
package/dist/index.mjs
CHANGED
|
@@ -376,4 +376,86 @@ async function readBlueprintData(targetPath) {
|
|
|
376
376
|
}
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
|
|
379
|
+
// src/generators/askDrivenAsset.ts
|
|
380
|
+
var NO_DATA_MSG5 = "No Blueprint data available. Run prism scan first.";
|
|
381
|
+
var FORMAT_GUIDES = {
|
|
382
|
+
markdown: { name: "Markdown document", structure: "Use headers, bullet lists, and code blocks where appropriate.", maxTokens: 3072 },
|
|
383
|
+
blog: { name: "blog post", structure: "Write with an engaging intro, clear sections, a conclusion, and a call-to-action.", maxTokens: 3072 },
|
|
384
|
+
social: { name: "social media post", structure: "Write concise, punchy content suitable for Twitter/LinkedIn. Max 280 characters for Twitter mode.", maxTokens: 512 },
|
|
385
|
+
email: { name: "email", structure: "Use Subject:, greeting, body paragraphs, and a sign-off.", maxTokens: 1024 },
|
|
386
|
+
slack: { name: "Slack message", structure: "Keep it conversational, use *bold* for emphasis, bullet points for lists. Max 3 paragraphs.", maxTokens: 512 },
|
|
387
|
+
slide: { name: "presentation outline", structure: "Structure as slide titles with 3-5 bullet points each. Include a title slide and summary slide.", maxTokens: 2048 }
|
|
388
|
+
};
|
|
389
|
+
function buildSystemPrompt6(format, tone) {
|
|
390
|
+
const toneMap = {
|
|
391
|
+
professional: "You are a professional technical writer and developer advocate.",
|
|
392
|
+
casual: "You are a friendly engineering blogger who writes in an approachable, conversational style.",
|
|
393
|
+
technical: "You are a senior software engineer writing precise, implementation-focused content.",
|
|
394
|
+
executive: "You are a VP of Engineering writing high-level, business-value-focused content for leadership."
|
|
395
|
+
};
|
|
396
|
+
const guide = FORMAT_GUIDES[format];
|
|
397
|
+
return `${toneMap[tone] ?? toneMap.professional} Generate a ${guide.name} based on the user's question and the provided codebase architecture context. ${guide.structure} Write only the requested content \u2014 no preamble, no meta-commentary.`;
|
|
398
|
+
}
|
|
399
|
+
function buildUserPrompt6(blueprint, question, opts) {
|
|
400
|
+
const format = opts.format ?? "markdown";
|
|
401
|
+
const tone = opts.tone ?? "professional";
|
|
402
|
+
const length = opts.length ?? "medium";
|
|
403
|
+
const guide = FORMAT_GUIDES[format];
|
|
404
|
+
const lengthGuide = {
|
|
405
|
+
short: "Keep it concise \u2014 1-2 paragraphs or equivalent.",
|
|
406
|
+
medium: "Medium length \u2014 3-5 paragraphs or equivalent.",
|
|
407
|
+
long: "Detailed and comprehensive \u2014 cover the topic thoroughly."
|
|
408
|
+
}[length];
|
|
409
|
+
const topFiles = blueprint.files.slice().sort((a, b) => (b.importedByCount ?? 0) - (a.importedByCount ?? 0)).slice(0, 12);
|
|
410
|
+
const lines = [
|
|
411
|
+
`## User's Question`,
|
|
412
|
+
question,
|
|
413
|
+
``,
|
|
414
|
+
`## Output Requirements`,
|
|
415
|
+
`- Format: ${guide.name}`,
|
|
416
|
+
`- Tone: ${tone}`,
|
|
417
|
+
`- Length: ${length} (${lengthGuide})`,
|
|
418
|
+
``,
|
|
419
|
+
`## Codebase Architecture Context`,
|
|
420
|
+
`Target: ${blueprint.targetPath}`,
|
|
421
|
+
`Total files: ${blueprint.stats.totalFiles} | Dependency edges: ${blueprint.stats.runtimeEdges}`,
|
|
422
|
+
`Categories: app=${blueprint.categories.app ?? 0}, components=${blueprint.categories.component ?? 0}, lib=${blueprint.categories.lib ?? 0}, hooks=${blueprint.categories.hook ?? 0}`,
|
|
423
|
+
``,
|
|
424
|
+
`Key files (by usage):`,
|
|
425
|
+
...topFiles.map((f) => `- ${f.path} [${f.category ?? "?"}] \u2014 imported by ${f.importedByCount ?? 0} files, ${f.lineCount ?? "?"} lines`)
|
|
426
|
+
];
|
|
427
|
+
if (blueprint.edges.length > 0) {
|
|
428
|
+
const edgeSample = blueprint.edges.slice(0, 15);
|
|
429
|
+
lines.push(``, `Dependency edges (sample):`, ...edgeSample.map((e) => `- ${e.from} \u2192 ${e.to}`));
|
|
430
|
+
}
|
|
431
|
+
lines.push(``, `Answer the user's question using the architecture context above. Generate the ${guide.name} now.`);
|
|
432
|
+
return lines.join("\n");
|
|
433
|
+
}
|
|
434
|
+
async function generateAskDrivenAsset(blueprint, question, opts, provider) {
|
|
435
|
+
if (!blueprint) {
|
|
436
|
+
return {
|
|
437
|
+
text: NO_DATA_MSG5,
|
|
438
|
+
metadata: { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), usedTokens: 0, generator: "forgesmith" }
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
if (!question || !question.trim()) {
|
|
442
|
+
return {
|
|
443
|
+
text: "No question provided. Please ask something about your codebase.",
|
|
444
|
+
metadata: { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), usedTokens: 0, generator: "forgesmith" }
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
const format = opts.format ?? "markdown";
|
|
448
|
+
const tone = opts.tone ?? "professional";
|
|
449
|
+
const maxTokens = FORMAT_GUIDES[format].maxTokens;
|
|
450
|
+
const response = await provider.complete({
|
|
451
|
+
systemPrompt: buildSystemPrompt6(format, tone),
|
|
452
|
+
messages: [{ role: "user", content: buildUserPrompt6(blueprint, question, opts) }],
|
|
453
|
+
maxTokens
|
|
454
|
+
});
|
|
455
|
+
return {
|
|
456
|
+
text: response.content,
|
|
457
|
+
metadata: { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), usedTokens: response.usedTokens, generator: "forgesmith" }
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export { generateArchitectureWalkthrough, generateAskDrivenAsset, generateChangesSince, generateOnboardingDoc, generateRefactoringReport, generateReleaseNotes, readBlueprintData, readPrismDirectory };
|
package/package.json
CHANGED