replen 1.0.1 → 1.0.3
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 +11 -2
- package/dist/commands.js +22 -0
- package/dist/mcp-setup.js +65 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# replen
|
|
2
2
|
|
|
3
|
-
**Make your AI coding tools aware of
|
|
3
|
+
**Make your AI coding tools aware of everything your code depends on, implements, and builds on.** One command, no API keys.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npx replen
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
Claude Code, Codex, Cursor already know your code. Replen tells them what
|
|
9
|
+
Claude Code, Codex, Cursor already know your code. Replen tells them what's happening *outside* it. The match decision happens *inside your AI tool's session* on your subscription tokens. Your code stays on your laptop. Replen never sees it.
|
|
10
|
+
|
|
11
|
+
Four lenses, all surfaced the same calm way — quietly, in your AI tool's next reply, only when there's something real:
|
|
12
|
+
|
|
13
|
+
- **🔭 OSS repos** — a new library that replaces code you maintain, a pattern worth porting, a dead dep to swap.
|
|
14
|
+
- **📦 Your stack** — a release in a dependency you actually use (`next`, `openai`, `prisma`, `viem`, … matched against your manifest).
|
|
15
|
+
- **📜 Standards you implement** — EIP/ERC, TC39, and Chrome-deprecation changes matched to what your code touches.
|
|
16
|
+
- **🩺 Upstream health** — a dep gone stale/archived, a hot bug others hit in your deps, or an incident on a service you use.
|
|
17
|
+
|
|
18
|
+
One discipline throughout: **silence beats a weak match** — if nothing clears the bar, Replen says nothing.
|
|
10
19
|
|
|
11
20
|
## What `npx replen` does
|
|
12
21
|
|
package/dist/commands.js
CHANGED
|
@@ -196,6 +196,25 @@ export async function runCheckNew(argv) {
|
|
|
196
196
|
console.error("Not signed in. Run `npx replen` first.");
|
|
197
197
|
process.exit(1);
|
|
198
198
|
}
|
|
199
|
+
// Hook mode surfaces the repo-scoped SKILL inventory directly — the good data
|
|
200
|
+
// (semantic + dependency / spec / health matches, with the relevance floor +
|
|
201
|
+
// cool-off applied server-side), using the server's pre-formatted footnote.
|
|
202
|
+
// It deliberately skips the legacy hosted `check-new` below, which returns
|
|
203
|
+
// generic, un-scoped, high-star matches that aren't useful to skill-tier
|
|
204
|
+
// users. Output is injected into the agent's opening context by Claude Code's
|
|
205
|
+
// SessionStart-hook stdout injection; the framing nudges the agent to relay
|
|
206
|
+
// it verbatim (final surfacing is still the model's call).
|
|
207
|
+
if (hookMode && !json) {
|
|
208
|
+
const cwdRepo = await detectCwdRepo();
|
|
209
|
+
if (!cwdRepo)
|
|
210
|
+
return; // not in a GitHub repo with a remote — silent
|
|
211
|
+
const inv = await fetchInventoryStatus(cfg, cwdRepo);
|
|
212
|
+
if (inv?.displayText) {
|
|
213
|
+
console.log("[Replen] Relay the following line to the user, verbatim, as a footnote at the very end of your first reply:");
|
|
214
|
+
console.log(inv.displayText);
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
199
218
|
const query = {};
|
|
200
219
|
if (repo !== undefined)
|
|
201
220
|
query.repo = repo;
|
|
@@ -482,6 +501,9 @@ async function fetchInventoryStatus(cfg, repo) {
|
|
|
482
501
|
count: cands.length,
|
|
483
502
|
topRepo: top.repo ?? null,
|
|
484
503
|
topSimilarity: simMatch ? Number(simMatch[1]) : null,
|
|
504
|
+
// The server's pre-formatted, pattern-aware footnote ("By the way — a
|
|
505
|
+
// dependency you use just shipped: …" / "… N candidates queued …").
|
|
506
|
+
displayText: (typeof data.displayText === "string" && data.displayText) ? data.displayText : null,
|
|
485
507
|
};
|
|
486
508
|
}
|
|
487
509
|
catch {
|
package/dist/mcp-setup.js
CHANGED
|
@@ -22,12 +22,31 @@
|
|
|
22
22
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, chmodSync } from "node:fs";
|
|
23
23
|
import { homedir } from "node:os";
|
|
24
24
|
import { join, dirname } from "node:path";
|
|
25
|
+
import { fileURLToPath } from "node:url";
|
|
25
26
|
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
26
27
|
import { installSkills } from "./skill-install.js";
|
|
27
28
|
const SERVER_NAME = "replen";
|
|
28
29
|
const CLAUDE_CONFIG = join(homedir(), ".claude.json");
|
|
30
|
+
const CLAUDE_SETTINGS = join(homedir(), ".claude", "settings.json");
|
|
29
31
|
const CODEX_CONFIG = join(homedir(), ".codex", "config.toml");
|
|
30
32
|
const GEMINI_CONFIG = join(homedir(), ".gemini", "settings.json");
|
|
33
|
+
// Read/triage replen MCP tools that are safe to auto-allow so the proactive
|
|
34
|
+
// footnote (replen_match) and in-session triage don't trigger a permission
|
|
35
|
+
// prompt every session. Deliberately EXCLUDES replen_run / replen_handoff /
|
|
36
|
+
// replen_feedback — those trigger pipeline runs / open PRs and should keep
|
|
37
|
+
// prompting for explicit consent.
|
|
38
|
+
const REPLEN_AUTO_ALLOW = [
|
|
39
|
+
"mcp__replen__replen_match",
|
|
40
|
+
"mcp__replen__replen_check_new",
|
|
41
|
+
"mcp__replen__replen_analyze",
|
|
42
|
+
"mcp__replen__replen_state",
|
|
43
|
+
"mcp__replen__replen_record_triage",
|
|
44
|
+
"mcp__replen__replen_today",
|
|
45
|
+
"mcp__replen__replen_search",
|
|
46
|
+
"mcp__replen__replen_starred",
|
|
47
|
+
"mcp__replen__replen_status",
|
|
48
|
+
"mcp__replen__replen_help",
|
|
49
|
+
];
|
|
31
50
|
// ============================================================================
|
|
32
51
|
// Public entry point
|
|
33
52
|
// ============================================================================
|
|
@@ -76,12 +95,37 @@ function setupClaude(token, base) {
|
|
|
76
95
|
};
|
|
77
96
|
const hooks = installSessionStartHook(config.hooks ?? {});
|
|
78
97
|
writeJsonAtomic(path, { ...config, mcpServers, hooks });
|
|
98
|
+
// Auto-allow the read/triage tools so the proactive footnote doesn't
|
|
99
|
+
// prompt every session. Best-effort: a failure here mustn't fail MCP setup.
|
|
100
|
+
try {
|
|
101
|
+
allowlistClaudeTools();
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
console.warn(` ⚠ Claude Code allowlist skipped — ${e.message}`);
|
|
105
|
+
}
|
|
79
106
|
return { ok: true, label: "Claude Code", path, action: existed ? "updated" : "added" };
|
|
80
107
|
}
|
|
81
108
|
catch (e) {
|
|
82
109
|
return { ok: false, label: "Claude Code", path, error: e.message };
|
|
83
110
|
}
|
|
84
111
|
}
|
|
112
|
+
// Merge the replen read/triage tools into Claude Code's permission allowlist
|
|
113
|
+
// (~/.claude/settings.json → permissions.allow) so the proactive footnote +
|
|
114
|
+
// in-session triage run without a per-session permission prompt. Non-
|
|
115
|
+
// destructive: preserves existing allow entries and every other setting.
|
|
116
|
+
function allowlistClaudeTools() {
|
|
117
|
+
const path = CLAUDE_SETTINGS;
|
|
118
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
119
|
+
const config = existsSync(path) ? readJson(path) : {};
|
|
120
|
+
const permissions = config.permissions ?? {};
|
|
121
|
+
const existingAllow = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
122
|
+
const allow = Array.from(new Set([...existingAllow, ...REPLEN_AUTO_ALLOW]));
|
|
123
|
+
if (allow.length === existingAllow.length)
|
|
124
|
+
return; // already fully allowlisted
|
|
125
|
+
backupIfExists(path);
|
|
126
|
+
writeJsonAtomic(path, { ...config, permissions: { ...permissions, allow } });
|
|
127
|
+
console.log(` ✓ Claude Code: allowlisted replen read tools (no more per-session prompts)`);
|
|
128
|
+
}
|
|
85
129
|
function setupGemini(token, base) {
|
|
86
130
|
const path = GEMINI_CONFIG;
|
|
87
131
|
try {
|
|
@@ -196,8 +240,27 @@ function backupIfExists(path) {
|
|
|
196
240
|
// their current docs). Both rely on the AGENTS.md / GEMINI.md project-
|
|
197
241
|
// context file being read at session start, which our inject step
|
|
198
242
|
// covers.
|
|
199
|
-
|
|
200
|
-
|
|
243
|
+
// Version-PIN the hook command. A bare `npx replen` resolves a LOCAL package
|
|
244
|
+
// named "replen" when one exists in cwd (e.g. the replen repo itself, whose
|
|
245
|
+
// server package is also "replen" and has no bin) → "could not determine
|
|
246
|
+
// executable to run", and the hook silently dies every session there. Pinning
|
|
247
|
+
// to the published version forces npx to the registry (collision-proof) and,
|
|
248
|
+
// because the exact version is cached, avoids the per-session `@latest`
|
|
249
|
+
// registry round-trip. Re-running setup refreshes the pin.
|
|
250
|
+
const HOOK_COMMAND = `npx --quiet replen@${cliVersion()} check-new --hook`;
|
|
251
|
+
// Match on the stable substring so we find/replace our hook regardless of the
|
|
252
|
+
// pinned version in any previously-written command.
|
|
253
|
+
const HOOK_MARKER = "check-new --hook";
|
|
254
|
+
function cliVersion() {
|
|
255
|
+
try {
|
|
256
|
+
const here = dirname(fileURLToPath(import.meta.url)); // cli/dist
|
|
257
|
+
const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
|
|
258
|
+
return typeof pkg.version === "string" && pkg.version ? pkg.version : "latest";
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return "latest";
|
|
262
|
+
}
|
|
263
|
+
}
|
|
201
264
|
function installSessionStartHook(existing) {
|
|
202
265
|
const sessionStart = (existing.SessionStart ?? []).slice();
|
|
203
266
|
const ours = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replen",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Make your AI coding tools smarter. One command, no API keys. Replen scouts the OSS firehose against your projects and surfaces drop-in libraries, ideas to port, and dead deps to swap — the match decision happens inside your AI tool's session on your subscription tokens. 1-3 actionable matches a month, by design.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|