@phren/cli
Version:
Knowledge layer for AI agents - CLI, MCP server, and data layer
168 lines (167 loc) • 6.95 kB
JavaScript
import { getPhrenPath } from "../shared.js";
import { installPreferencesFile } from "../phren-paths.js";
import { mergeConfig } from "../shared/governance.js";
import { governanceInstallPreferencesFile, readInstallPreferences, readGovernanceInstallPreferences, writeInstallPreferences, writeGovernanceInstallPreferences, } from "../init/preferences.js";
import { PROACTIVITY_LEVELS, getProactivityLevel, getProactivityLevelForTask, getProactivityLevelForFindings, } from "../proactivity.js";
import { PROJECT_OWNERSHIP_MODES, getProjectOwnershipDefault, parseProjectOwnershipMode, updateProjectConfigOverrides, } from "../project-config.js";
import { isValidProjectName } from "../utils.js";
import { parseProjectArg, warnIfUnregistered } from "./config-shared.js";
export function buildProactivitySnapshot(phrenPath) {
const prefs = readGovernanceInstallPreferences(phrenPath);
return {
path: governanceInstallPreferencesFile(phrenPath),
configured: {
proactivity: prefs.proactivity ?? null,
proactivityFindings: prefs.proactivityFindings ?? null,
proactivityTask: prefs.proactivityTask ?? null,
},
effective: {
proactivity: getProactivityLevel(phrenPath),
proactivityFindings: getProactivityLevelForFindings(phrenPath),
proactivityTask: getProactivityLevelForTask(phrenPath),
},
};
}
function normalizeProactivityLevel(raw) {
if (!raw)
return undefined;
const normalized = raw.trim().toLowerCase();
return PROACTIVITY_LEVELS.includes(normalized)
? normalized
: undefined;
}
function printProactivityUsage(subcommand) {
console.error(subcommand === "proactivity"
? "Usage: phren config proactivity [high|medium|low] [--scope base|findings|tasks] [--project <name>]"
: `Usage: phren config ${subcommand} [high|medium|low] [--project <name>]`);
}
const SCOPE_SUBCOMMANDS = {
base: "proactivity", findings: "proactivity.findings", tasks: "proactivity.tasks", task: "proactivity.tasks",
};
/** `--scope tasks` on the base command is the same as `proactivity.tasks`. */
function parseScopeArg(args) {
const rest = [];
let scope, invalid;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
const inline = arg.startsWith("--scope=") ? arg.slice("--scope=".length) : undefined;
if (arg === "--scope" || inline !== undefined) {
const raw = inline ?? args[++i] ?? "";
const match = SCOPE_SUBCOMMANDS[raw.trim().toLowerCase()];
if (match)
scope = match;
else
invalid = raw;
continue;
}
rest.push(arg);
}
return { scope, rest, invalid };
}
export function handleConfigProactivity(requested, args) {
const phrenPath = getPhrenPath();
const scoped = parseScopeArg(args);
if (scoped.invalid !== undefined || (scoped.scope && requested !== "proactivity")) {
printProactivityUsage(requested);
process.exitCode = 1;
return;
}
const subcommand = scoped.scope ?? requested;
const { project: projectArg, rest: filteredArgs } = parseProjectArg(scoped.rest);
const value = filteredArgs[0];
if (value === undefined) {
if (projectArg) {
if (!isValidProjectName(projectArg)) {
console.error(`Invalid project name: "${projectArg}"`);
process.exitCode = 1;
return;
}
const resolved = mergeConfig(phrenPath, projectArg);
console.log(JSON.stringify({
_project: projectArg,
base: resolved.proactivity.base ?? null,
findings: resolved.proactivity.findings ?? null,
tasks: resolved.proactivity.tasks ?? null,
}, null, 2));
return;
}
console.log(JSON.stringify(buildProactivitySnapshot(phrenPath), null, 2));
return;
}
if (filteredArgs.length !== 1) {
printProactivityUsage(subcommand);
process.exitCode = 1;
return;
}
const level = normalizeProactivityLevel(value);
if (!level) {
printProactivityUsage(subcommand);
process.exitCode = 1;
return;
}
if (projectArg) {
if (!isValidProjectName(projectArg)) {
console.error(`Invalid project name: "${projectArg}"`);
process.exitCode = 1;
return;
}
warnIfUnregistered(phrenPath, projectArg);
const key = subcommand === "proactivity" ? "proactivity"
: subcommand === "proactivity.findings" ? "proactivityFindings"
: "proactivityTask";
updateProjectConfigOverrides(phrenPath, projectArg, (current) => ({ ...current, [key]: level }));
const resolved = mergeConfig(phrenPath, projectArg);
console.log(JSON.stringify({
_project: projectArg,
base: resolved.proactivity.base ?? null,
findings: resolved.proactivity.findings ?? null,
tasks: resolved.proactivity.tasks ?? null,
}, null, 2));
return;
}
switch (subcommand) {
case "proactivity":
writeGovernanceInstallPreferences(phrenPath, { proactivity: level });
break;
case "proactivity.findings":
writeGovernanceInstallPreferences(phrenPath, { proactivityFindings: level });
break;
case "proactivity.tasks":
writeGovernanceInstallPreferences(phrenPath, { proactivityTask: level });
break;
}
console.log(JSON.stringify(buildProactivitySnapshot(phrenPath), null, 2));
}
function projectOwnershipConfigSnapshot(phrenPath) {
const prefs = readInstallPreferences(phrenPath);
return {
path: installPreferencesFile(phrenPath),
configured: {
projectOwnershipDefault: prefs.projectOwnershipDefault ?? null,
},
effective: {
projectOwnershipDefault: getProjectOwnershipDefault(phrenPath),
},
};
}
export function handleConfigProjectOwnership(args) {
const phrenPath = getPhrenPath();
const value = args[0];
if (value === undefined) {
console.log(JSON.stringify(projectOwnershipConfigSnapshot(phrenPath), null, 2));
return;
}
if (args.length !== 1) {
console.error(`Usage: phren config project-ownership [${PROJECT_OWNERSHIP_MODES.join("|")}]`);
process.exitCode = 1;
return;
}
const ownership = parseProjectOwnershipMode(value);
if (!ownership) {
console.error(`Usage: phren config project-ownership [${PROJECT_OWNERSHIP_MODES.join("|")}]`);
process.exitCode = 1;
return;
}
writeInstallPreferences(phrenPath, { projectOwnershipDefault: ownership });
console.log(JSON.stringify(projectOwnershipConfigSnapshot(phrenPath), null, 2));
}