@torus-engineering/tas-kit 1.12.0 → 1.13.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/README.md +3 -3
- package/.tas/commands/tas-init.md +1 -1
- package/README.md +3 -3
- package/bin/cli.js +1 -1
- package/lib/adapters/antigravity.js +7 -13
- package/lib/adapters/codex.js +8 -14
- package/lib/install.js +180 -40
- package/package.json +2 -2
package/.tas/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TAS Kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Turbo AI-first toolkit for modern agentic SDLC teams.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -33,8 +33,8 @@ All code starts from spec. Story.md is "context digest" that has absorbed all in
|
|
|
33
33
|
- When coding: MUST start new session (don't reuse old session that loaded PRD/SAD to avoid Window Context bloat)
|
|
34
34
|
|
|
35
35
|
### Role-Based & Template-Driven
|
|
36
|
-
- 3
|
|
37
|
-
- Separate templates for PRD, SAD, ADR, Epic, Feature, Story per
|
|
36
|
+
- 3 roles: PE (Product Engineer), SE (Software Engineer), DSE (DevOps Engineer)
|
|
37
|
+
- Separate templates for PRD, SAD, ADR, Epic, Feature, Story per team standards
|
|
38
38
|
- Flow configurable via `tas.yaml` file
|
|
39
39
|
|
|
40
40
|
### Azure DevOps Compatible
|
|
@@ -4,7 +4,7 @@ Initialize TAS kit for the current project.
|
|
|
4
4
|
|
|
5
5
|
## Actions
|
|
6
6
|
1. Need context from root/tas.yaml. If not exists, copy from .tas/tas-example.yaml to root and ask user to fill required information.
|
|
7
|
-
2. Create directory structure: .tas/templates/, docs/, docs/adr/, docs/epics/, docs/bugs/
|
|
7
|
+
2. Create directory structure: .tas/templates/, docs/, docs/adr/, docs/epics/, docs/bugs/, docs/ref/
|
|
8
8
|
3. Create root/project-status.yaml file with initial state (artifacts, epics, adrs all empty).
|
|
9
9
|
4. Copy default templates to .tas/templates/ if not exist.
|
|
10
10
|
5. If project type is brownfield and codebase_scan_on_init = true:
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TAS Kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Turbo AI-first toolkit for modern agentic SDLC teams.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -33,8 +33,8 @@ All code starts from spec. Story.md is "context digest" that has absorbed all in
|
|
|
33
33
|
- When coding: MUST start new session (don't reuse old session that loaded PRD/SAD to avoid Window Context bloat)
|
|
34
34
|
|
|
35
35
|
### Role-Based & Template-Driven
|
|
36
|
-
- 3
|
|
37
|
-
- Separate templates for PRD, SAD, ADR, Epic, Feature, Story per
|
|
36
|
+
- 3 roles: PE (Product Engineer), SE (Software Engineer), DSE (DevOps Engineer)
|
|
37
|
+
- Separate templates for PRD, SAD, ADR, Epic, Feature, Story per team standards
|
|
38
38
|
- Flow configurable via `tas.yaml` file
|
|
39
39
|
|
|
40
40
|
### Azure DevOps Compatible
|
package/bin/cli.js
CHANGED
|
@@ -55,9 +55,9 @@ export async function install({ tasDir, target }) {
|
|
|
55
55
|
console.log(' [ok] .agents/rules/ (from .tas/rules/)');
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
//
|
|
59
|
-
await
|
|
60
|
-
console.log(' [ok]
|
|
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
61
|
}
|
|
62
62
|
|
|
63
63
|
async function commandToWorkflow(file, agentsDir) {
|
|
@@ -95,7 +95,7 @@ async function convertRules(rulesDir, outDir) {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
async function
|
|
98
|
+
async function generateReadme(agentsDir) {
|
|
99
99
|
const workflowFiles = (await fs.readdir(agentsDir, { withFileTypes: true }))
|
|
100
100
|
.filter(e => e.isFile() && e.name.endsWith('.md'))
|
|
101
101
|
.map(e => `- \`/${e.name.replace('.md', '')}\``);
|
|
@@ -112,10 +112,8 @@ async function generateAgentsMd(agentsDir, target) {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
const
|
|
116
|
-
`#
|
|
117
|
-
``,
|
|
118
|
-
`This project uses TAS Kit — Torus Agentic SDLC Kit.`,
|
|
115
|
+
const readme = [
|
|
116
|
+
`# TAS Kit — Antigravity`,
|
|
119
117
|
``,
|
|
120
118
|
`## Workflows`,
|
|
121
119
|
``,
|
|
@@ -127,11 +125,7 @@ async function generateAgentsMd(agentsDir, target) {
|
|
|
127
125
|
``,
|
|
128
126
|
...skillLines,
|
|
129
127
|
``,
|
|
130
|
-
`## Project Configuration`,
|
|
131
|
-
``,
|
|
132
|
-
`See \`tas.yaml\` for project settings and project instructions file for detailed conventions.`,
|
|
133
|
-
``,
|
|
134
128
|
].join('\n');
|
|
135
129
|
|
|
136
|
-
await fs.writeFile(path.join(
|
|
130
|
+
await fs.writeFile(path.join(agentsDir, 'README.md'), readme);
|
|
137
131
|
}
|
package/lib/adapters/codex.js
CHANGED
|
@@ -52,9 +52,9 @@ export async function install({ tasDir, target }) {
|
|
|
52
52
|
console.log(' [ok] .codex/skills/ (rules)');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
//
|
|
56
|
-
await
|
|
57
|
-
console.log(' [ok]
|
|
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
58
|
}
|
|
59
59
|
|
|
60
60
|
async function commandToSkill(file, skillsOut) {
|
|
@@ -129,7 +129,7 @@ async function rulesToSkills(rulesDir, skillsOut) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
async function
|
|
132
|
+
async function generateReadme(skillsOut, codexDir) {
|
|
133
133
|
const skillDirs = await fs.readdir(skillsOut, { withFileTypes: true });
|
|
134
134
|
const skillLines = [];
|
|
135
135
|
for (const d of skillDirs.filter(e => e.isDirectory())) {
|
|
@@ -142,22 +142,16 @@ async function generateAgentsMd(skillsOut, target) {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
const
|
|
146
|
-
`#
|
|
147
|
-
``,
|
|
148
|
-
`This project uses TAS Kit — Torus Agentic SDLC Kit.`,
|
|
145
|
+
const readme = [
|
|
146
|
+
`# TAS Kit — Codex`,
|
|
149
147
|
``,
|
|
150
148
|
`## Available Skills`,
|
|
151
149
|
``,
|
|
152
|
-
`Use \`/skills\` to list and invoke skills
|
|
150
|
+
`Use \`/skills\` to list and invoke skills:`,
|
|
153
151
|
``,
|
|
154
152
|
...skillLines,
|
|
155
153
|
``,
|
|
156
|
-
`## Project Configuration`,
|
|
157
|
-
``,
|
|
158
|
-
`See \`tas.yaml\` for project settings and \`CLAUDE.md\` for detailed instructions.`,
|
|
159
|
-
``,
|
|
160
154
|
].join('\n');
|
|
161
155
|
|
|
162
|
-
await fs.writeFile(path.join(
|
|
156
|
+
await fs.writeFile(path.join(codexDir, 'README.md'), readme);
|
|
163
157
|
}
|
package/lib/install.js
CHANGED
|
@@ -10,6 +10,25 @@ const TAS_SRC = path.join(PACKAGE_DIR, '.tas');
|
|
|
10
10
|
const PLATFORMS_FILE = '.tas/platforms.json';
|
|
11
11
|
const require = createRequire(import.meta.url);
|
|
12
12
|
|
|
13
|
+
// ─── Logo ────────────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
function printLogo() {
|
|
16
|
+
const c = '\x1b[36m';
|
|
17
|
+
const d = '\x1b[90m';
|
|
18
|
+
const r = '\x1b[0m';
|
|
19
|
+
const art = [
|
|
20
|
+
` ████████╗ █████╗ ███████╗ ██╗ ██╗ ██╗ ████████╗`,
|
|
21
|
+
` ╚══██╔══╝ ██╔══██╗██╔════╝ ██║ ██╔╝ ██║ ╚══██╔══╝`,
|
|
22
|
+
` ██║ ███████║███████╗ █████╔╝ ██║ ██║ `,
|
|
23
|
+
` ██║ ██╔══██║╚════██║ ██╔═██╗ ██║ ██║ `,
|
|
24
|
+
` ██║ ██║ ██║███████║ ██║ ██╗ ██║ ██║ `,
|
|
25
|
+
` ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ `,
|
|
26
|
+
].map(l => c + l + r).join('\n');
|
|
27
|
+
process.stdout.write('\n' + art + '\n' + d + ' Turbo Agentic SDLC Kit' + r + '\n\n');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
13
32
|
async function getDeletedFiles() {
|
|
14
33
|
try {
|
|
15
34
|
const manifest = require('./deleted-files.json');
|
|
@@ -57,30 +76,151 @@ async function copyDir(src, dest) {
|
|
|
57
76
|
await fs.cp(src, dest, { recursive: true });
|
|
58
77
|
}
|
|
59
78
|
|
|
79
|
+
const TAS_ROOT_EXCLUDE = new Set([
|
|
80
|
+
'tas-example.yaml',
|
|
81
|
+
'project-status-example.yaml',
|
|
82
|
+
'platforms.json',
|
|
83
|
+
'agents',
|
|
84
|
+
'commands',
|
|
85
|
+
'skills',
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
async function copyTasDir(src, dest) {
|
|
89
|
+
await fs.cp(src, dest, {
|
|
90
|
+
recursive: true,
|
|
91
|
+
filter: (srcPath) => !TAS_ROOT_EXCLUDE.has(path.relative(src, srcPath)),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function chmodExecutables(target) {
|
|
96
|
+
const adoPy = path.join(target, '.tas', 'tools', 'tas-ado.py');
|
|
97
|
+
if (await exists(adoPy)) await fs.chmod(adoPy, 0o755);
|
|
98
|
+
|
|
99
|
+
const preCommit = path.join(target, '.tas', 'hooks', 'pre-commit');
|
|
100
|
+
if (await exists(preCommit)) await fs.chmod(preCommit, 0o755);
|
|
101
|
+
|
|
102
|
+
const hooksDir = path.join(target, '.tas', '_platform', 'hooks');
|
|
103
|
+
if (await exists(hooksDir)) {
|
|
104
|
+
const files = await fs.readdir(hooksDir);
|
|
105
|
+
for (const f of files) await fs.chmod(path.join(hooksDir, f), 0o755);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
60
109
|
// ─── Platform selection ───────────────────────────────────────────────────────
|
|
61
110
|
|
|
62
|
-
async function
|
|
63
|
-
if (
|
|
64
|
-
|
|
111
|
+
async function checkboxSelect(title, items, defaultIndexes = [0]) {
|
|
112
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
113
|
+
return fallbackTextSelect(title, items, defaultIndexes);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const selected = new Set(defaultIndexes);
|
|
117
|
+
let cursor = defaultIndexes[0] ?? 0;
|
|
118
|
+
let renderedLines = 0;
|
|
119
|
+
|
|
120
|
+
const clearRendered = () => {
|
|
121
|
+
if (renderedLines > 0) {
|
|
122
|
+
process.stdout.write(`\x1b[${renderedLines}A\x1b[0J`);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const render = () => {
|
|
127
|
+
clearRendered();
|
|
128
|
+
const lines = [];
|
|
129
|
+
lines.push(` ${title}`);
|
|
130
|
+
lines.push('');
|
|
131
|
+
for (let i = 0; i < items.length; i++) {
|
|
132
|
+
const mark = selected.has(i) ? '\x1b[32m◉\x1b[0m' : '○';
|
|
133
|
+
const arrow = cursor === i ? '\x1b[36m›\x1b[0m' : ' ';
|
|
134
|
+
const label = cursor === i ? `\x1b[36m${items[i].label}\x1b[0m` : items[i].label;
|
|
135
|
+
const note = items[i].note ? ` \x1b[90m${items[i].note}\x1b[0m` : '';
|
|
136
|
+
lines.push(` ${arrow} ${mark} ${label}${note}`);
|
|
137
|
+
}
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push('\x1b[90m ↑↓ navigate space select enter confirm\x1b[0m');
|
|
140
|
+
renderedLines = lines.length;
|
|
141
|
+
process.stdout.write(lines.join('\n') + '\n');
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return new Promise((resolve) => {
|
|
145
|
+
process.stdout.write('\x1b[?25l');
|
|
146
|
+
process.stdin.setRawMode(true);
|
|
147
|
+
process.stdin.resume();
|
|
148
|
+
process.stdin.setEncoding('utf8');
|
|
149
|
+
|
|
150
|
+
render();
|
|
151
|
+
|
|
152
|
+
const onKey = (key) => {
|
|
153
|
+
if (key === '\x03') {
|
|
154
|
+
finish(null);
|
|
155
|
+
process.exit(0);
|
|
156
|
+
} else if (key === '\x1b[A') {
|
|
157
|
+
cursor = (cursor - 1 + items.length) % items.length;
|
|
158
|
+
render();
|
|
159
|
+
} else if (key === '\x1b[B') {
|
|
160
|
+
cursor = (cursor + 1) % items.length;
|
|
161
|
+
render();
|
|
162
|
+
} else if (key === ' ') {
|
|
163
|
+
if (selected.has(cursor)) selected.delete(cursor);
|
|
164
|
+
else selected.add(cursor);
|
|
165
|
+
render();
|
|
166
|
+
} else if (key === '\r' || key === '\n') {
|
|
167
|
+
const ids = items.filter((_, i) => selected.has(i)).map(it => it.id);
|
|
168
|
+
finish(ids.length > 0 ? ids : [items[cursor].id]);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const finish = (result) => {
|
|
173
|
+
process.stdin.removeListener('data', onKey);
|
|
174
|
+
process.stdin.setRawMode(false);
|
|
175
|
+
process.stdin.pause();
|
|
176
|
+
process.stdout.write('\x1b[?25h');
|
|
177
|
+
clearRendered();
|
|
178
|
+
if (result) {
|
|
179
|
+
const labels = result.map(id => items.find(it => it.id === id)?.label || id).join(', ');
|
|
180
|
+
process.stdout.write(` Platforms: \x1b[36m${labels}\x1b[0m\n\n`);
|
|
181
|
+
resolve(result);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
process.stdin.on('data', onKey);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
65
188
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
189
|
+
async function fallbackTextSelect(title, items, defaultIndexes) {
|
|
190
|
+
console.log(`\n ${title}`);
|
|
191
|
+
items.forEach((item, i) => {
|
|
192
|
+
const def = defaultIndexes.includes(i) ? ' (default)' : '';
|
|
193
|
+
console.log(` [${i + 1}] ${item.label}${def}`);
|
|
70
194
|
});
|
|
71
195
|
console.log('');
|
|
72
196
|
console.log(' Enter numbers comma-separated for multiple platforms.');
|
|
73
|
-
const
|
|
197
|
+
const nums = items.map((_, i) => i + 1).join('/');
|
|
198
|
+
const answer = await ask(` Choose [${nums}] (default: 1): `, '1');
|
|
74
199
|
|
|
75
200
|
const selected = [];
|
|
76
201
|
for (const part of answer.split(',')) {
|
|
77
202
|
const idx = parseInt(part.trim(), 10) - 1;
|
|
78
|
-
if (idx >= 0 && idx <
|
|
79
|
-
const id =
|
|
203
|
+
if (idx >= 0 && idx < items.length) {
|
|
204
|
+
const id = items[idx].id;
|
|
80
205
|
if (!selected.includes(id)) selected.push(id);
|
|
81
206
|
}
|
|
82
207
|
}
|
|
83
|
-
return selected.length > 0 ? selected : [
|
|
208
|
+
return selected.length > 0 ? selected : [items[defaultIndexes[0] ?? 0].id];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function choosePlatforms({ yes, forced }) {
|
|
212
|
+
if (forced && forced.length > 0) return forced;
|
|
213
|
+
if (yes) return ['claude-code'];
|
|
214
|
+
|
|
215
|
+
return checkboxSelect(
|
|
216
|
+
'Agentic Coding Platform(s) to install:',
|
|
217
|
+
PLATFORMS.map(p => ({
|
|
218
|
+
id: p.id,
|
|
219
|
+
label: p.label,
|
|
220
|
+
note: p.id === 'claude-code' ? '(default)' : '',
|
|
221
|
+
})),
|
|
222
|
+
[0],
|
|
223
|
+
);
|
|
84
224
|
}
|
|
85
225
|
|
|
86
226
|
async function savePlatforms(target, platforms) {
|
|
@@ -240,6 +380,7 @@ async function installSecurityHook({ target, mode }) {
|
|
|
240
380
|
// ─── Update ──────────────────────────────────────────────────────────────────
|
|
241
381
|
|
|
242
382
|
export async function update({ directory, yes, securityHook, platforms: forcedPlatforms }) {
|
|
383
|
+
printLogo();
|
|
243
384
|
const target = path.resolve(directory);
|
|
244
385
|
|
|
245
386
|
const tasExists = await exists(path.join(target, '.tas'));
|
|
@@ -249,7 +390,7 @@ export async function update({ directory, yes, securityHook, platforms: forcedPl
|
|
|
249
390
|
process.exit(1);
|
|
250
391
|
}
|
|
251
392
|
|
|
252
|
-
console.log(
|
|
393
|
+
console.log(`Updating TAS Kit in: ${target}\n`);
|
|
253
394
|
|
|
254
395
|
const platforms = forcedPlatforms && forcedPlatforms.length > 0
|
|
255
396
|
? forcedPlatforms
|
|
@@ -270,7 +411,7 @@ export async function update({ directory, yes, securityHook, platforms: forcedPl
|
|
|
270
411
|
|
|
271
412
|
const tasDest = path.join(target, '.tas');
|
|
272
413
|
if (path.resolve(TAS_SRC) !== path.resolve(tasDest)) {
|
|
273
|
-
await
|
|
414
|
+
await copyTasDir(TAS_SRC, tasDest);
|
|
274
415
|
}
|
|
275
416
|
console.log(' [ok] .tas/ (updated)');
|
|
276
417
|
|
|
@@ -282,7 +423,7 @@ export async function update({ directory, yes, securityHook, platforms: forcedPl
|
|
|
282
423
|
}
|
|
283
424
|
}
|
|
284
425
|
|
|
285
|
-
await runPlatformAdapters(platforms, { tasDir:
|
|
426
|
+
await runPlatformAdapters(platforms, { tasDir: TAS_SRC, target });
|
|
286
427
|
|
|
287
428
|
await savePlatforms(target, platforms);
|
|
288
429
|
|
|
@@ -294,7 +435,8 @@ export async function update({ directory, yes, securityHook, platforms: forcedPl
|
|
|
294
435
|
await installSecurityHook({ target, mode: securityHook });
|
|
295
436
|
}
|
|
296
437
|
|
|
297
|
-
|
|
438
|
+
const configFilename = platforms.includes('claude-code') ? 'CLAUDE.md' : 'AGENTS.md';
|
|
439
|
+
console.log(` [--] ${configFilename}, tas.yaml, project-status.yaml, .env.example — not touched`);
|
|
298
440
|
console.log(`
|
|
299
441
|
TAS Kit updated successfully!
|
|
300
442
|
|
|
@@ -306,11 +448,12 @@ and manually merge changes into your CLAUDE.md and tas.yaml if needed.
|
|
|
306
448
|
// ─── Install ─────────────────────────────────────────────────────────────────
|
|
307
449
|
|
|
308
450
|
export async function install({ directory, yes, securityHook, platforms: forcedPlatforms }) {
|
|
451
|
+
printLogo();
|
|
309
452
|
const target = path.resolve(directory);
|
|
310
453
|
|
|
311
454
|
await fs.mkdir(target, { recursive: true });
|
|
312
455
|
|
|
313
|
-
console.log(
|
|
456
|
+
console.log(`Installing TAS Kit into: ${target}\n`);
|
|
314
457
|
|
|
315
458
|
const tasExists = await exists(path.join(target, '.tas'));
|
|
316
459
|
if (tasExists && !yes) {
|
|
@@ -327,7 +470,7 @@ export async function install({ directory, yes, securityHook, platforms: forcedP
|
|
|
327
470
|
// Copy .tas/ (skip if installing into the package itself)
|
|
328
471
|
const tasDest = path.join(target, '.tas');
|
|
329
472
|
if (path.resolve(TAS_SRC) !== path.resolve(tasDest)) {
|
|
330
|
-
await
|
|
473
|
+
await copyTasDir(TAS_SRC, tasDest);
|
|
331
474
|
console.log(' [ok] .tas/');
|
|
332
475
|
} else {
|
|
333
476
|
console.log(' [--] .tas/ (source = destination, skipped)');
|
|
@@ -335,16 +478,18 @@ export async function install({ directory, yes, securityHook, platforms: forcedP
|
|
|
335
478
|
|
|
336
479
|
// Platform selection
|
|
337
480
|
const platforms = await choosePlatforms({ yes, forced: forcedPlatforms });
|
|
338
|
-
await runPlatformAdapters(platforms, { tasDir:
|
|
481
|
+
await runPlatformAdapters(platforms, { tasDir: TAS_SRC, target });
|
|
339
482
|
await savePlatforms(target, platforms);
|
|
340
483
|
|
|
341
484
|
// Project config files (first-time only)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
485
|
+
// Claude Code uses CLAUDE.md; all other platforms use AGENTS.md
|
|
486
|
+
const configFilename = platforms.includes('claude-code') ? 'CLAUDE.md' : 'AGENTS.md';
|
|
487
|
+
const configTarget = path.join(target, configFilename);
|
|
488
|
+
if (!(await exists(configTarget))) {
|
|
489
|
+
await fs.copyFile(path.join(PACKAGE_DIR, '.tas', 'templates', 'AGENTS.md'), configTarget);
|
|
490
|
+
console.log(` [ok] ${configFilename} (from .tas/templates/AGENTS.md)`);
|
|
346
491
|
} else {
|
|
347
|
-
console.log(
|
|
492
|
+
console.log(` [--] ${configFilename} already exists, skipped`);
|
|
348
493
|
}
|
|
349
494
|
|
|
350
495
|
const envExampleTarget = path.join(target, '.env.example');
|
|
@@ -363,6 +508,17 @@ export async function install({ directory, yes, securityHook, platforms: forcedP
|
|
|
363
508
|
console.log(' [--] tas.yaml already exists, skipped');
|
|
364
509
|
}
|
|
365
510
|
|
|
511
|
+
const psYamlTarget = path.join(target, 'project-status.yaml');
|
|
512
|
+
if (!(await exists(psYamlTarget))) {
|
|
513
|
+
const psSrc = path.join(PACKAGE_DIR, '.tas', 'project-status-example.yaml');
|
|
514
|
+
if (await exists(psSrc)) {
|
|
515
|
+
await fs.copyFile(psSrc, psYamlTarget);
|
|
516
|
+
console.log(' [ok] project-status.yaml (from .tas/project-status-example.yaml)');
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
console.log(' [--] project-status.yaml already exists, skipped');
|
|
520
|
+
}
|
|
521
|
+
|
|
366
522
|
if (process.platform !== 'win32') {
|
|
367
523
|
await chmodExecutables(target);
|
|
368
524
|
}
|
|
@@ -375,7 +531,7 @@ export async function install({ directory, yes, securityHook, platforms: forcedP
|
|
|
375
531
|
TAS Kit installed successfully! (Platforms: ${platformLabels})
|
|
376
532
|
|
|
377
533
|
Next steps:
|
|
378
|
-
1. Edit
|
|
534
|
+
1. Edit ${configFilename.padEnd(10)} — add your project's tech stack and conventions
|
|
379
535
|
2. Edit tas.yaml — set project name, team and ADO config
|
|
380
536
|
3. Create .env — add AZURE_DEVOPS_PAT (see .env.example)
|
|
381
537
|
4. Open your IDE — run /tas-init to initialize your project
|
|
@@ -385,19 +541,3 @@ Docs:
|
|
|
385
541
|
.tas/hooks/README.md — pre-commit security hook details
|
|
386
542
|
`);
|
|
387
543
|
}
|
|
388
|
-
|
|
389
|
-
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
390
|
-
|
|
391
|
-
async function chmodExecutables(target) {
|
|
392
|
-
const adoPy = path.join(target, '.tas', 'tools', 'tas-ado.py');
|
|
393
|
-
if (await exists(adoPy)) await fs.chmod(adoPy, 0o755);
|
|
394
|
-
|
|
395
|
-
const preCommit = path.join(target, '.tas', 'hooks', 'pre-commit');
|
|
396
|
-
if (await exists(preCommit)) await fs.chmod(preCommit, 0o755);
|
|
397
|
-
|
|
398
|
-
const hooksDir = path.join(target, '.tas', '_platform', 'hooks');
|
|
399
|
-
if (await exists(hooksDir)) {
|
|
400
|
-
const files = await fs.readdir(hooksDir);
|
|
401
|
-
for (const f of files) await fs.chmod(path.join(hooksDir, f), 0o755);
|
|
402
|
-
}
|
|
403
|
-
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@torus-engineering/tas-kit",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.13.0",
|
|
4
|
+
"description": "Turbo AI-first toolkit for modern agentic SDLC teams.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"tas-kit": "bin/cli.js"
|