sverklo 0.28.2 → 0.29.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 (59) hide show
  1. package/README.md +6 -2
  2. package/dist/bin/sverklo.js +166 -4
  3. package/dist/bin/sverklo.js.map +1 -1
  4. package/dist/src/indexer/parser-tree-sitter.js +0 -1
  5. package/dist/src/indexer/parser-tree-sitter.js.map +1 -1
  6. package/dist/src/init-global.js +25 -24
  7. package/dist/src/init-global.js.map +1 -1
  8. package/dist/src/init.js +1 -0
  9. package/dist/src/init.js.map +1 -1
  10. package/dist/src/marketing/campaign-cycle.d.ts +13 -0
  11. package/dist/src/marketing/campaign-cycle.js +107 -0
  12. package/dist/src/marketing/campaign-cycle.js.map +1 -0
  13. package/dist/src/marketing/content-quality.d.ts +11 -0
  14. package/dist/src/marketing/content-quality.js +51 -0
  15. package/dist/src/marketing/content-quality.js.map +1 -0
  16. package/dist/src/marketing/content-seeding.d.ts +2 -0
  17. package/dist/src/marketing/content-seeding.js +105 -0
  18. package/dist/src/marketing/content-seeding.js.map +1 -0
  19. package/dist/src/marketing/decisions.d.ts +10 -0
  20. package/dist/src/marketing/decisions.js +142 -0
  21. package/dist/src/marketing/decisions.js.map +1 -0
  22. package/dist/src/marketing/index.d.ts +12 -0
  23. package/dist/src/marketing/index.js +13 -0
  24. package/dist/src/marketing/index.js.map +1 -0
  25. package/dist/src/marketing/models.d.ts +189 -0
  26. package/dist/src/marketing/models.js +5 -0
  27. package/dist/src/marketing/models.js.map +1 -0
  28. package/dist/src/marketing/opportunity-scout.d.ts +8 -0
  29. package/dist/src/marketing/opportunity-scout.js +73 -0
  30. package/dist/src/marketing/opportunity-scout.js.map +1 -0
  31. package/dist/src/marketing/profile-health.d.ts +2 -0
  32. package/dist/src/marketing/profile-health.js +96 -0
  33. package/dist/src/marketing/profile-health.js.map +1 -0
  34. package/dist/src/marketing/report.d.ts +4 -0
  35. package/dist/src/marketing/report.js +67 -0
  36. package/dist/src/marketing/report.js.map +1 -0
  37. package/dist/src/marketing/scoring.d.ts +5 -0
  38. package/dist/src/marketing/scoring.js +89 -0
  39. package/dist/src/marketing/scoring.js.map +1 -0
  40. package/dist/src/marketing/status.d.ts +3 -0
  41. package/dist/src/marketing/status.js +58 -0
  42. package/dist/src/marketing/status.js.map +1 -0
  43. package/dist/src/marketing/storage.d.ts +29 -0
  44. package/dist/src/marketing/storage.js +99 -0
  45. package/dist/src/marketing/storage.js.map +1 -0
  46. package/dist/src/marketing/test-fixtures.d.ts +7 -0
  47. package/dist/src/marketing/test-fixtures.js +31 -0
  48. package/dist/src/marketing/test-fixtures.js.map +1 -0
  49. package/dist/src/marketing/validation.d.ts +9 -0
  50. package/dist/src/marketing/validation.js +66 -0
  51. package/dist/src/marketing/validation.js.map +1 -0
  52. package/dist/src/prove.d.ts +6 -2
  53. package/dist/src/prove.js +119 -24
  54. package/dist/src/prove.js.map +1 -1
  55. package/dist/src/server/tools/review-diff.js +0 -1
  56. package/dist/src/server/tools/review-diff.js.map +1 -1
  57. package/dist/src/utils/logger.js +2 -0
  58. package/dist/src/utils/logger.js.map +1 -1
  59. package/package.json +3 -1
package/README.md CHANGED
@@ -22,6 +22,10 @@ sverklo prove
22
22
 
23
23
  `sverklo init` writes the MCP config for your agent, appends local instructions to `AGENTS.md` or `CLAUDE.md`, and runs `sverklo doctor` to verify the handshake. `sverklo prove` then shows central files, a real symbol with callers, and the exact prompt to paste into your agent. Your code stays on your machine.
24
24
 
25
+ Need something shareable? Run `sverklo prove --markdown` to print a GitHub/Discord-ready proof receipt from your repo, then [post it in the proof thread](https://github.com/sverklo/sverklo/discussions/79).
26
+
27
+ Need local launch planning for the Sverklo account? `sverklo marketing` runs a local-first Twitter/X agent-team workflow from operator-provided snapshots. It ranks opportunities, drafts seed-content queues, checks profile health, and records human decisions without posting, scraping, replying, liking, reposting, following, or changing the profile.
28
+
25
29
  > *"The map is not the territory."* — Alfred Korzybski
26
30
  >
27
31
  > Training data is the map. Your codebase is the territory. **Sverklo gives the agent the territory.**
@@ -78,7 +82,7 @@ cd your-project && sverklo init
78
82
  sverklo prove
79
83
  ```
80
84
 
81
- That's it. `sverklo init` auto-detects your installed AI coding agent (Claude Code, Cursor, Windsurf, Zed), writes the right MCP config, appends instructions to `AGENTS.md` if present (otherwise `CLAUDE.md`), and runs `sverklo doctor` to verify the setup. `sverklo prove` shows the first useful repo-memory proof from your own codebase. Works on macOS, Linux, and Windows. **No API keys. No cloud. Telemetry off by default.**
85
+ That's it. `sverklo init` auto-detects your installed AI coding agent (Claude Code, Cursor, Windsurf, Zed), writes the right MCP config, appends instructions to `AGENTS.md` if present (otherwise `CLAUDE.md`), and runs `sverklo doctor` to verify the setup. `sverklo prove` shows the first useful repo-memory proof from your own codebase; `sverklo prove --markdown` makes that proof shareable in [the public proof thread](https://github.com/sverklo/sverklo/discussions/79). Works on macOS, Linux, and Windows. **No API keys. No cloud. Telemetry off by default.**
82
86
 
83
87
  > The embedding model (`all-MiniLM-L6-v2` ONNX, ~86 MB) is downloaded from HuggingFace on first use into `~/.sverklo/models/` and cached forever — every subsequent run is fully offline.
84
88
 
@@ -452,7 +456,7 @@ cd your-project && sverklo init
452
456
  sverklo prove
453
457
  ```
454
458
 
455
- `sverklo init` auto-detects which AI coding agents you have (Claude Code, Cursor, Windsurf, Zed, Antigravity) and writes the right MCP config files. `sverklo prove` prints central files, a real caller graph, and a prompt to paste into your agent. Idempotent — safe to re-run. If sverklo doesn't appear in your agent after restart, run `sverklo doctor`.
459
+ `sverklo init` auto-detects which AI coding agents you have (Claude Code, Cursor, Windsurf, Zed, Antigravity) and writes the right MCP config files. `sverklo prove` prints central files, a real caller graph, and a prompt to paste into your agent. Add `--markdown` or `--receipt` for a shareable proof artifact, then post it in [the proof thread](https://github.com/sverklo/sverklo/discussions/79). Idempotent — safe to re-run. If sverklo doesn't appear in your agent after restart, run `sverklo doctor`.
456
460
 
457
461
  **Per-agent config locations** (`sverklo init` writes these for you):
458
462
  - Claude Code: `.mcp.json` at project root + appends to `CLAUDE.md` (or `AGENTS.md` if present)
@@ -37,6 +37,38 @@ async function resolveProjectPath(flags) {
37
37
  }
38
38
  return target;
39
39
  }
40
+ function parseProveArgs(rawArgs) {
41
+ let format = "text";
42
+ const pathArgs = [];
43
+ for (let i = 0; i < rawArgs.length; i++) {
44
+ const arg = rawArgs[i];
45
+ if (arg === "--markdown" || arg === "--receipt") {
46
+ format = "markdown";
47
+ continue;
48
+ }
49
+ if (arg === "--format") {
50
+ const value = rawArgs[i + 1];
51
+ if (value !== "text" && value !== "plain" && value !== "markdown") {
52
+ console.error("✗ --format expects text, plain, or markdown");
53
+ process.exit(2);
54
+ }
55
+ format = value === "markdown" ? "markdown" : "text";
56
+ i++;
57
+ continue;
58
+ }
59
+ if (arg.startsWith("--format=")) {
60
+ const value = arg.slice("--format=".length);
61
+ if (value !== "text" && value !== "plain" && value !== "markdown") {
62
+ console.error("✗ --format expects text, plain, or markdown");
63
+ process.exit(2);
64
+ }
65
+ format = value === "markdown" ? "markdown" : "text";
66
+ continue;
67
+ }
68
+ pathArgs.push(arg);
69
+ }
70
+ return { format, pathArgs };
71
+ }
40
72
  // Global --help / -h interceptor.
41
73
  //
42
74
  // Without this, `--help` falls through to whatever subcommand the user
@@ -52,7 +84,7 @@ if (command && command !== "--help" && command !== "-h") {
52
84
  const HELP_BLURBS = {
53
85
  init: "Set up sverklo in your project (.mcp.json + CLAUDE.md, auto-detects Claude Code/Cursor/Windsurf/Antigravity). With --global: one-time-per-machine setup — write SVERKLO_SNIPPET to ~/.claude/CLAUDE.md and ~/.codex/AGENTS.md, register the project, gitignore .sverklo/, import memories. Skips per-project boilerplate.",
54
86
  doctor: "Diagnose MCP setup issues. Run after `init` to verify the agent can reach sverklo.",
55
- prove: "Show a first-run repo-memory proof: central files, a real symbol with callers, and a paste-ready agent prompt.",
87
+ prove: "Show a first-run repo-memory proof: central files, a real symbol with callers, and a paste-ready agent prompt. Flags: --markdown, --receipt, --format text|markdown.",
56
88
  audit: "Run codebase audit and emit a graded report. Flags: --format markdown|html|json|graph|arch|obsidian, --output PATH, --open, --badge, --publish.",
57
89
  "audit-diff": "Incremental architectural quality gate. Audits `git diff` for new cycles + fan-in spikes. Flags: --against REF, --fan-in-threshold N, --format human|json, --show-existing, --verbose. Exits 1 on regression.",
58
90
  review: "Risk-scored diff review (CI-friendly). Flags: --ref REF, --ci, --format markdown|json, --max-files N, --fail-on low|medium|high.",
@@ -70,6 +102,7 @@ if (command && command !== "--help" && command !== "-h") {
70
102
  digest: "5-line summary of what changed in this project. Flags: --since 7d, --format markdown|plain.",
71
103
  receipt: "Token-spend receipt for your recent Claude Code sessions. Shows where tokens went and projected yearly cost. Flags: --since 7d, --format plain|json.",
72
104
  memory: "Manage the memory store. Subcommands: show, edit, import, export.",
105
+ marketing: "Run the local-first Sverklo Twitter agent team workflow. Subcommands: init, run-cycle, decide, status.",
73
106
  grammars: "Manage tree-sitter grammars for the SVERKLO_PARSER=tree-sitter opt-in path. Subcommands: install.",
74
107
  weights: "Inspect .sverklo.yaml weight rules. Subcommands: explain <path> — show which glob matched and the effective weight.",
75
108
  "audit-prompt": "Print a ready-to-paste codebase-audit prompt (hybrid agent workflow).",
@@ -117,6 +150,132 @@ if (command === "--version" || command === "-v" || command === "-V") {
117
150
  console.log("sverklo (version unknown)");
118
151
  process.exit(0);
119
152
  }
153
+ if (command === "marketing") {
154
+ const subcommand = args[1];
155
+ const flags = args.slice(2);
156
+ const flagVal = (name) => {
157
+ const idx = flags.indexOf(name);
158
+ if (idx !== -1 && flags[idx + 1])
159
+ return flags[idx + 1];
160
+ const prefixed = flags.find((f) => f.startsWith(`${name}=`));
161
+ return prefixed ? prefixed.slice(name.length + 1) : undefined;
162
+ };
163
+ const requireFlag = (name) => {
164
+ const value = flagVal(name);
165
+ if (!value) {
166
+ console.error(`✗ ${name} is required`);
167
+ process.exit(2);
168
+ }
169
+ return value;
170
+ };
171
+ const format = flagVal("--format") ?? "text";
172
+ if (format !== "text" && format !== "json") {
173
+ console.error(`✗ --format must be text or json, got "${format}"`);
174
+ process.exit(2);
175
+ }
176
+ const { appendOperatorDecision, applyOperatorDecision, assertEvidenceCatalog, assertProfileSnapshot, assertRecentPostsSnapshot, assertTrendSnapshot, buildStatusSummary, createOperatorDecision, initMarketingWorkspace, jsonFile, loadActiveCampaignCycle, loadCampaignCycle, loadMarketingWorkspace, marketingPaths, normalizeAccountHandle, recomputeCampaignReadiness, renderContentReport, renderOpportunityReport, renderProfileHealthReport, renderStatusText, resolveMarketingWorkspace, runCampaignCycle, saveCampaignCycle, saveMarketingWorkspace, writeReport, } = await import("../src/marketing/index.js");
177
+ const { existsSync } = await import("node:fs");
178
+ const { join } = await import("node:path");
179
+ const workspacePath = flagVal("--workspace");
180
+ const readOptional = (path) => {
181
+ return path && existsSync(path) ? jsonFile(path) : undefined;
182
+ };
183
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
184
+ console.log(`\nsverklo marketing — local-first Twitter agent team workflow\n\n` +
185
+ `Usage:\n` +
186
+ ` sverklo marketing init --account @sverklo [--workspace PATH]\n` +
187
+ ` sverklo marketing run-cycle [--workspace PATH] [--trends PATH] [--profile PATH] [--recent-posts PATH] [--evidence PATH] [--format text|json]\n` +
188
+ ` sverklo marketing decide --target-type TYPE --target-id ID --decision VALUE [--reason TEXT] [--future]\n` +
189
+ ` sverklo marketing status [--workspace PATH] [--format text|json]\n\n` +
190
+ `No command posts to X/Twitter, scrapes X, sends DMs, likes, reposts, follows, unfollows, or mutates the profile.\n`);
191
+ process.exit(0);
192
+ }
193
+ try {
194
+ if (subcommand === "init") {
195
+ const account = normalizeAccountHandle(requireFlag("--account"));
196
+ const workspace = initMarketingWorkspace({ workspacePath, accountHandle: account });
197
+ console.log(`Initialized marketing workspace for ${workspace.account_handle} at ${resolveMarketingWorkspace(workspacePath)}`);
198
+ process.exit(0);
199
+ }
200
+ if (subcommand === "run-cycle") {
201
+ const workspace = loadMarketingWorkspace(workspacePath);
202
+ const root = resolveMarketingWorkspace(workspacePath);
203
+ const inputDir = marketingPaths(root).inputs;
204
+ const existing = loadActiveCampaignCycle(workspacePath, workspace);
205
+ const trends = readOptional(flagVal("--trends") ?? join(inputDir, "trend-snapshot.json"));
206
+ const profile = readOptional(flagVal("--profile") ?? join(inputDir, "profile-snapshot.json"));
207
+ const recentPosts = readOptional(flagVal("--recent-posts") ?? join(inputDir, "recent-posts.json"));
208
+ const evidence = readOptional(flagVal("--evidence") ?? join(inputDir, "evidence.json"));
209
+ if (trends)
210
+ assertTrendSnapshot(trends);
211
+ if (profile)
212
+ assertProfileSnapshot(profile);
213
+ if (recentPosts)
214
+ assertRecentPostsSnapshot(recentPosts);
215
+ if (evidence)
216
+ assertEvidenceCatalog(evidence);
217
+ if (!trends && !profile && !recentPosts && !evidence && !existing) {
218
+ throw new Error("no local marketing inputs found; provide --trends, --profile, --recent-posts, or --evidence");
219
+ }
220
+ const cycle = runCampaignCycle({
221
+ workspace,
222
+ existingCycle: existing,
223
+ trendSnapshot: trends,
224
+ profileSnapshot: profile,
225
+ recentPosts,
226
+ evidence,
227
+ cycleId: flagVal("--cycle"),
228
+ });
229
+ saveMarketingWorkspace(workspacePath, workspace);
230
+ saveCampaignCycle(workspacePath, cycle);
231
+ writeReport(workspacePath, cycle.cycle_id, "opportunities", renderOpportunityReport(cycle));
232
+ writeReport(workspacePath, cycle.cycle_id, "content", renderContentReport(cycle));
233
+ writeReport(workspacePath, cycle.cycle_id, "profile-health", renderProfileHealthReport(cycle));
234
+ const summary = buildStatusSummary(cycle);
235
+ if (format === "json")
236
+ console.log(JSON.stringify(summary, null, 2));
237
+ else
238
+ process.stdout.write(renderStatusText(summary));
239
+ process.exit(0);
240
+ }
241
+ if (subcommand === "decide") {
242
+ const workspace = loadMarketingWorkspace(workspacePath);
243
+ if (!workspace.active_cycle_id)
244
+ throw new Error("no active marketing cycle");
245
+ const cycle = loadCampaignCycle(workspacePath, workspace.active_cycle_id);
246
+ const decision = createOperatorDecision({
247
+ targetType: requireFlag("--target-type"),
248
+ targetId: requireFlag("--target-id"),
249
+ decision: requireFlag("--decision"),
250
+ reason: flagVal("--reason"),
251
+ future: flags.includes("--future"),
252
+ });
253
+ applyOperatorDecision(workspace, cycle, decision);
254
+ recomputeCampaignReadiness(cycle);
255
+ saveMarketingWorkspace(workspacePath, workspace);
256
+ saveCampaignCycle(workspacePath, cycle);
257
+ appendOperatorDecision(workspacePath, decision);
258
+ console.log(`Recorded decision ${decision.decision_id}`);
259
+ process.exit(0);
260
+ }
261
+ if (subcommand === "status") {
262
+ const workspace = loadMarketingWorkspace(workspacePath);
263
+ const cycle = loadActiveCampaignCycle(workspacePath, workspace);
264
+ const summary = buildStatusSummary(cycle);
265
+ if (format === "json")
266
+ console.log(JSON.stringify(summary, null, 2));
267
+ else
268
+ process.stdout.write(renderStatusText(summary));
269
+ process.exit(0);
270
+ }
271
+ console.error(`✗ unknown marketing subcommand: ${subcommand}`);
272
+ process.exit(2);
273
+ }
274
+ catch (err) {
275
+ console.error(`✗ ${err instanceof Error ? err.message : String(err)}`);
276
+ process.exit(2);
277
+ }
278
+ }
120
279
  if (command === "init") {
121
280
  // Parse flags: --auto-capture, --mine-chats, --global
122
281
  const flags = args.filter((a) => a.startsWith("--"));
@@ -163,10 +322,10 @@ if (command === "register") {
163
322
  process.exit(0);
164
323
  }
165
324
  if (command === "prove") {
166
- const flags = args.slice(1);
167
- const projectPath = await resolveProjectPath(flags);
325
+ const { format, pathArgs } = parseProveArgs(args.slice(1));
326
+ const projectPath = await resolveProjectPath(pathArgs);
168
327
  const { runProve } = await import("../src/prove.js");
169
- const report = await runProve(projectPath);
328
+ const report = await runProve(projectPath, { format });
170
329
  process.stdout.write(report);
171
330
  process.exit(0);
172
331
  }
@@ -2589,6 +2748,7 @@ Usage:
2589
2748
  sverklo init Set up sverklo in your project (.mcp.json + CLAUDE.md)
2590
2749
  sverklo doctor Diagnose MCP setup issues
2591
2750
  sverklo prove [path] Show central files, a real caller graph, and an agent prompt
2751
+ Use --markdown or --receipt for a shareable artifact.
2592
2752
  sverklo reindex [path] Incremental rebuild of the index (changed files only)
2593
2753
  Use --force to clear and rebuild from scratch.
2594
2754
  Use --timing to see per-phase elapsed ms.
@@ -2613,6 +2773,7 @@ Memory + offline maintenance:
2613
2773
  sverklo wiki Generate a markdown wiki from the indexed codebase
2614
2774
  sverklo digest 5-line summary of what changed in this project (--since 7d)
2615
2775
  sverklo memory export Export memories to markdown / Notion / JSON
2776
+ sverklo marketing Local Sverklo Twitter agent team workflow
2616
2777
  sverklo grammars install Install tree-sitter grammars for the v0.17 opt-in parser
2617
2778
  sverklo prune Decay stale memories + consolidate similar episodic ones
2618
2779
  sverklo concept-index Label clusters with an LLM (requires Ollama)
@@ -2632,6 +2793,7 @@ Quick start (single project):
2632
2793
  npm install -g sverklo
2633
2794
  cd your-project && sverklo init
2634
2795
  sverklo prove
2796
+ sverklo prove --markdown # shareable proof for GitHub, Discord, Reddit, etc.
2635
2797
  claude # start coding — sverklo tools are preferred automatically
2636
2798
 
2637
2799
  Quick start (multi-repo, global):