@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
200 lines (199 loc) • 7.73 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { ProjectManager } from "../../core/projects/project-manager.js";
import chalk from "chalk";
import Table from "cli-table3";
function registerProjectCommands(program) {
const projects = program.command("projects").description("Manage multi-project organization");
projects.command("detect").description("Auto-detect current project").action(async () => {
const manager = ProjectManager.getInstance();
const project = await manager.detectProject();
console.log(chalk.green("\u2713 Project detected:"));
console.log(chalk.cyan(" Name:"), project.name);
console.log(
chalk.cyan(" Organization:"),
project.organization || "none"
);
console.log(chalk.cyan(" Account Type:"), project.accountType);
console.log(chalk.cyan(" Private:"), project.isPrivate ? "Yes" : "No");
console.log(
chalk.cyan(" Language:"),
project.primaryLanguage || "unknown"
);
console.log(chalk.cyan(" Framework:"), project.framework || "unknown");
console.log(chalk.cyan(" ID:"), project.id);
});
projects.command("scan").description("Scan and auto-categorize all Git projects").option("-p, --paths <paths...>", "Custom paths to scan").action(async (options) => {
const manager = ProjectManager.getInstance();
console.log(chalk.yellow("\u{1F50D} Scanning for Git repositories..."));
await manager.scanAndCategorizeAllProjects(options.paths);
const allProjects = manager.getAllProjects();
console.log(chalk.green(`\u2713 Found ${allProjects.length} projects`));
const byType = {};
const byOrg = {};
for (const project of allProjects) {
byType[project.accountType] = (byType[project.accountType] || 0) + 1;
if (project.organization) {
byOrg[project.organization] = (byOrg[project.organization] || 0) + 1;
}
}
console.log("\n\u{1F4CA} Summary by Account Type:");
for (const [type, count] of Object.entries(byType)) {
console.log(` ${type}: ${count} projects`);
}
console.log("\n\u{1F3E2} Top Organizations:");
const topOrgs = Object.entries(byOrg).sort((a, b) => b[1] - a[1]).slice(0, 5);
for (const [org, count] of topOrgs) {
console.log(` ${org}: ${count} projects`);
}
});
projects.command("list").description("List all discovered projects").option(
"-t, --type <type>",
"Filter by account type (personal/work/opensource/client)"
).option("-o, --org <org>", "Filter by organization").option("-l, --language <lang>", "Filter by language").action((options) => {
const manager = ProjectManager.getInstance();
let projectList = manager.getAllProjects();
if (options.type) {
projectList = projectList.filter((p) => p.accountType === options.type);
}
if (options.org) {
projectList = projectList.filter((p) => p.organization === options.org);
}
if (options.language) {
projectList = projectList.filter(
(p) => p.primaryLanguage?.toLowerCase().includes(options.language.toLowerCase())
);
}
if (projectList.length === 0) {
console.log(chalk.yellow("No projects found matching criteria"));
return;
}
const table = new Table({
head: [
"Name",
"Organization",
"Type",
"Language",
"Framework",
"Last Accessed"
],
style: { head: ["cyan"] }
});
for (const project of projectList.slice(0, 20)) {
table.push([
project.name,
project.organization || "-",
project.accountType,
project.primaryLanguage || "-",
project.framework || "-",
new Date(project.lastAccessed).toLocaleDateString()
]);
}
console.log(table.toString());
if (projectList.length > 20) {
console.log(
chalk.gray(`
... and ${projectList.length - 20} more projects`)
);
}
});
projects.command("orgs").description("List all detected organizations").action(() => {
const manager = ProjectManager.getInstance();
const allProjects = manager.getAllProjects();
const orgMap = {};
for (const project of allProjects) {
if (project.organization) {
if (!orgMap[project.organization]) {
orgMap[project.organization] = { count: 0, types: /* @__PURE__ */ new Set() };
}
orgMap[project.organization].count++;
orgMap[project.organization].types.add(project.accountType);
}
}
const table = new Table({
head: ["Organization", "Projects", "Account Types"],
style: { head: ["cyan"] }
});
const sorted = Object.entries(orgMap).sort(
(a, b) => b[1].count - a[1].count
);
for (const [org, data] of sorted) {
table.push([
org,
data.count.toString(),
Array.from(data.types).join(", ")
]);
}
console.log(table.toString());
});
projects.command("config-org <name>").description("Configure an organization").option(
"-t, --type <type>",
"Organization type (company/personal/opensource/client)"
).option(
"-a, --account <account>",
"Account type (personal/work/opensource/client)"
).option("-d, --domain <domain>", "Add domain pattern").action((name, options) => {
const manager = ProjectManager.getInstance();
manager.saveOrganization({
name,
type: options.type || "company",
accountType: options.account || "work",
domains: options.domain ? [options.domain] : [],
githubOrgs: [name],
autoPatterns: []
});
console.log(chalk.green(`\u2713 Organization '${name}' configured`));
});
projects.command("report").description("Generate project statistics report").action(() => {
const manager = ProjectManager.getInstance();
const report = manager.generateReport();
console.log(chalk.cyan("\n\u{1F4CA} Project Statistics:\n"));
console.log(report);
});
projects.command("switch <path>").description("Switch to a different project context").action(async (projectPath) => {
const manager = ProjectManager.getInstance();
const project = await manager.detectProject(projectPath);
console.log(chalk.green(`\u2713 Switched to project: ${project.name}`));
console.log(
chalk.cyan(` Organization: ${project.organization || "none"}`)
);
console.log(chalk.cyan(` Account Type: ${project.accountType}`));
});
projects.command("organize").description("Auto-organize projects into accounts").action(async () => {
const manager = ProjectManager.getInstance();
console.log(chalk.yellow("\u{1F504} Auto-organizing projects..."));
await manager.scanAndCategorizeAllProjects();
const allProjects = manager.getAllProjects();
const organized = {
personal: [],
work: [],
opensource: [],
client: []
};
for (const project of allProjects) {
organized[project.accountType]?.push(project.name);
}
console.log(chalk.green("\n\u2713 Projects organized by account type:\n"));
for (const [type, projects2] of Object.entries(organized)) {
if (projects2.length > 0) {
console.log(
chalk.cyan(`${type.toUpperCase()} (${projects2.length} projects):`)
);
for (const proj of projects2.slice(0, 5)) {
console.log(` - ${proj}`);
}
if (projects2.length > 5) {
console.log(chalk.gray(` ... and ${projects2.length - 5} more`));
}
console.log();
}
}
});
}
export {
registerProjectCommands
};
//# sourceMappingURL=projects.js.map