@sourcepress/cli 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/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +46 -0
- package/bin/sourcepress.js +2 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +70 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/commands/build.d.ts +3 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +27 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/dev.d.ts +3 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +21 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/eval.d.ts +3 -0
- package/dist/commands/eval.d.ts.map +1 -0
- package/dist/commands/eval.js +40 -0
- package/dist/commands/eval.js.map +1 -0
- package/dist/commands/gaps.d.ts +3 -0
- package/dist/commands/gaps.d.ts.map +1 -0
- package/dist/commands/gaps.js +31 -0
- package/dist/commands/gaps.js.map +1 -0
- package/dist/commands/graph.d.ts +3 -0
- package/dist/commands/graph.d.ts.map +1 -0
- package/dist/commands/graph.js +33 -0
- package/dist/commands/graph.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +117 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/stale.d.ts +3 -0
- package/dist/commands/stale.d.ts.map +1 -0
- package/dist/commands/stale.js +35 -0
- package/dist/commands/stale.js.map +1 -0
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +26 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +3 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +22 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +42 -0
- package/dist/config.js.map +1 -0
- package/dist/engine-boot.d.ts +4 -0
- package/dist/engine-boot.d.ts.map +1 -0
- package/dist/engine-boot.js +15 -0
- package/dist/engine-boot.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +133 -0
- package/dist/index.js.map +1 -0
- package/package.json +28 -0
- package/src/__tests__/config.test.ts +80 -0
- package/src/commands/build.ts +29 -0
- package/src/commands/dev.ts +27 -0
- package/src/commands/eval.ts +47 -0
- package/src/commands/gaps.ts +38 -0
- package/src/commands/graph.ts +41 -0
- package/src/commands/init.ts +123 -0
- package/src/commands/stale.ts +40 -0
- package/src/commands/status.ts +37 -0
- package/src/commands/sync.ts +28 -0
- package/src/config.ts +47 -0
- package/src/engine-boot.ts +17 -0
- package/src/index.ts +146 -0
- package/tsconfig.json +5 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAO3D,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAmC/E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { validateConfig } from "@sourcepress/core";
|
|
5
|
+
async function importTsFile(fullPath) {
|
|
6
|
+
const { tsImport } = await import("tsx/esm/api");
|
|
7
|
+
return tsImport(pathToFileURL(fullPath).href, import.meta.url);
|
|
8
|
+
}
|
|
9
|
+
export async function loadConfig(cwd) {
|
|
10
|
+
const candidates = ["sourcepress.config.ts", "sourcepress.config.js"];
|
|
11
|
+
for (const name of candidates) {
|
|
12
|
+
const fullPath = join(cwd, name);
|
|
13
|
+
if (existsSync(fullPath)) {
|
|
14
|
+
let mod;
|
|
15
|
+
try {
|
|
16
|
+
if (name.endsWith(".ts")) {
|
|
17
|
+
mod = await importTsFile(fullPath);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
mod = await import(pathToFileURL(fullPath).href);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
throw new Error(`Failed to load ${name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
25
|
+
}
|
|
26
|
+
let raw = mod != null && typeof mod === "object" && "default" in mod
|
|
27
|
+
? mod.default
|
|
28
|
+
: mod;
|
|
29
|
+
// tsx wraps modules with an extra layer — unwrap if needed
|
|
30
|
+
if (raw != null && typeof raw === "object" && "default" in raw) {
|
|
31
|
+
raw = raw.default;
|
|
32
|
+
}
|
|
33
|
+
const result = validateConfig(raw);
|
|
34
|
+
if (!result.success) {
|
|
35
|
+
throw new Error(`Invalid sourcepress config in ${name}:\n${result.errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
36
|
+
}
|
|
37
|
+
return result.data;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,KAAK,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IACjD,OAAO,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC3C,MAAM,UAAU,GAAG,CAAC,uBAAuB,EAAE,uBAAuB,CAAC,CAAC;IACtE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACjC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,IAAI,GAAY,CAAC;YACjB,IAAI,CAAC;gBACJ,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1B,GAAG,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACP,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC;gBAClD,CAAC;YACF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CACd,kBAAkB,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC7E,CAAC;YACH,CAAC;YACD,IAAI,GAAG,GACN,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,SAAS,IAAI,GAAG;gBACzD,CAAC,CAAE,GAA4B,CAAC,OAAO;gBACvC,CAAC,CAAC,GAAG,CAAC;YACR,2DAA2D;YAC3D,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;gBAChE,GAAG,GAAI,GAA4B,CAAC,OAAO,CAAC;YAC7C,CAAC;YACD,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;YACnC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CACd,iCAAiC,IAAI,MAAM,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5F,CAAC;YACH,CAAC;YACD,OAAO,MAAM,CAAC,IAAyB,CAAC;QACzC,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-boot.d.ts","sourceRoot":"","sources":["../src/engine-boot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,wBAAsB,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAYlF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createEngine } from "@sourcepress/server";
|
|
2
|
+
export async function bootEngine(config) {
|
|
3
|
+
const githubToken = process.env.GITHUB_TOKEN;
|
|
4
|
+
if (!githubToken) {
|
|
5
|
+
console.error("Error: GITHUB_TOKEN environment variable is required.");
|
|
6
|
+
console.error("Set it: export GITHUB_TOKEN=ghp_...");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
return createEngine({
|
|
10
|
+
config,
|
|
11
|
+
githubToken,
|
|
12
|
+
aiApiKey: process.env.AI_API_KEY,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=engine-boot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-boot.js","sourceRoot":"","sources":["../src/engine-boot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAyB;IACzD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC7C,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;QACvE,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,YAAY,CAAC;QACnB,MAAM;QACN,WAAW;QACX,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;KAChC,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { build } from "./commands/build.js";
|
|
4
|
+
import { dev } from "./commands/dev.js";
|
|
5
|
+
import { evalCmd } from "./commands/eval.js";
|
|
6
|
+
import { gaps } from "./commands/gaps.js";
|
|
7
|
+
import { graph } from "./commands/graph.js";
|
|
8
|
+
import { init } from "./commands/init.js";
|
|
9
|
+
import { stale } from "./commands/stale.js";
|
|
10
|
+
import { status } from "./commands/status.js";
|
|
11
|
+
import { sync } from "./commands/sync.js";
|
|
12
|
+
import { loadConfig } from "./config.js";
|
|
13
|
+
import { bootEngine } from "./engine-boot.js";
|
|
14
|
+
function loadEnvFile(dir) {
|
|
15
|
+
const envPath = join(dir, ".env");
|
|
16
|
+
if (!existsSync(envPath))
|
|
17
|
+
return;
|
|
18
|
+
const content = readFileSync(envPath, "utf-8");
|
|
19
|
+
for (const line of content.split("\n")) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
22
|
+
continue;
|
|
23
|
+
const eqIndex = trimmed.indexOf("=");
|
|
24
|
+
if (eqIndex === -1)
|
|
25
|
+
continue;
|
|
26
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
27
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
28
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
29
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
30
|
+
value = value.slice(1, -1);
|
|
31
|
+
}
|
|
32
|
+
if (!(key in process.env)) {
|
|
33
|
+
process.env[key] = value;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const COMMANDS_NEEDING_CONFIG = [
|
|
38
|
+
"dev",
|
|
39
|
+
"build",
|
|
40
|
+
"sync",
|
|
41
|
+
"graph",
|
|
42
|
+
"stale",
|
|
43
|
+
"eval",
|
|
44
|
+
"status",
|
|
45
|
+
"gaps",
|
|
46
|
+
];
|
|
47
|
+
function printHelp() {
|
|
48
|
+
console.log("sourcepress — Open source Content Engine with knowledge graph\n");
|
|
49
|
+
console.log("Commands:");
|
|
50
|
+
console.log(" init Initialize a new SourcePress project");
|
|
51
|
+
console.log(" dev Start the Content Engine server");
|
|
52
|
+
console.log(" build Sync content + build site");
|
|
53
|
+
console.log(" sync Sync content from GitHub");
|
|
54
|
+
console.log(" graph Show/rebuild knowledge graph");
|
|
55
|
+
console.log(" stale List stale content");
|
|
56
|
+
console.log(" eval Run evals");
|
|
57
|
+
console.log(" status Health check");
|
|
58
|
+
console.log(" gaps List knowledge gaps");
|
|
59
|
+
console.log("\nOptions:");
|
|
60
|
+
console.log(" --help Show this help");
|
|
61
|
+
console.log(" --version Show version");
|
|
62
|
+
}
|
|
63
|
+
function printVersion() {
|
|
64
|
+
console.log("0.1.0");
|
|
65
|
+
}
|
|
66
|
+
async function main() {
|
|
67
|
+
loadEnvFile(process.cwd());
|
|
68
|
+
const args = process.argv.slice(2);
|
|
69
|
+
const command = args[0];
|
|
70
|
+
if (command === "init") {
|
|
71
|
+
init(process.cwd());
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!command || command === "--help") {
|
|
75
|
+
printHelp();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (command === "--version") {
|
|
79
|
+
printVersion();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (COMMANDS_NEEDING_CONFIG.includes(command)) {
|
|
83
|
+
let config;
|
|
84
|
+
try {
|
|
85
|
+
config = await loadConfig(process.cwd());
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
if (!config) {
|
|
92
|
+
console.error("No sourcepress.config.ts found. Run `sourcepress init` first.");
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
const engine = await bootEngine(config);
|
|
96
|
+
// Command dispatch
|
|
97
|
+
switch (command) {
|
|
98
|
+
case "dev":
|
|
99
|
+
await dev(engine, args.slice(1));
|
|
100
|
+
break;
|
|
101
|
+
case "build":
|
|
102
|
+
await build(engine, process.cwd());
|
|
103
|
+
break;
|
|
104
|
+
case "sync":
|
|
105
|
+
await sync(engine);
|
|
106
|
+
break;
|
|
107
|
+
case "graph":
|
|
108
|
+
await graph(engine, args.slice(1));
|
|
109
|
+
break;
|
|
110
|
+
case "stale":
|
|
111
|
+
await stale(engine);
|
|
112
|
+
break;
|
|
113
|
+
case "eval":
|
|
114
|
+
await evalCmd(engine, args.slice(1));
|
|
115
|
+
break;
|
|
116
|
+
case "status":
|
|
117
|
+
await status(engine, config);
|
|
118
|
+
break;
|
|
119
|
+
case "gaps":
|
|
120
|
+
await gaps(engine);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
console.error(`Unknown command: ${command}`);
|
|
126
|
+
console.error("Run `sourcepress --help` for usage.");
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
main().catch((err) => {
|
|
130
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
});
|
|
133
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sourcepress/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": { "access": "public" },
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": { "sourcepress": "./bin/sourcepress.js" },
|
|
7
|
+
"exports": {
|
|
8
|
+
".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" }
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"clean": "rm -rf dist",
|
|
14
|
+
"test": "vitest run"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@sourcepress/core": "workspace:*",
|
|
18
|
+
"@sourcepress/server": "workspace:*",
|
|
19
|
+
"@sourcepress/mcp": "workspace:*",
|
|
20
|
+
"@hono/node-server": "^1.13.0",
|
|
21
|
+
"tsx": "^4.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.7.0",
|
|
25
|
+
"@types/node": "^22.0.0",
|
|
26
|
+
"vitest": "^3.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { loadConfig } from "../config.js";
|
|
6
|
+
|
|
7
|
+
const VALID_CONFIG_JS = `
|
|
8
|
+
export default {
|
|
9
|
+
repository: { owner: "acme", repo: "content", branch: "main" },
|
|
10
|
+
ai: { provider: "anthropic", model: "claude-sonnet-4-5-20250514" },
|
|
11
|
+
collections: {
|
|
12
|
+
posts: {
|
|
13
|
+
name: "posts",
|
|
14
|
+
path: "content/posts/",
|
|
15
|
+
format: "mdx",
|
|
16
|
+
fields: { title: { type: "string", required: true } },
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
knowledge: { path: "knowledge/", graph: { backend: "local" } },
|
|
20
|
+
intent: { path: "intent/" },
|
|
21
|
+
};
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
let tmpDir: string;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
tmpDir = mkdtempSync(join(tmpdir(), "sp-cli-test-"));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("loadConfig", () => {
|
|
35
|
+
it("returns null when no config file exists", async () => {
|
|
36
|
+
const result = await loadConfig(tmpDir);
|
|
37
|
+
expect(result).toBeNull();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("returns validated config when sourcepress.config.js exists", async () => {
|
|
41
|
+
writeFileSync(join(tmpDir, "sourcepress.config.js"), VALID_CONFIG_JS);
|
|
42
|
+
const config = await loadConfig(tmpDir);
|
|
43
|
+
expect(config).not.toBeNull();
|
|
44
|
+
expect(config?.repository.owner).toBe("acme");
|
|
45
|
+
expect(config?.repository.repo).toBe("content");
|
|
46
|
+
expect(config?.ai.provider).toBe("anthropic");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("prefers sourcepress.config.ts over .js when both exist", async () => {
|
|
50
|
+
// .ts file would be tried first but can't be dynamically imported without tsx in test env,
|
|
51
|
+
// so we write a .ts file that is actually valid JS (no TS-only syntax) to verify ordering.
|
|
52
|
+
const tsContent = `export default {
|
|
53
|
+
repository: { owner: "ts-owner", repo: "ts-repo", branch: "main" },
|
|
54
|
+
ai: { provider: "anthropic", model: "claude-sonnet-4-5-20250514" },
|
|
55
|
+
collections: {
|
|
56
|
+
posts: {
|
|
57
|
+
name: "posts",
|
|
58
|
+
path: "content/posts/",
|
|
59
|
+
format: "mdx",
|
|
60
|
+
fields: { title: { type: "string", required: true } },
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
knowledge: { path: "knowledge/", graph: { backend: "local" } },
|
|
64
|
+
intent: { path: "intent/" },
|
|
65
|
+
};`;
|
|
66
|
+
writeFileSync(join(tmpDir, "sourcepress.config.ts"), tsContent);
|
|
67
|
+
writeFileSync(join(tmpDir, "sourcepress.config.js"), VALID_CONFIG_JS);
|
|
68
|
+
const config = await loadConfig(tmpDir);
|
|
69
|
+
// .ts is tried first — should load ts-owner
|
|
70
|
+
expect(config?.repository.owner).toBe("ts-owner");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("throws on invalid config", async () => {
|
|
74
|
+
writeFileSync(
|
|
75
|
+
join(tmpDir, "sourcepress.config.js"),
|
|
76
|
+
`export default { repository: { owner: "" } };`,
|
|
77
|
+
);
|
|
78
|
+
await expect(loadConfig(tmpDir)).rejects.toThrow(/Invalid sourcepress config/);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { EngineContext } from "@sourcepress/server";
|
|
5
|
+
import { sync } from "./sync.js";
|
|
6
|
+
|
|
7
|
+
export async function build(engine: EngineContext, cwd: string): Promise<void> {
|
|
8
|
+
// Step 1: sync content from GitHub
|
|
9
|
+
await sync(engine);
|
|
10
|
+
|
|
11
|
+
// Step 2: build site if site/ directory exists
|
|
12
|
+
const siteDir = join(cwd, "site");
|
|
13
|
+
if (existsSync(join(siteDir, "package.json"))) {
|
|
14
|
+
console.log("\nBuilding site...");
|
|
15
|
+
try {
|
|
16
|
+
execSync("npm run build", { cwd: siteDir, stdio: "inherit" });
|
|
17
|
+
console.log("Build complete.");
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error("Site build failed.");
|
|
20
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
console.log("\nNo site/ directory found. Sync-only complete.");
|
|
25
|
+
console.log(
|
|
26
|
+
"To build a site, create a site/ directory with a package.json and `build` script.",
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { serve } from "@hono/node-server";
|
|
2
|
+
import { createApp } from "@sourcepress/server";
|
|
3
|
+
import type { EngineContext } from "@sourcepress/server";
|
|
4
|
+
|
|
5
|
+
export async function dev(engine: EngineContext, args: string[]): Promise<void> {
|
|
6
|
+
const portArg = args.find((a) => a.startsWith("--port="))?.split("=")[1];
|
|
7
|
+
const port = Number(process.env.PORT ?? portArg ?? 4321);
|
|
8
|
+
|
|
9
|
+
const app = createApp(engine);
|
|
10
|
+
|
|
11
|
+
console.log(`SourcePress engine running at http://localhost:${port}`);
|
|
12
|
+
console.log(`Health: http://localhost:${port}/api/health`);
|
|
13
|
+
console.log(`Content: http://localhost:${port}/api/content`);
|
|
14
|
+
console.log("\nMCP: npx sourcepress mcp (in another terminal)");
|
|
15
|
+
console.log("\nPress Ctrl+C to stop.\n");
|
|
16
|
+
|
|
17
|
+
const server = serve({ fetch: app.fetch, port });
|
|
18
|
+
|
|
19
|
+
process.on("SIGINT", () => {
|
|
20
|
+
console.log("\nShutting down...");
|
|
21
|
+
server.close(() => process.exit(0));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
process.on("SIGTERM", () => {
|
|
25
|
+
server.close(() => process.exit(0));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ContentFile } from "@sourcepress/core";
|
|
2
|
+
import type { EngineContext } from "@sourcepress/server";
|
|
3
|
+
|
|
4
|
+
export async function evalCmd(engine: EngineContext, args: string[]): Promise<void> {
|
|
5
|
+
const runLoop = args.includes("--run");
|
|
6
|
+
|
|
7
|
+
if (runLoop) {
|
|
8
|
+
console.log("Running eval loop (generate → judge → improve)...\n");
|
|
9
|
+
} else {
|
|
10
|
+
console.log("Evaluating content quality...\n");
|
|
11
|
+
console.log(" (Use --run to execute the full generate/improve loop)\n");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const collections = engine.listCollections();
|
|
15
|
+
|
|
16
|
+
if (collections.length === 0) {
|
|
17
|
+
console.log("No collections configured. Add collections to sourcepress.config.ts.");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const name of collections) {
|
|
22
|
+
let files: ContentFile[];
|
|
23
|
+
try {
|
|
24
|
+
files = await engine.listContent(name);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.error(` ${name}: error — ${err instanceof Error ? err.message : String(err)}`);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log(`${name}: ${files.length} file${files.length !== 1 ? "s" : ""}`);
|
|
31
|
+
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
const score = file.frontmatter?.eval_score as number | undefined;
|
|
34
|
+
|
|
35
|
+
if (score !== undefined) {
|
|
36
|
+
const indicator = score >= 70 ? "pass" : "fail";
|
|
37
|
+
console.log(` [${indicator}] ${file.path} — score: ${score}`);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(` [---] ${file.path} — not yet evaluated`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (runLoop) {
|
|
45
|
+
console.log("\nEval loop complete. Re-run `sourcepress eval` to see updated scores.");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { EngineContext } from "@sourcepress/server";
|
|
2
|
+
|
|
3
|
+
export async function gaps(engine: EngineContext): Promise<void> {
|
|
4
|
+
console.log("Scanning for knowledge gaps...\n");
|
|
5
|
+
|
|
6
|
+
// Gather all content files across collections
|
|
7
|
+
const collections = engine.listCollections();
|
|
8
|
+
const allContent = [];
|
|
9
|
+
|
|
10
|
+
for (const name of collections) {
|
|
11
|
+
try {
|
|
12
|
+
const files = await engine.listContent(name);
|
|
13
|
+
allContent.push(...files);
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.error(` ${name}: error — ${err instanceof Error ? err.message : String(err)}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Use the knowledge engine to find gaps
|
|
20
|
+
const foundGaps = engine.knowledge.findGaps(allContent);
|
|
21
|
+
|
|
22
|
+
if (foundGaps.length === 0) {
|
|
23
|
+
console.log("No knowledge gaps detected. Knowledge coverage looks complete.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(`Found ${foundGaps.length} knowledge gap${foundGaps.length !== 1 ? "s" : ""}:\n`);
|
|
28
|
+
|
|
29
|
+
for (const gap of foundGaps) {
|
|
30
|
+
console.log(` ${gap.entity_name} (${gap.entity_type})`);
|
|
31
|
+
console.log(` Knowledge files: ${gap.knowledge_file_count}`);
|
|
32
|
+
console.log(` Content files: ${gap.content_file_count}`);
|
|
33
|
+
console.log(` Reason: ${gap.reason}`);
|
|
34
|
+
console.log("");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log("Add knowledge files to the knowledge/ directory to fill these gaps.");
|
|
38
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { EngineContext } from "@sourcepress/server";
|
|
2
|
+
|
|
3
|
+
export async function graph(engine: EngineContext, args: string[]): Promise<void> {
|
|
4
|
+
const rebuild = args.includes("--rebuild");
|
|
5
|
+
|
|
6
|
+
if (rebuild) {
|
|
7
|
+
console.log("Rebuilding knowledge graph...\n");
|
|
8
|
+
try {
|
|
9
|
+
const knowledgeFiles = await engine.knowledgeStore.list();
|
|
10
|
+
console.log(
|
|
11
|
+
` Found ${knowledgeFiles.length} knowledge file${knowledgeFiles.length !== 1 ? "s" : ""}.`,
|
|
12
|
+
);
|
|
13
|
+
for (const file of knowledgeFiles) {
|
|
14
|
+
await engine.knowledge.ingest(file.path, file.body, file.source, file.source_url);
|
|
15
|
+
console.log(` Ingested: ${file.path}`);
|
|
16
|
+
}
|
|
17
|
+
console.log("\nKnowledge graph rebuilt.");
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error(`Error rebuilding graph: ${err instanceof Error ? err.message : String(err)}`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Show graph stats
|
|
25
|
+
const g = await engine.knowledgeStore.loadGraph();
|
|
26
|
+
if (!g) {
|
|
27
|
+
console.log(
|
|
28
|
+
"Knowledge graph is empty. Add knowledge files and run `sourcepress graph --rebuild`.",
|
|
29
|
+
);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const entityCount = g.entities instanceof Map ? g.entities.size : 0;
|
|
34
|
+
const relationCount = g.relations?.length ?? 0;
|
|
35
|
+
const clusterCount = g.clusters?.length ?? 0;
|
|
36
|
+
|
|
37
|
+
console.log("Knowledge graph stats:");
|
|
38
|
+
console.log(` Entities: ${entityCount}`);
|
|
39
|
+
console.log(` Relations: ${relationCount}`);
|
|
40
|
+
console.log(` Clusters: ${clusterCount}`);
|
|
41
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_CONFIG = `import { defineConfig, collection, field, relation } from '@sourcepress/core'
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
repository: {
|
|
8
|
+
owner: 'OWNER',
|
|
9
|
+
repo: 'REPO',
|
|
10
|
+
branch: 'main',
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
ai: {
|
|
14
|
+
provider: 'anthropic',
|
|
15
|
+
model: 'claude-sonnet-4-5-20250514',
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
collections: {},
|
|
19
|
+
|
|
20
|
+
knowledge: {
|
|
21
|
+
path: 'knowledge/',
|
|
22
|
+
graph: { backend: 'local' },
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
intent: {
|
|
26
|
+
path: 'intent/',
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
media: {
|
|
30
|
+
storage: 'git',
|
|
31
|
+
path: 'assets/',
|
|
32
|
+
registry: 'media.json',
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
jobs: {
|
|
36
|
+
backend: 'in-process',
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
evals: {
|
|
40
|
+
threshold: 70,
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
approval: {
|
|
44
|
+
provider: 'github-pr',
|
|
45
|
+
rules: {
|
|
46
|
+
'content/*': 'pr',
|
|
47
|
+
'knowledge/*': 'direct',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
auth: {
|
|
52
|
+
provider: 'github',
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
cache: {
|
|
56
|
+
backend: 'sqlite',
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
sync: {
|
|
60
|
+
reconciliation_interval: '1h',
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
const DEFAULT_TONE = `# Tone of Voice
|
|
66
|
+
|
|
67
|
+
Describe your brand's tone of voice here. This file guides AI content generation.
|
|
68
|
+
|
|
69
|
+
## Principles
|
|
70
|
+
- Be clear and concise
|
|
71
|
+
- Use active voice
|
|
72
|
+
- Be professional but approachable
|
|
73
|
+
|
|
74
|
+
## Forbidden words
|
|
75
|
+
- "revolutionary"
|
|
76
|
+
- "synergies"
|
|
77
|
+
- "holistic"
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const DEFAULT_JUDGE = `# Content Quality Judge
|
|
81
|
+
|
|
82
|
+
You are evaluating content quality. Score the content 0-100 based on:
|
|
83
|
+
|
|
84
|
+
1. **Factual grounding** — Every claim should be traceable to a knowledge source
|
|
85
|
+
2. **Tone match** — Does it follow the intent/tone-of-voice guidelines?
|
|
86
|
+
3. **Specificity** — Concrete details, names, numbers — not generic filler
|
|
87
|
+
4. **Structure** — Clear flow, appropriate length, good headings
|
|
88
|
+
5. **Readability** — Clear language, no jargon without explanation
|
|
89
|
+
|
|
90
|
+
Return a single JSON object:
|
|
91
|
+
{ "score": <0-100>, "reasoning": "<one paragraph explaining the score>" }
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
export function init(targetDir: string): void {
|
|
95
|
+
const dirs = ["knowledge", "content", "intent", "evals/standards", "evals/prompts", "assets"];
|
|
96
|
+
|
|
97
|
+
for (const dir of dirs) {
|
|
98
|
+
const fullPath = join(targetDir, dir);
|
|
99
|
+
if (!existsSync(fullPath)) {
|
|
100
|
+
mkdirSync(fullPath, { recursive: true });
|
|
101
|
+
console.log(` Created ${dir}/`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const files: Array<[string, string]> = [
|
|
106
|
+
["sourcepress.config.ts", DEFAULT_CONFIG],
|
|
107
|
+
["intent/tone-of-voice.md", DEFAULT_TONE],
|
|
108
|
+
["evals/judge.md", DEFAULT_JUDGE],
|
|
109
|
+
["media.json", "{}"],
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
for (const [filePath, content] of files) {
|
|
113
|
+
const fullPath = join(targetDir, filePath);
|
|
114
|
+
if (!existsSync(fullPath)) {
|
|
115
|
+
writeFileSync(fullPath, content, "utf-8");
|
|
116
|
+
console.log(` Created ${filePath}`);
|
|
117
|
+
} else {
|
|
118
|
+
console.log(` Skipped ${filePath} (already exists)`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log("\nSourcePress initialized. Edit sourcepress.config.ts to configure your project.");
|
|
123
|
+
}
|