cawdex 1.35.76 → 1.35.78

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 CHANGED
@@ -94,7 +94,8 @@ import { normalizeTypeaheadDraftForPrompt } from './prompt-buffer.js';
94
94
  import { maybeInstantAnswer } from './instant-answer.js';
95
95
  import { maybeCreateInstantArtifact } from './instant-artifact.js';
96
96
  import { getCurrentVersion, startStartupUpdateCheck } from './updater.js';
97
- import { activateFooter, askWithFooterPrompt, buildFooterSnapshot, deactivateFooter, isFooterActive, shouldUseFixedFooter, updateFooter, writeScrollableLine, } from './fixed-footer.js';
97
+ import { importStatus, previewImport, rollbackImport, runImport, scanImportSources, } from './imports.js';
98
+ import { activateFooter, askWithFooterPrompt, buildFooterSnapshot, deactivateFooter, isFooterActive, setFooterActivity, shouldUseFixedFooter, updateFooter, writeFooterSubmittedLine, writeScrollableLine, } from './fixed-footer.js';
98
99
  /**
99
100
  * Unified prompt resolver — prefers the bundled ECC prompt for a given
100
101
  * intent and falls back to the built-in builder when ECC isn't installed.
@@ -486,6 +487,56 @@ async function setupWizard(rl, currentConfig) {
486
487
  const defaultTarget = await askOptionalSwarmDefault('Default target platform/stack', currentConfig?.swarm?.defaultTarget);
487
488
  const defaultAssets = await askOptionalSwarmDefault('Default starting assets/resources', currentConfig?.swarm?.defaultAssets);
488
489
  const defaultQuality = await askOptionalSwarmDefault('Default quality bar/release target', currentConfig?.swarm?.defaultQuality);
490
+ console.log(chalk.white('\n Import settings'));
491
+ console.log(chalk.dim(' Defaults for /import and optional startup auto-sync.'));
492
+ const importSourceChoices = [
493
+ { label: 'Auto-detect', detail: 'scan all supported sources and import what is available', value: 'auto' },
494
+ { label: 'Claude', detail: 'prefer ~/.claude/projects exports', value: 'claude' },
495
+ { label: 'Codex', detail: 'prefer ~/.codex/sessions exports', value: 'codex' },
496
+ { label: 'MemPalace', detail: 'prefer ~/.mempalace notes and identity files', value: 'mempalace' },
497
+ ];
498
+ const currentImportSource = currentConfig?.import?.defaultSource ?? 'auto';
499
+ const importSourceDefaultIndex = Math.max(0, importSourceChoices.findIndex((c) => c.value === currentImportSource));
500
+ const defaultImportSource = await selectConfigChoice(rl, 'Import default source', importSourceChoices, {
501
+ defaultIndex: importSourceDefaultIndex,
502
+ fallbackPrompt: ` Import default source [${importSourceDefaultIndex + 1}]: `,
503
+ parseFallback: (answer, defaultIndex) => {
504
+ const raw = (answer || String(defaultIndex + 1)).trim().toLowerCase();
505
+ const byName = importSourceChoices.find((choice) => choice.value === raw);
506
+ if (byName)
507
+ return byName.value;
508
+ const byNumber = importSourceChoices[Number.parseInt(raw, 10) - 1];
509
+ return byNumber?.value;
510
+ },
511
+ });
512
+ const autoSyncDefaultIndex = currentConfig?.import?.autoSyncOnStartup ? 0 : 1;
513
+ const autoSyncOnStartup = await selectConfigChoice(rl, 'Import auto-sync on startup', [
514
+ { label: 'Enable', detail: 'run /import run <source> --limit N once at startup', value: true },
515
+ { label: 'Disable', detail: 'import only when requested via /import run', value: false },
516
+ ], {
517
+ defaultIndex: autoSyncDefaultIndex,
518
+ fallbackPrompt: ` Import auto-sync? [${autoSyncDefaultIndex === 0 ? 'Y/n' : 'y/N'}]: `,
519
+ parseFallback: (answer, defaultIndex) => {
520
+ const raw = answer.trim().toLowerCase();
521
+ if (!raw)
522
+ return defaultIndex === 0;
523
+ if (raw.startsWith('n'))
524
+ return false;
525
+ if (raw.startsWith('y'))
526
+ return true;
527
+ const byNumber = Number.parseInt(raw, 10);
528
+ if (byNumber === 1)
529
+ return true;
530
+ if (byNumber === 2)
531
+ return false;
532
+ return undefined;
533
+ },
534
+ });
535
+ const currentSyncLimit = Math.max(10, Math.min(5000, Number(currentConfig?.import?.syncLimit ?? 200)));
536
+ const syncLimitAnswer = await rl.question(chalk.yellow(` Import sync limit [${currentSyncLimit}]: `));
537
+ const importSyncLimit = syncLimitAnswer.trim()
538
+ ? Math.max(10, Math.min(5000, Number.parseInt(syncLimitAnswer.trim(), 10) || currentSyncLimit))
539
+ : currentSyncLimit;
489
540
  const config = {
490
541
  ...(currentConfig ?? {}),
491
542
  apiKey,
@@ -503,6 +554,11 @@ async function setupWizard(rl, currentConfig) {
503
554
  ...(defaultAssets ? { defaultAssets } : {}),
504
555
  ...(defaultQuality ? { defaultQuality } : {}),
505
556
  },
557
+ import: {
558
+ defaultSource: defaultImportSource,
559
+ autoSyncOnStartup,
560
+ syncLimit: importSyncLimit,
561
+ },
506
562
  };
507
563
  if (fallbackModel)
508
564
  config.fallbackModel = fallbackModel;
@@ -515,7 +571,7 @@ async function setupWizard(rl, currentConfig) {
515
571
  applyModelSelection(config, model);
516
572
  saveConfig(config);
517
573
  console.log(chalk.green(`\n Config saved to ${getConfigDir()}/config.json`));
518
- console.log(chalk.dim(` Configured: /model, /fallback, /perm, and /swarm. Memory stays under /memory.`));
574
+ console.log(chalk.dim(` Configured: /model, /fallback, /perm, /swarm, and /import defaults. Memory stays under /memory.`));
519
575
  console.log();
520
576
  return config;
521
577
  }
@@ -662,7 +718,7 @@ function printResumedHistory(messages, sessionName) {
662
718
  */
663
719
  async function askWithDecoratedPrompt(rl, sessionTag, modeTag, promptGlyph, prefill = '') {
664
720
  if (isFooterActive()) {
665
- return askWithFooterPrompt(rl, prefill);
721
+ return askWithFooterPrompt(rl, prefill, { echoSubmittedLine: false });
666
722
  }
667
723
  const decorative = sessionTag + modeTag;
668
724
  if (decorative.length > 0) {
@@ -925,6 +981,7 @@ export function handleSlashCommand(input, config, messages, session, mode) {
925
981
  console.log(d(' ') + c('/save [name]') + d(' — save current session'));
926
982
  console.log(d(' ') + c('/resume <id>') + d(' — resume a saved session'));
927
983
  console.log(d(' ') + c('/delete <id>') + d(' — delete a session'));
984
+ console.log(d(' ') + c('/import [sub]') + d(' — import Claude/Codex/MemPalace history and memory'));
928
985
  console.log(h('\n ── Git ──'));
929
986
  console.log(d(' ') + c('/commit') + d(' — AI-generated commit'));
930
987
  console.log(d(' ') + c('/pr') + d(' — AI-generated pull request'));
@@ -1809,6 +1866,121 @@ export function handleSlashCommand(input, config, messages, session, mode) {
1809
1866
  return { handled: true };
1810
1867
  }
1811
1868
  // ── Git ───────────────────────────────────────────
1869
+ case '/import': {
1870
+ const tokens = args.trim().split(/\s+/).filter(Boolean);
1871
+ const sub = (tokens[0] || 'help').toLowerCase();
1872
+ const sourceToken = (tokens[1] || 'auto').toLowerCase();
1873
+ const limitIdx = tokens.findIndex((t) => t === '--limit');
1874
+ const dryRun = tokens.includes('--dry-run');
1875
+ const knownSources = ['claude', 'codex', 'mempalace'];
1876
+ const parsedSource = knownSources.includes(sourceToken)
1877
+ ? sourceToken
1878
+ : null;
1879
+ const limit = limitIdx >= 0 && tokens[limitIdx + 1]
1880
+ ? Math.max(1, Math.min(10_000, Number(tokens[limitIdx + 1]) || 1000))
1881
+ : 1000;
1882
+ const detected = scanImportSources();
1883
+ const detectedSources = detected.filter((s) => s.detected && s.artifactsFound > 0).map((s) => s.source);
1884
+ const targets = parsedSource
1885
+ ? [parsedSource]
1886
+ : (detectedSources.length > 0 ? detectedSources : ['claude', 'codex', 'mempalace']);
1887
+ if (sub === 'help') {
1888
+ console.log(chalk.cyan('\n /import'));
1889
+ console.log(chalk.dim(' Migrate prior agent work into Cawdex without losing conversation history.'));
1890
+ console.log(chalk.dim(' /import scan'));
1891
+ console.log(chalk.dim(' /import preview <claude|codex|mempalace|auto> [--limit N]'));
1892
+ console.log(chalk.dim(' /import run <claude|codex|mempalace|auto> [--limit N] [--dry-run]'));
1893
+ console.log(chalk.dim(' /import status'));
1894
+ console.log(chalk.dim(' /import rollback <run-id>'));
1895
+ console.log(chalk.dim(' Notes:'));
1896
+ console.log(chalk.dim(' - Claude source: ~/.claude/projects/**/*.jsonl'));
1897
+ console.log(chalk.dim(' - Codex source: ~/.codex/sessions/**/*.jsonl'));
1898
+ console.log(chalk.dim(' - MemPalace source: ~/.mempalace/session_notes + identity.txt'));
1899
+ console.log();
1900
+ return { handled: true };
1901
+ }
1902
+ if (sub === 'scan') {
1903
+ console.log(chalk.cyan('\n Import sources:'));
1904
+ for (const item of detected) {
1905
+ const marker = item.detected ? chalk.green('✓') : chalk.yellow('•');
1906
+ const count = chalk.dim(`${item.artifactsFound} artifact(s)`);
1907
+ console.log(` ${marker} ${item.source.padEnd(10)} ${count} ${chalk.dim(item.rootPath)}`);
1908
+ if (item.note)
1909
+ console.log(chalk.dim(` ${item.note}`));
1910
+ }
1911
+ console.log();
1912
+ return { handled: true };
1913
+ }
1914
+ if (sub === 'status') {
1915
+ const status = importStatus();
1916
+ console.log(chalk.cyan('\n Import status:'));
1917
+ console.log(chalk.dim(` ledger entries: ${status.ledgerEntries}`));
1918
+ console.log(chalk.dim(` runs: ${status.runs}`));
1919
+ console.log(chalk.dim(` sessions imported: ${status.sessionsImported}`));
1920
+ console.log(chalk.dim(` memory drawers imported: ${status.drawersImported}`));
1921
+ for (const src of knownSources) {
1922
+ const row = status.bySource[src];
1923
+ console.log(chalk.dim(` ${src.padEnd(10)} sessions ${String(row.sessions).padStart(4)} drawers ${String(row.drawers).padStart(4)}`));
1924
+ }
1925
+ console.log();
1926
+ return { handled: true };
1927
+ }
1928
+ if (sub === 'rollback') {
1929
+ const runId = tokens[1];
1930
+ if (!runId) {
1931
+ console.log(chalk.yellow(' Usage: /import rollback <run-id>'));
1932
+ return { handled: true };
1933
+ }
1934
+ const res = rollbackImport(runId, process.cwd());
1935
+ console.log(chalk.green(` Rolled back ${runId}.`));
1936
+ console.log(chalk.dim(` deleted sessions: ${res.deletedSessions}`));
1937
+ console.log(chalk.dim(` deleted memory drawers: ${res.deletedDrawers}`));
1938
+ for (const warning of res.warnings)
1939
+ console.log(chalk.yellow(` warning: ${warning}`));
1940
+ return { handled: true };
1941
+ }
1942
+ if (sub === 'preview') {
1943
+ for (const src of targets) {
1944
+ const res = previewImport(src, { limit, cwd: process.cwd() });
1945
+ console.log(chalk.cyan(`\n Preview: ${src}`));
1946
+ console.log(chalk.dim(` artifacts found: ${res.artifactsFound}`));
1947
+ console.log(chalk.dim(` importable: ${res.importableArtifacts}`));
1948
+ console.log(chalk.dim(` already imported: ${res.alreadyImportedArtifacts}`));
1949
+ if (res.totalMessages > 0)
1950
+ console.log(chalk.dim(` messages in sample: ${res.totalMessages}`));
1951
+ for (const thread of res.threads.slice(0, 10)) {
1952
+ console.log(chalk.dim(` ${thread.sourceThreadId} ${thread.messageCount} msgs ${thread.updatedAt.slice(0, 10)} ${thread.model}`));
1953
+ }
1954
+ for (const warning of res.warnings.slice(0, 5))
1955
+ console.log(chalk.yellow(` warning: ${warning}`));
1956
+ }
1957
+ console.log();
1958
+ return { handled: true };
1959
+ }
1960
+ if (sub === 'run') {
1961
+ for (const src of targets) {
1962
+ const res = runImport(src, { limit, dryRun, cwd: process.cwd() });
1963
+ const prefix = dryRun ? 'Dry-run' : 'Imported';
1964
+ console.log(chalk.green(`\n ${prefix}: ${src}`));
1965
+ console.log(chalk.dim(` run id: ${res.runId}`));
1966
+ console.log(chalk.dim(` imported artifacts: ${res.importedArtifacts}`));
1967
+ console.log(chalk.dim(` skipped artifacts: ${res.skippedArtifacts}`));
1968
+ if (res.importedSessions > 0)
1969
+ console.log(chalk.dim(` sessions created: ${res.importedSessions}`));
1970
+ if (res.importedMessages > 0)
1971
+ console.log(chalk.dim(` messages imported: ${res.importedMessages}`));
1972
+ if (res.importedDrawers > 0)
1973
+ console.log(chalk.dim(` memory drawers created: ${res.importedDrawers}`));
1974
+ for (const warning of res.warnings.slice(0, 5))
1975
+ console.log(chalk.yellow(` warning: ${warning}`));
1976
+ }
1977
+ console.log();
1978
+ return { handled: true };
1979
+ }
1980
+ console.log(chalk.yellow(` Unknown /import subcommand: ${sub}`));
1981
+ console.log(chalk.dim(' Try: /import help'));
1982
+ return { handled: true };
1983
+ }
1812
1984
  case '/commit': {
1813
1985
  const prompt = buildCommitPrompt(process.cwd());
1814
1986
  if (!prompt) {
@@ -3663,6 +3835,31 @@ async function main() {
3663
3835
  if (memoryContext) {
3664
3836
  messages.push({ role: 'system', content: memoryContext });
3665
3837
  }
3838
+ if (!nonInteractive && config.import?.autoSyncOnStartup) {
3839
+ const wanted = config.import.defaultSource ?? 'auto';
3840
+ const limit = Math.max(10, Math.min(5000, Number(config.import.syncLimit ?? 200)));
3841
+ const scan = scanImportSources();
3842
+ const available = scan.filter((s) => s.detected && s.artifactsFound > 0).map((s) => s.source);
3843
+ const sources = wanted === 'auto'
3844
+ ? available
3845
+ : [wanted];
3846
+ if (sources.length > 0) {
3847
+ let totalImported = 0;
3848
+ let totalSkipped = 0;
3849
+ for (const src of sources) {
3850
+ try {
3851
+ const result = runImport(src, { limit, cwd: process.cwd() });
3852
+ totalImported += result.importedArtifacts;
3853
+ totalSkipped += result.skippedArtifacts;
3854
+ }
3855
+ catch (err) {
3856
+ const msg = err instanceof Error ? err.message : String(err);
3857
+ console.log(theme.warning(` import auto-sync (${src}) failed: ${msg}`));
3858
+ }
3859
+ }
3860
+ console.log(theme.dim(` import auto-sync: imported ${totalImported}, skipped ${totalSkipped} (limit ${limit})`));
3861
+ }
3862
+ }
3666
3863
  // Show startup display based on theme setting. Skipped entirely in
3667
3864
  // non-interactive mode — banners are noise when a harness is parsing
3668
3865
  // our stdout.
@@ -4714,6 +4911,9 @@ async function main() {
4714
4911
  const trimmed = input.trim();
4715
4912
  if (!trimmed)
4716
4913
  continue;
4914
+ if (isFooterActive()) {
4915
+ writeFooterSubmittedLine(input);
4916
+ }
4717
4917
  // Shell escape
4718
4918
  if (trimmed.startsWith('!')) {
4719
4919
  const { exec } = await import('node:child_process');
@@ -4745,6 +4945,9 @@ async function main() {
4745
4945
  nextTurnOverride = result.turnOverride;
4746
4946
  }
4747
4947
  syncFooter();
4948
+ if (isFooterActive()) {
4949
+ setFooterActivity('Ready', 0, null);
4950
+ }
4748
4951
  if (trimmed.startsWith('/config') && !result?.shouldExit) {
4749
4952
  deactivateFooter();
4750
4953
  config = await setupWizard(rl, config);