replen 1.0.12 → 1.0.14

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.
@@ -169,14 +169,15 @@ export function discoverProjects(roots) {
169
169
  if (seenGithub.has(githubFullName))
170
170
  continue;
171
171
  seenGithub.add(githubFullName);
172
- const { name, tags, primaryLanguage } = extractMetadata(repoPath, dirName);
173
- // Slug from the GitHub repo NAME, not the local folder name. The repo
174
- // name is the stable identity the server keys on; deriving the slug
175
- // from the folder meant a folder rename (or an org rename like
176
- // Covelent→nsokin) minted a fresh slug and the server inserted a
177
- // duplicate row. `~/code/drone` with remote `nsokin/palisade-website`
178
- // now registers as slug `palisade-website`, matching the dashboard.
172
+ // Slug + display name both anchor on the GitHub repo NAME, not the
173
+ // local folder name. Deriving from the folder meant a folder rename (or
174
+ // an org rename like Covelent→nsokin) minted a fresh slug and the server
175
+ // inserted a duplicate row. `~/code/drone` with remote
176
+ // `nsokin/palisade-website` now registers as slug `palisade-website`.
177
+ // The repo name is also the fallback display name, so a generic
178
+ // package.json name (the Next.js starter's "nextn", etc.) doesn't stick.
179
179
  const repoName = githubFullName.split("/").pop() || dirName;
180
+ const { name, tags, primaryLanguage } = extractMetadata(repoPath, repoName);
180
181
  projects.push({
181
182
  localPath: repoPath,
182
183
  slug: normaliseSlug(repoName),
@@ -292,6 +293,17 @@ function readGitRemote(repoPath) {
292
293
  return null;
293
294
  return `${m[1]}/${m[2]}`;
294
295
  }
296
+ // Scaffold/template default package.json names that aren't real project
297
+ // names — repos cloned from the same starter all share one (the Next.js
298
+ // starter's "nextn"). Mirrors the server's GENERIC_NAMES guard.
299
+ const GENERIC_PROJECT_NAMES = new Set([
300
+ "nextn", "next-app", "create-next-app", "nextjs", "next", "my-app", "myapp",
301
+ "my-project", "myproject", "app", "web", "webapp", "frontend", "backend",
302
+ "client", "server", "project", "vite-project", "vite-app", "react-app",
303
+ "turborepo", "my-turborepo", "monorepo", "example", "template", "starter",
304
+ "boilerplate", "hello-world", "test", "demo", "untitled",
305
+ ]);
306
+ const isGenericProjectName = (n) => GENERIC_PROJECT_NAMES.has(n.trim().toLowerCase());
295
307
  function extractMetadata(repoPath, fallbackName) {
296
308
  const tags = new Set();
297
309
  let name = fallbackName;
@@ -302,7 +314,7 @@ function extractMetadata(repoPath, fallbackName) {
302
314
  primaryLanguage = "TypeScript"; // updated below if no TS detected
303
315
  try {
304
316
  const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
305
- if (typeof pkg.name === "string" && pkg.name.length > 0)
317
+ if (typeof pkg.name === "string" && pkg.name.length > 0 && !isGenericProjectName(pkg.name))
306
318
  name = pkg.name;
307
319
  if (Array.isArray(pkg.keywords)) {
308
320
  for (const k of pkg.keywords) {
@@ -41,6 +41,40 @@ Parse the JSON response. Note:
41
41
  - `filterMode` — `tags`, `zero-knowledge`, or `fingerprint`
42
42
  - `scopedTo` — confirms the project context the user has open
43
43
  - `candidates[]` — the actual list to triage
44
+ - `candidates[].priorContext` — server-attached MEMORY: the user's earlier
45
+ verdicts on this repo, and whether the matched capability is already
46
+ covered by something they adopted/ported. Trust it — fold it into your
47
+ verdict instead of re-deriving history, and don't push a candidate whose
48
+ capability is covered unless it's materially better than the incumbent.
49
+ - `candidates[].source === "re-checked"` — a repo the user DEFERRED months
50
+ ago that is still actively developed. Re-evaluate it against today's
51
+ state of the project; the original "not now" note is in `priorContext`.
52
+ - `leap` — on quiet days the response may carry ONE portfolio connection
53
+ (a cross-project / adjacency / cross-user leap) instead of candidates.
54
+ `displayText` already words it; relay that and offer to explore it.
55
+ - `queuedActions` — work the user queued from their weekly brief / alert
56
+ emails (or a past session). The footnote offers the oldest one; if the
57
+ user says yes, DO the work (bump the dep, handle the deprecation,
58
+ evaluate the repo), then call `replen_queue` with `action: "done"` and
59
+ the item's id. If they decline for good, `action: "dismiss"`. Never
60
+ leave a handled item queued — it will keep reminding.
61
+ - `candidates[].alternatives` — on health/security stakes ("your upstream
62
+ is dying / has a CVE"), maintained catalogue libraries similar to the
63
+ flagged repo, with cross-user adoption counts. Use them in the writeup:
64
+ the verdict isn't just "X is risky" but "X is risky; Y is the maintained
65
+ replacement, N similar projects adopted it".
66
+
67
+ ### Step 2b — Keep the version picture fresh (cheap, do it)
68
+
69
+ If `git status` shows the lockfile changed since you last reported, or you
70
+ have never reported for this repo, call `replen_set_versions` with the
71
+ resolved DIRECT dependency versions from the lockfile (package-lock.json /
72
+ poetry.lock / uv.lock / Cargo.lock) plus runtimes under canonical keys
73
+ (`node` from .nvmrc/engines/Dockerfile, `python` from .python-version /
74
+ requires-python, `postgres`/`redis` when pinned in docker-compose). Names
75
+ and versions ONLY — never code. This is what turns Replen's deadline and
76
+ security lines from "worth checking your pins" into "affects `aegis`
77
+ (3.10.12)" — and silences alarms for versions this repo verifiably isn't on.
44
78
 
45
79
  If `candidates.length === 0`, tell the user "No new candidates today for
46
80
  `<owner/name>`. Calm-cadence working as designed — 1-3 actionable
@@ -85,6 +119,10 @@ Verdicts:
85
119
  candidate's runtime is incompatible, or the candidate's not actively
86
120
  maintained. Honest skips are valuable signal; don't manufacture
87
121
  reasons to keep something.
122
+ - **defer** — genuinely interesting but not NOW (too early / v0.x churn /
123
+ blocked on a milestone the project hasn't hit). Defer is a real promise,
124
+ not a soft skip: Replen automatically re-surfaces a deferred repo after
125
+ ~3 months if it's still actively developed (`source: "re-checked"`).
88
126
 
89
127
  **Watch for word-collisions** — the most common bad match. The candidate
90
128
  shares a *word* with the matched capability but its real domain diverges:
@@ -296,6 +334,21 @@ The user hasn't set up filter-mode B's tag list. Mention it once: "Heads
296
334
  up — you'd get sharper matches if you set project tags at /settings.
297
335
  Continuing with unfiltered for now."
298
336
 
337
+ ## The Atlas vault — local memory for any agent
338
+
339
+ Replen keeps an agent-readable markdown vault at `~/.replen/atlas/` — the
340
+ user's whole portfolio as linked notes: every project and what it does,
341
+ every capability (with how it's known: `grounded`/`extracted`/`inferred`),
342
+ every past decision with its reason code, plus themes and blind spots.
343
+ The MCP server refreshes it in the background (at most twice a day);
344
+ `replen atlas` forces a rewrite.
345
+
346
+ Use it whenever you need CROSS-PROJECT context: "what else does this user
347
+ build?", "have we solved X in another repo?", "what did we decide about
348
+ Y last quarter?". Start at `MAP.md`, or call `replen_recall` for a direct
349
+ query. Reading the vault beats re-deriving the portfolio from scratch —
350
+ it's the memory layer, and it's already on disk.
351
+
299
352
  ## When NOT to run this skill
300
353
 
301
354
  - The user is mid-task and just wants help with the current thing. The
@@ -128,6 +128,14 @@ specific — `cloudflare bypass`/`proxy rotation`, not just `web scraping`.
128
128
  grounded `capabilities` array AND the `report` from 2c. The server builds the
129
129
  facet vectors immediately and stores the report as grounding for its own
130
130
  summarization. Use the MCP tools, not hand-rolled `curl`.
131
+ 4. **Report pinned versions** with `replen_set_versions` — the resolved DIRECT
132
+ dependency versions from the lockfile (package-lock.json / poetry.lock /
133
+ uv.lock / Cargo.lock), plus runtimes under canonical keys (`node` from
134
+ .nvmrc/engines/Dockerfile, `python` from .python-version/requires-python,
135
+ `postgres`/`redis` when pinned in docker-compose). Names + versions ONLY,
136
+ never code. This makes EOL/deprecation/security awareness name the affected
137
+ repos with certainty — and suppresses alarms for versions the repo
138
+ verifiably isn't on.
131
139
 
132
140
  ## Step 3 — Group multi-repo products
133
141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replen",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Make your AI coding tools smarter. One command, no API keys, free. Replen watches what your projects actually do and surfaces a few things worth bringing in each month. Use one as is, port a piece of another, cherry pick an idea, or build it clean room. The match happens inside your AI tool's session. A few actionable matches a month, by design.",
5
5
  "type": "module",
6
6
  "bin": {