replen 1.0.3 → 1.0.5

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/dist/commands.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // Plain-shell subcommands. Each one mirrors an MCP tool but renders for the
2
2
  // terminal rather than returning JSON to an agent. `--json` flag on every
3
3
  // command dumps raw JSON for piping/scripting.
4
+ import { readdirSync, existsSync } from "node:fs";
4
5
  import { apiGet, apiPost, loadConfigOrExit } from "./api.js";
5
6
  import { configPath } from "./config.js";
6
7
  function hasFlag(argv, flag) {
@@ -206,8 +207,16 @@ export async function runCheckNew(argv) {
206
207
  // it verbatim (final surfacing is still the model's call).
207
208
  if (hookMode && !json) {
208
209
  const cwdRepo = await detectCwdRepo();
209
- if (!cwdRepo)
210
- return; // not in a GitHub repo with a remote silent
210
+ if (!cwdRepo) {
211
+ // No git remote Replen can't scope this project. If the cwd looks like
212
+ // a real project (has source / a manifest), nudge the user to set it up;
213
+ // otherwise stay silent (don't nag in /tmp or a random dir). Surfacing is
214
+ // the model's call, but the explicit framing maximises it.
215
+ if (looksLikeProject()) {
216
+ console.log("[Replen] This project has no git remote, so Replen can't scope or match against it. Mention to the user, briefly, as a footnote: 'By the way — this project isn't set up with Replen yet (no git remote), so I can't surface matches for it. Want me to scope it — init git, create the repo, write the docs, and add tags?'");
217
+ }
218
+ return;
219
+ }
211
220
  const inv = await fetchInventoryStatus(cfg, cwdRepo);
212
221
  if (inv?.displayText) {
213
222
  console.log("[Replen] Relay the following line to the user, verbatim, as a footnote at the very end of your first reply:");
@@ -474,6 +483,31 @@ async function detectCwdRepo() {
474
483
  return null;
475
484
  }
476
485
  }
486
+ // Cheap heuristic: does the cwd look like a real project worth nudging the user
487
+ // to scope with Replen? True when there's a recognised manifest, a src/lib/app
488
+ // dir, or a couple of source files. Keeps the "no git — want to scope?" prompt
489
+ // from firing in /tmp, $HOME, or an empty directory.
490
+ function looksLikeProject() {
491
+ try {
492
+ const manifests = ["package.json", "pyproject.toml", "requirements.txt", "Cargo.toml", "go.mod", "pom.xml", "build.gradle", "Gemfile", "composer.json", "pubspec.yaml"];
493
+ if (manifests.some((m) => existsSync(m)))
494
+ return true;
495
+ if (["src", "lib", "app", "cmd", "pkg"].some((d) => existsSync(d)))
496
+ return true;
497
+ const codeExt = /\.(ts|tsx|js|jsx|py|rs|go|java|rb|php|c|cc|cpp|h|hpp|swift|kt|scala|sol|ex|clj)$/i;
498
+ let codeFiles = 0;
499
+ for (const e of readdirSync(".", { withFileTypes: true })) {
500
+ if (e.isFile() && codeExt.test(e.name))
501
+ codeFiles++;
502
+ if (codeFiles >= 2)
503
+ return true;
504
+ }
505
+ return false;
506
+ }
507
+ catch {
508
+ return false;
509
+ }
510
+ }
477
511
  async function fetchInventoryStatus(cfg, repo) {
478
512
  // Hook mode is on the session-open critical path; cap latency hard.
479
513
  const ctrl = new AbortController();
@@ -181,11 +181,51 @@ fresh candidate shows up at session start via the hook).
181
181
  **Inventory call returns 401.** User's token expired or got rotated.
182
182
  Tell them to run `npx replen` to re-auth.
183
183
 
184
- **Inventory returns `scopedTo: null` with a `note` about "repo not in
185
- your project list"**. The cwd isn't a known project. Ask the user:
186
- "This repo isn't in your Replen projects yet. Add it via /projects?"
187
- Then either stop or re-run with `?repo=` (empty) to see the global
188
- firehose.
184
+ **The project isn't scoped — `scopedTo: null`, a `note` about "repo not in
185
+ your project list", OR the cwd has no git remote.** Replen can only match
186
+ against a repo it has registered, and it scopes by the git remote. When it's
187
+ unscoped you'll get the global trending firehose which is noise for this
188
+ codebase. **Do NOT triage the firehose** (manufacturing reasons to care about
189
+ random trending repos is exactly what this skill must not do).
190
+
191
+ Instead, **offer to onboard the project.** Lead with ONE line, not the whole
192
+ checklist: *"This project isn't set up with Replen yet, so I can only see the
193
+ global firehose (not matches for your code). Want me to scope it — init git,
194
+ create the repo, write the docs, and add tags? Then Replen can surface things
195
+ that actually fit."* If the user agrees, run this checklist:
196
+
197
+ 1. **Git + GitHub.** If there's no git repo, `git init`. Create the GitHub
198
+ repo using the user's existing `gh` auth — ask for owner/name or suggest a
199
+ sensible default from the folder name, and confirm public vs private:
200
+ `gh repo create <owner>/<name> --private --source=. --remote=origin --push`.
201
+ If a repo exists locally but has no remote, just add + push the remote.
202
+ 2. **Docs Replen can read.** Replen's scorer reads your `README.md` +
203
+ `CLAUDE.md` to understand the project — that's the difference between
204
+ useful matches and noise. Write a concrete `README.md` (what it is, stack,
205
+ domain) if missing, and a `CLAUDE.md` optimised for Replen (run the
206
+ `/replen-project-init` protocol, or draft the seven sections directly:
207
+ what it is · stack · niche/domain · active areas · constraints/non-goals ·
208
+ anti-patterns · integration preferences). Use the project's real domain
209
+ vocabulary, not abstractions.
210
+ 3. **Register + tag.** Register the repo: `npx replen sync-projects` (scans the
211
+ local repos and pushes them to Replen). Then **set the domain tags yourself
212
+ with the `replen_set_tags` tool** — derive them from the code you just read
213
+ (e.g. for a Python CCXT market-making engine:
214
+ `["crypto","trading","market-making","ccxt","quant","backtesting"]`).
215
+ **Do NOT tell the user to set tags on the web** — that's the sticky step this
216
+ replaces; set them with the tool. (They can still fine-tune later at
217
+ app.replen.dev/projects.) Tags matter most right after onboarding, before the
218
+ project has an embedding — without them a fresh project falls back to
219
+ language-only matching and surfaces noise.
220
+ 4. **Re-run.** Once it has a remote + docs + tags, call `replen_match` again —
221
+ now it scopes to the project and matches against the real code. (Its embedding
222
+ lands on the next pipeline run; until then, tags carry the relevance.)
223
+
224
+ Note on recording actions: always use the MCP tools — `replen_state` (star /
225
+ hide / handoff), `replen_record_triage` (your verdict), `replen_set_tags` — for
226
+ any write back to Replen. Don't hand-roll `curl` to the API for these; the MCP
227
+ path is the intended mechanism and avoids tripping host permission classifiers
228
+ on the candidate repo name in a curl payload.
189
229
 
190
230
  **Candidate's README is unreachable (WebFetch 404)**. Note it in the
191
231
  writeup (`Caveats: README unreachable; verdict based on description
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replen",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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": {