@jvittechs/j 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -169,7 +169,7 @@ import { basename as basename4 } from "path";
169
169
  // package.json
170
170
  var package_default = {
171
171
  name: "@jvittechs/j",
172
- version: "1.0.16",
172
+ version: "1.0.18",
173
173
  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.",
174
174
  type: "module",
175
175
  bin: {
@@ -1140,6 +1140,15 @@ function getMigrationIDEs() {
1140
1140
  return Object.keys(IDE_MIGRATION_CONFIGS);
1141
1141
  }
1142
1142
 
1143
+ // src/utils/heading-utils.ts
1144
+ function downshiftHeadings(content) {
1145
+ return content.replace(/^(#{1,5})\s/gm, (_match, hashes) => "#" + hashes + " ");
1146
+ }
1147
+ function extractBody(content) {
1148
+ const match = content.match(/^---\n[\s\S]*?\n---\n?([\s\S]*)$/);
1149
+ return match ? match[1].trim() : content.trim();
1150
+ }
1151
+
1143
1152
  // src/services/migrate-ide.service.ts
1144
1153
  var MigrateIdeService = class {
1145
1154
  projectPath;
@@ -1248,9 +1257,10 @@ var MigrateIdeService = class {
1248
1257
  return items;
1249
1258
  }
1250
1259
  /**
1251
- * Generate stub file content with @ reference
1260
+ * Generate copied content for IDE rule file.
1261
+ * Reads the actual source file and combines IDE-specific frontmatter with the body content.
1252
1262
  */
1253
- generateStubContent(ide, sourceItem) {
1263
+ async generateCopiedContent(ide, sourceItem) {
1254
1264
  const config = IDE_MIGRATION_CONFIGS[ide];
1255
1265
  if (!config) throw new Error(`Unknown IDE: ${ide}`);
1256
1266
  const frontmatter = config.generateFrontmatter({
@@ -1260,14 +1270,15 @@ var MigrateIdeService = class {
1260
1270
  alwaysApply: sourceItem.alwaysApply,
1261
1271
  sourceFile: sourceItem.relativePath
1262
1272
  });
1263
- const reference = `@${sourceItem.relativePath}`;
1273
+ const sourceContent = await fs5.readFile(sourceItem.filepath, "utf-8");
1274
+ const bodyContent = extractBody(sourceContent);
1264
1275
  if (frontmatter) {
1265
1276
  return `${frontmatter}
1266
1277
 
1267
- ${reference}
1278
+ ${bodyContent}
1268
1279
  `;
1269
1280
  }
1270
- return `${reference}
1281
+ return `${bodyContent}
1271
1282
  `;
1272
1283
  }
1273
1284
  /**
@@ -1311,12 +1322,15 @@ ${reference}
1311
1322
  }
1312
1323
  const lines = [
1313
1324
  "# AGENTS.md",
1314
- "",
1315
- "> Auto-generated by jai1 ide sync for OpenCode",
1316
1325
  ""
1317
1326
  ];
1318
1327
  for (const rule of rules) {
1319
- lines.push(`@${rule.relativePath}`);
1328
+ const content = await fs5.readFile(rule.filepath, "utf-8");
1329
+ const body = extractBody(content);
1330
+ if (body.trim()) {
1331
+ lines.push(downshiftHeadings(body));
1332
+ lines.push("");
1333
+ }
1320
1334
  }
1321
1335
  lines.push("");
1322
1336
  await fs5.writeFile(agentsPath, lines.join("\n"), "utf-8");
@@ -1372,8 +1386,8 @@ ${reference}
1372
1386
  status = "updated";
1373
1387
  } catch {
1374
1388
  }
1375
- const stubContent = this.generateStubContent(ide, item);
1376
- await fs5.writeFile(targetPath, stubContent, "utf-8");
1389
+ const copiedContent = await this.generateCopiedContent(ide, item);
1390
+ await fs5.writeFile(targetPath, copiedContent, "utf-8");
1377
1391
  return {
1378
1392
  source: item,
1379
1393
  targetIDE: ide,
@@ -3425,13 +3439,14 @@ async function resetSettings(groupKeys) {
3425
3439
  import { Command as Command9 } from "commander";
3426
3440
  import { checkbox as checkbox2, confirm as confirm3 } from "@inquirer/prompts";
3427
3441
  function createSyncSubcommand() {
3428
- const cmd = new Command9("sync").description("Sync .jai1 content to IDE directories (Cursor, Windsurf, Claude Code, etc.)").option("--ide <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--type <types...>", "Content types (rules, workflows, commands)").option("--dry-run", "Preview changes without writing files").action(async (options) => {
3442
+ 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) => {
3429
3443
  await runSync(options);
3430
3444
  });
3431
3445
  return cmd;
3432
3446
  }
3433
3447
  async function runSync(options) {
3434
3448
  const service = new MigrateIdeService();
3449
+ const headless = options.yes === true;
3435
3450
  console.log("\n\u{1F504} Sync rules and workflows to IDE(s)\n");
3436
3451
  console.log("\u{1F4C1} Scanning .jai1/ directory...");
3437
3452
  console.log(" \u2022 .jai1/rule-preset/ (active rule preset)");
@@ -3447,8 +3462,14 @@ async function runSync(options) {
3447
3462
  console.log(`\u2713 Found: ${content.rules.length} rules, ${content.workflows.length} workflows
3448
3463
  `);
3449
3464
  let selectedIdes;
3450
- if (options.ide && options.ide.length > 0) {
3451
- selectedIdes = options.ide;
3465
+ if (options.all) {
3466
+ selectedIdes = getMigrationIDEs();
3467
+ } else if (options.ides && options.ides.length > 0) {
3468
+ selectedIdes = options.ides;
3469
+ } else if (headless) {
3470
+ throw new Error(
3471
+ "Headless mode (-y) requires --ides or --all flag.\nExamples:\n j ide sync --ides cursor windsurf -y\n j ide sync --all -y"
3472
+ );
3452
3473
  } else {
3453
3474
  const ideChoices = getMigrationIDEs().map((ide) => {
3454
3475
  const config = IDE_MIGRATION_CONFIGS[ide];
@@ -3471,6 +3492,8 @@ async function runSync(options) {
3471
3492
  let selectedTypes;
3472
3493
  if (options.type && options.type.length > 0) {
3473
3494
  selectedTypes = options.type;
3495
+ } else if (headless) {
3496
+ selectedTypes = ["rules", "workflows", "commands"];
3474
3497
  } else {
3475
3498
  const typeChoices = [
3476
3499
  { name: `Rules (${content.rules.length} files)`, value: "rules" },
@@ -3500,13 +3523,15 @@ async function runSync(options) {
3500
3523
  if (options.dryRun) {
3501
3524
  console.log("\u{1F50D} DRY RUN - No files will be written\n");
3502
3525
  }
3503
- const confirmed = await confirm3({
3504
- message: "Proceed with sync?",
3505
- default: true
3506
- });
3507
- if (!confirmed) {
3508
- console.log("\n\u274C Sync cancelled.\n");
3509
- process.exit(0);
3526
+ if (!headless) {
3527
+ const confirmed = await confirm3({
3528
+ message: "Proceed with sync?",
3529
+ default: true
3530
+ });
3531
+ if (!confirmed) {
3532
+ console.log("\n\u274C Sync cancelled.\n");
3533
+ process.exit(0);
3534
+ }
3510
3535
  }
3511
3536
  console.log("\n\u{1F504} Syncing...\n");
3512
3537
  let completed = 0;
@@ -11968,7 +11993,7 @@ async function applyAgentsMdFormat(bundle) {
11968
11993
  import { Command as Command63 } from "commander";
11969
11994
  import { promises as fs25 } from "fs";
11970
11995
  import { join as join15 } from "path";
11971
- import { select as select5, confirm as confirm11, checkbox as checkbox5 } from "@inquirer/prompts";
11996
+ import { search, confirm as confirm11, checkbox as checkbox5 } from "@inquirer/prompts";
11972
11997
 
11973
11998
  // src/services/rules-generator.service.ts
11974
11999
  var RulesGeneratorService = class {
@@ -12053,7 +12078,7 @@ ${body.trim()}
12053
12078
  `;
12054
12079
  }
12055
12080
  /**
12056
- * Generate Antigravity format (.md files with trigger metadata + @AGENTS.md reference)
12081
+ * Generate Antigravity format (.md files with trigger metadata + actual content)
12057
12082
  */
12058
12083
  generateAntigravityFiles(bundle, format) {
12059
12084
  const files = [];
@@ -12084,9 +12109,7 @@ ${body.trim()}
12084
12109
  }
12085
12110
  const antigravityFrontmatter = `---
12086
12111
  trigger: ${trigger}
12087
- ---
12088
-
12089
- @AGENTS.md`;
12112
+ ---`;
12090
12113
  return `${antigravityFrontmatter}
12091
12114
 
12092
12115
  ${body.trim()}
@@ -12156,7 +12179,7 @@ ${body.trim()}
12156
12179
  const content = bundle.files[filename];
12157
12180
  const withoutFrontmatter = this.removeFrontmatter(content);
12158
12181
  if (withoutFrontmatter.trim()) {
12159
- sections.push(withoutFrontmatter.trim());
12182
+ sections.push(downshiftHeadings(withoutFrontmatter.trim()));
12160
12183
  sections.push("\n");
12161
12184
  }
12162
12185
  }
@@ -12481,7 +12504,7 @@ Restoring backup from ${metadata.timestamp}...`);
12481
12504
 
12482
12505
  // src/commands/rules/apply.ts
12483
12506
  function createRulesApplyCommand() {
12484
- return new Command63("apply").description("Apply rule preset to project with multi-IDE support").argument("[preset]", "Preset slug to apply (optional)").option("--ides <ides>", "Comma-separated list of IDE formats (cursor,windsurf,antigravity,claude,agentsmd,gemini)").option("--skip-backup", "Skip backup creation").option("-y, --yes", "Skip all confirmations (auto mode)").action(async (presetSlug, options) => {
12507
+ return new Command63("apply").description("Apply rule preset to project with multi-IDE support").argument("[preset]", "Preset slug to apply (optional)").option("--ides <ides>", 'Comma-separated list of IDE formats (cursor,windsurf,antigravity,claude,agentsmd,gemini) or "all"').option("--skip-backup", "Skip backup creation").option("-y, --yes", "Skip all confirmations (auto mode)").action(async (presetSlug, options) => {
12485
12508
  const configService = new ConfigService();
12486
12509
  const config = await configService.load();
12487
12510
  if (!config) {
@@ -12493,7 +12516,12 @@ function createRulesApplyCommand() {
12493
12516
  const skipBackup = options.skipBackup === true;
12494
12517
  let selectedIdes = [];
12495
12518
  if (options.ides) {
12496
- selectedIdes = options.ides.split(",").map((ide) => ide.trim()).filter((ide) => generatorService.isValidIde(ide));
12519
+ const idesValue = options.ides;
12520
+ if (idesValue.toLowerCase() === "all") {
12521
+ selectedIdes = generatorService.getAvailableIdes();
12522
+ } else {
12523
+ selectedIdes = idesValue.split(",").map((ide) => ide.trim()).filter((ide) => generatorService.isValidIde(ide));
12524
+ }
12497
12525
  if (selectedIdes.length === 0) {
12498
12526
  throw new ValidationError("No valid IDE formats specified");
12499
12527
  }
@@ -12513,13 +12541,20 @@ function createRulesApplyCommand() {
12513
12541
  console.log("No presets available.");
12514
12542
  return;
12515
12543
  }
12516
- presetSlug = await select5({
12517
- message: "Select a preset:",
12518
- choices: data.presets.map((p) => ({
12519
- name: `${p.name} - ${p.description}`,
12520
- value: p.slug,
12521
- description: `v${p.version} | ${p.tags.join(", ")}`
12522
- })),
12544
+ presetSlug = await search({
12545
+ message: "Search and select a preset (type keywords to filter):",
12546
+ source: async (input5) => {
12547
+ const keywords = (input5 || "").toLowerCase().split(/\s+/).filter(Boolean);
12548
+ const filtered = keywords.length === 0 ? data.presets : data.presets.filter((p) => {
12549
+ const searchable = [p.name, p.description, p.slug, ...p.tags].join(" ").toLowerCase();
12550
+ return keywords.every((kw) => searchable.includes(kw));
12551
+ });
12552
+ return filtered.map((p) => ({
12553
+ name: `${p.name} - ${p.description}`,
12554
+ value: p.slug,
12555
+ description: `v${p.version} | ${p.tags.join(", ")}`
12556
+ }));
12557
+ },
12523
12558
  theme: selectTheme
12524
12559
  });
12525
12560
  }
@@ -12542,7 +12577,9 @@ function createRulesApplyCommand() {
12542
12577
  console.log(` Files: ${Object.keys(bundle.files).length}`);
12543
12578
  if (selectedIdes.length === 0) {
12544
12579
  if (autoMode) {
12545
- selectedIdes = ["cursor"];
12580
+ throw new ValidationError(
12581
+ "Headless mode (-y) requires --ides flag.\nExamples:\n j rules apply <preset> --ides cursor,antigravity -y\n j rules apply <preset> --ides all -y"
12582
+ );
12546
12583
  } else {
12547
12584
  const detectionService = new IdeDetectionService();
12548
12585
  const suggestions = await detectionService.suggestIdes();
@@ -12719,13 +12756,52 @@ function createRulesApplyCommand() {
12719
12756
  console.log(' 3. Run "jai1 rules sync" to regenerate IDE outputs');
12720
12757
  console.log(" 4. Commit the rules to git");
12721
12758
  console.log(' 5. Use "jai1 ide status" to check IDE configuration\n');
12759
+ const migrationIdeIds = getMigrationIDEs();
12760
+ const ideSyncMap = {
12761
+ "cursor": "cursor",
12762
+ "windsurf": "windsurf",
12763
+ "antigravity": "antigravity",
12764
+ "claude": "claudecode",
12765
+ "agentsmd": "opencode",
12766
+ "opencode": "opencode"
12767
+ };
12768
+ const migrationIdes = resolvedIdes.map((ide) => ideSyncMap[ide]).filter((ide) => !!ide && migrationIdeIds.includes(ide)).filter((ide, index, self) => self.indexOf(ide) === index);
12769
+ if (migrationIdes.length > 0) {
12770
+ console.log("\u{1F504} Auto-syncing .jai1/ content to IDE(s)...\n");
12771
+ try {
12772
+ const migrateService = new MigrateIdeService();
12773
+ const content = await migrateService.scanJai1Content();
12774
+ if (content.totalCount > 0) {
12775
+ const contentTypes = ["rules", "workflows", "commands"];
12776
+ const results = await migrateService.migrate(
12777
+ migrationIdes,
12778
+ contentTypes,
12779
+ content,
12780
+ (result) => {
12781
+ const icon = result.status === "created" ? "\u2713" : result.status === "updated" ? "\u21BB" : result.status === "error" ? "\u2717" : "\u25CB";
12782
+ console.log(` ${icon} ${result.targetPath}`);
12783
+ }
12784
+ );
12785
+ const created = results.filter((r) => r.status === "created").length;
12786
+ const updated = results.filter((r) => r.status === "updated").length;
12787
+ console.log(`
12788
+ \u2705 IDE sync complete! (${created} created, ${updated} updated)
12789
+ `);
12790
+ } else {
12791
+ console.log(" No .jai1/ content found to sync.\n");
12792
+ }
12793
+ } catch (error) {
12794
+ console.warn("\u26A0\uFE0F IDE sync failed:", error instanceof Error ? error.message : error);
12795
+ console.log(' You can manually run "j ide sync" to retry.\n');
12796
+ }
12797
+ }
12722
12798
  });
12723
12799
  }
12724
12800
 
12725
12801
  // src/commands/rules/restore.ts
12726
12802
  import { Command as Command64 } from "commander";
12727
12803
  import { join as join16 } from "path";
12728
- import { select as select6, confirm as confirm12 } from "@inquirer/prompts";
12804
+ import { select as select5, confirm as confirm12 } from "@inquirer/prompts";
12729
12805
  function createRulesRestoreCommand() {
12730
12806
  return new Command64("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
12731
12807
  const backupService = new BackupService();
@@ -12740,7 +12816,7 @@ function createRulesRestoreCommand() {
12740
12816
  console.log(`\u{1F4E6} Selected latest backup: ${selectedBackup.timestamp}`);
12741
12817
  } else {
12742
12818
  console.log("\u{1F4CB} Available backups:\n");
12743
- const backupTimestamp = await select6({
12819
+ const backupTimestamp = await select5({
12744
12820
  message: "Select a backup to restore:",
12745
12821
  choices: backups.map((backup) => ({
12746
12822
  name: formatBackupInfo(backup),
@@ -13319,7 +13395,7 @@ function getInstallCommand(packageManager2) {
13319
13395
 
13320
13396
  // src/commands/clean.ts
13321
13397
  import { Command as Command69 } from "commander";
13322
- import { confirm as confirm15, select as select7 } from "@inquirer/prompts";
13398
+ import { confirm as confirm15, select as select6 } from "@inquirer/prompts";
13323
13399
  import { join as join19 } from "path";
13324
13400
  function createCleanCommand() {
13325
13401
  return new Command69("clean").description("Clean up backups, cache, and temporary files").option("-y, --yes", "Skip confirmation").option("--backups", "Clean only backup files").option("--all", "Clean all (backups + cache)").action(async (options) => {
@@ -13383,7 +13459,7 @@ async function handleClean(options) {
13383
13459
  console.log(` Path: ${target.path}
13384
13460
  `);
13385
13461
  }
13386
- const action = await select7({
13462
+ const action = await select6({
13387
13463
  message: "What do you want to clean?",
13388
13464
  choices: [
13389
13465
  ...availableTargets.map(({ target, info }) => ({
@@ -14566,7 +14642,7 @@ function createClearBackupsCommand() {
14566
14642
 
14567
14643
  // src/commands/vscode/index.ts
14568
14644
  import { Command as Command76 } from "commander";
14569
- import { checkbox as checkbox7, confirm as confirm18, select as select8 } from "@inquirer/prompts";
14645
+ import { checkbox as checkbox7, confirm as confirm18, select as select7 } from "@inquirer/prompts";
14570
14646
  import fs29 from "fs/promises";
14571
14647
  import path12 from "path";
14572
14648
  import { existsSync as existsSync3 } from "fs";
@@ -14749,7 +14825,7 @@ async function interactiveMode2() {
14749
14825
  console.log("\u2502 \u2022 Nh\u1EA5n ENTER \u0111\u1EC3 x\xE1c nh\u1EADn v\xE0 \xE1p d\u1EE5ng \u2502");
14750
14826
  console.log("\u2502 \u2022 Nh\u1EA5n Ctrl+C \u0111\u1EC3 h\u1EE7y \u2502");
14751
14827
  console.log("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n");
14752
- const action = await select8({
14828
+ const action = await select7({
14753
14829
  message: "B\u1EA1n mu\u1ED1n l\xE0m g\xEC?",
14754
14830
  choices: [
14755
14831
  { name: "\u2705 Enable c\xE1c nh\xF3m t\u1ED1i \u01B0u", value: "enable" },