replen 0.1.0 → 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 +5 -3
- package/dist/index.js +14 -7
- package/dist/init.js +37 -18
- package/dist/mcp-setup.js +5 -2
- package/dist/project-init.js +38 -0
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# replen
|
|
2
2
|
|
|
3
|
-
One-command setup for [replen](https://replen.dev)
|
|
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
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
While your AI coding tool waits for prompts, replen reads your code against the ecosystem and surfaces drop-in libraries, ideas to port, and patterns to learn from. A proactive layer for your AI coding workflow.
|
|
10
|
+
|
|
11
|
+
The single command above:
|
|
10
12
|
1. Opens your browser to sign in / sign up at `app.replen.dev`
|
|
11
13
|
2. Captures the auth back into the terminal (browser-callback flow, like `gh auth login`)
|
|
12
14
|
3. Wires the [@replen/mcp](https://www.npmjs.com/package/@replen/mcp) server into your Claude Code / Codex config
|
|
@@ -48,4 +50,4 @@ Rotate the ingest token on the [/settings](https://app.replen.dev/settings) page
|
|
|
48
50
|
|
|
49
51
|
## License
|
|
50
52
|
|
|
51
|
-
MIT
|
|
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
|
-
|
|
5
|
+
import { runProjectInit } from "./project-init.js";
|
|
6
|
+
const HELP = `replen: Smart AI Development workflows
|
|
6
7
|
|
|
7
8
|
Usage:
|
|
8
|
-
npx replen
|
|
9
|
-
npx replen status
|
|
10
|
-
npx replen mcp setup
|
|
11
|
-
npx replen
|
|
12
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
35
|
+
const code = url.searchParams.get("code");
|
|
36
36
|
const state = url.searchParams.get("state");
|
|
37
|
-
|
|
38
|
-
if (!token || !state) {
|
|
37
|
+
if (!code || !state) {
|
|
39
38
|
res.writeHead(400, { "content-type": "text/plain" });
|
|
40
|
-
res.end("missing
|
|
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({
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
102
|
-
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(
|
|
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
|
|
110
|
-
console.log(" use replen to digest_today
|
|
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: ${
|
|
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
|
|
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
|
-
|
|
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.
|
|
4
|
-
"description": "
|
|
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": [
|
|
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",
|