@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.
- package/.claude/commands/tas-adr.md +33 -29
- package/.claude/commands/tas-api-test.md +95 -0
- package/.claude/commands/tas-bug.md +113 -109
- package/.claude/commands/tas-design.md +37 -33
- package/.claude/commands/tas-dev.md +128 -115
- package/.claude/commands/tas-e2e-mobile.md +155 -0
- package/.claude/commands/tas-e2e-web.md +163 -0
- package/.claude/commands/tas-e2e.md +102 -0
- package/.claude/commands/tas-epic.md +35 -31
- package/.claude/commands/tas-feature.md +47 -43
- package/.claude/commands/tas-fix.md +51 -47
- package/.claude/commands/tas-functest-mobile.md +144 -0
- package/.claude/commands/tas-functest-web.md +192 -0
- package/.claude/commands/tas-functest.md +76 -0
- package/.claude/commands/tas-plan.md +200 -184
- package/.claude/commands/tas-prd.md +37 -33
- package/.claude/commands/tas-review.md +111 -104
- package/.claude/commands/tas-sad.md +43 -39
- package/.claude/commands/tas-security.md +81 -80
- package/.claude/commands/tas-story.md +91 -87
- package/.claude/commands/tas-verify.md +51 -41
- package/.claude/rules/common/post-review-agent.md +49 -39
- package/.claude/rules/common/testing.md +24 -0
- package/.claude/rules/common/token-logging.md +27 -0
- package/.claude/rules/csharp/api-testing.md +171 -0
- package/.claude/rules/csharp/patterns.md +10 -0
- package/.claude/rules/python/patterns.md +10 -0
- package/.claude/rules/typescript/patterns.md +10 -0
- package/.claude/rules/web/performance.md +9 -0
- package/.claude/skills/api-design/SKILL.md +3 -1
- package/.claude/skills/{backend-patterns → js-backend-patterns}/SKILL.md +2 -1
- package/.claude/skills/tas-implementation-complete/SKILL.md +99 -97
- package/.claude/skills/tas-tdd/SKILL.md +123 -82
- package/.claude/skills/token-logger/SKILL.md +19 -0
- package/.tas/templates/E2E-Execution-Report.md +198 -0
- package/.tas/templates/E2E-Mobile-Spec.md +130 -0
- package/.tas/templates/E2E-Report.md +174 -0
- package/.tas/templates/E2E-Scenario.md +180 -0
- package/.tas/templates/E2E-Web-Spec.md +164 -0
- package/.tas/templates/Feature.md +55 -55
- package/.tas/templates/Func-Test-Script.md +254 -0
- package/.tas/templates/Func-Test-Spec.md +187 -0
- package/.tas/templates/SAD.md +274 -64
- package/.tas/templates/Story.md +90 -88
- package/bin/cli.js +56 -49
- package/lib/deleted-files.json +33 -0
- package/lib/install.js +213 -114
- package/package.json +34 -34
- package/.claude/agents/README.md +0 -83
- package/.claude/agents/ado-agent.md +0 -39
- package/.claude/agents/code-architect.md +0 -62
- package/.claude/agents/code-simplifier.md +0 -53
- package/.claude/agents/comment-analyzer.md +0 -59
- package/.claude/agents/conversation-analyzer.md +0 -57
- package/.claude/agents/docs-lookup.md +0 -55
- package/.claude/agents/harness-optimizer.md +0 -62
- package/.claude/agents/loop-operator.md +0 -56
- package/.claude/agents/performance-optimizer.md +0 -78
- package/.claude/agents/pr-test-analyzer.md +0 -68
- package/.claude/agents/pytorch-build-resolver.md +0 -76
- package/.claude/agents/refactor-cleaner.md +0 -70
- package/.claude/agents/seo-specialist.md +0 -75
- package/.claude/agents/silent-failure-hunter.md +0 -69
- package/.claude/agents/type-design-analyzer.md +0 -75
- package/.claude/rules/common/agents.md +0 -65
- package/.claude/rules/common/coding-style.md +0 -90
- package/.claude/rules/common/development-workflow.md +0 -44
- package/.claude/rules/common/git-workflow.md +0 -24
- package/.claude/rules/common/performance.md +0 -55
- package/.claude/skills/agent-harness-construction/SKILL.md +0 -77
- package/.claude/skills/agent-introspection-debugging/SKILL.md +0 -157
package/.tas/templates/Story.md
CHANGED
|
@@ -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
|
-
|
|
45
|
-
-
|
|
46
|
-
- **
|
|
47
|
-
- **
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- [ ]
|
|
81
|
-
- [ ] Code
|
|
82
|
-
- [ ]
|
|
83
|
-
- [ ]
|
|
84
|
-
- [ ]
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
printHelp();
|
|
34
|
-
process.exit(
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (!
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
path.join(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
+
}
|