forgesmith 0.0.1 → 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/CHANGELOG.md +10 -0
- package/README.md +50 -22
- package/dist/index.cjs +105 -0
- package/dist/index.d.cts +63 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.mjs +97 -0
- package/dist/providers.cjs +36 -0
- package/dist/providers.d.cts +30 -0
- package/dist/providers.d.ts +30 -0
- package/dist/providers.mjs +30 -0
- package/package.json +54 -4
- package/index.js +0 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — Initial real release
|
|
4
|
+
|
|
5
|
+
- `generateReleaseNotes(data, opts, provider)` — LLM-powered release note generation from prism0x2A data
|
|
6
|
+
- `readPrismDirectory(path)` — reads `.prism/` folder structure into `PrismData`
|
|
7
|
+
- `DirectLlmProvider` — Anthropic SDK-backed LLM provider (import from `forgesmith/providers`)
|
|
8
|
+
- `LlmProvider` interface for custom/mock providers
|
|
9
|
+
- ESM + CJS dual-build via tsup
|
|
10
|
+
- 13 unit tests, all green
|
package/README.md
CHANGED
|
@@ -1,15 +1,53 @@
|
|
|
1
1
|
# forgesmith
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
>
|
|
5
|
-
> *The lens observes. The smith forges.*
|
|
3
|
+
Content & asset-generation engine for code intelligence.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
Forge release notes, blog posts, and social copy from [prism0x2A](https://github.com/dadenjo/prism0x2A) data.
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
## Status
|
|
8
|
+
|
|
9
|
+
**Phase 1** — release-notes generator. More asset-types coming in Phase 2.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install forgesmith
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Use
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { generateReleaseNotes, readPrismDirectory } from 'forgesmith'
|
|
21
|
+
import { DirectLlmProvider } from 'forgesmith/providers'
|
|
22
|
+
|
|
23
|
+
const data = await readPrismDirectory('./my-project/.prism')
|
|
24
|
+
const provider = new DirectLlmProvider({ apiKey: process.env.ANTHROPIC_KEY })
|
|
25
|
+
const result = await generateReleaseNotes(data, { tone: 'professional' }, provider)
|
|
26
|
+
console.log(result.text)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## API
|
|
30
|
+
|
|
31
|
+
### `generateReleaseNotes(data, opts, provider)`
|
|
32
|
+
|
|
33
|
+
Generates release notes from prism data.
|
|
34
|
+
|
|
35
|
+
- `data: PrismData` — sessions, recommendations, insights from prism0x2A
|
|
36
|
+
- `opts: ReleaseNotesOpts` — `tone`, `length`, `format`
|
|
37
|
+
- `provider: LlmProvider` — any compatible LLM provider
|
|
38
|
+
|
|
39
|
+
### `readPrismDirectory(prismPath)`
|
|
40
|
+
|
|
41
|
+
Reads a `.prism/` folder and returns a `PrismData` object.
|
|
42
|
+
|
|
43
|
+
### `DirectLlmProvider`
|
|
44
|
+
|
|
45
|
+
Anthropic SDK-backed provider. Import from `forgesmith/providers`.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { DirectLlmProvider } from 'forgesmith/providers'
|
|
49
|
+
const provider = new DirectLlmProvider({ apiKey: '...', model: 'claude-sonnet-4-6' })
|
|
50
|
+
```
|
|
13
51
|
|
|
14
52
|
## Architecture
|
|
15
53
|
|
|
@@ -20,20 +58,10 @@ prism0x2A (dashboard — persists .prism/ data)
|
|
|
20
58
|
↓
|
|
21
59
|
forgesmith (this — forges assets from data)
|
|
22
60
|
↓
|
|
23
|
-
forge0x2B (dashboard
|
|
61
|
+
forge0x2B (dashboard for marketing/comms)
|
|
24
62
|
```
|
|
25
63
|
|
|
26
|
-
##
|
|
27
|
-
|
|
28
|
-
- **prismlens** *observes* — splits code into spectral analysis
|
|
29
|
-
- **forgesmith** *produces* — takes the analysis and hammers out content
|
|
30
|
-
|
|
31
|
-
Engines in this family carry **craft-appropriate** suffixes, not a forced uniform suffix.
|
|
32
|
-
|
|
33
|
-
## Companion repos
|
|
64
|
+
## Related
|
|
34
65
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
| [`prismlens`](https://github.com/dadenjo/prismlens) | Code-intelligence engine |
|
|
38
|
-
| [`prism0x2A`](https://github.com/dadenjo/prism0x2A) | Code-intelligence dashboard |
|
|
39
|
-
| [`forge0x2B`](https://github.com/dadenjo/forge0x2B) | Content/comms dashboard |
|
|
66
|
+
- [prism0x2A](https://www.npmjs.com/package/prism0x2a) — the intelligence layer
|
|
67
|
+
- [forge0x2B](https://www.npmjs.com/package/forge0x2b) — the dashboard that uses this engine
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs/promises');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
9
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
10
|
+
|
|
11
|
+
// src/generators/releaseNotes.ts
|
|
12
|
+
function buildSystemPrompt() {
|
|
13
|
+
return `You are a technical writer and developer-relations expert. You generate clear, accurate release notes from structured code-intelligence data. Write only the release notes \u2014 no preamble, no meta-commentary.`;
|
|
14
|
+
}
|
|
15
|
+
function buildUserPrompt(data, opts) {
|
|
16
|
+
const tone = opts.tone ?? "professional";
|
|
17
|
+
const length = opts.length ?? "medium";
|
|
18
|
+
const format = opts.format ?? "markdown";
|
|
19
|
+
const lengthGuide = { short: "2-3 paragraphs or bullet groups", medium: "4-6 sections", long: "comprehensive, 6+ sections with details" }[length];
|
|
20
|
+
const toneGuide = { professional: "formal, clear, business-appropriate", casual: "friendly, approachable, conversational", technical: "precise, implementation-focused, developer-centric" }[tone];
|
|
21
|
+
const hasSessions = (data.sessions?.length ?? 0) > 0;
|
|
22
|
+
const hasRecs = (data.recommendations?.length ?? 0) > 0;
|
|
23
|
+
const hasInsights = (data.insights?.length ?? 0) > 0;
|
|
24
|
+
if (!hasSessions && !hasRecs && !hasInsights) {
|
|
25
|
+
return `Generate a brief release note in ${format} format stating there are no changes to report in this period${data.fromDate ? ` (${data.fromDate} to ${data.toDate ?? "now"})` : ""}.`;
|
|
26
|
+
}
|
|
27
|
+
const lines = [];
|
|
28
|
+
lines.push(`Generate release notes with the following requirements:`);
|
|
29
|
+
lines.push(`- Tone: ${tone} (${toneGuide})`);
|
|
30
|
+
lines.push(`- Length: ${length} (${lengthGuide})`);
|
|
31
|
+
lines.push(`- Format: ${format}`);
|
|
32
|
+
if (data.fromDate) lines.push(`- Period: ${data.fromDate}${data.toDate ? ` to ${data.toDate}` : " to now"}`);
|
|
33
|
+
lines.push(``);
|
|
34
|
+
if (hasSessions) {
|
|
35
|
+
lines.push(`## Sessions (${data.sessions.length})`);
|
|
36
|
+
for (const s of data.sessions) {
|
|
37
|
+
lines.push(`- **${s.title}**${s.summary ? `: ${s.summary}` : ""}${s.conclusion ? ` | Conclusion: ${s.conclusion}` : ""}`);
|
|
38
|
+
}
|
|
39
|
+
lines.push(``);
|
|
40
|
+
}
|
|
41
|
+
if (hasRecs) {
|
|
42
|
+
lines.push(`## Recommendations (${data.recommendations.length})`);
|
|
43
|
+
for (const r of data.recommendations) {
|
|
44
|
+
const accepted = r.accepted === true ? " [ACCEPTED]" : r.accepted === false ? " [DECLINED]" : "";
|
|
45
|
+
lines.push(`- [${r.severity?.toUpperCase() ?? "INFO"}${accepted}] **${r.title}**${r.description ? `: ${r.description}` : ""}`);
|
|
46
|
+
}
|
|
47
|
+
lines.push(``);
|
|
48
|
+
}
|
|
49
|
+
if (hasInsights) {
|
|
50
|
+
lines.push(`## Insights (${data.insights.length})`);
|
|
51
|
+
for (const i of data.insights) {
|
|
52
|
+
lines.push(`- **${i.title}**${i.body ? `: ${i.body}` : ""}`);
|
|
53
|
+
}
|
|
54
|
+
lines.push(``);
|
|
55
|
+
}
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
58
|
+
async function generateReleaseNotes(data, opts, provider) {
|
|
59
|
+
const systemPrompt = buildSystemPrompt();
|
|
60
|
+
const userPrompt = buildUserPrompt(data, opts);
|
|
61
|
+
const response = await provider.complete({
|
|
62
|
+
systemPrompt,
|
|
63
|
+
messages: [{ role: "user", content: userPrompt }],
|
|
64
|
+
maxTokens: 2048
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
text: response.content,
|
|
68
|
+
metadata: {
|
|
69
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
70
|
+
usedTokens: response.usedTokens,
|
|
71
|
+
generator: "forgesmith"
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async function readJsonFiles(dir) {
|
|
76
|
+
try {
|
|
77
|
+
const entries = await fs__default.default.readdir(dir);
|
|
78
|
+
const results = [];
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
if (!entry.endsWith(".json")) continue;
|
|
81
|
+
try {
|
|
82
|
+
const raw = await fs__default.default.readFile(path__default.default.join(dir, entry), "utf-8");
|
|
83
|
+
results.push(JSON.parse(raw));
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return results;
|
|
88
|
+
} catch {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function readPrismDirectory(prismPath) {
|
|
93
|
+
const sessionsDir = path__default.default.join(prismPath, "sessions");
|
|
94
|
+
const recsDir = path__default.default.join(prismPath, "recommendations");
|
|
95
|
+
const insightsDir = path__default.default.join(prismPath, "green", "insights", "accepted");
|
|
96
|
+
const [sessions, recommendations, insights] = await Promise.all([
|
|
97
|
+
readJsonFiles(sessionsDir),
|
|
98
|
+
readJsonFiles(recsDir),
|
|
99
|
+
readJsonFiles(insightsDir)
|
|
100
|
+
]);
|
|
101
|
+
return { sessions, recommendations, insights };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
exports.generateReleaseNotes = generateReleaseNotes;
|
|
105
|
+
exports.readPrismDirectory = readPrismDirectory;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
interface PrismSession {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
summary?: string;
|
|
5
|
+
conclusion?: string;
|
|
6
|
+
createdAt?: string;
|
|
7
|
+
}
|
|
8
|
+
interface PrismRecommendation {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
severity?: "low" | "medium" | "high" | "critical";
|
|
12
|
+
accepted?: boolean;
|
|
13
|
+
description?: string;
|
|
14
|
+
}
|
|
15
|
+
interface PrismInsight {
|
|
16
|
+
id: string;
|
|
17
|
+
title: string;
|
|
18
|
+
body?: string;
|
|
19
|
+
}
|
|
20
|
+
interface PrismData {
|
|
21
|
+
sessions?: PrismSession[];
|
|
22
|
+
recommendations?: PrismRecommendation[];
|
|
23
|
+
insights?: PrismInsight[];
|
|
24
|
+
fromDate?: string;
|
|
25
|
+
toDate?: string;
|
|
26
|
+
}
|
|
27
|
+
interface ReleaseNotesOpts {
|
|
28
|
+
tone?: "professional" | "casual" | "technical";
|
|
29
|
+
length?: "short" | "medium" | "long";
|
|
30
|
+
format?: "markdown" | "plain";
|
|
31
|
+
}
|
|
32
|
+
interface GenerationResult {
|
|
33
|
+
text: string;
|
|
34
|
+
metadata: {
|
|
35
|
+
generatedAt: string;
|
|
36
|
+
usedTokens: number;
|
|
37
|
+
generator: "forgesmith";
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface LlmMessage {
|
|
42
|
+
role: "user" | "assistant";
|
|
43
|
+
content: string;
|
|
44
|
+
}
|
|
45
|
+
interface LlmRequest {
|
|
46
|
+
model?: string;
|
|
47
|
+
messages: LlmMessage[];
|
|
48
|
+
maxTokens?: number;
|
|
49
|
+
systemPrompt?: string;
|
|
50
|
+
}
|
|
51
|
+
interface LlmResponse {
|
|
52
|
+
content: string;
|
|
53
|
+
usedTokens: number;
|
|
54
|
+
}
|
|
55
|
+
interface LlmProvider {
|
|
56
|
+
complete(request: LlmRequest): Promise<LlmResponse>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare function generateReleaseNotes(data: PrismData, opts: ReleaseNotesOpts, provider: LlmProvider): Promise<GenerationResult>;
|
|
60
|
+
|
|
61
|
+
declare function readPrismDirectory(prismPath: string): Promise<PrismData>;
|
|
62
|
+
|
|
63
|
+
export { type GenerationResult, type LlmMessage, type LlmProvider, type LlmRequest, type LlmResponse, type PrismData, type PrismInsight, type PrismRecommendation, type PrismSession, type ReleaseNotesOpts, generateReleaseNotes, readPrismDirectory };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
interface PrismSession {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
summary?: string;
|
|
5
|
+
conclusion?: string;
|
|
6
|
+
createdAt?: string;
|
|
7
|
+
}
|
|
8
|
+
interface PrismRecommendation {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
severity?: "low" | "medium" | "high" | "critical";
|
|
12
|
+
accepted?: boolean;
|
|
13
|
+
description?: string;
|
|
14
|
+
}
|
|
15
|
+
interface PrismInsight {
|
|
16
|
+
id: string;
|
|
17
|
+
title: string;
|
|
18
|
+
body?: string;
|
|
19
|
+
}
|
|
20
|
+
interface PrismData {
|
|
21
|
+
sessions?: PrismSession[];
|
|
22
|
+
recommendations?: PrismRecommendation[];
|
|
23
|
+
insights?: PrismInsight[];
|
|
24
|
+
fromDate?: string;
|
|
25
|
+
toDate?: string;
|
|
26
|
+
}
|
|
27
|
+
interface ReleaseNotesOpts {
|
|
28
|
+
tone?: "professional" | "casual" | "technical";
|
|
29
|
+
length?: "short" | "medium" | "long";
|
|
30
|
+
format?: "markdown" | "plain";
|
|
31
|
+
}
|
|
32
|
+
interface GenerationResult {
|
|
33
|
+
text: string;
|
|
34
|
+
metadata: {
|
|
35
|
+
generatedAt: string;
|
|
36
|
+
usedTokens: number;
|
|
37
|
+
generator: "forgesmith";
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface LlmMessage {
|
|
42
|
+
role: "user" | "assistant";
|
|
43
|
+
content: string;
|
|
44
|
+
}
|
|
45
|
+
interface LlmRequest {
|
|
46
|
+
model?: string;
|
|
47
|
+
messages: LlmMessage[];
|
|
48
|
+
maxTokens?: number;
|
|
49
|
+
systemPrompt?: string;
|
|
50
|
+
}
|
|
51
|
+
interface LlmResponse {
|
|
52
|
+
content: string;
|
|
53
|
+
usedTokens: number;
|
|
54
|
+
}
|
|
55
|
+
interface LlmProvider {
|
|
56
|
+
complete(request: LlmRequest): Promise<LlmResponse>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare function generateReleaseNotes(data: PrismData, opts: ReleaseNotesOpts, provider: LlmProvider): Promise<GenerationResult>;
|
|
60
|
+
|
|
61
|
+
declare function readPrismDirectory(prismPath: string): Promise<PrismData>;
|
|
62
|
+
|
|
63
|
+
export { type GenerationResult, type LlmMessage, type LlmProvider, type LlmRequest, type LlmResponse, type PrismData, type PrismInsight, type PrismRecommendation, type PrismSession, type ReleaseNotesOpts, generateReleaseNotes, readPrismDirectory };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// src/generators/releaseNotes.ts
|
|
5
|
+
function buildSystemPrompt() {
|
|
6
|
+
return `You are a technical writer and developer-relations expert. You generate clear, accurate release notes from structured code-intelligence data. Write only the release notes \u2014 no preamble, no meta-commentary.`;
|
|
7
|
+
}
|
|
8
|
+
function buildUserPrompt(data, opts) {
|
|
9
|
+
const tone = opts.tone ?? "professional";
|
|
10
|
+
const length = opts.length ?? "medium";
|
|
11
|
+
const format = opts.format ?? "markdown";
|
|
12
|
+
const lengthGuide = { short: "2-3 paragraphs or bullet groups", medium: "4-6 sections", long: "comprehensive, 6+ sections with details" }[length];
|
|
13
|
+
const toneGuide = { professional: "formal, clear, business-appropriate", casual: "friendly, approachable, conversational", technical: "precise, implementation-focused, developer-centric" }[tone];
|
|
14
|
+
const hasSessions = (data.sessions?.length ?? 0) > 0;
|
|
15
|
+
const hasRecs = (data.recommendations?.length ?? 0) > 0;
|
|
16
|
+
const hasInsights = (data.insights?.length ?? 0) > 0;
|
|
17
|
+
if (!hasSessions && !hasRecs && !hasInsights) {
|
|
18
|
+
return `Generate a brief release note in ${format} format stating there are no changes to report in this period${data.fromDate ? ` (${data.fromDate} to ${data.toDate ?? "now"})` : ""}.`;
|
|
19
|
+
}
|
|
20
|
+
const lines = [];
|
|
21
|
+
lines.push(`Generate release notes with the following requirements:`);
|
|
22
|
+
lines.push(`- Tone: ${tone} (${toneGuide})`);
|
|
23
|
+
lines.push(`- Length: ${length} (${lengthGuide})`);
|
|
24
|
+
lines.push(`- Format: ${format}`);
|
|
25
|
+
if (data.fromDate) lines.push(`- Period: ${data.fromDate}${data.toDate ? ` to ${data.toDate}` : " to now"}`);
|
|
26
|
+
lines.push(``);
|
|
27
|
+
if (hasSessions) {
|
|
28
|
+
lines.push(`## Sessions (${data.sessions.length})`);
|
|
29
|
+
for (const s of data.sessions) {
|
|
30
|
+
lines.push(`- **${s.title}**${s.summary ? `: ${s.summary}` : ""}${s.conclusion ? ` | Conclusion: ${s.conclusion}` : ""}`);
|
|
31
|
+
}
|
|
32
|
+
lines.push(``);
|
|
33
|
+
}
|
|
34
|
+
if (hasRecs) {
|
|
35
|
+
lines.push(`## Recommendations (${data.recommendations.length})`);
|
|
36
|
+
for (const r of data.recommendations) {
|
|
37
|
+
const accepted = r.accepted === true ? " [ACCEPTED]" : r.accepted === false ? " [DECLINED]" : "";
|
|
38
|
+
lines.push(`- [${r.severity?.toUpperCase() ?? "INFO"}${accepted}] **${r.title}**${r.description ? `: ${r.description}` : ""}`);
|
|
39
|
+
}
|
|
40
|
+
lines.push(``);
|
|
41
|
+
}
|
|
42
|
+
if (hasInsights) {
|
|
43
|
+
lines.push(`## Insights (${data.insights.length})`);
|
|
44
|
+
for (const i of data.insights) {
|
|
45
|
+
lines.push(`- **${i.title}**${i.body ? `: ${i.body}` : ""}`);
|
|
46
|
+
}
|
|
47
|
+
lines.push(``);
|
|
48
|
+
}
|
|
49
|
+
return lines.join("\n");
|
|
50
|
+
}
|
|
51
|
+
async function generateReleaseNotes(data, opts, provider) {
|
|
52
|
+
const systemPrompt = buildSystemPrompt();
|
|
53
|
+
const userPrompt = buildUserPrompt(data, opts);
|
|
54
|
+
const response = await provider.complete({
|
|
55
|
+
systemPrompt,
|
|
56
|
+
messages: [{ role: "user", content: userPrompt }],
|
|
57
|
+
maxTokens: 2048
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
text: response.content,
|
|
61
|
+
metadata: {
|
|
62
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
63
|
+
usedTokens: response.usedTokens,
|
|
64
|
+
generator: "forgesmith"
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async function readJsonFiles(dir) {
|
|
69
|
+
try {
|
|
70
|
+
const entries = await fs.readdir(dir);
|
|
71
|
+
const results = [];
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (!entry.endsWith(".json")) continue;
|
|
74
|
+
try {
|
|
75
|
+
const raw = await fs.readFile(path.join(dir, entry), "utf-8");
|
|
76
|
+
results.push(JSON.parse(raw));
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return results;
|
|
81
|
+
} catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function readPrismDirectory(prismPath) {
|
|
86
|
+
const sessionsDir = path.join(prismPath, "sessions");
|
|
87
|
+
const recsDir = path.join(prismPath, "recommendations");
|
|
88
|
+
const insightsDir = path.join(prismPath, "green", "insights", "accepted");
|
|
89
|
+
const [sessions, recommendations, insights] = await Promise.all([
|
|
90
|
+
readJsonFiles(sessionsDir),
|
|
91
|
+
readJsonFiles(recsDir),
|
|
92
|
+
readJsonFiles(insightsDir)
|
|
93
|
+
]);
|
|
94
|
+
return { sessions, recommendations, insights };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { generateReleaseNotes, readPrismDirectory };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Anthropic = require('@anthropic-ai/sdk');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var Anthropic__default = /*#__PURE__*/_interopDefault(Anthropic);
|
|
8
|
+
|
|
9
|
+
// src/llm/providers/direct.ts
|
|
10
|
+
var DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
11
|
+
var DirectLlmProvider = class {
|
|
12
|
+
client;
|
|
13
|
+
defaultModel;
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.client = new Anthropic__default.default({ apiKey: opts.apiKey });
|
|
16
|
+
this.defaultModel = opts.model ?? DEFAULT_MODEL;
|
|
17
|
+
}
|
|
18
|
+
async complete(request) {
|
|
19
|
+
const model = request.model ?? this.defaultModel;
|
|
20
|
+
const maxTokens = request.maxTokens ?? 4096;
|
|
21
|
+
const response = await this.client.messages.create({
|
|
22
|
+
model,
|
|
23
|
+
max_tokens: maxTokens,
|
|
24
|
+
system: request.systemPrompt,
|
|
25
|
+
messages: request.messages.map((m) => ({
|
|
26
|
+
role: m.role,
|
|
27
|
+
content: m.content
|
|
28
|
+
}))
|
|
29
|
+
});
|
|
30
|
+
const content = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
31
|
+
const usedTokens = (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0);
|
|
32
|
+
return { content, usedTokens };
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
exports.DirectLlmProvider = DirectLlmProvider;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
interface LlmMessage {
|
|
2
|
+
role: "user" | "assistant";
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
interface LlmRequest {
|
|
6
|
+
model?: string;
|
|
7
|
+
messages: LlmMessage[];
|
|
8
|
+
maxTokens?: number;
|
|
9
|
+
systemPrompt?: string;
|
|
10
|
+
}
|
|
11
|
+
interface LlmResponse {
|
|
12
|
+
content: string;
|
|
13
|
+
usedTokens: number;
|
|
14
|
+
}
|
|
15
|
+
interface LlmProvider {
|
|
16
|
+
complete(request: LlmRequest): Promise<LlmResponse>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface DirectLlmProviderOptions {
|
|
20
|
+
apiKey: string;
|
|
21
|
+
model?: string;
|
|
22
|
+
}
|
|
23
|
+
declare class DirectLlmProvider implements LlmProvider {
|
|
24
|
+
private client;
|
|
25
|
+
private defaultModel;
|
|
26
|
+
constructor(opts: DirectLlmProviderOptions);
|
|
27
|
+
complete(request: LlmRequest): Promise<LlmResponse>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { DirectLlmProvider, type DirectLlmProviderOptions };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
interface LlmMessage {
|
|
2
|
+
role: "user" | "assistant";
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
interface LlmRequest {
|
|
6
|
+
model?: string;
|
|
7
|
+
messages: LlmMessage[];
|
|
8
|
+
maxTokens?: number;
|
|
9
|
+
systemPrompt?: string;
|
|
10
|
+
}
|
|
11
|
+
interface LlmResponse {
|
|
12
|
+
content: string;
|
|
13
|
+
usedTokens: number;
|
|
14
|
+
}
|
|
15
|
+
interface LlmProvider {
|
|
16
|
+
complete(request: LlmRequest): Promise<LlmResponse>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface DirectLlmProviderOptions {
|
|
20
|
+
apiKey: string;
|
|
21
|
+
model?: string;
|
|
22
|
+
}
|
|
23
|
+
declare class DirectLlmProvider implements LlmProvider {
|
|
24
|
+
private client;
|
|
25
|
+
private defaultModel;
|
|
26
|
+
constructor(opts: DirectLlmProviderOptions);
|
|
27
|
+
complete(request: LlmRequest): Promise<LlmResponse>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { DirectLlmProvider, type DirectLlmProviderOptions };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
|
|
3
|
+
// src/llm/providers/direct.ts
|
|
4
|
+
var DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
5
|
+
var DirectLlmProvider = class {
|
|
6
|
+
client;
|
|
7
|
+
defaultModel;
|
|
8
|
+
constructor(opts) {
|
|
9
|
+
this.client = new Anthropic({ apiKey: opts.apiKey });
|
|
10
|
+
this.defaultModel = opts.model ?? DEFAULT_MODEL;
|
|
11
|
+
}
|
|
12
|
+
async complete(request) {
|
|
13
|
+
const model = request.model ?? this.defaultModel;
|
|
14
|
+
const maxTokens = request.maxTokens ?? 4096;
|
|
15
|
+
const response = await this.client.messages.create({
|
|
16
|
+
model,
|
|
17
|
+
max_tokens: maxTokens,
|
|
18
|
+
system: request.systemPrompt,
|
|
19
|
+
messages: request.messages.map((m) => ({
|
|
20
|
+
role: m.role,
|
|
21
|
+
content: m.content
|
|
22
|
+
}))
|
|
23
|
+
});
|
|
24
|
+
const content = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
25
|
+
const usedTokens = (response.usage?.input_tokens ?? 0) + (response.usage?.output_tokens ?? 0);
|
|
26
|
+
return { content, usedTokens };
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { DirectLlmProvider };
|
package/package.json
CHANGED
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forgesmith",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "forgesmith — content
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "forgesmith — content & asset-generation engine. Forge release notes, blog posts, social copy from prism0x2A data.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs",
|
|
14
|
+
"default": "./dist/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./providers": {
|
|
17
|
+
"types": "./dist/providers.d.ts",
|
|
18
|
+
"import": "./dist/providers.mjs",
|
|
19
|
+
"require": "./dist/providers.cjs",
|
|
20
|
+
"default": "./dist/providers.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"CHANGELOG.md"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"prepare": "npm run build",
|
|
34
|
+
"prepublishOnly": "npm run build && npm test"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@anthropic-ai/sdk": "^0.37.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.0.0",
|
|
41
|
+
"tsup": "^8.3.5",
|
|
42
|
+
"typescript": "^5.7.2",
|
|
43
|
+
"vitest": "^2.1.8"
|
|
44
|
+
},
|
|
6
45
|
"license": "UNLICENSED",
|
|
7
46
|
"private": false,
|
|
8
47
|
"homepage": "https://github.com/dadenjo/forgesmith",
|
|
9
|
-
"
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "https://github.com/dadenjo/forgesmith.git"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"code-intelligence",
|
|
54
|
+
"content-generation",
|
|
55
|
+
"release-notes",
|
|
56
|
+
"marketing",
|
|
57
|
+
"developer-tools",
|
|
58
|
+
"prism0x2a"
|
|
59
|
+
],
|
|
10
60
|
"author": "dadenjo"
|
|
11
61
|
}
|
package/index.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// forgesmith — placeholder reservation. Real release follows prism0x2A v0.3.x.
|
|
2
|
-
// The lens observes. The smith forges.
|
|
3
|
-
// See: https://github.com/dadenjo/forgesmith
|
|
4
|
-
console.log("forgesmith — engine for forge0x2B. Reserved.");
|
|
5
|
-
console.log("The lens observes. The smith forges.");
|