@jvittechs/jai1-cli 0.1.106 → 1.0.0
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 +404 -346
- package/dist/cli.js.map +1 -1
- package/package.json +12 -12
- package/scripts/redmine-sync-issue.sh +0 -0
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var NetworkError = class extends Jai1Error {
|
|
|
33
33
|
// package.json
|
|
34
34
|
var package_default = {
|
|
35
35
|
name: "@jvittechs/jai1-cli",
|
|
36
|
-
version: "0.
|
|
36
|
+
version: "1.0.0",
|
|
37
37
|
description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Please contact TeamAI for usage instructions.",
|
|
38
38
|
type: "module",
|
|
39
39
|
bin: {
|
|
@@ -480,6 +480,34 @@ var ComponentsService = class {
|
|
|
480
480
|
this.cacheDir = join2(homedir3(), ".jai1", "cache");
|
|
481
481
|
this.manifestFile = join2(projectRoot, ".jai1", "manifest.json");
|
|
482
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* Expand component paths with special prefixes
|
|
485
|
+
*
|
|
486
|
+
* Supported formats:
|
|
487
|
+
* - Standard paths: "rule-presets/react-spa-zustand", "rules/jai1.md", "workflows/commit-it.md"
|
|
488
|
+
* - Package prefix: "package:core" -> expands to all components in that package
|
|
489
|
+
*
|
|
490
|
+
* @param config - Jai1 config
|
|
491
|
+
* @param paths - Array of component paths (may include special prefixes)
|
|
492
|
+
* @returns Array of expanded component paths
|
|
493
|
+
*/
|
|
494
|
+
async expandPaths(config, paths) {
|
|
495
|
+
const expandedPaths = [];
|
|
496
|
+
for (const path13 of paths) {
|
|
497
|
+
if (path13.startsWith("package:")) {
|
|
498
|
+
const packageName = path13.substring("package:".length);
|
|
499
|
+
const components = await this.list(config);
|
|
500
|
+
if (packageName === "core") {
|
|
501
|
+
expandedPaths.push(...components.map((c) => c.filepath));
|
|
502
|
+
} else {
|
|
503
|
+
console.warn(`Warning: Unknown package '${packageName}', skipping`);
|
|
504
|
+
}
|
|
505
|
+
} else {
|
|
506
|
+
expandedPaths.push(path13);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return expandedPaths;
|
|
510
|
+
}
|
|
483
511
|
/**
|
|
484
512
|
* List components from API
|
|
485
513
|
*/
|
|
@@ -547,9 +575,6 @@ var ComponentsService = class {
|
|
|
547
575
|
if (!component.content) {
|
|
548
576
|
throw new Error(`Component ${filepath} has no content`);
|
|
549
577
|
}
|
|
550
|
-
const targetPath = join2(targetDir, filepath);
|
|
551
|
-
const targetFolder = join2(targetPath, "..");
|
|
552
|
-
await fs2.mkdir(targetFolder, { recursive: true });
|
|
553
578
|
let checksumContent = component.content;
|
|
554
579
|
if (component.contentType === "bundle" || component.contentType === "zip") {
|
|
555
580
|
let bundleJson;
|
|
@@ -594,6 +619,9 @@ var ComponentsService = class {
|
|
|
594
619
|
if (component.contentType === "markdown") {
|
|
595
620
|
checksumContent = component.content;
|
|
596
621
|
}
|
|
622
|
+
const targetPath = join2(targetDir, filepath);
|
|
623
|
+
const targetFolder = join2(targetPath, "..");
|
|
624
|
+
await fs2.mkdir(targetFolder, { recursive: true });
|
|
597
625
|
await fs2.writeFile(targetPath, component.content);
|
|
598
626
|
}
|
|
599
627
|
await this.markInstalled(filepath, component.version, this.calculateChecksum(checksumContent));
|
|
@@ -1359,12 +1387,18 @@ var UnifiedApplyApp = ({
|
|
|
1359
1387
|
setAvailableIdes(getMigrationIDEs());
|
|
1360
1388
|
}, []);
|
|
1361
1389
|
const filteredComponents = useMemo(() => {
|
|
1390
|
+
if (focusArea === "packages" && tags.length > 0) {
|
|
1391
|
+
const selectedTag = tags[selectedPackageIndex];
|
|
1392
|
+
if (selectedTag) {
|
|
1393
|
+
return components.filter((c) => c.tags?.includes(selectedTag.tag));
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1362
1396
|
if (!searchQuery.trim()) return components;
|
|
1363
1397
|
const query = searchQuery.toLowerCase();
|
|
1364
1398
|
return components.filter(
|
|
1365
1399
|
(c) => c.filepath.toLowerCase().includes(query) || c.tags?.some((t) => t.toLowerCase().includes(query))
|
|
1366
1400
|
);
|
|
1367
|
-
}, [components, searchQuery]);
|
|
1401
|
+
}, [components, searchQuery, focusArea, selectedPackageIndex, tags]);
|
|
1368
1402
|
useInput((input5, key) => {
|
|
1369
1403
|
if (viewState === "done") {
|
|
1370
1404
|
if (key.return || input5 === "q" || key.escape) {
|
|
@@ -1480,7 +1514,6 @@ var UnifiedApplyApp = ({
|
|
|
1480
1514
|
packageComponents.forEach((c) => next.add(c.filepath));
|
|
1481
1515
|
return next;
|
|
1482
1516
|
});
|
|
1483
|
-
setSearchQuery(tag.tag);
|
|
1484
1517
|
}
|
|
1485
1518
|
}
|
|
1486
1519
|
}
|
|
@@ -1601,7 +1634,7 @@ var UnifiedApplyApp = ({
|
|
|
1601
1634
|
onChange: setSearchQuery,
|
|
1602
1635
|
placeholder: "Type to filter..."
|
|
1603
1636
|
}
|
|
1604
|
-
) : /* @__PURE__ */ React3.createElement(Text3, null, searchQuery || /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Press Tab to search"))), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, dimColor: true }, "Quick Apply (Packages):"), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexWrap: "wrap" }, tags.
|
|
1637
|
+
) : /* @__PURE__ */ React3.createElement(Text3, null, searchQuery || /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Press Tab to search"))), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, dimColor: true }, "Quick Apply (Packages):"), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexWrap: "wrap" }, tags.map((tag, i) => {
|
|
1605
1638
|
const isSelected = focusArea === "packages" && i === selectedPackageIndex;
|
|
1606
1639
|
return /* @__PURE__ */ React3.createElement(Box2, { key: tag.tag, marginRight: 1 }, /* @__PURE__ */ React3.createElement(
|
|
1607
1640
|
Text3,
|
|
@@ -1615,12 +1648,12 @@ var UnifiedApplyApp = ({
|
|
|
1615
1648
|
tag.count,
|
|
1616
1649
|
"]"
|
|
1617
1650
|
));
|
|
1618
|
-
}))), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: focusArea === "components" ? "cyan" : "gray", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Components "), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "(", filteredComponents.length, " shown", hasMore ? `, scroll for more` : "", ")")), visibleComponents.map((comp, i) => {
|
|
1651
|
+
})), focusArea === "packages" && tags.length > 0 && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "\u2190 \u2192 to browse packages \xB7 Space/Enter to select package"))), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: focusArea === "components" ? "cyan" : "gray", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Components "), focusArea === "packages" && tags.length > 0 && /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, "[", tags[selectedPackageIndex]?.tag, "] "), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "(", filteredComponents.length, " shown", hasMore ? `, scroll for more` : "", ")")), visibleComponents.map((comp, i) => {
|
|
1619
1652
|
const isCursor = i === cursorIndex && focusArea === "components";
|
|
1620
1653
|
const isChecked = selectedPaths.has(comp.filepath);
|
|
1621
1654
|
const isInstalled = installedPaths.has(comp.filepath);
|
|
1622
1655
|
return /* @__PURE__ */ React3.createElement(Box2, { key: comp.filepath }, /* @__PURE__ */ React3.createElement(Text3, { color: isCursor ? "cyan" : "white" }, isCursor ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", comp.filepath), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ", isInstalled ? "\u2713 installed" : "\u25CB new"));
|
|
1623
|
-
}), filteredComponents.length === 0 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No components match your search")), 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, 4).map((fp) => /* @__PURE__ */ React3.createElement(Text3, { key: fp, dimColor: true }, "\u{1F4CC} ", fp)), selectedPaths.size > 4 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ... and ", selectedPaths.size - 4, " more"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "[
|
|
1656
|
+
}), filteredComponents.length === 0 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No components match your search")), 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, 4).map((fp) => /* @__PURE__ */ React3.createElement(Text3, { key: fp, dimColor: true }, "\u{1F4CC} ", fp)), selectedPaths.size > 4 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ... and ", selectedPaths.size - 4, " more"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, focusArea === "packages" && "[\u2190\u2192] Browse packages \xB7 [\u2423/Enter] Select package", focusArea === "components" && "[\u2191\u2193] Navigate \xB7 [\u2423] Toggle", focusArea === "search" && "Type to search...", " \xB7 [Tab] Switch area \xB7 [A] All \xB7 [C] Clear \xB7 [Enter] Apply \xB7 [Q] Quit")));
|
|
1624
1657
|
};
|
|
1625
1658
|
|
|
1626
1659
|
// src/commands/apply.ts
|
|
@@ -1967,7 +2000,7 @@ var MainMenuView = ({ ideContexts, onSelect }) => {
|
|
|
1967
2000
|
{
|
|
1968
2001
|
ide: "antigravity",
|
|
1969
2002
|
icon: "\u{1F680}",
|
|
1970
|
-
title: "Antigravity
|
|
2003
|
+
title: "Antigravity",
|
|
1971
2004
|
itemCount: ideContexts.find((ctx) => ctx.ide === "antigravity")?.stats.totalItems || 0,
|
|
1972
2005
|
available: ideContexts.some((ctx) => ctx.ide === "antigravity")
|
|
1973
2006
|
},
|
|
@@ -2294,7 +2327,7 @@ var IDE_CONFIGS = {
|
|
|
2294
2327
|
},
|
|
2295
2328
|
antigravity: {
|
|
2296
2329
|
id: "antigravity",
|
|
2297
|
-
name: "Antigravity
|
|
2330
|
+
name: "Antigravity",
|
|
2298
2331
|
icon: "\u{1F680}",
|
|
2299
2332
|
basePath: ".agent",
|
|
2300
2333
|
contentPaths: {
|
|
@@ -2795,6 +2828,25 @@ import { checkbox, confirm as confirm2, select } from "@inquirer/prompts";
|
|
|
2795
2828
|
import fs6 from "fs/promises";
|
|
2796
2829
|
import path3 from "path";
|
|
2797
2830
|
import { existsSync } from "fs";
|
|
2831
|
+
|
|
2832
|
+
// src/utils/prompt-theme.ts
|
|
2833
|
+
var keysHelpTipWithQuit = (keys) => {
|
|
2834
|
+
const keyStrings = keys.map(([key, action]) => `${key} ${action}`);
|
|
2835
|
+
keyStrings.push("Ctrl+C quit");
|
|
2836
|
+
return keyStrings.join(" \u2022 ");
|
|
2837
|
+
};
|
|
2838
|
+
var checkboxTheme = {
|
|
2839
|
+
style: {
|
|
2840
|
+
keysHelpTip: keysHelpTipWithQuit
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
var selectTheme = {
|
|
2844
|
+
style: {
|
|
2845
|
+
keysHelpTip: keysHelpTipWithQuit
|
|
2846
|
+
}
|
|
2847
|
+
};
|
|
2848
|
+
|
|
2849
|
+
// src/commands/ide/setup.ts
|
|
2798
2850
|
var PERFORMANCE_GROUPS = {
|
|
2799
2851
|
telemetry: {
|
|
2800
2852
|
name: "Telemetry",
|
|
@@ -2981,7 +3033,8 @@ async function interactiveMode() {
|
|
|
2981
3033
|
{ name: "\u274C Disable optimization groups", value: "disable" },
|
|
2982
3034
|
{ name: "\u{1F680} Max Performance (enable all)", value: "max" },
|
|
2983
3035
|
{ name: "\u{1F504} Reset to defaults", value: "reset" }
|
|
2984
|
-
]
|
|
3036
|
+
],
|
|
3037
|
+
theme: selectTheme
|
|
2985
3038
|
});
|
|
2986
3039
|
if (action === "max") {
|
|
2987
3040
|
const allGroups = Object.keys(PERFORMANCE_GROUPS);
|
|
@@ -3000,7 +3053,8 @@ async function selectGroupsToApply(action) {
|
|
|
3000
3053
|
try {
|
|
3001
3054
|
const selectedGroups = await checkbox({
|
|
3002
3055
|
message: `Select groups to ${action} (SPACE to select, ENTER to confirm):`,
|
|
3003
|
-
choices
|
|
3056
|
+
choices,
|
|
3057
|
+
theme: checkboxTheme
|
|
3004
3058
|
});
|
|
3005
3059
|
if (selectedGroups.length === 0) {
|
|
3006
3060
|
console.log("\n\u26A0\uFE0F No groups selected!");
|
|
@@ -3136,7 +3190,8 @@ async function runSync(options) {
|
|
|
3136
3190
|
});
|
|
3137
3191
|
selectedIdes = await checkbox2({
|
|
3138
3192
|
message: "Select target IDE(s) (SPACE to select, ENTER to confirm):",
|
|
3139
|
-
choices: ideChoices
|
|
3193
|
+
choices: ideChoices,
|
|
3194
|
+
theme: checkboxTheme
|
|
3140
3195
|
});
|
|
3141
3196
|
if (selectedIdes.length === 0) {
|
|
3142
3197
|
console.log("\n\u26A0\uFE0F No IDE selected!");
|
|
@@ -3155,7 +3210,8 @@ async function runSync(options) {
|
|
|
3155
3210
|
];
|
|
3156
3211
|
selectedTypes = await checkbox2({
|
|
3157
3212
|
message: "Select content types to sync:",
|
|
3158
|
-
choices: typeChoices
|
|
3213
|
+
choices: typeChoices,
|
|
3214
|
+
theme: checkboxTheme
|
|
3159
3215
|
});
|
|
3160
3216
|
if (selectedTypes.length === 0) {
|
|
3161
3217
|
console.log("\n\u26A0\uFE0F No content type selected!");
|
|
@@ -3281,78 +3337,6 @@ var IDE_FORMATS = {
|
|
|
3281
3337
|
// Gemini requires AGENTS.md
|
|
3282
3338
|
}
|
|
3283
3339
|
};
|
|
3284
|
-
var JAI1_BASE_RULE = `# Jai1 Framework Agent
|
|
3285
|
-
|
|
3286
|
-
You are an AI coding assistant integrated with the Jai1 Framework.
|
|
3287
|
-
|
|
3288
|
-
## Skills Reference
|
|
3289
|
-
|
|
3290
|
-
The Jai1 Framework provides specialized skills that you can reference and apply:
|
|
3291
|
-
|
|
3292
|
-
### Pattern Detection
|
|
3293
|
-
|
|
3294
|
-
When you encounter:
|
|
3295
|
-
- **Component creation**: Reference \`@jai1-component-patterns\`
|
|
3296
|
-
- **API development**: Reference \`@jai1-api-patterns\`
|
|
3297
|
-
- **Database operations**: Reference \`@jai1-database-patterns\`
|
|
3298
|
-
- **Testing**: Reference \`@jai1-testing-patterns\`
|
|
3299
|
-
- **Deployment**: Reference \`@jai1-deployment-patterns\`
|
|
3300
|
-
|
|
3301
|
-
### Skill Loading Procedure
|
|
3302
|
-
|
|
3303
|
-
1. **Detect the task type** from user request
|
|
3304
|
-
2. **Load relevant skill** using \`@jai1-[skill-name]\` syntax
|
|
3305
|
-
3. **Apply patterns** from the skill to your response
|
|
3306
|
-
4. **Follow conventions** defined in the skill
|
|
3307
|
-
|
|
3308
|
-
Example:
|
|
3309
|
-
\`\`\`
|
|
3310
|
-
User: "Create a new API endpoint for users"
|
|
3311
|
-
Assistant:
|
|
3312
|
-
1. Loading @jai1-api-patterns
|
|
3313
|
-
2. Applying RESTful conventions
|
|
3314
|
-
3. Following project structure from @jai1-component-patterns
|
|
3315
|
-
4. Implementing validation using @jai1-testing-patterns
|
|
3316
|
-
\`\`\`
|
|
3317
|
-
|
|
3318
|
-
### Application
|
|
3319
|
-
|
|
3320
|
-
- **Always reference skills** before implementing patterns
|
|
3321
|
-
- **Follow naming conventions** from loaded skills
|
|
3322
|
-
- **Apply best practices** defined in skills
|
|
3323
|
-
- **Maintain consistency** with existing codebase patterns
|
|
3324
|
-
|
|
3325
|
-
### Naming Convention
|
|
3326
|
-
|
|
3327
|
-
Skills are named using the pattern: \`@jai1-[domain]-patterns\`
|
|
3328
|
-
|
|
3329
|
-
Examples:
|
|
3330
|
-
- \`@jai1-component-patterns\`
|
|
3331
|
-
- \`@jai1-api-patterns\`
|
|
3332
|
-
- \`@jai1-database-patterns\`
|
|
3333
|
-
- \`@jai1-testing-patterns\`
|
|
3334
|
-
- \`@jai1-deployment-patterns\`
|
|
3335
|
-
|
|
3336
|
-
## Best Practices
|
|
3337
|
-
|
|
3338
|
-
1. **Load before applying**: Always reference the appropriate skill before implementing
|
|
3339
|
-
2. **Follow project structure**: Use patterns from \`@jai1-component-patterns\`
|
|
3340
|
-
3. **Maintain consistency**: Apply conventions consistently across the codebase
|
|
3341
|
-
4. **Document decisions**: Explain why specific patterns are used
|
|
3342
|
-
|
|
3343
|
-
## Integration
|
|
3344
|
-
|
|
3345
|
-
This base rule works in conjunction with:
|
|
3346
|
-
- Project-specific rules (e.g., 01-project.md)
|
|
3347
|
-
- Technology-specific rules (e.g., 03-frontend.md)
|
|
3348
|
-
- Custom rules (e.g., 09-custom.md)
|
|
3349
|
-
- AGENTS.md (if present)
|
|
3350
|
-
|
|
3351
|
-
When conflicts arise, prioritize:
|
|
3352
|
-
1. Custom rules (09-custom.*)
|
|
3353
|
-
2. Project rules (01-project.*)
|
|
3354
|
-
3. This base rule (00-jai1.*)
|
|
3355
|
-
`;
|
|
3356
3340
|
|
|
3357
3341
|
// src/services/ide-detection.service.ts
|
|
3358
3342
|
var IdeDetectionService = class {
|
|
@@ -7830,7 +7814,8 @@ async function handleInteractiveFeedback(config) {
|
|
|
7830
7814
|
name: "\u{1F4DD} Suggestion - Share ideas or improvements",
|
|
7831
7815
|
value: "suggestion"
|
|
7832
7816
|
}
|
|
7833
|
-
]
|
|
7817
|
+
],
|
|
7818
|
+
theme: selectTheme
|
|
7834
7819
|
});
|
|
7835
7820
|
const title = await input({
|
|
7836
7821
|
message: "Title (max 200 characters):",
|
|
@@ -11145,7 +11130,7 @@ ${"\u2501".repeat(80)}`));
|
|
|
11145
11130
|
} else {
|
|
11146
11131
|
try {
|
|
11147
11132
|
const selected = await checkbox3({
|
|
11148
|
-
message: "Ch\u1ECDn packages \u0111\u1EC3 upgrade (\u2191\u2193 di chuy\u1EC3n \u2022 space ch\u1ECDn \u2022 a t\u1EA5t c\u1EA3 \u2022 i \u0111\u1EA3o \u2022 \u23CE x\xE1c nh\u1EADn
|
|
11133
|
+
message: "Ch\u1ECDn packages \u0111\u1EC3 upgrade (\u2191\u2193 di chuy\u1EC3n \u2022 space ch\u1ECDn \u2022 a t\u1EA5t c\u1EA3 \u2022 i \u0111\u1EA3o \u2022 \u23CE x\xE1c nh\u1EADn):",
|
|
11149
11134
|
choices: packages.map((pkg) => {
|
|
11150
11135
|
const icon = pkg.upgradeType === "major" ? "\u{1F534}" : pkg.upgradeType === "minor" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
11151
11136
|
return {
|
|
@@ -11154,7 +11139,8 @@ ${"\u2501".repeat(80)}`));
|
|
|
11154
11139
|
checked: true
|
|
11155
11140
|
};
|
|
11156
11141
|
}),
|
|
11157
|
-
pageSize: 15
|
|
11142
|
+
pageSize: 15,
|
|
11143
|
+
theme: checkboxTheme
|
|
11158
11144
|
});
|
|
11159
11145
|
if (selected.length === 0) {
|
|
11160
11146
|
console.log(chalk12.yellow("\u23F8\uFE0F Kh\xF4ng c\xF3 packages n\xE0o \u0111\u01B0\u1EE3c ch\u1ECDn\n"));
|
|
@@ -11590,7 +11576,8 @@ function createKitCreateCommand() {
|
|
|
11590
11576
|
const answer = await select3({
|
|
11591
11577
|
message: v.prompt,
|
|
11592
11578
|
choices: v.options.map((opt) => ({ name: opt, value: opt })),
|
|
11593
|
-
default: v.default
|
|
11579
|
+
default: v.default,
|
|
11580
|
+
theme: selectTheme
|
|
11594
11581
|
});
|
|
11595
11582
|
variables[v.name] = answer;
|
|
11596
11583
|
} else {
|
|
@@ -11609,9 +11596,13 @@ function createKitCreateCommand() {
|
|
|
11609
11596
|
if (!options.skipFramework && kit.config.framework?.apply) {
|
|
11610
11597
|
console.log("\u{1F916} Applying jai1 framework...");
|
|
11611
11598
|
const componentsService = new ComponentsService(targetDir);
|
|
11612
|
-
|
|
11599
|
+
const expandedPaths = await componentsService.expandPaths(
|
|
11600
|
+
config,
|
|
11601
|
+
kit.config.framework.components
|
|
11602
|
+
);
|
|
11603
|
+
for (const filepath of expandedPaths) {
|
|
11613
11604
|
console.log(` \u{1F4E5} Installing ${filepath}...`);
|
|
11614
|
-
await componentsService.install(config, filepath);
|
|
11605
|
+
await componentsService.install(config, filepath, join8(targetDir, ".jai1"));
|
|
11615
11606
|
}
|
|
11616
11607
|
console.log(" \u2713 Framework components applied");
|
|
11617
11608
|
}
|
|
@@ -11649,7 +11640,8 @@ function createKitCreateCommand() {
|
|
|
11649
11640
|
choices: ideChoices.map((c) => ({
|
|
11650
11641
|
...c,
|
|
11651
11642
|
checked: defaultTargets.includes(c.value)
|
|
11652
|
-
}))
|
|
11643
|
+
})),
|
|
11644
|
+
theme: checkboxTheme
|
|
11653
11645
|
});
|
|
11654
11646
|
selectedIdes = answer;
|
|
11655
11647
|
}
|
|
@@ -11793,7 +11785,7 @@ function createRulesListCommand() {
|
|
|
11793
11785
|
console.log(table.toString());
|
|
11794
11786
|
console.log();
|
|
11795
11787
|
}
|
|
11796
|
-
console.log(chalk16.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules
|
|
11788
|
+
console.log(chalk16.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
|
|
11797
11789
|
} catch (error) {
|
|
11798
11790
|
throw new Error(
|
|
11799
11791
|
`L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -11804,9 +11796,129 @@ function createRulesListCommand() {
|
|
|
11804
11796
|
|
|
11805
11797
|
// src/commands/rules/init.ts
|
|
11806
11798
|
import { Command as Command44 } from "commander";
|
|
11799
|
+
import { promises as fs20 } from "fs";
|
|
11800
|
+
import { join as join10 } from "path";
|
|
11801
|
+
import { select as select4, confirm as confirm7 } from "@inquirer/prompts";
|
|
11802
|
+
|
|
11803
|
+
// src/services/project-config.service.ts
|
|
11807
11804
|
import { promises as fs19 } from "fs";
|
|
11808
11805
|
import { join as join9 } from "path";
|
|
11809
|
-
|
|
11806
|
+
var ProjectConfigService = class {
|
|
11807
|
+
projectRoot;
|
|
11808
|
+
configDir;
|
|
11809
|
+
configPath;
|
|
11810
|
+
constructor(projectRoot = process.cwd()) {
|
|
11811
|
+
this.projectRoot = projectRoot;
|
|
11812
|
+
this.configDir = join9(this.projectRoot, ".jai1");
|
|
11813
|
+
this.configPath = join9(this.configDir, "project.json");
|
|
11814
|
+
}
|
|
11815
|
+
/**
|
|
11816
|
+
* Check if config file exists
|
|
11817
|
+
*/
|
|
11818
|
+
async exists() {
|
|
11819
|
+
try {
|
|
11820
|
+
await fs19.access(this.configPath);
|
|
11821
|
+
return true;
|
|
11822
|
+
} catch {
|
|
11823
|
+
return false;
|
|
11824
|
+
}
|
|
11825
|
+
}
|
|
11826
|
+
/**
|
|
11827
|
+
* Load full project configuration
|
|
11828
|
+
* @returns Config object or null if not found
|
|
11829
|
+
*/
|
|
11830
|
+
async load() {
|
|
11831
|
+
if (!await this.exists()) {
|
|
11832
|
+
return null;
|
|
11833
|
+
}
|
|
11834
|
+
try {
|
|
11835
|
+
const content = await fs19.readFile(this.configPath, "utf-8");
|
|
11836
|
+
return JSON.parse(content);
|
|
11837
|
+
} catch (error) {
|
|
11838
|
+
throw new Error(
|
|
11839
|
+
`Failed to load project config: ${error instanceof Error ? error.message : String(error)}`
|
|
11840
|
+
);
|
|
11841
|
+
}
|
|
11842
|
+
}
|
|
11843
|
+
/**
|
|
11844
|
+
* Save full project configuration
|
|
11845
|
+
* Creates .jai1 directory if it doesn't exist
|
|
11846
|
+
*/
|
|
11847
|
+
async save(config) {
|
|
11848
|
+
try {
|
|
11849
|
+
await fs19.mkdir(this.configDir, { recursive: true });
|
|
11850
|
+
await fs19.writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
11851
|
+
} catch (error) {
|
|
11852
|
+
throw new Error(
|
|
11853
|
+
`Failed to save project config: ${error instanceof Error ? error.message : String(error)}`
|
|
11854
|
+
);
|
|
11855
|
+
}
|
|
11856
|
+
}
|
|
11857
|
+
/**
|
|
11858
|
+
* Load rules configuration only
|
|
11859
|
+
* @returns RulesConfig or null if not found
|
|
11860
|
+
*/
|
|
11861
|
+
async loadRules() {
|
|
11862
|
+
const config = await this.load();
|
|
11863
|
+
return config?.rules ?? null;
|
|
11864
|
+
}
|
|
11865
|
+
/**
|
|
11866
|
+
* Save rules configuration
|
|
11867
|
+
* Merges with existing config, preserving other sections
|
|
11868
|
+
*/
|
|
11869
|
+
async saveRules(rulesConfig) {
|
|
11870
|
+
const existingConfig = await this.load() ?? {};
|
|
11871
|
+
existingConfig.rules = rulesConfig;
|
|
11872
|
+
await this.save(existingConfig);
|
|
11873
|
+
}
|
|
11874
|
+
/**
|
|
11875
|
+
* Update rules configuration partially
|
|
11876
|
+
* Merges with existing rules config
|
|
11877
|
+
*/
|
|
11878
|
+
async updateRules(partialRulesConfig) {
|
|
11879
|
+
const existingRules = await this.loadRules();
|
|
11880
|
+
if (!existingRules) {
|
|
11881
|
+
throw new Error('No rules configuration found. Run "jai1 rules apply" first.');
|
|
11882
|
+
}
|
|
11883
|
+
const updatedRules = {
|
|
11884
|
+
...existingRules,
|
|
11885
|
+
...partialRulesConfig
|
|
11886
|
+
};
|
|
11887
|
+
await this.saveRules(updatedRules);
|
|
11888
|
+
}
|
|
11889
|
+
/**
|
|
11890
|
+
* Add a backup entry to rules config
|
|
11891
|
+
* Keeps only the last 5 backups
|
|
11892
|
+
*/
|
|
11893
|
+
async addBackup(backup) {
|
|
11894
|
+
const rules = await this.loadRules();
|
|
11895
|
+
if (!rules) {
|
|
11896
|
+
throw new Error("No rules configuration found.");
|
|
11897
|
+
}
|
|
11898
|
+
const backups = [backup, ...rules.backups ?? []].slice(0, 5);
|
|
11899
|
+
await this.updateRules({ backups });
|
|
11900
|
+
}
|
|
11901
|
+
/**
|
|
11902
|
+
* Get config file path
|
|
11903
|
+
*/
|
|
11904
|
+
getConfigPath() {
|
|
11905
|
+
return this.configPath;
|
|
11906
|
+
}
|
|
11907
|
+
/**
|
|
11908
|
+
* Get config directory path (.jai1/)
|
|
11909
|
+
*/
|
|
11910
|
+
getConfigDir() {
|
|
11911
|
+
return this.configDir;
|
|
11912
|
+
}
|
|
11913
|
+
/**
|
|
11914
|
+
* Get project root path
|
|
11915
|
+
*/
|
|
11916
|
+
getProjectRoot() {
|
|
11917
|
+
return this.projectRoot;
|
|
11918
|
+
}
|
|
11919
|
+
};
|
|
11920
|
+
|
|
11921
|
+
// src/commands/rules/init.ts
|
|
11810
11922
|
function createRulesInitCommand() {
|
|
11811
11923
|
return new Command44("init").description("Apply rule preset to project").option("--preset <slug>", "Preset slug to apply").option("--output <format>", "Output format: cursor, agents-md, both (default: cursor)", "cursor").option("-y, --yes", "Skip confirmations").action(async (options) => {
|
|
11812
11924
|
const configService = new ConfigService();
|
|
@@ -11836,7 +11948,8 @@ function createRulesInitCommand() {
|
|
|
11836
11948
|
name: `${p.name} - ${p.description}`,
|
|
11837
11949
|
value: p.slug,
|
|
11838
11950
|
description: `v${p.version} | ${p.tags.join(", ")}`
|
|
11839
|
-
}))
|
|
11951
|
+
})),
|
|
11952
|
+
theme: selectTheme
|
|
11840
11953
|
});
|
|
11841
11954
|
}
|
|
11842
11955
|
console.log(`
|
|
@@ -11864,7 +11977,8 @@ function createRulesInitCommand() {
|
|
|
11864
11977
|
{ name: "Cursor (.cursor/rules/)", value: "cursor" },
|
|
11865
11978
|
{ name: "AGENTS.md (single file)", value: "agents-md" },
|
|
11866
11979
|
{ name: "Both", value: "both" }
|
|
11867
|
-
]
|
|
11980
|
+
],
|
|
11981
|
+
theme: selectTheme
|
|
11868
11982
|
});
|
|
11869
11983
|
}
|
|
11870
11984
|
if (!options.yes) {
|
|
@@ -11884,14 +11998,16 @@ function createRulesInitCommand() {
|
|
|
11884
11998
|
if (outputFormat === "agents-md" || outputFormat === "both") {
|
|
11885
11999
|
await applyAgentsMdFormat(bundle);
|
|
11886
12000
|
}
|
|
11887
|
-
const
|
|
12001
|
+
const projectConfigService = new ProjectConfigService();
|
|
12002
|
+
const rulesConfig = {
|
|
11888
12003
|
preset: bundle.preset.slug,
|
|
11889
12004
|
version: bundle.preset.version,
|
|
11890
12005
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12006
|
+
ides: [],
|
|
11891
12007
|
customContext: "09-custom.mdc"
|
|
11892
12008
|
};
|
|
11893
|
-
await
|
|
11894
|
-
console.log("\u2713 Created jai1
|
|
12009
|
+
await projectConfigService.saveRules(rulesConfig);
|
|
12010
|
+
console.log("\u2713 Created .jai1/project.json");
|
|
11895
12011
|
console.log("\n\u2705 Preset applied successfully!\n");
|
|
11896
12012
|
console.log("Next steps:");
|
|
11897
12013
|
console.log(" 1. Edit 09-custom.mdc to add project-specific rules");
|
|
@@ -11900,11 +12016,11 @@ function createRulesInitCommand() {
|
|
|
11900
12016
|
});
|
|
11901
12017
|
}
|
|
11902
12018
|
async function applyCursorFormat(bundle) {
|
|
11903
|
-
const rulesDir =
|
|
11904
|
-
await
|
|
12019
|
+
const rulesDir = join10(process.cwd(), ".cursor", "rules");
|
|
12020
|
+
await fs20.mkdir(rulesDir, { recursive: true });
|
|
11905
12021
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
11906
|
-
const filePath =
|
|
11907
|
-
await
|
|
12022
|
+
const filePath = join10(rulesDir, filename);
|
|
12023
|
+
await fs20.writeFile(filePath, content, "utf-8");
|
|
11908
12024
|
console.log(`\u2713 Created .cursor/rules/${filename}`);
|
|
11909
12025
|
}
|
|
11910
12026
|
}
|
|
@@ -11931,14 +12047,14 @@ async function applyAgentsMdFormat(bundle) {
|
|
|
11931
12047
|
}
|
|
11932
12048
|
}
|
|
11933
12049
|
const agentsMd = sections.join("\n");
|
|
11934
|
-
await
|
|
12050
|
+
await fs20.writeFile("AGENTS.md", agentsMd, "utf-8");
|
|
11935
12051
|
console.log("\u2713 Created AGENTS.md");
|
|
11936
12052
|
}
|
|
11937
12053
|
|
|
11938
12054
|
// src/commands/rules/apply.ts
|
|
11939
12055
|
import { Command as Command45 } from "commander";
|
|
11940
|
-
import { promises as
|
|
11941
|
-
import { join as
|
|
12056
|
+
import { promises as fs22 } from "fs";
|
|
12057
|
+
import { join as join12 } from "path";
|
|
11942
12058
|
import { select as select5, confirm as confirm8, checkbox as checkbox5 } from "@inquirer/prompts";
|
|
11943
12059
|
|
|
11944
12060
|
// src/services/rules-generator.service.ts
|
|
@@ -11973,11 +12089,6 @@ var RulesGeneratorService = class {
|
|
|
11973
12089
|
*/
|
|
11974
12090
|
generateCursorFiles(bundle, format) {
|
|
11975
12091
|
const files = [];
|
|
11976
|
-
files.push({
|
|
11977
|
-
path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
|
|
11978
|
-
content: this.generateCursorJai1Rule(),
|
|
11979
|
-
description: "Jai1 Framework base rule"
|
|
11980
|
-
});
|
|
11981
12092
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
11982
12093
|
files.push({
|
|
11983
12094
|
path: `${format.rulesPath}/${filename}`,
|
|
@@ -11988,28 +12099,11 @@ var RulesGeneratorService = class {
|
|
|
11988
12099
|
}
|
|
11989
12100
|
return files;
|
|
11990
12101
|
}
|
|
11991
|
-
/**
|
|
11992
|
-
* Generate Cursor jai1 base rule
|
|
11993
|
-
*/
|
|
11994
|
-
generateCursorJai1Rule() {
|
|
11995
|
-
return `---
|
|
11996
|
-
description: Jai1 Framework integration and skill loading
|
|
11997
|
-
globs: ["**/*"]
|
|
11998
|
-
alwaysApply: true
|
|
11999
|
-
---
|
|
12000
|
-
|
|
12001
|
-
${JAI1_BASE_RULE}`;
|
|
12002
|
-
}
|
|
12003
12102
|
/**
|
|
12004
12103
|
* Generate Windsurf format (.md files with trigger metadata)
|
|
12005
12104
|
*/
|
|
12006
12105
|
generateWindsurfFiles(bundle, format) {
|
|
12007
12106
|
const files = [];
|
|
12008
|
-
files.push({
|
|
12009
|
-
path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
|
|
12010
|
-
content: this.generateWindsurfJai1Rule(),
|
|
12011
|
-
description: "Jai1 Framework base rule"
|
|
12012
|
-
});
|
|
12013
12107
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
12014
12108
|
const converted = this.convertToWindsurf(content, filename);
|
|
12015
12109
|
const newFilename = filename.replace(/\.mdc$/, ".md");
|
|
@@ -12044,27 +12138,12 @@ trigger: ${trigger}
|
|
|
12044
12138
|
|
|
12045
12139
|
${body.trim()}
|
|
12046
12140
|
`;
|
|
12047
|
-
}
|
|
12048
|
-
/**
|
|
12049
|
-
* Generate Windsurf jai1 base rule
|
|
12050
|
-
*/
|
|
12051
|
-
generateWindsurfJai1Rule() {
|
|
12052
|
-
return `---
|
|
12053
|
-
trigger: always
|
|
12054
|
-
---
|
|
12055
|
-
|
|
12056
|
-
${JAI1_BASE_RULE}`;
|
|
12057
12141
|
}
|
|
12058
12142
|
/**
|
|
12059
12143
|
* Generate Antigravity format (.md files with trigger metadata + @AGENTS.md reference)
|
|
12060
12144
|
*/
|
|
12061
12145
|
generateAntigravityFiles(bundle, format) {
|
|
12062
12146
|
const files = [];
|
|
12063
|
-
files.push({
|
|
12064
|
-
path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
|
|
12065
|
-
content: this.generateAntigravityJai1Rule(),
|
|
12066
|
-
description: "Jai1 Framework base rule"
|
|
12067
|
-
});
|
|
12068
12147
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
12069
12148
|
const converted = this.convertToAntigravity(content, filename);
|
|
12070
12149
|
const newFilename = filename.replace(/\.mdc$/, ".md");
|
|
@@ -12099,29 +12178,12 @@ trigger: ${trigger}
|
|
|
12099
12178
|
|
|
12100
12179
|
${body.trim()}
|
|
12101
12180
|
`;
|
|
12102
|
-
}
|
|
12103
|
-
/**
|
|
12104
|
-
* Generate Antigravity jai1 base rule
|
|
12105
|
-
*/
|
|
12106
|
-
generateAntigravityJai1Rule() {
|
|
12107
|
-
return `---
|
|
12108
|
-
trigger: always_on
|
|
12109
|
-
---
|
|
12110
|
-
|
|
12111
|
-
@AGENTS.md
|
|
12112
|
-
|
|
12113
|
-
${JAI1_BASE_RULE}`;
|
|
12114
12181
|
}
|
|
12115
12182
|
/**
|
|
12116
12183
|
* Generate Claude Code format (.md files with paths metadata)
|
|
12117
12184
|
*/
|
|
12118
12185
|
generateClaudeFiles(bundle, format) {
|
|
12119
12186
|
const files = [];
|
|
12120
|
-
files.push({
|
|
12121
|
-
path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
|
|
12122
|
-
content: this.generateClaudeJai1Rule(),
|
|
12123
|
-
description: "Jai1 Framework base rule"
|
|
12124
|
-
});
|
|
12125
12187
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
12126
12188
|
const converted = this.convertToClaude(content, filename);
|
|
12127
12189
|
const newFilename = filename.replace(/\.mdc$/, ".md");
|
|
@@ -12155,17 +12217,6 @@ paths: ${paths}`;
|
|
|
12155
12217
|
|
|
12156
12218
|
${body.trim()}
|
|
12157
12219
|
`;
|
|
12158
|
-
}
|
|
12159
|
-
/**
|
|
12160
|
-
* Generate Claude jai1 base rule
|
|
12161
|
-
*/
|
|
12162
|
-
generateClaudeJai1Rule() {
|
|
12163
|
-
return `---
|
|
12164
|
-
description: Jai1 Framework integration and skill loading
|
|
12165
|
-
paths: **/*
|
|
12166
|
-
---
|
|
12167
|
-
|
|
12168
|
-
${JAI1_BASE_RULE}`;
|
|
12169
12220
|
}
|
|
12170
12221
|
/**
|
|
12171
12222
|
* Generate AGENTS.md file (single merged file)
|
|
@@ -12175,10 +12226,8 @@ ${JAI1_BASE_RULE}`;
|
|
|
12175
12226
|
sections.push("# AGENTS.md\n");
|
|
12176
12227
|
sections.push(`<!-- Generated by jai1 rules - Preset: ${bundle.preset.slug} v${bundle.preset.version} -->
|
|
12177
12228
|
`);
|
|
12178
|
-
sections.push("## Jai1 Framework Integration\n");
|
|
12179
|
-
sections.push(JAI1_BASE_RULE.trim());
|
|
12180
|
-
sections.push("\n---\n");
|
|
12181
12229
|
const fileOrder = [
|
|
12230
|
+
"00-jai1.mdc",
|
|
12182
12231
|
"01-project.mdc",
|
|
12183
12232
|
"02-standards.mdc",
|
|
12184
12233
|
"03-frontend.mdc",
|
|
@@ -12298,8 +12347,8 @@ Follow all instructions and patterns defined in AGENTS.md above.
|
|
|
12298
12347
|
};
|
|
12299
12348
|
|
|
12300
12349
|
// src/services/backup.service.ts
|
|
12301
|
-
import { promises as
|
|
12302
|
-
import { join as
|
|
12350
|
+
import { promises as fs21 } from "fs";
|
|
12351
|
+
import { join as join11, dirname } from "path";
|
|
12303
12352
|
var BackupService = class {
|
|
12304
12353
|
backupDir = ".jai1/backups";
|
|
12305
12354
|
/**
|
|
@@ -12307,7 +12356,7 @@ var BackupService = class {
|
|
|
12307
12356
|
*/
|
|
12308
12357
|
async createBackup(ides, presetSlug) {
|
|
12309
12358
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
12310
|
-
const backupPath =
|
|
12359
|
+
const backupPath = join11(this.backupDir, timestamp);
|
|
12311
12360
|
const backedUpFiles = [];
|
|
12312
12361
|
let hasContent = false;
|
|
12313
12362
|
for (const ideId of ides) {
|
|
@@ -12316,7 +12365,7 @@ var BackupService = class {
|
|
|
12316
12365
|
console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
|
|
12317
12366
|
continue;
|
|
12318
12367
|
}
|
|
12319
|
-
const rulesPath = format.rulesPath === "." ? process.cwd() :
|
|
12368
|
+
const rulesPath = format.rulesPath === "." ? process.cwd() : join11(process.cwd(), format.rulesPath);
|
|
12320
12369
|
try {
|
|
12321
12370
|
const exists = await this.pathExists(rulesPath);
|
|
12322
12371
|
if (!exists) {
|
|
@@ -12329,19 +12378,19 @@ var BackupService = class {
|
|
|
12329
12378
|
await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
|
|
12330
12379
|
hasContent = true;
|
|
12331
12380
|
} else {
|
|
12332
|
-
const stats = await
|
|
12381
|
+
const stats = await fs21.stat(rulesPath);
|
|
12333
12382
|
if (stats.isDirectory()) {
|
|
12334
|
-
const files = await
|
|
12383
|
+
const files = await fs21.readdir(rulesPath);
|
|
12335
12384
|
for (const file of files) {
|
|
12336
12385
|
if (file.endsWith(format.fileExtension)) {
|
|
12337
|
-
const originalPath =
|
|
12338
|
-
const relativePath =
|
|
12339
|
-
const destPath =
|
|
12340
|
-
await
|
|
12341
|
-
await
|
|
12386
|
+
const originalPath = join11(rulesPath, file);
|
|
12387
|
+
const relativePath = join11(format.rulesPath, file);
|
|
12388
|
+
const destPath = join11(backupPath, ideId, file);
|
|
12389
|
+
await fs21.mkdir(dirname(destPath), { recursive: true });
|
|
12390
|
+
await fs21.copyFile(originalPath, destPath);
|
|
12342
12391
|
backedUpFiles.push({
|
|
12343
12392
|
originalPath: relativePath,
|
|
12344
|
-
backupPath:
|
|
12393
|
+
backupPath: join11(ideId, file),
|
|
12345
12394
|
ide: ideId
|
|
12346
12395
|
});
|
|
12347
12396
|
hasContent = true;
|
|
@@ -12362,9 +12411,9 @@ var BackupService = class {
|
|
|
12362
12411
|
ides,
|
|
12363
12412
|
files: backedUpFiles
|
|
12364
12413
|
};
|
|
12365
|
-
await
|
|
12366
|
-
await
|
|
12367
|
-
|
|
12414
|
+
await fs21.mkdir(backupPath, { recursive: true });
|
|
12415
|
+
await fs21.writeFile(
|
|
12416
|
+
join11(backupPath, "metadata.json"),
|
|
12368
12417
|
JSON.stringify(metadata, null, 2),
|
|
12369
12418
|
"utf-8"
|
|
12370
12419
|
);
|
|
@@ -12374,18 +12423,18 @@ var BackupService = class {
|
|
|
12374
12423
|
* Backup a single file (for AGENTS.md, GEMINI.md)
|
|
12375
12424
|
*/
|
|
12376
12425
|
async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
|
|
12377
|
-
const originalPath =
|
|
12426
|
+
const originalPath = join11(process.cwd(), filename);
|
|
12378
12427
|
try {
|
|
12379
12428
|
const exists = await this.pathExists(originalPath);
|
|
12380
12429
|
if (!exists) {
|
|
12381
12430
|
return;
|
|
12382
12431
|
}
|
|
12383
|
-
const destPath =
|
|
12384
|
-
await
|
|
12385
|
-
await
|
|
12432
|
+
const destPath = join11(backupPath, ideId, filename);
|
|
12433
|
+
await fs21.mkdir(dirname(destPath), { recursive: true });
|
|
12434
|
+
await fs21.copyFile(originalPath, destPath);
|
|
12386
12435
|
backedUpFiles.push({
|
|
12387
12436
|
originalPath: filename,
|
|
12388
|
-
backupPath:
|
|
12437
|
+
backupPath: join11(ideId, filename),
|
|
12389
12438
|
ide: ideId
|
|
12390
12439
|
});
|
|
12391
12440
|
} catch (error) {
|
|
@@ -12395,16 +12444,16 @@ var BackupService = class {
|
|
|
12395
12444
|
* Restore files from a backup
|
|
12396
12445
|
*/
|
|
12397
12446
|
async restoreBackup(backupPath) {
|
|
12398
|
-
const metadataPath =
|
|
12399
|
-
const metadataContent = await
|
|
12447
|
+
const metadataPath = join11(backupPath, "metadata.json");
|
|
12448
|
+
const metadataContent = await fs21.readFile(metadataPath, "utf-8");
|
|
12400
12449
|
const metadata = JSON.parse(metadataContent);
|
|
12401
12450
|
console.log(`
|
|
12402
12451
|
Restoring backup from ${metadata.timestamp}...`);
|
|
12403
12452
|
for (const file of metadata.files) {
|
|
12404
|
-
const sourcePath =
|
|
12405
|
-
const destPath =
|
|
12406
|
-
await
|
|
12407
|
-
await
|
|
12453
|
+
const sourcePath = join11(backupPath, file.backupPath);
|
|
12454
|
+
const destPath = join11(process.cwd(), file.originalPath);
|
|
12455
|
+
await fs21.mkdir(dirname(destPath), { recursive: true });
|
|
12456
|
+
await fs21.copyFile(sourcePath, destPath);
|
|
12408
12457
|
console.log(`\u2713 Restored ${file.originalPath}`);
|
|
12409
12458
|
}
|
|
12410
12459
|
console.log("\n\u2705 Backup restored successfully!");
|
|
@@ -12414,18 +12463,18 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
12414
12463
|
*/
|
|
12415
12464
|
async listBackups() {
|
|
12416
12465
|
try {
|
|
12417
|
-
const backupDirPath =
|
|
12466
|
+
const backupDirPath = join11(process.cwd(), this.backupDir);
|
|
12418
12467
|
const exists = await this.pathExists(backupDirPath);
|
|
12419
12468
|
if (!exists) {
|
|
12420
12469
|
return [];
|
|
12421
12470
|
}
|
|
12422
|
-
const entries = await
|
|
12471
|
+
const entries = await fs21.readdir(backupDirPath, { withFileTypes: true });
|
|
12423
12472
|
const backups = [];
|
|
12424
12473
|
for (const entry of entries) {
|
|
12425
12474
|
if (entry.isDirectory()) {
|
|
12426
|
-
const metadataPath =
|
|
12475
|
+
const metadataPath = join11(backupDirPath, entry.name, "metadata.json");
|
|
12427
12476
|
try {
|
|
12428
|
-
const metadataContent = await
|
|
12477
|
+
const metadataContent = await fs21.readFile(metadataPath, "utf-8");
|
|
12429
12478
|
const metadata = JSON.parse(metadataContent);
|
|
12430
12479
|
backups.push(metadata);
|
|
12431
12480
|
} catch {
|
|
@@ -12442,7 +12491,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
12442
12491
|
* Delete a specific backup
|
|
12443
12492
|
*/
|
|
12444
12493
|
async deleteBackup(timestamp) {
|
|
12445
|
-
const backupPath =
|
|
12494
|
+
const backupPath = join11(process.cwd(), this.backupDir, timestamp);
|
|
12446
12495
|
await this.deleteDirectory(backupPath);
|
|
12447
12496
|
}
|
|
12448
12497
|
/**
|
|
@@ -12474,7 +12523,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
12474
12523
|
*/
|
|
12475
12524
|
async pathExists(path13) {
|
|
12476
12525
|
try {
|
|
12477
|
-
await
|
|
12526
|
+
await fs21.access(path13);
|
|
12478
12527
|
return true;
|
|
12479
12528
|
} catch {
|
|
12480
12529
|
return false;
|
|
@@ -12489,16 +12538,16 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
12489
12538
|
if (!exists) {
|
|
12490
12539
|
return;
|
|
12491
12540
|
}
|
|
12492
|
-
const entries = await
|
|
12541
|
+
const entries = await fs21.readdir(path13, { withFileTypes: true });
|
|
12493
12542
|
for (const entry of entries) {
|
|
12494
|
-
const fullPath =
|
|
12543
|
+
const fullPath = join11(path13, entry.name);
|
|
12495
12544
|
if (entry.isDirectory()) {
|
|
12496
12545
|
await this.deleteDirectory(fullPath);
|
|
12497
12546
|
} else {
|
|
12498
|
-
await
|
|
12547
|
+
await fs21.unlink(fullPath);
|
|
12499
12548
|
}
|
|
12500
12549
|
}
|
|
12501
|
-
await
|
|
12550
|
+
await fs21.rmdir(path13);
|
|
12502
12551
|
} catch (error) {
|
|
12503
12552
|
}
|
|
12504
12553
|
}
|
|
@@ -12506,14 +12555,14 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
12506
12555
|
* Get backup directory path
|
|
12507
12556
|
*/
|
|
12508
12557
|
getBackupDir() {
|
|
12509
|
-
return
|
|
12558
|
+
return join11(process.cwd(), this.backupDir);
|
|
12510
12559
|
}
|
|
12511
12560
|
/**
|
|
12512
12561
|
* Ensure backup directory exists
|
|
12513
12562
|
*/
|
|
12514
12563
|
async ensureBackupDir() {
|
|
12515
|
-
const backupDirPath =
|
|
12516
|
-
await
|
|
12564
|
+
const backupDirPath = join11(process.cwd(), this.backupDir);
|
|
12565
|
+
await fs21.mkdir(backupDirPath, { recursive: true });
|
|
12517
12566
|
}
|
|
12518
12567
|
};
|
|
12519
12568
|
|
|
@@ -12557,7 +12606,8 @@ function createRulesApplyCommand() {
|
|
|
12557
12606
|
name: `${p.name} - ${p.description}`,
|
|
12558
12607
|
value: p.slug,
|
|
12559
12608
|
description: `v${p.version} | ${p.tags.join(", ")}`
|
|
12560
|
-
}))
|
|
12609
|
+
})),
|
|
12610
|
+
theme: selectTheme
|
|
12561
12611
|
});
|
|
12562
12612
|
}
|
|
12563
12613
|
console.log(`
|
|
@@ -12627,7 +12677,8 @@ function createRulesApplyCommand() {
|
|
|
12627
12677
|
selectedIdes = await checkbox5({
|
|
12628
12678
|
message: "Select IDE formats to generate (pre-selected are recommended):",
|
|
12629
12679
|
choices,
|
|
12630
|
-
required: true
|
|
12680
|
+
required: true,
|
|
12681
|
+
theme: checkboxTheme
|
|
12631
12682
|
});
|
|
12632
12683
|
}
|
|
12633
12684
|
}
|
|
@@ -12669,21 +12720,21 @@ function createRulesApplyCommand() {
|
|
|
12669
12720
|
}
|
|
12670
12721
|
}
|
|
12671
12722
|
console.log("\n\u{1F4DD} Applying preset...\n");
|
|
12672
|
-
const rulePresetDir =
|
|
12723
|
+
const rulePresetDir = join12(process.cwd(), ".jai1", "rule-preset");
|
|
12673
12724
|
try {
|
|
12674
|
-
await
|
|
12725
|
+
await fs22.rm(rulePresetDir, { recursive: true, force: true });
|
|
12675
12726
|
} catch {
|
|
12676
12727
|
}
|
|
12677
|
-
await
|
|
12678
|
-
await
|
|
12679
|
-
|
|
12728
|
+
await fs22.mkdir(rulePresetDir, { recursive: true });
|
|
12729
|
+
await fs22.writeFile(
|
|
12730
|
+
join12(rulePresetDir, "preset.json"),
|
|
12680
12731
|
JSON.stringify(bundle.preset, null, 2),
|
|
12681
12732
|
"utf-8"
|
|
12682
12733
|
);
|
|
12683
12734
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
12684
|
-
const filePath =
|
|
12685
|
-
await
|
|
12686
|
-
await
|
|
12735
|
+
const filePath = join12(rulePresetDir, filename);
|
|
12736
|
+
await fs22.mkdir(join12(filePath, ".."), { recursive: true });
|
|
12737
|
+
await fs22.writeFile(filePath, content, "utf-8");
|
|
12687
12738
|
}
|
|
12688
12739
|
console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
|
|
12689
12740
|
const allGeneratedFiles = [];
|
|
@@ -12691,9 +12742,9 @@ function createRulesApplyCommand() {
|
|
|
12691
12742
|
try {
|
|
12692
12743
|
const files = generatorService.generateForIde(bundle, ideId);
|
|
12693
12744
|
for (const file of files) {
|
|
12694
|
-
const fullPath =
|
|
12695
|
-
await
|
|
12696
|
-
await
|
|
12745
|
+
const fullPath = join12(process.cwd(), file.path);
|
|
12746
|
+
await fs22.mkdir(join12(fullPath, ".."), { recursive: true });
|
|
12747
|
+
await fs22.writeFile(fullPath, file.content, "utf-8");
|
|
12697
12748
|
console.log(`\u2713 [${ideId}] ${file.path}`);
|
|
12698
12749
|
allGeneratedFiles.push({
|
|
12699
12750
|
ide: ideId,
|
|
@@ -12705,7 +12756,8 @@ function createRulesApplyCommand() {
|
|
|
12705
12756
|
console.error(`\u2717 Failed to generate ${ideId} files:`, error);
|
|
12706
12757
|
}
|
|
12707
12758
|
}
|
|
12708
|
-
const
|
|
12759
|
+
const projectConfigService = new ProjectConfigService();
|
|
12760
|
+
const rulesConfig = {
|
|
12709
12761
|
preset: bundle.preset.slug,
|
|
12710
12762
|
version: bundle.preset.version,
|
|
12711
12763
|
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -12720,24 +12772,18 @@ function createRulesApplyCommand() {
|
|
|
12720
12772
|
] : []
|
|
12721
12773
|
};
|
|
12722
12774
|
try {
|
|
12723
|
-
const
|
|
12724
|
-
|
|
12725
|
-
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
...projectConfig.backups,
|
|
12729
|
-
...existingConfig.backups.slice(0, 5)
|
|
12775
|
+
const existingRules = await projectConfigService.loadRules();
|
|
12776
|
+
if (existingRules?.backups && existingRules.backups.length > 0) {
|
|
12777
|
+
rulesConfig.backups = [
|
|
12778
|
+
...rulesConfig.backups,
|
|
12779
|
+
...existingRules.backups.slice(0, 5)
|
|
12730
12780
|
// Keep last 5 backups
|
|
12731
12781
|
];
|
|
12732
12782
|
}
|
|
12733
12783
|
} catch {
|
|
12734
12784
|
}
|
|
12735
|
-
await
|
|
12736
|
-
|
|
12737
|
-
JSON.stringify(projectConfig, null, 2),
|
|
12738
|
-
"utf-8"
|
|
12739
|
-
);
|
|
12740
|
-
console.log("\n\u2713 Updated jai1-rules.json");
|
|
12785
|
+
await projectConfigService.saveRules(rulesConfig);
|
|
12786
|
+
console.log("\n\u2713 Updated .jai1/project.json");
|
|
12741
12787
|
console.log("\n\u2705 Preset applied successfully!\n");
|
|
12742
12788
|
console.log("\u{1F4CA} Summary:");
|
|
12743
12789
|
console.log(` Generated ${allGeneratedFiles.length} files across ${resolvedIdes.length} IDE(s)`);
|
|
@@ -12765,7 +12811,7 @@ function createRulesApplyCommand() {
|
|
|
12765
12811
|
|
|
12766
12812
|
// src/commands/rules/restore.ts
|
|
12767
12813
|
import { Command as Command46 } from "commander";
|
|
12768
|
-
import { join as
|
|
12814
|
+
import { join as join13 } from "path";
|
|
12769
12815
|
import { select as select6, confirm as confirm9 } from "@inquirer/prompts";
|
|
12770
12816
|
function createRulesRestoreCommand() {
|
|
12771
12817
|
return new Command46("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
@@ -12787,7 +12833,8 @@ function createRulesRestoreCommand() {
|
|
|
12787
12833
|
name: formatBackupInfo(backup),
|
|
12788
12834
|
value: backup.timestamp,
|
|
12789
12835
|
description: `${backup.files.length} files | IDEs: ${backup.ides.join(", ")}`
|
|
12790
|
-
}))
|
|
12836
|
+
})),
|
|
12837
|
+
theme: selectTheme
|
|
12791
12838
|
});
|
|
12792
12839
|
selectedBackup = backups.find((b) => b.timestamp === backupTimestamp);
|
|
12793
12840
|
if (!selectedBackup) {
|
|
@@ -12811,7 +12858,7 @@ function createRulesRestoreCommand() {
|
|
|
12811
12858
|
}
|
|
12812
12859
|
console.log("\n\u{1F504} Restoring backup...\n");
|
|
12813
12860
|
try {
|
|
12814
|
-
const backupPath =
|
|
12861
|
+
const backupPath = join13(backupService.getBackupDir(), selectedBackup.timestamp);
|
|
12815
12862
|
await backupService.restoreBackup(backupPath);
|
|
12816
12863
|
console.log("\n\u2705 Backup restored successfully!\n");
|
|
12817
12864
|
console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
|
|
@@ -12837,23 +12884,41 @@ function formatTimestamp(timestamp) {
|
|
|
12837
12884
|
|
|
12838
12885
|
// src/commands/rules/sync.ts
|
|
12839
12886
|
import { Command as Command47 } from "commander";
|
|
12840
|
-
import { promises as
|
|
12841
|
-
import { join as
|
|
12842
|
-
import { checkbox as checkbox6, confirm as confirm10 } from "@inquirer/prompts";
|
|
12887
|
+
import { promises as fs23 } from "fs";
|
|
12888
|
+
import { join as join14 } from "path";
|
|
12889
|
+
import { checkbox as checkbox6, confirm as confirm10, Separator } from "@inquirer/prompts";
|
|
12843
12890
|
function createRulesSyncCommand() {
|
|
12844
12891
|
return new Command47("sync").description("Regenerate rule outputs for all configured IDEs").option("--ides <ides>", "Comma-separated list of IDEs to sync (default: all configured)").option("--detect", "Auto-detect active IDEs instead of using config").option("-y, --yes", "Skip confirmations").action(async (options) => {
|
|
12845
|
-
const
|
|
12846
|
-
|
|
12892
|
+
const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
|
|
12893
|
+
const presetJsonPath = join14(rulePresetDir, "preset.json");
|
|
12894
|
+
let presetExists = false;
|
|
12895
|
+
let presetData = null;
|
|
12847
12896
|
try {
|
|
12848
|
-
const
|
|
12849
|
-
|
|
12897
|
+
const presetContent = await fs23.readFile(presetJsonPath, "utf-8");
|
|
12898
|
+
presetData = JSON.parse(presetContent);
|
|
12899
|
+
presetExists = true;
|
|
12850
12900
|
} catch {
|
|
12901
|
+
}
|
|
12902
|
+
if (!presetExists) {
|
|
12851
12903
|
throw new ValidationError(
|
|
12852
|
-
'No
|
|
12904
|
+
'No rule preset found in .jai1/rule-preset/\nRun "jai1 rules apply <preset>" or create a project with "jai1 kit create <kit>" first.'
|
|
12853
12905
|
);
|
|
12854
12906
|
}
|
|
12907
|
+
const projectConfigService = new ProjectConfigService();
|
|
12908
|
+
let rulesConfig = await projectConfigService.loadRules();
|
|
12909
|
+
if (!rulesConfig) {
|
|
12910
|
+
console.log("\u26A0\uFE0F No .jai1/project.json found, creating from preset...\n");
|
|
12911
|
+
rulesConfig = {
|
|
12912
|
+
preset: presetData.slug,
|
|
12913
|
+
version: presetData.version,
|
|
12914
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12915
|
+
ides: [],
|
|
12916
|
+
customContext: "09-custom.md",
|
|
12917
|
+
backups: []
|
|
12918
|
+
};
|
|
12919
|
+
}
|
|
12855
12920
|
console.log("\u{1F504} Syncing rules...\n");
|
|
12856
|
-
console.log(`Preset: ${
|
|
12921
|
+
console.log(`Preset: ${rulesConfig.preset} v${rulesConfig.version}`);
|
|
12857
12922
|
let idesToSync = [];
|
|
12858
12923
|
if (options.detect) {
|
|
12859
12924
|
console.log("\n\u{1F50D} Auto-detecting active IDEs...");
|
|
@@ -12884,16 +12949,16 @@ Detected ${detected.length} active IDE(s):
|
|
|
12884
12949
|
idesToSync = detected.map((d) => d.id);
|
|
12885
12950
|
} else if (options.ides) {
|
|
12886
12951
|
const requested = options.ides.split(",").map((ide) => ide.trim());
|
|
12887
|
-
const configured =
|
|
12952
|
+
const configured = rulesConfig.ides || [];
|
|
12888
12953
|
idesToSync = requested;
|
|
12889
12954
|
const notConfigured = requested.filter((ide) => !configured.includes(ide));
|
|
12890
12955
|
if (notConfigured.length > 0) {
|
|
12891
12956
|
console.log(`
|
|
12892
12957
|
\u26A0\uFE0F IDEs not in config: ${notConfigured.join(", ")}`);
|
|
12893
|
-
console.log(" They will still be synced, but may not be in jai1
|
|
12958
|
+
console.log(" They will still be synced, but may not be in .jai1/project.json");
|
|
12894
12959
|
}
|
|
12895
12960
|
} else if (!options.yes) {
|
|
12896
|
-
const currentIdes =
|
|
12961
|
+
const currentIdes = rulesConfig.ides || [];
|
|
12897
12962
|
console.log(`
|
|
12898
12963
|
Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
12899
12964
|
const detectionService = new IdeDetectionService();
|
|
@@ -12913,18 +12978,23 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
|
12913
12978
|
}
|
|
12914
12979
|
const choices = buildIdeChoices(currentIdes, detected, suggestions);
|
|
12915
12980
|
idesToSync = await checkbox6({
|
|
12916
|
-
message: "Select IDE formats to sync
|
|
12981
|
+
message: "Select IDE formats to sync:",
|
|
12917
12982
|
choices,
|
|
12918
|
-
required:
|
|
12983
|
+
required: false,
|
|
12984
|
+
theme: checkboxTheme
|
|
12919
12985
|
});
|
|
12986
|
+
if (idesToSync.includes("__quit__")) {
|
|
12987
|
+
console.log("Cancelled.");
|
|
12988
|
+
return;
|
|
12989
|
+
}
|
|
12920
12990
|
if (idesToSync.length === 0) {
|
|
12921
12991
|
console.log("Cancelled.");
|
|
12922
12992
|
return;
|
|
12923
12993
|
}
|
|
12924
12994
|
} else {
|
|
12925
|
-
idesToSync =
|
|
12995
|
+
idesToSync = rulesConfig.ides || [];
|
|
12926
12996
|
if (idesToSync.length === 0) {
|
|
12927
|
-
console.log("\n\u26A0\uFE0F No IDEs configured in jai1
|
|
12997
|
+
console.log("\n\u26A0\uFE0F No IDEs configured in .jai1/project.json");
|
|
12928
12998
|
console.log(" Detecting from existing files...\n");
|
|
12929
12999
|
const detectionService = new IdeDetectionService();
|
|
12930
13000
|
idesToSync = await detectionService.detectExistingIdes();
|
|
@@ -12945,7 +13015,7 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
|
12945
13015
|
if (!config) {
|
|
12946
13016
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
12947
13017
|
}
|
|
12948
|
-
const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${
|
|
13018
|
+
const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${rulesConfig.preset}`, {
|
|
12949
13019
|
headers: {
|
|
12950
13020
|
"JAI1-Access-Key": config.accessKey
|
|
12951
13021
|
}
|
|
@@ -12954,21 +13024,13 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
|
12954
13024
|
throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
|
|
12955
13025
|
}
|
|
12956
13026
|
const bundle = await presetResponse.json();
|
|
12957
|
-
const
|
|
12958
|
-
const
|
|
12959
|
-
|
|
12960
|
-
|
|
12961
|
-
|
|
12962
|
-
|
|
12963
|
-
const filePath = join13(rulePresetDir, file);
|
|
12964
|
-
const content = await fs22.readFile(filePath, "utf-8");
|
|
12965
|
-
bundle.files[file] = content;
|
|
12966
|
-
}
|
|
13027
|
+
const files = await fs23.readdir(rulePresetDir);
|
|
13028
|
+
for (const file of files) {
|
|
13029
|
+
if (file.endsWith(".mdc") || file.endsWith(".md")) {
|
|
13030
|
+
const filePath = join14(rulePresetDir, file);
|
|
13031
|
+
const content = await fs23.readFile(filePath, "utf-8");
|
|
13032
|
+
bundle.files[file] = content;
|
|
12967
13033
|
}
|
|
12968
|
-
} else {
|
|
12969
|
-
console.log("\n\u26A0\uFE0F No rule preset found in .jai1/rule-preset/");
|
|
12970
|
-
console.log(' Run "jai1 rules apply" first to apply a preset.\n');
|
|
12971
|
-
return;
|
|
12972
13034
|
}
|
|
12973
13035
|
const generatorService = new RulesGeneratorService();
|
|
12974
13036
|
for (const ideId of idesToSync) {
|
|
@@ -12978,25 +13040,21 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
|
12978
13040
|
console.log(`\u26A0\uFE0F Unknown IDE format: ${ideId}, skipping`);
|
|
12979
13041
|
continue;
|
|
12980
13042
|
}
|
|
12981
|
-
const
|
|
12982
|
-
for (const file of
|
|
12983
|
-
const fullPath =
|
|
12984
|
-
await
|
|
12985
|
-
await
|
|
13043
|
+
const files2 = generatorService.generateForIde(bundle, ideId);
|
|
13044
|
+
for (const file of files2) {
|
|
13045
|
+
const fullPath = join14(process.cwd(), file.path);
|
|
13046
|
+
await fs23.mkdir(join14(fullPath, ".."), { recursive: true });
|
|
13047
|
+
await fs23.writeFile(fullPath, file.content, "utf-8");
|
|
12986
13048
|
}
|
|
12987
|
-
console.log(`\u2713 ${format.name} - ${
|
|
13049
|
+
console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
|
|
12988
13050
|
} catch (error) {
|
|
12989
13051
|
console.error(`\u2717 Failed to sync ${ideId}:`, error);
|
|
12990
13052
|
}
|
|
12991
13053
|
}
|
|
12992
|
-
if (JSON.stringify(
|
|
12993
|
-
|
|
12994
|
-
await
|
|
12995
|
-
|
|
12996
|
-
JSON.stringify(projectConfig, null, 2),
|
|
12997
|
-
"utf-8"
|
|
12998
|
-
);
|
|
12999
|
-
console.log("\n\u2713 Updated jai1-rules.json with synced IDEs");
|
|
13054
|
+
if (JSON.stringify(rulesConfig.ides) !== JSON.stringify(idesToSync)) {
|
|
13055
|
+
rulesConfig.ides = idesToSync;
|
|
13056
|
+
await projectConfigService.saveRules(rulesConfig);
|
|
13057
|
+
console.log("\n\u2713 Updated .jai1/project.json with synced IDEs");
|
|
13000
13058
|
}
|
|
13001
13059
|
console.log("\n\u2705 Rules synced successfully!\n");
|
|
13002
13060
|
console.log("\u{1F4A1} Next steps:");
|
|
@@ -13036,48 +13094,43 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
|
|
|
13036
13094
|
name: "Gemini CLI (GEMINI.md)",
|
|
13037
13095
|
value: "gemini",
|
|
13038
13096
|
checked: currentIdes.includes("gemini") || detected.some((d) => d.id === "gemini")
|
|
13097
|
+
},
|
|
13098
|
+
new Separator(),
|
|
13099
|
+
{
|
|
13100
|
+
name: "\u2716 Quit",
|
|
13101
|
+
value: "__quit__",
|
|
13102
|
+
checked: false
|
|
13039
13103
|
}
|
|
13040
13104
|
];
|
|
13041
13105
|
return choices;
|
|
13042
13106
|
}
|
|
13043
|
-
async function checkPathExists(absolutePath) {
|
|
13044
|
-
try {
|
|
13045
|
-
await fs22.access(absolutePath);
|
|
13046
|
-
return true;
|
|
13047
|
-
} catch {
|
|
13048
|
-
return false;
|
|
13049
|
-
}
|
|
13050
|
-
}
|
|
13051
13107
|
|
|
13052
13108
|
// src/commands/rules/info.ts
|
|
13053
13109
|
import { Command as Command48 } from "commander";
|
|
13054
|
-
import { promises as
|
|
13055
|
-
import { join as
|
|
13110
|
+
import { promises as fs24 } from "fs";
|
|
13111
|
+
import { join as join15 } from "path";
|
|
13056
13112
|
function createRulesInfoCommand() {
|
|
13057
13113
|
return new Command48("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
|
|
13058
|
-
const
|
|
13059
|
-
|
|
13060
|
-
|
|
13061
|
-
const configContent = await fs23.readFile(configPath, "utf-8");
|
|
13062
|
-
projectConfig = JSON.parse(configContent);
|
|
13063
|
-
} catch {
|
|
13114
|
+
const projectConfigService = new ProjectConfigService();
|
|
13115
|
+
const rulesConfig = await projectConfigService.loadRules();
|
|
13116
|
+
if (!rulesConfig) {
|
|
13064
13117
|
throw new ValidationError(
|
|
13065
|
-
'No jai1
|
|
13118
|
+
'No .jai1/project.json found. Run "jai1 rules apply" first.'
|
|
13066
13119
|
);
|
|
13067
13120
|
}
|
|
13068
13121
|
if (options.json) {
|
|
13069
|
-
console.log(JSON.stringify(
|
|
13122
|
+
console.log(JSON.stringify(rulesConfig, null, 2));
|
|
13070
13123
|
return;
|
|
13071
13124
|
}
|
|
13072
13125
|
console.log("\u{1F4CB} Current Preset Information\n");
|
|
13073
|
-
const rulePresetDir =
|
|
13074
|
-
const presetJsonPath =
|
|
13126
|
+
const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
|
|
13127
|
+
const presetJsonPath = join15(rulePresetDir, "preset.json");
|
|
13075
13128
|
let presetMetadata = null;
|
|
13076
13129
|
let presetFiles = [];
|
|
13077
13130
|
try {
|
|
13078
|
-
const presetContent = await
|
|
13131
|
+
const presetContent = await fs24.readFile(presetJsonPath, "utf-8");
|
|
13079
13132
|
presetMetadata = JSON.parse(presetContent);
|
|
13080
|
-
const files = await
|
|
13133
|
+
const files = await fs24.readdir(rulePresetDir);
|
|
13081
13134
|
presetFiles = files.filter((f) => f.endsWith(".mdc"));
|
|
13082
13135
|
} catch {
|
|
13083
13136
|
}
|
|
@@ -13089,10 +13142,10 @@ function createRulesInfoCommand() {
|
|
|
13089
13142
|
console.log(`Tags: ${presetMetadata.tags.join(", ")}`);
|
|
13090
13143
|
}
|
|
13091
13144
|
} else {
|
|
13092
|
-
console.log(`Preset: ${
|
|
13093
|
-
console.log(`Version: ${
|
|
13145
|
+
console.log(`Preset: ${rulesConfig.preset}`);
|
|
13146
|
+
console.log(`Version: ${rulesConfig.version}`);
|
|
13094
13147
|
}
|
|
13095
|
-
console.log(`Applied at: ${new Date(
|
|
13148
|
+
console.log(`Applied at: ${new Date(rulesConfig.appliedAt).toLocaleString()}`);
|
|
13096
13149
|
console.log(`
|
|
13097
13150
|
Source of Truth:`);
|
|
13098
13151
|
if (presetFiles.length > 0) {
|
|
@@ -13107,10 +13160,10 @@ Source of Truth:`);
|
|
|
13107
13160
|
console.log(` \u26A0\uFE0F .jai1/rule-preset/ not found`);
|
|
13108
13161
|
console.log(` Run "jai1 rules apply" to create it`);
|
|
13109
13162
|
}
|
|
13110
|
-
if (
|
|
13163
|
+
if (rulesConfig.ides && rulesConfig.ides.length > 0) {
|
|
13111
13164
|
console.log(`
|
|
13112
|
-
Configured IDEs (${
|
|
13113
|
-
for (const ideId of
|
|
13165
|
+
Configured IDEs (${rulesConfig.ides.length}):`);
|
|
13166
|
+
for (const ideId of rulesConfig.ides) {
|
|
13114
13167
|
const format = IDE_FORMATS[ideId];
|
|
13115
13168
|
if (format) {
|
|
13116
13169
|
const exists = await checkIdeFilesExist(ideId, format);
|
|
@@ -13119,15 +13172,15 @@ Configured IDEs (${projectConfig.ides.length}):`);
|
|
|
13119
13172
|
}
|
|
13120
13173
|
}
|
|
13121
13174
|
}
|
|
13122
|
-
if (
|
|
13175
|
+
if (rulesConfig.backups && rulesConfig.backups.length > 0) {
|
|
13123
13176
|
console.log(`
|
|
13124
|
-
Available Backups (${
|
|
13125
|
-
for (const backup of
|
|
13177
|
+
Available Backups (${rulesConfig.backups.length}):`);
|
|
13178
|
+
for (const backup of rulesConfig.backups.slice(0, 3)) {
|
|
13126
13179
|
const timestamp = new Date(backup.timestamp).toLocaleString();
|
|
13127
13180
|
console.log(` \u2022 ${timestamp} - IDEs: ${backup.ides.join(", ")}`);
|
|
13128
13181
|
}
|
|
13129
|
-
if (
|
|
13130
|
-
console.log(` ... and ${
|
|
13182
|
+
if (rulesConfig.backups.length > 3) {
|
|
13183
|
+
console.log(` ... and ${rulesConfig.backups.length - 3} more`);
|
|
13131
13184
|
}
|
|
13132
13185
|
}
|
|
13133
13186
|
console.log("\n\u2139\uFE0F Commands:");
|
|
@@ -13136,9 +13189,9 @@ Available Backups (${projectConfig.backups.length}):`);
|
|
|
13136
13189
|
console.log(' \u2022 "jai1 rules apply" - Apply a different preset (replaces current)');
|
|
13137
13190
|
});
|
|
13138
13191
|
}
|
|
13139
|
-
async function
|
|
13192
|
+
async function checkPathExists(path13) {
|
|
13140
13193
|
try {
|
|
13141
|
-
await
|
|
13194
|
+
await fs24.access(join15(process.cwd(), path13));
|
|
13142
13195
|
return true;
|
|
13143
13196
|
} catch {
|
|
13144
13197
|
return false;
|
|
@@ -13147,11 +13200,11 @@ async function checkPathExists2(path13) {
|
|
|
13147
13200
|
async function checkIdeFilesExist(ideId, format) {
|
|
13148
13201
|
try {
|
|
13149
13202
|
if (ideId === "agentsmd") {
|
|
13150
|
-
return await
|
|
13203
|
+
return await checkPathExists("AGENTS.md");
|
|
13151
13204
|
} else if (ideId === "gemini") {
|
|
13152
|
-
return await
|
|
13205
|
+
return await checkPathExists("GEMINI.md");
|
|
13153
13206
|
} else {
|
|
13154
|
-
return await
|
|
13207
|
+
return await checkPathExists(format.rulesPath);
|
|
13155
13208
|
}
|
|
13156
13209
|
} catch {
|
|
13157
13210
|
return false;
|
|
@@ -13167,7 +13220,7 @@ function showRulesHelp() {
|
|
|
13167
13220
|
console.log(` ${chalk17.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
|
|
13168
13221
|
console.log(` ${chalk17.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
|
|
13169
13222
|
console.log(` ${chalk17.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
|
|
13170
|
-
console.log(` ${chalk17.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules
|
|
13223
|
+
console.log(` ${chalk17.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
|
|
13171
13224
|
console.log(` ${chalk17.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
|
|
13172
13225
|
console.log();
|
|
13173
13226
|
console.log(chalk17.bold("V\xED d\u1EE5:"));
|
|
@@ -13354,7 +13407,7 @@ function getInstallCommand(packageManager2) {
|
|
|
13354
13407
|
// src/commands/clean.ts
|
|
13355
13408
|
import { Command as Command51 } from "commander";
|
|
13356
13409
|
import { confirm as confirm12, select as select7 } from "@inquirer/prompts";
|
|
13357
|
-
import { join as
|
|
13410
|
+
import { join as join16 } from "path";
|
|
13358
13411
|
function createCleanCommand() {
|
|
13359
13412
|
return new Command51("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) => {
|
|
13360
13413
|
await handleClean(options);
|
|
@@ -13367,7 +13420,7 @@ async function handleClean(options) {
|
|
|
13367
13420
|
{
|
|
13368
13421
|
name: "Backups",
|
|
13369
13422
|
description: "Component backup files (.jai1_backup/)",
|
|
13370
|
-
path:
|
|
13423
|
+
path: join16(cwd, ".jai1_backup"),
|
|
13371
13424
|
check: async () => {
|
|
13372
13425
|
const backups = await service.listBackups(cwd);
|
|
13373
13426
|
return { exists: backups.length > 0, count: backups.length };
|
|
@@ -13426,7 +13479,8 @@ async function handleClean(options) {
|
|
|
13426
13479
|
})),
|
|
13427
13480
|
{ name: "\u{1F9F9} Clean all", value: "all" },
|
|
13428
13481
|
{ name: "\u274C Cancel", value: "cancel" }
|
|
13429
|
-
]
|
|
13482
|
+
],
|
|
13483
|
+
theme: selectTheme
|
|
13430
13484
|
});
|
|
13431
13485
|
if (action === "cancel") {
|
|
13432
13486
|
console.log("\n\u274C Operation cancelled.");
|
|
@@ -14358,8 +14412,8 @@ async function handleSyncProject(options) {
|
|
|
14358
14412
|
|
|
14359
14413
|
// src/commands/framework/info.ts
|
|
14360
14414
|
import { Command as Command55 } from "commander";
|
|
14361
|
-
import { promises as
|
|
14362
|
-
import { join as
|
|
14415
|
+
import { promises as fs25 } from "fs";
|
|
14416
|
+
import { join as join17 } from "path";
|
|
14363
14417
|
import { homedir as homedir5 } from "os";
|
|
14364
14418
|
function createInfoCommand() {
|
|
14365
14419
|
const cmd = new Command55("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
|
|
@@ -14373,7 +14427,7 @@ async function handleInfo(options) {
|
|
|
14373
14427
|
if (!config) {
|
|
14374
14428
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
14375
14429
|
}
|
|
14376
|
-
const frameworkPath =
|
|
14430
|
+
const frameworkPath = join17(homedir5(), ".jai1", "framework");
|
|
14377
14431
|
const projectStatus = await getProjectStatus2();
|
|
14378
14432
|
const info = {
|
|
14379
14433
|
configPath: configService.getConfigPath(),
|
|
@@ -14408,9 +14462,9 @@ function maskKey3(key) {
|
|
|
14408
14462
|
return "****" + key.slice(-4);
|
|
14409
14463
|
}
|
|
14410
14464
|
async function getProjectStatus2() {
|
|
14411
|
-
const projectJai1 =
|
|
14465
|
+
const projectJai1 = join17(process.cwd(), ".jai1");
|
|
14412
14466
|
try {
|
|
14413
|
-
await
|
|
14467
|
+
await fs25.access(projectJai1);
|
|
14414
14468
|
return { exists: true, version: "Synced" };
|
|
14415
14469
|
} catch {
|
|
14416
14470
|
return { exists: false };
|
|
@@ -14600,7 +14654,7 @@ function createClearBackupsCommand() {
|
|
|
14600
14654
|
// src/commands/vscode/index.ts
|
|
14601
14655
|
import { Command as Command58 } from "commander";
|
|
14602
14656
|
import { checkbox as checkbox7, confirm as confirm15, select as select8 } from "@inquirer/prompts";
|
|
14603
|
-
import
|
|
14657
|
+
import fs26 from "fs/promises";
|
|
14604
14658
|
import path12 from "path";
|
|
14605
14659
|
import { existsSync as existsSync3 } from "fs";
|
|
14606
14660
|
var PERFORMANCE_GROUPS2 = {
|
|
@@ -14789,7 +14843,8 @@ async function interactiveMode2() {
|
|
|
14789
14843
|
{ name: "\u274C Disable c\xE1c nh\xF3m t\u1ED1i \u01B0u", value: "disable" },
|
|
14790
14844
|
{ name: "\u{1F680} Max Performance (enable t\u1EA5t c\u1EA3)", value: "max" },
|
|
14791
14845
|
{ name: "\u{1F504} Reset v\u1EC1 m\u1EB7c \u0111\u1ECBnh", value: "reset" }
|
|
14792
|
-
]
|
|
14846
|
+
],
|
|
14847
|
+
theme: selectTheme
|
|
14793
14848
|
});
|
|
14794
14849
|
if (action === "max") {
|
|
14795
14850
|
const allGroups = Object.keys(PERFORMANCE_GROUPS2);
|
|
@@ -14808,7 +14863,8 @@ async function selectGroupsToApply2(action) {
|
|
|
14808
14863
|
try {
|
|
14809
14864
|
const selectedGroups = await checkbox7({
|
|
14810
14865
|
message: `Ch\u1ECDn c\xE1c nh\xF3m \u0111\u1EC3 ${action === "enable" ? "enable" : "disable"} (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):`,
|
|
14811
|
-
choices
|
|
14866
|
+
choices,
|
|
14867
|
+
theme: checkboxTheme
|
|
14812
14868
|
});
|
|
14813
14869
|
if (selectedGroups.length === 0) {
|
|
14814
14870
|
console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn nh\xF3m n\xE0o!");
|
|
@@ -14831,13 +14887,13 @@ async function applyGroups2(groupKeys, action) {
|
|
|
14831
14887
|
return;
|
|
14832
14888
|
}
|
|
14833
14889
|
if (!existsSync3(vscodeDir)) {
|
|
14834
|
-
await
|
|
14890
|
+
await fs26.mkdir(vscodeDir, { recursive: true });
|
|
14835
14891
|
console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
|
|
14836
14892
|
}
|
|
14837
14893
|
let currentSettings = {};
|
|
14838
14894
|
if (existsSync3(settingsPath)) {
|
|
14839
14895
|
try {
|
|
14840
|
-
const content = await
|
|
14896
|
+
const content = await fs26.readFile(settingsPath, "utf-8");
|
|
14841
14897
|
currentSettings = JSON.parse(content);
|
|
14842
14898
|
console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
|
|
14843
14899
|
} catch {
|
|
@@ -14877,7 +14933,7 @@ async function applyGroups2(groupKeys, action) {
|
|
|
14877
14933
|
}
|
|
14878
14934
|
}
|
|
14879
14935
|
}
|
|
14880
|
-
await
|
|
14936
|
+
await fs26.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
14881
14937
|
console.log(`
|
|
14882
14938
|
\u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
|
|
14883
14939
|
console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
|
|
@@ -14898,7 +14954,7 @@ async function resetSettings2(groupKeys) {
|
|
|
14898
14954
|
return;
|
|
14899
14955
|
}
|
|
14900
14956
|
if (groupKeys.length === 0) {
|
|
14901
|
-
await
|
|
14957
|
+
await fs26.unlink(settingsPath);
|
|
14902
14958
|
console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
|
|
14903
14959
|
} else {
|
|
14904
14960
|
await applyGroups2(groupKeys, "disable");
|
|
@@ -15021,7 +15077,8 @@ async function runMigrateIde(options) {
|
|
|
15021
15077
|
});
|
|
15022
15078
|
selectedIdes = await checkbox8({
|
|
15023
15079
|
message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 migrate (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
|
|
15024
|
-
choices: ideChoices
|
|
15080
|
+
choices: ideChoices,
|
|
15081
|
+
theme: checkboxTheme
|
|
15025
15082
|
});
|
|
15026
15083
|
if (selectedIdes.length === 0) {
|
|
15027
15084
|
console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn IDE n\xE0o!");
|
|
@@ -15040,7 +15097,8 @@ async function runMigrateIde(options) {
|
|
|
15040
15097
|
];
|
|
15041
15098
|
selectedTypes = await checkbox8({
|
|
15042
15099
|
message: "Ch\u1ECDn content types \u0111\u1EC3 migrate:",
|
|
15043
|
-
choices: typeChoices
|
|
15100
|
+
choices: typeChoices,
|
|
15101
|
+
theme: checkboxTheme
|
|
15044
15102
|
});
|
|
15045
15103
|
if (selectedTypes.length === 0) {
|
|
15046
15104
|
console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn content type n\xE0o!");
|