@torus-engineering/tas-kit 1.13.0 → 2.1.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/.tas/_platform/claude-code/settings.json +58 -46
- package/.tas/_platform/hooks/code-quality.js +127 -127
- package/.tas/_platform/hooks/session-end.js +111 -111
- package/.tas/agents/architect.md +53 -53
- package/.tas/agents/aws-reviewer.md +71 -71
- package/.tas/agents/build-resolver.md +89 -59
- package/.tas/agents/code-explorer.md +63 -63
- package/.tas/agents/csharp-reviewer.md +62 -62
- package/.tas/agents/database-reviewer.md +73 -73
- package/.tas/agents/doc-updater.md +68 -66
- package/.tas/agents/python-reviewer.md +67 -67
- package/.tas/agents/security-reviewer.md +79 -79
- package/.tas/agents/software-engineer.md +53 -0
- package/.tas/agents/typescript-reviewer.md +65 -65
- package/.tas/commands/ado-create.md +33 -28
- package/.tas/commands/ado-delete.md +26 -22
- package/.tas/commands/ado-get.md +24 -20
- package/.tas/commands/ado-status.md +22 -18
- package/.tas/commands/ado-update.md +31 -27
- package/.tas/commands/tas-adr.md +37 -33
- package/.tas/commands/tas-apitest-plan.md +177 -173
- package/.tas/commands/tas-apitest.md +147 -143
- package/.tas/commands/tas-brainstorm.md +23 -19
- package/.tas/commands/tas-brd.md +50 -0
- package/.tas/commands/tas-bug.md +127 -113
- package/.tas/commands/tas-checklist.md +180 -0
- package/.tas/commands/tas-debug.md +103 -0
- package/.tas/commands/tas-design.md +41 -37
- package/.tas/commands/tas-dev.md +225 -125
- package/.tas/commands/tas-e2e-mobile.md +146 -155
- package/.tas/commands/tas-e2e-web.md +150 -163
- package/.tas/commands/tas-e2e.md +289 -102
- package/.tas/commands/tas-feature.md +181 -47
- package/.tas/commands/tas-fix.md +72 -51
- package/.tas/commands/tas-functest-mobile.md +138 -144
- package/.tas/commands/tas-functest-web.md +176 -192
- package/.tas/commands/tas-functest.md +225 -76
- package/.tas/commands/tas-init.md +22 -17
- package/.tas/commands/tas-master-plan.md +300 -0
- package/.tas/commands/tas-orchestrate.md +159 -0
- package/.tas/commands/tas-plan.md +152 -117
- package/.tas/commands/tas-prd.md +57 -37
- package/.tas/commands/tas-review-pr.md +174 -0
- package/.tas/commands/tas-review.md +115 -113
- package/.tas/commands/tas-sad.md +47 -43
- package/.tas/commands/tas-security.md +91 -87
- package/.tas/commands/tas-spec.md +54 -50
- package/.tas/commands/tas-status.md +25 -16
- package/.tas/project-status-example.yaml +3 -1
- package/.tas/rules/ado-integration.md +67 -65
- package/.tas/rules/common/api-design.md +517 -517
- package/.tas/rules/common/build-debug-loop.md +233 -0
- package/.tas/rules/common/code-review.md +4 -0
- package/.tas/rules/common/feature-done.md +42 -0
- package/.tas/rules/common/post-implementation-review.md +4 -0
- package/.tas/rules/common/project-status.md +33 -16
- package/.tas/rules/common/sad-impact.md +81 -0
- package/.tas/rules/common/tdd.md +104 -89
- package/.tas/rules/csharp/api-testing.md +2 -2
- package/.tas/rules/csharp/torus-core-framework.md +128 -0
- package/.tas/tas-example.yaml +9 -32
- package/.tas/templates/AGENTS.md +13 -0
- package/.tas/templates/API-Test-Spec.md +5 -4
- package/.tas/templates/BRD.md +133 -0
- package/.tas/templates/Bug.md +15 -0
- package/.tas/templates/E2E-Execution-Report.md +8 -8
- package/.tas/templates/E2E-Mobile-Spec.md +6 -8
- package/.tas/templates/E2E-Report.md +2 -2
- package/.tas/templates/E2E-Scenario.md +22 -22
- package/.tas/templates/E2E-Test-Spec.md +274 -0
- package/.tas/templates/E2E-Web-Spec.md +4 -4
- package/.tas/templates/Feature-Technical-Part.md +69 -0
- package/.tas/templates/Feature-Technical-Stack.md +74 -0
- package/.tas/templates/Feature-Technical.md +329 -0
- package/.tas/templates/Feature.md +50 -26
- package/.tas/templates/Func-Test-Script.md +29 -56
- package/.tas/templates/Func-Test-Spec.md +144 -142
- package/.tas/templates/PRD.md +173 -142
- package/.tas/templates/TestChecklist.md +96 -0
- package/.tas/templates/torus-dotnet-bootstrap.md +223 -0
- package/.tas/tools/tas-ado-readme.md +24 -27
- package/.tas/tools/tas-ado.py +328 -25
- package/.tas/tools/tas-github.py +339 -0
- package/README.md +142 -57
- package/bin/cli.js +90 -90
- package/lib/adapters/antigravity.js +131 -131
- package/lib/adapters/claude-code.js +71 -35
- package/lib/adapters/codex.js +157 -157
- package/lib/adapters/cursor.js +80 -80
- package/lib/adapters/index.js +20 -20
- package/lib/adapters/utils.js +81 -81
- package/lib/deleted-files.json +7 -0
- package/lib/install.js +546 -543
- package/package.json +2 -2
- package/.tas/README.md +0 -334
- package/.tas/commands/tas-epic.md +0 -35
- package/.tas/commands/tas-story.md +0 -91
- package/.tas/rules/common/story-done.md +0 -30
- package/.tas/templates/Epic.md +0 -46
- package/.tas/templates/Story.md +0 -90
package/lib/adapters/codex.js
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
exists, listFilesRecursive,
|
|
5
|
-
parseFrontmatter, extractDescription, extractCommandName, extractH1, yamlValue,
|
|
6
|
-
} from './utils.js';
|
|
7
|
-
|
|
8
|
-
export const PLATFORM_ID = 'codex';
|
|
9
|
-
export const PLATFORM_LABEL = 'Codex';
|
|
10
|
-
|
|
11
|
-
export async function install({ tasDir, target }) {
|
|
12
|
-
const codexDir = path.join(target, '.codex');
|
|
13
|
-
const skillsOut = path.join(codexDir, 'skills');
|
|
14
|
-
await fs.mkdir(skillsOut, { recursive: true });
|
|
15
|
-
|
|
16
|
-
// commands → skills
|
|
17
|
-
const commandsDir = path.join(tasDir, 'commands');
|
|
18
|
-
if (await exists(commandsDir)) {
|
|
19
|
-
const files = await listFilesRecursive(commandsDir, '.md');
|
|
20
|
-
for (const file of files) {
|
|
21
|
-
await commandToSkill(file, skillsOut);
|
|
22
|
-
}
|
|
23
|
-
console.log(' [ok] .codex/skills/ (commands)');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// agents → skills
|
|
27
|
-
const agentsDir = path.join(tasDir, 'agents');
|
|
28
|
-
if (await exists(agentsDir)) {
|
|
29
|
-
const files = await listFilesRecursive(agentsDir, '.md');
|
|
30
|
-
for (const file of files) {
|
|
31
|
-
await agentToSkill(file, skillsOut);
|
|
32
|
-
}
|
|
33
|
-
console.log(' [ok] .codex/skills/ (agents)');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// skills → skills (copy as-is, same format)
|
|
37
|
-
const skillsSrc = path.join(tasDir, 'skills');
|
|
38
|
-
if (await exists(skillsSrc)) {
|
|
39
|
-
const skillDirs = await fs.readdir(skillsSrc, { withFileTypes: true });
|
|
40
|
-
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
41
|
-
const src = path.join(skillsSrc, d.name);
|
|
42
|
-
const dest = path.join(skillsOut, d.name);
|
|
43
|
-
await fs.cp(src, dest, { recursive: true });
|
|
44
|
-
}
|
|
45
|
-
console.log(' [ok] .codex/skills/ (skills)');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// rules → skills with references/
|
|
49
|
-
const rulesDir = path.join(tasDir, 'rules');
|
|
50
|
-
if (await exists(rulesDir)) {
|
|
51
|
-
await rulesToSkills(rulesDir, skillsOut);
|
|
52
|
-
console.log(' [ok] .codex/skills/ (rules)');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// platform index (not root AGENTS.md — that's the template/system prompt)
|
|
56
|
-
await generateReadme(skillsOut, codexDir);
|
|
57
|
-
console.log(' [ok] .codex/README.md');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function commandToSkill(file, skillsOut) {
|
|
61
|
-
const content = await fs.readFile(file, 'utf8');
|
|
62
|
-
const name = extractCommandName(content) || path.basename(file, '.md');
|
|
63
|
-
const description = extractDescription(content, name);
|
|
64
|
-
const skillDir = path.join(skillsOut, name);
|
|
65
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
66
|
-
const skillMd = `---\nname: ${yamlValue(name)}\ndescription: ${yamlValue(description)}\n---\n\n${content}`;
|
|
67
|
-
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function agentToSkill(file, skillsOut) {
|
|
71
|
-
const content = await fs.readFile(file, 'utf8');
|
|
72
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
73
|
-
const name = frontmatter.name || path.basename(file, '.md');
|
|
74
|
-
const description = frontmatter.description || extractDescription(body, name);
|
|
75
|
-
const skillDir = path.join(skillsOut, name);
|
|
76
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
77
|
-
const skillMd = `---\nname: ${yamlValue(name)}\ndescription: ${yamlValue(description)}\n---\n\n${body}`;
|
|
78
|
-
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function rulesToSkills(rulesDir, skillsOut) {
|
|
82
|
-
const stacks = await fs.readdir(rulesDir, { withFileTypes: true });
|
|
83
|
-
for (const entry of stacks) {
|
|
84
|
-
if (!entry.isDirectory() && !entry.name.endsWith('.md')) continue;
|
|
85
|
-
|
|
86
|
-
if (entry.isDirectory()) {
|
|
87
|
-
const stack = entry.name;
|
|
88
|
-
const stackDir = path.join(rulesDir, stack);
|
|
89
|
-
const files = await listFilesRecursive(stackDir, '.md');
|
|
90
|
-
if (files.length === 0) continue;
|
|
91
|
-
|
|
92
|
-
const skillName = `${stack}-rules`;
|
|
93
|
-
const skillDir = path.join(skillsOut, skillName);
|
|
94
|
-
const refsDir = path.join(skillDir, 'references');
|
|
95
|
-
await fs.mkdir(refsDir, { recursive: true });
|
|
96
|
-
|
|
97
|
-
const refList = [];
|
|
98
|
-
for (const f of files) {
|
|
99
|
-
const dest = path.join(refsDir, path.basename(f));
|
|
100
|
-
await fs.copyFile(f, dest);
|
|
101
|
-
refList.push(`- ${path.basename(f, '.md')}`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const skillMd = [
|
|
105
|
-
`---`,
|
|
106
|
-
`name: ${yamlValue(skillName)}`,
|
|
107
|
-
`description: ${yamlValue(`${stack} coding standards, patterns and security rules`)}`,
|
|
108
|
-
`---`,
|
|
109
|
-
``,
|
|
110
|
-
`# ${stack} Rules`,
|
|
111
|
-
``,
|
|
112
|
-
`References (loaded on demand):`,
|
|
113
|
-
...refList,
|
|
114
|
-
].join('\n') + '\n';
|
|
115
|
-
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
116
|
-
} else {
|
|
117
|
-
// top-level .md file (e.g. ado-integration.md)
|
|
118
|
-
const file = path.join(rulesDir, entry.name);
|
|
119
|
-
const content = await fs.readFile(file, 'utf8');
|
|
120
|
-
const name = path.basename(entry.name, '.md') + '-rules';
|
|
121
|
-
const skillDir = path.join(skillsOut, name);
|
|
122
|
-
const refsDir = path.join(skillDir, 'references');
|
|
123
|
-
await fs.mkdir(refsDir, { recursive: true });
|
|
124
|
-
await fs.copyFile(file, path.join(refsDir, entry.name));
|
|
125
|
-
const title = extractH1(content) || name;
|
|
126
|
-
const skillMd = `---\nname: ${yamlValue(name)}\ndescription: ${yamlValue(title)}\n---\n\n# ${title}\n\nSee references/${entry.name}\n`;
|
|
127
|
-
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function generateReadme(skillsOut, codexDir) {
|
|
133
|
-
const skillDirs = await fs.readdir(skillsOut, { withFileTypes: true });
|
|
134
|
-
const skillLines = [];
|
|
135
|
-
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
136
|
-
const skillFile = path.join(skillsOut, d.name, 'SKILL.md');
|
|
137
|
-
if (!(await exists(skillFile))) continue;
|
|
138
|
-
const content = await fs.readFile(skillFile, 'utf8');
|
|
139
|
-
const { frontmatter } = parseFrontmatter(content);
|
|
140
|
-
if (frontmatter.name) {
|
|
141
|
-
skillLines.push(`- **${frontmatter.name}**: ${frontmatter.description || ''}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const readme = [
|
|
146
|
-
`# TAS Kit — Codex`,
|
|
147
|
-
``,
|
|
148
|
-
`## Available Skills`,
|
|
149
|
-
``,
|
|
150
|
-
`Use \`/skills\` to list and invoke skills:`,
|
|
151
|
-
``,
|
|
152
|
-
...skillLines,
|
|
153
|
-
``,
|
|
154
|
-
].join('\n');
|
|
155
|
-
|
|
156
|
-
await fs.writeFile(path.join(codexDir, 'README.md'), readme);
|
|
157
|
-
}
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {
|
|
4
|
+
exists, listFilesRecursive,
|
|
5
|
+
parseFrontmatter, extractDescription, extractCommandName, extractH1, yamlValue,
|
|
6
|
+
} from './utils.js';
|
|
7
|
+
|
|
8
|
+
export const PLATFORM_ID = 'codex';
|
|
9
|
+
export const PLATFORM_LABEL = 'Codex';
|
|
10
|
+
|
|
11
|
+
export async function install({ tasDir, target }) {
|
|
12
|
+
const codexDir = path.join(target, '.codex');
|
|
13
|
+
const skillsOut = path.join(codexDir, 'skills');
|
|
14
|
+
await fs.mkdir(skillsOut, { recursive: true });
|
|
15
|
+
|
|
16
|
+
// commands → skills
|
|
17
|
+
const commandsDir = path.join(tasDir, 'commands');
|
|
18
|
+
if (await exists(commandsDir)) {
|
|
19
|
+
const files = await listFilesRecursive(commandsDir, '.md');
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
await commandToSkill(file, skillsOut);
|
|
22
|
+
}
|
|
23
|
+
console.log(' [ok] .codex/skills/ (commands)');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// agents → skills
|
|
27
|
+
const agentsDir = path.join(tasDir, 'agents');
|
|
28
|
+
if (await exists(agentsDir)) {
|
|
29
|
+
const files = await listFilesRecursive(agentsDir, '.md');
|
|
30
|
+
for (const file of files) {
|
|
31
|
+
await agentToSkill(file, skillsOut);
|
|
32
|
+
}
|
|
33
|
+
console.log(' [ok] .codex/skills/ (agents)');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// skills → skills (copy as-is, same format)
|
|
37
|
+
const skillsSrc = path.join(tasDir, 'skills');
|
|
38
|
+
if (await exists(skillsSrc)) {
|
|
39
|
+
const skillDirs = await fs.readdir(skillsSrc, { withFileTypes: true });
|
|
40
|
+
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
41
|
+
const src = path.join(skillsSrc, d.name);
|
|
42
|
+
const dest = path.join(skillsOut, d.name);
|
|
43
|
+
await fs.cp(src, dest, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
console.log(' [ok] .codex/skills/ (skills)');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// rules → skills with references/
|
|
49
|
+
const rulesDir = path.join(tasDir, 'rules');
|
|
50
|
+
if (await exists(rulesDir)) {
|
|
51
|
+
await rulesToSkills(rulesDir, skillsOut);
|
|
52
|
+
console.log(' [ok] .codex/skills/ (rules)');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// platform index (not root AGENTS.md — that's the template/system prompt)
|
|
56
|
+
await generateReadme(skillsOut, codexDir);
|
|
57
|
+
console.log(' [ok] .codex/README.md');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function commandToSkill(file, skillsOut) {
|
|
61
|
+
const content = await fs.readFile(file, 'utf8');
|
|
62
|
+
const name = extractCommandName(content) || path.basename(file, '.md');
|
|
63
|
+
const description = extractDescription(content, name);
|
|
64
|
+
const skillDir = path.join(skillsOut, name);
|
|
65
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
66
|
+
const skillMd = `---\nname: ${yamlValue(name)}\ndescription: ${yamlValue(description)}\n---\n\n${content}`;
|
|
67
|
+
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function agentToSkill(file, skillsOut) {
|
|
71
|
+
const content = await fs.readFile(file, 'utf8');
|
|
72
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
73
|
+
const name = frontmatter.name || path.basename(file, '.md');
|
|
74
|
+
const description = frontmatter.description || extractDescription(body, name);
|
|
75
|
+
const skillDir = path.join(skillsOut, name);
|
|
76
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
77
|
+
const skillMd = `---\nname: ${yamlValue(name)}\ndescription: ${yamlValue(description)}\n---\n\n${body}`;
|
|
78
|
+
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function rulesToSkills(rulesDir, skillsOut) {
|
|
82
|
+
const stacks = await fs.readdir(rulesDir, { withFileTypes: true });
|
|
83
|
+
for (const entry of stacks) {
|
|
84
|
+
if (!entry.isDirectory() && !entry.name.endsWith('.md')) continue;
|
|
85
|
+
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
const stack = entry.name;
|
|
88
|
+
const stackDir = path.join(rulesDir, stack);
|
|
89
|
+
const files = await listFilesRecursive(stackDir, '.md');
|
|
90
|
+
if (files.length === 0) continue;
|
|
91
|
+
|
|
92
|
+
const skillName = `${stack}-rules`;
|
|
93
|
+
const skillDir = path.join(skillsOut, skillName);
|
|
94
|
+
const refsDir = path.join(skillDir, 'references');
|
|
95
|
+
await fs.mkdir(refsDir, { recursive: true });
|
|
96
|
+
|
|
97
|
+
const refList = [];
|
|
98
|
+
for (const f of files) {
|
|
99
|
+
const dest = path.join(refsDir, path.basename(f));
|
|
100
|
+
await fs.copyFile(f, dest);
|
|
101
|
+
refList.push(`- ${path.basename(f, '.md')}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const skillMd = [
|
|
105
|
+
`---`,
|
|
106
|
+
`name: ${yamlValue(skillName)}`,
|
|
107
|
+
`description: ${yamlValue(`${stack} coding standards, patterns and security rules`)}`,
|
|
108
|
+
`---`,
|
|
109
|
+
``,
|
|
110
|
+
`# ${stack} Rules`,
|
|
111
|
+
``,
|
|
112
|
+
`References (loaded on demand):`,
|
|
113
|
+
...refList,
|
|
114
|
+
].join('\n') + '\n';
|
|
115
|
+
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
116
|
+
} else {
|
|
117
|
+
// top-level .md file (e.g. ado-integration.md)
|
|
118
|
+
const file = path.join(rulesDir, entry.name);
|
|
119
|
+
const content = await fs.readFile(file, 'utf8');
|
|
120
|
+
const name = path.basename(entry.name, '.md') + '-rules';
|
|
121
|
+
const skillDir = path.join(skillsOut, name);
|
|
122
|
+
const refsDir = path.join(skillDir, 'references');
|
|
123
|
+
await fs.mkdir(refsDir, { recursive: true });
|
|
124
|
+
await fs.copyFile(file, path.join(refsDir, entry.name));
|
|
125
|
+
const title = extractH1(content) || name;
|
|
126
|
+
const skillMd = `---\nname: ${yamlValue(name)}\ndescription: ${yamlValue(title)}\n---\n\n# ${title}\n\nSee references/${entry.name}\n`;
|
|
127
|
+
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function generateReadme(skillsOut, codexDir) {
|
|
133
|
+
const skillDirs = await fs.readdir(skillsOut, { withFileTypes: true });
|
|
134
|
+
const skillLines = [];
|
|
135
|
+
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
136
|
+
const skillFile = path.join(skillsOut, d.name, 'SKILL.md');
|
|
137
|
+
if (!(await exists(skillFile))) continue;
|
|
138
|
+
const content = await fs.readFile(skillFile, 'utf8');
|
|
139
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
140
|
+
if (frontmatter.name) {
|
|
141
|
+
skillLines.push(`- **${frontmatter.name}**: ${frontmatter.description || ''}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const readme = [
|
|
146
|
+
`# TAS Kit — Codex`,
|
|
147
|
+
``,
|
|
148
|
+
`## Available Skills`,
|
|
149
|
+
``,
|
|
150
|
+
`Use \`/skills\` to list and invoke skills:`,
|
|
151
|
+
``,
|
|
152
|
+
...skillLines,
|
|
153
|
+
``,
|
|
154
|
+
].join('\n');
|
|
155
|
+
|
|
156
|
+
await fs.writeFile(path.join(codexDir, 'README.md'), readme);
|
|
157
|
+
}
|
package/lib/adapters/cursor.js
CHANGED
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { copyDir, exists, listFilesRecursive, extractH1, yamlValue } from './utils.js';
|
|
4
|
-
|
|
5
|
-
export const PLATFORM_ID = 'cursor';
|
|
6
|
-
export const PLATFORM_LABEL = 'Cursor';
|
|
7
|
-
|
|
8
|
-
const HOOKS_JSON = {
|
|
9
|
-
postToolUse: [
|
|
10
|
-
{
|
|
11
|
-
matcher: '^(Write|Edit|MultiEdit)$',
|
|
12
|
-
hooks: [
|
|
13
|
-
{
|
|
14
|
-
type: 'command',
|
|
15
|
-
command: 'node .tas/_platform/hooks/code-quality.js',
|
|
16
|
-
description: 'Auto format source files: prettier (TS/JS), ruff/black (Python), dotnet format (C#)',
|
|
17
|
-
},
|
|
18
|
-
],
|
|
19
|
-
},
|
|
20
|
-
],
|
|
21
|
-
agentLoopEnd: [
|
|
22
|
-
{
|
|
23
|
-
hooks: [
|
|
24
|
-
{
|
|
25
|
-
type: 'command',
|
|
26
|
-
command: 'node .tas/_platform/hooks/session-end.js',
|
|
27
|
-
description: 'Run test suite, check project-status.yaml updated, remind next steps',
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export async function install({ tasDir, target }) {
|
|
35
|
-
const cursorDir = path.join(target, '.cursor');
|
|
36
|
-
await fs.mkdir(cursorDir, { recursive: true });
|
|
37
|
-
|
|
38
|
-
// commands — same format as Claude Code
|
|
39
|
-
await copyDir(path.join(tasDir, 'commands'), path.join(cursorDir, 'commands'));
|
|
40
|
-
console.log(' [ok] .cursor/commands/');
|
|
41
|
-
|
|
42
|
-
// agents — identical frontmatter format
|
|
43
|
-
await copyDir(path.join(tasDir, 'agents'), path.join(cursorDir, 'agents'));
|
|
44
|
-
console.log(' [ok] .cursor/agents/');
|
|
45
|
-
|
|
46
|
-
// skills — same open standard
|
|
47
|
-
const skillsSrc = path.join(tasDir, 'skills');
|
|
48
|
-
if (await exists(skillsSrc)) {
|
|
49
|
-
await copyDir(skillsSrc, path.join(cursorDir, 'skills'));
|
|
50
|
-
console.log(' [ok] .cursor/skills/');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// rules → .mdc files with frontmatter
|
|
54
|
-
const rulesDir = path.join(tasDir, 'rules');
|
|
55
|
-
const rulesOut = path.join(cursorDir, 'rules');
|
|
56
|
-
await fs.mkdir(rulesOut, { recursive: true });
|
|
57
|
-
await convertRulesToMdc(rulesDir, rulesOut);
|
|
58
|
-
console.log(' [ok] .cursor/rules/ (converted from .tas/rules/)');
|
|
59
|
-
|
|
60
|
-
// hooks.json
|
|
61
|
-
await fs.writeFile(
|
|
62
|
-
path.join(cursorDir, 'hooks.json'),
|
|
63
|
-
JSON.stringify(HOOKS_JSON, null, 2) + '\n'
|
|
64
|
-
);
|
|
65
|
-
console.log(' [ok] .cursor/hooks.json');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function convertRulesToMdc(rulesDir, outDir) {
|
|
69
|
-
if (!(await exists(rulesDir))) return;
|
|
70
|
-
const files = await listFilesRecursive(rulesDir, '.md');
|
|
71
|
-
for (const file of files) {
|
|
72
|
-
const rel = path.relative(rulesDir, file);
|
|
73
|
-
// flatten: common/code-review.md → common-code-review.mdc
|
|
74
|
-
const flatName = rel.replace(/[\\/]/g, '-').replace(/\.md$/, '.mdc');
|
|
75
|
-
const content = await fs.readFile(file, 'utf8');
|
|
76
|
-
const title = extractH1(content) || path.basename(file, '.md');
|
|
77
|
-
const mdc = `---\ndescription: ${yamlValue(title)}\nalwaysApply: false\n---\n\n${content}`;
|
|
78
|
-
await fs.writeFile(path.join(outDir, flatName), mdc);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { copyDir, exists, listFilesRecursive, extractH1, yamlValue } from './utils.js';
|
|
4
|
+
|
|
5
|
+
export const PLATFORM_ID = 'cursor';
|
|
6
|
+
export const PLATFORM_LABEL = 'Cursor';
|
|
7
|
+
|
|
8
|
+
const HOOKS_JSON = {
|
|
9
|
+
postToolUse: [
|
|
10
|
+
{
|
|
11
|
+
matcher: '^(Write|Edit|MultiEdit)$',
|
|
12
|
+
hooks: [
|
|
13
|
+
{
|
|
14
|
+
type: 'command',
|
|
15
|
+
command: 'node .tas/_platform/hooks/code-quality.js',
|
|
16
|
+
description: 'Auto format source files: prettier (TS/JS), ruff/black (Python), dotnet format (C#)',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
agentLoopEnd: [
|
|
22
|
+
{
|
|
23
|
+
hooks: [
|
|
24
|
+
{
|
|
25
|
+
type: 'command',
|
|
26
|
+
command: 'node .tas/_platform/hooks/session-end.js',
|
|
27
|
+
description: 'Run test suite, check project-status.yaml updated, remind next steps',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export async function install({ tasDir, target }) {
|
|
35
|
+
const cursorDir = path.join(target, '.cursor');
|
|
36
|
+
await fs.mkdir(cursorDir, { recursive: true });
|
|
37
|
+
|
|
38
|
+
// commands — same format as Claude Code
|
|
39
|
+
await copyDir(path.join(tasDir, 'commands'), path.join(cursorDir, 'commands'));
|
|
40
|
+
console.log(' [ok] .cursor/commands/');
|
|
41
|
+
|
|
42
|
+
// agents — identical frontmatter format
|
|
43
|
+
await copyDir(path.join(tasDir, 'agents'), path.join(cursorDir, 'agents'));
|
|
44
|
+
console.log(' [ok] .cursor/agents/');
|
|
45
|
+
|
|
46
|
+
// skills — same open standard
|
|
47
|
+
const skillsSrc = path.join(tasDir, 'skills');
|
|
48
|
+
if (await exists(skillsSrc)) {
|
|
49
|
+
await copyDir(skillsSrc, path.join(cursorDir, 'skills'));
|
|
50
|
+
console.log(' [ok] .cursor/skills/');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// rules → .mdc files with frontmatter
|
|
54
|
+
const rulesDir = path.join(tasDir, 'rules');
|
|
55
|
+
const rulesOut = path.join(cursorDir, 'rules');
|
|
56
|
+
await fs.mkdir(rulesOut, { recursive: true });
|
|
57
|
+
await convertRulesToMdc(rulesDir, rulesOut);
|
|
58
|
+
console.log(' [ok] .cursor/rules/ (converted from .tas/rules/)');
|
|
59
|
+
|
|
60
|
+
// hooks.json
|
|
61
|
+
await fs.writeFile(
|
|
62
|
+
path.join(cursorDir, 'hooks.json'),
|
|
63
|
+
JSON.stringify(HOOKS_JSON, null, 2) + '\n'
|
|
64
|
+
);
|
|
65
|
+
console.log(' [ok] .cursor/hooks.json');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function convertRulesToMdc(rulesDir, outDir) {
|
|
69
|
+
if (!(await exists(rulesDir))) return;
|
|
70
|
+
const files = await listFilesRecursive(rulesDir, '.md');
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
const rel = path.relative(rulesDir, file);
|
|
73
|
+
// flatten: common/code-review.md → common-code-review.mdc
|
|
74
|
+
const flatName = rel.replace(/[\\/]/g, '-').replace(/\.md$/, '.mdc');
|
|
75
|
+
const content = await fs.readFile(file, 'utf8');
|
|
76
|
+
const title = extractH1(content) || path.basename(file, '.md');
|
|
77
|
+
const mdc = `---\ndescription: ${yamlValue(title)}\nalwaysApply: false\n---\n\n${content}`;
|
|
78
|
+
await fs.writeFile(path.join(outDir, flatName), mdc);
|
|
79
|
+
}
|
|
80
|
+
}
|
package/lib/adapters/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
export { PLATFORM_ID as CLAUDE_CODE, install as installClaudeCode } from './claude-code.js';
|
|
2
|
-
export { PLATFORM_ID as CURSOR, install as installCursor } from './cursor.js';
|
|
3
|
-
export { PLATFORM_ID as CODEX, install as installCodex } from './codex.js';
|
|
4
|
-
export { PLATFORM_ID as ANTIGRAVITY, install as installAntigravity } from './antigravity.js';
|
|
5
|
-
|
|
6
|
-
import * as claudeCode from './claude-code.js';
|
|
7
|
-
import * as cursor from './cursor.js';
|
|
8
|
-
import * as codex from './codex.js';
|
|
9
|
-
import * as antigravity from './antigravity.js';
|
|
10
|
-
|
|
11
|
-
export const PLATFORMS = [
|
|
12
|
-
{ id: claudeCode.PLATFORM_ID, label: claudeCode.PLATFORM_LABEL, install: claudeCode.install },
|
|
13
|
-
{ id: cursor.PLATFORM_ID, label: cursor.PLATFORM_LABEL, install: cursor.install },
|
|
14
|
-
{ id: codex.PLATFORM_ID, label: codex.PLATFORM_LABEL, install: codex.install },
|
|
15
|
-
{ id: antigravity.PLATFORM_ID, label: antigravity.PLATFORM_LABEL, install: antigravity.install },
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
export function getPlatform(id) {
|
|
19
|
-
return PLATFORMS.find(p => p.id === id);
|
|
20
|
-
}
|
|
1
|
+
export { PLATFORM_ID as CLAUDE_CODE, install as installClaudeCode } from './claude-code.js';
|
|
2
|
+
export { PLATFORM_ID as CURSOR, install as installCursor } from './cursor.js';
|
|
3
|
+
export { PLATFORM_ID as CODEX, install as installCodex } from './codex.js';
|
|
4
|
+
export { PLATFORM_ID as ANTIGRAVITY, install as installAntigravity } from './antigravity.js';
|
|
5
|
+
|
|
6
|
+
import * as claudeCode from './claude-code.js';
|
|
7
|
+
import * as cursor from './cursor.js';
|
|
8
|
+
import * as codex from './codex.js';
|
|
9
|
+
import * as antigravity from './antigravity.js';
|
|
10
|
+
|
|
11
|
+
export const PLATFORMS = [
|
|
12
|
+
{ id: claudeCode.PLATFORM_ID, label: claudeCode.PLATFORM_LABEL, install: claudeCode.install },
|
|
13
|
+
{ id: cursor.PLATFORM_ID, label: cursor.PLATFORM_LABEL, install: cursor.install },
|
|
14
|
+
{ id: codex.PLATFORM_ID, label: codex.PLATFORM_LABEL, install: codex.install },
|
|
15
|
+
{ id: antigravity.PLATFORM_ID, label: antigravity.PLATFORM_LABEL, install: antigravity.install },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export function getPlatform(id) {
|
|
19
|
+
return PLATFORMS.find(p => p.id === id);
|
|
20
|
+
}
|