agendex-cli 0.13.0 → 0.14.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/cli.js +427 -33
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2256,6 +2256,16 @@ async function promptForAdapterSelection(options = {}) {
|
|
|
2256
2256
|
|
|
2257
2257
|
// ../shared/src/config.ts
|
|
2258
2258
|
var devModeOverride;
|
|
2259
|
+
function getHomeDir() {
|
|
2260
|
+
if (process.env.HOME)
|
|
2261
|
+
return process.env.HOME;
|
|
2262
|
+
if (process.env.USERPROFILE)
|
|
2263
|
+
return process.env.USERPROFILE;
|
|
2264
|
+
if (process.env.HOMEDRIVE && process.env.HOMEPATH) {
|
|
2265
|
+
return `${process.env.HOMEDRIVE}${process.env.HOMEPATH}`;
|
|
2266
|
+
}
|
|
2267
|
+
return homedir7();
|
|
2268
|
+
}
|
|
2259
2269
|
function setDevMode(dev) {
|
|
2260
2270
|
devModeOverride = dev;
|
|
2261
2271
|
}
|
|
@@ -2265,7 +2275,7 @@ function isDevMode() {
|
|
|
2265
2275
|
return process.env.AGENDEX_DEV === "1";
|
|
2266
2276
|
}
|
|
2267
2277
|
function getConfigDir() {
|
|
2268
|
-
return join7(
|
|
2278
|
+
return join7(getHomeDir(), isDevMode() ? ".agendex-dev" : ".agendex");
|
|
2269
2279
|
}
|
|
2270
2280
|
function ensureConfigDir() {
|
|
2271
2281
|
const dir = getConfigDir();
|
|
@@ -2292,7 +2302,7 @@ function normalizeAdapterIds(input) {
|
|
|
2292
2302
|
}
|
|
2293
2303
|
function expandHomePath(p) {
|
|
2294
2304
|
if (p.startsWith("~/") || p === "~")
|
|
2295
|
-
return join7(
|
|
2305
|
+
return join7(getHomeDir(), p.slice(1));
|
|
2296
2306
|
return p;
|
|
2297
2307
|
}
|
|
2298
2308
|
function resolveCustomPlanDirPath(userPath) {
|
|
@@ -2440,8 +2450,335 @@ var CLI_DAEMON_STALE_AFTER_MS = CLI_DAEMON_HEARTBEAT_INTERVAL_MS * 5;
|
|
|
2440
2450
|
// ../shared/src/services/plan-service.ts
|
|
2441
2451
|
import { existsSync as existsSync4, readdirSync as readdirSync3, statSync } from "node:fs";
|
|
2442
2452
|
import { lstat, mkdir, readdir as readdir2, readFile as readFile6, stat as stat6, writeFile as writeFile3 } from "node:fs/promises";
|
|
2443
|
-
import { homedir as homedir8 } from "node:os";
|
|
2444
2453
|
import { join as join8, resolve as resolve3, sep as sep2 } from "node:path";
|
|
2454
|
+
|
|
2455
|
+
// ../shared/src/services/plan-value.ts
|
|
2456
|
+
function isLowValuePlan(plan) {
|
|
2457
|
+
const metadata = plan.metadata;
|
|
2458
|
+
return typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) && metadata.lowValue === true;
|
|
2459
|
+
}
|
|
2460
|
+
function isIndexablePlan(plan) {
|
|
2461
|
+
return !isLowValuePlan(plan);
|
|
2462
|
+
}
|
|
2463
|
+
var PROPOSED_PLAN_TAG_REGEX2 = /<\s*\/?\s*proposed_plan\s*>/gi;
|
|
2464
|
+
var ESCAPED_PROPOSED_PLAN_TAG_REGEX2 = /<\s*\/?\s*proposed_plan\s*>/gi;
|
|
2465
|
+
var VISIBLE_TEXT_REGEX = /[\p{L}\p{N}]/u;
|
|
2466
|
+
var LOW_VALUE_METADATA_KEYS = ["lowValue", "lowValueReasons", "lowValueSignals"];
|
|
2467
|
+
function normalizeLineEndings2(text) {
|
|
2468
|
+
return text.replace(/\r\n?/g, `
|
|
2469
|
+
`);
|
|
2470
|
+
}
|
|
2471
|
+
function stripBoundaryHtmlComments(text) {
|
|
2472
|
+
let next = text;
|
|
2473
|
+
let previous = "";
|
|
2474
|
+
while (next !== previous) {
|
|
2475
|
+
previous = next;
|
|
2476
|
+
next = next.replace(/^\s*<!--[\s\S]*?-->\s*/, "").replace(/\s*<!--[\s\S]*?-->\s*$/, "");
|
|
2477
|
+
}
|
|
2478
|
+
return next;
|
|
2479
|
+
}
|
|
2480
|
+
function normalizePlanContent(content) {
|
|
2481
|
+
return stripBoundaryHtmlComments(normalizeLineEndings2(content).replace(/^\uFEFF/, "").replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, "").replace(ESCAPED_PROPOSED_PLAN_TAG_REGEX2, "").replace(PROPOSED_PLAN_TAG_REGEX2, "")).trim();
|
|
2482
|
+
}
|
|
2483
|
+
function withoutLowValueMetadata(metadata) {
|
|
2484
|
+
const next = { ...metadata ?? {} };
|
|
2485
|
+
for (const key of LOW_VALUE_METADATA_KEYS)
|
|
2486
|
+
delete next[key];
|
|
2487
|
+
return next;
|
|
2488
|
+
}
|
|
2489
|
+
function unique(items) {
|
|
2490
|
+
return Array.from(new Set(items));
|
|
2491
|
+
}
|
|
2492
|
+
function visibleText(text) {
|
|
2493
|
+
return text.replace(/<!--[\s\S]*?-->/g, "").replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, "").replace(ESCAPED_PROPOSED_PLAN_TAG_REGEX2, "").replace(PROPOSED_PLAN_TAG_REGEX2, "").replace(/[`*_#[\](){}<>:|~\-+=.]/g, " ").trim();
|
|
2494
|
+
}
|
|
2495
|
+
function isSeparatorLine(line) {
|
|
2496
|
+
return /^(?:-{3,}|_{3,}|\*{3,})$/.test(line.trim());
|
|
2497
|
+
}
|
|
2498
|
+
function isFenceLine(line) {
|
|
2499
|
+
return /^(?:`{3,}|~{3,})/.test(line.trim());
|
|
2500
|
+
}
|
|
2501
|
+
function meaningfulLines(text) {
|
|
2502
|
+
return text.split(`
|
|
2503
|
+
`).map((line) => line.trim()).filter((line) => line && !isSeparatorLine(line) && !isFenceLine(line));
|
|
2504
|
+
}
|
|
2505
|
+
function cleanMarkdownLine(line) {
|
|
2506
|
+
return line.trim().replace(/^>+\s*/, "").replace(/^#{1,6}\s+/, "").replace(/^[-*+]\s+/, "").replace(/^\d+[.)]\s+/, "").replace(/^\[[ xX]\]\s+/, "").replace(/^\*\*|\*\*$/g, "").replace(/^`|`$/g, "").trim();
|
|
2507
|
+
}
|
|
2508
|
+
function isHeadingLine(line) {
|
|
2509
|
+
return /^#{1,6}\s+\S/.test(line.trim());
|
|
2510
|
+
}
|
|
2511
|
+
function isChecklistLine(line) {
|
|
2512
|
+
return /^[-*+]\s+\[[ xX]\]\s+\S/.test(line.trim());
|
|
2513
|
+
}
|
|
2514
|
+
function isOrderedListLine(line) {
|
|
2515
|
+
return /^\d+[.)]\s+\S/.test(line.trim());
|
|
2516
|
+
}
|
|
2517
|
+
function isHeadingOnly(lines) {
|
|
2518
|
+
return lines.length > 0 && lines.some(isHeadingLine) && lines.every(isHeadingLine);
|
|
2519
|
+
}
|
|
2520
|
+
function metadataHasPlanBlocks(metadata) {
|
|
2521
|
+
const planBlocks = metadata?.planBlocks;
|
|
2522
|
+
return typeof planBlocks === "number" && planBlocks > 0;
|
|
2523
|
+
}
|
|
2524
|
+
function sectionName(line) {
|
|
2525
|
+
return cleanMarkdownLine(line).replace(/:$/, "").toLowerCase().replace(/\s+/g, " ");
|
|
2526
|
+
}
|
|
2527
|
+
var SECTION_LABELS = new Set([
|
|
2528
|
+
"context",
|
|
2529
|
+
"background",
|
|
2530
|
+
"problem",
|
|
2531
|
+
"goal",
|
|
2532
|
+
"goals",
|
|
2533
|
+
"scope",
|
|
2534
|
+
"approach",
|
|
2535
|
+
"strategy",
|
|
2536
|
+
"design",
|
|
2537
|
+
"implementation plan",
|
|
2538
|
+
"implementation",
|
|
2539
|
+
"plan",
|
|
2540
|
+
"files to modify",
|
|
2541
|
+
"files changed",
|
|
2542
|
+
"affected files",
|
|
2543
|
+
"steps",
|
|
2544
|
+
"step",
|
|
2545
|
+
"tasks",
|
|
2546
|
+
"task",
|
|
2547
|
+
"checklist",
|
|
2548
|
+
"todo",
|
|
2549
|
+
"todos",
|
|
2550
|
+
"verification",
|
|
2551
|
+
"testing",
|
|
2552
|
+
"tests",
|
|
2553
|
+
"test",
|
|
2554
|
+
"validation",
|
|
2555
|
+
"reuse",
|
|
2556
|
+
"existing utilities",
|
|
2557
|
+
"existing code",
|
|
2558
|
+
"acceptance criteria",
|
|
2559
|
+
"success criteria"
|
|
2560
|
+
]);
|
|
2561
|
+
function hasSectionSyntax(line, section) {
|
|
2562
|
+
const cleaned = cleanMarkdownLine(line);
|
|
2563
|
+
return isHeadingLine(line) || /^[a-z][\w\s/&+-]{1,48}:/i.test(cleaned) || SECTION_LABELS.has(section);
|
|
2564
|
+
}
|
|
2565
|
+
function collectPositiveSignals(normalized, lines, metadata) {
|
|
2566
|
+
const signals = [];
|
|
2567
|
+
if (metadataHasPlanBlocks(metadata))
|
|
2568
|
+
signals.push("metadata:proposed-plan-block");
|
|
2569
|
+
for (const line of lines) {
|
|
2570
|
+
const section = sectionName(line);
|
|
2571
|
+
if (!hasSectionSyntax(line, section))
|
|
2572
|
+
continue;
|
|
2573
|
+
if (/^(context|background|problem|goal|goals|scope)\b/.test(section)) {
|
|
2574
|
+
signals.push("section:context");
|
|
2575
|
+
continue;
|
|
2576
|
+
}
|
|
2577
|
+
if (/^(approach|strategy|design)\b/.test(section)) {
|
|
2578
|
+
signals.push("section:approach");
|
|
2579
|
+
continue;
|
|
2580
|
+
}
|
|
2581
|
+
if (/^(implementation plan|implementation|plan)\b/.test(section)) {
|
|
2582
|
+
signals.push("section:implementation-plan");
|
|
2583
|
+
continue;
|
|
2584
|
+
}
|
|
2585
|
+
if (/^(files? to modify|files? changed|affected files?)\b/.test(section)) {
|
|
2586
|
+
signals.push("section:files-to-modify");
|
|
2587
|
+
continue;
|
|
2588
|
+
}
|
|
2589
|
+
if (/^(steps?|tasks?|checklist|todo|todos)\b/.test(section)) {
|
|
2590
|
+
signals.push("section:steps");
|
|
2591
|
+
continue;
|
|
2592
|
+
}
|
|
2593
|
+
if (/^(verification|testing|tests?|validation)\b/.test(section)) {
|
|
2594
|
+
signals.push("section:verification");
|
|
2595
|
+
continue;
|
|
2596
|
+
}
|
|
2597
|
+
if (/^(reuse|existing utilities|existing code)\b/.test(section)) {
|
|
2598
|
+
signals.push("section:reuse");
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
if (/^(acceptance criteria|success criteria)\b/.test(section)) {
|
|
2602
|
+
signals.push("section:acceptance-criteria");
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
if (lines.some(isChecklistLine))
|
|
2606
|
+
signals.push("checklist");
|
|
2607
|
+
const orderedStepCount = lines.filter(isOrderedListLine).length;
|
|
2608
|
+
if (orderedStepCount >= 2)
|
|
2609
|
+
signals.push("ordered-steps");
|
|
2610
|
+
const actionBulletCount = lines.filter((line) => {
|
|
2611
|
+
const trimmed = line.trim();
|
|
2612
|
+
if (!/^(?:[-*+]|\d+[.)])\s+/.test(trimmed))
|
|
2613
|
+
return false;
|
|
2614
|
+
const cleaned = cleanMarkdownLine(trimmed);
|
|
2615
|
+
return /^(?:add|implement|update|modify|create|remove|delete|refactor|test|verify|run|wire|persist|handle|ensure|document|rename|move|extract|reuse|validate)\b/i.test(cleaned);
|
|
2616
|
+
}).length;
|
|
2617
|
+
if (actionBulletCount >= 2)
|
|
2618
|
+
signals.push("action-bullets");
|
|
2619
|
+
if (lines.length >= 2 && /\b(?:will|should|need to|needs to|plan to|planned|approach is to|implementation will)\b/i.test(normalized)) {
|
|
2620
|
+
signals.push("future-plan-language");
|
|
2621
|
+
}
|
|
2622
|
+
return unique(signals);
|
|
2623
|
+
}
|
|
2624
|
+
function isStrongPositiveSignal(signal) {
|
|
2625
|
+
return signal === "metadata:proposed-plan-block" || signal === "checklist" || signal === "ordered-steps" || signal === "action-bullets" || signal === "section:approach" || signal === "section:implementation-plan" || signal === "section:files-to-modify" || signal === "section:steps" || signal === "section:acceptance-criteria";
|
|
2626
|
+
}
|
|
2627
|
+
function hasStrongPlanSignal(positiveSignals) {
|
|
2628
|
+
const sectionCount = positiveSignals.filter((signal) => signal.startsWith("section:")).length;
|
|
2629
|
+
return positiveSignals.some(isStrongPositiveSignal) || sectionCount >= 2;
|
|
2630
|
+
}
|
|
2631
|
+
function isPromptLikeOneLiner(line) {
|
|
2632
|
+
if (isHeadingLine(line) || isChecklistLine(line) || isOrderedListLine(line))
|
|
2633
|
+
return false;
|
|
2634
|
+
const cleaned = cleanMarkdownLine(line).toLowerCase();
|
|
2635
|
+
if (!cleaned)
|
|
2636
|
+
return false;
|
|
2637
|
+
return [
|
|
2638
|
+
/^(?:important:\s*)?work in\b/,
|
|
2639
|
+
/^(?:please|pls)\b/,
|
|
2640
|
+
/^(?:can|could|would|will)\s+you\b/,
|
|
2641
|
+
/^(?:i|we)\s+(?:need|want|would like|have to)\b/,
|
|
2642
|
+
/^(?:help|fix|implement|create|add|update|remove|delete|refactor|write|review|investigate|debug|plan)\b/,
|
|
2643
|
+
/\?$/,
|
|
2644
|
+
/\b(?:repository|repo|existing branch|worktree|pull request|pr)\b/
|
|
2645
|
+
].some((pattern) => pattern.test(cleaned));
|
|
2646
|
+
}
|
|
2647
|
+
function normalizedTitle(title) {
|
|
2648
|
+
return cleanMarkdownLine(title ?? "").toLowerCase();
|
|
2649
|
+
}
|
|
2650
|
+
function looksLikeWrapperTitle(title) {
|
|
2651
|
+
return /^<user_(?:action|instructions|prompt)>$/i.test((title ?? "").trim());
|
|
2652
|
+
}
|
|
2653
|
+
function looksLikePromptTitle(title) {
|
|
2654
|
+
const cleaned = normalizedTitle(title);
|
|
2655
|
+
if (!cleaned)
|
|
2656
|
+
return false;
|
|
2657
|
+
return [
|
|
2658
|
+
/^(?:important:\s*)?work in\b/,
|
|
2659
|
+
/^review the code changes against\b/,
|
|
2660
|
+
/^perform a .*review\b/,
|
|
2661
|
+
/^(?:please|pls)\b/,
|
|
2662
|
+
/^(?:can|could|would|will)\s+you\b/,
|
|
2663
|
+
/^(?:i|we)\s+(?:need|want|would like|have to)\b/,
|
|
2664
|
+
/^(?:help|fix|implement|create|add|update|remove|delete|refactor|write|review|investigate|debug|plan)\b/,
|
|
2665
|
+
/\?$/,
|
|
2666
|
+
/\b(?:repository|repo|existing branch|worktree|pull request|pr)\b/
|
|
2667
|
+
].some((pattern) => pattern.test(cleaned));
|
|
2668
|
+
}
|
|
2669
|
+
function looksLikeReviewOutput(normalized, title) {
|
|
2670
|
+
const cleanedTitle = normalizedTitle(title);
|
|
2671
|
+
const lower = normalized.toLowerCase();
|
|
2672
|
+
return /^review the code changes against\b/.test(cleanedTitle) || /^perform a .*review\b/.test(cleanedTitle) || /"findings"\s*:\s*\[/.test(normalized) || /"overall_correctness"\s*:/.test(normalized) || /\bfull review comments\s*:/i.test(normalized) || /\bthe patch (?:currently )?(?:breaks|introduces|regresses)\b/i.test(normalized) || /\bshould not be considered correct\b/i.test(lower);
|
|
2673
|
+
}
|
|
2674
|
+
function looksLikeSystemContext(normalized, lines) {
|
|
2675
|
+
const lower = normalized.toLowerCase();
|
|
2676
|
+
if (lower.startsWith("# agents.md instructions") || lower.startsWith("<environment_context>") || lower.startsWith("<system-reminder>") || lower.startsWith("<thinking>")) {
|
|
2677
|
+
return true;
|
|
2678
|
+
}
|
|
2679
|
+
const wrapperMatches = normalized.match(/<\/?(?:environment_context|system-reminder|thinking|analysis|reasoning|tool_call|tool_result)\b[^>]*>/gi);
|
|
2680
|
+
if (wrapperMatches && wrapperMatches.length >= 2)
|
|
2681
|
+
return true;
|
|
2682
|
+
const wrapperLineCount = lines.filter((line) => /^(?:analysis|reasoning|thought|assistant thought|system|developer):\b/i.test(line)).length;
|
|
2683
|
+
return wrapperLineCount > 0 && wrapperLineCount / Math.max(lines.length, 1) >= 0.4;
|
|
2684
|
+
}
|
|
2685
|
+
function looksLikeToolLog(normalized) {
|
|
2686
|
+
return /::[a-z0-9_-]+(?:\{|\[|\s*$)/i.test(normalized) || /<\/?(?:tool_call|tool_result)\b[^>]*>/i.test(normalized) || /\b(?:function_call|tool_calls|tool_result)\b/i.test(normalized);
|
|
2687
|
+
}
|
|
2688
|
+
function looksLikeExecutionReport(normalized) {
|
|
2689
|
+
const hasPastCompletion = /\b(?:fixed|pushed|committed|completed|done|implemented|updated|changed|patched|merged|deployed|passed|failed|resolved|reverted)\b/i.test(normalized);
|
|
2690
|
+
const hasReportSection = /^\s*(?:summary|result|results|changes|verification|status)\s*:/im.test(normalized);
|
|
2691
|
+
const hasReviewReportMarker = /\b(?:review findings?|review issues?|review comments?)\b/i.test(normalized);
|
|
2692
|
+
const hasCommandMarker = /::[a-z0-9_-]+(?:\{|\[|\s*$)/im.test(normalized) || /`[^`]*(?:bun|npm|pnpm|yarn|git|tsc|biome)[^`]*`/i.test(normalized) || /\b(?:git\s+(?:stage|commit|push|status)|bunx?\s+|npm\s+|pnpm\s+|yarn\s+)\b/i.test(normalized);
|
|
2693
|
+
return hasPastCompletion && (hasReportSection || hasCommandMarker || hasReviewReportMarker);
|
|
2694
|
+
}
|
|
2695
|
+
function lowValueAssessment(reasons, signals) {
|
|
2696
|
+
return {
|
|
2697
|
+
lowValue: reasons.length > 0,
|
|
2698
|
+
reasons: unique(reasons),
|
|
2699
|
+
signals: unique(signals).slice(0, 20)
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2702
|
+
function assessPlanValue(input) {
|
|
2703
|
+
const metadata = withoutLowValueMetadata(input.metadata);
|
|
2704
|
+
const normalized = normalizePlanContent(input.content);
|
|
2705
|
+
const lines = meaningfulLines(normalized);
|
|
2706
|
+
const positiveSignals = collectPositiveSignals(normalized, lines, metadata);
|
|
2707
|
+
const signals = [...positiveSignals];
|
|
2708
|
+
const reasons = [];
|
|
2709
|
+
if (!VISIBLE_TEXT_REGEX.test(visibleText(normalized))) {
|
|
2710
|
+
return lowValueAssessment(["empty-content"], ["negative:empty-content"]);
|
|
2711
|
+
}
|
|
2712
|
+
if (isHeadingOnly(lines)) {
|
|
2713
|
+
return lowValueAssessment(["heading-only"], [...signals, "negative:heading-only"]);
|
|
2714
|
+
}
|
|
2715
|
+
const explicitPlanBlock = metadataHasPlanBlocks(metadata);
|
|
2716
|
+
const strongPositive = hasStrongPlanSignal(positiveSignals);
|
|
2717
|
+
const systemContext = looksLikeSystemContext(normalized, lines);
|
|
2718
|
+
const toolLog = looksLikeToolLog(normalized);
|
|
2719
|
+
const executionReport = looksLikeExecutionReport(normalized);
|
|
2720
|
+
const wrapperTitle = looksLikeWrapperTitle(input.title);
|
|
2721
|
+
const promptTitle = looksLikePromptTitle(input.title);
|
|
2722
|
+
const reviewOutput = looksLikeReviewOutput(normalized, input.title);
|
|
2723
|
+
if (systemContext)
|
|
2724
|
+
signals.push("negative:system-context");
|
|
2725
|
+
if (toolLog)
|
|
2726
|
+
signals.push("negative:tool-log");
|
|
2727
|
+
if (executionReport)
|
|
2728
|
+
signals.push("negative:execution-report");
|
|
2729
|
+
if (wrapperTitle)
|
|
2730
|
+
signals.push("negative:wrapper-title");
|
|
2731
|
+
if (promptTitle)
|
|
2732
|
+
signals.push("negative:prompt-title");
|
|
2733
|
+
if (reviewOutput)
|
|
2734
|
+
signals.push("negative:review-output");
|
|
2735
|
+
if (lines.length === 1)
|
|
2736
|
+
signals.push("shape:single-line");
|
|
2737
|
+
if (lines.length === 1 && positiveSignals.length === 0) {
|
|
2738
|
+
reasons.push(isPromptLikeOneLiner(lines[0] ?? "") ? "prompt-like" : "no-plan-signals");
|
|
2739
|
+
}
|
|
2740
|
+
if (systemContext && !strongPositive)
|
|
2741
|
+
reasons.push("system-context");
|
|
2742
|
+
if (executionReport && !strongPositive)
|
|
2743
|
+
reasons.push("execution-report");
|
|
2744
|
+
if (wrapperTitle && !explicitPlanBlock)
|
|
2745
|
+
reasons.push("wrapper-title");
|
|
2746
|
+
if (reviewOutput && !explicitPlanBlock)
|
|
2747
|
+
reasons.push("review-output");
|
|
2748
|
+
if (promptTitle && !strongPositive && positiveSignals.length === 0)
|
|
2749
|
+
reasons.push("prompt-like");
|
|
2750
|
+
if (toolLog && !strongPositive && positiveSignals.length === 0)
|
|
2751
|
+
reasons.push("no-plan-signals");
|
|
2752
|
+
if (reasons.length === 0 && positiveSignals.length === 0 && lines.length <= 3) {
|
|
2753
|
+
reasons.push("no-plan-signals");
|
|
2754
|
+
}
|
|
2755
|
+
return lowValueAssessment(reasons, signals);
|
|
2756
|
+
}
|
|
2757
|
+
function annotatePlanValueMetadata(plan) {
|
|
2758
|
+
const baseMetadata = withoutLowValueMetadata(plan.metadata);
|
|
2759
|
+
const assessment = assessPlanValue({
|
|
2760
|
+
content: plan.content,
|
|
2761
|
+
title: plan.title,
|
|
2762
|
+
metadata: baseMetadata
|
|
2763
|
+
});
|
|
2764
|
+
if (!assessment.lowValue) {
|
|
2765
|
+
return {
|
|
2766
|
+
...plan,
|
|
2767
|
+
metadata: baseMetadata
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
return {
|
|
2771
|
+
...plan,
|
|
2772
|
+
metadata: {
|
|
2773
|
+
...baseMetadata,
|
|
2774
|
+
lowValue: true,
|
|
2775
|
+
lowValueReasons: assessment.reasons,
|
|
2776
|
+
lowValueSignals: assessment.signals
|
|
2777
|
+
}
|
|
2778
|
+
};
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
// ../shared/src/services/plan-service.ts
|
|
2445
2782
|
function getUserPlansDir() {
|
|
2446
2783
|
return join8(getConfigDir(), "plans");
|
|
2447
2784
|
}
|
|
@@ -2482,7 +2819,7 @@ var SKIP_DIRS = new Set([
|
|
|
2482
2819
|
"iCloud Drive"
|
|
2483
2820
|
]);
|
|
2484
2821
|
function discoverProjectPlanDirs() {
|
|
2485
|
-
const home2 =
|
|
2822
|
+
const home2 = getHomeDir();
|
|
2486
2823
|
const results = [];
|
|
2487
2824
|
function walk(dir, depth) {
|
|
2488
2825
|
if (depth > DISCOVERY_MAX_DEPTH)
|
|
@@ -2663,7 +3000,8 @@ async function scan() {
|
|
|
2663
3000
|
continue;
|
|
2664
3001
|
const plans = await adapter.parse(file);
|
|
2665
3002
|
for (const plan of plans) {
|
|
2666
|
-
|
|
3003
|
+
const annotated = annotatePlanValueMetadata(plan);
|
|
3004
|
+
next.set(annotated.id, annotated);
|
|
2667
3005
|
}
|
|
2668
3006
|
}
|
|
2669
3007
|
}
|
|
@@ -2682,7 +3020,8 @@ async function scan() {
|
|
|
2682
3020
|
continue;
|
|
2683
3021
|
const plans = await adapter.parse(file);
|
|
2684
3022
|
for (const plan of plans) {
|
|
2685
|
-
|
|
3023
|
+
const annotated = annotatePlanValueMetadata(plan);
|
|
3024
|
+
next.set(annotated.id, annotated);
|
|
2686
3025
|
}
|
|
2687
3026
|
}
|
|
2688
3027
|
coveredPaths.add(resolvedDir);
|
|
@@ -2692,11 +3031,17 @@ async function scan() {
|
|
|
2692
3031
|
await scanCustomPlanDirs(coveredPaths, next);
|
|
2693
3032
|
store = next;
|
|
2694
3033
|
notifyPlansChanged();
|
|
2695
|
-
|
|
3034
|
+
const indexableCount = getIndexablePlans().length;
|
|
3035
|
+
const hiddenCount = store.size - indexableCount;
|
|
3036
|
+
const hiddenSuffix = hiddenCount > 0 ? ` (${hiddenCount} hidden as low-value)` : "";
|
|
3037
|
+
console.log(`[agendex] indexed ${indexableCount} plans${hiddenSuffix} from ${adapters.length} adapters`);
|
|
2696
3038
|
}
|
|
2697
3039
|
function getAll() {
|
|
2698
3040
|
return Array.from(store.values());
|
|
2699
3041
|
}
|
|
3042
|
+
function getIndexablePlans() {
|
|
3043
|
+
return getAll().filter(isIndexablePlan);
|
|
3044
|
+
}
|
|
2700
3045
|
async function rescanFile(filePath) {
|
|
2701
3046
|
const adapters = getActiveAdapters();
|
|
2702
3047
|
const normalized = resolve3(filePath);
|
|
@@ -2711,7 +3056,7 @@ async function rescanFile(filePath) {
|
|
|
2711
3056
|
const isInSearchPath = allSearchPaths.some((sp) => normalized.startsWith(sp + sep2) || normalized === sp);
|
|
2712
3057
|
if (!isInSearchPath)
|
|
2713
3058
|
continue;
|
|
2714
|
-
const plans = await adapter.parse(filePath);
|
|
3059
|
+
const plans = (await adapter.parse(filePath)).map(annotatePlanValueMetadata);
|
|
2715
3060
|
for (const plan of plans) {
|
|
2716
3061
|
store.set(plan.id, plan);
|
|
2717
3062
|
}
|
|
@@ -2909,6 +3254,22 @@ function getCloudConfig() {
|
|
|
2909
3254
|
convexUrl: config.convexUrl
|
|
2910
3255
|
};
|
|
2911
3256
|
}
|
|
3257
|
+
function parseSyncSuccess(body) {
|
|
3258
|
+
try {
|
|
3259
|
+
const parsed = JSON.parse(body);
|
|
3260
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
3261
|
+
return { ok: true };
|
|
3262
|
+
}
|
|
3263
|
+
const result = parsed;
|
|
3264
|
+
return {
|
|
3265
|
+
ok: true,
|
|
3266
|
+
skippedLowValue: result.skippedLowValue === true,
|
|
3267
|
+
deleted: result.deleted === true
|
|
3268
|
+
};
|
|
3269
|
+
} catch {
|
|
3270
|
+
return { ok: true };
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
2912
3273
|
async function syncPlan(plan) {
|
|
2913
3274
|
const { token, convexUrl } = getCloudConfig();
|
|
2914
3275
|
const url = `${convexUrl}/api/cli/sync`;
|
|
@@ -2940,7 +3301,7 @@ async function syncPlan(plan) {
|
|
|
2940
3301
|
if (res.status < 200 || res.status >= 300) {
|
|
2941
3302
|
return { ok: false, error: `${res.status}: ${res.body}` };
|
|
2942
3303
|
}
|
|
2943
|
-
return
|
|
3304
|
+
return parseSyncSuccess(res.body);
|
|
2944
3305
|
}
|
|
2945
3306
|
async function refreshStoredToken(currentToken, convexUrl) {
|
|
2946
3307
|
const refreshed = await refreshToken(currentToken, convexUrl);
|
|
@@ -2995,7 +3356,6 @@ async function sendHeartbeat() {
|
|
|
2995
3356
|
}
|
|
2996
3357
|
async function sendShutdown() {
|
|
2997
3358
|
try {
|
|
2998
|
-
const { token, convexUrl } = getCloudConfig();
|
|
2999
3359
|
cachedDeviceId ??= loadOrCreateDeviceId();
|
|
3000
3360
|
await deleteDaemons([cachedDeviceId]);
|
|
3001
3361
|
} catch {}
|
|
@@ -3413,6 +3773,8 @@ async function runWorker() {
|
|
|
3413
3773
|
syncing = true;
|
|
3414
3774
|
const batch = syncQueue.splice(0);
|
|
3415
3775
|
let syncedCount = 0;
|
|
3776
|
+
let lowValueSkippedCount = 0;
|
|
3777
|
+
let lowValueDeletedCount = 0;
|
|
3416
3778
|
let failedCount = 0;
|
|
3417
3779
|
try {
|
|
3418
3780
|
for (const payload of batch) {
|
|
@@ -3433,19 +3795,26 @@ async function runWorker() {
|
|
|
3433
3795
|
failedCount++;
|
|
3434
3796
|
console.error(`[agendex] sync failed for "${payload.title}": ${result.error}`);
|
|
3435
3797
|
} else {
|
|
3436
|
-
|
|
3798
|
+
if (result.skippedLowValue) {
|
|
3799
|
+
lowValueSkippedCount++;
|
|
3800
|
+
if (result.deleted)
|
|
3801
|
+
lowValueDeletedCount++;
|
|
3802
|
+
} else {
|
|
3803
|
+
syncedCount++;
|
|
3804
|
+
}
|
|
3437
3805
|
syncCache[payload.localPlanId] = computePayloadHash(payload);
|
|
3438
3806
|
}
|
|
3439
3807
|
}
|
|
3440
3808
|
} catch (err) {
|
|
3441
3809
|
console.error("[agendex] sync error:", err);
|
|
3442
|
-
syncQueue.unshift(...batch.slice(syncedCount + failedCount));
|
|
3810
|
+
syncQueue.unshift(...batch.slice(syncedCount + lowValueSkippedCount + failedCount));
|
|
3443
3811
|
} finally {
|
|
3444
3812
|
syncing = false;
|
|
3445
3813
|
}
|
|
3446
|
-
if (syncedCount > 0 || failedCount > 0) {
|
|
3814
|
+
if (syncedCount > 0 || lowValueSkippedCount > 0 || failedCount > 0) {
|
|
3447
3815
|
saveSyncCache(syncCache);
|
|
3448
|
-
|
|
3816
|
+
const lowValueSuffix2 = lowValueSkippedCount > 0 ? `, ${lowValueSkippedCount} low-value skipped/pruned${lowValueDeletedCount > 0 ? ` (${lowValueDeletedCount} deleted)` : ""}` : "";
|
|
3817
|
+
console.log(`[agendex] sync complete: ${syncedCount} synced${lowValueSuffix2}, ${failedCount} failed`);
|
|
3449
3818
|
}
|
|
3450
3819
|
if (syncQueue.length > 0)
|
|
3451
3820
|
processSyncQueue();
|
|
@@ -3454,7 +3823,11 @@ async function runWorker() {
|
|
|
3454
3823
|
console.log(`[agendex] initial scan...`);
|
|
3455
3824
|
await scan();
|
|
3456
3825
|
const plans = getAll();
|
|
3826
|
+
const syncablePlanCount = plans.filter(isIndexablePlan).length;
|
|
3827
|
+
const lowValuePlanCount = plans.length - syncablePlanCount;
|
|
3457
3828
|
let initialSkipped = 0;
|
|
3829
|
+
let initialQueuedSyncable = 0;
|
|
3830
|
+
let initialQueuedLowValue = 0;
|
|
3458
3831
|
for (const plan of plans) {
|
|
3459
3832
|
const payload = planToPayload(plan);
|
|
3460
3833
|
const hash = computePayloadHash(payload);
|
|
@@ -3462,6 +3835,11 @@ async function runWorker() {
|
|
|
3462
3835
|
initialSkipped++;
|
|
3463
3836
|
continue;
|
|
3464
3837
|
}
|
|
3838
|
+
if (isLowValuePlan(plan)) {
|
|
3839
|
+
initialQueuedLowValue++;
|
|
3840
|
+
} else {
|
|
3841
|
+
initialQueuedSyncable++;
|
|
3842
|
+
}
|
|
3465
3843
|
syncQueue.push(payload);
|
|
3466
3844
|
}
|
|
3467
3845
|
const activePlanIds = new Set(plans.map((plan) => plan.id));
|
|
@@ -3470,7 +3848,8 @@ async function runWorker() {
|
|
|
3470
3848
|
delete syncCache[id];
|
|
3471
3849
|
}
|
|
3472
3850
|
saveSyncCache(syncCache, { replace: true });
|
|
3473
|
-
|
|
3851
|
+
const lowValueSuffix = lowValuePlanCount > 0 ? `, ${lowValuePlanCount} low-value hidden/pruned` : "";
|
|
3852
|
+
console.log(`[agendex] syncing ${initialQueuedSyncable} plans${lowValueSuffix} (${initialQueuedLowValue} low-value queued, ${initialSkipped} unchanged)...`);
|
|
3474
3853
|
await processSyncQueue();
|
|
3475
3854
|
setInterval(() => void sendHeartbeat(), CLI_DAEMON_HEARTBEAT_INTERVAL_MS);
|
|
3476
3855
|
startWatching((changedPlans) => {
|
|
@@ -3538,33 +3917,41 @@ async function startSupervisor() {
|
|
|
3538
3917
|
}
|
|
3539
3918
|
|
|
3540
3919
|
// src/sync.ts
|
|
3920
|
+
function planToPayload2(plan) {
|
|
3921
|
+
return {
|
|
3922
|
+
localPlanId: plan.id,
|
|
3923
|
+
agent: plan.agent,
|
|
3924
|
+
title: plan.title,
|
|
3925
|
+
content: plan.content,
|
|
3926
|
+
format: plan.format,
|
|
3927
|
+
filePath: plan.filePath,
|
|
3928
|
+
workspace: plan.workspace,
|
|
3929
|
+
metadata: plan.metadata,
|
|
3930
|
+
createdAt: plan.createdAt.getTime(),
|
|
3931
|
+
updatedAt: plan.updatedAt.getTime()
|
|
3932
|
+
};
|
|
3933
|
+
}
|
|
3541
3934
|
async function syncAll(force = false) {
|
|
3542
3935
|
const config = await loadOrInitConfig();
|
|
3543
3936
|
const adapters = resolveAdapters(config.enabledAdapters);
|
|
3544
3937
|
setActiveAdapters(adapters);
|
|
3545
3938
|
console.log(`[agendex] Scanning local plans...`);
|
|
3546
3939
|
await scan();
|
|
3547
|
-
const
|
|
3548
|
-
|
|
3940
|
+
const allPlans = getAll();
|
|
3941
|
+
const syncablePlans = allPlans.filter(isIndexablePlan);
|
|
3942
|
+
const lowValuePlans = allPlans.filter(isLowValuePlan);
|
|
3943
|
+
const hiddenSuffix = lowValuePlans.length > 0 ? ` (${lowValuePlans.length} low-value hidden/pruned)` : "";
|
|
3944
|
+
console.log(`[agendex] Found ${syncablePlans.length} syncable plans${hiddenSuffix}. Syncing to cloud...`);
|
|
3549
3945
|
const cache = force ? {} : loadSyncCache();
|
|
3550
3946
|
const activePlanIds = new Set;
|
|
3551
3947
|
let synced = 0;
|
|
3948
|
+
let lowValueSkipped = 0;
|
|
3949
|
+
let lowValueDeleted = 0;
|
|
3552
3950
|
let skipped = 0;
|
|
3553
3951
|
let failed = 0;
|
|
3554
|
-
for (const plan of
|
|
3952
|
+
for (const plan of [...syncablePlans, ...lowValuePlans]) {
|
|
3555
3953
|
activePlanIds.add(plan.id);
|
|
3556
|
-
const payload =
|
|
3557
|
-
localPlanId: plan.id,
|
|
3558
|
-
agent: plan.agent,
|
|
3559
|
-
title: plan.title,
|
|
3560
|
-
content: plan.content,
|
|
3561
|
-
format: plan.format,
|
|
3562
|
-
filePath: plan.filePath,
|
|
3563
|
-
workspace: plan.workspace,
|
|
3564
|
-
metadata: plan.metadata,
|
|
3565
|
-
createdAt: plan.createdAt.getTime(),
|
|
3566
|
-
updatedAt: plan.updatedAt.getTime()
|
|
3567
|
-
};
|
|
3954
|
+
const payload = planToPayload2(plan);
|
|
3568
3955
|
const hash = computePayloadHash(payload);
|
|
3569
3956
|
if (!force && cache[plan.id] === hash) {
|
|
3570
3957
|
skipped++;
|
|
@@ -3572,7 +3959,13 @@ async function syncAll(force = false) {
|
|
|
3572
3959
|
}
|
|
3573
3960
|
const result = await syncPlan(payload);
|
|
3574
3961
|
if (result.ok) {
|
|
3575
|
-
|
|
3962
|
+
if (result.skippedLowValue) {
|
|
3963
|
+
lowValueSkipped++;
|
|
3964
|
+
if (result.deleted)
|
|
3965
|
+
lowValueDeleted++;
|
|
3966
|
+
} else {
|
|
3967
|
+
synced++;
|
|
3968
|
+
}
|
|
3576
3969
|
cache[plan.id] = hash;
|
|
3577
3970
|
} else {
|
|
3578
3971
|
failed++;
|
|
@@ -3584,7 +3977,8 @@ async function syncAll(force = false) {
|
|
|
3584
3977
|
delete cache[id];
|
|
3585
3978
|
}
|
|
3586
3979
|
saveSyncCache(cache, { replace: true });
|
|
3587
|
-
|
|
3980
|
+
const lowValueSuffix = lowValueSkipped > 0 ? `, ${lowValueSkipped} low-value skipped/pruned${lowValueDeleted > 0 ? ` (${lowValueDeleted} deleted)` : ""}` : "";
|
|
3981
|
+
console.log(`[agendex] Sync complete: ${synced} synced${lowValueSuffix}, ${skipped} unchanged, ${failed} failed`);
|
|
3588
3982
|
}
|
|
3589
3983
|
|
|
3590
3984
|
// src/version.ts
|
|
@@ -3594,7 +3988,7 @@ import { join as join12 } from "node:path";
|
|
|
3594
3988
|
// package.json
|
|
3595
3989
|
var package_default = {
|
|
3596
3990
|
name: "agendex-cli",
|
|
3597
|
-
version: "0.
|
|
3991
|
+
version: "0.14.0",
|
|
3598
3992
|
description: "Agendex CLI for login, sync, and daemon workflows",
|
|
3599
3993
|
homepage: "https://github.com/Tyru5/Agendex#readme",
|
|
3600
3994
|
repository: {
|