@jvittechs/j 1.0.66 → 1.0.68

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.
@@ -318,4 +318,4 @@ export {
318
318
  NetworkError,
319
319
  ComponentsService
320
320
  };
321
- //# sourceMappingURL=chunk-XZ7VS36G.js.map
321
+ //# sourceMappingURL=chunk-NOCP6JJX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/components.service.ts","../src/errors/index.ts"],"sourcesContent":["import { promises as fs } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { gunzipSync } from 'zlib';\nimport { createHash } from 'crypto';\nimport type { Jai1Config } from '../types/framework.types.js';\nimport { NetworkError } from '../errors/index.js';\n\n/**\n * Component from API\n */\nexport interface Component {\n id: number;\n filepath: string;\n name: string;\n description: string | null;\n content: string | null;\n contentType: 'markdown' | 'bundle' | 'zip' | 'rule-preset'; // 'zip' for legacy compatibility\n version: string;\n downloads: number;\n status: string;\n tags?: string[];\n dependencies?: { filepath: string; required: boolean }[];\n}\n\n/**\n * Tag from API\n */\nexport interface Tag {\n tag: string;\n count: number;\n defaultTarget?: string[];\n}\n\n/**\n * Installed component info\n */\nexport interface InstalledComponent {\n filepath: string;\n version: string;\n checksum: string;\n installedAt: string;\n modified: boolean;\n}\n\n/**\n * Rule preset bundle structure\n */\ninterface RulePresetBundle {\n preset: {\n slug: string;\n name: string;\n description: string;\n version: string;\n tags: string[];\n stack: Record<string, string | null>;\n files: {\n required: string[];\n optional: string[];\n template: string[];\n };\n output?: {\n agentsMd?: boolean;\n ideFolder?: string;\n };\n };\n files: Record<string, string>;\n}\n\n/**\n * Service for managing components\n */\nexport class ComponentsService {\n private readonly cacheDir: string;\n private readonly manifestFile: string;\n\n constructor(projectRoot: string = process.cwd()) {\n this.cacheDir = join(homedir(), '.jai1', 'cache');\n this.manifestFile = join(projectRoot, '.jai1', 'manifest.json');\n }\n\n /**\n * Expand component paths with special prefixes\n *\n * Supported formats:\n * - Standard paths: \"rule-presets/react-spa-zustand\", \"rules/jai1.md\", \"workflows/commit-it.md\"\n * - Package prefix: \"package:core\" -> expands to all components in that package\n *\n * @param config - Jai1 config\n * @param paths - Array of component paths (may include special prefixes)\n * @returns Array of expanded component paths\n */\n async expandPaths(config: Jai1Config, paths: string[]): Promise<string[]> {\n const expandedPaths: string[] = [];\n\n for (const path of paths) {\n if (path.startsWith('package:')) {\n // Package prefix: fetch all components and filter by package\n const packageName = path.substring('package:'.length);\n const components = await this.list(config);\n\n // Currently only 'core' package is supported\n // All components in the API are from the core package\n if (packageName === 'core') {\n expandedPaths.push(...components.map(c => c.filepath));\n } else {\n console.warn(`Warning: Unknown package '${packageName}', skipping`);\n }\n } else {\n // Standard path: use as-is\n expandedPaths.push(path);\n }\n }\n\n return expandedPaths;\n }\n\n /**\n * List components from API\n */\n async list(config: Jai1Config, options?: { tag?: string; search?: string }): Promise<Component[]> {\n const params = new URLSearchParams();\n if (options?.tag) params.set('tag', options.tag);\n if (options?.search) params.set('search', options.search);\n\n const url = `${config.apiUrl}/api/components${params.toString() ? '?' + params.toString() : ''}`;\n\n const response = await fetch(url, {\n headers: { 'JAI1-Access-Key': config.accessKey },\n });\n\n if (!response.ok) {\n throw new NetworkError(`Failed to list components: HTTP ${response.status}`);\n }\n\n const data = await response.json() as { components: Component[] };\n return data.components;\n }\n\n /**\n * List available tags\n */\n async listTags(config: Jai1Config): Promise<Tag[]> {\n const response = await fetch(`${config.apiUrl}/api/tags`, {\n headers: { 'JAI1-Access-Key': config.accessKey },\n });\n\n if (!response.ok) {\n throw new NetworkError(`Failed to list tags: HTTP ${response.status}`);\n }\n\n const data = await response.json() as { tags: Tag[] };\n return data.tags;\n }\n\n /**\n * Get single component\n */\n async get(config: Jai1Config, filepath: string): Promise<Component> {\n const encodedPath = encodeURIComponent(filepath);\n const response = await fetch(`${config.apiUrl}/api/components/${encodedPath}`, {\n headers: { 'JAI1-Access-Key': config.accessKey },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(`Component not found: ${filepath}`);\n }\n throw new NetworkError(`Failed to get component: HTTP ${response.status}`);\n }\n\n return await response.json() as Component;\n }\n\n /**\n * Get core components\n */\n async getCore(config: Jai1Config): Promise<Component[]> {\n const response = await fetch(`${config.apiUrl}/api/components/core`, {\n headers: { 'JAI1-Access-Key': config.accessKey },\n });\n\n if (!response.ok) {\n throw new NetworkError(`Failed to get core components: HTTP ${response.status}`);\n }\n\n const data = await response.json() as { components: Component[] };\n return data.components;\n }\n\n /**\n * Download and install a component\n */\n async install(config: Jai1Config, filepath: string, targetDir: string): Promise<void> {\n const component = await this.get(config, filepath);\n\n if (!component.content) {\n throw new Error(`Component ${filepath} has no content`);\n }\n\n let checksumContent = component.content; // Default for simple types\n\n if (component.contentType === 'bundle' || component.contentType === 'zip') {\n // Decompress if gzipped (bundle type), or parse directly (legacy zip)\n let bundleJson: string;\n if (component.contentType === 'bundle') {\n // Gzipped base64 content\n const compressed = Buffer.from(component.content, 'base64');\n bundleJson = gunzipSync(compressed).toString('utf-8');\n checksumContent = bundleJson;\n } else {\n // Legacy: raw JSON string\n bundleJson = component.content;\n }\n\n const bundle = JSON.parse(bundleJson) as { main: string; assets: Record<string, string> };\n\n // Extract skill folder\n const skillDir = join(targetDir, filepath);\n await fs.mkdir(skillDir, { recursive: true });\n\n // Write main SKILL.md\n await fs.writeFile(join(skillDir, 'SKILL.md'), bundle.main);\n\n // Write assets\n for (const [assetPath, content] of Object.entries(bundle.assets)) {\n if (assetPath === 'SKILL.md') continue;\n const assetFullPath = join(skillDir, assetPath);\n await fs.mkdir(join(assetFullPath, '..'), { recursive: true });\n await fs.writeFile(assetFullPath, content);\n }\n } else if (component.contentType === 'rule-preset') {\n // Rule preset: gzipped base64 bundle with preset metadata and .mdc files\n // Single preset per project: always save to .jai1/rule-preset/ (singular)\n const compressed = Buffer.from(component.content, 'base64');\n const bundleJson = gunzipSync(compressed).toString('utf-8');\n checksumContent = bundleJson;\n\n const bundle = JSON.parse(bundleJson) as RulePresetBundle;\n\n // Single preset directory: .jai1/rule-preset/ (replaces old preset)\n const presetDir = join(targetDir, 'rule-preset');\n\n // Remove old preset if exists\n try {\n await fs.rm(presetDir, { recursive: true, force: true });\n } catch {\n // Directory doesn't exist, that's fine\n }\n\n await fs.mkdir(presetDir, { recursive: true });\n\n // Write preset.json\n await fs.writeFile(\n join(presetDir, 'preset.json'),\n JSON.stringify(bundle.preset, null, 2)\n );\n\n // Write all .mdc files\n for (const [filename, content] of Object.entries(bundle.files)) {\n const filePath = join(presetDir, filename);\n await fs.mkdir(join(filePath, '..'), { recursive: true });\n await fs.writeFile(filePath, content);\n }\n } else {\n // Checksum content for markdown is just the content\n if (component.contentType === 'markdown') {\n checksumContent = component.content;\n }\n // Regular markdown file - create parent directory\n const targetPath = join(targetDir, filepath);\n const targetFolder = join(targetPath, '..');\n await fs.mkdir(targetFolder, { recursive: true });\n await fs.writeFile(targetPath, component.content);\n }\n\n // Update installed.json\n await this.markInstalled(filepath, component.version, this.calculateChecksum(checksumContent));\n }\n\n /**\n * Get installed components\n */\n async getInstalled(): Promise<Record<string, InstalledComponent>> {\n try {\n const content = await fs.readFile(this.manifestFile, 'utf-8');\n return JSON.parse(content);\n } catch {\n return {};\n }\n }\n\n /**\n * Mark component as installed\n */\n async markInstalled(filepath: string, version: string, checksum: string): Promise<void> {\n const installed = await this.getInstalled();\n installed[filepath] = {\n filepath,\n version,\n checksum,\n installedAt: new Date().toISOString(),\n modified: false,\n };\n\n await fs.mkdir(join(this.manifestFile, '..'), { recursive: true });\n await fs.writeFile(this.manifestFile, JSON.stringify(installed, null, 2));\n }\n\n /**\n * Get checksums for multiple components\n */\n async getChecksums(config: Jai1Config, filepaths: string[]): Promise<Record<string, { version: string; checksum: string }>> {\n const response = await fetch(`${config.apiUrl}/api/components/checksums`, {\n method: 'POST',\n headers: {\n 'JAI1-Access-Key': config.accessKey,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ filepaths }),\n });\n\n if (!response.ok) {\n throw new NetworkError(`Failed to get checksums: HTTP ${response.status}`);\n }\n\n const data = await response.json() as { checksums: Record<string, { version: string; checksum: string }> };\n return data.checksums;\n }\n\n /**\n * Resolve dependencies recursively\n */\n async resolveWithDependencies(config: Jai1Config, filepaths: string[]): Promise<string[]> {\n const resolved = new Set<string>();\n const queue = [...filepaths];\n const seen = new Set<string>();\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (seen.has(current)) continue;\n seen.add(current);\n resolved.add(current);\n\n try {\n // We need metadata to check deps\n // For efficiency, we could batch fetch metadata, but for now simple get is fine\n // Optimization: getBatch API could be used here\n const component = await this.get(config, current);\n\n if (component.dependencies) {\n for (const dep of component.dependencies) {\n if (!dep.filepath) continue; // Should have filepath\n // Handle both string and object format if API changes\n const depPath = typeof dep === 'string' ? dep : dep.filepath;\n if (!seen.has(depPath)) {\n queue.push(depPath);\n }\n }\n }\n } catch (error) {\n console.warn(`Warning: Could not resolve dependencies for ${current}: ${error}`);\n }\n }\n\n return Array.from(resolved);\n }\n\n /**\n * Backup component file before update\n */\n async backupFile(filepath: string, targetDir: string): Promise<string | null> {\n const sourcePath = join(targetDir, filepath);\n\n try {\n await fs.access(sourcePath);\n } catch {\n return null; // File doesn't exist, no backup needed\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-');\n const backupDir = join(targetDir, '..', '.jai1_backup', timestamp);\n const backupPath = join(backupDir, filepath);\n\n const stats = await fs.stat(sourcePath);\n\n await fs.mkdir(join(backupPath, '..'), { recursive: true });\n\n if (stats.isDirectory()) {\n await fs.cp(sourcePath, backupPath, { recursive: true });\n } else {\n await fs.copyFile(sourcePath, backupPath);\n }\n\n return backupPath;\n }\n\n /**\n * List all backup directories\n */\n async listBackups(projectRoot: string): Promise<string[]> {\n const backupRoot = join(projectRoot, '.jai1_backup');\n try {\n const entries = await fs.readdir(backupRoot, { withFileTypes: true });\n return entries\n .filter(e => e.isDirectory())\n .map(e => e.name)\n .sort()\n .reverse();\n } catch {\n return [];\n }\n }\n\n /**\n * Clear all backups\n */\n async clearBackups(projectRoot: string): Promise<void> {\n const backupRoot = join(projectRoot, '.jai1_backup');\n try {\n await fs.rm(backupRoot, { recursive: true, force: true });\n } catch (error) {\n // Ignore error if dir doesn't exist\n }\n }\n\n /**\n * Calculate SHA256 checksum (matches server logic)\n */\n calculateChecksum(content: string): string {\n return createHash('sha256').update(content).digest('hex').substring(0, 16);\n }\n}\n","/**\n * Base error class for jai1-client\n */\nexport class Jai1Error extends Error {\n constructor(\n message: string,\n public exitCode: number\n ) {\n super(message);\n this.name = 'Jai1Error';\n }\n}\n\n/**\n * Validation error (exit code 2)\n */\nexport class ValidationError extends Jai1Error {\n constructor(message: string) {\n super(message, 2);\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Authentication error (exit code 3)\n */\nexport class AuthenticationError extends Jai1Error {\n constructor(message: string) {\n super(message, 3);\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Not found error (exit code 4)\n */\nexport class NotFoundError extends Jai1Error {\n constructor(message: string) {\n super(message, 4);\n this.name = 'NotFoundError';\n }\n}\n\n/**\n * Network/System error (exit code 5)\n */\nexport class NetworkError extends Jai1Error {\n constructor(message: string) {\n super(message, 5);\n this.name = 'NetworkError';\n }\n}\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  NetworkError,
6
6
  NotFoundError,
7
7
  ValidationError
8
- } from "./chunk-XZ7VS36G.js";
8
+ } from "./chunk-NOCP6JJX.js";
9
9
  import {
10
10
  createSettingsShowCommand
11
11
  } from "./chunk-5RDBJYO2.js";
@@ -160,7 +160,7 @@ import { basename as basename5 } from "path";
160
160
  // package.json
161
161
  var package_default = {
162
162
  name: "@jvittechs/j",
163
- version: "1.0.66",
163
+ version: "1.0.68",
164
164
  description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Supports both `j` and `jai1` commands. Please contact TeamAI for usage instructions.",
165
165
  type: "module",
166
166
  bin: {
@@ -618,7 +618,7 @@ async function getProjectStatus() {
618
618
  // src/commands/apply.ts
619
619
  import React4 from "react";
620
620
  import { render } from "ink";
621
- import { Command as Command3 } from "commander";
621
+ import { Command as Command4 } from "commander";
622
622
 
623
623
  // src/ui/apply/UnifiedApplyApp.tsx
624
624
  import React3, { useState, useEffect, useMemo } from "react";
@@ -1581,9 +1581,162 @@ var UnifiedApplyApp = ({
1581
1581
  }), filteredComponents.length === 0 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No components match your filter")), selectedPaths.size > 0 && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Selected: ", selectedPaths.size, " components"), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginLeft: 2 }, Array.from(selectedPaths).slice(0, 3).map((fp) => /* @__PURE__ */ React3.createElement(Text3, { key: fp, dimColor: true }, "\u{1F4CC} ", fp)), selectedPaths.size > 3 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ... +", selectedPaths.size - 3, " more"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, focusArea === "packages" && "[\u2190\u2192] Package \xB7 [\u2193] List \xB7 [\u2423] Select all", focusArea === "search" && "Type to filter \xB7 [\u2193] List", focusArea === "components" && "[\u2191\u2193] Navigate \xB7 [\u2423] Toggle", " \xB7 [Tab] Switch \xB7 [A] All \xB7 [C] Clear \xB7 [Enter] Apply")));
1582
1582
  };
1583
1583
 
1584
+ // src/commands/ide/sync.ts
1585
+ import { Command as Command3 } from "commander";
1586
+ import { checkbox, confirm } from "@inquirer/prompts";
1587
+
1588
+ // src/utils/prompt-theme.ts
1589
+ var keysHelpTipWithQuit = (keys) => {
1590
+ const keyStrings = keys.map(([key, action]) => `${key} ${action}`);
1591
+ keyStrings.push("Ctrl+C quit");
1592
+ return keyStrings.join(" \u2022 ");
1593
+ };
1594
+ var checkboxTheme = {
1595
+ style: {
1596
+ keysHelpTip: keysHelpTipWithQuit
1597
+ }
1598
+ };
1599
+ var selectTheme = {
1600
+ style: {
1601
+ keysHelpTip: keysHelpTipWithQuit
1602
+ }
1603
+ };
1604
+
1605
+ // src/commands/ide/sync.ts
1606
+ function createSyncSubcommand() {
1607
+ const cmd = new Command3("sync").description("Sync .jai1 content to IDE directories (Cursor, Windsurf, Claude Code, etc.)").option("--ides <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--type <types...>", "Content types (rules, workflows, commands)").option("--dry-run", "Preview changes without writing files").option("-y, --yes", "Headless mode (skip all prompts)").option("--all", "Select all available IDEs").action(async (options) => {
1608
+ await runSync(options);
1609
+ });
1610
+ return cmd;
1611
+ }
1612
+ async function runSync(options) {
1613
+ const service = new MigrateIdeService();
1614
+ const headless = options.yes === true;
1615
+ console.log("\n\u{1F504} Sync rules and workflows to IDE(s)\n");
1616
+ console.log("\u{1F4C1} Scanning .jai1/ directory...");
1617
+ console.log(" \u2022 .jai1/rule-preset/ (active rule preset)");
1618
+ console.log(" \u2022 .jai1/rules/ (manual rules)");
1619
+ console.log(" \u2022 .jai1/workflows/\n");
1620
+ const content = await service.scanJai1Content();
1621
+ if (content.totalCount === 0) {
1622
+ console.log("\u26A0\uFE0F No content found in .jai1/");
1623
+ console.log(' \u{1F4A1} Run "jai1 rules apply" to apply a rule preset, or');
1624
+ console.log(" \u{1F4A1} Create files in .jai1/rules/ or .jai1/workflows/\n");
1625
+ process.exit(1);
1626
+ }
1627
+ console.log(`\u2713 Found: ${content.rules.length} rules, ${content.workflows.length} workflows
1628
+ `);
1629
+ let selectedIdes;
1630
+ if (options.all) {
1631
+ selectedIdes = getMigrationIDEs();
1632
+ } else if (options.ides && options.ides.length > 0) {
1633
+ selectedIdes = options.ides;
1634
+ } else if (headless) {
1635
+ throw new Error(
1636
+ "Headless mode (-y) requires --ides or --all flag.\nExamples:\n j ide sync --ides cursor windsurf -y\n j ide sync --all -y"
1637
+ );
1638
+ } else {
1639
+ const ideChoices = getMigrationIDEs().map((ide) => {
1640
+ const config = IDE_MIGRATION_CONFIGS[ide];
1641
+ return {
1642
+ name: `${config.icon} ${config.name}`,
1643
+ value: ide
1644
+ };
1645
+ });
1646
+ selectedIdes = await checkbox({
1647
+ message: "Select target IDE(s) (SPACE to select, ENTER to confirm):",
1648
+ choices: ideChoices,
1649
+ theme: checkboxTheme
1650
+ });
1651
+ if (selectedIdes.length === 0) {
1652
+ console.log("\n\u26A0\uFE0F No IDE selected!");
1653
+ console.log(" \u{1F4A1} Select at least 1 IDE.\n");
1654
+ process.exit(0);
1655
+ }
1656
+ }
1657
+ let selectedTypes;
1658
+ if (options.type && options.type.length > 0) {
1659
+ selectedTypes = options.type;
1660
+ } else if (headless) {
1661
+ selectedTypes = ["rules", "workflows", "commands"];
1662
+ } else {
1663
+ const typeChoices = [
1664
+ { name: `Rules (${content.rules.length} files)`, value: "rules" },
1665
+ { name: `Workflows (${content.workflows.length} files)`, value: "workflows" },
1666
+ { name: `Commands (${content.commands.length} files)`, value: "commands" }
1667
+ ];
1668
+ selectedTypes = await checkbox({
1669
+ message: "Select content types to sync:",
1670
+ choices: typeChoices,
1671
+ theme: checkboxTheme
1672
+ });
1673
+ if (selectedTypes.length === 0) {
1674
+ console.log("\n\u26A0\uFE0F No content type selected!");
1675
+ console.log(" \u{1F4A1} Select at least 1 content type.\n");
1676
+ process.exit(0);
1677
+ }
1678
+ }
1679
+ const totalItems = selectedTypes.reduce((sum, type) => {
1680
+ return sum + (type === "rules" ? content.rules.length : type === "workflows" ? content.workflows.length : content.commands.length);
1681
+ }, 0);
1682
+ const totalFiles = totalItems * selectedIdes.length;
1683
+ console.log("\n\u{1F4CA} Preview:\n");
1684
+ console.log(` IDEs: ${selectedIdes.map((ide) => IDE_MIGRATION_CONFIGS[ide].name).join(", ")}`);
1685
+ console.log(` Content types: ${selectedTypes.join(", ")}`);
1686
+ console.log(` Will create/update: ${totalFiles} stub files
1687
+ `);
1688
+ if (options.dryRun) {
1689
+ console.log("\u{1F50D} DRY RUN - No files will be written\n");
1690
+ }
1691
+ if (!headless) {
1692
+ const confirmed = await confirm({
1693
+ message: "Proceed with sync?",
1694
+ default: true
1695
+ });
1696
+ if (!confirmed) {
1697
+ console.log("\n\u274C Sync cancelled.\n");
1698
+ process.exit(0);
1699
+ }
1700
+ }
1701
+ console.log("\n\u{1F504} Syncing...\n");
1702
+ let completed = 0;
1703
+ const results = await service.migrate(
1704
+ selectedIdes,
1705
+ selectedTypes,
1706
+ content,
1707
+ (result) => {
1708
+ completed++;
1709
+ const icon = result.status === "created" ? "\u2713" : result.status === "updated" ? "\u21BB" : result.status === "error" ? "\u2717" : "\u25CB";
1710
+ const percentage = Math.round(completed / totalFiles * 100);
1711
+ console.log(` ${icon} [${percentage}%] ${result.targetPath}`);
1712
+ if (result.status === "error" && result.error) {
1713
+ console.log(` Error: ${result.error}`);
1714
+ }
1715
+ }
1716
+ );
1717
+ const created = results.filter((r) => r.status === "created").length;
1718
+ const updated = results.filter((r) => r.status === "updated").length;
1719
+ const skipped = results.filter((r) => r.status === "skipped").length;
1720
+ const errors = results.filter((r) => r.status === "error").length;
1721
+ console.log("\n\u2705 Sync complete!\n");
1722
+ console.log(` Created: ${created}`);
1723
+ console.log(` Updated: ${updated}`);
1724
+ if (skipped > 0) console.log(` Skipped: ${skipped}`);
1725
+ if (errors > 0) console.log(` Errors: ${errors}`);
1726
+ console.log("");
1727
+ }
1728
+
1584
1729
  // src/commands/apply.ts
1730
+ var TARGET_IDE_MAP = {
1731
+ "cursor": "cursor",
1732
+ "windsurf": "windsurf",
1733
+ "antigravity": "antigravity",
1734
+ "claude": "claudecode",
1735
+ "claudecode": "claudecode",
1736
+ "opencode": "opencode"
1737
+ };
1585
1738
  function createApplyCommand() {
1586
- const cmd = new Command3("apply").description("Apply components to your project (interactive UI)").argument("[items...]", "Package name or component filepaths (non-interactive mode)").option("--force", "Force overwrite even if modified locally").option("--no-interactive", "Disable interactive UI (for CI/CD)").action(async (items, options) => {
1739
+ const cmd = new Command4("apply").description("Apply components to your project (interactive UI)").argument("[items...]", "Package name or component filepaths (non-interactive mode)").option("--force", "Force overwrite even if modified locally").option("--no-interactive", "Disable interactive UI (for CI/CD)").option("--target <ides...>", "Auto-sync to target IDE(s) after apply (claude, cursor, windsurf, etc.)").action(async (items, options) => {
1587
1740
  const configService = new ConfigService();
1588
1741
  const config = await configService.load();
1589
1742
  if (!config) {
@@ -1621,6 +1774,14 @@ async function nonInteractiveApply(config, items, options) {
1621
1774
  console.log(`\u{1F4E6} Applying package '${items[0]}'...`);
1622
1775
  const components = await componentsService.list(config, { tag: items[0] });
1623
1776
  filepaths = components.map((c) => c.filepath);
1777
+ const matchedTag = tags.find((t) => t.tag === items[0]);
1778
+ if (matchedTag?.defaultTarget?.length) {
1779
+ if (options.target) {
1780
+ console.log(`\u26A0\uFE0F Package '${items[0]}' has fixed target: ${matchedTag.defaultTarget.join(", ")}. Ignoring --target.`);
1781
+ }
1782
+ options.target = matchedTag.defaultTarget;
1783
+ console.log(`\u{1F3AF} Target: ${options.target.join(", ")}`);
1784
+ }
1624
1785
  } else {
1625
1786
  filepaths = items;
1626
1787
  }
@@ -1667,16 +1828,29 @@ async function nonInteractiveApply(config, items, options) {
1667
1828
  console.log(`
1668
1829
  \u2705 Complete: ${added} added, ${updated} updated, ${skipped} skipped`);
1669
1830
  console.log(`\u{1F4C1} Location: ${targetDir}`);
1670
- console.log(`
1831
+ if (options.target && options.target.length > 0) {
1832
+ const resolvedIdes = options.target.map((ide) => TARGET_IDE_MAP[ide.toLowerCase()]).filter((ide) => !!ide);
1833
+ if (resolvedIdes.length > 0) {
1834
+ console.log(`
1835
+ \u{1F504} Auto-syncing to IDE(s): ${resolvedIdes.join(", ")}...`);
1836
+ await runSync({ ides: resolvedIdes, yes: true });
1837
+ } else {
1838
+ const valid = Object.keys(TARGET_IDE_MAP).join(", ");
1839
+ console.log(`
1840
+ \u26A0\uFE0F Unknown --target IDE(s): ${options.target.join(", ")}. Valid: ${valid}`);
1841
+ }
1842
+ } else {
1843
+ console.log(`
1671
1844
  \u{1F4A1} B\u01B0\u1EDBc ti\u1EBFp theo:`);
1672
- console.log(` \u2022 Ch\u1EA1y "${name} ide sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 n\u1ED9i dung sang c\xE1c IDE b\u1EA1n \u0111ang d\xF9ng.`);
1673
- console.log(` \u2022 Ch\u1EA1y "${name} doctor" \u0111\u1EC3 ki\u1EC3m tra d\u1EF1 \xE1n \u0111\xE3 setup Jai1 \u0111\u1EA7y \u0111\u1EE7 ch\u01B0a.`);
1845
+ console.log(` \u2022 Ch\u1EA1y "${name} ide sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 n\u1ED9i dung sang c\xE1c IDE b\u1EA1n \u0111ang d\xF9ng.`);
1846
+ console.log(` \u2022 Ch\u1EA1y "${name} doctor" \u0111\u1EC3 ki\u1EC3m tra d\u1EF1 \xE1n \u0111\xE3 setup Jai1 \u0111\u1EA7y \u0111\u1EE7 ch\u01B0a.`);
1847
+ }
1674
1848
  }
1675
1849
 
1676
1850
  // src/commands/apply-new.ts
1677
1851
  import React6 from "react";
1678
1852
  import { render as render2 } from "ink";
1679
- import { Command as Command4 } from "commander";
1853
+ import { Command as Command5 } from "commander";
1680
1854
 
1681
1855
  // src/ui/apply/TwoPaneApplyApp.tsx
1682
1856
  import React5, { useState as useState2, useEffect as useEffect2, useMemo as useMemo2 } from "react";
@@ -2035,7 +2209,7 @@ var TwoPaneApplyApp = ({
2035
2209
 
2036
2210
  // src/commands/apply-new.ts
2037
2211
  function createApplyNewCommand() {
2038
- const cmd = new Command4("apply-new").description("Apply components (Two-Pane UI - experimental)").option("--force", "Force overwrite even if modified locally").action(async (options) => {
2212
+ const cmd = new Command5("apply-new").description("Apply components (Two-Pane UI - experimental)").option("--force", "Force overwrite even if modified locally").action(async (options) => {
2039
2213
  const configService = new ConfigService();
2040
2214
  const config = await configService.load();
2041
2215
  if (!config) {
@@ -2056,11 +2230,11 @@ function createApplyNewCommand() {
2056
2230
  }
2057
2231
 
2058
2232
  // src/commands/update.ts
2059
- import { Command as Command5 } from "commander";
2060
- import { confirm } from "@inquirer/prompts";
2233
+ import { Command as Command6 } from "commander";
2234
+ import { confirm as confirm2 } from "@inquirer/prompts";
2061
2235
  import chalk4 from "chalk";
2062
2236
  function createUpdateCommand() {
2063
- return new Command5("update").description("Update installed components to latest versions").option("--force", "Force update even if files are modified locally").action(async (options) => {
2237
+ return new Command6("update").description("Update installed components to latest versions").option("--force", "Force update even if files are modified locally").action(async (options) => {
2064
2238
  await handleUpdate(options);
2065
2239
  });
2066
2240
  }
@@ -2121,7 +2295,7 @@ async function handleUpdate(options) {
2121
2295
  }
2122
2296
  console.log();
2123
2297
  if (!options.force) {
2124
- const shouldUpdate = await confirm({ message: "C\u1EADp nh\u1EADt ngay?", default: true });
2298
+ const shouldUpdate = await confirm2({ message: "C\u1EADp nh\u1EADt ngay?", default: true });
2125
2299
  if (!shouldUpdate) {
2126
2300
  console.log(chalk4.dim("\u0110\xE3 h\u1EE7y c\u1EADp nh\u1EADt."));
2127
2301
  return;
@@ -2155,7 +2329,7 @@ async function handleUpdate(options) {
2155
2329
  if (backupPaths.length > 0) {
2156
2330
  console.log(chalk4.dim(`
2157
2331
  \u{1F4C1} \u0110\xE3 t\u1EA1o backup t\u1EA1i .jai1_backup/`));
2158
- const cleanBackups = await confirm({ message: "X\xF3a c\xE1c backup n\xE0y?", default: false });
2332
+ const cleanBackups = await confirm2({ message: "X\xF3a c\xE1c backup n\xE0y?", default: false });
2159
2333
  if (cleanBackups) {
2160
2334
  await componentsService.clearBackups(process.cwd());
2161
2335
  console.log(chalk4.green("\u{1F5D1}\uFE0F \u0110\xE3 x\xF3a backups."));
@@ -2170,11 +2344,11 @@ async function handleUpdate(options) {
2170
2344
  }
2171
2345
 
2172
2346
  // src/commands/framework/check.ts
2173
- import { Command as Command6 } from "commander";
2347
+ import { Command as Command7 } from "commander";
2174
2348
  import chalk5 from "chalk";
2175
2349
  import Table from "cli-table3";
2176
2350
  function createCheckCommand() {
2177
- const cmd = new Command6("check").description("Ki\u1EC3m tra c\u1EADp nh\u1EADt framework").option("--json", "Output as JSON").action(async (options) => {
2351
+ const cmd = new Command7("check").description("Ki\u1EC3m tra c\u1EADp nh\u1EADt framework").option("--json", "Output as JSON").action(async (options) => {
2178
2352
  await handleCheck(options);
2179
2353
  });
2180
2354
  return cmd;
@@ -2289,7 +2463,7 @@ import { Command as Command11 } from "commander";
2289
2463
  import chalk7 from "chalk";
2290
2464
 
2291
2465
  // src/commands/context.ts
2292
- import { Command as Command7 } from "commander";
2466
+ import { Command as Command8 } from "commander";
2293
2467
  import chalk6 from "chalk";
2294
2468
 
2295
2469
  // src/services/context-scanner.service.ts
@@ -2797,7 +2971,7 @@ function renderIDEContext(ideContext) {
2797
2971
  console.log("");
2798
2972
  }
2799
2973
  function createContextCommand() {
2800
- const cmd = new Command7("context").description("Visualize IDE context window token budget").argument("[ide]", "Show specific IDE only (cursor, windsurf, antigravity, claudecode, opencode)").action(async (ideArg) => {
2974
+ const cmd = new Command8("context").description("Visualize IDE context window token budget").argument("[ide]", "Show specific IDE only (cursor, windsurf, antigravity, claudecode, opencode)").action(async (ideArg) => {
2801
2975
  const name = getCliName();
2802
2976
  const allIDEs = ["cursor", "windsurf", "antigravity", "claudecode", "opencode"];
2803
2977
  if (ideArg) {
@@ -2841,30 +3015,11 @@ function createContextSubcommand() {
2841
3015
  }
2842
3016
 
2843
3017
  // src/commands/ide/setup.ts
2844
- import { Command as Command8 } from "commander";
2845
- import { checkbox, confirm as confirm2, select } from "@inquirer/prompts";
3018
+ import { Command as Command9 } from "commander";
3019
+ import { checkbox as checkbox2, confirm as confirm3, select } from "@inquirer/prompts";
2846
3020
  import fs5 from "fs/promises";
2847
3021
  import path3 from "path";
2848
3022
  import { existsSync } from "fs";
2849
-
2850
- // src/utils/prompt-theme.ts
2851
- var keysHelpTipWithQuit = (keys) => {
2852
- const keyStrings = keys.map(([key, action]) => `${key} ${action}`);
2853
- keyStrings.push("Ctrl+C quit");
2854
- return keyStrings.join(" \u2022 ");
2855
- };
2856
- var checkboxTheme = {
2857
- style: {
2858
- keysHelpTip: keysHelpTipWithQuit
2859
- }
2860
- };
2861
- var selectTheme = {
2862
- style: {
2863
- keysHelpTip: keysHelpTipWithQuit
2864
- }
2865
- };
2866
-
2867
- // src/commands/ide/setup.ts
2868
3023
  var PERFORMANCE_GROUPS = {
2869
3024
  telemetry: {
2870
3025
  name: "Telemetry",
@@ -3000,7 +3155,7 @@ var PERFORMANCE_GROUPS = {
3000
3155
  }
3001
3156
  };
3002
3157
  function createSetupSubcommand() {
3003
- const setupCommand = new Command8("setup").description("Configure IDE settings and optimizations");
3158
+ const setupCommand = new Command9("setup").description("Configure IDE settings and optimizations");
3004
3159
  setupCommand.action(async () => {
3005
3160
  await interactiveMode();
3006
3161
  });
@@ -3069,7 +3224,7 @@ async function selectGroupsToApply(action) {
3069
3224
  value: key
3070
3225
  }));
3071
3226
  try {
3072
- const selectedGroups = await checkbox({
3227
+ const selectedGroups = await checkbox2({
3073
3228
  message: `Select groups to ${action} (SPACE to select, ENTER to confirm):`,
3074
3229
  choices,
3075
3230
  theme: checkboxTheme
@@ -3106,7 +3261,7 @@ async function applyGroups(groupKeys, action) {
3106
3261
  console.log("\u{1F4C4} Read current settings from settings.json");
3107
3262
  } catch {
3108
3263
  console.warn("\u26A0\uFE0F Cannot read settings.json (may contain comments).");
3109
- const confirmOverwrite = await confirm2({
3264
+ const confirmOverwrite = await confirm3({
3110
3265
  message: "Overwrite current settings.json file?",
3111
3266
  default: false
3112
3267
  });
@@ -3153,7 +3308,7 @@ async function resetSettings(groupKeys) {
3153
3308
  console.log("\n\u26A0\uFE0F No settings.json file found");
3154
3309
  return;
3155
3310
  }
3156
- const confirmReset = await confirm2({
3311
+ const confirmReset = await confirm3({
3157
3312
  message: groupKeys.length === 0 ? "Reset ALL settings to default (delete entire file)?" : `Reset groups: ${groupKeys.join(", ")}?`,
3158
3313
  default: false
3159
3314
  });
@@ -3170,132 +3325,6 @@ async function resetSettings(groupKeys) {
3170
3325
  console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
3171
3326
  }
3172
3327
 
3173
- // src/commands/ide/sync.ts
3174
- import { Command as Command9 } from "commander";
3175
- import { checkbox as checkbox2, confirm as confirm3 } from "@inquirer/prompts";
3176
- function createSyncSubcommand() {
3177
- const cmd = new Command9("sync").description("Sync .jai1 content to IDE directories (Cursor, Windsurf, Claude Code, etc.)").option("--ides <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--type <types...>", "Content types (rules, workflows, commands)").option("--dry-run", "Preview changes without writing files").option("-y, --yes", "Headless mode (skip all prompts)").option("--all", "Select all available IDEs").action(async (options) => {
3178
- await runSync(options);
3179
- });
3180
- return cmd;
3181
- }
3182
- async function runSync(options) {
3183
- const service = new MigrateIdeService();
3184
- const headless = options.yes === true;
3185
- console.log("\n\u{1F504} Sync rules and workflows to IDE(s)\n");
3186
- console.log("\u{1F4C1} Scanning .jai1/ directory...");
3187
- console.log(" \u2022 .jai1/rule-preset/ (active rule preset)");
3188
- console.log(" \u2022 .jai1/rules/ (manual rules)");
3189
- console.log(" \u2022 .jai1/workflows/\n");
3190
- const content = await service.scanJai1Content();
3191
- if (content.totalCount === 0) {
3192
- console.log("\u26A0\uFE0F No content found in .jai1/");
3193
- console.log(' \u{1F4A1} Run "jai1 rules apply" to apply a rule preset, or');
3194
- console.log(" \u{1F4A1} Create files in .jai1/rules/ or .jai1/workflows/\n");
3195
- process.exit(1);
3196
- }
3197
- console.log(`\u2713 Found: ${content.rules.length} rules, ${content.workflows.length} workflows
3198
- `);
3199
- let selectedIdes;
3200
- if (options.all) {
3201
- selectedIdes = getMigrationIDEs();
3202
- } else if (options.ides && options.ides.length > 0) {
3203
- selectedIdes = options.ides;
3204
- } else if (headless) {
3205
- throw new Error(
3206
- "Headless mode (-y) requires --ides or --all flag.\nExamples:\n j ide sync --ides cursor windsurf -y\n j ide sync --all -y"
3207
- );
3208
- } else {
3209
- const ideChoices = getMigrationIDEs().map((ide) => {
3210
- const config = IDE_MIGRATION_CONFIGS[ide];
3211
- return {
3212
- name: `${config.icon} ${config.name}`,
3213
- value: ide
3214
- };
3215
- });
3216
- selectedIdes = await checkbox2({
3217
- message: "Select target IDE(s) (SPACE to select, ENTER to confirm):",
3218
- choices: ideChoices,
3219
- theme: checkboxTheme
3220
- });
3221
- if (selectedIdes.length === 0) {
3222
- console.log("\n\u26A0\uFE0F No IDE selected!");
3223
- console.log(" \u{1F4A1} Select at least 1 IDE.\n");
3224
- process.exit(0);
3225
- }
3226
- }
3227
- let selectedTypes;
3228
- if (options.type && options.type.length > 0) {
3229
- selectedTypes = options.type;
3230
- } else if (headless) {
3231
- selectedTypes = ["rules", "workflows", "commands"];
3232
- } else {
3233
- const typeChoices = [
3234
- { name: `Rules (${content.rules.length} files)`, value: "rules" },
3235
- { name: `Workflows (${content.workflows.length} files)`, value: "workflows" },
3236
- { name: `Commands (${content.commands.length} files)`, value: "commands" }
3237
- ];
3238
- selectedTypes = await checkbox2({
3239
- message: "Select content types to sync:",
3240
- choices: typeChoices,
3241
- theme: checkboxTheme
3242
- });
3243
- if (selectedTypes.length === 0) {
3244
- console.log("\n\u26A0\uFE0F No content type selected!");
3245
- console.log(" \u{1F4A1} Select at least 1 content type.\n");
3246
- process.exit(0);
3247
- }
3248
- }
3249
- const totalItems = selectedTypes.reduce((sum, type) => {
3250
- return sum + (type === "rules" ? content.rules.length : type === "workflows" ? content.workflows.length : content.commands.length);
3251
- }, 0);
3252
- const totalFiles = totalItems * selectedIdes.length;
3253
- console.log("\n\u{1F4CA} Preview:\n");
3254
- console.log(` IDEs: ${selectedIdes.map((ide) => IDE_MIGRATION_CONFIGS[ide].name).join(", ")}`);
3255
- console.log(` Content types: ${selectedTypes.join(", ")}`);
3256
- console.log(` Will create/update: ${totalFiles} stub files
3257
- `);
3258
- if (options.dryRun) {
3259
- console.log("\u{1F50D} DRY RUN - No files will be written\n");
3260
- }
3261
- if (!headless) {
3262
- const confirmed = await confirm3({
3263
- message: "Proceed with sync?",
3264
- default: true
3265
- });
3266
- if (!confirmed) {
3267
- console.log("\n\u274C Sync cancelled.\n");
3268
- process.exit(0);
3269
- }
3270
- }
3271
- console.log("\n\u{1F504} Syncing...\n");
3272
- let completed = 0;
3273
- const results = await service.migrate(
3274
- selectedIdes,
3275
- selectedTypes,
3276
- content,
3277
- (result) => {
3278
- completed++;
3279
- const icon = result.status === "created" ? "\u2713" : result.status === "updated" ? "\u21BB" : result.status === "error" ? "\u2717" : "\u25CB";
3280
- const percentage = Math.round(completed / totalFiles * 100);
3281
- console.log(` ${icon} [${percentage}%] ${result.targetPath}`);
3282
- if (result.status === "error" && result.error) {
3283
- console.log(` Error: ${result.error}`);
3284
- }
3285
- }
3286
- );
3287
- const created = results.filter((r) => r.status === "created").length;
3288
- const updated = results.filter((r) => r.status === "updated").length;
3289
- const skipped = results.filter((r) => r.status === "skipped").length;
3290
- const errors = results.filter((r) => r.status === "error").length;
3291
- console.log("\n\u2705 Sync complete!\n");
3292
- console.log(` Created: ${created}`);
3293
- console.log(` Updated: ${updated}`);
3294
- if (skipped > 0) console.log(` Skipped: ${skipped}`);
3295
- if (errors > 0) console.log(` Errors: ${errors}`);
3296
- console.log("");
3297
- }
3298
-
3299
3328
  // src/commands/ide/status.ts
3300
3329
  import { Command as Command10 } from "commander";
3301
3330
 
@@ -15000,7 +15029,7 @@ function createSkillsInfoCommand() {
15000
15029
  process.exit(1);
15001
15030
  }
15002
15031
  const filepath = name.startsWith("skills/") ? name : `skills/${name}`;
15003
- const { ComponentsService: ComponentsService2 } = await import("./components.service-NWAWKII3.js");
15032
+ const { ComponentsService: ComponentsService2 } = await import("./components.service-LNJNZYH7.js");
15004
15033
  const componentsService = new ComponentsService2();
15005
15034
  try {
15006
15035
  const component = await componentsService.get(config, filepath);
@@ -17100,10 +17129,11 @@ function showCustomHelp(version) {
17100
17129
  console.log(" rules Qu\u1EA3n l\xFD rule presets");
17101
17130
  console.log(" deps Qu\u1EA3n l\xFD dependencies");
17102
17131
  console.log(" redmine Redmine context sync");
17132
+ console.log(chalk57.bold("\n\u{1F6E0}\uFE0F Utilities"));
17133
+ console.log(" utils Developer utilities");
17103
17134
  console.log(chalk57.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
17104
17135
  console.log(" upgrade C\u1EADp nh\u1EADt CLI client");
17105
17136
  console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
17106
- console.log(" utils Developer utilities");
17107
17137
  const name = getCliName();
17108
17138
  console.log(chalk57.dim(`
17109
17139
  S\u1EED d\u1EE5ng: ${name} [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt`));