@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.
- package/dist/{chunk-XZ7VS36G.js → chunk-NOCP6JJX.js} +1 -1
- package/dist/chunk-NOCP6JJX.js.map +1 -0
- package/dist/cli.js +201 -171
- package/dist/cli.js.map +1 -1
- package/dist/components.service-LNJNZYH7.js +7 -0
- package/package.json +1 -1
- package/dist/chunk-XZ7VS36G.js.map +0 -1
- package/dist/components.service-NWAWKII3.js +0 -7
- /package/dist/{components.service-NWAWKII3.js.map → components.service-LNJNZYH7.js.map} +0 -0
|
@@ -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-
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1673
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2845
|
-
import { checkbox, confirm as
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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`));
|