@torus-engineering/tas-kit 1.6.0 → 1.8.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.
Files changed (71) hide show
  1. package/.claude/commands/tas-adr.md +33 -29
  2. package/.claude/commands/tas-api-test.md +95 -0
  3. package/.claude/commands/tas-bug.md +113 -109
  4. package/.claude/commands/tas-design.md +37 -33
  5. package/.claude/commands/tas-dev.md +128 -115
  6. package/.claude/commands/tas-e2e-mobile.md +155 -0
  7. package/.claude/commands/tas-e2e-web.md +163 -0
  8. package/.claude/commands/tas-e2e.md +102 -0
  9. package/.claude/commands/tas-epic.md +35 -31
  10. package/.claude/commands/tas-feature.md +47 -43
  11. package/.claude/commands/tas-fix.md +51 -47
  12. package/.claude/commands/tas-functest-mobile.md +144 -0
  13. package/.claude/commands/tas-functest-web.md +192 -0
  14. package/.claude/commands/tas-functest.md +76 -0
  15. package/.claude/commands/tas-plan.md +200 -184
  16. package/.claude/commands/tas-prd.md +37 -33
  17. package/.claude/commands/tas-review.md +111 -104
  18. package/.claude/commands/tas-sad.md +43 -39
  19. package/.claude/commands/tas-security.md +81 -80
  20. package/.claude/commands/tas-story.md +91 -87
  21. package/.claude/commands/tas-verify.md +51 -41
  22. package/.claude/rules/common/post-review-agent.md +49 -39
  23. package/.claude/rules/common/testing.md +24 -0
  24. package/.claude/rules/common/token-logging.md +27 -0
  25. package/.claude/rules/csharp/api-testing.md +171 -0
  26. package/.claude/rules/csharp/patterns.md +10 -0
  27. package/.claude/rules/python/patterns.md +10 -0
  28. package/.claude/rules/typescript/patterns.md +10 -0
  29. package/.claude/rules/web/performance.md +9 -0
  30. package/.claude/skills/api-design/SKILL.md +3 -1
  31. package/.claude/skills/{backend-patterns → js-backend-patterns}/SKILL.md +2 -1
  32. package/.claude/skills/tas-implementation-complete/SKILL.md +99 -97
  33. package/.claude/skills/tas-tdd/SKILL.md +123 -82
  34. package/.claude/skills/token-logger/SKILL.md +19 -0
  35. package/.tas/templates/E2E-Execution-Report.md +198 -0
  36. package/.tas/templates/E2E-Mobile-Spec.md +130 -0
  37. package/.tas/templates/E2E-Report.md +174 -0
  38. package/.tas/templates/E2E-Scenario.md +180 -0
  39. package/.tas/templates/E2E-Web-Spec.md +164 -0
  40. package/.tas/templates/Feature.md +55 -55
  41. package/.tas/templates/Func-Test-Script.md +254 -0
  42. package/.tas/templates/Func-Test-Spec.md +187 -0
  43. package/.tas/templates/SAD.md +274 -64
  44. package/.tas/templates/Story.md +90 -88
  45. package/bin/cli.js +56 -49
  46. package/lib/deleted-files.json +33 -0
  47. package/lib/install.js +213 -114
  48. package/package.json +34 -34
  49. package/.claude/agents/README.md +0 -83
  50. package/.claude/agents/ado-agent.md +0 -39
  51. package/.claude/agents/code-architect.md +0 -62
  52. package/.claude/agents/code-simplifier.md +0 -53
  53. package/.claude/agents/comment-analyzer.md +0 -59
  54. package/.claude/agents/conversation-analyzer.md +0 -57
  55. package/.claude/agents/docs-lookup.md +0 -55
  56. package/.claude/agents/harness-optimizer.md +0 -62
  57. package/.claude/agents/loop-operator.md +0 -56
  58. package/.claude/agents/performance-optimizer.md +0 -78
  59. package/.claude/agents/pr-test-analyzer.md +0 -68
  60. package/.claude/agents/pytorch-build-resolver.md +0 -76
  61. package/.claude/agents/refactor-cleaner.md +0 -70
  62. package/.claude/agents/seo-specialist.md +0 -75
  63. package/.claude/agents/silent-failure-hunter.md +0 -69
  64. package/.claude/agents/type-design-analyzer.md +0 -75
  65. package/.claude/rules/common/agents.md +0 -65
  66. package/.claude/rules/common/coding-style.md +0 -90
  67. package/.claude/rules/common/development-workflow.md +0 -44
  68. package/.claude/rules/common/git-workflow.md +0 -24
  69. package/.claude/rules/common/performance.md +0 -55
  70. package/.claude/skills/agent-harness-construction/SKILL.md +0 -77
  71. package/.claude/skills/agent-introspection-debugging/SKILL.md +0 -157
@@ -1,88 +1,90 @@
1
- ---
2
- ado_id:
3
- ado_type: User Story
4
- ado_title: "{Title}"
5
- ado_state: New
6
- ado_assigned_to:
7
- ado_created:
8
- last_ado_sync:
9
- parent_ado_id:
10
- plan_status: pending
11
- plan_date:
12
- ---
13
- # Story-{NNN}: {Title}
14
-
15
- > **Status:** New | Committed | In Progress | Deploy Test | Verify Test | Deploy Stag | Verify Stag | Deploy Prod | Verify Prod | Done
16
- > **Feature:** Feature-{NNN}
17
- > **Assigned to:** {SE name}
18
- > **Estimate:** {S/M/L}
19
- > **Created:** {Date}
20
-
21
- ## User Story
22
- As a {role}, I want {goal}, so that {benefit}.
23
-
24
- ---
25
- ## Business Requirements (nếu có)
26
- *(Yêu cầu nghiệp vụ đặc biệt, business rules, constraints từ stakeholders — chỉ thêm khi cần)*
27
-
28
- ---
29
- ## Design Notes (nếu có)
30
- *(UI/UX specs, mockup links, design decisions, flow diagrams — chỉ thêm khi cần)*
31
-
32
- ---
33
- ## Prerequisites (nếu có)
34
- *(Dependencies từ Stories khác hoặc điều kiện phải có trước — chỉ thêm khi cần)*
35
-
36
- ---
37
- ## Acceptance Criteria
38
-
39
- ### AC-1: {title}
40
- - **Given** {precondition}
41
- - **When** {action}
42
- - **Then** {expected result}
43
-
44
- ### AC-2: {title}
45
- - **Given** {precondition}
46
- - **When** {action}
47
- - **Then** {expected result}
48
-
49
- ---
50
- ## Unit Test Cases
51
- *(PE thiết kế happy path + edge cases + negative cases. SE implement trong code.)*
52
-
53
- ### Happy Path
54
- | ID | Description | Input | Expected Output |
55
- |----|-------------|-------|-----------------|
56
- | UT-1 | {description} | {input} | {expected} |
57
-
58
- ### Edge Cases
59
- | ID | Description | Input | Expected Output |
60
- |----|-------------|-------|-----------------|
61
- | UT-E1 | {description} | {input} | {expected} |
62
-
63
- ### Negative Cases
64
- | ID | Description | Input | Expected Output |
65
- |----|-------------|-------|-----------------|
66
- | UT-N1 | {description} | {input} | {expected} |
67
-
68
- ### Dependencies to Mock
69
- - {external API, database state, third-party service...}
70
-
71
- ---
72
- <!-- ═══════════════════════════════════════════════════════════════════
73
- TECHNICAL PLAN — generated by /tas-plan, do not edit manually
74
- ═══════════════════════════════════════════════════════════════════ -->
75
-
76
- ---
77
- ## Definition of Done
78
- - [ ] Technical plan completed (`/tas-plan`)
79
- - [ ] Code implemented
80
- - [ ] Unit tests pass
81
- - [ ] Code review passed
82
- - [ ] Acceptance criteria verified
83
- - [ ] No regression
84
- - [ ] Documentation updated (if needed)
85
-
86
- ## Changelog
87
- | Date | Changes | Author |
88
- |------|---------|--------|
1
+ ---
2
+ ado_id:
3
+ ado_type: User Story
4
+ ado_title: "{Title}"
5
+ ado_state: New
6
+ ado_assigned_to:
7
+ ado_created:
8
+ last_ado_sync:
9
+ parent_ado_id:
10
+ plan_status: pending
11
+ plan_date:
12
+ ---
13
+ # Story-{NNN}: {Title}
14
+
15
+ > **Status:** New | Committed | In Progress | Deploy Test | Verify Test | Deploy Stag | Verify Stag | Deploy Prod | Verify Prod | Done
16
+ > **Feature:** Feature-{NNN}
17
+ > **Assigned to:** {SE name}
18
+ > **Estimate:** {S/M/L}
19
+ > **Created:** {Date}
20
+
21
+ ## User Story
22
+ As a {role}, I want {goal}, so that {benefit}.
23
+
24
+ ---
25
+ ## Business Requirements (nếu có)
26
+ *(Yêu cầu nghiệp vụ đặc biệt, business rules, constraints từ stakeholders — chỉ thêm khi cần)*
27
+
28
+ ---
29
+ ## Design Notes (nếu có)
30
+ *(UI/UX specs, mockup links, design decisions, flow diagrams — chỉ thêm khi cần)*
31
+
32
+ ---
33
+ ## Prerequisites (nếu có)
34
+ *(Dependencies từ Stories khác hoặc điều kiện phải có trước — chỉ thêm khi cần)*
35
+
36
+ ---
37
+ ## Acceptance Criteria
38
+
39
+ ### AC-1: {title}
40
+ - **Given** {precondition}
41
+ - **When** {action}
42
+ - **Then** {expected result}
43
+ - **Functional Tests**: *(điền FT IDs sau khi chạy `/tas-functest` — vd: `{PROJECT}_E{EPIC}_F{FEATURE}_S{STORY}_FT_001_H`, `FT_002_N`)*
44
+
45
+ ### AC-2: {title}
46
+ - **Given** {precondition}
47
+ - **When** {action}
48
+ - **Then** {expected result}
49
+ - **Functional Tests**: *(điền FT IDs sau khi chạy `/tas-functest`)*
50
+
51
+ ---
52
+ ## Unit Test Cases
53
+ *(PE thiết kế happy path + edge cases + negative cases. SE implement trong code.)*
54
+
55
+ ### Happy Path
56
+ | ID | AC Ref | Description | Input | Expected Output |
57
+ |----|--------|-------------|-------|-----------------|
58
+ | UT-1 | AC-1 | {description} | {input} | {expected} |
59
+
60
+ ### Edge Cases
61
+ | ID | AC Ref | Description | Input | Expected Output |
62
+ |----|--------|-------------|-------|-----------------|
63
+ | UT-E1 | AC-1 | {description} | {input} | {expected} |
64
+
65
+ ### Negative Cases
66
+ | ID | AC Ref | Description | Input | Expected Output |
67
+ |----|--------|-------------|-------|-----------------|
68
+ | UT-N1 | AC-1 | {description} | {input} | {expected} |
69
+
70
+ ### Dependencies to Mock
71
+ - {external API, database state, third-party service...}
72
+
73
+ ---
74
+ <!-- ═══════════════════════════════════════════════════════════════════
75
+ TECHNICAL PLAN — generated by /tas-plan, do not edit manually
76
+ ═══════════════════════════════════════════════════════════════════ -->
77
+
78
+ ---
79
+ ## Definition of Done
80
+ - [ ] Technical plan completed (`/tas-plan`)
81
+ - [ ] Code implemented
82
+ - [ ] Unit tests pass
83
+ - [ ] Code review passed
84
+ - [ ] Acceptance criteria verified
85
+ - [ ] No regression
86
+ - [ ] Documentation updated (if needed)
87
+
88
+ ## Changelog
89
+ | Date | Changes | Author |
90
+ |------|---------|--------|
package/bin/cli.js CHANGED
@@ -1,49 +1,56 @@
1
- #!/usr/bin/env node
2
- import { install } 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 — Torus Agentic SDLC Kit installer
10
-
11
- Usage:
12
- npx @torus-engineering/tas-kit install [options]
13
-
14
- Options:
15
- --directory <path> Target directory (default: current working directory)
16
- --yes, -y Skip confirmation prompts
17
- --help, -h Show this help message
18
-
19
- Examples:
20
- npx @torus-engineering/tas-kit install
21
- npx @torus-engineering/tas-kit install --directory /path/to/my-project
22
- npx @torus-engineering/tas-kit install --yes
23
- `.trim());
24
- }
25
-
26
- if (!command || command === '--help' || command === '-h') {
27
- printHelp();
28
- process.exit(0);
29
- }
30
-
31
- if (command !== 'install') {
32
- console.error(`Unknown command: "${command}"\n`);
33
- printHelp();
34
- process.exit(1);
35
- }
36
-
37
- const opts = { directory: process.cwd(), yes: false };
38
- for (let i = 1; i < args.length; i++) {
39
- if ((args[i] === '--directory' || args[i] === '-d') && args[i + 1]) {
40
- opts.directory = args[++i];
41
- } else if (args[i] === '--yes' || args[i] === '-y') {
42
- opts.yes = true;
43
- }
44
- }
45
-
46
- install(opts).catch((err) => {
47
- console.error('\nInstall failed:', err.message);
48
- process.exit(1);
49
- });
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 — Torus 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 .claude/ and .tas/ 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
+ --help, -h Show this help message
23
+
24
+ Examples:
25
+ npx @torus-engineering/tas-kit install
26
+ npx @torus-engineering/tas-kit install --directory /path/to/my-project
27
+ npx @torus-engineering/tas-kit update
28
+ npx @torus-engineering/tas-kit update --yes
29
+ `.trim());
30
+ }
31
+
32
+ if (!command || command === '--help' || command === '-h') {
33
+ printHelp();
34
+ process.exit(0);
35
+ }
36
+
37
+ if (command !== 'install' && command !== 'update') {
38
+ console.error(`Unknown command: "${command}"\n`);
39
+ printHelp();
40
+ process.exit(1);
41
+ }
42
+
43
+ const opts = { directory: process.cwd(), yes: false };
44
+ for (let i = 1; i < args.length; i++) {
45
+ if ((args[i] === '--directory' || args[i] === '-d') && args[i + 1]) {
46
+ opts.directory = args[++i];
47
+ } else if (args[i] === '--yes' || args[i] === '-y') {
48
+ opts.yes = true;
49
+ }
50
+ }
51
+
52
+ const runner = command === 'update' ? update : install;
53
+ runner(opts).catch((err) => {
54
+ console.error(`\n${command === 'update' ? 'Update' : 'Install'} failed:`, err.message);
55
+ process.exit(1);
56
+ });
@@ -0,0 +1,33 @@
1
+ {
2
+ "1.6.0": [
3
+ ".claude/commands/tas-dev-story.md",
4
+ ".claude/commands/tas-review-code.md",
5
+ ".claude/commands/tas-security-check.md"
6
+ ],
7
+ "1.8.0": [
8
+ ".claude/agents/README.md",
9
+ ".claude/agents/ado-agent.md",
10
+ ".claude/agents/code-architect.md",
11
+ ".claude/agents/code-simplifier.md",
12
+ ".claude/agents/comment-analyzer.md",
13
+ ".claude/agents/conversation-analyzer.md",
14
+ ".claude/agents/docs-lookup.md",
15
+ ".claude/agents/harness-optimizer.md",
16
+ ".claude/agents/loop-operator.md",
17
+ ".claude/agents/performance-optimizer.md",
18
+ ".claude/agents/pr-test-analyzer.md",
19
+ ".claude/agents/pytorch-build-resolver.md",
20
+ ".claude/agents/refactor-cleaner.md",
21
+ ".claude/agents/seo-specialist.md",
22
+ ".claude/agents/silent-failure-hunter.md",
23
+ ".claude/agents/type-design-analyzer.md",
24
+ ".claude/rules/common/agents.md",
25
+ ".claude/rules/common/coding-style.md",
26
+ ".claude/rules/common/development-workflow.md",
27
+ ".claude/rules/common/git-workflow.md",
28
+ ".claude/rules/common/performance.md",
29
+ ".claude/skills/agent-harness-construction/SKILL.md",
30
+ ".claude/skills/agent-introspection-debugging/SKILL.md",
31
+ ".claude/skills/backend-patterns/SKILL.md"
32
+ ]
33
+ }
package/lib/install.js CHANGED
@@ -1,114 +1,213 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import readline from 'node:readline';
4
- import { fileURLToPath } from 'node:url';
5
-
6
- const PACKAGE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
7
-
8
- async function confirm(question) {
9
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
10
- return new Promise((resolve) => {
11
- rl.question(`${question} [y/N] `, (answer) => {
12
- rl.close();
13
- resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
14
- });
15
- });
16
- }
17
-
18
- async function exists(p) {
19
- return fs.access(p).then(() => true).catch(() => false);
20
- }
21
-
22
- async function copyDir(src, dest) {
23
- await fs.cp(src, dest, { recursive: true });
24
- }
25
-
26
- export async function install({ directory, yes }) {
27
- const target = path.resolve(directory);
28
-
29
- // Ensure target directory exists
30
- await fs.mkdir(target, { recursive: true });
31
-
32
- console.log(`\nInstalling TAS Kit into: ${target}\n`);
33
-
34
- // Warn if .claude/ or .tas/ already exist
35
- const claudeExists = await exists(path.join(target, '.claude'));
36
- const tasExists = await exists(path.join(target, '.tas'));
37
-
38
- if ((claudeExists || tasExists) && !yes) {
39
- const existing = [claudeExists && '.claude/', tasExists && '.tas/'].filter(Boolean).join(', ');
40
- console.warn(` WARNING: ${existing} already exist in target directory.`);
41
- console.warn(` Existing files with the same names will be overwritten.\n`);
42
- const ok = await confirm('Continue?');
43
- if (!ok) {
44
- console.log('Installation cancelled.');
45
- process.exit(0);
46
- }
47
- console.log();
48
- }
49
-
50
- // Copy .claude/
51
- await copyDir(
52
- path.join(PACKAGE_DIR, '.claude'),
53
- path.join(target, '.claude')
54
- );
55
- console.log(' [ok] .claude/');
56
-
57
- // Copy .tas/
58
- await copyDir(
59
- path.join(PACKAGE_DIR, '.tas'),
60
- path.join(target, '.tas')
61
- );
62
- console.log(' [ok] .tas/');
63
-
64
- // Copy CLAUDE-Example.md as CLAUDE.md (only if absent)
65
- const claudeMdTarget = path.join(target, 'CLAUDE.md');
66
- if (!(await exists(claudeMdTarget))) {
67
- await fs.copyFile(
68
- path.join(PACKAGE_DIR, 'CLAUDE-Example.md'),
69
- claudeMdTarget
70
- );
71
- console.log(' [ok] CLAUDE.md (from CLAUDE-Example.md)');
72
- } else {
73
- console.log(' [--] CLAUDE.md already exists, skipped');
74
- }
75
-
76
- // Copy .env.example
77
- await fs.copyFile(
78
- path.join(PACKAGE_DIR, '.env.example'),
79
- path.join(target, '.env.example')
80
- );
81
- console.log(' [ok] .env.example');
82
-
83
- // Copy tas-example.yaml as tas.yaml (only if absent)
84
- const tasYamlTarget = path.join(target, 'tas.yaml');
85
- if (!(await exists(tasYamlTarget))) {
86
- await fs.copyFile(
87
- path.join(PACKAGE_DIR, '.tas', 'tas-example.yaml'),
88
- tasYamlTarget
89
- );
90
- console.log(' [ok] tas.yaml (from .tas/tas-example.yaml)');
91
- } else {
92
- console.log(' [--] tas.yaml already exists, skipped');
93
- }
94
-
95
- // Set executable bit on tas-ado.py (Unix/macOS)
96
- if (process.platform !== 'win32') {
97
- const adoPy = path.join(target, '.tas', 'tools', 'tas-ado.py');
98
- if (await exists(adoPy)) {
99
- await fs.chmod(adoPy, 0o755);
100
- }
101
- }
102
-
103
- console.log(`
104
- TAS Kit installed successfully!
105
-
106
- Next steps:
107
- 1. Edit CLAUDE.md — add your project's tech stack and conventions
108
- 2. Edit tas.yaml — set project name, team and ADO config
109
- 3. Create .env — add AZURE_DEVOPS_PAT (see .env.example)
110
- 4. Open Claude Code — run /tas-init to initialize your project
111
-
112
- Docs: .tas/README.md
113
- `);
114
- }
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import readline from 'node:readline';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { createRequire } from 'node:module';
6
+
7
+ const PACKAGE_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
8
+ const require = createRequire(import.meta.url);
9
+
10
+ async function getDeletedFiles() {
11
+ try {
12
+ const manifest = require('./deleted-files.json');
13
+ return Object.values(manifest).flat();
14
+ } catch {
15
+ return [];
16
+ }
17
+ }
18
+
19
+ async function removeDeletedFiles(target, deletedFiles) {
20
+ const removed = [];
21
+ const skipped = [];
22
+ for (const relPath of deletedFiles) {
23
+ const fullPath = path.join(target, relPath);
24
+ try {
25
+ await fs.rm(fullPath, { force: false });
26
+ removed.push(relPath);
27
+ } catch {
28
+ skipped.push(relPath);
29
+ }
30
+ }
31
+ return { removed, skipped };
32
+ }
33
+
34
+ async function confirm(question) {
35
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
36
+ return new Promise((resolve) => {
37
+ rl.question(`${question} [y/N] `, (answer) => {
38
+ rl.close();
39
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
40
+ });
41
+ });
42
+ }
43
+
44
+ async function exists(p) {
45
+ return fs.access(p).then(() => true).catch(() => false);
46
+ }
47
+
48
+ async function copyDir(src, dest) {
49
+ await fs.cp(src, dest, { recursive: true });
50
+ }
51
+
52
+ export async function update({ directory, yes }) {
53
+ const target = path.resolve(directory);
54
+
55
+ // Must already have .claude/ or .tas/ — otherwise suggest install
56
+ const claudeExists = await exists(path.join(target, '.claude'));
57
+ const tasExists = await exists(path.join(target, '.tas'));
58
+ if (!claudeExists && !tasExists) {
59
+ console.error(` ERROR: No TAS Kit found in: ${target}`);
60
+ console.error(` Run "install" first to set up TAS Kit.`);
61
+ process.exit(1);
62
+ }
63
+
64
+ console.log(`\nUpdating TAS Kit in: ${target}\n`);
65
+
66
+ if (!yes) {
67
+ console.warn(` This will overwrite .claude/ and .tas/ with the latest kit files.`);
68
+ console.warn(` Your CLAUDE.md, tas.yaml, and .env.example will NOT be touched.\n`);
69
+ const ok = await confirm('Continue?');
70
+ if (!ok) {
71
+ console.log('Update cancelled.');
72
+ process.exit(0);
73
+ }
74
+ console.log();
75
+ }
76
+
77
+ // Overwrite .claude/
78
+ await copyDir(
79
+ path.join(PACKAGE_DIR, '.claude'),
80
+ path.join(target, '.claude')
81
+ );
82
+ console.log(' [ok] .claude/ (updated)');
83
+
84
+ // Overwrite .tas/
85
+ await copyDir(
86
+ path.join(PACKAGE_DIR, '.tas'),
87
+ path.join(target, '.tas')
88
+ );
89
+ console.log(' [ok] .tas/ (updated)');
90
+
91
+ // Remove files deleted from the kit in previous versions
92
+ const deletedFiles = await getDeletedFiles();
93
+ if (deletedFiles.length > 0) {
94
+ const { removed } = await removeDeletedFiles(target, deletedFiles);
95
+ if (removed.length > 0) {
96
+ for (const f of removed) {
97
+ console.log(` [rm] ${f} (removed in this version)`);
98
+ }
99
+ }
100
+ }
101
+
102
+ // Set executable bit on tas-ado.py (Unix/macOS)
103
+ if (process.platform !== 'win32') {
104
+ const adoPy = path.join(target, '.tas', 'tools', 'tas-ado.py');
105
+ if (await exists(adoPy)) {
106
+ await fs.chmod(adoPy, 0o755);
107
+ }
108
+ }
109
+
110
+ console.log(` [--] CLAUDE.md, tas.yaml, .env.example not touched`);
111
+
112
+ console.log(`
113
+ TAS Kit updated successfully!
114
+
115
+ If this version added new settings or templates, check the changelog
116
+ and manually merge changes into your CLAUDE.md and tas.yaml if needed.
117
+ `);
118
+ }
119
+
120
+ export async function install({ directory, yes }) {
121
+ const target = path.resolve(directory);
122
+
123
+ // Ensure target directory exists
124
+ await fs.mkdir(target, { recursive: true });
125
+
126
+ console.log(`\nInstalling TAS Kit into: ${target}\n`);
127
+
128
+ // Warn if .claude/ or .tas/ already exist
129
+ const claudeExists = await exists(path.join(target, '.claude'));
130
+ const tasExists = await exists(path.join(target, '.tas'));
131
+
132
+ if ((claudeExists || tasExists) && !yes) {
133
+ const existing = [claudeExists && '.claude/', tasExists && '.tas/'].filter(Boolean).join(', ');
134
+ console.warn(` WARNING: ${existing} already exist in target directory.`);
135
+ console.warn(` Existing files with the same names will be overwritten.\n`);
136
+ const ok = await confirm('Continue?');
137
+ if (!ok) {
138
+ console.log('Installation cancelled.');
139
+ process.exit(0);
140
+ }
141
+ console.log();
142
+ }
143
+
144
+ // Copy .claude/
145
+ await copyDir(
146
+ path.join(PACKAGE_DIR, '.claude'),
147
+ path.join(target, '.claude')
148
+ );
149
+ console.log(' [ok] .claude/');
150
+
151
+ // Copy .tas/
152
+ await copyDir(
153
+ path.join(PACKAGE_DIR, '.tas'),
154
+ path.join(target, '.tas')
155
+ );
156
+ console.log(' [ok] .tas/');
157
+
158
+ // Copy CLAUDE-Example.md as CLAUDE.md (only if absent)
159
+ const claudeMdTarget = path.join(target, 'CLAUDE.md');
160
+ if (!(await exists(claudeMdTarget))) {
161
+ await fs.copyFile(
162
+ path.join(PACKAGE_DIR, 'CLAUDE-Example.md'),
163
+ claudeMdTarget
164
+ );
165
+ console.log(' [ok] CLAUDE.md (from CLAUDE-Example.md)');
166
+ } else {
167
+ console.log(' [--] CLAUDE.md already exists, skipped');
168
+ }
169
+
170
+ // Copy .env.example (only if absent)
171
+ const envExampleTarget = path.join(target, '.env.example');
172
+ if (!(await exists(envExampleTarget))) {
173
+ await fs.copyFile(
174
+ path.join(PACKAGE_DIR, '.env.example'),
175
+ envExampleTarget
176
+ );
177
+ console.log(' [ok] .env.example');
178
+ } else {
179
+ console.log(' [--] .env.example already exists, skipped');
180
+ }
181
+
182
+ // Copy tas-example.yaml as tas.yaml (only if absent)
183
+ const tasYamlTarget = path.join(target, 'tas.yaml');
184
+ if (!(await exists(tasYamlTarget))) {
185
+ await fs.copyFile(
186
+ path.join(PACKAGE_DIR, '.tas', 'tas-example.yaml'),
187
+ tasYamlTarget
188
+ );
189
+ console.log(' [ok] tas.yaml (from .tas/tas-example.yaml)');
190
+ } else {
191
+ console.log(' [--] tas.yaml already exists, skipped');
192
+ }
193
+
194
+ // Set executable bit on tas-ado.py (Unix/macOS)
195
+ if (process.platform !== 'win32') {
196
+ const adoPy = path.join(target, '.tas', 'tools', 'tas-ado.py');
197
+ if (await exists(adoPy)) {
198
+ await fs.chmod(adoPy, 0o755);
199
+ }
200
+ }
201
+
202
+ console.log(`
203
+ TAS Kit installed successfully!
204
+
205
+ Next steps:
206
+ 1. Edit CLAUDE.md — add your project's tech stack and conventions
207
+ 2. Edit tas.yaml — set project name, team and ADO config
208
+ 3. Create .env — add AZURE_DEVOPS_PAT (see .env.example)
209
+ 4. Open Claude Code — run /tas-init to initialize your project
210
+
211
+ Docs: .tas/README.md
212
+ `);
213
+ }