@hasna/configs 0.1.0 → 0.1.1
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/index.js +164 -118
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +225 -111
- package/dist/lib/sync-dir.d.ts +13 -0
- package/dist/lib/sync-dir.d.ts.map +1 -0
- package/dist/lib/sync.d.ts +24 -8
- package/dist/lib/sync.d.ts.map +1 -1
- package/dist/mcp/index.js +82 -88
- package/dist/server/index.js +82 -88
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -524,83 +524,26 @@ async function applyConfigs(configs, opts = {}) {
|
|
|
524
524
|
return results;
|
|
525
525
|
}
|
|
526
526
|
// src/lib/sync.ts
|
|
527
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync3 } from "fs";
|
|
528
|
+
import { extname, join as join3 } from "path";
|
|
529
|
+
import { homedir as homedir3 } from "os";
|
|
530
|
+
|
|
531
|
+
// src/lib/sync-dir.ts
|
|
527
532
|
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
528
|
-
import {
|
|
533
|
+
import { join as join2, relative } from "path";
|
|
529
534
|
import { homedir as homedir2 } from "os";
|
|
530
|
-
|
|
531
|
-
const p = filePath.toLowerCase().replace(homedir2(), "~");
|
|
532
|
-
if (p.includes("/.claude/rules/") || p.endsWith("claude.md") || p.endsWith("agents.md") || p.endsWith("gemini.md"))
|
|
533
|
-
return "rules";
|
|
534
|
-
if (p.includes("/.claude/") || p.includes("/.codex/") || p.includes("/.gemini/") || p.includes("/.cursor/"))
|
|
535
|
-
return "agent";
|
|
536
|
-
if (p.includes(".mcp.json") || p.includes("mcp"))
|
|
537
|
-
return "mcp";
|
|
538
|
-
if (p.includes(".zshrc") || p.includes(".zprofile") || p.includes(".bashrc") || p.includes(".bash_profile"))
|
|
539
|
-
return "shell";
|
|
540
|
-
if (p.includes(".gitconfig") || p.includes(".gitignore"))
|
|
541
|
-
return "git";
|
|
542
|
-
if (p.includes(".npmrc") || p.includes("tsconfig") || p.includes("bunfig"))
|
|
543
|
-
return "tools";
|
|
544
|
-
if (p.includes(".secrets"))
|
|
545
|
-
return "secrets_schema";
|
|
546
|
-
return "tools";
|
|
547
|
-
}
|
|
548
|
-
function detectAgent(filePath) {
|
|
549
|
-
const p = filePath.toLowerCase().replace(homedir2(), "~");
|
|
550
|
-
if (p.includes("/.claude/") || p.endsWith("claude.md"))
|
|
551
|
-
return "claude";
|
|
552
|
-
if (p.includes("/.codex/") || p.endsWith("agents.md"))
|
|
553
|
-
return "codex";
|
|
554
|
-
if (p.includes("/.gemini/") || p.endsWith("gemini.md"))
|
|
555
|
-
return "gemini";
|
|
556
|
-
if (p.includes(".zshrc") || p.includes(".zprofile") || p.includes(".bashrc"))
|
|
557
|
-
return "zsh";
|
|
558
|
-
if (p.includes(".gitconfig") || p.includes(".gitignore"))
|
|
559
|
-
return "git";
|
|
560
|
-
if (p.includes(".npmrc"))
|
|
561
|
-
return "npm";
|
|
562
|
-
return "global";
|
|
563
|
-
}
|
|
564
|
-
function detectFormat(filePath) {
|
|
565
|
-
const ext = extname(filePath).toLowerCase();
|
|
566
|
-
if (ext === ".json")
|
|
567
|
-
return "json";
|
|
568
|
-
if (ext === ".toml")
|
|
569
|
-
return "toml";
|
|
570
|
-
if (ext === ".yaml" || ext === ".yml")
|
|
571
|
-
return "yaml";
|
|
572
|
-
if (ext === ".md" || ext === ".markdown")
|
|
573
|
-
return "markdown";
|
|
574
|
-
if (ext === ".ini" || ext === ".cfg")
|
|
575
|
-
return "ini";
|
|
576
|
-
return "text";
|
|
577
|
-
}
|
|
578
|
-
var SKIP_PATTERNS = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
|
|
535
|
+
var SKIP = [".db", ".db-shm", ".db-wal", ".log", ".lock", ".DS_Store", "node_modules", ".git"];
|
|
579
536
|
function shouldSkip(p) {
|
|
580
|
-
return
|
|
581
|
-
}
|
|
582
|
-
function walkDir(dir, files = []) {
|
|
583
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
584
|
-
for (const entry of entries) {
|
|
585
|
-
const full = join2(dir, entry.name);
|
|
586
|
-
if (shouldSkip(full))
|
|
587
|
-
continue;
|
|
588
|
-
if (entry.isDirectory()) {
|
|
589
|
-
walkDir(full, files);
|
|
590
|
-
} else if (entry.isFile()) {
|
|
591
|
-
files.push(full);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
return files;
|
|
537
|
+
return SKIP.some((s) => p.includes(s));
|
|
595
538
|
}
|
|
596
539
|
async function syncFromDir(dir, opts = {}) {
|
|
597
540
|
const d = opts.db || getDatabase();
|
|
598
541
|
const absDir = expandPath(dir);
|
|
599
|
-
if (!existsSync3(absDir))
|
|
600
|
-
return { added: 0, updated: 0, unchanged: 0, skipped: [`
|
|
601
|
-
}
|
|
542
|
+
if (!existsSync3(absDir))
|
|
543
|
+
return { added: 0, updated: 0, unchanged: 0, skipped: [`Not found: ${absDir}`] };
|
|
602
544
|
const files = opts.recursive !== false ? walkDir(absDir) : readdirSync(absDir).map((f) => join2(absDir, f)).filter((f) => statSync(f).isFile());
|
|
603
545
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
546
|
+
const home = homedir2();
|
|
604
547
|
const allConfigs = listConfigs(undefined, d);
|
|
605
548
|
for (const file of files) {
|
|
606
549
|
if (shouldSkip(file)) {
|
|
@@ -609,25 +552,19 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
609
552
|
}
|
|
610
553
|
try {
|
|
611
554
|
const content = readFileSync2(file, "utf-8");
|
|
612
|
-
|
|
555
|
+
if (content.length > 500000) {
|
|
556
|
+
result.skipped.push(file + " (too large)");
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
const targetPath = file.replace(home, "~");
|
|
613
560
|
const existing = allConfigs.find((c) => c.target_path === targetPath);
|
|
614
561
|
if (!existing) {
|
|
615
|
-
if (!opts.dryRun)
|
|
616
|
-
|
|
617
|
-
createConfig({
|
|
618
|
-
name,
|
|
619
|
-
category: detectCategory(file),
|
|
620
|
-
agent: detectAgent(file),
|
|
621
|
-
target_path: targetPath,
|
|
622
|
-
format: detectFormat(file),
|
|
623
|
-
content
|
|
624
|
-
}, d);
|
|
625
|
-
}
|
|
562
|
+
if (!opts.dryRun)
|
|
563
|
+
createConfig({ name: relative(absDir, file), category: detectCategory(file), agent: detectAgent(file), target_path: targetPath, format: detectFormat(file), content }, d);
|
|
626
564
|
result.added++;
|
|
627
565
|
} else if (existing.content !== content) {
|
|
628
|
-
if (!opts.dryRun)
|
|
566
|
+
if (!opts.dryRun)
|
|
629
567
|
updateConfig(existing.id, { content }, d);
|
|
630
|
-
}
|
|
631
568
|
result.updated++;
|
|
632
569
|
} else {
|
|
633
570
|
result.unchanged++;
|
|
@@ -640,22 +577,148 @@ async function syncFromDir(dir, opts = {}) {
|
|
|
640
577
|
}
|
|
641
578
|
async function syncToDir(dir, opts = {}) {
|
|
642
579
|
const d = opts.db || getDatabase();
|
|
580
|
+
const home = homedir2();
|
|
643
581
|
const absDir = expandPath(dir);
|
|
644
|
-
const
|
|
645
|
-
const configs = listConfigs(undefined, d).filter((c) => c.target_path && (c.target_path.startsWith(
|
|
582
|
+
const normalized = dir.startsWith("~/") ? dir : absDir.replace(home, "~");
|
|
583
|
+
const configs = listConfigs(undefined, d).filter((c) => c.target_path && (c.target_path.startsWith(normalized) || c.target_path.startsWith(absDir)));
|
|
646
584
|
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
647
585
|
for (const config of configs) {
|
|
648
586
|
if (config.kind === "reference")
|
|
649
587
|
continue;
|
|
650
588
|
try {
|
|
651
589
|
const r = await applyConfig(config, { dryRun: opts.dryRun, db: d });
|
|
652
|
-
|
|
653
|
-
|
|
590
|
+
r.changed ? result.updated++ : result.unchanged++;
|
|
591
|
+
} catch {
|
|
592
|
+
result.skipped.push(config.target_path || config.id);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return result;
|
|
596
|
+
}
|
|
597
|
+
function walkDir(dir, files = []) {
|
|
598
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
599
|
+
const full = join2(dir, entry.name);
|
|
600
|
+
if (shouldSkip(full))
|
|
601
|
+
continue;
|
|
602
|
+
if (entry.isDirectory())
|
|
603
|
+
walkDir(full, files);
|
|
604
|
+
else if (entry.isFile())
|
|
605
|
+
files.push(full);
|
|
606
|
+
}
|
|
607
|
+
return files;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/lib/sync.ts
|
|
611
|
+
var KNOWN_CONFIGS = [
|
|
612
|
+
{ path: "~/.claude/CLAUDE.md", name: "claude-claude-md", category: "rules", agent: "claude", format: "markdown" },
|
|
613
|
+
{ path: "~/.claude/settings.json", name: "claude-settings", category: "agent", agent: "claude", format: "json" },
|
|
614
|
+
{ path: "~/.claude/settings.local.json", name: "claude-settings-local", category: "agent", agent: "claude", format: "json" },
|
|
615
|
+
{ path: "~/.claude/keybindings.json", name: "claude-keybindings", category: "agent", agent: "claude", format: "json" },
|
|
616
|
+
{ path: "~/.claude/rules", name: "claude-rules", category: "rules", agent: "claude", rulesDir: "~/.claude/rules" },
|
|
617
|
+
{ path: "~/.codex/config.toml", name: "codex-config", category: "agent", agent: "codex", format: "toml" },
|
|
618
|
+
{ path: "~/.codex/AGENTS.md", name: "codex-agents-md", category: "rules", agent: "codex", format: "markdown" },
|
|
619
|
+
{ path: "~/.gemini/settings.json", name: "gemini-settings", category: "agent", agent: "gemini", format: "json" },
|
|
620
|
+
{ path: "~/.gemini/GEMINI.md", name: "gemini-gemini-md", category: "rules", agent: "gemini", format: "markdown" },
|
|
621
|
+
{ path: "~/.claude.json", name: "claude-json", category: "mcp", agent: "claude", format: "json", description: "Claude Code global config (includes MCP server entries)" },
|
|
622
|
+
{ path: "~/.zshrc", name: "zshrc", category: "shell", agent: "zsh" },
|
|
623
|
+
{ path: "~/.zprofile", name: "zprofile", category: "shell", agent: "zsh" },
|
|
624
|
+
{ path: "~/.bashrc", name: "bashrc", category: "shell", agent: "zsh" },
|
|
625
|
+
{ path: "~/.bash_profile", name: "bash-profile", category: "shell", agent: "zsh" },
|
|
626
|
+
{ path: "~/.gitconfig", name: "gitconfig", category: "git", agent: "git", format: "ini" },
|
|
627
|
+
{ path: "~/.gitignore_global", name: "gitignore-global", category: "git", agent: "git" },
|
|
628
|
+
{ path: "~/.npmrc", name: "npmrc", category: "tools", agent: "npm", format: "ini" },
|
|
629
|
+
{ path: "~/.bunfig.toml", name: "bunfig", category: "tools", agent: "global", format: "toml" }
|
|
630
|
+
];
|
|
631
|
+
async function syncKnown(opts = {}) {
|
|
632
|
+
const d = opts.db || getDatabase();
|
|
633
|
+
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
634
|
+
const home = homedir3();
|
|
635
|
+
let targets = KNOWN_CONFIGS;
|
|
636
|
+
if (opts.agent)
|
|
637
|
+
targets = targets.filter((k) => k.agent === opts.agent);
|
|
638
|
+
if (opts.category)
|
|
639
|
+
targets = targets.filter((k) => k.category === opts.category);
|
|
640
|
+
const allConfigs = listConfigs(undefined, d);
|
|
641
|
+
for (const known of targets) {
|
|
642
|
+
if (known.rulesDir) {
|
|
643
|
+
const absDir = expandPath(known.rulesDir);
|
|
644
|
+
if (!existsSync4(absDir)) {
|
|
645
|
+
result.skipped.push(known.rulesDir);
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
const mdFiles = readdirSync2(absDir).filter((f) => f.endsWith(".md"));
|
|
649
|
+
for (const f of mdFiles) {
|
|
650
|
+
const abs2 = join3(absDir, f);
|
|
651
|
+
const targetPath = abs2.replace(home, "~");
|
|
652
|
+
const content = readFileSync3(abs2, "utf-8");
|
|
653
|
+
const name = `claude-rules-${f}`;
|
|
654
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
|
|
655
|
+
const existing = allConfigs.find((c) => c.target_path === targetPath || c.slug === slug);
|
|
656
|
+
if (!existing) {
|
|
657
|
+
if (!opts.dryRun)
|
|
658
|
+
createConfig({ name, category: "rules", agent: "claude", format: "markdown", content, target_path: targetPath }, d);
|
|
659
|
+
result.added++;
|
|
660
|
+
} else if (existing.content !== content) {
|
|
661
|
+
if (!opts.dryRun)
|
|
662
|
+
updateConfig(existing.id, { content }, d);
|
|
663
|
+
result.updated++;
|
|
664
|
+
} else {
|
|
665
|
+
result.unchanged++;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
const abs = expandPath(known.path);
|
|
671
|
+
if (!existsSync4(abs)) {
|
|
672
|
+
result.skipped.push(known.path);
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
const content = readFileSync3(abs, "utf-8");
|
|
677
|
+
if (content.length > 500000) {
|
|
678
|
+
result.skipped.push(known.path + " (too large)");
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
const targetPath = abs.replace(home, "~");
|
|
682
|
+
const existing = allConfigs.find((c) => c.target_path === targetPath || c.slug === known.name);
|
|
683
|
+
if (!existing) {
|
|
684
|
+
if (!opts.dryRun) {
|
|
685
|
+
createConfig({
|
|
686
|
+
name: known.name,
|
|
687
|
+
category: known.category,
|
|
688
|
+
agent: known.agent,
|
|
689
|
+
format: known.format ?? detectFormat(abs),
|
|
690
|
+
content,
|
|
691
|
+
target_path: known.kind === "reference" ? null : targetPath,
|
|
692
|
+
kind: known.kind ?? "file",
|
|
693
|
+
description: known.description
|
|
694
|
+
}, d);
|
|
695
|
+
}
|
|
696
|
+
result.added++;
|
|
697
|
+
} else if (existing.content !== content) {
|
|
698
|
+
if (!opts.dryRun)
|
|
699
|
+
updateConfig(existing.id, { content }, d);
|
|
700
|
+
result.updated++;
|
|
654
701
|
} else {
|
|
655
702
|
result.unchanged++;
|
|
656
703
|
}
|
|
657
704
|
} catch {
|
|
658
|
-
result.skipped.push(
|
|
705
|
+
result.skipped.push(known.path);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return result;
|
|
709
|
+
}
|
|
710
|
+
async function syncToDisk(opts = {}) {
|
|
711
|
+
const d = opts.db || getDatabase();
|
|
712
|
+
const result = { added: 0, updated: 0, unchanged: 0, skipped: [] };
|
|
713
|
+
let configs = listConfigs({ kind: "file", ...opts.agent ? { agent: opts.agent } : {}, ...opts.category ? { category: opts.category } : {} }, d);
|
|
714
|
+
for (const config of configs) {
|
|
715
|
+
if (!config.target_path)
|
|
716
|
+
continue;
|
|
717
|
+
try {
|
|
718
|
+
const r = await applyConfig(config, { dryRun: opts.dryRun, db: d });
|
|
719
|
+
r.changed ? result.updated++ : result.unchanged++;
|
|
720
|
+
} catch {
|
|
721
|
+
result.skipped.push(config.target_path);
|
|
659
722
|
}
|
|
660
723
|
}
|
|
661
724
|
return result;
|
|
@@ -664,9 +727,9 @@ function diffConfig(config) {
|
|
|
664
727
|
if (!config.target_path)
|
|
665
728
|
return "(reference \u2014 no target path)";
|
|
666
729
|
const path = expandPath(config.target_path);
|
|
667
|
-
if (!
|
|
730
|
+
if (!existsSync4(path))
|
|
668
731
|
return `(file not found on disk: ${path})`;
|
|
669
|
-
const diskContent =
|
|
732
|
+
const diskContent = readFileSync3(path, "utf-8");
|
|
670
733
|
if (diskContent === config.content)
|
|
671
734
|
return "(no diff \u2014 identical)";
|
|
672
735
|
const stored = config.content.split(`
|
|
@@ -677,30 +740,78 @@ function diffConfig(config) {
|
|
|
677
740
|
const maxLen = Math.max(stored.length, disk.length);
|
|
678
741
|
for (let i = 0;i < maxLen; i++) {
|
|
679
742
|
const s = stored[i];
|
|
680
|
-
const
|
|
681
|
-
if (s ===
|
|
743
|
+
const dk = disk[i];
|
|
744
|
+
if (s === dk) {
|
|
682
745
|
if (s !== undefined)
|
|
683
746
|
lines.push(` ${s}`);
|
|
684
747
|
} else {
|
|
685
748
|
if (s !== undefined)
|
|
686
749
|
lines.push(`-${s}`);
|
|
687
|
-
if (
|
|
688
|
-
lines.push(`+${
|
|
750
|
+
if (dk !== undefined)
|
|
751
|
+
lines.push(`+${dk}`);
|
|
689
752
|
}
|
|
690
753
|
}
|
|
691
754
|
return lines.join(`
|
|
692
755
|
`);
|
|
693
756
|
}
|
|
757
|
+
function detectCategory(filePath) {
|
|
758
|
+
const p = filePath.toLowerCase().replace(homedir3(), "~");
|
|
759
|
+
if (p.includes("/.claude/rules/") || p.endsWith("claude.md") || p.endsWith("agents.md") || p.endsWith("gemini.md"))
|
|
760
|
+
return "rules";
|
|
761
|
+
if (p.includes("/.claude/") || p.includes("/.codex/") || p.includes("/.gemini/") || p.includes("/.cursor/"))
|
|
762
|
+
return "agent";
|
|
763
|
+
if (p.includes(".mcp.json") || p.includes("mcp"))
|
|
764
|
+
return "mcp";
|
|
765
|
+
if (p.includes(".zshrc") || p.includes(".zprofile") || p.includes(".bashrc") || p.includes(".bash_profile"))
|
|
766
|
+
return "shell";
|
|
767
|
+
if (p.includes(".gitconfig") || p.includes(".gitignore"))
|
|
768
|
+
return "git";
|
|
769
|
+
if (p.includes(".npmrc") || p.includes("tsconfig") || p.includes("bunfig"))
|
|
770
|
+
return "tools";
|
|
771
|
+
if (p.includes(".secrets"))
|
|
772
|
+
return "secrets_schema";
|
|
773
|
+
return "tools";
|
|
774
|
+
}
|
|
775
|
+
function detectAgent(filePath) {
|
|
776
|
+
const p = filePath.toLowerCase().replace(homedir3(), "~");
|
|
777
|
+
if (p.includes("/.claude/") || p.endsWith("claude.md"))
|
|
778
|
+
return "claude";
|
|
779
|
+
if (p.includes("/.codex/") || p.endsWith("agents.md"))
|
|
780
|
+
return "codex";
|
|
781
|
+
if (p.includes("/.gemini/") || p.endsWith("gemini.md"))
|
|
782
|
+
return "gemini";
|
|
783
|
+
if (p.includes(".zshrc") || p.includes(".zprofile") || p.includes(".bashrc"))
|
|
784
|
+
return "zsh";
|
|
785
|
+
if (p.includes(".gitconfig") || p.includes(".gitignore"))
|
|
786
|
+
return "git";
|
|
787
|
+
if (p.includes(".npmrc"))
|
|
788
|
+
return "npm";
|
|
789
|
+
return "global";
|
|
790
|
+
}
|
|
791
|
+
function detectFormat(filePath) {
|
|
792
|
+
const ext = extname(filePath).toLowerCase();
|
|
793
|
+
if (ext === ".json")
|
|
794
|
+
return "json";
|
|
795
|
+
if (ext === ".toml")
|
|
796
|
+
return "toml";
|
|
797
|
+
if (ext === ".yaml" || ext === ".yml")
|
|
798
|
+
return "yaml";
|
|
799
|
+
if (ext === ".md" || ext === ".markdown")
|
|
800
|
+
return "markdown";
|
|
801
|
+
if (ext === ".ini" || ext === ".cfg")
|
|
802
|
+
return "ini";
|
|
803
|
+
return "text";
|
|
804
|
+
}
|
|
694
805
|
// src/lib/export.ts
|
|
695
|
-
import { existsSync as
|
|
696
|
-
import { join as
|
|
806
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
807
|
+
import { join as join4, resolve as resolve3 } from "path";
|
|
697
808
|
import { tmpdir } from "os";
|
|
698
809
|
async function exportConfigs(outputPath, opts = {}) {
|
|
699
810
|
const d = opts.db || getDatabase();
|
|
700
811
|
const configs = listConfigs(opts.filter, d);
|
|
701
|
-
const absOutput =
|
|
702
|
-
const tmpDir =
|
|
703
|
-
const contentsDir =
|
|
812
|
+
const absOutput = resolve3(outputPath);
|
|
813
|
+
const tmpDir = join4(tmpdir(), `configs-export-${Date.now()}`);
|
|
814
|
+
const contentsDir = join4(tmpDir, "contents");
|
|
704
815
|
try {
|
|
705
816
|
mkdirSync3(contentsDir, { recursive: true });
|
|
706
817
|
const manifest = {
|
|
@@ -708,10 +819,10 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
708
819
|
exported_at: now(),
|
|
709
820
|
configs: configs.map(({ content: _content, ...meta }) => meta)
|
|
710
821
|
};
|
|
711
|
-
writeFileSync2(
|
|
822
|
+
writeFileSync2(join4(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf-8");
|
|
712
823
|
for (const config of configs) {
|
|
713
824
|
const fileName = `${config.slug}.${config.format === "text" ? "txt" : config.format}`;
|
|
714
|
-
writeFileSync2(
|
|
825
|
+
writeFileSync2(join4(contentsDir, fileName), config.content, "utf-8");
|
|
715
826
|
}
|
|
716
827
|
const proc = Bun.spawn(["tar", "czf", absOutput, "-C", tmpDir, "."], {
|
|
717
828
|
stdout: "pipe",
|
|
@@ -724,20 +835,20 @@ async function exportConfigs(outputPath, opts = {}) {
|
|
|
724
835
|
}
|
|
725
836
|
return { path: absOutput, count: configs.length };
|
|
726
837
|
} finally {
|
|
727
|
-
if (
|
|
838
|
+
if (existsSync5(tmpDir)) {
|
|
728
839
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
729
840
|
}
|
|
730
841
|
}
|
|
731
842
|
}
|
|
732
843
|
// src/lib/import.ts
|
|
733
|
-
import { existsSync as
|
|
734
|
-
import { join as
|
|
844
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2 } from "fs";
|
|
845
|
+
import { join as join5, resolve as resolve4 } from "path";
|
|
735
846
|
import { tmpdir as tmpdir2 } from "os";
|
|
736
847
|
async function importConfigs(bundlePath, opts = {}) {
|
|
737
848
|
const d = opts.db || getDatabase();
|
|
738
849
|
const conflict = opts.conflict ?? "skip";
|
|
739
|
-
const absPath =
|
|
740
|
-
const tmpDir =
|
|
850
|
+
const absPath = resolve4(bundlePath);
|
|
851
|
+
const tmpDir = join5(tmpdir2(), `configs-import-${Date.now()}`);
|
|
741
852
|
const result = { created: 0, updated: 0, skipped: 0, errors: [] };
|
|
742
853
|
try {
|
|
743
854
|
mkdirSync4(tmpDir, { recursive: true });
|
|
@@ -750,15 +861,15 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
750
861
|
const stderr = await new Response(proc.stderr).text();
|
|
751
862
|
throw new Error(`tar extraction failed: ${stderr}`);
|
|
752
863
|
}
|
|
753
|
-
const manifestPath =
|
|
754
|
-
if (!
|
|
864
|
+
const manifestPath = join5(tmpDir, "manifest.json");
|
|
865
|
+
if (!existsSync6(manifestPath))
|
|
755
866
|
throw new Error("Invalid bundle: missing manifest.json");
|
|
756
|
-
const manifest = JSON.parse(
|
|
867
|
+
const manifest = JSON.parse(readFileSync4(manifestPath, "utf-8"));
|
|
757
868
|
for (const meta of manifest.configs) {
|
|
758
869
|
try {
|
|
759
870
|
const ext = meta.format === "text" ? "txt" : meta.format;
|
|
760
|
-
const contentFile =
|
|
761
|
-
const content =
|
|
871
|
+
const contentFile = join5(tmpDir, "contents", `${meta.slug}.${ext}`);
|
|
872
|
+
const content = existsSync6(contentFile) ? readFileSync4(contentFile, "utf-8") : "";
|
|
762
873
|
let existing = null;
|
|
763
874
|
try {
|
|
764
875
|
existing = getConfig(meta.slug, d);
|
|
@@ -791,7 +902,7 @@ async function importConfigs(bundlePath, opts = {}) {
|
|
|
791
902
|
}
|
|
792
903
|
return result;
|
|
793
904
|
} finally {
|
|
794
|
-
if (
|
|
905
|
+
if (existsSync6(tmpDir)) {
|
|
795
906
|
rmSync2(tmpDir, { recursive: true, force: true });
|
|
796
907
|
}
|
|
797
908
|
}
|
|
@@ -844,7 +955,9 @@ export {
|
|
|
844
955
|
updateProfile,
|
|
845
956
|
updateMachineApplied,
|
|
846
957
|
updateConfig,
|
|
958
|
+
syncToDisk,
|
|
847
959
|
syncToDir,
|
|
960
|
+
syncKnown,
|
|
848
961
|
syncFromDir,
|
|
849
962
|
slugify,
|
|
850
963
|
resetDatabase,
|
|
@@ -887,6 +1000,7 @@ export {
|
|
|
887
1000
|
addConfigToProfile,
|
|
888
1001
|
TemplateRenderError,
|
|
889
1002
|
ProfileNotFoundError,
|
|
1003
|
+
KNOWN_CONFIGS,
|
|
890
1004
|
ConfigNotFoundError,
|
|
891
1005
|
ConfigApplyError,
|
|
892
1006
|
CONFIG_KINDS,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SyncResult } from "../types/index.js";
|
|
2
|
+
import { getDatabase } from "../db/database.js";
|
|
3
|
+
export interface SyncFromDirOptions {
|
|
4
|
+
db?: ReturnType<typeof getDatabase>;
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
recursive?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function syncFromDir(dir: string, opts?: SyncFromDirOptions): Promise<SyncResult>;
|
|
9
|
+
export declare function syncToDir(dir: string, opts?: {
|
|
10
|
+
db?: ReturnType<typeof getDatabase>;
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
}): Promise<SyncResult>;
|
|
13
|
+
//# sourceMappingURL=sync-dir.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-dir.d.ts","sourceRoot":"","sources":["../../src/lib/sync-dir.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,MAAM,WAAW,kBAAkB;IACjC,EAAE,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,UAAU,CAAC,CA8BjG;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,EAAE,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAetI"}
|
package/dist/lib/sync.d.ts
CHANGED
|
@@ -1,19 +1,35 @@
|
|
|
1
1
|
import type { Config, ConfigAgent, ConfigCategory, ConfigFormat, SyncResult } from "../types/index.js";
|
|
2
2
|
import { getDatabase } from "../db/database.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
export interface KnownConfig {
|
|
4
|
+
path: string;
|
|
5
|
+
name: string;
|
|
6
|
+
category: ConfigCategory;
|
|
7
|
+
agent: ConfigAgent;
|
|
8
|
+
format?: ConfigFormat;
|
|
9
|
+
kind?: "file" | "reference";
|
|
10
|
+
description?: string;
|
|
11
|
+
rulesDir?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const KNOWN_CONFIGS: KnownConfig[];
|
|
14
|
+
export interface SyncKnownOptions {
|
|
7
15
|
db?: ReturnType<typeof getDatabase>;
|
|
8
16
|
dryRun?: boolean;
|
|
9
|
-
|
|
17
|
+
agent?: ConfigAgent;
|
|
18
|
+
category?: ConfigCategory;
|
|
10
19
|
}
|
|
11
|
-
export declare function
|
|
12
|
-
export interface
|
|
20
|
+
export declare function syncKnown(opts?: SyncKnownOptions): Promise<SyncResult>;
|
|
21
|
+
export interface SyncToDiskOptions {
|
|
13
22
|
db?: ReturnType<typeof getDatabase>;
|
|
14
23
|
dryRun?: boolean;
|
|
24
|
+
agent?: ConfigAgent;
|
|
25
|
+
category?: ConfigCategory;
|
|
15
26
|
}
|
|
16
|
-
export declare function
|
|
27
|
+
export declare function syncToDisk(opts?: SyncToDiskOptions): Promise<SyncResult>;
|
|
17
28
|
export declare function diffConfig(config: Config): string;
|
|
29
|
+
export declare function detectCategory(filePath: string): ConfigCategory;
|
|
30
|
+
export declare function detectAgent(filePath: string): ConfigAgent;
|
|
31
|
+
export declare function detectFormat(filePath: string): ConfigFormat;
|
|
18
32
|
export { Config };
|
|
33
|
+
export type { SyncFromDirOptions } from "./sync-dir.js";
|
|
34
|
+
export { syncFromDir, syncToDir } from "./sync-dir.js";
|
|
19
35
|
//# sourceMappingURL=sync.d.ts.map
|
package/dist/lib/sync.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/lib/sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACvG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/lib/sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACvG,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EAiCtC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAuEhF;AAGD,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,wBAAsB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAgBlF;AAGD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAqBjD;AAGD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAU/D;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CASzD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAQ3D;AAGD,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
|