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 +36 -2
- package/extras/skills/replen-match/SKILL.md +45 -5
- package/package.json +1 -1
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
|
-
|
|
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
|
-
**
|
|
185
|
-
your project list"
|
|
186
|
-
|
|
187
|
-
|
|
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
|
+
"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": {
|