gnosys 5.2.6 → 5.2.8
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/cli.js +274 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +41 -29
- package/dist/index.js.map +1 -1
- package/dist/lib/portfolio.d.ts +93 -0
- package/dist/lib/portfolio.d.ts.map +1 -0
- package/dist/lib/portfolio.js +618 -0
- package/dist/lib/portfolio.js.map +1 -0
- package/dist/lib/portfolioHtml.d.ts +9 -0
- package/dist/lib/portfolioHtml.d.ts.map +1 -0
- package/dist/lib/portfolioHtml.js +537 -0
- package/dist/lib/portfolioHtml.js.map +1 -0
- package/dist/postinstall.d.ts +2 -1
- package/dist/postinstall.d.ts.map +1 -1
- package/dist/postinstall.js +61 -9
- package/dist/postinstall.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2465,8 +2465,9 @@ program
|
|
|
2465
2465
|
// ─── gnosys upgrade ─────────────────────────────────────────────────────
|
|
2466
2466
|
program
|
|
2467
2467
|
.command("upgrade")
|
|
2468
|
-
.description("Re-initialize all registered projects after a Gnosys version upgrade. Updates agent rules, project registry,
|
|
2469
|
-
.
|
|
2468
|
+
.description("Re-initialize all registered projects after a Gnosys version upgrade. Updates agent rules, project registry, stamps the central DB, and regenerates the portfolio dashboard.")
|
|
2469
|
+
.option("--skip-dashboard", "Skip regenerating the portfolio dashboard")
|
|
2470
|
+
.action(async (opts) => {
|
|
2470
2471
|
const currentVersion = pkg.version;
|
|
2471
2472
|
console.log(`Gnosys v${currentVersion} — upgrading registered projects...\n`);
|
|
2472
2473
|
// 1. Read registered projects from file registry AND central DB
|
|
@@ -2659,6 +2660,28 @@ program
|
|
|
2659
2660
|
console.log(`\nNote: ${skipped.length} project(s) not found on this machine.`);
|
|
2660
2661
|
console.log(`If they exist on another machine, run 'gnosys upgrade' there too.`);
|
|
2661
2662
|
}
|
|
2663
|
+
// 6. Regenerate portfolio dashboard
|
|
2664
|
+
if (!opts.skipDashboard) {
|
|
2665
|
+
try {
|
|
2666
|
+
const dashboardPath = path.join(home, "gnosys-dashboard.html");
|
|
2667
|
+
const dashboardMdPath = path.join(home, "gnosys-dashboard.md");
|
|
2668
|
+
const centralDb = GnosysDB.openCentral();
|
|
2669
|
+
if (centralDb.isAvailable()) {
|
|
2670
|
+
const { generatePortfolio, formatPortfolioMarkdown } = await import("./lib/portfolio.js");
|
|
2671
|
+
const { generatePortfolioHtml } = await import("./lib/portfolioHtml.js");
|
|
2672
|
+
const report = generatePortfolio(centralDb);
|
|
2673
|
+
await fs.writeFile(dashboardPath, generatePortfolioHtml(report, dashboardPath), "utf-8");
|
|
2674
|
+
await fs.writeFile(dashboardMdPath, formatPortfolioMarkdown(report), "utf-8");
|
|
2675
|
+
centralDb.close();
|
|
2676
|
+
console.log(`\nPortfolio dashboard regenerated:`);
|
|
2677
|
+
console.log(` HTML: ${dashboardPath}`);
|
|
2678
|
+
console.log(` MD: ${dashboardMdPath}`);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
catch {
|
|
2682
|
+
console.log(`\n Could not regenerate portfolio dashboard`);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2662
2685
|
});
|
|
2663
2686
|
// ─── gnosys doctor ──────────────────────────────────────────────────────
|
|
2664
2687
|
program
|
|
@@ -3765,6 +3788,255 @@ program
|
|
|
3765
3788
|
centralDb?.close();
|
|
3766
3789
|
}
|
|
3767
3790
|
});
|
|
3791
|
+
// ─── gnosys portfolio ───────────────────────────────────────────────────
|
|
3792
|
+
program
|
|
3793
|
+
.command("portfolio")
|
|
3794
|
+
.description("Portfolio dashboard — all projects with status, roadmap, and recent activity")
|
|
3795
|
+
.option("-o, --output <file>", "Write dashboard to a file (auto-detects format from extension)")
|
|
3796
|
+
.option("--html", "Output as HTML dashboard")
|
|
3797
|
+
.option("--json", "Output as JSON")
|
|
3798
|
+
.action(async (opts) => {
|
|
3799
|
+
let centralDb = null;
|
|
3800
|
+
try {
|
|
3801
|
+
centralDb = GnosysDB.openCentral();
|
|
3802
|
+
if (!centralDb.isAvailable()) {
|
|
3803
|
+
console.error("Central DB not available.");
|
|
3804
|
+
process.exit(1);
|
|
3805
|
+
}
|
|
3806
|
+
const { generatePortfolio, formatPortfolioMarkdown } = await import("./lib/portfolio.js");
|
|
3807
|
+
const report = generatePortfolio(centralDb);
|
|
3808
|
+
// Detect format from output extension if not explicitly set
|
|
3809
|
+
const useHtml = opts.html || (opts.output?.endsWith(".html") ?? false);
|
|
3810
|
+
const useJson = opts.json || (opts.output?.endsWith(".json") ?? false);
|
|
3811
|
+
if (useJson) {
|
|
3812
|
+
const json = JSON.stringify(report, null, 2);
|
|
3813
|
+
if (opts.output) {
|
|
3814
|
+
const { writeFileSync } = await import("fs");
|
|
3815
|
+
writeFileSync(opts.output, json, "utf-8");
|
|
3816
|
+
console.log(`Portfolio written to ${opts.output}`);
|
|
3817
|
+
}
|
|
3818
|
+
else {
|
|
3819
|
+
console.log(json);
|
|
3820
|
+
}
|
|
3821
|
+
return;
|
|
3822
|
+
}
|
|
3823
|
+
if (useHtml) {
|
|
3824
|
+
const { generatePortfolioHtml } = await import("./lib/portfolioHtml.js");
|
|
3825
|
+
const html = generatePortfolioHtml(report, opts.output);
|
|
3826
|
+
if (opts.output) {
|
|
3827
|
+
const { writeFileSync } = await import("fs");
|
|
3828
|
+
writeFileSync(opts.output, html, "utf-8");
|
|
3829
|
+
console.log(`Portfolio dashboard written to ${opts.output}`);
|
|
3830
|
+
}
|
|
3831
|
+
else {
|
|
3832
|
+
console.log(html);
|
|
3833
|
+
}
|
|
3834
|
+
return;
|
|
3835
|
+
}
|
|
3836
|
+
const markdown = formatPortfolioMarkdown(report);
|
|
3837
|
+
if (opts.output) {
|
|
3838
|
+
const { writeFileSync } = await import("fs");
|
|
3839
|
+
writeFileSync(opts.output, markdown, "utf-8");
|
|
3840
|
+
console.log(`Portfolio dashboard written to ${opts.output}`);
|
|
3841
|
+
}
|
|
3842
|
+
else {
|
|
3843
|
+
console.log(markdown);
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
catch (err) {
|
|
3847
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
3848
|
+
process.exit(1);
|
|
3849
|
+
}
|
|
3850
|
+
finally {
|
|
3851
|
+
centralDb?.close();
|
|
3852
|
+
}
|
|
3853
|
+
});
|
|
3854
|
+
// ─── gnosys status ──────────────────────────────────────────────────────
|
|
3855
|
+
program
|
|
3856
|
+
.command("status")
|
|
3857
|
+
.description("Show project status. From a project dir: shows that project. With --global: shows all projects. With --web: opens the HTML dashboard.")
|
|
3858
|
+
.option("-d, --directory <dir>", "Project directory (auto-detects if omitted)")
|
|
3859
|
+
.option("-p, --project <id>", "Project ID")
|
|
3860
|
+
.option("-g, --global", "Show all projects")
|
|
3861
|
+
.option("-w, --web", "Open the HTML dashboard in the browser")
|
|
3862
|
+
.option("--json", "Output as JSON")
|
|
3863
|
+
.action(async (opts) => {
|
|
3864
|
+
let centralDb = null;
|
|
3865
|
+
try {
|
|
3866
|
+
centralDb = GnosysDB.openCentral();
|
|
3867
|
+
if (!centralDb.isAvailable()) {
|
|
3868
|
+
console.error("Central DB not available.");
|
|
3869
|
+
process.exit(1);
|
|
3870
|
+
}
|
|
3871
|
+
const { detectCurrentProject } = await import("./lib/federated.js");
|
|
3872
|
+
const { generatePortfolio, formatPortfolioMarkdown } = await import("./lib/portfolio.js");
|
|
3873
|
+
const report = generatePortfolio(centralDb);
|
|
3874
|
+
// --web: regenerate HTML dashboard and open it
|
|
3875
|
+
if (opts.web) {
|
|
3876
|
+
const { generatePortfolioHtml } = await import("./lib/portfolioHtml.js");
|
|
3877
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
3878
|
+
const dashboardPath = path.join(home, "gnosys-dashboard.html");
|
|
3879
|
+
const { writeFileSync } = await import("fs");
|
|
3880
|
+
writeFileSync(dashboardPath, generatePortfolioHtml(report, dashboardPath), "utf-8");
|
|
3881
|
+
const { exec } = await import("child_process");
|
|
3882
|
+
exec(`open "${dashboardPath}"`);
|
|
3883
|
+
console.log(`Dashboard opened: ${dashboardPath}`);
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3886
|
+
// --global: show all projects
|
|
3887
|
+
if (opts.global) {
|
|
3888
|
+
if (opts.json) {
|
|
3889
|
+
console.log(JSON.stringify(report, null, 2));
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
console.log(`\n Portfolio — ${report.totalProjects} projects, ${report.totalMemories} memories\n`);
|
|
3893
|
+
// Action items summary
|
|
3894
|
+
if (report.allActionItems.length > 0) {
|
|
3895
|
+
console.log(` \x1b[31mACTION ITEMS (${report.allActionItems.length}):\x1b[0m`);
|
|
3896
|
+
for (const a of report.allActionItems.slice(0, 8)) {
|
|
3897
|
+
const icon = a.type === "question" ? "?" : a.type === "blocker" ? "!" : a.type === "manual" ? ">" : "*";
|
|
3898
|
+
console.log(` [${icon}] ${a.projectName}: ${a.text.slice(0, 80)}`);
|
|
3899
|
+
}
|
|
3900
|
+
if (report.allActionItems.length > 8)
|
|
3901
|
+
console.log(` ... and ${report.allActionItems.length - 8} more`);
|
|
3902
|
+
console.log("");
|
|
3903
|
+
}
|
|
3904
|
+
// Per-project summary
|
|
3905
|
+
for (const snap of report.projects) {
|
|
3906
|
+
const r = snap.readiness;
|
|
3907
|
+
const color = r.score >= 90 ? "\x1b[32m" : r.score >= 65 ? "\x1b[34m" : r.score >= 40 ? "\x1b[33m" : "\x1b[31m";
|
|
3908
|
+
const reset = "\x1b[0m";
|
|
3909
|
+
const blockers = snap.actionItems.length + r.blocking.length;
|
|
3910
|
+
const blockerStr = blockers > 0 ? ` — \x1b[31m${blockers} blocker${blockers !== 1 ? "s" : ""}\x1b[0m` : "";
|
|
3911
|
+
console.log(` ${color}${String(r.score).padStart(3)}%${reset} ${r.label.padEnd(12)} ${snap.project.name}${blockerStr}`);
|
|
3912
|
+
}
|
|
3913
|
+
console.log(`\n Run 'gnosys status --web' to open the visual dashboard.`);
|
|
3914
|
+
return;
|
|
3915
|
+
}
|
|
3916
|
+
// Single project (default): auto-detect from cwd
|
|
3917
|
+
let pid = opts.project || null;
|
|
3918
|
+
if (!pid)
|
|
3919
|
+
pid = await detectCurrentProject(centralDb, opts.directory || undefined);
|
|
3920
|
+
if (!pid) {
|
|
3921
|
+
console.error("No project detected. Run from a project directory, use --project, or use --global for all.");
|
|
3922
|
+
process.exit(1);
|
|
3923
|
+
}
|
|
3924
|
+
const project = centralDb.getProject(pid);
|
|
3925
|
+
if (!project) {
|
|
3926
|
+
console.error(`Project not found: ${pid}`);
|
|
3927
|
+
process.exit(1);
|
|
3928
|
+
}
|
|
3929
|
+
const snap = report.projects.find((s) => s.project.id === pid);
|
|
3930
|
+
if (!snap) {
|
|
3931
|
+
console.error(`No memories found for project: ${project.name}`);
|
|
3932
|
+
console.log(`\nRun 'gnosys update-status' to create a status snapshot.`);
|
|
3933
|
+
process.exit(1);
|
|
3934
|
+
}
|
|
3935
|
+
if (opts.json) {
|
|
3936
|
+
console.log(JSON.stringify({
|
|
3937
|
+
project: project.name,
|
|
3938
|
+
readiness: snap.readiness,
|
|
3939
|
+
actionItems: snap.actionItems,
|
|
3940
|
+
memoryCounts: snap.memoryCounts,
|
|
3941
|
+
latestStatus: snap.latestStatus ? { id: snap.latestStatus.id, title: snap.latestStatus.title, modified: snap.latestStatus.modified } : null,
|
|
3942
|
+
}, null, 2));
|
|
3943
|
+
return;
|
|
3944
|
+
}
|
|
3945
|
+
// Formatted output
|
|
3946
|
+
const r = snap.readiness;
|
|
3947
|
+
const color = r.score >= 90 ? "\x1b[32m" : r.score >= 65 ? "\x1b[34m" : r.score >= 40 ? "\x1b[33m" : "\x1b[31m";
|
|
3948
|
+
const reset = "\x1b[0m";
|
|
3949
|
+
console.log(`\n ${project.name} — ${color}${r.label} (${r.score}%)${reset}`);
|
|
3950
|
+
console.log(` ${snap.memoryCounts.total} memories across ${Object.keys(snap.memoryCounts.byCategory).length} categories\n`);
|
|
3951
|
+
if (snap.latestStatus) {
|
|
3952
|
+
const age = Math.floor((Date.now() - new Date(snap.latestStatus.modified).getTime()) / (1000 * 60 * 60 * 24));
|
|
3953
|
+
const stale = age > 7 ? ` \x1b[33m(${age}d old — consider running 'gnosys update-status')\x1b[0m` : ` (${age}d ago)`;
|
|
3954
|
+
console.log(` Last status: ${snap.latestStatus.title}${stale}\n`);
|
|
3955
|
+
}
|
|
3956
|
+
else {
|
|
3957
|
+
console.log(` \x1b[33mNo status snapshot found. Run 'gnosys update-status' to create one.\x1b[0m\n`);
|
|
3958
|
+
}
|
|
3959
|
+
// Action items
|
|
3960
|
+
if (snap.actionItems.length > 0) {
|
|
3961
|
+
console.log(` ACTION ITEMS (${snap.actionItems.length}):`);
|
|
3962
|
+
for (const a of snap.actionItems) {
|
|
3963
|
+
const icon = a.type === "question" ? "?" : a.type === "blocker" ? "!" : a.type === "manual" ? ">" : "*";
|
|
3964
|
+
console.log(` [${icon}] ${a.text}`);
|
|
3965
|
+
}
|
|
3966
|
+
console.log("");
|
|
3967
|
+
}
|
|
3968
|
+
// Blocking
|
|
3969
|
+
if (r.blocking.length > 0) {
|
|
3970
|
+
console.log(` BLOCKING GO-LIVE (${r.blocking.length}):`);
|
|
3971
|
+
for (const b of r.blocking.slice(0, 10)) {
|
|
3972
|
+
console.log(` - ${b}`);
|
|
3973
|
+
}
|
|
3974
|
+
if (r.blocking.length > 10)
|
|
3975
|
+
console.log(` ... and ${r.blocking.length - 10} more`);
|
|
3976
|
+
console.log("");
|
|
3977
|
+
}
|
|
3978
|
+
// Done summary
|
|
3979
|
+
if (r.done.length > 0) {
|
|
3980
|
+
console.log(` COMPLETED (${r.done.length} items)`);
|
|
3981
|
+
for (const d of r.done.slice(0, 5)) {
|
|
3982
|
+
console.log(` + ${d}`);
|
|
3983
|
+
}
|
|
3984
|
+
if (r.done.length > 5)
|
|
3985
|
+
console.log(` ... and ${r.done.length - 5} more`);
|
|
3986
|
+
console.log("");
|
|
3987
|
+
}
|
|
3988
|
+
// Suggest update if no status or stale
|
|
3989
|
+
if (!snap.latestStatus) {
|
|
3990
|
+
console.log(` Tip: Run 'gnosys update-status' to generate a status snapshot.`);
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
catch (err) {
|
|
3994
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
3995
|
+
process.exit(1);
|
|
3996
|
+
}
|
|
3997
|
+
finally {
|
|
3998
|
+
centralDb?.close();
|
|
3999
|
+
}
|
|
4000
|
+
});
|
|
4001
|
+
// ─── gnosys update-status ────────────────────────────────────────────────
|
|
4002
|
+
program
|
|
4003
|
+
.command("update-status")
|
|
4004
|
+
.description("Show the prompt to give an AI agent to update this project's status for the portfolio dashboard")
|
|
4005
|
+
.option("-d, --directory <dir>", "Project directory (auto-detects if omitted)")
|
|
4006
|
+
.option("-p, --project <id>", "Project ID")
|
|
4007
|
+
.action(async (opts) => {
|
|
4008
|
+
let centralDb = null;
|
|
4009
|
+
try {
|
|
4010
|
+
centralDb = GnosysDB.openCentral();
|
|
4011
|
+
if (!centralDb.isAvailable()) {
|
|
4012
|
+
console.error("Central DB not available.");
|
|
4013
|
+
process.exit(1);
|
|
4014
|
+
}
|
|
4015
|
+
const { detectCurrentProject } = await import("./lib/federated.js");
|
|
4016
|
+
const { generateStatusPrompt } = await import("./lib/portfolio.js");
|
|
4017
|
+
let pid = opts.project || null;
|
|
4018
|
+
if (!pid)
|
|
4019
|
+
pid = await detectCurrentProject(centralDb, opts.directory || undefined);
|
|
4020
|
+
if (!pid) {
|
|
4021
|
+
console.error("No project specified and none detected.");
|
|
4022
|
+
process.exit(1);
|
|
4023
|
+
}
|
|
4024
|
+
const project = centralDb.getProject(pid);
|
|
4025
|
+
if (!project) {
|
|
4026
|
+
console.error(`Project not found: ${pid}`);
|
|
4027
|
+
process.exit(1);
|
|
4028
|
+
}
|
|
4029
|
+
const prompt = generateStatusPrompt(project.name, project.working_directory);
|
|
4030
|
+
console.log(prompt);
|
|
4031
|
+
}
|
|
4032
|
+
catch (err) {
|
|
4033
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
4034
|
+
process.exit(1);
|
|
4035
|
+
}
|
|
4036
|
+
finally {
|
|
4037
|
+
centralDb?.close();
|
|
4038
|
+
}
|
|
4039
|
+
});
|
|
3768
4040
|
// ─── gnosys working-set ──────────────────────────────────────────────────
|
|
3769
4041
|
program
|
|
3770
4042
|
.command("working-set")
|