@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.
Files changed (70) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +46 -0
  3. package/bin/sourcepress.js +2 -0
  4. package/dist/__tests__/config.test.d.ts +2 -0
  5. package/dist/__tests__/config.test.d.ts.map +1 -0
  6. package/dist/__tests__/config.test.js +70 -0
  7. package/dist/__tests__/config.test.js.map +1 -0
  8. package/dist/commands/build.d.ts +3 -0
  9. package/dist/commands/build.d.ts.map +1 -0
  10. package/dist/commands/build.js +27 -0
  11. package/dist/commands/build.js.map +1 -0
  12. package/dist/commands/dev.d.ts +3 -0
  13. package/dist/commands/dev.d.ts.map +1 -0
  14. package/dist/commands/dev.js +21 -0
  15. package/dist/commands/dev.js.map +1 -0
  16. package/dist/commands/eval.d.ts +3 -0
  17. package/dist/commands/eval.d.ts.map +1 -0
  18. package/dist/commands/eval.js +40 -0
  19. package/dist/commands/eval.js.map +1 -0
  20. package/dist/commands/gaps.d.ts +3 -0
  21. package/dist/commands/gaps.d.ts.map +1 -0
  22. package/dist/commands/gaps.js +31 -0
  23. package/dist/commands/gaps.js.map +1 -0
  24. package/dist/commands/graph.d.ts +3 -0
  25. package/dist/commands/graph.d.ts.map +1 -0
  26. package/dist/commands/graph.js +33 -0
  27. package/dist/commands/graph.js.map +1 -0
  28. package/dist/commands/init.d.ts +2 -0
  29. package/dist/commands/init.d.ts.map +1 -0
  30. package/dist/commands/init.js +117 -0
  31. package/dist/commands/init.js.map +1 -0
  32. package/dist/commands/stale.d.ts +3 -0
  33. package/dist/commands/stale.d.ts.map +1 -0
  34. package/dist/commands/stale.js +35 -0
  35. package/dist/commands/stale.js.map +1 -0
  36. package/dist/commands/status.d.ts +4 -0
  37. package/dist/commands/status.d.ts.map +1 -0
  38. package/dist/commands/status.js +26 -0
  39. package/dist/commands/status.js.map +1 -0
  40. package/dist/commands/sync.d.ts +3 -0
  41. package/dist/commands/sync.d.ts.map +1 -0
  42. package/dist/commands/sync.js +22 -0
  43. package/dist/commands/sync.js.map +1 -0
  44. package/dist/config.d.ts +3 -0
  45. package/dist/config.d.ts.map +1 -0
  46. package/dist/config.js +42 -0
  47. package/dist/config.js.map +1 -0
  48. package/dist/engine-boot.d.ts +4 -0
  49. package/dist/engine-boot.d.ts.map +1 -0
  50. package/dist/engine-boot.js +15 -0
  51. package/dist/engine-boot.js.map +1 -0
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +133 -0
  55. package/dist/index.js.map +1 -0
  56. package/package.json +28 -0
  57. package/src/__tests__/config.test.ts +80 -0
  58. package/src/commands/build.ts +29 -0
  59. package/src/commands/dev.ts +27 -0
  60. package/src/commands/eval.ts +47 -0
  61. package/src/commands/gaps.ts +38 -0
  62. package/src/commands/graph.ts +41 -0
  63. package/src/commands/init.ts +123 -0
  64. package/src/commands/stale.ts +40 -0
  65. package/src/commands/status.ts +37 -0
  66. package/src/commands/sync.ts +28 -0
  67. package/src/config.ts +47 -0
  68. package/src/engine-boot.ts +17 -0
  69. package/src/index.ts +146 -0
  70. package/tsconfig.json +5 -0
@@ -0,0 +1,40 @@
1
+ import type { ContentFile } from "@sourcepress/core";
2
+ import type { EngineContext } from "@sourcepress/server";
3
+
4
+ export async function stale(engine: EngineContext): Promise<void> {
5
+ console.log("Checking for stale content...\n");
6
+
7
+ const collections = engine.listCollections();
8
+ let staleCount = 0;
9
+
10
+ for (const name of collections) {
11
+ let files: ContentFile[];
12
+ try {
13
+ files = await engine.listContent(name);
14
+ } catch (err) {
15
+ console.error(` ${name}: error — ${err instanceof Error ? err.message : String(err)}`);
16
+ continue;
17
+ }
18
+
19
+ for (const file of files) {
20
+ const generatedAt = file.frontmatter?.generated_at as string | undefined;
21
+ const staleSources = file.frontmatter?.stale_sources as string[] | undefined;
22
+
23
+ if (staleSources && staleSources.length > 0) {
24
+ console.log(` [stale] ${file.path}`);
25
+ console.log(` Stale sources: ${staleSources.join(", ")}`);
26
+ if (generatedAt) {
27
+ console.log(` Generated at: ${generatedAt}`);
28
+ }
29
+ staleCount++;
30
+ }
31
+ }
32
+ }
33
+
34
+ if (staleCount === 0) {
35
+ console.log("All content is up to date.");
36
+ } else {
37
+ console.log(`\n${staleCount} stale file${staleCount !== 1 ? "s" : ""} found.`);
38
+ console.log("Run `sourcepress eval --run` to regenerate stale content.");
39
+ }
40
+ }
@@ -0,0 +1,37 @@
1
+ import type { SourcePressConfig } from "@sourcepress/core";
2
+ import type { EngineContext } from "@sourcepress/server";
3
+
4
+ export async function status(engine: EngineContext, config: SourcePressConfig): Promise<void> {
5
+ console.log("SourcePress status\n");
6
+
7
+ console.log(` Repository: ${config.repository.owner}/${config.repository.repo}`);
8
+ console.log(` Branch: ${config.repository.branch}`);
9
+ console.log(` AI provider: ${config.ai.provider} (${config.ai.model})`);
10
+ console.log(` GitHub: ${process.env.GITHUB_TOKEN ? "configured" : "MISSING"}`);
11
+ console.log(` AI key: ${process.env.AI_API_KEY ? "configured" : "not set"}`);
12
+
13
+ const collections = engine.listCollections();
14
+ console.log(
15
+ ` Collections: ${collections.length}${collections.length > 0 ? ` (${collections.join(", ")})` : ""}`,
16
+ );
17
+
18
+ // Knowledge graph summary
19
+ try {
20
+ const knowledgeFiles = await engine.knowledgeStore.list();
21
+ console.log(
22
+ ` Knowledge: ${knowledgeFiles.length} file${knowledgeFiles.length !== 1 ? "s" : ""}`,
23
+ );
24
+ } catch {
25
+ console.log(" Knowledge: unavailable");
26
+ }
27
+
28
+ // Cache info
29
+ const cacheBackend = config.cache?.backend ?? "memory";
30
+ console.log(` Cache: ${cacheBackend}`);
31
+
32
+ // Jobs
33
+ const jobsBackend = config.jobs?.backend ?? "in-process";
34
+ console.log(` Jobs: ${jobsBackend}`);
35
+
36
+ console.log("");
37
+ }
@@ -0,0 +1,28 @@
1
+ import type { EngineContext } from "@sourcepress/server";
2
+
3
+ export async function sync(engine: EngineContext): Promise<void> {
4
+ console.log("Syncing content from GitHub...\n");
5
+
6
+ const collections = engine.listCollections();
7
+
8
+ if (collections.length === 0) {
9
+ console.log(" No collections configured.");
10
+ console.log("\nSync complete.");
11
+ return;
12
+ }
13
+
14
+ let total = 0;
15
+ for (const name of collections) {
16
+ try {
17
+ const files = await engine.listContent(name);
18
+ console.log(` ${name}: ${files.length} file${files.length !== 1 ? "s" : ""}`);
19
+ total += files.length;
20
+ } catch (err) {
21
+ console.error(` ${name}: error — ${err instanceof Error ? err.message : String(err)}`);
22
+ }
23
+ }
24
+
25
+ console.log(
26
+ `\nSync complete. ${total} file${total !== 1 ? "s" : ""} across ${collections.length} collection${collections.length !== 1 ? "s" : ""}.`,
27
+ );
28
+ }
package/src/config.ts ADDED
@@ -0,0 +1,47 @@
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
+ import type { SourcePressConfig } from "@sourcepress/core";
6
+
7
+ async function importTsFile(fullPath: string): Promise<unknown> {
8
+ const { tsImport } = await import("tsx/esm/api");
9
+ return tsImport(pathToFileURL(fullPath).href, import.meta.url);
10
+ }
11
+
12
+ export async function loadConfig(cwd: string): Promise<SourcePressConfig | null> {
13
+ const candidates = ["sourcepress.config.ts", "sourcepress.config.js"];
14
+ for (const name of candidates) {
15
+ const fullPath = join(cwd, name);
16
+ if (existsSync(fullPath)) {
17
+ let mod: unknown;
18
+ try {
19
+ if (name.endsWith(".ts")) {
20
+ mod = await importTsFile(fullPath);
21
+ } else {
22
+ mod = await import(pathToFileURL(fullPath).href);
23
+ }
24
+ } catch (err) {
25
+ throw new Error(
26
+ `Failed to load ${name}: ${err instanceof Error ? err.message : String(err)}`,
27
+ );
28
+ }
29
+ let raw =
30
+ mod != null && typeof mod === "object" && "default" in mod
31
+ ? (mod as { default: unknown }).default
32
+ : mod;
33
+ // tsx wraps modules with an extra layer — unwrap if needed
34
+ if (raw != null && typeof raw === "object" && "default" in raw) {
35
+ raw = (raw as { default: unknown }).default;
36
+ }
37
+ const result = validateConfig(raw);
38
+ if (!result.success) {
39
+ throw new Error(
40
+ `Invalid sourcepress config in ${name}:\n${result.errors.map((e) => ` - ${e}`).join("\n")}`,
41
+ );
42
+ }
43
+ return result.data as SourcePressConfig;
44
+ }
45
+ }
46
+ return null;
47
+ }
@@ -0,0 +1,17 @@
1
+ import type { SourcePressConfig } from "@sourcepress/core";
2
+ import { createEngine } from "@sourcepress/server";
3
+ import type { EngineContext } from "@sourcepress/server";
4
+
5
+ export async function bootEngine(config: SourcePressConfig): Promise<EngineContext> {
6
+ const githubToken = process.env.GITHUB_TOKEN;
7
+ if (!githubToken) {
8
+ console.error("Error: GITHUB_TOKEN environment variable is required.");
9
+ console.error("Set it: export GITHUB_TOKEN=ghp_...");
10
+ process.exit(1);
11
+ }
12
+ return createEngine({
13
+ config,
14
+ githubToken,
15
+ aiApiKey: process.env.AI_API_KEY,
16
+ });
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,146 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import type { SourcePressConfig } from "@sourcepress/core";
4
+ import { build } from "./commands/build.js";
5
+ import { dev } from "./commands/dev.js";
6
+ import { evalCmd } from "./commands/eval.js";
7
+ import { gaps } from "./commands/gaps.js";
8
+ import { graph } from "./commands/graph.js";
9
+ import { init } from "./commands/init.js";
10
+ import { stale } from "./commands/stale.js";
11
+ import { status } from "./commands/status.js";
12
+ import { sync } from "./commands/sync.js";
13
+ import { loadConfig } from "./config.js";
14
+ import { bootEngine } from "./engine-boot.js";
15
+
16
+ function loadEnvFile(dir: string): void {
17
+ const envPath = join(dir, ".env");
18
+ if (!existsSync(envPath)) return;
19
+
20
+ const content = readFileSync(envPath, "utf-8");
21
+ for (const line of content.split("\n")) {
22
+ const trimmed = line.trim();
23
+ if (!trimmed || trimmed.startsWith("#")) continue;
24
+ const eqIndex = trimmed.indexOf("=");
25
+ if (eqIndex === -1) continue;
26
+ const key = trimmed.slice(0, eqIndex).trim();
27
+ let value = trimmed.slice(eqIndex + 1).trim();
28
+ if (
29
+ (value.startsWith('"') && value.endsWith('"')) ||
30
+ (value.startsWith("'") && value.endsWith("'"))
31
+ ) {
32
+ value = value.slice(1, -1);
33
+ }
34
+ if (!(key in process.env)) {
35
+ process.env[key] = value;
36
+ }
37
+ }
38
+ }
39
+
40
+ const COMMANDS_NEEDING_CONFIG = [
41
+ "dev",
42
+ "build",
43
+ "sync",
44
+ "graph",
45
+ "stale",
46
+ "eval",
47
+ "status",
48
+ "gaps",
49
+ ];
50
+
51
+ function printHelp(): void {
52
+ console.log("sourcepress — Open source Content Engine with knowledge graph\n");
53
+ console.log("Commands:");
54
+ console.log(" init Initialize a new SourcePress project");
55
+ console.log(" dev Start the Content Engine server");
56
+ console.log(" build Sync content + build site");
57
+ console.log(" sync Sync content from GitHub");
58
+ console.log(" graph Show/rebuild knowledge graph");
59
+ console.log(" stale List stale content");
60
+ console.log(" eval Run evals");
61
+ console.log(" status Health check");
62
+ console.log(" gaps List knowledge gaps");
63
+ console.log("\nOptions:");
64
+ console.log(" --help Show this help");
65
+ console.log(" --version Show version");
66
+ }
67
+
68
+ function printVersion(): void {
69
+ console.log("0.1.0");
70
+ }
71
+
72
+ async function main(): Promise<void> {
73
+ loadEnvFile(process.cwd());
74
+ const args = process.argv.slice(2);
75
+ const command = args[0];
76
+
77
+ if (command === "init") {
78
+ init(process.cwd());
79
+ return;
80
+ }
81
+
82
+ if (!command || command === "--help") {
83
+ printHelp();
84
+ return;
85
+ }
86
+
87
+ if (command === "--version") {
88
+ printVersion();
89
+ return;
90
+ }
91
+
92
+ if (COMMANDS_NEEDING_CONFIG.includes(command)) {
93
+ let config: SourcePressConfig | null;
94
+ try {
95
+ config = await loadConfig(process.cwd());
96
+ } catch (err) {
97
+ console.error(err instanceof Error ? err.message : String(err));
98
+ process.exit(1);
99
+ }
100
+
101
+ if (!config) {
102
+ console.error("No sourcepress.config.ts found. Run `sourcepress init` first.");
103
+ process.exit(1);
104
+ }
105
+
106
+ const engine = await bootEngine(config);
107
+
108
+ // Command dispatch
109
+ switch (command) {
110
+ case "dev":
111
+ await dev(engine, args.slice(1));
112
+ break;
113
+ case "build":
114
+ await build(engine, process.cwd());
115
+ break;
116
+ case "sync":
117
+ await sync(engine);
118
+ break;
119
+ case "graph":
120
+ await graph(engine, args.slice(1));
121
+ break;
122
+ case "stale":
123
+ await stale(engine);
124
+ break;
125
+ case "eval":
126
+ await evalCmd(engine, args.slice(1));
127
+ break;
128
+ case "status":
129
+ await status(engine, config);
130
+ break;
131
+ case "gaps":
132
+ await gaps(engine);
133
+ break;
134
+ }
135
+ return;
136
+ }
137
+
138
+ console.error(`Unknown command: ${command}`);
139
+ console.error("Run `sourcepress --help` for usage.");
140
+ process.exit(1);
141
+ }
142
+
143
+ main().catch((err: unknown) => {
144
+ console.error(err instanceof Error ? err.message : String(err));
145
+ process.exit(1);
146
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": { "outDir": "dist", "rootDir": "src", "types": ["node"] },
4
+ "include": ["src"]
5
+ }