@phren/cli 0.0.42 → 0.0.44
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/mcp/dist/cli/actions.js +24 -1
- package/mcp/dist/cli/cli.js +6 -1
- package/mcp/dist/cli/hooks-session.js +8 -8
- package/mcp/dist/cli/namespaces.js +3 -4
- package/mcp/dist/cli/team.js +301 -0
- package/mcp/dist/cli-hooks-session-handlers.js +26 -8
- package/mcp/dist/cli-hooks-stop.js +35 -5
- package/mcp/dist/content/dedup.js +5 -2
- package/mcp/dist/entrypoint.js +11 -3
- package/mcp/dist/finding/context.js +3 -2
- package/mcp/dist/init/config.js +1 -1
- package/mcp/dist/init/init-configure.js +1 -1
- package/mcp/dist/init/init-hooks-mode.js +1 -1
- package/mcp/dist/init/init-mcp-mode.js +2 -2
- package/mcp/dist/init/init-walkthrough.js +9 -9
- package/mcp/dist/init/init.js +8 -8
- package/mcp/dist/init/setup.js +46 -1
- package/mcp/dist/init-fresh.js +4 -4
- package/mcp/dist/init-hooks.js +1 -1
- package/mcp/dist/init-modes.js +3 -3
- package/mcp/dist/init-update.js +3 -3
- package/mcp/dist/init-walkthrough.js +9 -9
- package/mcp/dist/link/doctor.js +1 -1
- package/mcp/dist/link/link.js +1 -1
- package/mcp/dist/phren-paths.js +2 -2
- package/mcp/dist/profile-store.js +2 -2
- package/mcp/dist/shared/retrieval.js +9 -3
- package/mcp/dist/status.js +1 -1
- package/mcp/dist/store-registry.js +15 -1
- package/mcp/dist/tools/finding.js +114 -12
- package/mcp/dist/tools/memory.js +49 -4
- package/mcp/dist/tools/search.js +10 -1
- package/mcp/dist/tools/session.js +10 -4
- package/mcp/dist/tools/tasks.js +60 -1
- package/mcp/dist/tools/types.js +10 -0
- package/package.json +1 -1
- package/skills/sync/SKILL.md +1 -1
- package/starter/README.md +6 -6
- package/starter/machines.yaml +1 -1
- package/starter/my-first-project/tasks.md +1 -1
- package/starter/templates/README.md +1 -1
|
@@ -89,7 +89,7 @@ export function configureHooksIfEnabled(phrenPath, hooksEnabled, verb) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
else {
|
|
92
|
-
log(` Hooks are disabled by preference (run:
|
|
92
|
+
log(` Hooks are disabled by preference (run: phren hooks-mode on)`);
|
|
93
93
|
}
|
|
94
94
|
// Install phren CLI wrapper at ~/.local/bin/phren so the bare command works
|
|
95
95
|
const wrapperInstalled = installPhrenCliWrapper(phrenPath);
|
|
@@ -14,7 +14,7 @@ export async function runHooksMode(modeArg) {
|
|
|
14
14
|
if (!normalizedArg || normalizedArg === "status") {
|
|
15
15
|
const current = getHooksEnabledPreference(phrenPath);
|
|
16
16
|
log(`Hooks mode: ${current ? "on (active)" : "off (disabled)"}`);
|
|
17
|
-
log(`Change mode:
|
|
17
|
+
log(`Change mode: phren hooks-mode on|off`);
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
20
|
const mode = parseMcpMode(normalizedArg);
|
|
@@ -15,8 +15,8 @@ export async function runMcpMode(modeArg) {
|
|
|
15
15
|
const hooks = getHooksEnabledPreference(phrenPath);
|
|
16
16
|
log(`MCP mode: ${current ? "on (recommended)" : "off (hooks-only fallback)"}`);
|
|
17
17
|
log(`Hooks mode: ${hooks ? "on (active)" : "off (disabled)"}`);
|
|
18
|
-
log(`Change mode:
|
|
19
|
-
log(`Hooks toggle:
|
|
18
|
+
log(`Change mode: phren mcp-mode on|off`);
|
|
19
|
+
log(`Hooks toggle: phren hooks-mode on|off`);
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
const mode = parseMcpMode(normalizedArg);
|
|
@@ -280,7 +280,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
280
280
|
log(" phren-managed: Phren may mirror CLAUDE.md / AGENTS.md into the repo");
|
|
281
281
|
log(" detached: Phren keeps its own docs but does not write into the repo");
|
|
282
282
|
log(" repo-managed: keep the repo's existing CLAUDE/AGENTS files as canonical");
|
|
283
|
-
log(" Change later:
|
|
283
|
+
log(" Change later: phren config project-ownership <mode>");
|
|
284
284
|
const projectOwnershipDefault = await prompts.select("Default project ownership", [
|
|
285
285
|
{ value: "detached", name: "detached (default)" },
|
|
286
286
|
{ value: "phren-managed", name: "phren-managed" },
|
|
@@ -291,7 +291,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
291
291
|
log("directly: search memory, manage tasks, save findings, etc.");
|
|
292
292
|
log(" Recommended for: Claude Code, Cursor, Copilot CLI, Codex");
|
|
293
293
|
log(" Alternative: hooks-only mode (read-only context injection, any agent)");
|
|
294
|
-
log(" Change later:
|
|
294
|
+
log(" Change later: phren mcp-mode on|off");
|
|
295
295
|
const mcp = (await prompts.confirm("Enable MCP?", true)) ? "on" : "off";
|
|
296
296
|
printSection("Hooks");
|
|
297
297
|
log("Hooks run shell commands at session start, prompt submit, and session end.");
|
|
@@ -299,7 +299,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
299
299
|
log(" - UserPromptSubmit: searches phren and injects relevant context");
|
|
300
300
|
log(" - Stop: commits and pushes any new findings after each response");
|
|
301
301
|
log(" What they touch: ~/.claude/settings.json (hooks section only)");
|
|
302
|
-
log(" Change later:
|
|
302
|
+
log(" Change later: phren hooks-mode on|off");
|
|
303
303
|
const hooks = (await prompts.confirm("Enable hooks?", true)) ? "on" : "off";
|
|
304
304
|
printSection("Semantic Search (Optional)");
|
|
305
305
|
log("Phren can use a local embedding model for semantic (fuzzy) search via Ollama.");
|
|
@@ -347,7 +347,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
347
347
|
let findingsProactivity = "high";
|
|
348
348
|
if (autoCaptureEnabled) {
|
|
349
349
|
log(" Findings capture level controls how eager phren is to save lessons automatically.");
|
|
350
|
-
log(" Change later:
|
|
350
|
+
log(" Change later: phren config proactivity.findings <high|medium|low>");
|
|
351
351
|
findingsProactivity = await prompts.select("Findings capture level", [
|
|
352
352
|
{ value: "high", name: "high (recommended)" },
|
|
353
353
|
{ value: "medium", name: "medium" },
|
|
@@ -363,7 +363,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
363
363
|
log(" suggest: proposes tasks but waits for approval before writing");
|
|
364
364
|
log(" manual: tasks are fully manual — you add them yourself");
|
|
365
365
|
log(" off: never touch tasks automatically");
|
|
366
|
-
log(" Change later:
|
|
366
|
+
log(" Change later: phren config workflow set --taskMode=<mode>");
|
|
367
367
|
const taskMode = await prompts.select("Task mode", [
|
|
368
368
|
{ value: "auto", name: "auto (recommended)" },
|
|
369
369
|
{ value: "suggest", name: "suggest" },
|
|
@@ -376,7 +376,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
376
376
|
log(" high (recommended): captures tasks as they come up naturally");
|
|
377
377
|
log(" medium: only when you explicitly mention a task");
|
|
378
378
|
log(" low: minimal auto-capture");
|
|
379
|
-
log(" Change later:
|
|
379
|
+
log(" Change later: phren config proactivity.tasks <high|medium|low>");
|
|
380
380
|
taskProactivity = await prompts.select("Task proactivity", [
|
|
381
381
|
{ value: "high", name: "high (recommended)" },
|
|
382
382
|
{ value: "medium", name: "medium" },
|
|
@@ -387,7 +387,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
387
387
|
log("Choose how strict review gates should be for risky or low-confidence writes.");
|
|
388
388
|
log(" lowConfidenceThreshold: confidence cutoff used to mark writes as risky");
|
|
389
389
|
log(" riskySections: sections always treated as risky");
|
|
390
|
-
log(" Change later:
|
|
390
|
+
log(" Change later: phren config workflow set --lowConfidenceThreshold=0.7 --riskySections=Stale,Conflicts");
|
|
391
391
|
const thresholdAnswer = await prompts.input("Low-confidence threshold [0.0-1.0]", "0.7");
|
|
392
392
|
const lowConfidenceThreshold = parseLowConfidenceThreshold(thresholdAnswer, 0.7);
|
|
393
393
|
const riskySectionsAnswer = await prompts.input("Risky sections [Review,Stale,Conflicts]", "Stale,Conflicts");
|
|
@@ -437,7 +437,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
437
437
|
log(" conservative — decisions and pitfalls only");
|
|
438
438
|
log(" balanced — non-obvious patterns, decisions, pitfalls, bugs (recommended)");
|
|
439
439
|
log(" aggressive — everything worth remembering, err on the side of capturing");
|
|
440
|
-
log(" Change later:
|
|
440
|
+
log(" Change later: phren config finding-sensitivity <level>");
|
|
441
441
|
const findingSensitivity = await prompts.select("Finding sensitivity", [
|
|
442
442
|
{ value: "balanced", name: "balanced (recommended)" },
|
|
443
443
|
{ value: "conservative", name: "conservative" },
|
|
@@ -466,7 +466,7 @@ export async function runWalkthrough(phrenPath, options) {
|
|
|
466
466
|
bootstrapCurrentProject = await prompts.confirm("Add this project to phren now?", true);
|
|
467
467
|
if (!bootstrapCurrentProject) {
|
|
468
468
|
bootstrapCurrentProject = false;
|
|
469
|
-
log(style.warning(` Skipped. Later: cd ${detectedProject} &&
|
|
469
|
+
log(style.warning(` Skipped. Later: cd ${detectedProject} && phren add`));
|
|
470
470
|
}
|
|
471
471
|
else {
|
|
472
472
|
bootstrapOwnership = await prompts.select("Ownership for detected project", [
|
package/mcp/dist/init/init.js
CHANGED
|
@@ -260,7 +260,7 @@ export async function runInit(opts = {}) {
|
|
|
260
260
|
shouldBootstrapCurrentProject = await prompts.confirm("Add this project to phren now?", true);
|
|
261
261
|
if (!shouldBootstrapCurrentProject) {
|
|
262
262
|
shouldBootstrapCurrentProject = false;
|
|
263
|
-
log(style.warning(` Skipped. Later: cd ${pendingBootstrap.path} &&
|
|
263
|
+
log(style.warning(` Skipped. Later: cd ${pendingBootstrap.path} && phren add`));
|
|
264
264
|
}
|
|
265
265
|
else {
|
|
266
266
|
bootstrapOwnership = await prompts.select("Ownership for detected project", [
|
|
@@ -352,7 +352,7 @@ export async function runInit(opts = {}) {
|
|
|
352
352
|
const previousVersion = prefs.installedVersion;
|
|
353
353
|
if (isVersionNewer(VERSION, previousVersion)) {
|
|
354
354
|
log(`\n Starter template update available: v${previousVersion} -> v${VERSION}`);
|
|
355
|
-
log(` Run \`
|
|
355
|
+
log(` Run \`phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
|
|
356
356
|
}
|
|
357
357
|
if (opts.applyStarterUpdate) {
|
|
358
358
|
const updated = applyStarterTemplateUpdates(phrenPath);
|
|
@@ -395,8 +395,8 @@ export async function runInit(opts = {}) {
|
|
|
395
395
|
log(`\n\x1b[95m◆\x1b[0m phren updated successfully`);
|
|
396
396
|
log(`\nNext steps:`);
|
|
397
397
|
log(` 1. Start a new Claude session in your project directory — phren injects context automatically`);
|
|
398
|
-
log(` 2. Run \`
|
|
399
|
-
log(` 3. Change defaults anytime: \`
|
|
398
|
+
log(` 2. Run \`phren doctor\` to verify everything is wired correctly`);
|
|
399
|
+
log(` 3. Change defaults anytime: \`phren config project-ownership\`, \`phren config workflow\`, \`phren config proactivity.findings\`, \`phren config proactivity.tasks\``);
|
|
400
400
|
log(` 4. After your first week, run phren-discover to surface gaps in your project knowledge`);
|
|
401
401
|
log(` 5. After working across projects, run phren-consolidate to find cross-project patterns`);
|
|
402
402
|
log(``);
|
|
@@ -581,8 +581,8 @@ export async function runInit(opts = {}) {
|
|
|
581
581
|
log(`\nNext steps:`);
|
|
582
582
|
let step = 1;
|
|
583
583
|
log(` ${step++}. Start a new Claude session in your project directory — phren injects context automatically`);
|
|
584
|
-
log(` ${step++}. Run \`
|
|
585
|
-
log(` ${step++}. Change defaults anytime: \`
|
|
584
|
+
log(` ${step++}. Run \`phren doctor\` to verify everything is wired correctly`);
|
|
585
|
+
log(` ${step++}. Change defaults anytime: \`phren config project-ownership\`, \`phren config workflow\`, \`phren config proactivity.findings\`, \`phren config proactivity.tasks\``);
|
|
586
586
|
const gh = opts._walkthroughGithub;
|
|
587
587
|
if (gh) {
|
|
588
588
|
const remote = gh.username
|
|
@@ -607,9 +607,9 @@ export async function runInit(opts = {}) {
|
|
|
607
607
|
log(` git remote add origin git@github.com:YOUR_USERNAME/my-phren.git`);
|
|
608
608
|
log(` git push -u origin main`);
|
|
609
609
|
}
|
|
610
|
-
log(` ${step++}. Add more projects: cd ~/your-project &&
|
|
610
|
+
log(` ${step++}. Add more projects: cd ~/your-project && phren add`);
|
|
611
611
|
if (!mcpEnabled) {
|
|
612
|
-
log(` ${step++}. Turn MCP on:
|
|
612
|
+
log(` ${step++}. Turn MCP on: phren mcp-mode on`);
|
|
613
613
|
}
|
|
614
614
|
log(` ${step++}. After your first week, run phren-discover to surface gaps in your project knowledge`);
|
|
615
615
|
log(` ${step++}. After working across projects, run phren-consolidate to find cross-project patterns`);
|
package/mcp/dist/init/setup.js
CHANGED
|
@@ -278,6 +278,7 @@ export function repairPreexistingInstall(phrenPath) {
|
|
|
278
278
|
const profileRepair = pruneLegacySampleProjectsFromProfiles(phrenPath);
|
|
279
279
|
const preferredHome = resolvePreferredHomeDir(phrenPath);
|
|
280
280
|
const createdSkillArtifacts = ensureGeneratedSkillArtifacts(phrenPath, preferredHome);
|
|
281
|
+
const repairedGlobalSymlink = repairGlobalClaudeSymlink(phrenPath);
|
|
281
282
|
return {
|
|
282
283
|
profileFilesUpdated: profileRepair.filesUpdated,
|
|
283
284
|
removedLegacyProjects: profileRepair.removed,
|
|
@@ -287,8 +288,46 @@ export function repairPreexistingInstall(phrenPath) {
|
|
|
287
288
|
createdRuntimeAssets,
|
|
288
289
|
createdFeatureDefaults,
|
|
289
290
|
createdSkillArtifacts,
|
|
291
|
+
repairedGlobalSymlink,
|
|
290
292
|
};
|
|
291
293
|
}
|
|
294
|
+
/** Re-create ~/.claude/CLAUDE.md symlink if the source exists but the link is missing/broken. */
|
|
295
|
+
function repairGlobalClaudeSymlink(phrenPath) {
|
|
296
|
+
const src = path.join(phrenPath, "global", "CLAUDE.md");
|
|
297
|
+
if (!fs.existsSync(src))
|
|
298
|
+
return false;
|
|
299
|
+
const dest = homePath(".claude", "CLAUDE.md");
|
|
300
|
+
try {
|
|
301
|
+
const stat = fs.lstatSync(dest);
|
|
302
|
+
if (stat.isSymbolicLink()) {
|
|
303
|
+
const target = path.resolve(path.dirname(dest), fs.readlinkSync(dest));
|
|
304
|
+
if (target === path.resolve(src))
|
|
305
|
+
return false; // already correct
|
|
306
|
+
// Stale symlink pointing elsewhere — managed by phren, safe to replace
|
|
307
|
+
if (target.includes(".phren"))
|
|
308
|
+
fs.unlinkSync(dest);
|
|
309
|
+
else
|
|
310
|
+
return false; // not ours, don't touch
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
return false; // regular file exists, don't clobber
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
if (err.code !== "ENOENT")
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
322
|
+
fs.symlinkSync(src, dest);
|
|
323
|
+
debugLog(`repaired global CLAUDE.md symlink: ${dest} -> ${src}`);
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
debugLog(`failed to repair global CLAUDE.md symlink: ${errorMessage(err)}`);
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
292
331
|
function isExpectedVerifyFailure(phrenPath, check) {
|
|
293
332
|
if (check.ok)
|
|
294
333
|
return false;
|
|
@@ -1089,9 +1128,15 @@ export function updateMachinesYaml(phrenPath, machine, profile) {
|
|
|
1089
1128
|
*/
|
|
1090
1129
|
export function detectProjectDir(dir, phrenPath) {
|
|
1091
1130
|
const home = os.homedir();
|
|
1131
|
+
const tmpRoot = path.resolve(os.tmpdir());
|
|
1092
1132
|
const resolvedPhrenPath = path.resolve(phrenPath);
|
|
1093
1133
|
let current = path.resolve(dir);
|
|
1094
1134
|
while (true) {
|
|
1135
|
+
// Never treat the shared OS temp root itself as a project. Tools may drop
|
|
1136
|
+
// global instruction files there, which would otherwise hijack detection
|
|
1137
|
+
// for arbitrary temp subdirectories.
|
|
1138
|
+
if (current === tmpRoot)
|
|
1139
|
+
return null;
|
|
1095
1140
|
if (current === home || current === resolvedPhrenPath)
|
|
1096
1141
|
return null;
|
|
1097
1142
|
if (current.startsWith(resolvedPhrenPath + path.sep))
|
|
@@ -1306,7 +1351,7 @@ export function runPostInitVerify(phrenPath) {
|
|
|
1306
1351
|
ok: true, // always pass — wrapper is optional (global install or npx work too)
|
|
1307
1352
|
detail: cliWrapperOk
|
|
1308
1353
|
? `CLI wrapper exists: ${cliWrapperPath}`
|
|
1309
|
-
: `CLI wrapper not found (optional — use '
|
|
1354
|
+
: `CLI wrapper not found (optional — use 'npx @phren/cli' instead)`,
|
|
1310
1355
|
});
|
|
1311
1356
|
const ok = checks.every((c) => c.ok);
|
|
1312
1357
|
return { ok, checks };
|
package/mcp/dist/init-fresh.js
CHANGED
|
@@ -197,8 +197,8 @@ export async function runFreshInstall(phrenPath, opts, params) {
|
|
|
197
197
|
log(`\nNext steps:`);
|
|
198
198
|
let step = 1;
|
|
199
199
|
log(` ${step++}. Start a new Claude session in your project directory — phren injects context automatically`);
|
|
200
|
-
log(` ${step++}. Run \`
|
|
201
|
-
log(` ${step++}. Change defaults anytime: \`
|
|
200
|
+
log(` ${step++}. Run \`phren doctor\` to verify everything is wired correctly`);
|
|
201
|
+
log(` ${step++}. Change defaults anytime: \`phren config project-ownership\`, \`phren config workflow\`, \`phren config proactivity.findings\`, \`phren config proactivity.tasks\``);
|
|
202
202
|
const gh = opts._walkthroughGithub;
|
|
203
203
|
if (gh) {
|
|
204
204
|
const remote = gh.username
|
|
@@ -223,9 +223,9 @@ export async function runFreshInstall(phrenPath, opts, params) {
|
|
|
223
223
|
log(` git remote add origin git@github.com:YOUR_USERNAME/my-phren.git`);
|
|
224
224
|
log(` git push -u origin main`);
|
|
225
225
|
}
|
|
226
|
-
log(` ${step++}. Add more projects: cd ~/your-project &&
|
|
226
|
+
log(` ${step++}. Add more projects: cd ~/your-project && phren add`);
|
|
227
227
|
if (!mcpEnabled) {
|
|
228
|
-
log(` ${step++}. Turn MCP on:
|
|
228
|
+
log(` ${step++}. Turn MCP on: phren mcp-mode on`);
|
|
229
229
|
}
|
|
230
230
|
log(` ${step++}. After your first week, run phren-discover to surface gaps in your project knowledge`);
|
|
231
231
|
log(` ${step++}. After working across projects, run phren-consolidate to find cross-project patterns`);
|
package/mcp/dist/init-hooks.js
CHANGED
package/mcp/dist/init-modes.js
CHANGED
|
@@ -24,8 +24,8 @@ export async function runMcpMode(modeArg) {
|
|
|
24
24
|
const hooks = getHooksEnabledPreference(phrenPath);
|
|
25
25
|
log(`MCP mode: ${current ? "on (recommended)" : "off (hooks-only fallback)"}`);
|
|
26
26
|
log(`Hooks mode: ${hooks ? "on (active)" : "off (disabled)"}`);
|
|
27
|
-
log(`Change mode:
|
|
28
|
-
log(`Hooks toggle:
|
|
27
|
+
log(`Change mode: phren mcp-mode on|off`);
|
|
28
|
+
log(`Hooks toggle: phren hooks-mode on|off`);
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
const mode = parseMcpMode(normalizedArg);
|
|
@@ -93,7 +93,7 @@ export async function runHooksMode(modeArg) {
|
|
|
93
93
|
if (!normalizedArg || normalizedArg === "status") {
|
|
94
94
|
const current = getHooksEnabledPreference(phrenPath);
|
|
95
95
|
log(`Hooks mode: ${current ? "on (active)" : "off (disabled)"}`);
|
|
96
|
-
log(`Change mode:
|
|
96
|
+
log(`Change mode: phren hooks-mode on|off`);
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
const mode = parseMcpMode(normalizedArg);
|
package/mcp/dist/init-update.js
CHANGED
|
@@ -53,7 +53,7 @@ export async function runExistingInstallUpdate(phrenPath, opts, params) {
|
|
|
53
53
|
const previousVersion = prefs.installedVersion;
|
|
54
54
|
if (isVersionNewer(VERSION, previousVersion)) {
|
|
55
55
|
log(`\n Starter template update available: v${previousVersion} -> v${VERSION}`);
|
|
56
|
-
log(` Run \`
|
|
56
|
+
log(` Run \`phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
|
|
57
57
|
}
|
|
58
58
|
if (opts.applyStarterUpdate) {
|
|
59
59
|
const updated = applyStarterTemplateUpdates(phrenPath);
|
|
@@ -88,8 +88,8 @@ export async function runExistingInstallUpdate(phrenPath, opts, params) {
|
|
|
88
88
|
log(`\n\x1b[95m◆\x1b[0m phren updated successfully`);
|
|
89
89
|
log(`\nNext steps:`);
|
|
90
90
|
log(` 1. Start a new Claude session in your project directory — phren injects context automatically`);
|
|
91
|
-
log(` 2. Run \`
|
|
92
|
-
log(` 3. Change defaults anytime: \`
|
|
91
|
+
log(` 2. Run \`phren doctor\` to verify everything is wired correctly`);
|
|
92
|
+
log(` 3. Change defaults anytime: \`phren config project-ownership\`, \`phren config workflow\`, \`phren config proactivity.findings\`, \`phren config proactivity.tasks\``);
|
|
93
93
|
log(` 4. After your first week, run phren-discover to surface gaps in your project knowledge`);
|
|
94
94
|
log(` 5. After working across projects, run phren-consolidate to find cross-project patterns`);
|
|
95
95
|
log(``);
|
|
@@ -269,7 +269,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
269
269
|
log(" phren-managed: Phren may mirror CLAUDE.md / AGENTS.md into the repo");
|
|
270
270
|
log(" detached: Phren keeps its own docs but does not write into the repo");
|
|
271
271
|
log(" repo-managed: keep the repo's existing CLAUDE/AGENTS files as canonical");
|
|
272
|
-
log(" Change later:
|
|
272
|
+
log(" Change later: phren config project-ownership <mode>");
|
|
273
273
|
const projectOwnershipDefault = await prompts.select("Default project ownership", [
|
|
274
274
|
{ value: "detached", name: "detached (default)" },
|
|
275
275
|
{ value: "phren-managed", name: "phren-managed" },
|
|
@@ -280,7 +280,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
280
280
|
log("directly: search memory, manage tasks, save findings, etc.");
|
|
281
281
|
log(" Recommended for: Claude Code, Cursor, Copilot CLI, Codex");
|
|
282
282
|
log(" Alternative: hooks-only mode (read-only context injection, any agent)");
|
|
283
|
-
log(" Change later:
|
|
283
|
+
log(" Change later: phren mcp-mode on|off");
|
|
284
284
|
const mcp = (await prompts.confirm("Enable MCP?", true)) ? "on" : "off";
|
|
285
285
|
printSection("Hooks");
|
|
286
286
|
log("Hooks run shell commands at session start, prompt submit, and session end.");
|
|
@@ -288,7 +288,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
288
288
|
log(" - UserPromptSubmit: searches phren and injects relevant context");
|
|
289
289
|
log(" - Stop: commits and pushes any new findings after each response");
|
|
290
290
|
log(" What they touch: ~/.claude/settings.json (hooks section only)");
|
|
291
|
-
log(" Change later:
|
|
291
|
+
log(" Change later: phren hooks-mode on|off");
|
|
292
292
|
const hooks = (await prompts.confirm("Enable hooks?", true)) ? "on" : "off";
|
|
293
293
|
printSection("Semantic Search (Optional)");
|
|
294
294
|
log("Phren can use a local embedding model for semantic (fuzzy) search via Ollama.");
|
|
@@ -336,7 +336,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
336
336
|
let findingsProactivity = "high";
|
|
337
337
|
if (autoCaptureEnabled) {
|
|
338
338
|
log(" Findings capture level controls how eager phren is to save lessons automatically.");
|
|
339
|
-
log(" Change later:
|
|
339
|
+
log(" Change later: phren config proactivity.findings <high|medium|low>");
|
|
340
340
|
findingsProactivity = await prompts.select("Findings capture level", [
|
|
341
341
|
{ value: "high", name: "high (recommended)" },
|
|
342
342
|
{ value: "medium", name: "medium" },
|
|
@@ -352,7 +352,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
352
352
|
log(" suggest: proposes tasks but waits for approval before writing");
|
|
353
353
|
log(" manual: tasks are fully manual — you add them yourself");
|
|
354
354
|
log(" off: never touch tasks automatically");
|
|
355
|
-
log(" Change later:
|
|
355
|
+
log(" Change later: phren config workflow set --taskMode=<mode>");
|
|
356
356
|
const taskMode = await prompts.select("Task mode", [
|
|
357
357
|
{ value: "auto", name: "auto (recommended)" },
|
|
358
358
|
{ value: "suggest", name: "suggest" },
|
|
@@ -365,7 +365,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
365
365
|
log(" high (recommended): captures tasks as they come up naturally");
|
|
366
366
|
log(" medium: only when you explicitly mention a task");
|
|
367
367
|
log(" low: minimal auto-capture");
|
|
368
|
-
log(" Change later:
|
|
368
|
+
log(" Change later: phren config proactivity.tasks <high|medium|low>");
|
|
369
369
|
taskProactivity = await prompts.select("Task proactivity", [
|
|
370
370
|
{ value: "high", name: "high (recommended)" },
|
|
371
371
|
{ value: "medium", name: "medium" },
|
|
@@ -376,7 +376,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
376
376
|
log("Choose how strict review gates should be for risky or low-confidence writes.");
|
|
377
377
|
log(" lowConfidenceThreshold: confidence cutoff used to mark writes as risky");
|
|
378
378
|
log(" riskySections: sections always treated as risky");
|
|
379
|
-
log(" Change later:
|
|
379
|
+
log(" Change later: phren config workflow set --lowConfidenceThreshold=0.7 --riskySections=Stale,Conflicts");
|
|
380
380
|
const thresholdAnswer = await prompts.input("Low-confidence threshold [0.0-1.0]", "0.7");
|
|
381
381
|
const lowConfidenceThreshold = parseLowConfidenceThreshold(thresholdAnswer, 0.7);
|
|
382
382
|
const riskySectionsAnswer = await prompts.input("Risky sections [Review,Stale,Conflicts]", "Stale,Conflicts");
|
|
@@ -426,7 +426,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
426
426
|
log(" conservative — decisions and pitfalls only");
|
|
427
427
|
log(" balanced — non-obvious patterns, decisions, pitfalls, bugs (recommended)");
|
|
428
428
|
log(" aggressive — everything worth remembering, err on the side of capturing");
|
|
429
|
-
log(" Change later:
|
|
429
|
+
log(" Change later: phren config finding-sensitivity <level>");
|
|
430
430
|
const findingSensitivity = await prompts.select("Finding sensitivity", [
|
|
431
431
|
{ value: "balanced", name: "balanced (recommended)" },
|
|
432
432
|
{ value: "conservative", name: "conservative" },
|
|
@@ -455,7 +455,7 @@ export async function runWalkthrough(phrenPath) {
|
|
|
455
455
|
bootstrapCurrentProject = await prompts.confirm("Add this project to phren now?", true);
|
|
456
456
|
if (!bootstrapCurrentProject) {
|
|
457
457
|
bootstrapCurrentProject = false;
|
|
458
|
-
log(style.warning(` Skipped. Later: cd ${detectedProject} &&
|
|
458
|
+
log(style.warning(` Skipped. Later: cd ${detectedProject} && phren add`));
|
|
459
459
|
}
|
|
460
460
|
else {
|
|
461
461
|
bootstrapOwnership = await prompts.select("Ownership for detected project", [
|
package/mcp/dist/link/doctor.js
CHANGED
|
@@ -409,7 +409,7 @@ export async function runDoctor(phrenPath, fix = false, checkData = false) {
|
|
|
409
409
|
ok: phrenCliActive,
|
|
410
410
|
detail: phrenCliActive
|
|
411
411
|
? "phren CLI wrapper active via ~/.local/bin/phren"
|
|
412
|
-
: "phren CLI wrapper missing — run init to install
|
|
412
|
+
: "phren CLI wrapper missing — run 'npx @phren/cli init' to install",
|
|
413
413
|
});
|
|
414
414
|
if (fix) {
|
|
415
415
|
const repaired = repairPreexistingInstall(phrenPath);
|
package/mcp/dist/link/link.js
CHANGED
|
@@ -76,7 +76,7 @@ function maybeOfferStarterTemplateUpdate(phrenPath) {
|
|
|
76
76
|
const prefs = JSON.parse(fs.readFileSync(prefsPath, "utf8"));
|
|
77
77
|
if (isVersionNewer(current, prefs.installedVersion)) {
|
|
78
78
|
log(` Starter template update available: v${prefs.installedVersion} -> v${current}`);
|
|
79
|
-
log(` Run \`
|
|
79
|
+
log(` Run \`phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
catch (err) {
|
package/mcp/dist/phren-paths.js
CHANGED
|
@@ -209,7 +209,7 @@ export function findPhrenPathWithArg(arg) {
|
|
|
209
209
|
const existing = findPhrenPath();
|
|
210
210
|
if (existing)
|
|
211
211
|
return existing;
|
|
212
|
-
throw new Error(`${PhrenError.NOT_FOUND}: phren root not found. Run '
|
|
212
|
+
throw new Error(`${PhrenError.NOT_FOUND}: phren root not found. Run 'phren init'.`);
|
|
213
213
|
}
|
|
214
214
|
export function isProjectLocalMode(phrenPath) {
|
|
215
215
|
try {
|
|
@@ -513,7 +513,7 @@ export function getPhrenPath() {
|
|
|
513
513
|
if (!lazyPhrenPath) {
|
|
514
514
|
const existing = findPhrenPath();
|
|
515
515
|
if (!existing)
|
|
516
|
-
throw new Error(`${PhrenError.NOT_FOUND}: phren root not found. Run '
|
|
516
|
+
throw new Error(`${PhrenError.NOT_FOUND}: phren root not found. Run 'phren init'.`);
|
|
517
517
|
lazyPhrenPath = existing;
|
|
518
518
|
}
|
|
519
519
|
return lazyPhrenPath;
|
|
@@ -45,7 +45,7 @@ export function resolveActiveProfile(phrenPath, requestedProfile) {
|
|
|
45
45
|
export function listMachines(phrenPath) {
|
|
46
46
|
const machinesPath = path.join(phrenPath, "machines.yaml");
|
|
47
47
|
if (!fs.existsSync(machinesPath))
|
|
48
|
-
return phrenErr(`machines.yaml not found. Run '
|
|
48
|
+
return phrenErr(`machines.yaml not found. Run 'phren init' to set up your phren.`, PhrenError.FILE_NOT_FOUND);
|
|
49
49
|
try {
|
|
50
50
|
const raw = fs.readFileSync(machinesPath, "utf8");
|
|
51
51
|
const parsed = yaml.load(raw, { schema: yaml.CORE_SCHEMA });
|
|
@@ -205,7 +205,7 @@ export function getActiveProfileDefaults(phrenPath, profile) {
|
|
|
205
205
|
export function listProfiles(phrenPath) {
|
|
206
206
|
const profilesDir = path.join(phrenPath, "profiles");
|
|
207
207
|
if (!fs.existsSync(profilesDir))
|
|
208
|
-
return phrenErr(`No profiles/ directory found. Run '
|
|
208
|
+
return phrenErr(`No profiles/ directory found. Run 'phren init' to set up your phren.`, PhrenError.FILE_NOT_FOUND);
|
|
209
209
|
const files = fs.readdirSync(profilesDir).filter((file) => file.endsWith(".yaml")).sort();
|
|
210
210
|
const profiles = [];
|
|
211
211
|
for (const file of files) {
|
|
@@ -627,9 +627,15 @@ export function rankResults(rows, intent, gitCtx, detectedProject, phrenPathLoca
|
|
|
627
627
|
}
|
|
628
628
|
return false;
|
|
629
629
|
});
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
630
|
+
// Always-inject: truths for detected project are prepended regardless of search
|
|
631
|
+
// relevance. Dedup against rows already in the result set.
|
|
632
|
+
const canonicalRows = queryDocRows(db, "SELECT project, filename, type, content, path FROM docs WHERE project = ? AND type = 'canonical'", [detectedProject]);
|
|
633
|
+
if (canonicalRows) {
|
|
634
|
+
const existingPaths = new Set(ranked.map((r) => r.path));
|
|
635
|
+
const newCanonicals = canonicalRows.filter((r) => !existingPaths.has(r.path));
|
|
636
|
+
if (newCanonicals.length > 0)
|
|
637
|
+
ranked = [...newCanonicals, ...ranked];
|
|
638
|
+
}
|
|
633
639
|
}
|
|
634
640
|
const entityBoost = query ? getFragmentBoostDocs(db, query) : new Set();
|
|
635
641
|
const entityBoostPaths = new Set();
|
package/mcp/dist/status.js
CHANGED
|
@@ -53,7 +53,7 @@ function hasCommandHook(value) {
|
|
|
53
53
|
export async function runStatus() {
|
|
54
54
|
const phrenPath = findPhrenPath();
|
|
55
55
|
if (!phrenPath) {
|
|
56
|
-
console.log(`${RED}phren not found${RESET}. Run ${CYAN}npx phren init${RESET} to set up.`);
|
|
56
|
+
console.log(`${RED}phren not found${RESET}. Run ${CYAN}npx @phren/cli init${RESET} to set up.`);
|
|
57
57
|
process.exit(1);
|
|
58
58
|
}
|
|
59
59
|
const cwd = process.cwd();
|
|
@@ -165,6 +165,19 @@ export function removeStoreFromRegistry(phrenPath, name) {
|
|
|
165
165
|
return entry;
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
|
+
/** Update the projects[] claim list for a store. Uses file locking. */
|
|
169
|
+
export function updateStoreProjects(phrenPath, storeName, projects) {
|
|
170
|
+
withFileLock(storesFilePath(phrenPath), () => {
|
|
171
|
+
const registry = readStoreRegistry(phrenPath);
|
|
172
|
+
if (!registry)
|
|
173
|
+
throw new Error(`${PhrenError.FILE_NOT_FOUND}: No stores.yaml found`);
|
|
174
|
+
const store = registry.stores.find((s) => s.name === storeName);
|
|
175
|
+
if (!store)
|
|
176
|
+
throw new Error(`${PhrenError.NOT_FOUND}: Store "${storeName}" not found`);
|
|
177
|
+
store.projects = projects.length > 0 ? projects : undefined;
|
|
178
|
+
writeStoreRegistry(phrenPath, registry);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
168
181
|
// ── Validation ───────────────────────────────────────────────────────────────
|
|
169
182
|
function validateRegistry(registry) {
|
|
170
183
|
if (registry.version !== 1)
|
|
@@ -247,8 +260,9 @@ function parseFederationPathsEnv(localPhrenPath) {
|
|
|
247
260
|
const raw = process.env.PHREN_FEDERATION_PATHS ?? "";
|
|
248
261
|
if (!raw.trim())
|
|
249
262
|
return [];
|
|
263
|
+
// Use path.delimiter (';' on Windows, ':' on Unix) so Windows drive letters aren't split
|
|
250
264
|
return raw
|
|
251
|
-
.split(
|
|
265
|
+
.split(path.delimiter)
|
|
252
266
|
.map((p) => p.trim())
|
|
253
267
|
.filter((p) => p.length > 0)
|
|
254
268
|
.map((p) => path.resolve(expandHomePath(p)))
|