replen 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # replen
2
2
 
3
- **Smart AI Development workflows.** One-command setup for [replen](https://replen.dev) the AI that asks *"can we do this better?"* on your codebase, every morning.
3
+ **Smart AI Development workflows.** One-command setup for [replen](https://replen.dev) - the AI that asks *"can we do this better?"* on your codebase, every morning.
4
4
 
5
5
  ```bash
6
6
  npx replen
@@ -50,4 +50,4 @@ Rotate the ingest token on the [/settings](https://app.replen.dev/settings) page
50
50
 
51
51
  ## License
52
52
 
53
- MIT see [LICENSE](./LICENSE).
53
+ MIT - see [LICENSE](./LICENSE).
package/dist/index.js CHANGED
@@ -2,14 +2,17 @@
2
2
  import { runInit } from "./init.js";
3
3
  import { setupMcp } from "./mcp-setup.js";
4
4
  import { readConfig, configPath } from "./config.js";
5
- const HELP = `replen Smart AI Development workflows
5
+ import { runProjectInit } from "./project-init.js";
6
+ const HELP = `replen: Smart AI Development workflows
6
7
 
7
8
  Usage:
8
- npx replen Sign up / sign in + wire MCP into Claude Code
9
- npx replen status Show current config
10
- npx replen mcp setup Re-wire MCP using saved auth
11
- npx replen logout Forget saved auth
12
- npx replen --help This help
9
+ npx replen Sign up / sign in + wire MCP into Claude Code
10
+ npx replen status Show current config
11
+ npx replen mcp setup Re-wire MCP using saved auth
12
+ npx replen project-init Print a prompt your AI coding tool uses to draft
13
+ a CLAUDE.md tuned for replen
14
+ npx replen logout Forget saved auth
15
+ npx replen --help This help
13
16
 
14
17
  Env:
15
18
  REPLEN_BASE Override dashboard URL (default https://app.replen.dev)
@@ -36,6 +39,10 @@ async function main() {
36
39
  console.log(` Config: ${configPath()}`);
37
40
  return;
38
41
  }
42
+ if (cmd === "project-init") {
43
+ runProjectInit();
44
+ return;
45
+ }
39
46
  if (cmd === "mcp" && argv[1] === "setup") {
40
47
  const cfg = await readConfig();
41
48
  if (!cfg) {
@@ -54,7 +61,7 @@ async function main() {
54
61
  }
55
62
  catch (e) {
56
63
  if (e?.code === "ENOENT") {
57
- console.log(`No saved auth nothing to forget.`);
64
+ console.log(`No saved auth; nothing to forget.`);
58
65
  }
59
66
  else {
60
67
  throw e;
package/dist/init.js CHANGED
@@ -4,7 +4,7 @@ import { spawn } from "node:child_process";
4
4
  import { platform } from "node:os";
5
5
  import { writeConfig, configPath } from "./config.js";
6
6
  import { setupMcp } from "./mcp-setup.js";
7
- // Default web app URL override with REPLEN_BASE for self-host.
7
+ // Default web app URL. Override with REPLEN_BASE for self-host.
8
8
  const DEFAULT_BASE = process.env.REPLEN_BASE || "https://app.replen.dev";
9
9
  // Range for the local callback listener. Anything in 1024-65535 works.
10
10
  const PORT_MIN = 38000;
@@ -16,10 +16,10 @@ function openBrowser(url) {
16
16
  const cmd = platform() === "darwin" ? "open"
17
17
  : platform() === "win32" ? "start"
18
18
  : "xdg-open";
19
- // Detach we don't care about its exit.
19
+ // Detach. We don't care about its exit.
20
20
  const proc = spawn(cmd, [url], { stdio: "ignore", detached: true });
21
21
  proc.on("error", () => {
22
- // Fail silently we print the URL anyway as fallback.
22
+ // Fail silently. We print the URL anyway as fallback.
23
23
  });
24
24
  proc.unref();
25
25
  }
@@ -32,12 +32,11 @@ function waitForCallback(port, expectedState) {
32
32
  res.end("not found");
33
33
  return;
34
34
  }
35
- const token = url.searchParams.get("token");
35
+ const code = url.searchParams.get("code");
36
36
  const state = url.searchParams.get("state");
37
- const base = url.searchParams.get("base") ?? DEFAULT_BASE;
38
- if (!token || !state) {
37
+ if (!code || !state) {
39
38
  res.writeHead(400, { "content-type": "text/plain" });
40
- res.end("missing token/state");
39
+ res.end("missing code/state");
41
40
  return;
42
41
  }
43
42
  if (state !== expectedState) {
@@ -49,7 +48,7 @@ function waitForCallback(port, expectedState) {
49
48
  res.end(SUCCESS_HTML);
50
49
  // Give the response a tick to flush before we shut down the listener.
51
50
  setTimeout(() => server.close(), 100);
52
- resolve({ token, base });
51
+ resolve({ code });
53
52
  });
54
53
  server.on("error", reject);
55
54
  server.listen(port, "127.0.0.1");
@@ -61,8 +60,20 @@ function waitForCallback(port, expectedState) {
61
60
  server.on("close", () => clearTimeout(timeout));
62
61
  });
63
62
  }
63
+ async function exchangeCode(base, code, state) {
64
+ const res = await fetch(`${base}/api/cli-auth/exchange`, {
65
+ method: "POST",
66
+ headers: { "content-type": "application/json" },
67
+ body: JSON.stringify({ code, state }),
68
+ });
69
+ const body = (await res.json().catch(() => ({})));
70
+ if (!res.ok || !body.ok || !body.token) {
71
+ throw new Error(`Exchange failed: ${body.error ?? `HTTP ${res.status}`}`);
72
+ }
73
+ return { token: body.token, base: body.base || base };
74
+ }
64
75
  const SUCCESS_HTML = `<!doctype html>
65
- <html><head><meta charset="utf-8"><title>replen authorized</title>
76
+ <html><head><meta charset="utf-8"><title>replen: authorized</title>
66
77
  <style>
67
78
  body { font: 15px system-ui, -apple-system, sans-serif; max-width: 480px;
68
79
  margin: 80px auto; padding: 0 24px; color: #111; line-height: 1.55; }
@@ -73,7 +84,7 @@ const SUCCESS_HTML = `<!doctype html>
73
84
  <body>
74
85
  <h1><span class="ok">✓</span> Authorized</h1>
75
86
  <p>The replen CLI is now connected to your account.</p>
76
- <p>You can close this tab and head back to your terminal the CLI is finishing setup.</p>
87
+ <p>You can close this tab and head back to your terminal. The CLI is finishing setup.</p>
77
88
  </body></html>`;
78
89
  export async function runInit() {
79
90
  const state = randomBytes(32).toString("hex");
@@ -89,26 +100,34 @@ export async function runInit() {
89
100
  console.log(" (Waiting for browser callback on http://127.0.0.1:" + port + "…)");
90
101
  console.log("");
91
102
  openBrowser(authUrl);
92
- let result;
103
+ let cb;
104
+ try {
105
+ cb = await waitForCallback(port, state);
106
+ }
107
+ catch (e) {
108
+ console.error(" ✗ " + (e?.message ?? String(e)));
109
+ process.exit(1);
110
+ }
111
+ let exchange;
93
112
  try {
94
- result = await waitForCallback(port, state);
113
+ exchange = await exchangeCode(base, cb.code, state);
95
114
  }
96
115
  catch (e) {
97
116
  console.error(" ✗ " + (e?.message ?? String(e)));
98
117
  process.exit(1);
99
118
  }
100
119
  await writeConfig({
101
- token: result.token,
102
- base: result.base,
120
+ token: exchange.token,
121
+ base: exchange.base,
103
122
  savedAt: new Date().toISOString(),
104
123
  });
105
124
  console.log(` ✓ Saved auth to ${configPath()}`);
106
- await setupMcp(result.token, result.base);
125
+ await setupMcp(exchange.token, exchange.base);
107
126
  console.log("");
108
127
  console.log(" All set. Restart Claude Code (or Codex) and try:");
109
- console.log(" /replen-triage runs the morning triage protocol");
110
- console.log(" use replen to digest_today pulls today's matches");
128
+ console.log(" /replen-triage runs the morning triage protocol");
129
+ console.log(" use replen to digest_today pulls today's matches");
111
130
  console.log("");
112
- console.log(` Dashboard: ${result.base}`);
131
+ console.log(` Dashboard: ${exchange.base}`);
113
132
  console.log("");
114
133
  }
package/dist/mcp-setup.js CHANGED
@@ -2,7 +2,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from "
2
2
  import { homedir } from "node:os";
3
3
  import { join, dirname } from "node:path";
4
4
  // Write the @replen/mcp server entry into Claude Code's config. Uses npx so
5
- // the user doesn't need a separate global install Claude Code will fetch
5
+ // the user doesn't need a separate global install; Claude Code will fetch
6
6
  // @replen/mcp on first MCP launch and cache it.
7
7
  const SERVER_NAME = "replen";
8
8
  const CONFIG_PATH = join(homedir(), ".claude.json");
@@ -23,7 +23,10 @@ function writeJsonAtomic(path, data) {
23
23
  export async function setupMcp(token, base) {
24
24
  console.log(` Wiring replen MCP into Claude Code config…`);
25
25
  if (existsSync(CONFIG_PATH)) {
26
- const backup = `${CONFIG_PATH}.bak`;
26
+ // Timestamp the backup so re-running setup never overwrites a previous
27
+ // backup. Each run preserves the prior state.
28
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
29
+ const backup = `${CONFIG_PATH}.bak.${ts}`;
27
30
  writeFileSync(backup, readFileSync(CONFIG_PATH));
28
31
  console.log(` (backed up existing config to ${backup})`);
29
32
  }
@@ -0,0 +1,38 @@
1
+ // `replen project-init` — prints a copy-pasteable prompt the user feeds to
2
+ // their AI coding tool (Claude Code, Codex, Cursor, Aider, whatever) so it
3
+ // can draft a CLAUDE.md tuned for replen's relevance scorer.
4
+ //
5
+ // The prompt deliberately points at the live template URL so changes to the
6
+ // template don't require shipping a new CLI version. We do not write any
7
+ // files ourselves — the AI tool does, locally, with full source access.
8
+ const PROMPT = `Read this repository and produce a CLAUDE.md at the root, optimised for replen (https://replen.dev) — the daily research tool that scores new OSS repos against this project.
9
+
10
+ Follow the template + section guidance at https://docs.replen.dev/project-docs.html. The seven sections are:
11
+
12
+ ## What this project is
13
+ ## Stack
14
+ ## Niche / problem domain
15
+ ## Active areas
16
+ ## Constraints & non-goals
17
+ ## Anti-patterns
18
+ ## Integration preferences
19
+
20
+ Be specific. Use the project's actual domain vocabulary, not generic abstractions. Derive content from the source, the existing README, and the last 30 git commits — not boilerplate. If you can't verify a section from the code or git history, leave it short rather than making things up.
21
+
22
+ When you're done, print the draft in chat and ask me to confirm before writing the file. If a CLAUDE.md already exists, don't overwrite it without my explicit say-so.
23
+
24
+ Total length target: 200-500 words, bullet points over paragraphs.`;
25
+ export function runProjectInit() {
26
+ console.log("");
27
+ console.log("Copy the prompt below into your AI coding tool (Claude Code / Codex / Cursor / Aider).");
28
+ console.log("It will read this repo and draft a CLAUDE.md tuned for replen.");
29
+ console.log("");
30
+ console.log("─".repeat(64));
31
+ console.log(PROMPT);
32
+ console.log("─".repeat(64));
33
+ console.log("");
34
+ console.log("If you have the replen-project-init skill installed in Claude Code,");
35
+ console.log("you can also invoke it with: /replen-project-init");
36
+ console.log("(install: copy skills/replen-project-init/ into ~/.claude/skills/)");
37
+ console.log("");
38
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "replen",
3
- "version": "0.1.1",
4
- "description": "Smart AI Development workflows. The AI that asks 'can we do this better?' replen reads your codebase against the live ecosystem every morning, surfaces drop-in libraries, ideas to port, and patterns to learn from. A proactive layer for your AI coding workflow. One-command setup: opens a browser to sign in, then wires the MCP server into Claude Code / Codex.",
3
+ "version": "0.1.2",
4
+ "description": "Smart AI Development workflows. The AI that asks 'can we do this better?' - replen reads your codebase against the live ecosystem every morning, surfaces drop-in libraries, ideas to port, and patterns to learn from. A proactive layer for your AI coding workflow. One-command setup: opens a browser to sign in, then wires the MCP server into Claude Code / Codex.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "replen": "dist/index.js"
@@ -16,7 +16,13 @@
16
16
  "README.md",
17
17
  "LICENSE"
18
18
  ],
19
- "keywords": ["replen", "oss-discovery", "claude-code", "mcp", "cli"],
19
+ "keywords": [
20
+ "replen",
21
+ "oss-discovery",
22
+ "claude-code",
23
+ "mcp",
24
+ "cli"
25
+ ],
20
26
  "homepage": "https://replen.dev",
21
27
  "repository": {
22
28
  "type": "git",