formalconf 2.0.19 → 2.0.20

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/README.md CHANGED
@@ -295,6 +295,11 @@ bun run theme <name>:<variant> # Apply a theme (e.g., catppuccin:dark, tokyo-ni
295
295
  bun run theme --install-templates # Install/update default templates
296
296
  bun run theme --template-status # Check template versions
297
297
  bun run theme --migrate <name> # Migrate legacy theme to JSON format
298
+ bun run template update --all # Update all templates (persists mode metadata)
299
+ bun run template list # List installed templates with their type
300
+ bun run template check # Check for available template updates
301
+ bun run template lock <name> # Lock a template from updates
302
+ bun run template unlock <name> # Unlock a template for updates
298
303
  bun run typecheck # Run TypeScript type checking
299
304
  ```
300
305
 
@@ -303,10 +308,11 @@ bun run typecheck # Run TypeScript type checking
303
308
  ```
304
309
  src/
305
310
  ├── cli/ # Entry points (run directly with bun)
306
- │ ├── formalconf.tsx # Main TUI app
307
- │ ├── config-manager.ts # Stow operations
308
- │ ├── pkg-sync.ts # Homebrew/MAS sync
309
- └── set-theme.ts # Theme switching
311
+ │ ├── formalconf.tsx # Main TUI app
312
+ │ ├── config-manager.ts # Stow operations
313
+ │ ├── pkg-sync.ts # Homebrew/MAS sync
314
+ ├── set-theme.ts # Theme switching
315
+ │ └── template-manager.ts # Template management
310
316
  ├── components/ # Ink React components
311
317
  │ ├── layout/ # Layout primitives (Panel, Breadcrumb, Footer)
312
318
  │ └── ui/ # UI elements (StatusIndicator, Divider)
@@ -4181,7 +4181,8 @@ async function installTemplate(templateName) {
4181
4181
  manifest.templates[templateName] = {
4182
4182
  version: bundledMeta.version,
4183
4183
  installedAt: new Date().toISOString(),
4184
- customOverride: false
4184
+ customOverride: false,
4185
+ mode: bundledMeta.mode
4185
4186
  };
4186
4187
  await saveTemplatesManifest(manifest);
4187
4188
  }
@@ -4191,11 +4192,32 @@ async function installAllTemplates() {
4191
4192
  await installTemplate(name);
4192
4193
  }
4193
4194
  }
4195
+ async function lockTemplate(templateName) {
4196
+ const manifest = await loadTemplatesManifest();
4197
+ if (!manifest.templates[templateName]) {
4198
+ throw new Error(`Template '${templateName}' is not installed`);
4199
+ }
4200
+ manifest.templates[templateName].customOverride = true;
4201
+ await saveTemplatesManifest(manifest);
4202
+ }
4203
+ async function unlockTemplate(templateName) {
4204
+ const manifest = await loadTemplatesManifest();
4205
+ if (!manifest.templates[templateName]) {
4206
+ throw new Error(`Template '${templateName}' is not installed`);
4207
+ }
4208
+ manifest.templates[templateName].customOverride = false;
4209
+ await saveTemplatesManifest(manifest);
4210
+ }
4194
4211
  async function getTemplateType(filename) {
4195
- const manifest = await loadBundledManifest();
4196
- const meta = manifest.templates[filename];
4197
- if (meta?.mode) {
4198
- return meta.mode;
4212
+ const installed = await loadTemplatesManifest();
4213
+ const installedMeta = installed.templates[filename];
4214
+ if (installedMeta?.mode) {
4215
+ return installedMeta.mode;
4216
+ }
4217
+ const bundled = await loadBundledManifest();
4218
+ const bundledMeta = bundled.templates[filename];
4219
+ if (bundledMeta?.mode) {
4220
+ return bundledMeta.mode;
4199
4221
  }
4200
4222
  if (filename.includes("-dark.") || filename.includes("-light.")) {
4201
4223
  return "partial";
@@ -5410,8 +5432,136 @@ var init_set_theme = __esm(() => {
5410
5432
  }
5411
5433
  });
5412
5434
 
5435
+ // src/cli/template-manager.ts
5436
+ var exports_template_manager = {};
5437
+ __export(exports_template_manager, {
5438
+ main: () => main5
5439
+ });
5440
+ import { parseArgs as parseArgs5 } from "util";
5441
+ function printHelp() {
5442
+ console.log(`
5443
+ ${colors6.cyan}Template Manager - Manage formalconf templates${colors6.reset}
5444
+
5445
+ Usage:
5446
+ formalconf template <command> [options]
5447
+
5448
+ Commands:
5449
+ ${colors6.blue}update${colors6.reset} [name] Update templates (all if no name specified)
5450
+ ${colors6.blue}list${colors6.reset} List installed templates
5451
+ ${colors6.blue}check${colors6.reset} Check for available updates
5452
+ ${colors6.blue}lock${colors6.reset} <name> Lock a template from updates
5453
+ ${colors6.blue}unlock${colors6.reset} <name> Unlock a template for updates
5454
+
5455
+ Options:
5456
+ -h, --help Show this help message
5457
+ --all Update all templates (with update command)
5458
+
5459
+ Examples:
5460
+ formalconf template update --all
5461
+ formalconf template check
5462
+ formalconf template lock neovim.lua.template
5463
+ `);
5464
+ }
5465
+ async function main5() {
5466
+ const { positionals, values } = parseArgs5({
5467
+ args: process.argv.slice(2),
5468
+ options: {
5469
+ help: { type: "boolean", short: "h" },
5470
+ all: { type: "boolean" }
5471
+ },
5472
+ allowPositionals: true,
5473
+ strict: false
5474
+ });
5475
+ const [subCommand, ...args] = positionals;
5476
+ if (values.help || !subCommand) {
5477
+ printHelp();
5478
+ process.exit(0);
5479
+ }
5480
+ switch (subCommand) {
5481
+ case "update":
5482
+ await handleUpdate(args[0], values.all);
5483
+ break;
5484
+ case "list":
5485
+ await handleList();
5486
+ break;
5487
+ case "check":
5488
+ await handleCheck();
5489
+ break;
5490
+ case "lock":
5491
+ if (!args[0]) {
5492
+ console.error(`${colors6.red}Error: Template name required${colors6.reset}`);
5493
+ process.exit(1);
5494
+ }
5495
+ await lockTemplate(args[0]);
5496
+ console.log(`${colors6.green}Locked ${args[0]}${colors6.reset}`);
5497
+ break;
5498
+ case "unlock":
5499
+ if (!args[0]) {
5500
+ console.error(`${colors6.red}Error: Template name required${colors6.reset}`);
5501
+ process.exit(1);
5502
+ }
5503
+ await unlockTemplate(args[0]);
5504
+ console.log(`${colors6.green}Unlocked ${args[0]}${colors6.reset}`);
5505
+ break;
5506
+ default:
5507
+ console.error(`${colors6.red}Unknown command: ${subCommand}${colors6.reset}`);
5508
+ printHelp();
5509
+ process.exit(1);
5510
+ }
5511
+ }
5512
+ async function handleUpdate(name, all) {
5513
+ if (all || !name) {
5514
+ console.log(`${colors6.cyan}Updating all templates...${colors6.reset}`);
5515
+ await installAllTemplates();
5516
+ console.log(`${colors6.green}All templates updated${colors6.reset}`);
5517
+ } else {
5518
+ console.log(`${colors6.cyan}Updating ${name}...${colors6.reset}`);
5519
+ await installTemplate(name);
5520
+ console.log(`${colors6.green}Updated ${name}${colors6.reset}`);
5521
+ }
5522
+ }
5523
+ async function handleList() {
5524
+ const templates = await listInstalledTemplates();
5525
+ console.log(`
5526
+ ${colors6.cyan}Installed templates:${colors6.reset}`);
5527
+ for (const t of templates) {
5528
+ const typeLabel = t.type === "dual" ? colors6.blue + "dual" : t.type === "partial" ? colors6.yellow + "partial" : colors6.dim + "single";
5529
+ console.log(` ${colors6.blue}•${colors6.reset} ${t.name} (${typeLabel}${colors6.reset})`);
5530
+ }
5531
+ console.log();
5532
+ }
5533
+ async function handleCheck() {
5534
+ const updates = await checkTemplateUpdates();
5535
+ const available = updates.filter((u) => u.updateAvailable);
5536
+ if (available.length === 0) {
5537
+ console.log(`${colors6.green}All templates are up to date${colors6.reset}`);
5538
+ } else {
5539
+ console.log(`${colors6.yellow}Updates available:${colors6.reset}`);
5540
+ for (const u of available) {
5541
+ console.log(` ${u.name}: ${u.installedVersion} -> ${u.bundledVersion}`);
5542
+ }
5543
+ }
5544
+ }
5545
+ var colors6, isMainModule5;
5546
+ var init_template_manager = __esm(() => {
5547
+ init_versioning();
5548
+ colors6 = {
5549
+ red: "\x1B[0;31m",
5550
+ green: "\x1B[0;32m",
5551
+ blue: "\x1B[0;34m",
5552
+ yellow: "\x1B[1;33m",
5553
+ cyan: "\x1B[0;36m",
5554
+ dim: "\x1B[2m",
5555
+ reset: "\x1B[0m"
5556
+ };
5557
+ isMainModule5 = process.argv[1]?.includes("template-manager");
5558
+ if (isMainModule5) {
5559
+ main5().catch(console.error);
5560
+ }
5561
+ });
5562
+
5413
5563
  // src/cli/formalconf.tsx
5414
- import { parseArgs as parseArgs5 } from "node:util";
5564
+ import { parseArgs as parseArgs6 } from "node:util";
5415
5565
  import { useState as useState11, useEffect as useEffect7 } from "react";
5416
5566
  import { render, useApp as useApp2, useInput as useInput11 } from "ink";
5417
5567
  import { Spinner as Spinner2 } from "@inkjs/ui";
@@ -5554,7 +5704,7 @@ function StatusIndicator({
5554
5704
  // package.json
5555
5705
  var package_default = {
5556
5706
  name: "formalconf",
5557
- version: "2.0.19",
5707
+ version: "2.0.20",
5558
5708
  description: "Dotfiles management TUI for macOS and Linux - config management, package sync, and theme switching",
5559
5709
  type: "module",
5560
5710
  main: "./dist/formalconf.js",
@@ -7778,7 +7928,7 @@ function ThemeMenu({ onBack }) {
7778
7928
  init_paths();
7779
7929
  init_runtime();
7780
7930
  import { jsxDEV as jsxDEV20 } from "react/jsx-dev-runtime";
7781
- function printHelp() {
7931
+ function printHelp2() {
7782
7932
  console.log(`
7783
7933
  FormalConf - Dotfiles Management TUI
7784
7934
 
@@ -7787,6 +7937,7 @@ Usage:
7787
7937
  formalconf theme <name> Apply a theme (e.g., nord:dark)
7788
7938
  formalconf config <cmd> Config management (stow, unstow, status, list)
7789
7939
  formalconf pkg-sync [flags] Sync packages from pkg-config.json
7940
+ formalconf template <cmd> Template management (update, list, check)
7790
7941
 
7791
7942
  Options:
7792
7943
  -h, --help Show this help message
@@ -7795,6 +7946,7 @@ Examples:
7795
7946
  formalconf theme nord:dark
7796
7947
  formalconf config stow nvim
7797
7948
  formalconf pkg-sync --purge
7949
+ formalconf template update --all
7798
7950
  `);
7799
7951
  }
7800
7952
  var BREADCRUMBS = {
@@ -7871,8 +8023,8 @@ function App() {
7871
8023
  ]
7872
8024
  }, undefined, true, undefined, this);
7873
8025
  }
7874
- async function main5() {
7875
- const { positionals, values } = parseArgs5({
8026
+ async function main6() {
8027
+ const { positionals, values } = parseArgs6({
7876
8028
  args: process.argv.slice(2),
7877
8029
  options: {
7878
8030
  help: { type: "boolean", short: "h" }
@@ -7882,36 +8034,42 @@ async function main5() {
7882
8034
  });
7883
8035
  const [subcommand] = positionals;
7884
8036
  if (values.help && !subcommand) {
7885
- printHelp();
8037
+ printHelp2();
7886
8038
  process.exit(0);
7887
8039
  }
7888
8040
  if (subcommand) {
7889
8041
  const scriptMap = {
7890
8042
  theme: "set-theme",
7891
8043
  config: "config-manager",
7892
- "pkg-sync": "pkg-sync"
8044
+ "pkg-sync": "pkg-sync",
8045
+ template: "template-manager"
7893
8046
  };
7894
8047
  const scriptName = scriptMap[subcommand];
7895
8048
  if (!scriptName) {
7896
8049
  console.error(`Unknown subcommand: ${subcommand}`);
7897
- printHelp();
8050
+ printHelp2();
7898
8051
  process.exit(1);
7899
8052
  }
7900
8053
  process.argv = [process.argv[0], scriptName, ...process.argv.slice(3)];
7901
8054
  switch (subcommand) {
7902
8055
  case "theme": {
7903
- const { main: main6 } = await Promise.resolve().then(() => (init_set_theme(), exports_set_theme));
7904
- await main6();
8056
+ const { main: main7 } = await Promise.resolve().then(() => (init_set_theme(), exports_set_theme));
8057
+ await main7();
7905
8058
  break;
7906
8059
  }
7907
8060
  case "config": {
7908
- const { main: main6 } = await Promise.resolve().then(() => (init_config_manager(), exports_config_manager));
7909
- await main6();
8061
+ const { main: main7 } = await Promise.resolve().then(() => (init_config_manager(), exports_config_manager));
8062
+ await main7();
7910
8063
  break;
7911
8064
  }
7912
8065
  case "pkg-sync": {
7913
- const { main: main6 } = await Promise.resolve().then(() => (init_pkg_sync(), exports_pkg_sync));
7914
- await main6();
8066
+ const { main: main7 } = await Promise.resolve().then(() => (init_pkg_sync(), exports_pkg_sync));
8067
+ await main7();
8068
+ break;
8069
+ }
8070
+ case "template": {
8071
+ const { main: main7 } = await Promise.resolve().then(() => (init_template_manager(), exports_template_manager));
8072
+ await main7();
7915
8073
  break;
7916
8074
  }
7917
8075
  }
@@ -7919,4 +8077,4 @@ async function main5() {
7919
8077
  }
7920
8078
  render(/* @__PURE__ */ jsxDEV20(App, {}, undefined, false, undefined, this));
7921
8079
  }
7922
- main5();
8080
+ main6();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "formalconf",
3
- "version": "2.0.19",
3
+ "version": "2.0.20",
4
4
  "description": "Dotfiles management TUI for macOS and Linux - config management, package sync, and theme switching",
5
5
  "type": "module",
6
6
  "main": "./dist/formalconf.js",