@torus-engineering/tas-kit 1.14.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 +131 -54
- 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 -546
- package/package.json +1 -1
- 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/bin/cli.js
CHANGED
|
@@ -1,91 +1,91 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { install, update } from '../lib/install.js';
|
|
3
|
-
|
|
4
|
-
const args = process.argv.slice(2);
|
|
5
|
-
const command = args[0];
|
|
6
|
-
|
|
7
|
-
function printHelp() {
|
|
8
|
-
console.log(`
|
|
9
|
-
tas-kit — Turbo Agentic SDLC Kit installer
|
|
10
|
-
|
|
11
|
-
Usage:
|
|
12
|
-
npx @torus-engineering/tas-kit <command> [options]
|
|
13
|
-
|
|
14
|
-
Commands:
|
|
15
|
-
install Copy TAS Kit files into a project (first-time setup)
|
|
16
|
-
update Overwrite .tas/ and platform directories with the latest kit version
|
|
17
|
-
(preserves CLAUDE.md, tas.yaml, .env.example)
|
|
18
|
-
|
|
19
|
-
Options:
|
|
20
|
-
--directory <path> Target directory (default: current working directory)
|
|
21
|
-
--yes, -y Skip confirmation prompts
|
|
22
|
-
--platform <id,...> Platform(s) to install: claude-code, cursor, codex, antigravity
|
|
23
|
-
Comma-separated for multiple (default: interactive prompt)
|
|
24
|
-
--security-hook <mode> Pre-commit hook mode: husky | native | none
|
|
25
|
-
--help, -h Show this help message
|
|
26
|
-
|
|
27
|
-
Platforms:
|
|
28
|
-
claude-code Claude Code CLI (default)
|
|
29
|
-
cursor Cursor IDE
|
|
30
|
-
codex OpenAI Codex CLI
|
|
31
|
-
antigravity Google Antigravity IDE
|
|
32
|
-
|
|
33
|
-
Examples:
|
|
34
|
-
npx @torus-engineering/tas-kit install
|
|
35
|
-
npx @torus-engineering/tas-kit install --platform claude-code,cursor
|
|
36
|
-
npx @torus-engineering/tas-kit install --platform codex --yes
|
|
37
|
-
npx @torus-engineering/tas-kit install --yes --security-hook=husky
|
|
38
|
-
npx @torus-engineering/tas-kit update
|
|
39
|
-
npx @torus-engineering/tas-kit update --platform cursor
|
|
40
|
-
`.trim());
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (!command || command === '--help' || command === '-h') {
|
|
44
|
-
printHelp();
|
|
45
|
-
process.exit(0);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (command !== 'install' && command !== 'update') {
|
|
49
|
-
console.error(`Unknown command: "${command}"\n`);
|
|
50
|
-
printHelp();
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const VALID_PLATFORMS = ['claude-code', 'cursor', 'codex', 'antigravity'];
|
|
55
|
-
const opts = { directory: process.cwd(), yes: false, securityHook: null, platforms: [] };
|
|
56
|
-
|
|
57
|
-
for (let i = 1; i < args.length; i++) {
|
|
58
|
-
const a = args[i];
|
|
59
|
-
if ((a === '--directory' || a === '-d') && args[i + 1]) {
|
|
60
|
-
opts.directory = args[++i];
|
|
61
|
-
} else if (a === '--yes' || a === '-y') {
|
|
62
|
-
opts.yes = true;
|
|
63
|
-
} else if (a.startsWith('--security-hook=')) {
|
|
64
|
-
opts.securityHook = a.slice('--security-hook='.length).toLowerCase();
|
|
65
|
-
} else if (a === '--security-hook' && args[i + 1]) {
|
|
66
|
-
opts.securityHook = args[++i].toLowerCase();
|
|
67
|
-
} else if (a.startsWith('--platform=')) {
|
|
68
|
-
opts.platforms = a.slice('--platform='.length).split(',').map(s => s.trim().toLowerCase());
|
|
69
|
-
} else if (a === '--platform' && args[i + 1]) {
|
|
70
|
-
opts.platforms = args[++i].split(',').map(s => s.trim().toLowerCase());
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (opts.securityHook && !['husky', 'native', 'none'].includes(opts.securityHook)) {
|
|
75
|
-
console.error(`Invalid --security-hook value: "${opts.securityHook}"`);
|
|
76
|
-
console.error(`Expected one of: husky, native, none`);
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const invalidPlatforms = opts.platforms.filter(p => !VALID_PLATFORMS.includes(p));
|
|
81
|
-
if (invalidPlatforms.length > 0) {
|
|
82
|
-
console.error(`Invalid platform(s): ${invalidPlatforms.join(', ')}`);
|
|
83
|
-
console.error(`Valid platforms: ${VALID_PLATFORMS.join(', ')}`);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const runner = command === 'update' ? update : install;
|
|
88
|
-
runner(opts).catch((err) => {
|
|
89
|
-
console.error(`\n${command === 'update' ? 'Update' : 'Install'} failed:`, err.message);
|
|
90
|
-
process.exit(1);
|
|
91
|
-
});
|
|
2
|
+
import { install, update } from '../lib/install.js';
|
|
3
|
+
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const command = args[0];
|
|
6
|
+
|
|
7
|
+
function printHelp() {
|
|
8
|
+
console.log(`
|
|
9
|
+
tas-kit — Turbo Agentic SDLC Kit installer
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
npx @torus-engineering/tas-kit <command> [options]
|
|
13
|
+
|
|
14
|
+
Commands:
|
|
15
|
+
install Copy TAS Kit files into a project (first-time setup)
|
|
16
|
+
update Overwrite .tas/ and platform directories with the latest kit version
|
|
17
|
+
(preserves CLAUDE.md, tas.yaml, .env.example)
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
--directory <path> Target directory (default: current working directory)
|
|
21
|
+
--yes, -y Skip confirmation prompts
|
|
22
|
+
--platform <id,...> Platform(s) to install: claude-code, cursor, codex, antigravity
|
|
23
|
+
Comma-separated for multiple (default: interactive prompt)
|
|
24
|
+
--security-hook <mode> Pre-commit hook mode: husky | native | none
|
|
25
|
+
--help, -h Show this help message
|
|
26
|
+
|
|
27
|
+
Platforms:
|
|
28
|
+
claude-code Claude Code CLI (default)
|
|
29
|
+
cursor Cursor IDE
|
|
30
|
+
codex OpenAI Codex CLI
|
|
31
|
+
antigravity Google Antigravity IDE
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
npx @torus-engineering/tas-kit install
|
|
35
|
+
npx @torus-engineering/tas-kit install --platform claude-code,cursor
|
|
36
|
+
npx @torus-engineering/tas-kit install --platform codex --yes
|
|
37
|
+
npx @torus-engineering/tas-kit install --yes --security-hook=husky
|
|
38
|
+
npx @torus-engineering/tas-kit update
|
|
39
|
+
npx @torus-engineering/tas-kit update --platform cursor
|
|
40
|
+
`.trim());
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!command || command === '--help' || command === '-h') {
|
|
44
|
+
printHelp();
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (command !== 'install' && command !== 'update') {
|
|
49
|
+
console.error(`Unknown command: "${command}"\n`);
|
|
50
|
+
printHelp();
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const VALID_PLATFORMS = ['claude-code', 'cursor', 'codex', 'antigravity'];
|
|
55
|
+
const opts = { directory: process.cwd(), yes: false, securityHook: null, platforms: [] };
|
|
56
|
+
|
|
57
|
+
for (let i = 1; i < args.length; i++) {
|
|
58
|
+
const a = args[i];
|
|
59
|
+
if ((a === '--directory' || a === '-d') && args[i + 1]) {
|
|
60
|
+
opts.directory = args[++i];
|
|
61
|
+
} else if (a === '--yes' || a === '-y') {
|
|
62
|
+
opts.yes = true;
|
|
63
|
+
} else if (a.startsWith('--security-hook=')) {
|
|
64
|
+
opts.securityHook = a.slice('--security-hook='.length).toLowerCase();
|
|
65
|
+
} else if (a === '--security-hook' && args[i + 1]) {
|
|
66
|
+
opts.securityHook = args[++i].toLowerCase();
|
|
67
|
+
} else if (a.startsWith('--platform=')) {
|
|
68
|
+
opts.platforms = a.slice('--platform='.length).split(',').map(s => s.trim().toLowerCase());
|
|
69
|
+
} else if (a === '--platform' && args[i + 1]) {
|
|
70
|
+
opts.platforms = args[++i].split(',').map(s => s.trim().toLowerCase());
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (opts.securityHook && !['husky', 'native', 'none'].includes(opts.securityHook)) {
|
|
75
|
+
console.error(`Invalid --security-hook value: "${opts.securityHook}"`);
|
|
76
|
+
console.error(`Expected one of: husky, native, none`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const invalidPlatforms = opts.platforms.filter(p => !VALID_PLATFORMS.includes(p));
|
|
81
|
+
if (invalidPlatforms.length > 0) {
|
|
82
|
+
console.error(`Invalid platform(s): ${invalidPlatforms.join(', ')}`);
|
|
83
|
+
console.error(`Valid platforms: ${VALID_PLATFORMS.join(', ')}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const runner = command === 'update' ? update : install;
|
|
88
|
+
runner(opts).catch((err) => {
|
|
89
|
+
console.error(`\n${command === 'update' ? 'Update' : 'Install'} failed:`, err.message);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|
|
@@ -1,131 +1,131 @@
|
|
|
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 = 'antigravity';
|
|
9
|
-
export const PLATFORM_LABEL = 'Antigravity';
|
|
10
|
-
|
|
11
|
-
export async function install({ tasDir, target }) {
|
|
12
|
-
const agentsDir = path.join(target, '.agents');
|
|
13
|
-
await fs.mkdir(agentsDir, { recursive: true });
|
|
14
|
-
await fs.mkdir(path.join(agentsDir, 'rules'), { recursive: true });
|
|
15
|
-
await fs.mkdir(path.join(agentsDir, 'skills'), { recursive: true });
|
|
16
|
-
|
|
17
|
-
// commands → .agents/<name>.md (Antigravity workflows)
|
|
18
|
-
const commandsDir = path.join(tasDir, 'commands');
|
|
19
|
-
if (await exists(commandsDir)) {
|
|
20
|
-
const files = await listFilesRecursive(commandsDir, '.md');
|
|
21
|
-
for (const file of files) {
|
|
22
|
-
await commandToWorkflow(file, agentsDir);
|
|
23
|
-
}
|
|
24
|
-
console.log(' [ok] .agents/ (workflows from commands)');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// agents → .agents/skills/<name>/SKILL.md
|
|
28
|
-
const agentsSrc = path.join(tasDir, 'agents');
|
|
29
|
-
if (await exists(agentsSrc)) {
|
|
30
|
-
const files = await listFilesRecursive(agentsSrc, '.md');
|
|
31
|
-
for (const file of files) {
|
|
32
|
-
await agentToSkill(file, path.join(agentsDir, 'skills'));
|
|
33
|
-
}
|
|
34
|
-
console.log(' [ok] .agents/skills/ (agents)');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// kit skills → .agents/skills/ (copy as-is, same open standard)
|
|
38
|
-
const skillsSrc = path.join(tasDir, 'skills');
|
|
39
|
-
if (await exists(skillsSrc)) {
|
|
40
|
-
const skillDirs = await fs.readdir(skillsSrc, { withFileTypes: true });
|
|
41
|
-
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
42
|
-
await fs.cp(
|
|
43
|
-
path.join(skillsSrc, d.name),
|
|
44
|
-
path.join(agentsDir, 'skills', d.name),
|
|
45
|
-
{ recursive: true }
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
console.log(' [ok] .agents/skills/ (skills)');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// rules → .agents/rules/<stack>-<topic>.md
|
|
52
|
-
const rulesDir = path.join(tasDir, 'rules');
|
|
53
|
-
if (await exists(rulesDir)) {
|
|
54
|
-
await convertRules(rulesDir, path.join(agentsDir, 'rules'));
|
|
55
|
-
console.log(' [ok] .agents/rules/ (from .tas/rules/)');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// platform index (not root AGENTS.md — that's the template/system prompt)
|
|
59
|
-
await generateReadme(agentsDir);
|
|
60
|
-
console.log(' [ok] .agents/README.md');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function commandToWorkflow(file, agentsDir) {
|
|
64
|
-
const content = await fs.readFile(file, 'utf8');
|
|
65
|
-
const name = extractCommandName(content) || path.basename(file, '.md');
|
|
66
|
-
// Antigravity workflows: same .md format, stored in .agents/<name>.md
|
|
67
|
-
await fs.writeFile(path.join(agentsDir, `${name}.md`), content);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function agentToSkill(file, skillsDir) {
|
|
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(skillsDir, name);
|
|
76
|
-
await fs.mkdir(skillDir, { recursive: true });
|
|
77
|
-
// Antigravity SKILL.md: same open standard, drop allowed-tools (platform manages tool access)
|
|
78
|
-
const skillMd = `---\nname: ${yamlValue(name)}\ndescription: ${yamlValue(description)}\n---\n\n${body}`;
|
|
79
|
-
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function convertRules(rulesDir, outDir) {
|
|
83
|
-
const entries = await fs.readdir(rulesDir, { withFileTypes: true });
|
|
84
|
-
for (const entry of entries) {
|
|
85
|
-
if (entry.isDirectory()) {
|
|
86
|
-
const stack = entry.name;
|
|
87
|
-
const files = await listFilesRecursive(path.join(rulesDir, stack), '.md');
|
|
88
|
-
for (const f of files) {
|
|
89
|
-
const topic = path.basename(f, '.md');
|
|
90
|
-
await fs.copyFile(f, path.join(outDir, `${stack}-${topic}.md`));
|
|
91
|
-
}
|
|
92
|
-
} else if (entry.name.endsWith('.md')) {
|
|
93
|
-
await fs.copyFile(path.join(rulesDir, entry.name), path.join(outDir, entry.name));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async function generateReadme(agentsDir) {
|
|
99
|
-
const workflowFiles = (await fs.readdir(agentsDir, { withFileTypes: true }))
|
|
100
|
-
.filter(e => e.isFile() && e.name.endsWith('.md'))
|
|
101
|
-
.map(e => `- \`/${e.name.replace('.md', '')}\``);
|
|
102
|
-
|
|
103
|
-
const skillDirs = await fs.readdir(path.join(agentsDir, 'skills'), { withFileTypes: true });
|
|
104
|
-
const skillLines = [];
|
|
105
|
-
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
106
|
-
const skillFile = path.join(agentsDir, 'skills', d.name, 'SKILL.md');
|
|
107
|
-
if (!(await exists(skillFile))) continue;
|
|
108
|
-
const content = await fs.readFile(skillFile, 'utf8');
|
|
109
|
-
const { frontmatter } = parseFrontmatter(content);
|
|
110
|
-
if (frontmatter.name) {
|
|
111
|
-
skillLines.push(`- **${frontmatter.name}**: ${frontmatter.description || ''}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const readme = [
|
|
116
|
-
`# TAS Kit — Antigravity`,
|
|
117
|
-
``,
|
|
118
|
-
`## Workflows`,
|
|
119
|
-
``,
|
|
120
|
-
`Invoke via \`/workflow-name\` in chat:`,
|
|
121
|
-
``,
|
|
122
|
-
...workflowFiles,
|
|
123
|
-
``,
|
|
124
|
-
`## Skills`,
|
|
125
|
-
``,
|
|
126
|
-
...skillLines,
|
|
127
|
-
``,
|
|
128
|
-
].join('\n');
|
|
129
|
-
|
|
130
|
-
await fs.writeFile(path.join(agentsDir, 'README.md'), readme);
|
|
131
|
-
}
|
|
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 = 'antigravity';
|
|
9
|
+
export const PLATFORM_LABEL = 'Antigravity';
|
|
10
|
+
|
|
11
|
+
export async function install({ tasDir, target }) {
|
|
12
|
+
const agentsDir = path.join(target, '.agents');
|
|
13
|
+
await fs.mkdir(agentsDir, { recursive: true });
|
|
14
|
+
await fs.mkdir(path.join(agentsDir, 'rules'), { recursive: true });
|
|
15
|
+
await fs.mkdir(path.join(agentsDir, 'skills'), { recursive: true });
|
|
16
|
+
|
|
17
|
+
// commands → .agents/<name>.md (Antigravity workflows)
|
|
18
|
+
const commandsDir = path.join(tasDir, 'commands');
|
|
19
|
+
if (await exists(commandsDir)) {
|
|
20
|
+
const files = await listFilesRecursive(commandsDir, '.md');
|
|
21
|
+
for (const file of files) {
|
|
22
|
+
await commandToWorkflow(file, agentsDir);
|
|
23
|
+
}
|
|
24
|
+
console.log(' [ok] .agents/ (workflows from commands)');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// agents → .agents/skills/<name>/SKILL.md
|
|
28
|
+
const agentsSrc = path.join(tasDir, 'agents');
|
|
29
|
+
if (await exists(agentsSrc)) {
|
|
30
|
+
const files = await listFilesRecursive(agentsSrc, '.md');
|
|
31
|
+
for (const file of files) {
|
|
32
|
+
await agentToSkill(file, path.join(agentsDir, 'skills'));
|
|
33
|
+
}
|
|
34
|
+
console.log(' [ok] .agents/skills/ (agents)');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// kit skills → .agents/skills/ (copy as-is, same open standard)
|
|
38
|
+
const skillsSrc = path.join(tasDir, 'skills');
|
|
39
|
+
if (await exists(skillsSrc)) {
|
|
40
|
+
const skillDirs = await fs.readdir(skillsSrc, { withFileTypes: true });
|
|
41
|
+
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
42
|
+
await fs.cp(
|
|
43
|
+
path.join(skillsSrc, d.name),
|
|
44
|
+
path.join(agentsDir, 'skills', d.name),
|
|
45
|
+
{ recursive: true }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
console.log(' [ok] .agents/skills/ (skills)');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// rules → .agents/rules/<stack>-<topic>.md
|
|
52
|
+
const rulesDir = path.join(tasDir, 'rules');
|
|
53
|
+
if (await exists(rulesDir)) {
|
|
54
|
+
await convertRules(rulesDir, path.join(agentsDir, 'rules'));
|
|
55
|
+
console.log(' [ok] .agents/rules/ (from .tas/rules/)');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// platform index (not root AGENTS.md — that's the template/system prompt)
|
|
59
|
+
await generateReadme(agentsDir);
|
|
60
|
+
console.log(' [ok] .agents/README.md');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function commandToWorkflow(file, agentsDir) {
|
|
64
|
+
const content = await fs.readFile(file, 'utf8');
|
|
65
|
+
const name = extractCommandName(content) || path.basename(file, '.md');
|
|
66
|
+
// Antigravity workflows: same .md format, stored in .agents/<name>.md
|
|
67
|
+
await fs.writeFile(path.join(agentsDir, `${name}.md`), content);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function agentToSkill(file, skillsDir) {
|
|
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(skillsDir, name);
|
|
76
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
77
|
+
// Antigravity SKILL.md: same open standard, drop allowed-tools (platform manages tool access)
|
|
78
|
+
const skillMd = `---\nname: ${yamlValue(name)}\ndescription: ${yamlValue(description)}\n---\n\n${body}`;
|
|
79
|
+
await fs.writeFile(path.join(skillDir, 'SKILL.md'), skillMd);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function convertRules(rulesDir, outDir) {
|
|
83
|
+
const entries = await fs.readdir(rulesDir, { withFileTypes: true });
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
if (entry.isDirectory()) {
|
|
86
|
+
const stack = entry.name;
|
|
87
|
+
const files = await listFilesRecursive(path.join(rulesDir, stack), '.md');
|
|
88
|
+
for (const f of files) {
|
|
89
|
+
const topic = path.basename(f, '.md');
|
|
90
|
+
await fs.copyFile(f, path.join(outDir, `${stack}-${topic}.md`));
|
|
91
|
+
}
|
|
92
|
+
} else if (entry.name.endsWith('.md')) {
|
|
93
|
+
await fs.copyFile(path.join(rulesDir, entry.name), path.join(outDir, entry.name));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function generateReadme(agentsDir) {
|
|
99
|
+
const workflowFiles = (await fs.readdir(agentsDir, { withFileTypes: true }))
|
|
100
|
+
.filter(e => e.isFile() && e.name.endsWith('.md'))
|
|
101
|
+
.map(e => `- \`/${e.name.replace('.md', '')}\``);
|
|
102
|
+
|
|
103
|
+
const skillDirs = await fs.readdir(path.join(agentsDir, 'skills'), { withFileTypes: true });
|
|
104
|
+
const skillLines = [];
|
|
105
|
+
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
106
|
+
const skillFile = path.join(agentsDir, 'skills', d.name, 'SKILL.md');
|
|
107
|
+
if (!(await exists(skillFile))) continue;
|
|
108
|
+
const content = await fs.readFile(skillFile, 'utf8');
|
|
109
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
110
|
+
if (frontmatter.name) {
|
|
111
|
+
skillLines.push(`- **${frontmatter.name}**: ${frontmatter.description || ''}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const readme = [
|
|
116
|
+
`# TAS Kit — Antigravity`,
|
|
117
|
+
``,
|
|
118
|
+
`## Workflows`,
|
|
119
|
+
``,
|
|
120
|
+
`Invoke via \`/workflow-name\` in chat:`,
|
|
121
|
+
``,
|
|
122
|
+
...workflowFiles,
|
|
123
|
+
``,
|
|
124
|
+
`## Skills`,
|
|
125
|
+
``,
|
|
126
|
+
...skillLines,
|
|
127
|
+
``,
|
|
128
|
+
].join('\n');
|
|
129
|
+
|
|
130
|
+
await fs.writeFile(path.join(agentsDir, 'README.md'), readme);
|
|
131
|
+
}
|
|
@@ -1,35 +1,71 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { copyDir, exists } from './utils.js';
|
|
4
|
-
|
|
5
|
-
export const PLATFORM_ID = 'claude-code';
|
|
6
|
-
export const PLATFORM_LABEL = 'Claude Code';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { copyDir, exists } from './utils.js';
|
|
4
|
+
|
|
5
|
+
export const PLATFORM_ID = 'claude-code';
|
|
6
|
+
export const PLATFORM_LABEL = 'Claude Code';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Merge template settings into existing settings without overwriting user values.
|
|
10
|
+
* - Objects (mcpServers, hooks.*): add template keys absent in existing
|
|
11
|
+
* - Arrays (permissions.allow/deny): union — add template entries absent in existing
|
|
12
|
+
* - Scalars: existing wins
|
|
13
|
+
*/
|
|
14
|
+
function mergeSettings(existing, template) {
|
|
15
|
+
const result = structuredClone(existing);
|
|
16
|
+
|
|
17
|
+
for (const [key, tval] of Object.entries(template)) {
|
|
18
|
+
if (!(key in result)) {
|
|
19
|
+
result[key] = structuredClone(tval);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const eval_ = result[key];
|
|
23
|
+
if (Array.isArray(tval) && Array.isArray(eval_)) {
|
|
24
|
+
const set = new Set(eval_);
|
|
25
|
+
for (const item of tval) if (!set.has(item)) eval_.push(item);
|
|
26
|
+
} else if (tval && typeof tval === 'object' && !Array.isArray(tval)) {
|
|
27
|
+
result[key] = mergeSettings(eval_, tval);
|
|
28
|
+
}
|
|
29
|
+
// scalar: existing wins — no action
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function install({ tasDir, target }) {
|
|
36
|
+
const claudeDir = path.join(target, '.claude');
|
|
37
|
+
await fs.mkdir(claudeDir, { recursive: true });
|
|
38
|
+
|
|
39
|
+
await copyDir(path.join(tasDir, 'commands'), path.join(claudeDir, 'commands'));
|
|
40
|
+
console.log(' [ok] .claude/commands/');
|
|
41
|
+
|
|
42
|
+
await copyDir(path.join(tasDir, 'agents'), path.join(claudeDir, 'agents'));
|
|
43
|
+
console.log(' [ok] .claude/agents/');
|
|
44
|
+
|
|
45
|
+
const skillsSrc = path.join(tasDir, 'skills');
|
|
46
|
+
if (await exists(skillsSrc)) {
|
|
47
|
+
await copyDir(skillsSrc, path.join(claudeDir, 'skills'));
|
|
48
|
+
console.log(' [ok] .claude/skills/');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const hooksSrc = path.join(tasDir, '_platform', 'hooks');
|
|
52
|
+
if (await exists(hooksSrc)) {
|
|
53
|
+
await copyDir(hooksSrc, path.join(claudeDir, 'hooks'));
|
|
54
|
+
console.log(' [ok] .claude/hooks/');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const settingsSrc = path.join(tasDir, '_platform', 'claude-code', 'settings.json');
|
|
58
|
+
const settingsDest = path.join(claudeDir, 'settings.json');
|
|
59
|
+
if (await exists(settingsSrc)) {
|
|
60
|
+
const template = JSON.parse(await fs.readFile(settingsSrc, 'utf8'));
|
|
61
|
+
if (await exists(settingsDest)) {
|
|
62
|
+
const existing = JSON.parse(await fs.readFile(settingsDest, 'utf8'));
|
|
63
|
+
const merged = mergeSettings(existing, template);
|
|
64
|
+
await fs.writeFile(settingsDest, JSON.stringify(merged, null, 2) + '\n', 'utf8');
|
|
65
|
+
console.log(' [merged] .claude/settings.json (existing preserved, new keys added)');
|
|
66
|
+
} else {
|
|
67
|
+
await fs.writeFile(settingsDest, JSON.stringify(template, null, 2) + '\n', 'utf8');
|
|
68
|
+
console.log(' [ok] .claude/settings.json');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|