replen 1.0.10 → 1.0.12
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 +33 -0
- package/dist/discover-projects.js +18 -12
- package/dist/index.js +7 -1
- package/package.json +2 -2
package/dist/commands.js
CHANGED
|
@@ -547,3 +547,36 @@ async function fetchInventoryStatus(cfg, repo) {
|
|
|
547
547
|
clearTimeout(timer);
|
|
548
548
|
}
|
|
549
549
|
}
|
|
550
|
+
// `replen atlas` — write your knowledge graph as an owned, Obsidian-compatible
|
|
551
|
+
// markdown vault to ~/.replen/atlas/. Fetches the rendered files from the server
|
|
552
|
+
// (one source of truth) and writes them locally; you own and can open them.
|
|
553
|
+
export async function runAtlas(argv) {
|
|
554
|
+
const { mkdirSync, writeFileSync, rmSync } = await import("node:fs");
|
|
555
|
+
const { join, dirname } = await import("node:path");
|
|
556
|
+
const { homedir } = await import("node:os");
|
|
557
|
+
const cfg = await loadConfigOrExit();
|
|
558
|
+
const dir = getFlag(argv, "--out") ?? join(homedir(), ".replen", "atlas");
|
|
559
|
+
const data = await apiGet(cfg, "/api/graph/atlas");
|
|
560
|
+
if (!data.files?.length) {
|
|
561
|
+
console.log("No Atlas yet — run /replen-onboard and a pipeline run first so the graph has something to map.");
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
// Fresh write: clear the managed subdirs, then write.
|
|
565
|
+
for (const sub of ["projects", "capabilities", "candidates", "themes"]) {
|
|
566
|
+
try {
|
|
567
|
+
rmSync(join(dir, sub), { recursive: true, force: true });
|
|
568
|
+
}
|
|
569
|
+
catch { /* */ }
|
|
570
|
+
}
|
|
571
|
+
for (const f of data.files) {
|
|
572
|
+
const full = join(dir, f.path);
|
|
573
|
+
mkdirSync(dirname(full), { recursive: true });
|
|
574
|
+
writeFileSync(full, f.content);
|
|
575
|
+
}
|
|
576
|
+
if (hasFlag(argv, "--json")) {
|
|
577
|
+
console.log(JSON.stringify({ dir, count: data.files.length }));
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
console.log(`Atlas written: ${data.files.length} notes → ${dir}`);
|
|
581
|
+
console.log(`Open ${dir} in Obsidian (or any markdown editor) to explore the graph view of your projects, capabilities, and decisions.`);
|
|
582
|
+
}
|
|
@@ -137,18 +137,17 @@ const DEP_TO_TAGS = [
|
|
|
137
137
|
* repos) and by `githubFullName` (so cloning the same repo to two
|
|
138
138
|
* paths doesn't create two project rows).
|
|
139
139
|
*
|
|
140
|
-
* Slug = the
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* gets `-<owner>` appended to disambiguate
|
|
144
|
-
*
|
|
145
|
-
* collisions.
|
|
140
|
+
* Slug = the GitHub repo NAME (normalised), so it's stable across local
|
|
141
|
+
* folder renames and org renames. When two distinct repos would share a
|
|
142
|
+
* slug (e.g. two `flight-controller` repos under different owners), the
|
|
143
|
+
* second+ gets `-<owner>` appended to disambiguate, preventing server-side
|
|
144
|
+
* `uniq_profile_user_slug` collisions.
|
|
146
145
|
*
|
|
147
|
-
* Identity is `githubFullName` on the server side
|
|
148
|
-
* URL-safe display label
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* the
|
|
146
|
+
* Identity is `githubFullName` on the server side (the bulk endpoint
|
|
147
|
+
* upserts by it); slug is the URL-safe display label derived from the
|
|
148
|
+
* repo name. So a local `~/projects/drone/` whose remote is
|
|
149
|
+
* `acme/palisade-website` registers as slug `palisade-website` — matching
|
|
150
|
+
* the repo, not the local folder.
|
|
152
151
|
*/
|
|
153
152
|
export function discoverProjects(roots) {
|
|
154
153
|
const seenPaths = new Set();
|
|
@@ -171,9 +170,16 @@ export function discoverProjects(roots) {
|
|
|
171
170
|
continue;
|
|
172
171
|
seenGithub.add(githubFullName);
|
|
173
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.
|
|
179
|
+
const repoName = githubFullName.split("/").pop() || dirName;
|
|
174
180
|
projects.push({
|
|
175
181
|
localPath: repoPath,
|
|
176
|
-
slug: normaliseSlug(
|
|
182
|
+
slug: normaliseSlug(repoName),
|
|
177
183
|
name,
|
|
178
184
|
githubFullName,
|
|
179
185
|
tags,
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ 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
|
-
import { runCheckNew, runFeed, runHandoff, runProgress, runRun, runSearch, runStarred, runWatch } from "./commands.js";
|
|
6
|
+
import { runCheckNew, runFeed, runHandoff, runProgress, runRun, runSearch, runStarred, runWatch, runAtlas } from "./commands.js";
|
|
7
7
|
const HELP = `replen: Smarter AI Development workflows
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
@@ -21,6 +21,10 @@ Usage:
|
|
|
21
21
|
[--root PATH ...] and register them with Replen. Run after
|
|
22
22
|
cloning a new repo, or pass --root to point
|
|
23
23
|
at a non-conventional layout.
|
|
24
|
+
npx replen atlas Write your knowledge graph as an owned,
|
|
25
|
+
Obsidian-compatible markdown vault to
|
|
26
|
+
~/.replen/atlas/ (projects, capabilities,
|
|
27
|
+
decisions, themes, all cross-linked)
|
|
24
28
|
npx replen logout Forget saved auth
|
|
25
29
|
npx replen --help This help
|
|
26
30
|
|
|
@@ -146,6 +150,8 @@ async function main() {
|
|
|
146
150
|
return runStarred(argv);
|
|
147
151
|
if (cmd === "handoff")
|
|
148
152
|
return runHandoff(argv);
|
|
153
|
+
if (cmd === "atlas")
|
|
154
|
+
return runAtlas(argv);
|
|
149
155
|
if (cmd === undefined) {
|
|
150
156
|
// Default: if already signed in, just rerun mcp setup. Otherwise, full flow.
|
|
151
157
|
const cfg = await readConfig();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replen",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Make your AI coding tools smarter. One command, no API keys. Replen
|
|
3
|
+
"version": "1.0.12",
|
|
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": {
|
|
7
7
|
"replen": "dist/index.js"
|