@neuralconfig/nrepo 0.0.3 → 0.0.5
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 +217 -81
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk21 from "chalk";
|
|
6
6
|
|
|
7
7
|
// src/config.ts
|
|
8
8
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -96,6 +96,7 @@ var listIdeas = (c, params) => {
|
|
|
96
96
|
var createIdea = (c, data) => request(c, "POST", "/ideas", data);
|
|
97
97
|
var getIdea = (c, id) => request(c, "GET", `/ideas/${id}`);
|
|
98
98
|
var updateIdea = (c, id, data) => request(c, "PATCH", `/ideas/${id}`, data);
|
|
99
|
+
var bulkUpdateIdeas = (c, data) => request(c, "PATCH", "/ideas/bulk", data);
|
|
99
100
|
var searchIdeas = (c, query, limit) => {
|
|
100
101
|
const sp = new URLSearchParams({ q: query });
|
|
101
102
|
if (limit) sp.set("limit", String(limit));
|
|
@@ -115,6 +116,10 @@ var createRelation = (c, sourceId, targetId, relationType = "related", note, for
|
|
|
115
116
|
...note ? { note } : {}
|
|
116
117
|
});
|
|
117
118
|
};
|
|
119
|
+
var createBulkRelations = (c, links, force) => {
|
|
120
|
+
const qs = force ? "?force=true" : "";
|
|
121
|
+
return request(c, "POST", `/map/relations${qs}`, { links });
|
|
122
|
+
};
|
|
118
123
|
var deleteRelation = (c, relationId) => request(c, "DELETE", `/map/relations/${relationId}`);
|
|
119
124
|
var deleteIdea = (c, id) => request(c, "DELETE", `/ideas/${id}`);
|
|
120
125
|
var dismissDuplicate = (c, dupId) => request(c, "POST", `/ideas/duplicates/${dupId}/dismiss`);
|
|
@@ -471,21 +476,34 @@ import ora4 from "ora";
|
|
|
471
476
|
async function logCommand(opts) {
|
|
472
477
|
const config = await getAuthenticatedConfig();
|
|
473
478
|
const spinner = opts.json ? null : ora4("Loading ideas...").start();
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
+
const explicitLimit = opts.limit ? parseInt(opts.limit, 10) : void 0;
|
|
480
|
+
const pageSize = 100;
|
|
481
|
+
const allIdeas = [];
|
|
482
|
+
let offset = 0;
|
|
483
|
+
let remaining = explicitLimit ?? Infinity;
|
|
484
|
+
while (remaining > 0) {
|
|
485
|
+
const fetchLimit = Math.min(pageSize, remaining);
|
|
486
|
+
const data = await listIdeas(config, {
|
|
487
|
+
limit: fetchLimit,
|
|
488
|
+
offset,
|
|
489
|
+
status: opts.status,
|
|
490
|
+
tag: opts.tag
|
|
491
|
+
});
|
|
492
|
+
allIdeas.push(...data.ideas);
|
|
493
|
+
offset += data.ideas.length;
|
|
494
|
+
remaining -= data.ideas.length;
|
|
495
|
+
if (data.ideas.length < fetchLimit || !data.has_more) break;
|
|
496
|
+
}
|
|
479
497
|
spinner?.stop();
|
|
480
498
|
if (opts.json) {
|
|
481
|
-
console.log(JSON.stringify(
|
|
499
|
+
console.log(JSON.stringify(allIdeas, null, 2));
|
|
482
500
|
return;
|
|
483
501
|
}
|
|
484
|
-
if (
|
|
502
|
+
if (allIdeas.length === 0) {
|
|
485
503
|
console.log(chalk6.dim("No ideas found."));
|
|
486
504
|
return;
|
|
487
505
|
}
|
|
488
|
-
for (const idea of
|
|
506
|
+
for (const idea of allIdeas) {
|
|
489
507
|
console.log(formatIdeaRow(idea));
|
|
490
508
|
}
|
|
491
509
|
}
|
|
@@ -500,11 +518,23 @@ var statusStyle2 = {
|
|
|
500
518
|
shipped: chalk7.green,
|
|
501
519
|
shelved: chalk7.dim
|
|
502
520
|
};
|
|
521
|
+
async function fetchAllIdeas(config) {
|
|
522
|
+
const all = [];
|
|
523
|
+
let offset = 0;
|
|
524
|
+
const pageSize = 100;
|
|
525
|
+
while (true) {
|
|
526
|
+
const data = await listIdeas(config, { limit: pageSize, offset });
|
|
527
|
+
all.push(...data.ideas);
|
|
528
|
+
if (data.ideas.length < pageSize || !data.has_more) break;
|
|
529
|
+
offset += data.ideas.length;
|
|
530
|
+
}
|
|
531
|
+
return all;
|
|
532
|
+
}
|
|
503
533
|
async function statusCommand(opts) {
|
|
504
534
|
const config = await getAuthenticatedConfig();
|
|
505
535
|
const spinner = opts.json ? null : ora5("Loading dashboard...").start();
|
|
506
|
-
const [
|
|
507
|
-
|
|
536
|
+
const [allIdeas, dupsData, user] = await Promise.all([
|
|
537
|
+
fetchAllIdeas(config),
|
|
508
538
|
listDuplicates(config),
|
|
509
539
|
getMe(config)
|
|
510
540
|
]);
|
|
@@ -512,8 +542,8 @@ async function statusCommand(opts) {
|
|
|
512
542
|
if (opts.json) {
|
|
513
543
|
console.log(JSON.stringify({
|
|
514
544
|
user: { email: user.email, plan: user.plan },
|
|
515
|
-
counts: formatStatusCounts(
|
|
516
|
-
total:
|
|
545
|
+
counts: formatStatusCounts(allIdeas),
|
|
546
|
+
total: allIdeas.length,
|
|
517
547
|
pending_duplicates: dupsData.duplicates.length
|
|
518
548
|
}, null, 2));
|
|
519
549
|
return;
|
|
@@ -521,16 +551,16 @@ async function statusCommand(opts) {
|
|
|
521
551
|
console.log(chalk7.bold("NeuralRepo Dashboard"));
|
|
522
552
|
console.log(chalk7.dim(`${user.display_name ?? user.email} \xB7 ${user.plan}
|
|
523
553
|
`));
|
|
524
|
-
const counts = formatStatusCounts(
|
|
554
|
+
const counts = formatStatusCounts(allIdeas);
|
|
525
555
|
console.log(chalk7.bold("Status breakdown"));
|
|
526
556
|
for (const [status, count] of Object.entries(counts)) {
|
|
527
557
|
const style = statusStyle2[status] ?? chalk7.white;
|
|
528
558
|
const bar = "\u2588".repeat(Math.min(count, 40));
|
|
529
559
|
console.log(` ${style(status.padEnd(10))} ${style(bar)} ${count}`);
|
|
530
560
|
}
|
|
531
|
-
console.log(` ${"total".padEnd(10)} ${chalk7.bold(String(
|
|
561
|
+
console.log(` ${"total".padEnd(10)} ${chalk7.bold(String(allIdeas.length))}
|
|
532
562
|
`);
|
|
533
|
-
const recent =
|
|
563
|
+
const recent = allIdeas.filter((i) => i.status === "captured").slice(0, 5);
|
|
534
564
|
if (recent.length > 0) {
|
|
535
565
|
console.log(chalk7.bold("Recent captures"));
|
|
536
566
|
for (const idea of recent) {
|
|
@@ -601,30 +631,20 @@ async function moveBulkCommand(status, opts) {
|
|
|
601
631
|
process.exit(1);
|
|
602
632
|
}
|
|
603
633
|
const spinner = opts.json ? null : ora7(`Moving ${ids.length} ideas to ${status}...`).start();
|
|
604
|
-
const
|
|
605
|
-
ids.map(async (id) => {
|
|
606
|
-
const idea = await updateIdea(config, id, { status });
|
|
607
|
-
return { id, title: idea.title };
|
|
608
|
-
})
|
|
609
|
-
);
|
|
634
|
+
const result = await bulkUpdateIdeas(config, { ids, status });
|
|
610
635
|
spinner?.stop();
|
|
611
636
|
if (opts.json) {
|
|
612
|
-
|
|
613
|
-
id: ids[i],
|
|
614
|
-
success: r.status === "fulfilled",
|
|
615
|
-
error: r.status === "rejected" ? r.reason.message : void 0
|
|
616
|
-
}));
|
|
617
|
-
console.log(JSON.stringify(output, null, 2));
|
|
637
|
+
console.log(JSON.stringify(result, null, 2));
|
|
618
638
|
return;
|
|
619
639
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
console.log(` ${chalk8.green("\u2713")} #${ids[i]} ${r.value.title}`);
|
|
640
|
+
for (const r of result.results) {
|
|
641
|
+
if (r.status === "updated") {
|
|
642
|
+
console.log(` ${chalk8.green("\u2713")} #${r.id}`);
|
|
624
643
|
} else {
|
|
625
|
-
console.log(` ${chalk8.red("\u2717")} #${
|
|
644
|
+
console.log(` ${chalk8.red("\u2717")} #${r.id} ${r.error}`);
|
|
626
645
|
}
|
|
627
|
-
}
|
|
646
|
+
}
|
|
647
|
+
console.log(`${chalk8.green(result.updated.toString())} moved to ${status}, ${result.errors > 0 ? chalk8.red(result.errors.toString()) : "0"} errors`);
|
|
628
648
|
}
|
|
629
649
|
|
|
630
650
|
// src/commands/tag.ts
|
|
@@ -660,32 +680,20 @@ async function tagAddCommand(tag, opts) {
|
|
|
660
680
|
process.exit(1);
|
|
661
681
|
}
|
|
662
682
|
const spinner = opts.json ? null : ora8(`Adding tag "${tag}" to ${ids.length} ideas...`).start();
|
|
663
|
-
const
|
|
664
|
-
ids.map(async (id) => {
|
|
665
|
-
const existing = await getIdea(config, id);
|
|
666
|
-
const merged = [.../* @__PURE__ */ new Set([...existing.tags, tag])];
|
|
667
|
-
await updateIdea(config, id, { tags: merged });
|
|
668
|
-
return { id, title: existing.title };
|
|
669
|
-
})
|
|
670
|
-
);
|
|
683
|
+
const result = await bulkUpdateIdeas(config, { ids, add_tags: [tag] });
|
|
671
684
|
spinner?.stop();
|
|
672
685
|
if (opts.json) {
|
|
673
|
-
|
|
674
|
-
id: ids[i],
|
|
675
|
-
success: r.status === "fulfilled",
|
|
676
|
-
error: r.status === "rejected" ? r.reason.message : void 0
|
|
677
|
-
}));
|
|
678
|
-
console.log(JSON.stringify(output, null, 2));
|
|
686
|
+
console.log(JSON.stringify(result, null, 2));
|
|
679
687
|
return;
|
|
680
688
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
console.log(` ${chalk9.green("\u2713")} #${ids[i]} ${r.value.title}`);
|
|
689
|
+
for (const r of result.results) {
|
|
690
|
+
if (r.status === "updated") {
|
|
691
|
+
console.log(` ${chalk9.green("\u2713")} #${r.id}`);
|
|
685
692
|
} else {
|
|
686
|
-
console.log(` ${chalk9.red("\u2717")} #${
|
|
693
|
+
console.log(` ${chalk9.red("\u2717")} #${r.id} ${r.error}`);
|
|
687
694
|
}
|
|
688
|
-
}
|
|
695
|
+
}
|
|
696
|
+
console.log(`${chalk9.green(result.updated.toString())} tagged with "${tag}", ${result.errors > 0 ? chalk9.red(result.errors.toString()) : "0"} errors`);
|
|
689
697
|
}
|
|
690
698
|
async function tagRemoveCommand(tag, opts) {
|
|
691
699
|
const config = await getAuthenticatedConfig();
|
|
@@ -695,32 +703,20 @@ async function tagRemoveCommand(tag, opts) {
|
|
|
695
703
|
process.exit(1);
|
|
696
704
|
}
|
|
697
705
|
const spinner = opts.json ? null : ora8(`Removing tag "${tag}" from ${ids.length} ideas...`).start();
|
|
698
|
-
const
|
|
699
|
-
ids.map(async (id) => {
|
|
700
|
-
const existing = await getIdea(config, id);
|
|
701
|
-
const filtered = existing.tags.filter((t) => t !== tag);
|
|
702
|
-
await updateIdea(config, id, { tags: filtered });
|
|
703
|
-
return { id, title: existing.title };
|
|
704
|
-
})
|
|
705
|
-
);
|
|
706
|
+
const result = await bulkUpdateIdeas(config, { ids, remove_tags: [tag] });
|
|
706
707
|
spinner?.stop();
|
|
707
708
|
if (opts.json) {
|
|
708
|
-
|
|
709
|
-
id: ids[i],
|
|
710
|
-
success: r.status === "fulfilled",
|
|
711
|
-
error: r.status === "rejected" ? r.reason.message : void 0
|
|
712
|
-
}));
|
|
713
|
-
console.log(JSON.stringify(output, null, 2));
|
|
709
|
+
console.log(JSON.stringify(result, null, 2));
|
|
714
710
|
return;
|
|
715
711
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
console.log(` ${chalk9.green("\u2713")} #${ids[i]} ${r.value.title}`);
|
|
712
|
+
for (const r of result.results) {
|
|
713
|
+
if (r.status === "updated") {
|
|
714
|
+
console.log(` ${chalk9.green("\u2713")} #${r.id}`);
|
|
720
715
|
} else {
|
|
721
|
-
console.log(` ${chalk9.red("\u2717")} #${
|
|
716
|
+
console.log(` ${chalk9.red("\u2717")} #${r.id} ${r.error}`);
|
|
722
717
|
}
|
|
723
|
-
}
|
|
718
|
+
}
|
|
719
|
+
console.log(`${chalk9.green(result.updated.toString())} untagged "${tag}", ${result.errors > 0 ? chalk9.red(result.errors.toString()) : "0"} errors`);
|
|
724
720
|
}
|
|
725
721
|
function parseIds(idsStr) {
|
|
726
722
|
return idsStr.split(",").map((s) => parseInt(s.trim(), 10)).filter((n) => !isNaN(n) && n > 0);
|
|
@@ -1111,6 +1107,9 @@ import chalk17 from "chalk";
|
|
|
1111
1107
|
import ora16 from "ora";
|
|
1112
1108
|
var VALID_TYPES = RELATION_TYPES.filter((t) => t !== "duplicate");
|
|
1113
1109
|
async function linkCommand(sourceId, targetId, opts) {
|
|
1110
|
+
if (opts.batch) {
|
|
1111
|
+
return linkBatchCommand(opts);
|
|
1112
|
+
}
|
|
1114
1113
|
const config = await getAuthenticatedConfig();
|
|
1115
1114
|
const src = parseInt(sourceId, 10);
|
|
1116
1115
|
const tgt = parseInt(targetId, 10);
|
|
@@ -1151,6 +1150,47 @@ async function linkCommand(sourceId, targetId, opts) {
|
|
|
1151
1150
|
throw err;
|
|
1152
1151
|
}
|
|
1153
1152
|
}
|
|
1153
|
+
async function linkBatchCommand(opts) {
|
|
1154
|
+
const config = await getAuthenticatedConfig();
|
|
1155
|
+
const chunks = [];
|
|
1156
|
+
for await (const chunk of process.stdin) {
|
|
1157
|
+
chunks.push(chunk);
|
|
1158
|
+
}
|
|
1159
|
+
const input = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1160
|
+
if (!input) {
|
|
1161
|
+
console.error("No input received. Pipe a JSON array to stdin.");
|
|
1162
|
+
console.error(chalk17.dim(` Example: echo '[{"source_idea_id":1,"target_idea_id":2}]' | nrepo link --batch`));
|
|
1163
|
+
process.exit(1);
|
|
1164
|
+
}
|
|
1165
|
+
let links;
|
|
1166
|
+
try {
|
|
1167
|
+
links = JSON.parse(input);
|
|
1168
|
+
if (!Array.isArray(links)) throw new Error("Input must be a JSON array");
|
|
1169
|
+
} catch (err) {
|
|
1170
|
+
console.error(`Invalid JSON: ${err.message}`);
|
|
1171
|
+
process.exit(1);
|
|
1172
|
+
}
|
|
1173
|
+
if (links.length === 0) {
|
|
1174
|
+
console.error("Empty array \u2014 nothing to link.");
|
|
1175
|
+
process.exit(1);
|
|
1176
|
+
}
|
|
1177
|
+
const spinner = opts.json ? null : ora16(`Creating ${links.length} links...`).start();
|
|
1178
|
+
const result = await createBulkRelations(config, links, opts.force);
|
|
1179
|
+
spinner?.stop();
|
|
1180
|
+
if (opts.json) {
|
|
1181
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
for (const r of result.results) {
|
|
1185
|
+
if (r.status === "created") {
|
|
1186
|
+
console.log(chalk17.green("\u2713") + ` Linked #${r.source_idea_id} \u2192 #${r.target_idea_id} (${r.relation_type})`);
|
|
1187
|
+
} else {
|
|
1188
|
+
console.log(chalk17.red("\u2717") + ` #${r.source_idea_id} \u2192 #${r.target_idea_id}: ${r.error}`);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
console.log("");
|
|
1192
|
+
console.log(`${chalk17.green(result.linked.toString())} created, ${result.errors > 0 ? chalk17.red(result.errors.toString()) : "0"} errors`);
|
|
1193
|
+
}
|
|
1154
1194
|
async function unlinkCommand(sourceId, targetId, opts) {
|
|
1155
1195
|
const config = await getAuthenticatedConfig();
|
|
1156
1196
|
const src = parseInt(sourceId, 10);
|
|
@@ -1415,9 +1455,104 @@ async function graphCommand(id, opts) {
|
|
|
1415
1455
|
}
|
|
1416
1456
|
}
|
|
1417
1457
|
|
|
1458
|
+
// src/update-check.ts
|
|
1459
|
+
import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
1460
|
+
import { writeFile as writeFile3, mkdir as mkdir3, copyFile } from "fs/promises";
|
|
1461
|
+
import { homedir as homedir2 } from "os";
|
|
1462
|
+
import { join as join3, dirname } from "path";
|
|
1463
|
+
import { fileURLToPath } from "url";
|
|
1464
|
+
import chalk20 from "chalk";
|
|
1465
|
+
var CHECK_FILE = join3(CONFIG_DIR, "update-check.json");
|
|
1466
|
+
var WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1467
|
+
var PACKAGE_NAME = "@neuralconfig/nrepo";
|
|
1468
|
+
function checkForUpdates(currentVersion) {
|
|
1469
|
+
if (process.env["NREPO_NO_UPDATE_CHECK"] === "1") return;
|
|
1470
|
+
try {
|
|
1471
|
+
const cached = readCachedCheck();
|
|
1472
|
+
if (cached?.latest_version && isNewer(cached.latest_version, currentVersion)) {
|
|
1473
|
+
printUpdateNotice(currentVersion, cached.latest_version);
|
|
1474
|
+
}
|
|
1475
|
+
if (!cached || isStale(cached.last_checked)) {
|
|
1476
|
+
fetchAndCache(currentVersion);
|
|
1477
|
+
}
|
|
1478
|
+
} catch {
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
function printUpdateNotice(current, latest) {
|
|
1482
|
+
console.error(
|
|
1483
|
+
chalk20.dim(` nrepo ${latest} available (current: ${current}). Run `) + chalk20.dim.bold("npm i -g @neuralconfig/nrepo") + chalk20.dim(" to update.")
|
|
1484
|
+
);
|
|
1485
|
+
console.error("");
|
|
1486
|
+
}
|
|
1487
|
+
function readCachedCheck() {
|
|
1488
|
+
if (!existsSync2(CHECK_FILE)) return null;
|
|
1489
|
+
try {
|
|
1490
|
+
const raw = readFileSync(CHECK_FILE, "utf-8");
|
|
1491
|
+
return JSON.parse(raw);
|
|
1492
|
+
} catch {
|
|
1493
|
+
return null;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
function isStale(lastChecked) {
|
|
1497
|
+
return Date.now() - new Date(lastChecked).getTime() > WEEK_MS;
|
|
1498
|
+
}
|
|
1499
|
+
function isNewer(latest, current) {
|
|
1500
|
+
const l = latest.split(".").map(Number);
|
|
1501
|
+
const c = current.split(".").map(Number);
|
|
1502
|
+
for (let i = 0; i < 3; i++) {
|
|
1503
|
+
if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
|
|
1504
|
+
if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
|
|
1505
|
+
}
|
|
1506
|
+
return false;
|
|
1507
|
+
}
|
|
1508
|
+
function fetchAndCache(currentVersion) {
|
|
1509
|
+
(async () => {
|
|
1510
|
+
const controller = new AbortController();
|
|
1511
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
1512
|
+
try {
|
|
1513
|
+
const res = await fetch(
|
|
1514
|
+
`https://registry.npmjs.org/${PACKAGE_NAME}/latest`,
|
|
1515
|
+
{ signal: controller.signal }
|
|
1516
|
+
);
|
|
1517
|
+
clearTimeout(timeout);
|
|
1518
|
+
if (!res.ok) return;
|
|
1519
|
+
const data = await res.json();
|
|
1520
|
+
const latest = data.version;
|
|
1521
|
+
if (!existsSync2(CONFIG_DIR)) {
|
|
1522
|
+
await mkdir3(CONFIG_DIR, { recursive: true });
|
|
1523
|
+
}
|
|
1524
|
+
const check = {
|
|
1525
|
+
last_checked: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1526
|
+
latest_version: latest
|
|
1527
|
+
};
|
|
1528
|
+
await writeFile3(CHECK_FILE, JSON.stringify(check, null, 2) + "\n", "utf-8");
|
|
1529
|
+
if (isNewer(latest, currentVersion)) {
|
|
1530
|
+
await updateSkillFile();
|
|
1531
|
+
}
|
|
1532
|
+
} catch {
|
|
1533
|
+
}
|
|
1534
|
+
})();
|
|
1535
|
+
}
|
|
1536
|
+
async function updateSkillFile() {
|
|
1537
|
+
try {
|
|
1538
|
+
const claudeDir = join3(homedir2(), ".claude");
|
|
1539
|
+
if (!existsSync2(claudeDir)) return;
|
|
1540
|
+
const skillDir = join3(claudeDir, "skills", "neuralrepo");
|
|
1541
|
+
if (!existsSync2(skillDir)) {
|
|
1542
|
+
await mkdir3(skillDir, { recursive: true });
|
|
1543
|
+
}
|
|
1544
|
+
const src = join3(dirname(fileURLToPath(import.meta.url)), "..", "skill", "SKILL.md");
|
|
1545
|
+
if (!existsSync2(src)) return;
|
|
1546
|
+
const dest = join3(skillDir, "SKILL.md");
|
|
1547
|
+
await copyFile(src, dest);
|
|
1548
|
+
} catch {
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1418
1552
|
// src/index.ts
|
|
1553
|
+
var VERSION = "0.0.4";
|
|
1419
1554
|
var program = new Command();
|
|
1420
|
-
program.name("nrepo").description("NeuralRepo \u2014 capture and manage ideas from the terminal").version(
|
|
1555
|
+
program.name("nrepo").description("NeuralRepo \u2014 capture and manage ideas from the terminal").version(VERSION);
|
|
1421
1556
|
program.command("login").description("Authenticate with NeuralRepo").option("--api-key", "Login with an API key instead of browser OAuth").action(wrap(loginCommand));
|
|
1422
1557
|
program.command("logout").description("Clear stored credentials").action(wrap(async () => {
|
|
1423
1558
|
await clearConfig();
|
|
@@ -1451,7 +1586,7 @@ tagCmd.argument("[id]").argument("[tags...]").option("--json", "Output as JSON")
|
|
|
1451
1586
|
program.command("pull <id>").description("Export idea + context as local files").option("--to <dir>", "Output directory (default: current)").option("--json", "Output as JSON (prints idea data instead of writing files)").option("--human", "Force human-readable output").action(wrap(pullCommand));
|
|
1452
1587
|
program.command("diff <id> [id2]").description("Compare two ideas side-by-side (or diff against parent/related)").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(diffCommand));
|
|
1453
1588
|
program.command("branch <id>").description("Fork an idea into a new variant").option("--title <title>", "Override title for the branch").option("--body <body>", "Override body for the branch").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(branchCommand));
|
|
1454
|
-
program.command("link
|
|
1589
|
+
program.command("link [source-id] [target-id]").description("Create a link between two ideas. Use --batch to pipe a JSON array from stdin.").option("--type <type>", "Link type (related|blocks|inspires|supersedes|parent)", "related").option("--note <note>", "Add a note to the link").option("--force", "Bypass cycle detection for soft-block types").option("--batch", "Bulk mode: read JSON array of links from stdin").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(linkCommand));
|
|
1455
1590
|
program.command("unlink <source-id> <target-id>").description("Remove a link between two ideas").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(unlinkCommand));
|
|
1456
1591
|
program.command("links <id>").description("Show all links for an idea").option("--type <type>", "Filter by link type").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(linksCommand));
|
|
1457
1592
|
program.command("merge <keep-id> <absorb-id>").description("Merge two ideas (absorb the second into the first)").option("--force", "Skip confirmation").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(mergeCommand));
|
|
@@ -1465,6 +1600,7 @@ var keys = program.command("key").description("Manage API keys");
|
|
|
1465
1600
|
keys.command("list").description("List all API keys").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(keysListCommand));
|
|
1466
1601
|
keys.command("create <label>").description("Create a new API key").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(keysCreateCommand));
|
|
1467
1602
|
keys.command("revoke <key-id>").description("Revoke an API key").option("--json", "Output as JSON").option("--human", "Force human-readable output").action(wrap(keysRevokeCommand));
|
|
1603
|
+
checkForUpdates(VERSION);
|
|
1468
1604
|
program.parse();
|
|
1469
1605
|
function collect(value, previous) {
|
|
1470
1606
|
return previous.concat([value]);
|
|
@@ -1496,21 +1632,21 @@ function wrap(fn) {
|
|
|
1496
1632
|
process.exit(1);
|
|
1497
1633
|
}
|
|
1498
1634
|
if (err instanceof AuthError) {
|
|
1499
|
-
console.error(
|
|
1635
|
+
console.error(chalk21.red(err.message));
|
|
1500
1636
|
process.exit(1);
|
|
1501
1637
|
}
|
|
1502
1638
|
if (err instanceof ApiError) {
|
|
1503
1639
|
if (err.status === 401) {
|
|
1504
|
-
console.error(
|
|
1640
|
+
console.error(chalk21.red("Authentication expired. Run `nrepo login` to re-authenticate."));
|
|
1505
1641
|
} else if (err.status === 403) {
|
|
1506
|
-
console.error(
|
|
1642
|
+
console.error(chalk21.yellow("This feature requires a Pro plan. Upgrade at https://neuralrepo.com/settings"));
|
|
1507
1643
|
} else {
|
|
1508
|
-
console.error(
|
|
1644
|
+
console.error(chalk21.red(`API error (${err.status}): ${err.message}`));
|
|
1509
1645
|
}
|
|
1510
1646
|
process.exit(1);
|
|
1511
1647
|
}
|
|
1512
1648
|
if (err instanceof Error && err.message.startsWith("Network error")) {
|
|
1513
|
-
console.error(
|
|
1649
|
+
console.error(chalk21.red(err.message));
|
|
1514
1650
|
process.exit(1);
|
|
1515
1651
|
}
|
|
1516
1652
|
throw err;
|