@clubmatto/ai-kit 0.0.1 → 0.0.4
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/.nvmrc +1 -0
- package/CHANGELOG.md +10 -2
- package/LICENSE +17 -0
- package/README.md +7 -7
- package/dist/src/index.js +0 -0
- package/dist/src/template.js +1 -1
- package/package.json +5 -23
- package/src/agents/monorepo.md +2 -8
- package/src/agents/opencode.json +0 -9
- package/src/commands/commit.md +6 -5
- package/src/gradient-string.d.ts +1 -0
- package/src/template.ts +1 -1
- package/dist/scripts/fetch-playwright-skills.js +0 -63
- package/dist/src/commands/sync.js +0 -111
- package/dist/src/content.js +0 -99
- package/dist/tests/content.test.js +0 -141
- package/dist/tests/integration/cli.test.js +0 -43
- package/dist/tests/output.js +0 -36
- package/dist/tests/reader.test.js +0 -141
- package/dist/tests/sync.test.js +0 -90
- package/dist/tests/utils.js +0 -20
- package/dist/vitest.config.js +0 -9
- package/docs/roadmap.md +0 -16
- package/scripts/fetch-playwright-skills.ts +0 -79
- package/src/skills/image-gen/scripts/generate.js +0 -166
- package/tests/fixtures/agents/another.json +0 -4
- package/tests/fixtures/agents/monorepo.md +0 -5
- package/tests/fixtures/agents/opencode.json +0 -4
- package/tests/fixtures/commands/another.md +0 -5
- package/tests/fixtures/commands/commit.md +0 -7
- package/tests/fixtures/commands/test.md +0 -13
- package/tests/fixtures/rules/nested/nested-rule.md +0 -3
- package/tests/fixtures/rules/test-rule.md +0 -5
- package/tests/fixtures/rules/typescript.md +0 -5
- package/tests/fixtures/skills/test-skill/SKILL.md +0 -7
- package/tests/fixtures/skills/test-skill/nested-refs/doc.md +0 -3
- package/tests/fixtures/skills/test-skill/skill-details.md +0 -7
- package/tests/integration/cli.test.ts +0 -55
- package/tests/output.ts +0 -37
- package/tests/reader.test.ts +0 -193
- package/tests/sync.test.ts +0 -136
- package/tests/utils.ts +0 -17
- package/tsconfig.json +0 -23
- package/vitest.config.ts +0 -8
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22
|
package/CHANGELOG.md
CHANGED
|
@@ -4,12 +4,20 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.0.4] - 2026-04-16
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Upgrade npm and add provenance for trusted publishing
|
|
12
|
+
|
|
13
|
+
## [0.0.3] - 2026-04-16
|
|
14
|
+
|
|
7
15
|
### Added
|
|
8
16
|
|
|
9
|
-
-
|
|
17
|
+
- Added prompt to ask user if unrelated changes should be included before committing
|
|
10
18
|
|
|
11
19
|
## [0.0.1] - 2026-02-21
|
|
12
20
|
|
|
13
21
|
### Added
|
|
14
22
|
|
|
15
|
-
- Initial release
|
|
23
|
+
- Initial release
|
package/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
2
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
3
|
+
in the Software without restriction, including without limitation the rights
|
|
4
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
5
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
6
|
+
furnished to do so, subject to the following conditions:
|
|
7
|
+
|
|
8
|
+
The above copyright notice and this permission notice shall be included in all
|
|
9
|
+
copies or substantial portions of the Software.
|
|
10
|
+
|
|
11
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
12
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
13
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
14
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
15
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
16
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
17
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# ai-kit
|
|
2
2
|
|
|
3
|
+
[](https://circleci.com/gh/clubmatto/vetrina)
|
|
3
4
|
[](https://www.npmjs.com/package/@clubmatto/ai-kit)
|
|
4
5
|
[](/LICENSE)
|
|
5
6
|
|
|
@@ -33,13 +34,12 @@ ai-kit sync --skip-opencode
|
|
|
33
34
|
|
|
34
35
|
## What's Installed
|
|
35
36
|
|
|
36
|
-
| Location
|
|
37
|
-
|
|
|
38
|
-
| `.
|
|
39
|
-
| `.
|
|
40
|
-
|
|
|
41
|
-
| `
|
|
42
|
-
| `AGENTS.md` | Agent instructions |
|
|
37
|
+
| Location | Description |
|
|
38
|
+
| ----------------- | --------------------------------- |
|
|
39
|
+
| `.agents/rules/` | Language/framework rules |
|
|
40
|
+
| `.agents/skills/` | Reusable AI capabilities |
|
|
41
|
+
| `opencode.json` | Opencode configuration (optional) |
|
|
42
|
+
| `AGENTS.md` | Agent instructions |
|
|
43
43
|
|
|
44
44
|
## Commands
|
|
45
45
|
|
package/dist/src/index.js
CHANGED
|
File without changes
|
package/dist/src/template.js
CHANGED
|
@@ -6,5 +6,5 @@ function processTemplate(content) {
|
|
|
6
6
|
const isoDate = now.toISOString().split("T")[0];
|
|
7
7
|
return content
|
|
8
8
|
.replace(/\{\{FOOTER}}/g, `Last updated: ${isoDate}. This file extends the global rules in @AGENTS.md. Always check both files.`)
|
|
9
|
-
.replace(/\{\{AGENTS_FOOTER}}/g, `This file was last updated: ${isoDate}. Always check the \`.
|
|
9
|
+
.replace(/\{\{AGENTS_FOOTER}}/g, `This file was last updated: ${isoDate}. Always check the \`.agents/rules/\` directory for the most current language-specific guidelines.`);
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clubmatto/ai-kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "The AI configuration CLI from Club Matto",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"bugs": {
|
|
10
10
|
"url": "https://github.com/clubmatto/vetrina/issues"
|
|
11
11
|
},
|
|
12
|
-
"homepage": "https://github.com/clubmatto/vetrina
|
|
12
|
+
"homepage": "https://github.com/clubmatto/vetrina/tree/main/ai-kit",
|
|
13
13
|
"bin": {
|
|
14
14
|
"ai-kit": "./dist/src/index.js"
|
|
15
15
|
},
|
|
@@ -18,9 +18,8 @@
|
|
|
18
18
|
"dev": "tsx src/index.ts",
|
|
19
19
|
"dev:link": "npm run build && npm link",
|
|
20
20
|
"fetch-playwright-skills": "tsx scripts/fetch-playwright-skills.ts v0.1.1",
|
|
21
|
-
"
|
|
22
|
-
"test": "vitest run --
|
|
23
|
-
"test:integration": "npm run build && vitest run tests/integration",
|
|
21
|
+
"test": "vitest run --config vitest.config.mts --exclude 'tests/integration/**'",
|
|
22
|
+
"test:integration": "npm run build && vitest run --config vitest.config.mts tests/integration",
|
|
24
23
|
"test:watch": "vitest",
|
|
25
24
|
"typecheck": "tsc --noEmit",
|
|
26
25
|
"prettier:write": "prettier --write .",
|
|
@@ -31,16 +30,14 @@
|
|
|
31
30
|
},
|
|
32
31
|
"dependencies": {
|
|
33
32
|
"commander": "^14.0.3",
|
|
34
|
-
"gradient-string": "^
|
|
33
|
+
"gradient-string": "^2.0.0"
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
37
36
|
"@eslint/js": "^10.0.1",
|
|
38
|
-
"@release-it/keep-a-changelog": "^7.0.1",
|
|
39
37
|
"@types/node": "^25.3.0",
|
|
40
38
|
"eslint": "^10.0.1",
|
|
41
39
|
"knip": "^5.85.0",
|
|
42
40
|
"prettier": "^3.4.2",
|
|
43
|
-
"release-it": "^19.2.4",
|
|
44
41
|
"tsx": "latest",
|
|
45
42
|
"typescript": "^5.9.3",
|
|
46
43
|
"typescript-eslint": "^8.56.0",
|
|
@@ -55,21 +52,6 @@
|
|
|
55
52
|
"cli"
|
|
56
53
|
],
|
|
57
54
|
"license": "MIT",
|
|
58
|
-
"release-it": {
|
|
59
|
-
"npm": {
|
|
60
|
-
"publish": true
|
|
61
|
-
},
|
|
62
|
-
"git": {
|
|
63
|
-
"commitMessage": "chore: release v${version}",
|
|
64
|
-
"tagName": "v${version}"
|
|
65
|
-
},
|
|
66
|
-
"version": {
|
|
67
|
-
"bump": false
|
|
68
|
-
},
|
|
69
|
-
"plugins": {
|
|
70
|
-
"@release-it/keep-a-changelog": {}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
55
|
"knip": {
|
|
74
56
|
"ignore": [
|
|
75
57
|
"src/skills/**/scripts/*.js"
|
package/src/agents/monorepo.md
CHANGED
|
@@ -3,13 +3,7 @@
|
|
|
3
3
|
This is a monorepo containing apps in many languages.
|
|
4
4
|
|
|
5
5
|
You MUST follow the specific rules for each language. **ALWAYS** start from
|
|
6
|
-
checking out README.md.
|
|
7
|
-
|
|
8
|
-
## Build Systems
|
|
9
|
-
|
|
10
|
-
- **Kotlin/Java**: Gradle (Kotlin DSL). Use `./gradlew` commands.
|
|
11
|
-
- **Go**: Go modules. Use `go` commands.
|
|
12
|
-
- **TypeScript**: Use `npm`.
|
|
6
|
+
checking out the closest README.md.
|
|
13
7
|
|
|
14
8
|
## How to Find the Right Rules
|
|
15
9
|
|
|
@@ -20,7 +14,7 @@ checking out README.md.
|
|
|
20
14
|
- Kotlin → `kotlin.md`
|
|
21
15
|
- etc.
|
|
22
16
|
|
|
23
|
-
CRITICAL: When you encounter a file reference (e.g., `@.
|
|
17
|
+
CRITICAL: When you encounter a file reference (e.g., `@.agents/rules/go.md`), use the Read tool to load it on a need-to-know basis.
|
|
24
18
|
|
|
25
19
|
## Additional Guidelines
|
|
26
20
|
|
package/src/agents/opencode.json
CHANGED
|
@@ -9,15 +9,6 @@
|
|
|
9
9
|
},
|
|
10
10
|
"enabled": false
|
|
11
11
|
},
|
|
12
|
-
"circleci": {
|
|
13
|
-
"type": "local",
|
|
14
|
-
"command": ["npx", "-y", "@circleci/mcp-server-circleci@latest"],
|
|
15
|
-
"environment": {
|
|
16
|
-
"CIRCLECI_TOKEN": "{env:CIRCLECI_TOKEN}",
|
|
17
|
-
"CIRCLECI_BASE_URL": "https://circleci.com"
|
|
18
|
-
},
|
|
19
|
-
"enabled": false
|
|
20
|
-
},
|
|
21
12
|
"MiniMax": {
|
|
22
13
|
"type": "local",
|
|
23
14
|
"command": ["uvx", "minimax-coding-plan-mcp", "-y"],
|
package/src/commands/commit.md
CHANGED
|
@@ -27,7 +27,7 @@ Create a commit with the following format:
|
|
|
27
27
|
feat: add init and update commands
|
|
28
28
|
|
|
29
29
|
- Created init command for first-time setup
|
|
30
|
-
- Added manifest tracking in .
|
|
30
|
+
- Added manifest tracking in .agents/.ai-kit
|
|
31
31
|
- Implemented update command for version sync
|
|
32
32
|
- Added --skip-opencode option
|
|
33
33
|
|
|
@@ -37,7 +37,8 @@ created with the help of MiniMax
|
|
|
37
37
|
## Process
|
|
38
38
|
|
|
39
39
|
1. First, review all changes with `git status` and `git diff`
|
|
40
|
-
2.
|
|
41
|
-
3.
|
|
42
|
-
4.
|
|
43
|
-
5.
|
|
40
|
+
2. If there changes you did not make, ask if you should include them
|
|
41
|
+
3. Write a concise one-liner following conventional commits
|
|
42
|
+
4. List the key changes as bullet points
|
|
43
|
+
5. Add the sign-off line with the current model
|
|
44
|
+
6. Commit with `git commit -m "your message"`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module 'gradient-string';
|
package/src/template.ts
CHANGED
|
@@ -9,6 +9,6 @@ export function processTemplate(content: string): string {
|
|
|
9
9
|
)
|
|
10
10
|
.replace(
|
|
11
11
|
/\{\{AGENTS_FOOTER}}/g,
|
|
12
|
-
`This file was last updated: ${isoDate}. Always check the \`.
|
|
12
|
+
`This file was last updated: ${isoDate}. Always check the \`.agents/rules/\` directory for the most current language-specific guidelines.`,
|
|
13
13
|
);
|
|
14
14
|
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const fs_1 = require("fs");
|
|
4
|
-
const path_1 = require("path");
|
|
5
|
-
const rootDir = (0, path_1.join)(__dirname, "..");
|
|
6
|
-
const skillsDir = (0, path_1.join)(rootDir, "src", "skills", "playwright-cli");
|
|
7
|
-
const GITHUB_API = "https://api.github.com";
|
|
8
|
-
const REPO = "microsoft/playwright-cli";
|
|
9
|
-
const args = process.argv.slice(2);
|
|
10
|
-
const VERSION = args[0] || "v0.1.1";
|
|
11
|
-
async function fetchJson(url) {
|
|
12
|
-
const res = await fetch(url, {
|
|
13
|
-
headers: {
|
|
14
|
-
Accept: "application/vnd.github.v3+json",
|
|
15
|
-
"User-Agent": "ai-kit",
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
if (!res.ok) {
|
|
19
|
-
throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
|
|
20
|
-
}
|
|
21
|
-
return (await res.json());
|
|
22
|
-
}
|
|
23
|
-
async function fetchFile(url) {
|
|
24
|
-
const res = await fetch(url, {
|
|
25
|
-
headers: {
|
|
26
|
-
Accept: "application/vnd.github.v3.raw",
|
|
27
|
-
"User-Agent": "ai-kit",
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
if (!res.ok) {
|
|
31
|
-
throw new Error(`Failed to fetch ${url}: ${res.status} ${res.statusText}`);
|
|
32
|
-
}
|
|
33
|
-
return res.text();
|
|
34
|
-
}
|
|
35
|
-
async function main() {
|
|
36
|
-
console.log(`Fetching playwright-cli skills from ${REPO}@${VERSION}...`);
|
|
37
|
-
const baseUrl = `${GITHUB_API}/repos/${REPO}/contents/skills/playwright-cli?ref=${VERSION}`;
|
|
38
|
-
const files = await fetchJson(baseUrl);
|
|
39
|
-
(0, fs_1.mkdirSync)(skillsDir, { recursive: true });
|
|
40
|
-
(0, fs_1.mkdirSync)((0, path_1.join)(skillsDir, "references"), { recursive: true });
|
|
41
|
-
for (const file of files) {
|
|
42
|
-
if (file.type === "dir" && file.name === "references") {
|
|
43
|
-
const refs = await fetchJson(file.url);
|
|
44
|
-
for (const ref of refs) {
|
|
45
|
-
const content = await fetchFile(ref.download_url);
|
|
46
|
-
const targetPath = (0, path_1.join)(skillsDir, "references", ref.name);
|
|
47
|
-
(0, fs_1.writeFileSync)(targetPath, content);
|
|
48
|
-
console.log(` - references/${ref.name}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
else if (file.type === "file") {
|
|
52
|
-
const content = await fetchFile(file.download_url);
|
|
53
|
-
const targetPath = (0, path_1.join)(skillsDir, file.name);
|
|
54
|
-
(0, fs_1.writeFileSync)(targetPath, content);
|
|
55
|
-
console.log(` - ${file.name}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
console.log(`\nDone! Skills written to src/skills/playwright-cli/`);
|
|
59
|
-
}
|
|
60
|
-
main().catch((err) => {
|
|
61
|
-
console.error(err);
|
|
62
|
-
process.exit(1);
|
|
63
|
-
});
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, existsSync, writeFileSync } from "fs";
|
|
2
|
-
import { join, dirname } from "path";
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
4
|
-
import { getContentFiles, getRootFiles, getAgentsFile, getCommandConfig, } from "../content.js";
|
|
5
|
-
import { readManifest, writeManifest } from "../manifest.js";
|
|
6
|
-
import { processTemplate } from "../template.js";
|
|
7
|
-
import { log } from "../output.js";
|
|
8
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
const rootDir = join(__dirname, "..", "..", "..");
|
|
10
|
-
const defaultSourceDirs = {
|
|
11
|
-
rules: join(rootDir, "src", "rules"),
|
|
12
|
-
skills: join(rootDir, "src", "skills"),
|
|
13
|
-
agents: join(rootDir, "src", "agents"),
|
|
14
|
-
commands: join(rootDir, "src", "commands"),
|
|
15
|
-
};
|
|
16
|
-
export async function sync(cwd, version, options, logger = log, sourceDirs = defaultSourceDirs) {
|
|
17
|
-
const manifest = readManifest(cwd);
|
|
18
|
-
logger.logo(version);
|
|
19
|
-
if (!manifest) {
|
|
20
|
-
logger.action("Initializing ai-kit...");
|
|
21
|
-
const count = await doSync(cwd, version, options, logger, sourceDirs);
|
|
22
|
-
logger.final(`ai-kit initialized (${count} files)`);
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (manifest.version === version) {
|
|
26
|
-
logger.success(`Already at latest version (${version})`);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
logger.action(`Updating from ${manifest.version} to ${version}...`);
|
|
30
|
-
const count = await doSync(cwd, version, options, logger, sourceDirs);
|
|
31
|
-
logger.final(`Updated to ${version} (${count} files)`);
|
|
32
|
-
}
|
|
33
|
-
async function doSync(cwd, version, options, logger, sourceDirs) {
|
|
34
|
-
const aiDir = join(cwd, ".agents");
|
|
35
|
-
if (!existsSync(aiDir)) {
|
|
36
|
-
mkdirSync(aiDir, { recursive: true });
|
|
37
|
-
}
|
|
38
|
-
const contentFiles = getContentFiles(sourceDirs.rules, sourceDirs.skills);
|
|
39
|
-
const rootFiles = getRootFiles(sourceDirs.agents);
|
|
40
|
-
const agentsFile = getAgentsFile(sourceDirs.agents);
|
|
41
|
-
const rules = contentFiles.filter((f) => f.type === "rules");
|
|
42
|
-
const skills = contentFiles.filter((f) => f.type === "skills");
|
|
43
|
-
let count = 0;
|
|
44
|
-
if (rules.length > 0) {
|
|
45
|
-
logger.section("rules");
|
|
46
|
-
for (const file of rules) {
|
|
47
|
-
const targetDir = join(aiDir, file.type);
|
|
48
|
-
if (!existsSync(targetDir)) {
|
|
49
|
-
mkdirSync(targetDir, { recursive: true });
|
|
50
|
-
}
|
|
51
|
-
const targetPath = join(targetDir, file.name);
|
|
52
|
-
const parentDir = dirname(targetPath);
|
|
53
|
-
if (!existsSync(parentDir)) {
|
|
54
|
-
mkdirSync(parentDir, { recursive: true });
|
|
55
|
-
}
|
|
56
|
-
writeFileSync(targetPath, processTemplate(file.content));
|
|
57
|
-
logger.success(`${file.type}/${file.name}`);
|
|
58
|
-
count++;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (skills.length > 0) {
|
|
62
|
-
logger.section("skills");
|
|
63
|
-
for (const file of skills) {
|
|
64
|
-
const targetDir = join(aiDir, file.type);
|
|
65
|
-
if (!existsSync(targetDir)) {
|
|
66
|
-
mkdirSync(targetDir, { recursive: true });
|
|
67
|
-
}
|
|
68
|
-
const targetPath = join(targetDir, file.name);
|
|
69
|
-
const parentDir = dirname(targetPath);
|
|
70
|
-
if (!existsSync(parentDir)) {
|
|
71
|
-
mkdirSync(parentDir, { recursive: true });
|
|
72
|
-
}
|
|
73
|
-
writeFileSync(targetPath, processTemplate(file.content));
|
|
74
|
-
logger.success(`${file.type}/${file.name}`);
|
|
75
|
-
count++;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const installedRootFiles = [];
|
|
79
|
-
if (!options.skipOpencode) {
|
|
80
|
-
const commandConfig = getCommandConfig(sourceDirs.commands);
|
|
81
|
-
if (rootFiles.length > 0) {
|
|
82
|
-
logger.section("root files");
|
|
83
|
-
for (const file of rootFiles) {
|
|
84
|
-
let content = file.content;
|
|
85
|
-
if (file.name === "opencode.json" &&
|
|
86
|
-
Object.keys(commandConfig).length > 0) {
|
|
87
|
-
const config = JSON.parse(content);
|
|
88
|
-
config.command = commandConfig;
|
|
89
|
-
content = JSON.stringify(config, null, 2) + "\n";
|
|
90
|
-
}
|
|
91
|
-
const targetPath = join(cwd, file.name);
|
|
92
|
-
writeFileSync(targetPath, content);
|
|
93
|
-
logger.success(`${file.name}`);
|
|
94
|
-
installedRootFiles.push(file.name);
|
|
95
|
-
count++;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (agentsFile) {
|
|
100
|
-
const targetPath = join(cwd, agentsFile.name);
|
|
101
|
-
writeFileSync(targetPath, processTemplate(agentsFile.content));
|
|
102
|
-
logger.success(`${agentsFile.name}`);
|
|
103
|
-
count++;
|
|
104
|
-
}
|
|
105
|
-
writeManifest(cwd, {
|
|
106
|
-
version,
|
|
107
|
-
installedAt: new Date().toISOString(),
|
|
108
|
-
rootFiles: installedRootFiles,
|
|
109
|
-
});
|
|
110
|
-
return count;
|
|
111
|
-
}
|
package/dist/src/content.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCommandConfig = getCommandConfig;
|
|
4
|
-
exports.getContentFiles = getContentFiles;
|
|
5
|
-
exports.getRootFiles = getRootFiles;
|
|
6
|
-
exports.getAgentsFile = getAgentsFile;
|
|
7
|
-
const fs_1 = require("fs");
|
|
8
|
-
const path_1 = require("path");
|
|
9
|
-
function parseFrontmatter(content) {
|
|
10
|
-
const result = {};
|
|
11
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
12
|
-
if (!match)
|
|
13
|
-
return result;
|
|
14
|
-
const frontmatter = match[1];
|
|
15
|
-
for (const line of frontmatter.split("\n")) {
|
|
16
|
-
const colonIndex = line.indexOf(":");
|
|
17
|
-
if (colonIndex === -1)
|
|
18
|
-
continue;
|
|
19
|
-
const key = line.slice(0, colonIndex).trim();
|
|
20
|
-
result[key] = line.slice(colonIndex + 1).trim();
|
|
21
|
-
}
|
|
22
|
-
return result;
|
|
23
|
-
}
|
|
24
|
-
function getCommandConfig(commandsDir) {
|
|
25
|
-
const config = {};
|
|
26
|
-
try {
|
|
27
|
-
const files = (0, fs_1.readdirSync)(commandsDir);
|
|
28
|
-
for (const file of files) {
|
|
29
|
-
if (!file.endsWith(".md"))
|
|
30
|
-
continue;
|
|
31
|
-
const filePath = (0, path_1.join)(commandsDir, file);
|
|
32
|
-
const content = (0, fs_1.readFileSync)(filePath, "utf-8");
|
|
33
|
-
const frontmatter = parseFrontmatter(content);
|
|
34
|
-
const name = file.replace(/\.md$/, "");
|
|
35
|
-
const body = content.replace(/^---[\s\S]*?---\n/, "").trim();
|
|
36
|
-
config[name] = {
|
|
37
|
-
description: frontmatter.description || "",
|
|
38
|
-
template: body,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return config;
|
|
44
|
-
}
|
|
45
|
-
return config;
|
|
46
|
-
}
|
|
47
|
-
function getFiles(dir, type, baseDir) {
|
|
48
|
-
if (!(0, fs_1.readdirSync)(dir, { withFileTypes: true }).length) {
|
|
49
|
-
return [];
|
|
50
|
-
}
|
|
51
|
-
const base = baseDir || dir;
|
|
52
|
-
return (0, fs_1.readdirSync)(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
53
|
-
const path = (0, path_1.join)(dir, entry.name);
|
|
54
|
-
const relativePath = path.slice(base.length + 1);
|
|
55
|
-
if (entry.isDirectory()) {
|
|
56
|
-
return getFiles(path, type, base);
|
|
57
|
-
}
|
|
58
|
-
if (entry.name.endsWith(".md")) {
|
|
59
|
-
return [
|
|
60
|
-
{
|
|
61
|
-
type,
|
|
62
|
-
name: relativePath,
|
|
63
|
-
content: (0, fs_1.readFileSync)(path, "utf-8"),
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
}
|
|
67
|
-
return [];
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
function getContentFiles(rulesDir, skillsDir) {
|
|
71
|
-
return [...getFiles(rulesDir, "rules"), ...getFiles(skillsDir, "skills")];
|
|
72
|
-
}
|
|
73
|
-
function getRootFiles(agentsDir) {
|
|
74
|
-
try {
|
|
75
|
-
return (0, fs_1.readdirSync)(agentsDir, { withFileTypes: true })
|
|
76
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
77
|
-
.map((entry) => ({
|
|
78
|
-
type: "config",
|
|
79
|
-
name: entry.name,
|
|
80
|
-
content: (0, fs_1.readFileSync)((0, path_1.join)(agentsDir, entry.name), "utf-8"),
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
return [];
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
function getAgentsFile(agentsDir) {
|
|
88
|
-
const sourcePath = (0, path_1.join)(agentsDir, "monorepo.md");
|
|
89
|
-
try {
|
|
90
|
-
return {
|
|
91
|
-
type: "config",
|
|
92
|
-
name: "AGENTS.md",
|
|
93
|
-
content: (0, fs_1.readFileSync)(sourcePath, "utf-8"),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const path_1 = require("path");
|
|
5
|
-
const content_1 = require("../src/content");
|
|
6
|
-
const template_1 = require("../src/template");
|
|
7
|
-
const fixturesDir = (0, path_1.join)(__dirname, "fixtures");
|
|
8
|
-
const commandsDir = (0, path_1.join)(fixturesDir, "commands");
|
|
9
|
-
const rulesDir = (0, path_1.join)(fixturesDir, "rules");
|
|
10
|
-
const skillsDir = (0, path_1.join)(fixturesDir, "skills");
|
|
11
|
-
const agentsDir = (0, path_1.join)(fixturesDir, "agents");
|
|
12
|
-
(0, vitest_1.describe)("getCommandConfig", () => {
|
|
13
|
-
(0, vitest_1.it)("parses command files and returns config object", () => {
|
|
14
|
-
const config = (0, content_1.getCommandConfig)(commandsDir);
|
|
15
|
-
(0, vitest_1.expect)(config).toHaveProperty("test");
|
|
16
|
-
(0, vitest_1.expect)(config.test.description).toBe("A test command for testing purposes.");
|
|
17
|
-
(0, vitest_1.expect)(config.test.template).toContain("This is a test command template.");
|
|
18
|
-
});
|
|
19
|
-
(0, vitest_1.it)("includes all command files", () => {
|
|
20
|
-
const config = (0, content_1.getCommandConfig)(commandsDir);
|
|
21
|
-
(0, vitest_1.expect)(config).toHaveProperty("test");
|
|
22
|
-
(0, vitest_1.expect)(config).toHaveProperty("another");
|
|
23
|
-
});
|
|
24
|
-
(0, vitest_1.it)("has correct structure for opencode.json command format", () => {
|
|
25
|
-
const config = (0, content_1.getCommandConfig)(commandsDir);
|
|
26
|
-
for (const [_, cmd] of Object.entries(config)) {
|
|
27
|
-
(0, vitest_1.expect)(cmd).toHaveProperty("description");
|
|
28
|
-
(0, vitest_1.expect)(cmd).toHaveProperty("template");
|
|
29
|
-
(0, vitest_1.expect)(typeof cmd.description).toBe("string");
|
|
30
|
-
(0, vitest_1.expect)(typeof cmd.template).toBe("string");
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
(0, vitest_1.it)("returns empty object for non-existent directory", () => {
|
|
34
|
-
const config = (0, content_1.getCommandConfig)("/non/existent/dir");
|
|
35
|
-
(0, vitest_1.expect)(config).toEqual({});
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
(0, vitest_1.describe)("getContentFiles", () => {
|
|
39
|
-
(0, vitest_1.it)("returns files from rules and skills directories", () => {
|
|
40
|
-
const files = (0, content_1.getContentFiles)(rulesDir, skillsDir);
|
|
41
|
-
const ruleFiles = files.filter((f) => f.type === "rules");
|
|
42
|
-
const skillFiles = files.filter((f) => f.type === "skills");
|
|
43
|
-
(0, vitest_1.expect)(ruleFiles.length).toBeGreaterThan(0);
|
|
44
|
-
(0, vitest_1.expect)(skillFiles.length).toBeGreaterThan(0);
|
|
45
|
-
});
|
|
46
|
-
(0, vitest_1.it)("includes nested files recursively", () => {
|
|
47
|
-
const files = (0, content_1.getContentFiles)(rulesDir, skillsDir);
|
|
48
|
-
const nestedRule = files.find((f) => f.name === "nested/nested-rule.md" && f.type === "rules");
|
|
49
|
-
(0, vitest_1.expect)(nestedRule).toBeDefined();
|
|
50
|
-
(0, vitest_1.expect)(nestedRule?.content).toContain("Nested Rule");
|
|
51
|
-
});
|
|
52
|
-
(0, vitest_1.it)("returns correct file structure", () => {
|
|
53
|
-
const files = (0, content_1.getContentFiles)(rulesDir, skillsDir);
|
|
54
|
-
for (const file of files) {
|
|
55
|
-
(0, vitest_1.expect)(file).toHaveProperty("type");
|
|
56
|
-
(0, vitest_1.expect)(file).toHaveProperty("name");
|
|
57
|
-
(0, vitest_1.expect)(file).toHaveProperty("content");
|
|
58
|
-
(0, vitest_1.expect)(["rules", "skills"]).toContain(file.type);
|
|
59
|
-
(0, vitest_1.expect)(file.name.endsWith(".md")).toBe(true);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
(0, vitest_1.it)("includes skill directory name in file path", () => {
|
|
63
|
-
const files = (0, content_1.getContentFiles)(rulesDir, skillsDir);
|
|
64
|
-
const skillFile = files.find((f) => f.type === "skills" && f.name === "test-skill/SKILL.md");
|
|
65
|
-
(0, vitest_1.expect)(skillFile).toBeDefined();
|
|
66
|
-
(0, vitest_1.expect)(skillFile?.content).toContain("Test Skill");
|
|
67
|
-
});
|
|
68
|
-
(0, vitest_1.it)("includes additional files in skill directories", () => {
|
|
69
|
-
const files = (0, content_1.getContentFiles)(rulesDir, skillsDir);
|
|
70
|
-
const detailsFile = files.find((f) => f.type === "skills" && f.name === "test-skill/skill-details.md");
|
|
71
|
-
(0, vitest_1.expect)(detailsFile).toBeDefined();
|
|
72
|
-
(0, vitest_1.expect)(detailsFile?.content).toContain("Skill Details");
|
|
73
|
-
});
|
|
74
|
-
(0, vitest_1.it)("includes nested references in skill directories", () => {
|
|
75
|
-
const files = (0, content_1.getContentFiles)(rulesDir, skillsDir);
|
|
76
|
-
const refFile = files.find((f) => f.type === "skills" && f.name === "test-skill/nested-refs/doc.md");
|
|
77
|
-
(0, vitest_1.expect)(refFile).toBeDefined();
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
(0, vitest_1.describe)("getRootFiles", () => {
|
|
81
|
-
(0, vitest_1.it)("returns JSON files from agents directory", () => {
|
|
82
|
-
const files = (0, content_1.getRootFiles)(agentsDir);
|
|
83
|
-
(0, vitest_1.expect)(files.length).toBe(2);
|
|
84
|
-
(0, vitest_1.expect)(files.map((f) => f.name).sort()).toEqual([
|
|
85
|
-
"another.json",
|
|
86
|
-
"opencode.json",
|
|
87
|
-
]);
|
|
88
|
-
});
|
|
89
|
-
(0, vitest_1.it)("returns file content as string", () => {
|
|
90
|
-
const files = (0, content_1.getRootFiles)(agentsDir);
|
|
91
|
-
const opencodeFile = files.find((f) => f.name === "opencode.json");
|
|
92
|
-
(0, vitest_1.expect)(opencodeFile?.content).toContain("test-agent");
|
|
93
|
-
});
|
|
94
|
-
(0, vitest_1.it)("returns empty array for non-existent directory", () => {
|
|
95
|
-
const files = (0, content_1.getRootFiles)("/non/existent/dir");
|
|
96
|
-
(0, vitest_1.expect)(files).toEqual([]);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
(0, vitest_1.describe)("getAgentsFile", () => {
|
|
100
|
-
(0, vitest_1.it)("returns AGENTS.md file content", () => {
|
|
101
|
-
const file = (0, content_1.getAgentsFile)(agentsDir);
|
|
102
|
-
(0, vitest_1.expect)(file).not.toBeNull();
|
|
103
|
-
(0, vitest_1.expect)(file?.name).toBe("AGENTS.md");
|
|
104
|
-
(0, vitest_1.expect)(file?.content).toContain("AGENTS.md");
|
|
105
|
-
});
|
|
106
|
-
(0, vitest_1.it)("returns null when monorepo.md does not exist", () => {
|
|
107
|
-
const file = (0, content_1.getAgentsFile)(rulesDir);
|
|
108
|
-
(0, vitest_1.expect)(file).toBeNull();
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
(0, vitest_1.describe)("processTemplate", () => {
|
|
112
|
-
(0, vitest_1.it)("replaces {{FOOTER}} with full footer text and ISO date", () => {
|
|
113
|
-
const result = (0, template_1.processTemplate)("{{FOOTER}}");
|
|
114
|
-
const today = new Date().toISOString().split("T")[0];
|
|
115
|
-
(0, vitest_1.expect)(result).toBe(`Last updated: ${today}. This file extends the global rules in @AGENTS.md. Always check both files.`);
|
|
116
|
-
});
|
|
117
|
-
(0, vitest_1.it)("replaces {{AGENTS_FOOTER}} with agents footer text and ISO date", () => {
|
|
118
|
-
const result = (0, template_1.processTemplate)("{{AGENTS_FOOTER}}");
|
|
119
|
-
const today = new Date().toISOString().split("T")[0];
|
|
120
|
-
(0, vitest_1.expect)(result).toBe(`This file was last updated: ${today}. Always check the \`.ai/rules/\` directory for the most current language-specific guidelines.`);
|
|
121
|
-
});
|
|
122
|
-
(0, vitest_1.it)("replaces multiple placeholders in same content", () => {
|
|
123
|
-
const result = (0, template_1.processTemplate)("{{FOOTER}} and {{AGENTS_FOOTER}}");
|
|
124
|
-
const today = new Date().toISOString().split("T")[0];
|
|
125
|
-
(0, vitest_1.expect)(result).toContain(`Last updated: ${today}`);
|
|
126
|
-
(0, vitest_1.expect)(result).toContain(`This file was last updated: ${today}`);
|
|
127
|
-
});
|
|
128
|
-
(0, vitest_1.it)("leaves non-matching content unchanged", () => {
|
|
129
|
-
const input = "Some {{OTHER}} content {{NOTREAL}}";
|
|
130
|
-
const result = (0, template_1.processTemplate)(input);
|
|
131
|
-
(0, vitest_1.expect)(result).toBe(input);
|
|
132
|
-
});
|
|
133
|
-
(0, vitest_1.it)("handles empty string", () => {
|
|
134
|
-
const result = (0, template_1.processTemplate)("");
|
|
135
|
-
(0, vitest_1.expect)(result).toBe("");
|
|
136
|
-
});
|
|
137
|
-
(0, vitest_1.it)("handles content without placeholders", () => {
|
|
138
|
-
const result = (0, template_1.processTemplate)("No placeholders here");
|
|
139
|
-
(0, vitest_1.expect)(result).toBe("No placeholders here");
|
|
140
|
-
});
|
|
141
|
-
});
|