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 +123 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
|
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
|
|
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}`);
|