unforgit 0.4.0 → 0.5.0

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command30 } from "commander";
4
+ import { Command as Command31 } from "commander";
5
5
 
6
6
  // src/commands/init.ts
7
7
  import fs2 from "fs";
@@ -3740,6 +3740,126 @@ async function runRemoteCurate(remoteUrl, options) {
3740
3740
  return client.runLifecycle(options);
3741
3741
  }
3742
3742
 
3743
+ // src/commands/suggestions.ts
3744
+ import { Command as Command30 } from "commander";
3745
+ import { getDbPath as getDbPath22, isInitialized as isInitialized18, loadConfig as loadConfig24 } from "unforgit-config";
3746
+ import { LocalStore as LocalStore21 } from "unforgit-db";
3747
+ import { generateSuggestions, persistReviewableSuggestions } from "unforgit-core";
3748
+ function openStore() {
3749
+ if (!isInitialized18()) {
3750
+ logger.error("Unforgit not initialized. Run 'unforgit init' first.");
3751
+ process.exit(EXIT_CONFIG_ERROR);
3752
+ }
3753
+ const config = loadConfig24();
3754
+ return {
3755
+ config,
3756
+ orgId: config.remote.orgId || "local",
3757
+ repoId: config.remote.repoId || "local",
3758
+ store: new LocalStore21(getDbPath22())
3759
+ };
3760
+ }
3761
+ function formatSuggestionLine(suggestion) {
3762
+ const memoryIds = suggestion.memoryIds.map((id) => id.slice(0, 8)).join(", ");
3763
+ return [
3764
+ `${suggestion.id.slice(0, 8)} [${suggestion.priority}] ${suggestion.type}`,
3765
+ ` status: ${suggestion.status}`,
3766
+ ` confidence: ${Math.round(suggestion.confidence * 100)}%`,
3767
+ ` memories: ${memoryIds || "none"}`,
3768
+ ` reason: ${suggestion.reason}`
3769
+ ].join("\n");
3770
+ }
3771
+ function parseStatus(value) {
3772
+ if (!value) return ["pending"];
3773
+ const statuses = value.split(",").map((status) => status.trim()).filter(Boolean);
3774
+ const allowed = /* @__PURE__ */ new Set(["pending", "approved", "rejected", "applied"]);
3775
+ const invalid = statuses.find((status) => !allowed.has(status));
3776
+ if (invalid) {
3777
+ throw new Error(`Invalid suggestion status: ${invalid}`);
3778
+ }
3779
+ return statuses;
3780
+ }
3781
+ var suggestionsCommand = new Command30("suggestions").description("Reviewable curation suggestions inbox").addCommand(
3782
+ new Command30("generate").description("Generate and persist pending review suggestions").option("--max <n>", "Maximum generated suggestions", (value) => Number.parseInt(value, 10), 20).option("--created-by <name>", "Actor name stored on generated suggestions", "cli").action((opts) => {
3783
+ const { store, orgId, repoId } = openStore();
3784
+ try {
3785
+ const generated = generateSuggestions(store, orgId, repoId, {
3786
+ maxSuggestions: opts.max
3787
+ });
3788
+ const persisted = persistReviewableSuggestions(
3789
+ store,
3790
+ orgId,
3791
+ repoId,
3792
+ generated.suggestions,
3793
+ { createdBy: opts.createdBy }
3794
+ );
3795
+ const result = { ...generated.stats, ...persisted };
3796
+ if (isJsonMode()) {
3797
+ outputJson(result);
3798
+ return;
3799
+ }
3800
+ logger.info(
3801
+ `Generated ${generated.stats.suggestionsGenerated} suggestions; created ${persisted.created} pending review items; skipped ${persisted.skippedExisting} existing.`
3802
+ );
3803
+ } finally {
3804
+ store.close();
3805
+ }
3806
+ })
3807
+ ).addCommand(
3808
+ new Command30("list").description("List curation suggestions by review status").option("--status <statuses>", "Comma-separated statuses (default: pending)").option("--limit <n>", "Maximum suggestions to show", (value) => Number.parseInt(value, 10), 20).action((opts) => {
3809
+ const { store, orgId, repoId } = openStore();
3810
+ try {
3811
+ const status = parseStatus(opts.status);
3812
+ const suggestions = store.listCurationSuggestions({
3813
+ orgId,
3814
+ repoId,
3815
+ status,
3816
+ limit: opts.limit
3817
+ });
3818
+ if (isJsonMode()) {
3819
+ outputJson({ suggestions });
3820
+ return;
3821
+ }
3822
+ const label = status?.join(",") ?? "all";
3823
+ logger.info(`${label === "pending" ? "Pending" : label} curation suggestions`);
3824
+ if (suggestions.length === 0) {
3825
+ logger.info("No suggestions found.");
3826
+ return;
3827
+ }
3828
+ for (const suggestion of suggestions) {
3829
+ logger.info(formatSuggestionLine(suggestion));
3830
+ logger.info("");
3831
+ }
3832
+ } finally {
3833
+ store.close();
3834
+ }
3835
+ })
3836
+ ).addCommand(
3837
+ new Command30("review").description("Approve, reject, or mark a curation suggestion as applied").argument("<id>", "Suggestion id").option("--approve", "Mark suggestion as approved").option("--reject", "Mark suggestion as rejected").option("--applied", "Mark suggestion as applied").option("--reviewer <name>", "Reviewer name").option("--note <note>", "Review note").action((id, opts) => {
3838
+ const selected = [opts.approve, opts.reject, opts.applied].filter(Boolean).length;
3839
+ if (selected !== 1) {
3840
+ logger.error("Choose exactly one of --approve, --reject, or --applied.");
3841
+ process.exit(EXIT_ERROR);
3842
+ }
3843
+ const status = opts.approve ? "approved" : opts.reject ? "rejected" : "applied";
3844
+ const { store } = openStore();
3845
+ try {
3846
+ const suggestion = store.reviewCurationSuggestion({
3847
+ id,
3848
+ status,
3849
+ reviewedBy: opts.reviewer,
3850
+ reviewNote: opts.note
3851
+ });
3852
+ if (isJsonMode()) {
3853
+ outputJson(suggestion);
3854
+ return;
3855
+ }
3856
+ logger.info(`Suggestion ${suggestion.id.slice(0, 8)} marked ${suggestion.status}.`);
3857
+ } finally {
3858
+ store.close();
3859
+ }
3860
+ })
3861
+ );
3862
+
3743
3863
  // src/index.ts
3744
3864
  import { createRequire } from "module";
3745
3865
  var require2 = createRequire(import.meta.url);
@@ -3778,7 +3898,7 @@ process.on("unhandledRejection", (err) => {
3778
3898
  runCleanup();
3779
3899
  process.exit(EXIT_ERROR);
3780
3900
  });
3781
- var program = new Command30();
3901
+ var program = new Command31();
3782
3902
  program.name("unforgit").description("Unforgit \u2014 repository memory for agents and developers").version(pkg.version).option("--verbose", "Enable verbose output").option("--quiet", "Suppress non-essential output").option("--json", "Output results as JSON (for scripting)").hook("preAction", () => {
3783
3903
  const opts = program.opts();
3784
3904
  if (opts.quiet) setVerbosity(0);
@@ -3819,6 +3939,7 @@ program.addCommand(backupsCommand);
3819
3939
  program.addCommand(dashboardCommand);
3820
3940
  program.addCommand(doctorCommand);
3821
3941
  program.addCommand(curateCommand);
3942
+ program.addCommand(suggestionsCommand);
3822
3943
  program.addCommand(completionCommand);
3823
3944
  program.parseAsync().catch((err) => {
3824
3945
  console.error(`fatal: ${err instanceof Error ? err.message : err}`);