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.
Files changed (2) hide show
  1. package/dist/cli.js +427 -33
  2. 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(homedir7(), isDevMode() ? ".agendex-dev" : ".agendex");
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(homedir7(), p.slice(1));
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 = /&lt;\s*\/?\s*proposed_plan\s*&gt;/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 = homedir8();
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
- next.set(plan.id, plan);
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
- next.set(plan.id, plan);
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
- console.log(`[agendex] indexed ${store.size} plans from ${adapters.length} adapters`);
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 { ok: true };
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
- syncedCount++;
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
- console.log(`[agendex] sync complete: ${syncedCount} synced, ${failedCount} failed`);
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
- console.log(`[agendex] syncing ${syncQueue.length} plans (${initialSkipped} unchanged)...`);
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 plans = getAll();
3548
- console.log(`[agendex] Found ${plans.length} plans. Syncing to cloud...`);
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 plans) {
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
- synced++;
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
- console.log(`[agendex] Sync complete: ${synced} synced, ${skipped} unchanged, ${failed} failed`);
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.13.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: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agendex-cli",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Agendex CLI for login, sync, and daemon workflows",
5
5
  "homepage": "https://github.com/Tyru5/Agendex#readme",
6
6
  "repository": {