@npow/ghostwriter 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 (113) hide show
  1. package/dist/commands/connect.d.ts +30 -0
  2. package/dist/commands/connect.d.ts.map +1 -0
  3. package/dist/commands/connect.js +442 -0
  4. package/dist/commands/connect.js.map +1 -0
  5. package/dist/commands/create/index.d.ts +6 -0
  6. package/dist/commands/create/index.d.ts.map +1 -0
  7. package/dist/commands/create/index.js +89 -0
  8. package/dist/commands/create/index.js.map +1 -0
  9. package/dist/commands/create/prompts/example-article.d.ts +3 -0
  10. package/dist/commands/create/prompts/example-article.d.ts.map +1 -0
  11. package/dist/commands/create/prompts/example-article.js +32 -0
  12. package/dist/commands/create/prompts/example-article.js.map +1 -0
  13. package/dist/commands/create/prompts/intent-parsing.d.ts +3 -0
  14. package/dist/commands/create/prompts/intent-parsing.d.ts.map +1 -0
  15. package/dist/commands/create/prompts/intent-parsing.js +45 -0
  16. package/dist/commands/create/prompts/intent-parsing.js.map +1 -0
  17. package/dist/commands/create/prompts/page-content.d.ts +3 -0
  18. package/dist/commands/create/prompts/page-content.d.ts.map +1 -0
  19. package/dist/commands/create/prompts/page-content.js +56 -0
  20. package/dist/commands/create/prompts/page-content.js.map +1 -0
  21. package/dist/commands/create/prompts/source-discovery.d.ts +3 -0
  22. package/dist/commands/create/prompts/source-discovery.d.ts.map +1 -0
  23. package/dist/commands/create/prompts/source-discovery.js +46 -0
  24. package/dist/commands/create/prompts/source-discovery.js.map +1 -0
  25. package/dist/commands/create/prompts/voice-generation.d.ts +3 -0
  26. package/dist/commands/create/prompts/voice-generation.d.ts.map +1 -0
  27. package/dist/commands/create/prompts/voice-generation.js +43 -0
  28. package/dist/commands/create/prompts/voice-generation.js.map +1 -0
  29. package/dist/commands/create/steps/assemble-config.d.ts +5 -0
  30. package/dist/commands/create/steps/assemble-config.d.ts.map +1 -0
  31. package/dist/commands/create/steps/assemble-config.js +82 -0
  32. package/dist/commands/create/steps/assemble-config.js.map +1 -0
  33. package/dist/commands/create/steps/discover-sources.d.ts +5 -0
  34. package/dist/commands/create/steps/discover-sources.d.ts.map +1 -0
  35. package/dist/commands/create/steps/discover-sources.js +53 -0
  36. package/dist/commands/create/steps/discover-sources.js.map +1 -0
  37. package/dist/commands/create/steps/fingerprint-style.d.ts +5 -0
  38. package/dist/commands/create/steps/fingerprint-style.d.ts.map +1 -0
  39. package/dist/commands/create/steps/fingerprint-style.js +46 -0
  40. package/dist/commands/create/steps/fingerprint-style.js.map +1 -0
  41. package/dist/commands/create/steps/generate-example.d.ts +4 -0
  42. package/dist/commands/create/steps/generate-example.d.ts.map +1 -0
  43. package/dist/commands/create/steps/generate-example.js +23 -0
  44. package/dist/commands/create/steps/generate-example.js.map +1 -0
  45. package/dist/commands/create/steps/generate-voice.d.ts +4 -0
  46. package/dist/commands/create/steps/generate-voice.d.ts.map +1 -0
  47. package/dist/commands/create/steps/generate-voice.js +32 -0
  48. package/dist/commands/create/steps/generate-voice.js.map +1 -0
  49. package/dist/commands/create/steps/parse-intent.d.ts +4 -0
  50. package/dist/commands/create/steps/parse-intent.d.ts.map +1 -0
  51. package/dist/commands/create/steps/parse-intent.js +16 -0
  52. package/dist/commands/create/steps/parse-intent.js.map +1 -0
  53. package/dist/commands/create/steps/resolve-connection.d.ts +5 -0
  54. package/dist/commands/create/steps/resolve-connection.d.ts.map +1 -0
  55. package/dist/commands/create/steps/resolve-connection.js +100 -0
  56. package/dist/commands/create/steps/resolve-connection.js.map +1 -0
  57. package/dist/commands/create/steps/resolve-schedule.d.ts +13 -0
  58. package/dist/commands/create/steps/resolve-schedule.d.ts.map +1 -0
  59. package/dist/commands/create/steps/resolve-schedule.js +70 -0
  60. package/dist/commands/create/steps/resolve-schedule.js.map +1 -0
  61. package/dist/commands/create/steps/setup-site.d.ts +5 -0
  62. package/dist/commands/create/steps/setup-site.d.ts.map +1 -0
  63. package/dist/commands/create/steps/setup-site.js +44 -0
  64. package/dist/commands/create/steps/setup-site.js.map +1 -0
  65. package/dist/commands/create/steps/validate-and-summary.d.ts +4 -0
  66. package/dist/commands/create/steps/validate-and-summary.d.ts.map +1 -0
  67. package/dist/commands/create/steps/validate-and-summary.js +60 -0
  68. package/dist/commands/create/steps/validate-and-summary.js.map +1 -0
  69. package/dist/commands/create/steps/validate-site-access.d.ts +4 -0
  70. package/dist/commands/create/steps/validate-site-access.d.ts.map +1 -0
  71. package/dist/commands/create/steps/validate-site-access.js +25 -0
  72. package/dist/commands/create/steps/validate-site-access.js.map +1 -0
  73. package/dist/commands/create/types.d.ts +72 -0
  74. package/dist/commands/create/types.d.ts.map +1 -0
  75. package/dist/commands/create/types.js +2 -0
  76. package/dist/commands/create/types.js.map +1 -0
  77. package/dist/commands/fingerprint.d.ts +4 -0
  78. package/dist/commands/fingerprint.d.ts.map +1 -0
  79. package/dist/commands/fingerprint.js +31 -0
  80. package/dist/commands/fingerprint.js.map +1 -0
  81. package/dist/commands/init.d.ts +2 -0
  82. package/dist/commands/init.d.ts.map +1 -0
  83. package/dist/commands/init.js +117 -0
  84. package/dist/commands/init.js.map +1 -0
  85. package/dist/commands/list.d.ts +2 -0
  86. package/dist/commands/list.d.ts.map +1 -0
  87. package/dist/commands/list.js +36 -0
  88. package/dist/commands/list.js.map +1 -0
  89. package/dist/commands/logs.d.ts +6 -0
  90. package/dist/commands/logs.d.ts.map +1 -0
  91. package/dist/commands/logs.js +49 -0
  92. package/dist/commands/logs.js.map +1 -0
  93. package/dist/commands/patterns.d.ts +6 -0
  94. package/dist/commands/patterns.d.ts.map +1 -0
  95. package/dist/commands/patterns.js +51 -0
  96. package/dist/commands/patterns.js.map +1 -0
  97. package/dist/commands/run.d.ts +6 -0
  98. package/dist/commands/run.d.ts.map +1 -0
  99. package/dist/commands/run.js +139 -0
  100. package/dist/commands/run.js.map +1 -0
  101. package/dist/commands/status.d.ts +2 -0
  102. package/dist/commands/status.d.ts.map +1 -0
  103. package/dist/commands/status.js +66 -0
  104. package/dist/commands/status.js.map +1 -0
  105. package/dist/commands/validate.d.ts +2 -0
  106. package/dist/commands/validate.d.ts.map +1 -0
  107. package/dist/commands/validate.js +83 -0
  108. package/dist/commands/validate.js.map +1 -0
  109. package/dist/index.d.ts +3 -0
  110. package/dist/index.d.ts.map +1 -0
  111. package/dist/index.js +76 -0
  112. package/dist/index.js.map +1 -0
  113. package/package.json +38 -0
@@ -0,0 +1,117 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { getChannelsDir } from "@ghostwriter/core";
4
+ import chalk from "chalk";
5
+ const TEMPLATE_CONFIG = `id: {CHANNEL_ID}
6
+ name: "{CHANNEL_NAME}"
7
+ contentType: article
8
+
9
+ topic:
10
+ domain: your-domain
11
+ focus: "What this channel covers"
12
+ keywords:
13
+ - keyword1
14
+ - keyword2
15
+ constraints: "Any constraints on content"
16
+
17
+ dataSources:
18
+ - type: rss
19
+ url: https://example.com/feed.xml
20
+ maxItems: 10
21
+
22
+ voice:
23
+ name: "Your Writer Name"
24
+ persona: "Brief description of the writer's personality and style"
25
+ age: 35
26
+ backstory: "How they got into this topic"
27
+ opinions:
28
+ - "A strong opinion they hold"
29
+ verbalTics:
30
+ - "A catchphrase or verbal habit"
31
+ exampleContent:
32
+ - ./examples/sample-1.md
33
+ vocabulary:
34
+ preferred:
35
+ - word1
36
+ - word2
37
+ forbidden:
38
+ - "it's important to note"
39
+ - "delve"
40
+ tone: conversational
41
+
42
+ publishTargets:
43
+ - platform: wordpress
44
+ id: wordpress-com
45
+ - platform: twitter
46
+ format: thread
47
+ maxTweets: 10
48
+
49
+ schedule:
50
+ cron: "0 10 * * MON"
51
+ timezone: America/New_York
52
+ enabled: true
53
+
54
+ qualityGate:
55
+ minScores:
56
+ structure: 7
57
+ readability: 7
58
+ voiceMatch: 7
59
+ factualAccuracy: 7
60
+ sourceCoverage: 7
61
+ hookStrength: 7
62
+ engagementPotential: 7
63
+ naturalness: 7
64
+ perplexityVariance: 7
65
+ maxRevisions: 3
66
+
67
+ targetWordCount: 1500
68
+ batchApi: false
69
+ `;
70
+ const SAMPLE_CONTENT = `# Sample Article Title
71
+
72
+ Here's an example of the writing style you want this channel to replicate.
73
+
74
+ Write 3-5 sample articles that demonstrate your preferred voice, tone, and structure. The style fingerprinting system will analyze these to extract quantitative features like:
75
+
76
+ - Sentence length distribution
77
+ - Paragraph variation
78
+ - Vocabulary richness
79
+ - Use of questions, contractions, first/second person
80
+ - Opening and closing styles
81
+
82
+ The more representative your examples, the better the generated content will match your desired style.
83
+
84
+ ## Tips for Good Examples
85
+
86
+ Mix up your examples. Include some shorter pieces and some longer ones. Show the range of your style — serious and light, data-heavy and opinion-heavy.
87
+
88
+ Don't overthink it. Just write naturally in the voice you want the AI to replicate.
89
+ `;
90
+ export async function initCommand(channelName) {
91
+ const channelId = channelName
92
+ .toLowerCase()
93
+ .replace(/[^a-z0-9-]/g, "-")
94
+ .replace(/-+/g, "-");
95
+ const channelsDir = getChannelsDir();
96
+ const channelDir = join(channelsDir, channelId);
97
+ const examplesDir = join(channelDir, "examples");
98
+ console.log(chalk.blue(`\nInitializing channel: ${channelId}\n`));
99
+ // Create directories
100
+ await mkdir(examplesDir, { recursive: true });
101
+ // Write config template
102
+ const config = TEMPLATE_CONFIG.replace(/{CHANNEL_ID}/g, channelId).replace(/{CHANNEL_NAME}/g, channelName);
103
+ await writeFile(join(channelDir, "config.yml"), config, "utf-8");
104
+ // Write example content
105
+ await writeFile(join(examplesDir, "sample-1.md"), SAMPLE_CONTENT, "utf-8");
106
+ console.log(chalk.green(" Created:"));
107
+ console.log(` ${join(channelDir, "config.yml")}`);
108
+ console.log(` ${join(examplesDir, "sample-1.md")}`);
109
+ console.log();
110
+ console.log(chalk.yellow(" Next steps:"));
111
+ console.log(` 1. Edit ${join(channelDir, "config.yml")} with your settings`);
112
+ console.log(` 2. Add example content to ${examplesDir}/`);
113
+ console.log(` 3. Run: ghostwriter validate ${channelId}`);
114
+ console.log(` 4. Run: ghostwriter run ${channelId} --dry-run`);
115
+ console.log();
116
+ }
117
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEvB,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;CAmBtB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,WAAmB;IACnD,MAAM,SAAS,GAAG,WAAW;SAC1B,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAEjD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2BAA2B,SAAS,IAAI,CAAC,CAAC,CAAC;IAElE,qBAAqB;IACrB,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE9C,wBAAwB;IACxB,MAAM,MAAM,GAAG,eAAe,CAAC,OAAO,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC,OAAO,CACxE,iBAAiB,EACjB,WAAW,CACZ,CAAC;IACF,MAAM,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAEjE,wBAAwB;IACxB,MAAM,SAAS,CACb,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,EAChC,cAAc,EACd,OAAO,CACR,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,qBAAqB,CAAC,CAAC;IAChF,OAAO,CAAC,GAAG,CACT,iCAAiC,WAAW,GAAG,CAChD,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,oCAAoC,SAAS,EAAE,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,+BAA+B,SAAS,YAAY,CAAC,CAAC;IAClE,OAAO,CAAC,GAAG,EAAE,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function listCommand(): Promise<void>;
2
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAIA,wBAAsB,WAAW,kBAsChC"}
@@ -0,0 +1,36 @@
1
+ import { listChannels, loadChannelConfig } from "@ghostwriter/core";
2
+ import chalk from "chalk";
3
+ import Table from "cli-table3";
4
+ export async function listCommand() {
5
+ const channelIds = await listChannels();
6
+ if (channelIds.length === 0) {
7
+ console.log(chalk.yellow("\nNo channels found. Run: ghostwriter init <channel-name>\n"));
8
+ return;
9
+ }
10
+ const table = new Table({
11
+ head: [
12
+ chalk.blue("Channel ID"),
13
+ chalk.blue("Name"),
14
+ chalk.blue("Content Type"),
15
+ chalk.blue("Voice"),
16
+ chalk.blue("Schedule"),
17
+ ],
18
+ });
19
+ for (const id of channelIds) {
20
+ try {
21
+ const config = await loadChannelConfig(id);
22
+ table.push([
23
+ config.id,
24
+ config.name,
25
+ config.contentType,
26
+ config.voice.name,
27
+ config.schedule.cron,
28
+ ]);
29
+ }
30
+ catch {
31
+ table.push([id, chalk.red("(error)"), "", "", ""]);
32
+ }
33
+ }
34
+ console.log(`\n${table.toString()}\n`);
35
+ }
36
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,UAAU,GAAG,MAAM,YAAY,EAAE,CAAC;IAExC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,6DAA6D,CAC9D,CACF,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,IAAI,EAAE;YACJ,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;SACvB;KACF,CAAC,CAAC;IAEH,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,CAAC,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC;gBACT,MAAM,CAAC,EAAE;gBACT,MAAM,CAAC,IAAI;gBACX,MAAM,CAAC,WAAW;gBAClB,MAAM,CAAC,KAAK,CAAC,IAAI;gBACjB,MAAM,CAAC,QAAQ,CAAC,IAAI;aACrB,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,6 @@
1
+ interface LogsOptions {
2
+ lines: string;
3
+ }
4
+ export declare function logsCommand(channelName: string, options: LogsOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=logs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"AAGA,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,iBA2E1E"}
@@ -0,0 +1,49 @@
1
+ import chalk from "chalk";
2
+ import { getRecentRuns } from "@ghostwriter/monitoring";
3
+ export async function logsCommand(channelName, options) {
4
+ const limit = parseInt(options.lines, 10) || 20;
5
+ console.log(chalk.blue(`\nRecent pipeline runs for channel: ${channelName} (last ${limit})\n`));
6
+ try {
7
+ const runs = await getRecentRuns(channelName, limit);
8
+ if (runs.length === 0) {
9
+ console.log(chalk.gray(" No pipeline runs found for this channel."));
10
+ console.log(chalk.gray(" Run your first pipeline with: ghostwriter run " + channelName));
11
+ console.log();
12
+ return;
13
+ }
14
+ for (const run of runs) {
15
+ const statusColor = run.status === "completed"
16
+ ? chalk.green
17
+ : run.status === "failed" || run.status === "dead_letter"
18
+ ? chalk.red
19
+ : chalk.yellow;
20
+ const startedAt = run.startedAt
21
+ ? new Date(run.startedAt).toLocaleString()
22
+ : "unknown";
23
+ const duration = run.completedAt && run.startedAt
24
+ ? `${Math.round((new Date(run.completedAt).getTime() - new Date(run.startedAt).getTime()) / 1000)}s`
25
+ : "—";
26
+ const cost = run.totalCost != null ? `$${run.totalCost.toFixed(4)}` : "—";
27
+ console.log(` ${statusColor("●")} ${chalk.bold(run.id.slice(0, 8))} ${statusColor(run.status.padEnd(12))} ${startedAt} ${chalk.gray(`duration: ${duration}`)} ${chalk.gray(`cost: ${cost}`)}`);
28
+ if (run.error) {
29
+ console.log(chalk.red(` Error: ${run.error}`));
30
+ }
31
+ if (run.currentStage) {
32
+ console.log(chalk.gray(` Stage: ${run.currentStage}`));
33
+ }
34
+ }
35
+ console.log();
36
+ }
37
+ catch (err) {
38
+ const message = err instanceof Error ? err.message : String(err);
39
+ if (message.includes("DATABASE_URL")) {
40
+ console.log(chalk.gray(" Database not configured. Set DATABASE_URL to enable run history."));
41
+ console.log(chalk.gray(" Alternatively, use the Temporal UI at http://localhost:8233 for workflow logs."));
42
+ }
43
+ else {
44
+ console.log(chalk.red(` Failed to fetch logs: ${message}`));
45
+ }
46
+ console.log();
47
+ }
48
+ }
49
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/commands/logs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ interface PatternsOptions {
2
+ days?: string;
3
+ }
4
+ export declare function patternsCommand(subcommand: string, channelId: string, options: PatternsOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/commands/patterns.ts"],"names":[],"mappings":"AAMA,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,eAAe,iBAgBzB"}
@@ -0,0 +1,51 @@
1
+ import chalk from "chalk";
2
+ import { loadLearnedPatterns, saveLearnedPatterns, } from "@ghostwriter/core";
3
+ export async function patternsCommand(subcommand, channelId, options) {
4
+ if (!subcommand || !channelId) {
5
+ console.log(chalk.red("Usage: ghostwriter patterns <list|prune> <channel-id>"));
6
+ return;
7
+ }
8
+ switch (subcommand) {
9
+ case "list":
10
+ return listPatterns(channelId);
11
+ case "prune":
12
+ return prunePatterns(channelId, options);
13
+ default:
14
+ console.log(chalk.red(`Unknown subcommand: ${subcommand}`));
15
+ console.log(chalk.gray("Available: list, prune"));
16
+ }
17
+ }
18
+ async function listPatterns(channelId) {
19
+ const patterns = await loadLearnedPatterns(channelId);
20
+ if (patterns.length === 0) {
21
+ console.log(chalk.gray(`\n No learned patterns for channel: ${channelId}`));
22
+ console.log(chalk.gray(" Patterns are discovered automatically during review.\n"));
23
+ return;
24
+ }
25
+ console.log(chalk.blue(`\nLearned patterns for ${channelId} (${patterns.length}):\n`));
26
+ const sorted = [...patterns].sort((a, b) => b.confidence - a.confidence);
27
+ for (const p of sorted) {
28
+ const age = Math.round((Date.now() - new Date(p.lastSeenAt).getTime()) / (1000 * 60 * 60 * 24));
29
+ const confidenceColor = p.confidence >= 0.8
30
+ ? chalk.green
31
+ : p.confidence >= 0.6
32
+ ? chalk.yellow
33
+ : chalk.gray;
34
+ console.log(` ${confidenceColor("●")} ${chalk.bold(p.phrase)} ${chalk.gray(`[${p.category}]`)} conf: ${confidenceColor(p.confidence.toFixed(2))} seen: ${p.occurrences}x last: ${age}d ago`);
35
+ }
36
+ console.log();
37
+ }
38
+ async function prunePatterns(channelId, options) {
39
+ const days = parseInt(options.days ?? "90", 10);
40
+ const patterns = await loadLearnedPatterns(channelId);
41
+ if (patterns.length === 0) {
42
+ console.log(chalk.gray(`\n No patterns to prune for channel: ${channelId}\n`));
43
+ return;
44
+ }
45
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
46
+ const kept = patterns.filter((p) => new Date(p.lastSeenAt).getTime() >= cutoff);
47
+ const pruned = patterns.length - kept.length;
48
+ await saveLearnedPatterns(channelId, kept);
49
+ console.log(chalk.blue(`\n Pruned ${pruned} pattern(s) not seen in ${days} days. ${kept.length} remaining.\n`));
50
+ }
51
+ //# sourceMappingURL=patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/commands/patterns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ interface RunOptions {
2
+ dryRun: boolean;
3
+ }
4
+ export declare function runCommand(channelName: string, options: RunOptions): Promise<void>;
5
+ export {};
6
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAeA,UAAU,UAAU;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,iBAoKxE"}
@@ -0,0 +1,139 @@
1
+ import { loadChannelConfig, getChannelsDir, loadHistory, appendHistory, formatHistoryForPrompt, } from "@ghostwriter/core";
2
+ import { ingestData } from "@ghostwriter/data-ingestion";
3
+ import { runPipeline, analyzeStyleFingerprint } from "@ghostwriter/content-pipeline";
4
+ import { publishAll } from "@ghostwriter/publishing";
5
+ import { readFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+ import chalk from "chalk";
8
+ import ora from "ora";
9
+ export async function runCommand(channelName, options) {
10
+ console.log(chalk.blue(`\n${options.dryRun ? "[DRY RUN] " : ""}Running pipeline for: ${channelName}\n`));
11
+ const spinner = ora();
12
+ try {
13
+ // Load config
14
+ spinner.start("Loading channel config...");
15
+ const config = await loadChannelConfig(channelName);
16
+ spinner.succeed(`Loaded config: ${config.name}`);
17
+ // Ingest data
18
+ spinner.start("Ingesting data from sources...");
19
+ const sources = await ingestData(config.id, config.dataSources);
20
+ spinner.succeed(`Ingested ${sources.length} source materials`);
21
+ // Load style fingerprint
22
+ let fingerprint;
23
+ if (config.voice.exampleContent.length > 0) {
24
+ spinner.start("Analyzing style fingerprint...");
25
+ const channelsDir = getChannelsDir();
26
+ const exampleTexts = [];
27
+ for (const examplePath of config.voice.exampleContent) {
28
+ try {
29
+ const text = await readFile(join(channelsDir, config.id, examplePath), "utf-8");
30
+ exampleTexts.push(text);
31
+ }
32
+ catch {
33
+ // Skip missing
34
+ }
35
+ }
36
+ if (exampleTexts.length > 0) {
37
+ fingerprint = analyzeStyleFingerprint(config.id, exampleTexts);
38
+ spinner.succeed(`Style fingerprint computed from ${exampleTexts.length} example(s)`);
39
+ }
40
+ else {
41
+ spinner.warn("No example content found, skipping fingerprint");
42
+ }
43
+ }
44
+ // Load article history for deduplication
45
+ spinner.start("Loading article history...");
46
+ const history = await loadHistory(config.id);
47
+ const historyPrompt = formatHistoryForPrompt(history);
48
+ if (history.length > 0) {
49
+ spinner.succeed(`Loaded ${history.length} past article(s) for deduplication`);
50
+ }
51
+ else {
52
+ spinner.info("No article history yet — first run for this channel");
53
+ }
54
+ // Run pipeline with progress callbacks
55
+ const result = await runPipeline(config, sources, {
56
+ fingerprint,
57
+ skipAdapt: false,
58
+ performanceContext: historyPrompt || undefined,
59
+ callbacks: {
60
+ onStageStart: (stage) => {
61
+ spinner.start(`Running ${stage} stage...`);
62
+ },
63
+ onStageComplete: (stage, cost) => {
64
+ spinner.succeed(`${stage} complete ${chalk.gray(`($${cost.toFixed(3)})`)}`);
65
+ },
66
+ onRevision: (revision, feedback) => {
67
+ console.log(chalk.yellow(`\n Revision ${revision} — Feedback:`));
68
+ for (const f of feedback.slice(0, 5)) {
69
+ console.log(chalk.yellow(` - ${f}`));
70
+ }
71
+ console.log();
72
+ },
73
+ },
74
+ });
75
+ // Display results
76
+ console.log(chalk.blue("\n─── Results ───\n"));
77
+ console.log(` Status: ${result.passed ? chalk.green("PASSED") : chalk.red("FAILED")}`);
78
+ console.log(` Revisions: ${result.revisions}`);
79
+ console.log(` Total Cost: $${result.totalCost.toFixed(3)}`);
80
+ console.log(` Word Count: ${result.draft.wordCount}`);
81
+ console.log(chalk.blue("\n─── Quality Scores ───\n"));
82
+ const scores = result.review.aggregateScores;
83
+ for (const [key, value] of Object.entries(scores)) {
84
+ const color = value >= 8 ? chalk.green : value >= 7 ? chalk.yellow : chalk.red;
85
+ const bar = "█".repeat(value) + "░".repeat(10 - value);
86
+ console.log(` ${key.padEnd(22)} ${color(bar)} ${color(value.toString())}/10`);
87
+ }
88
+ // Show agent feedback
89
+ console.log(chalk.blue("\n─── Agent Feedback ───\n"));
90
+ for (const agent of result.review.agentResults) {
91
+ const icon = agent.passed ? chalk.green("✓") : chalk.red("✗");
92
+ console.log(` ${icon} ${agent.agent}`);
93
+ for (const f of agent.feedback.slice(0, 3)) {
94
+ console.log(chalk.gray(` ${f}`));
95
+ }
96
+ }
97
+ // Preview
98
+ console.log(chalk.blue("\n─── Content Preview ───\n"));
99
+ const preview = result.draft.content.slice(0, 500);
100
+ console.log(chalk.gray(preview));
101
+ if (result.draft.content.length > 500) {
102
+ console.log(chalk.gray(" ..."));
103
+ }
104
+ // Publish
105
+ if (!options.dryRun && result.passed) {
106
+ console.log(chalk.blue("\n─── Publishing ───\n"));
107
+ spinner.start("Publishing to platforms...");
108
+ const publishResults = await publishAll(config, result.adaptations);
109
+ for (const pub of publishResults) {
110
+ if (pub.success) {
111
+ spinner.succeed(`Published to ${pub.platform}${pub.url ? `: ${pub.url}` : ""}`);
112
+ }
113
+ else {
114
+ spinner.fail(`Failed to publish to ${pub.platform}: ${pub.error}`);
115
+ }
116
+ }
117
+ }
118
+ else if (!options.dryRun && !result.passed) {
119
+ console.log(chalk.red("\n Content did not pass quality gate. Not publishing."));
120
+ }
121
+ // Append to article history so future runs avoid repeating this topic
122
+ if (result.passed) {
123
+ const summary = result.draft.content.slice(0, 200).replace(/\n/g, " ").trim() + "...";
124
+ await appendHistory(config.id, {
125
+ headline: result.draft.headline,
126
+ summary,
127
+ topics: [config.topic.focus],
128
+ publishedAt: new Date().toISOString(),
129
+ });
130
+ spinner.succeed("Article added to history");
131
+ }
132
+ console.log();
133
+ }
134
+ catch (err) {
135
+ spinner.fail(`Pipeline failed: ${err instanceof Error ? err.message : err}`);
136
+ process.exitCode = 1;
137
+ }
138
+ }
139
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function statusCommand(channelName?: string): Promise<void>;
2
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAIA,wBAAsB,aAAa,CAAC,WAAW,CAAC,EAAE,MAAM,iBAMvD"}
@@ -0,0 +1,66 @@
1
+ import { loadChannelConfig, listChannels } from "@ghostwriter/core";
2
+ import chalk from "chalk";
3
+ import Table from "cli-table3";
4
+ export async function statusCommand(channelName) {
5
+ if (channelName) {
6
+ await showChannelStatus(channelName);
7
+ }
8
+ else {
9
+ await showAllStatus();
10
+ }
11
+ }
12
+ async function showChannelStatus(channelName) {
13
+ try {
14
+ const config = await loadChannelConfig(channelName);
15
+ console.log(chalk.blue(`\nChannel: ${config.name}\n`));
16
+ console.log(` ID: ${config.id}`);
17
+ console.log(` Content Type: ${config.contentType}`);
18
+ console.log(` Topic: ${config.topic.focus}`);
19
+ console.log(` Voice: ${config.voice.name} (${config.voice.tone})`);
20
+ console.log(` Schedule: ${config.schedule.cron}`);
21
+ console.log(` Enabled: ${config.schedule.enabled}`);
22
+ console.log(` Platforms: ${config.publishTargets.map((t) => t.platform).join(", ")}`);
23
+ console.log(` Data Sources: ${config.dataSources.map((d) => d.type).join(", ")}`);
24
+ console.log(` Word Target: ${config.targetWordCount}`);
25
+ console.log();
26
+ }
27
+ catch (err) {
28
+ console.log(chalk.red(`Error: ${err instanceof Error ? err.message : err}`));
29
+ process.exitCode = 1;
30
+ }
31
+ }
32
+ async function showAllStatus() {
33
+ const channelIds = await listChannels();
34
+ if (channelIds.length === 0) {
35
+ console.log(chalk.yellow("\nNo channels found. Run: ghostwriter init <channel-name>\n"));
36
+ return;
37
+ }
38
+ const table = new Table({
39
+ head: [
40
+ chalk.blue("ID"),
41
+ chalk.blue("Name"),
42
+ chalk.blue("Type"),
43
+ chalk.blue("Schedule"),
44
+ chalk.blue("Platforms"),
45
+ chalk.blue("Enabled"),
46
+ ],
47
+ });
48
+ for (const id of channelIds) {
49
+ try {
50
+ const config = await loadChannelConfig(id);
51
+ table.push([
52
+ config.id,
53
+ config.name,
54
+ config.contentType,
55
+ config.schedule.cron,
56
+ config.publishTargets.map((t) => t.platform).join(", "),
57
+ config.schedule.enabled ? chalk.green("yes") : chalk.red("no"),
58
+ ]);
59
+ }
60
+ catch {
61
+ table.push([id, chalk.red("(invalid config)"), "", "", "", ""]);
62
+ }
63
+ }
64
+ console.log(`\n${table.toString()}\n`);
65
+ }
66
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function validateCommand(channelName: string): Promise<void>;
2
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAGA,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,iBAoHxD"}
@@ -0,0 +1,83 @@
1
+ import { loadChannelConfig, env } from "@ghostwriter/core";
2
+ import chalk from "chalk";
3
+ export async function validateCommand(channelName) {
4
+ console.log(chalk.blue(`\nValidating channel: ${channelName}\n`));
5
+ let hasErrors = false;
6
+ // 1. Validate config schema
7
+ try {
8
+ const config = await loadChannelConfig(channelName);
9
+ console.log(chalk.green(" [PASS] Config schema is valid"));
10
+ // 2. Check data sources
11
+ for (const source of config.dataSources) {
12
+ console.log(chalk.green(` [PASS] Data source configured: ${source.type}${source.type === "api" ? ` (${source.provider})` : ""}`));
13
+ }
14
+ // 3. Check publish targets have required env vars
15
+ for (const target of config.publishTargets) {
16
+ switch (target.platform) {
17
+ case "twitter":
18
+ if (env.twitterApiKey &&
19
+ env.twitterApiSecret &&
20
+ env.twitterAccessToken &&
21
+ env.twitterAccessSecret) {
22
+ console.log(chalk.green(" [PASS] Twitter credentials found"));
23
+ }
24
+ else {
25
+ console.log(chalk.red(" [FAIL] Twitter credentials incomplete"));
26
+ hasErrors = true;
27
+ }
28
+ break;
29
+ case "podcast":
30
+ if (env.elevenLabsApiKey) {
31
+ console.log(chalk.green(" [PASS] ElevenLabs API key found"));
32
+ }
33
+ else {
34
+ console.log(chalk.red(" [FAIL] ELEVENLABS_API_KEY missing"));
35
+ hasErrors = true;
36
+ }
37
+ break;
38
+ case "wordpress":
39
+ if ((target.url || env.wordpressUrl) &&
40
+ (target.username || env.wordpressUsername) &&
41
+ (target.password || env.wordpressPassword)) {
42
+ console.log(chalk.green(" [PASS] WordPress credentials found"));
43
+ }
44
+ else {
45
+ console.log(chalk.red(" [FAIL] WordPress credentials missing — run: ghostwriter connect wordpress"));
46
+ hasErrors = true;
47
+ }
48
+ break;
49
+ }
50
+ }
51
+ // 4. Check Anthropic API key
52
+ try {
53
+ env.anthropicApiKey;
54
+ console.log(chalk.green(" [PASS] Anthropic API key found"));
55
+ }
56
+ catch {
57
+ console.log(chalk.red(" [FAIL] ANTHROPIC_API_KEY missing"));
58
+ hasErrors = true;
59
+ }
60
+ // 5. Check voice examples exist
61
+ if (config.voice.exampleContent.length > 0) {
62
+ console.log(chalk.green(` [PASS] ${config.voice.exampleContent.length} example content file(s) configured`));
63
+ }
64
+ else {
65
+ console.log(chalk.yellow(" [WARN] No example content — style fingerprinting will be skipped"));
66
+ }
67
+ console.log();
68
+ if (hasErrors) {
69
+ console.log(chalk.red(" Validation failed. Fix the errors above before running."));
70
+ process.exitCode = 1;
71
+ }
72
+ else {
73
+ console.log(chalk.green(" All checks passed!"));
74
+ console.log(`\n Run: ghostwriter run ${channelName} --dry-run`);
75
+ }
76
+ }
77
+ catch (err) {
78
+ console.log(chalk.red(` [FAIL] Config error: ${err instanceof Error ? err.message : err}`));
79
+ process.exitCode = 1;
80
+ }
81
+ console.log();
82
+ }
83
+ //# sourceMappingURL=validate.js.map