planmode 0.3.0 → 0.4.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 +408 -7
- package/dist/mcp.js +491 -115
- package/package.json +1 -1
- package/src/commands/context.ts +111 -0
- package/src/commands/interactive.ts +107 -0
- package/src/index.ts +3 -1
- package/src/lib/context.ts +265 -0
- package/src/mcp.ts +146 -0
- package/src/types/index.ts +28 -0
package/dist/index.js
CHANGED
|
@@ -1377,18 +1377,240 @@ var init_doctor = __esm({
|
|
|
1377
1377
|
}
|
|
1378
1378
|
});
|
|
1379
1379
|
|
|
1380
|
+
// src/lib/context.ts
|
|
1381
|
+
import fs15 from "fs";
|
|
1382
|
+
import path15 from "path";
|
|
1383
|
+
import { parse as parse4, stringify as stringify6 } from "yaml";
|
|
1384
|
+
function getContextPath(projectDir) {
|
|
1385
|
+
return path15.join(projectDir, CONTEXT_DIR, CONTEXT_FILE);
|
|
1386
|
+
}
|
|
1387
|
+
function emptyIndex() {
|
|
1388
|
+
return { version: 1, repos: [] };
|
|
1389
|
+
}
|
|
1390
|
+
function readContextIndex(projectDir = process.cwd()) {
|
|
1391
|
+
const filePath = getContextPath(projectDir);
|
|
1392
|
+
try {
|
|
1393
|
+
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
1394
|
+
const data = parse4(raw);
|
|
1395
|
+
return data ?? emptyIndex();
|
|
1396
|
+
} catch {
|
|
1397
|
+
return emptyIndex();
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function writeContextIndex(index, projectDir = process.cwd()) {
|
|
1401
|
+
const dirPath = path15.join(projectDir, CONTEXT_DIR);
|
|
1402
|
+
fs15.mkdirSync(dirPath, { recursive: true });
|
|
1403
|
+
const filePath = getContextPath(projectDir);
|
|
1404
|
+
fs15.writeFileSync(filePath, stringify6(index), "utf-8");
|
|
1405
|
+
}
|
|
1406
|
+
function walkDirectory(dirPath) {
|
|
1407
|
+
const files = [];
|
|
1408
|
+
function walk(currentPath) {
|
|
1409
|
+
let entries;
|
|
1410
|
+
try {
|
|
1411
|
+
entries = fs15.readdirSync(currentPath, { withFileTypes: true });
|
|
1412
|
+
} catch {
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
for (const entry of entries) {
|
|
1416
|
+
if (entry.name.startsWith(".") && IGNORED_DIRS.has(entry.name)) continue;
|
|
1417
|
+
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
1418
|
+
const fullPath = path15.join(currentPath, entry.name);
|
|
1419
|
+
if (entry.isDirectory()) {
|
|
1420
|
+
walk(fullPath);
|
|
1421
|
+
} else if (entry.isFile()) {
|
|
1422
|
+
const ext = path15.extname(entry.name).toLowerCase();
|
|
1423
|
+
if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
|
|
1424
|
+
try {
|
|
1425
|
+
const stat = fs15.statSync(fullPath);
|
|
1426
|
+
const relativePath = path15.relative(dirPath, fullPath);
|
|
1427
|
+
files.push({
|
|
1428
|
+
path: relativePath,
|
|
1429
|
+
extension: ext,
|
|
1430
|
+
size: stat.size,
|
|
1431
|
+
modified_at: stat.mtime.toISOString()
|
|
1432
|
+
});
|
|
1433
|
+
} catch {
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
walk(dirPath);
|
|
1439
|
+
return files;
|
|
1440
|
+
}
|
|
1441
|
+
function addContextRepo(repoPath, options = {}) {
|
|
1442
|
+
const projectDir = options.projectDir ?? process.cwd();
|
|
1443
|
+
const absolutePath = path15.resolve(projectDir, repoPath);
|
|
1444
|
+
if (!fs15.existsSync(absolutePath)) {
|
|
1445
|
+
throw new Error(`Directory not found: ${repoPath}`);
|
|
1446
|
+
}
|
|
1447
|
+
if (!fs15.statSync(absolutePath).isDirectory()) {
|
|
1448
|
+
throw new Error(`Not a directory: ${repoPath}`);
|
|
1449
|
+
}
|
|
1450
|
+
const index = readContextIndex(projectDir);
|
|
1451
|
+
const relative = path15.relative(projectDir, absolutePath);
|
|
1452
|
+
const isInsideProject = !relative.startsWith("..") && !path15.isAbsolute(relative);
|
|
1453
|
+
const storedPath = isInsideProject ? relative : absolutePath;
|
|
1454
|
+
const existing = index.repos.find(
|
|
1455
|
+
(r) => r.repo.path === storedPath || r.repo.name === options.name
|
|
1456
|
+
);
|
|
1457
|
+
if (existing) {
|
|
1458
|
+
throw new Error(
|
|
1459
|
+
`Context repo already exists: ${existing.repo.name ?? existing.repo.path}. Use \`planmode context reindex\` to refresh.`
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
logger.info(`Scanning ${absolutePath}...`);
|
|
1463
|
+
const files = walkDirectory(absolutePath);
|
|
1464
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1465
|
+
const repoIndex = {
|
|
1466
|
+
repo: {
|
|
1467
|
+
path: storedPath,
|
|
1468
|
+
name: options.name,
|
|
1469
|
+
added_at: now
|
|
1470
|
+
},
|
|
1471
|
+
files,
|
|
1472
|
+
indexed_at: now,
|
|
1473
|
+
file_count: files.length,
|
|
1474
|
+
total_size: files.reduce((sum, f) => sum + f.size, 0)
|
|
1475
|
+
};
|
|
1476
|
+
index.repos.push(repoIndex);
|
|
1477
|
+
writeContextIndex(index, projectDir);
|
|
1478
|
+
logger.success(`Added "${options.name ?? storedPath}" \u2014 ${files.length} file(s), ${formatSize(repoIndex.total_size)}`);
|
|
1479
|
+
const breakdown = getTypeBreakdown(files);
|
|
1480
|
+
if (breakdown.length > 0) {
|
|
1481
|
+
logger.dim(` ${breakdown.join(", ")}`);
|
|
1482
|
+
}
|
|
1483
|
+
return repoIndex;
|
|
1484
|
+
}
|
|
1485
|
+
function removeContextRepo(pathOrName, projectDir = process.cwd()) {
|
|
1486
|
+
const index = readContextIndex(projectDir);
|
|
1487
|
+
const idx = index.repos.findIndex(
|
|
1488
|
+
(r) => r.repo.path === pathOrName || r.repo.name === pathOrName
|
|
1489
|
+
);
|
|
1490
|
+
if (idx === -1) {
|
|
1491
|
+
throw new Error(`Context repo not found: ${pathOrName}`);
|
|
1492
|
+
}
|
|
1493
|
+
const removed = index.repos[idx];
|
|
1494
|
+
index.repos.splice(idx, 1);
|
|
1495
|
+
writeContextIndex(index, projectDir);
|
|
1496
|
+
logger.success(`Removed "${removed.repo.name ?? removed.repo.path}"`);
|
|
1497
|
+
}
|
|
1498
|
+
function reindexContext(pathOrName, projectDir = process.cwd()) {
|
|
1499
|
+
const index = readContextIndex(projectDir);
|
|
1500
|
+
if (index.repos.length === 0) {
|
|
1501
|
+
throw new Error("No context repos configured. Use `planmode context add <path>` first.");
|
|
1502
|
+
}
|
|
1503
|
+
const targets = pathOrName ? index.repos.filter(
|
|
1504
|
+
(r) => r.repo.path === pathOrName || r.repo.name === pathOrName
|
|
1505
|
+
) : index.repos;
|
|
1506
|
+
if (pathOrName && targets.length === 0) {
|
|
1507
|
+
throw new Error(`Context repo not found: ${pathOrName}`);
|
|
1508
|
+
}
|
|
1509
|
+
for (const repo of targets) {
|
|
1510
|
+
const absolutePath = path15.resolve(projectDir, repo.repo.path);
|
|
1511
|
+
if (!fs15.existsSync(absolutePath)) {
|
|
1512
|
+
logger.warn(`Directory not found, skipping: ${repo.repo.path}`);
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
logger.info(`Re-scanning ${repo.repo.name ?? repo.repo.path}...`);
|
|
1516
|
+
const files = walkDirectory(absolutePath);
|
|
1517
|
+
repo.files = files;
|
|
1518
|
+
repo.indexed_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1519
|
+
repo.file_count = files.length;
|
|
1520
|
+
repo.total_size = files.reduce((sum, f) => sum + f.size, 0);
|
|
1521
|
+
logger.success(`Reindexed "${repo.repo.name ?? repo.repo.path}" \u2014 ${files.length} file(s), ${formatSize(repo.total_size)}`);
|
|
1522
|
+
}
|
|
1523
|
+
writeContextIndex(index, projectDir);
|
|
1524
|
+
}
|
|
1525
|
+
function getContextSummary(projectDir = process.cwd()) {
|
|
1526
|
+
const index = readContextIndex(projectDir);
|
|
1527
|
+
return {
|
|
1528
|
+
totalRepos: index.repos.length,
|
|
1529
|
+
totalFiles: index.repos.reduce((sum, r) => sum + r.file_count, 0),
|
|
1530
|
+
totalSize: index.repos.reduce((sum, r) => sum + r.total_size, 0),
|
|
1531
|
+
repos: index.repos.map((r) => ({
|
|
1532
|
+
name: r.repo.name ?? r.repo.path,
|
|
1533
|
+
path: r.repo.path,
|
|
1534
|
+
fileCount: r.file_count,
|
|
1535
|
+
totalSize: r.total_size,
|
|
1536
|
+
typeBreakdown: getTypeBreakdown(r.files),
|
|
1537
|
+
indexedAt: r.indexed_at
|
|
1538
|
+
}))
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
function getTypeBreakdown(files) {
|
|
1542
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1543
|
+
for (const file of files) {
|
|
1544
|
+
counts.set(file.extension, (counts.get(file.extension) ?? 0) + 1);
|
|
1545
|
+
}
|
|
1546
|
+
return Array.from(counts.entries()).sort((a, b) => b[1] - a[1]).map(([ext, count]) => `${ext}: ${count}`);
|
|
1547
|
+
}
|
|
1548
|
+
function formatSize(bytes) {
|
|
1549
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1550
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1551
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1552
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
1553
|
+
}
|
|
1554
|
+
var CONTEXT_DIR, CONTEXT_FILE, SUPPORTED_EXTENSIONS, IGNORED_DIRS;
|
|
1555
|
+
var init_context = __esm({
|
|
1556
|
+
"src/lib/context.ts"() {
|
|
1557
|
+
"use strict";
|
|
1558
|
+
init_logger();
|
|
1559
|
+
CONTEXT_DIR = ".planmode";
|
|
1560
|
+
CONTEXT_FILE = "context.yaml";
|
|
1561
|
+
SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1562
|
+
".txt",
|
|
1563
|
+
".md",
|
|
1564
|
+
".markdown",
|
|
1565
|
+
".pdf",
|
|
1566
|
+
".rtf",
|
|
1567
|
+
".doc",
|
|
1568
|
+
".docx",
|
|
1569
|
+
".csv",
|
|
1570
|
+
".tsv",
|
|
1571
|
+
".json",
|
|
1572
|
+
".yaml",
|
|
1573
|
+
".yml",
|
|
1574
|
+
".xml",
|
|
1575
|
+
".html",
|
|
1576
|
+
".htm",
|
|
1577
|
+
".rst",
|
|
1578
|
+
".org",
|
|
1579
|
+
".tex",
|
|
1580
|
+
".log"
|
|
1581
|
+
]);
|
|
1582
|
+
IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
1583
|
+
"node_modules",
|
|
1584
|
+
".git",
|
|
1585
|
+
"dist",
|
|
1586
|
+
"build",
|
|
1587
|
+
".next",
|
|
1588
|
+
"__pycache__",
|
|
1589
|
+
".venv",
|
|
1590
|
+
"venv",
|
|
1591
|
+
".tox",
|
|
1592
|
+
"target",
|
|
1593
|
+
"out",
|
|
1594
|
+
".cache",
|
|
1595
|
+
".turbo",
|
|
1596
|
+
"coverage",
|
|
1597
|
+
".nyc_output"
|
|
1598
|
+
]);
|
|
1599
|
+
}
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1380
1602
|
// src/commands/interactive.ts
|
|
1381
1603
|
var interactive_exports = {};
|
|
1382
1604
|
__export(interactive_exports, {
|
|
1383
1605
|
runInteractiveMenu: () => runInteractiveMenu
|
|
1384
1606
|
});
|
|
1385
|
-
import
|
|
1386
|
-
import
|
|
1607
|
+
import fs16 from "fs";
|
|
1608
|
+
import path16 from "path";
|
|
1387
1609
|
import os3 from "os";
|
|
1388
1610
|
import * as p12 from "@clack/prompts";
|
|
1389
1611
|
function isFirstRun() {
|
|
1390
|
-
const configPath =
|
|
1391
|
-
const hasConfig =
|
|
1612
|
+
const configPath = path16.join(os3.homedir(), ".planmode", "config");
|
|
1613
|
+
const hasConfig = fs16.existsSync(configPath);
|
|
1392
1614
|
const lockfile = readLockfile();
|
|
1393
1615
|
const hasPackages = Object.keys(lockfile.packages).length > 0;
|
|
1394
1616
|
return !hasConfig && !hasPackages;
|
|
@@ -1517,6 +1739,7 @@ async function mainMenu() {
|
|
|
1517
1739
|
{ value: "install", label: "Install a package", hint: "install by name" },
|
|
1518
1740
|
{ value: "create", label: "Create a new package" },
|
|
1519
1741
|
{ value: "list", label: "My installed packages" },
|
|
1742
|
+
{ value: "context", label: "Manage context", hint: "document directories for AI" },
|
|
1520
1743
|
{ value: "doctor", label: "Health check" },
|
|
1521
1744
|
{ value: "exit", label: "Exit" }
|
|
1522
1745
|
]
|
|
@@ -1540,6 +1763,9 @@ async function mainMenu() {
|
|
|
1540
1763
|
case "list":
|
|
1541
1764
|
listFlow();
|
|
1542
1765
|
break;
|
|
1766
|
+
case "context":
|
|
1767
|
+
await contextFlow();
|
|
1768
|
+
break;
|
|
1543
1769
|
case "doctor":
|
|
1544
1770
|
doctorFlow();
|
|
1545
1771
|
break;
|
|
@@ -1697,6 +1923,94 @@ async function installFlow() {
|
|
|
1697
1923
|
p12.log.error(err.message);
|
|
1698
1924
|
}
|
|
1699
1925
|
}
|
|
1926
|
+
async function contextFlow() {
|
|
1927
|
+
const summary = getContextSummary();
|
|
1928
|
+
if (summary.totalRepos === 0) {
|
|
1929
|
+
p12.log.info("No context repos yet.");
|
|
1930
|
+
} else {
|
|
1931
|
+
const lines = summary.repos.map(
|
|
1932
|
+
(r) => `${r.name} \u2014 ${r.fileCount} file(s), ${formatSize(r.totalSize)}`
|
|
1933
|
+
);
|
|
1934
|
+
p12.note(lines.join("\n"), `${summary.totalRepos} context repo(s)`);
|
|
1935
|
+
}
|
|
1936
|
+
const action = handleCancel(
|
|
1937
|
+
await p12.select({
|
|
1938
|
+
message: "What would you like to do?",
|
|
1939
|
+
options: [
|
|
1940
|
+
{ value: "add", label: "Add a directory" },
|
|
1941
|
+
{ value: "remove", label: "Remove a directory" },
|
|
1942
|
+
{ value: "reindex", label: "Re-index all" },
|
|
1943
|
+
{ value: "back", label: "Back" }
|
|
1944
|
+
]
|
|
1945
|
+
})
|
|
1946
|
+
);
|
|
1947
|
+
if (action === "back") return;
|
|
1948
|
+
if (action === "add") {
|
|
1949
|
+
const dirPath = handleCancel(
|
|
1950
|
+
await p12.text({
|
|
1951
|
+
message: "Path to document directory:",
|
|
1952
|
+
placeholder: "e.g. docs, specs, ./reference",
|
|
1953
|
+
validate(input) {
|
|
1954
|
+
if (!input) return "Please enter a directory path";
|
|
1955
|
+
}
|
|
1956
|
+
})
|
|
1957
|
+
);
|
|
1958
|
+
const name = handleCancel(
|
|
1959
|
+
await p12.text({
|
|
1960
|
+
message: "Label (optional):",
|
|
1961
|
+
placeholder: "e.g. Project Documentation"
|
|
1962
|
+
})
|
|
1963
|
+
);
|
|
1964
|
+
try {
|
|
1965
|
+
await withSpinner(
|
|
1966
|
+
"Indexing documents...",
|
|
1967
|
+
async () => addContextRepo(dirPath, { name: name || void 0 }),
|
|
1968
|
+
"Indexing complete"
|
|
1969
|
+
);
|
|
1970
|
+
} catch (err) {
|
|
1971
|
+
p12.log.error(err.message);
|
|
1972
|
+
}
|
|
1973
|
+
} else if (action === "remove") {
|
|
1974
|
+
if (summary.totalRepos === 0) {
|
|
1975
|
+
p12.log.warn("No context repos to remove.");
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
const selected = handleCancel(
|
|
1979
|
+
await p12.select({
|
|
1980
|
+
message: "Select a repo to remove:",
|
|
1981
|
+
options: [
|
|
1982
|
+
...summary.repos.map((r) => ({
|
|
1983
|
+
value: r.name,
|
|
1984
|
+
label: r.name,
|
|
1985
|
+
hint: `${r.fileCount} files, ${formatSize(r.totalSize)}`
|
|
1986
|
+
})),
|
|
1987
|
+
{ value: "__back__", label: "Back" }
|
|
1988
|
+
]
|
|
1989
|
+
})
|
|
1990
|
+
);
|
|
1991
|
+
if (selected === "__back__") return;
|
|
1992
|
+
try {
|
|
1993
|
+
removeContextRepo(selected);
|
|
1994
|
+
p12.log.success(`Removed "${selected}"`);
|
|
1995
|
+
} catch (err) {
|
|
1996
|
+
p12.log.error(err.message);
|
|
1997
|
+
}
|
|
1998
|
+
} else if (action === "reindex") {
|
|
1999
|
+
if (summary.totalRepos === 0) {
|
|
2000
|
+
p12.log.warn("No context repos to reindex.");
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
try {
|
|
2004
|
+
await withSpinner(
|
|
2005
|
+
"Re-scanning documents...",
|
|
2006
|
+
async () => reindexContext(),
|
|
2007
|
+
"Reindex complete"
|
|
2008
|
+
);
|
|
2009
|
+
} catch (err) {
|
|
2010
|
+
p12.log.error(err.message);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
1700
2014
|
function listFlow() {
|
|
1701
2015
|
const lockfile = readLockfile();
|
|
1702
2016
|
const entries = Object.entries(lockfile.packages);
|
|
@@ -1738,6 +2052,7 @@ var init_interactive = __esm({
|
|
|
1738
2052
|
init_installer();
|
|
1739
2053
|
init_lockfile();
|
|
1740
2054
|
init_doctor();
|
|
2055
|
+
init_context();
|
|
1741
2056
|
CATEGORIES2 = [
|
|
1742
2057
|
"frontend",
|
|
1743
2058
|
"backend",
|
|
@@ -1754,7 +2069,7 @@ var init_interactive = __esm({
|
|
|
1754
2069
|
});
|
|
1755
2070
|
|
|
1756
2071
|
// src/index.ts
|
|
1757
|
-
import { Command as
|
|
2072
|
+
import { Command as Command17 } from "commander";
|
|
1758
2073
|
|
|
1759
2074
|
// src/commands/install.ts
|
|
1760
2075
|
init_installer();
|
|
@@ -3243,10 +3558,95 @@ var snapshotCommand = new Command15("snapshot").description("Analyze the current
|
|
|
3243
3558
|
}
|
|
3244
3559
|
});
|
|
3245
3560
|
|
|
3561
|
+
// src/commands/context.ts
|
|
3562
|
+
init_context();
|
|
3563
|
+
init_logger();
|
|
3564
|
+
init_prompts();
|
|
3565
|
+
import { Command as Command16 } from "commander";
|
|
3566
|
+
var contextCommand = new Command16("context").description("Manage project document context for AI");
|
|
3567
|
+
contextCommand.command("add <path>").description("Add a document directory to the project context").option("--name <name>", "Human-readable label for this directory").action(async (dirPath, options) => {
|
|
3568
|
+
try {
|
|
3569
|
+
const interactive = isInteractive();
|
|
3570
|
+
if (interactive) {
|
|
3571
|
+
await withSpinner(
|
|
3572
|
+
"Indexing documents...",
|
|
3573
|
+
async () => addContextRepo(dirPath, { name: options.name }),
|
|
3574
|
+
"Indexing complete"
|
|
3575
|
+
);
|
|
3576
|
+
} else {
|
|
3577
|
+
logger.blank();
|
|
3578
|
+
addContextRepo(dirPath, { name: options.name });
|
|
3579
|
+
logger.blank();
|
|
3580
|
+
}
|
|
3581
|
+
} catch (err) {
|
|
3582
|
+
logger.error(err.message);
|
|
3583
|
+
process.exit(1);
|
|
3584
|
+
}
|
|
3585
|
+
});
|
|
3586
|
+
contextCommand.command("remove <path-or-name>").description("Remove a directory from the project context").action((pathOrName) => {
|
|
3587
|
+
try {
|
|
3588
|
+
logger.blank();
|
|
3589
|
+
removeContextRepo(pathOrName);
|
|
3590
|
+
logger.blank();
|
|
3591
|
+
} catch (err) {
|
|
3592
|
+
logger.error(err.message);
|
|
3593
|
+
process.exit(1);
|
|
3594
|
+
}
|
|
3595
|
+
});
|
|
3596
|
+
contextCommand.command("list").description("Show all directories in the project context").option("--json", "Output as JSON").action((options) => {
|
|
3597
|
+
try {
|
|
3598
|
+
const summary = getContextSummary();
|
|
3599
|
+
if (options.json) {
|
|
3600
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3603
|
+
logger.blank();
|
|
3604
|
+
if (summary.totalRepos === 0) {
|
|
3605
|
+
logger.info("No context repos configured. Run `planmode context add <path>` to add one.");
|
|
3606
|
+
logger.blank();
|
|
3607
|
+
return;
|
|
3608
|
+
}
|
|
3609
|
+
logger.bold(`${summary.totalRepos} context repo(s) \u2014 ${summary.totalFiles} file(s), ${formatSize(summary.totalSize)}`);
|
|
3610
|
+
logger.blank();
|
|
3611
|
+
for (const repo of summary.repos) {
|
|
3612
|
+
logger.info(`${repo.name}`);
|
|
3613
|
+
logger.dim(` Path: ${repo.path}`);
|
|
3614
|
+
logger.dim(` Files: ${repo.fileCount} (${formatSize(repo.totalSize)})`);
|
|
3615
|
+
if (repo.typeBreakdown.length > 0) {
|
|
3616
|
+
logger.dim(` Types: ${repo.typeBreakdown.join(", ")}`);
|
|
3617
|
+
}
|
|
3618
|
+
logger.dim(` Indexed: ${repo.indexedAt}`);
|
|
3619
|
+
logger.blank();
|
|
3620
|
+
}
|
|
3621
|
+
} catch (err) {
|
|
3622
|
+
logger.error(err.message);
|
|
3623
|
+
process.exit(1);
|
|
3624
|
+
}
|
|
3625
|
+
});
|
|
3626
|
+
contextCommand.command("reindex [path-or-name]").description("Re-scan files in one or all context directories").action(async (pathOrName) => {
|
|
3627
|
+
try {
|
|
3628
|
+
const interactive = isInteractive();
|
|
3629
|
+
if (interactive) {
|
|
3630
|
+
await withSpinner(
|
|
3631
|
+
"Re-scanning documents...",
|
|
3632
|
+
async () => reindexContext(pathOrName),
|
|
3633
|
+
"Reindex complete"
|
|
3634
|
+
);
|
|
3635
|
+
} else {
|
|
3636
|
+
logger.blank();
|
|
3637
|
+
reindexContext(pathOrName);
|
|
3638
|
+
logger.blank();
|
|
3639
|
+
}
|
|
3640
|
+
} catch (err) {
|
|
3641
|
+
logger.error(err.message);
|
|
3642
|
+
process.exit(1);
|
|
3643
|
+
}
|
|
3644
|
+
});
|
|
3645
|
+
|
|
3246
3646
|
// src/index.ts
|
|
3247
3647
|
init_prompts();
|
|
3248
|
-
var program = new
|
|
3249
|
-
program.name("planmode").description("The open source package manager for AI plans, rules, and prompts.").version("0.
|
|
3648
|
+
var program = new Command17();
|
|
3649
|
+
program.name("planmode").description("The open source package manager for AI plans, rules, and prompts.").version("0.4.0");
|
|
3250
3650
|
program.addCommand(installCommand);
|
|
3251
3651
|
program.addCommand(uninstallCommand);
|
|
3252
3652
|
program.addCommand(searchCommand);
|
|
@@ -3262,6 +3662,7 @@ program.addCommand(doctorCommand);
|
|
|
3262
3662
|
program.addCommand(testCommand);
|
|
3263
3663
|
program.addCommand(recordCommand);
|
|
3264
3664
|
program.addCommand(snapshotCommand);
|
|
3665
|
+
program.addCommand(contextCommand);
|
|
3265
3666
|
if (process.argv.length <= 2 && isInteractive()) {
|
|
3266
3667
|
const { runInteractiveMenu: runInteractiveMenu2 } = await Promise.resolve().then(() => (init_interactive(), interactive_exports));
|
|
3267
3668
|
runInteractiveMenu2();
|