specweave 0.28.0 ā 0.28.1
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/README.md +64 -72
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +6 -2
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +28 -8
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync-cli.d.ts +21 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync-cli.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync-cli.js +166 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync-cli.js.map +1 -0
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +5 -11
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/sync/label-detector.d.ts.map +1 -1
- package/dist/src/core/sync/label-detector.js +22 -9
- package/dist/src/core/sync/label-detector.js.map +1 -1
- package/dist/src/metrics/calculators/deployment-frequency.d.ts +12 -8
- package/dist/src/metrics/calculators/deployment-frequency.d.ts.map +1 -1
- package/dist/src/metrics/calculators/deployment-frequency.js +16 -12
- package/dist/src/metrics/calculators/deployment-frequency.js.map +1 -1
- package/dist/src/metrics/dora-calculator.d.ts +2 -1
- package/dist/src/metrics/dora-calculator.d.ts.map +1 -1
- package/dist/src/metrics/dora-calculator.js +9 -4
- package/dist/src/metrics/dora-calculator.js.map +1 -1
- package/dist/src/metrics/github-client.d.ts +12 -0
- package/dist/src/metrics/github-client.d.ts.map +1 -1
- package/dist/src/metrics/github-client.js +30 -0
- package/dist/src/metrics/github-client.js.map +1 -1
- package/dist/src/sync/sync-coordinator.d.ts +33 -0
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +203 -2
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/dist/src/utils/env-file-generator.d.ts +8 -23
- package/dist/src/utils/env-file-generator.d.ts.map +1 -1
- package/dist/src/utils/env-file-generator.js +31 -71
- package/dist/src/utils/env-file-generator.js.map +1 -1
- package/package.json +7 -2
- package/plugins/specweave/agents/architect/AGENT.md +2 -2
- package/plugins/specweave/agents/docs-writer/AGENT.md +2 -2
- package/plugins/specweave/agents/pm/AGENT.md +2 -2
- package/plugins/specweave/agents/qa-lead/AGENT.md +2 -2
- package/plugins/specweave/agents/security/AGENT.md +2 -2
- package/plugins/specweave/agents/tdd-orchestrator/AGENT.md +2 -2
- package/plugins/specweave/agents/tech-lead/AGENT.md +2 -2
- package/plugins/specweave/agents/test-aware-planner/AGENT.md +2 -2
- package/plugins/specweave/hooks/post-increment-completion.sh +84 -0
- package/plugins/specweave/hooks/post-increment-planning.sh +114 -7
- package/plugins/specweave/lib/hooks/sync-increment-closure.js +66 -0
- package/plugins/specweave/lib/hooks/sync-increment-closure.ts +111 -0
- package/plugins/specweave-github/lib/github-client-v2.js +32 -8
- package/plugins/specweave-github/lib/github-client-v2.ts +31 -9
- package/plugins/specweave-github/lib/github-feature-sync-cli.js +135 -0
- package/plugins/specweave-github/lib/github-feature-sync-cli.ts +194 -0
- package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +43 -0
- package/plugins/specweave-infrastructure/agents/devops/AGENT.md +2 -2
- package/src/templates/.env.example +9 -26
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { GitHubFeatureSync } from "./github-feature-sync.js";
|
|
5
|
+
import { GitHubClientV2 } from "./github-client-v2.js";
|
|
6
|
+
async function loadGitHubConfig() {
|
|
7
|
+
const projectRoot = process.cwd();
|
|
8
|
+
const configPath = path.join(projectRoot, ".specweave/config.json");
|
|
9
|
+
let owner = process.env.GITHUB_OWNER || "";
|
|
10
|
+
let repo = process.env.GITHUB_REPO || "";
|
|
11
|
+
const token = process.env.GITHUB_TOKEN || "";
|
|
12
|
+
if (existsSync(configPath)) {
|
|
13
|
+
try {
|
|
14
|
+
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
15
|
+
if (config.sync?.github?.owner && config.sync?.github?.repo) {
|
|
16
|
+
owner = config.sync.github.owner;
|
|
17
|
+
repo = config.sync.github.repo;
|
|
18
|
+
} else if (config.multiProject?.enabled && config.multiProject?.activeProject) {
|
|
19
|
+
const activeProject = config.multiProject.activeProject;
|
|
20
|
+
const projectConfig = config.multiProject.projects?.[activeProject];
|
|
21
|
+
if (projectConfig?.externalTools?.github?.repository) {
|
|
22
|
+
const parts = projectConfig.externalTools.github.repository.split("/");
|
|
23
|
+
if (parts.length === 2) {
|
|
24
|
+
owner = parts[0];
|
|
25
|
+
repo = parts[1];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
} else if (config.sync?.activeProfile && config.sync?.profiles) {
|
|
29
|
+
const profile = config.sync.profiles[config.sync.activeProfile];
|
|
30
|
+
if (profile?.config?.owner && profile?.config?.repo) {
|
|
31
|
+
owner = profile.config.owner;
|
|
32
|
+
repo = profile.config.repo;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error("\u26A0\uFE0F Failed to parse config.json:", error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!owner || !repo) {
|
|
40
|
+
try {
|
|
41
|
+
const { execSync } = await import("child_process");
|
|
42
|
+
const remoteUrl = execSync("git remote get-url origin 2>/dev/null", {
|
|
43
|
+
encoding: "utf-8",
|
|
44
|
+
cwd: projectRoot
|
|
45
|
+
}).trim();
|
|
46
|
+
const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
|
|
47
|
+
if (match) {
|
|
48
|
+
owner = owner || match[1];
|
|
49
|
+
repo = repo || match[2];
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (!token) {
|
|
55
|
+
console.error("\u274C GITHUB_TOKEN not set");
|
|
56
|
+
console.error(" Set it in .env file or export GITHUB_TOKEN=ghp_xxx");
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
if (!owner || !repo) {
|
|
60
|
+
console.error("\u274C Could not detect GitHub owner/repo");
|
|
61
|
+
console.error(" Set sync.github.owner and sync.github.repo in .specweave/config.json");
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return { owner, repo, token };
|
|
65
|
+
}
|
|
66
|
+
async function main() {
|
|
67
|
+
const args = process.argv.slice(2);
|
|
68
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
69
|
+
console.log("Usage: node github-feature-sync-cli.js <feature-id>");
|
|
70
|
+
console.log("");
|
|
71
|
+
console.log("Arguments:");
|
|
72
|
+
console.log(" feature-id Feature ID (e.g., FS-062)");
|
|
73
|
+
console.log("");
|
|
74
|
+
console.log("Environment:");
|
|
75
|
+
console.log(" GITHUB_TOKEN Required - GitHub personal access token");
|
|
76
|
+
console.log("");
|
|
77
|
+
console.log("Example:");
|
|
78
|
+
console.log(" GITHUB_TOKEN=ghp_xxx node github-feature-sync-cli.js FS-062");
|
|
79
|
+
process.exit(args.length === 0 ? 1 : 0);
|
|
80
|
+
}
|
|
81
|
+
const featureId = args[0];
|
|
82
|
+
if (!featureId.match(/^FS-\d+$/i)) {
|
|
83
|
+
console.error(`\u274C Invalid feature ID: ${featureId}`);
|
|
84
|
+
console.error(" Expected format: FS-XXX (e.g., FS-062)");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
console.log(`
|
|
88
|
+
\u{1F419} GitHub Feature Sync CLI`);
|
|
89
|
+
console.log(` Feature: ${featureId}`);
|
|
90
|
+
const config = await loadGitHubConfig();
|
|
91
|
+
if (!config) {
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
console.log(` Repository: ${config.owner}/${config.repo}`);
|
|
95
|
+
const projectRoot = process.cwd();
|
|
96
|
+
const specsDir = path.join(projectRoot, ".specweave/docs/internal/specs");
|
|
97
|
+
const profile = {
|
|
98
|
+
provider: "github",
|
|
99
|
+
displayName: "GitHub",
|
|
100
|
+
config: {
|
|
101
|
+
owner: config.owner,
|
|
102
|
+
repo: config.repo,
|
|
103
|
+
token: config.token
|
|
104
|
+
},
|
|
105
|
+
timeRange: {
|
|
106
|
+
default: "1M",
|
|
107
|
+
max: "3M"
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const client = new GitHubClientV2(profile);
|
|
111
|
+
const sync = new GitHubFeatureSync(client, specsDir, projectRoot);
|
|
112
|
+
try {
|
|
113
|
+
console.log(`
|
|
114
|
+
\u{1F504} Syncing ${featureId} to GitHub...`);
|
|
115
|
+
const result = await sync.syncFeatureToGitHub(featureId);
|
|
116
|
+
console.log(`
|
|
117
|
+
\u2705 Sync complete!`);
|
|
118
|
+
console.log(` \u{1F3AF} Milestone: #${result.milestoneNumber}`);
|
|
119
|
+
console.log(` \u{1F4DD} Issues created: ${result.issuesCreated}`);
|
|
120
|
+
console.log(` \u{1F504} Issues updated: ${result.issuesUpdated}`);
|
|
121
|
+
console.log(` \u{1F4DA} User stories processed: ${result.userStoriesProcessed}`);
|
|
122
|
+
if (result.milestoneUrl) {
|
|
123
|
+
console.log(` \u{1F517} ${result.milestoneUrl}`);
|
|
124
|
+
}
|
|
125
|
+
process.exit(0);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(`
|
|
128
|
+
\u274C Sync failed:`, error);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
main().catch((error) => {
|
|
133
|
+
console.error("Fatal error:", error);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* GitHub Feature Sync CLI
|
|
4
|
+
*
|
|
5
|
+
* CLI wrapper for GitHubFeatureSync.syncFeatureToGitHub()
|
|
6
|
+
* Called by post-increment-planning.sh hook to create GitHub issues
|
|
7
|
+
* after increment creation.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node github-feature-sync-cli.js <feature-id>
|
|
11
|
+
* node github-feature-sync-cli.js FS-062
|
|
12
|
+
*
|
|
13
|
+
* Environment:
|
|
14
|
+
* GITHUB_TOKEN - Required
|
|
15
|
+
* GITHUB_OWNER - Optional (detected from config.json or git remote)
|
|
16
|
+
* GITHUB_REPO - Optional (detected from config.json or git remote)
|
|
17
|
+
*
|
|
18
|
+
* @see ADR-0139 (Unified Post-Increment Sync)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { existsSync, readFileSync } from 'fs';
|
|
22
|
+
import * as path from 'path';
|
|
23
|
+
import { GitHubFeatureSync } from './github-feature-sync.js';
|
|
24
|
+
import { GitHubClientV2 } from './github-client-v2.js';
|
|
25
|
+
|
|
26
|
+
interface GitHubConfig {
|
|
27
|
+
owner: string;
|
|
28
|
+
repo: string;
|
|
29
|
+
token: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function loadGitHubConfig(): Promise<GitHubConfig | null> {
|
|
33
|
+
const projectRoot = process.cwd();
|
|
34
|
+
const configPath = path.join(projectRoot, '.specweave/config.json');
|
|
35
|
+
|
|
36
|
+
let owner = process.env.GITHUB_OWNER || '';
|
|
37
|
+
let repo = process.env.GITHUB_REPO || '';
|
|
38
|
+
const token = process.env.GITHUB_TOKEN || '';
|
|
39
|
+
|
|
40
|
+
// Try to load from config.json
|
|
41
|
+
if (existsSync(configPath)) {
|
|
42
|
+
try {
|
|
43
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
44
|
+
|
|
45
|
+
// Method 1: sync.github
|
|
46
|
+
if (config.sync?.github?.owner && config.sync?.github?.repo) {
|
|
47
|
+
owner = config.sync.github.owner;
|
|
48
|
+
repo = config.sync.github.repo;
|
|
49
|
+
}
|
|
50
|
+
// Method 2: multiProject.projects[activeProject].externalTools.github
|
|
51
|
+
else if (config.multiProject?.enabled && config.multiProject?.activeProject) {
|
|
52
|
+
const activeProject = config.multiProject.activeProject;
|
|
53
|
+
const projectConfig = config.multiProject.projects?.[activeProject];
|
|
54
|
+
if (projectConfig?.externalTools?.github?.repository) {
|
|
55
|
+
const parts = projectConfig.externalTools.github.repository.split('/');
|
|
56
|
+
if (parts.length === 2) {
|
|
57
|
+
owner = parts[0];
|
|
58
|
+
repo = parts[1];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Method 3: sync.profiles[activeProfile]
|
|
63
|
+
else if (config.sync?.activeProfile && config.sync?.profiles) {
|
|
64
|
+
const profile = config.sync.profiles[config.sync.activeProfile];
|
|
65
|
+
if (profile?.config?.owner && profile?.config?.repo) {
|
|
66
|
+
owner = profile.config.owner;
|
|
67
|
+
repo = profile.config.repo;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('ā ļø Failed to parse config.json:', error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fallback: detect from git remote
|
|
76
|
+
if (!owner || !repo) {
|
|
77
|
+
try {
|
|
78
|
+
const { execSync } = await import('child_process');
|
|
79
|
+
const remoteUrl = execSync('git remote get-url origin 2>/dev/null', {
|
|
80
|
+
encoding: 'utf-8',
|
|
81
|
+
cwd: projectRoot
|
|
82
|
+
}).trim();
|
|
83
|
+
|
|
84
|
+
// Parse GitHub URL (HTTPS or SSH)
|
|
85
|
+
const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
|
|
86
|
+
if (match) {
|
|
87
|
+
owner = owner || match[1];
|
|
88
|
+
repo = repo || match[2];
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// Git detection failed, continue with what we have
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!token) {
|
|
96
|
+
console.error('ā GITHUB_TOKEN not set');
|
|
97
|
+
console.error(' Set it in .env file or export GITHUB_TOKEN=ghp_xxx');
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!owner || !repo) {
|
|
102
|
+
console.error('ā Could not detect GitHub owner/repo');
|
|
103
|
+
console.error(' Set sync.github.owner and sync.github.repo in .specweave/config.json');
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { owner, repo, token };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function main() {
|
|
111
|
+
const args = process.argv.slice(2);
|
|
112
|
+
|
|
113
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
114
|
+
console.log('Usage: node github-feature-sync-cli.js <feature-id>');
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log('Arguments:');
|
|
117
|
+
console.log(' feature-id Feature ID (e.g., FS-062)');
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log('Environment:');
|
|
120
|
+
console.log(' GITHUB_TOKEN Required - GitHub personal access token');
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log('Example:');
|
|
123
|
+
console.log(' GITHUB_TOKEN=ghp_xxx node github-feature-sync-cli.js FS-062');
|
|
124
|
+
process.exit(args.length === 0 ? 1 : 0);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const featureId = args[0];
|
|
128
|
+
|
|
129
|
+
// Validate feature ID format
|
|
130
|
+
if (!featureId.match(/^FS-\d+$/i)) {
|
|
131
|
+
console.error(`ā Invalid feature ID: ${featureId}`);
|
|
132
|
+
console.error(' Expected format: FS-XXX (e.g., FS-062)');
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log(`\nš GitHub Feature Sync CLI`);
|
|
137
|
+
console.log(` Feature: ${featureId}`);
|
|
138
|
+
|
|
139
|
+
// Load config
|
|
140
|
+
const config = await loadGitHubConfig();
|
|
141
|
+
if (!config) {
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(` Repository: ${config.owner}/${config.repo}`);
|
|
146
|
+
|
|
147
|
+
// Create client and sync
|
|
148
|
+
const projectRoot = process.cwd();
|
|
149
|
+
const specsDir = path.join(projectRoot, '.specweave/docs/internal/specs');
|
|
150
|
+
|
|
151
|
+
const profile = {
|
|
152
|
+
provider: 'github' as const,
|
|
153
|
+
displayName: 'GitHub',
|
|
154
|
+
config: {
|
|
155
|
+
owner: config.owner,
|
|
156
|
+
repo: config.repo,
|
|
157
|
+
token: config.token
|
|
158
|
+
},
|
|
159
|
+
timeRange: {
|
|
160
|
+
default: '1M' as const,
|
|
161
|
+
max: '3M' as const
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const client = new GitHubClientV2(profile);
|
|
166
|
+
const sync = new GitHubFeatureSync(client, specsDir, projectRoot);
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
console.log(`\nš Syncing ${featureId} to GitHub...`);
|
|
170
|
+
|
|
171
|
+
const result = await sync.syncFeatureToGitHub(featureId);
|
|
172
|
+
|
|
173
|
+
console.log(`\nā
Sync complete!`);
|
|
174
|
+
console.log(` šÆ Milestone: #${result.milestoneNumber}`);
|
|
175
|
+
console.log(` š Issues created: ${result.issuesCreated}`);
|
|
176
|
+
console.log(` š Issues updated: ${result.issuesUpdated}`);
|
|
177
|
+
console.log(` š User stories processed: ${result.userStoriesProcessed}`);
|
|
178
|
+
|
|
179
|
+
if (result.milestoneUrl) {
|
|
180
|
+
console.log(` š ${result.milestoneUrl}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
process.exit(0);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error(`\nā Sync failed:`, error);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Run CLI
|
|
191
|
+
main().catch(error => {
|
|
192
|
+
console.error('Fatal error:', error);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
});
|
|
@@ -11,6 +11,49 @@ description: Standard format for ALL GitHub issues created by SpecWeave. Ensures
|
|
|
11
11
|
- Increments (0001-* folders)
|
|
12
12
|
- Specs (spec-*.md files)
|
|
13
13
|
|
|
14
|
+
## Issue Title Format (MANDATORY)
|
|
15
|
+
|
|
16
|
+
### ā
ONLY Allowed Title Formats
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
[FS-XXX][US-YYY] User Story Title ā STANDARD (User Stories)
|
|
20
|
+
[FS-XXX] Feature Title ā Rare (Feature-level only)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Examples**:
|
|
24
|
+
- ā
`[FS-059][US-003] Hook Optimization (P0)`
|
|
25
|
+
- ā
`[FS-054][US-001] Fix Reopen Desync Bug (P0)`
|
|
26
|
+
- ā
`[FS-048] Smart Pagination Feature`
|
|
27
|
+
|
|
28
|
+
### ā PROHIBITED Title Formats (NEVER USE)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
[BUG] Title ā WRONG! Bug is a LABEL, not title prefix
|
|
32
|
+
[HOTFIX] Title ā WRONG! Hotfix is a LABEL
|
|
33
|
+
[FEATURE] Title ā WRONG! Feature is a LABEL
|
|
34
|
+
[DOCS] Title ā WRONG! Docs is a LABEL
|
|
35
|
+
[Increment XXXX] Title ā DEPRECATED! Old format
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Why?** Type-based prefixes like `[BUG]` break traceability:
|
|
39
|
+
- Cannot link to Feature Spec (FS-XXX)
|
|
40
|
+
- Cannot link to User Story (US-YYY)
|
|
41
|
+
- Violates SpecWeave's data flow: `Increment ā Living Docs ā GitHub`
|
|
42
|
+
|
|
43
|
+
**What to do instead?**
|
|
44
|
+
1. Link work to a Feature (FS-XXX) in living docs
|
|
45
|
+
2. Create User Story (US-YYY) under that feature
|
|
46
|
+
3. Use GitHub **labels** for categorization: `bug`, `enhancement`, `hotfix`
|
|
47
|
+
|
|
48
|
+
### Validation
|
|
49
|
+
|
|
50
|
+
The GitHub client (`github-client-v2.ts`) enforces this:
|
|
51
|
+
- Rejects titles starting with `[BUG]`, `[HOTFIX]`, `[FEATURE]`, etc.
|
|
52
|
+
- Rejects deprecated `[Increment XXXX]` format
|
|
53
|
+
- Only allows `[FS-XXX][US-YYY]` or `[FS-XXX]` formats
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
14
57
|
## The Standard Format
|
|
15
58
|
|
|
16
59
|
### ā
Required Elements
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: devops
|
|
3
3
|
description: DevOps and infrastructure expert that generates IaC ONE COMPONENT AT A TIME (VPC ā Compute ā Database ā Monitoring) to prevent crashes. Handles Terraform, Kubernetes, Docker, CI/CD. **CRITICAL CHUNKING RULE - Large deployments (EKS + RDS + monitoring = 20+ files) done incrementally.** Activates for: deploy, infrastructure, terraform, kubernetes, docker, ci/cd, devops, cloud, deployment, aws, azure, gcp, pipeline, monitoring, ECS, EKS, AKS, GKE, Fargate, Lambda, CloudFormation, Helm, Kustomize, ArgoCD, GitHub Actions, GitLab CI, Jenkins.
|
|
4
4
|
tools: Read, Write, Edit, Bash
|
|
5
|
-
model: claude-
|
|
6
|
-
model_preference:
|
|
5
|
+
model: claude-opus-4-5-20251101
|
|
6
|
+
model_preference: opus
|
|
7
7
|
cost_profile: execution
|
|
8
8
|
fallback_behavior: flexible
|
|
9
9
|
max_response_tokens: 2000
|
|
@@ -53,35 +53,18 @@ VERCEL_TOKEN=your-vercel-token-here
|
|
|
53
53
|
# ============================================================================
|
|
54
54
|
|
|
55
55
|
# ----------------------------------------------------------------------------
|
|
56
|
-
# GitHub
|
|
56
|
+
# GitHub
|
|
57
57
|
# ----------------------------------------------------------------------------
|
|
58
|
+
# RECOMMENDED: Use gh CLI instead of tokens
|
|
59
|
+
# gh auth login
|
|
60
|
+
#
|
|
61
|
+
# Only use GITHUB_TOKEN if gh CLI is not available (e.g., CI/CD)
|
|
58
62
|
# Guide: https://github.com/settings/tokens
|
|
59
|
-
# Scopes: repo, read:org
|
|
63
|
+
# Scopes: repo, read:org
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# Strategy 1: Repository-per-team (MOST COMMON)
|
|
65
|
-
# Use when: Each team has separate repositories (microservices, multi-repo)
|
|
66
|
-
# Example: Frontend team ā frontend-app, Backend team ā backend-api
|
|
67
|
-
GITHUB_STRATEGY=repository-per-team
|
|
68
|
-
GITHUB_OWNER=myorg
|
|
69
|
-
GITHUB_REPOS=frontend-app,backend-api,mobile-app,qa-tools
|
|
70
|
-
|
|
71
|
-
# Strategy 2: Team-based (MONOREPO)
|
|
72
|
-
# Use when: Single repository with team-based filtering
|
|
73
|
-
# Example: Monorepo with teams working on different parts
|
|
74
|
-
# GITHUB_STRATEGY=team-based
|
|
75
|
-
# GITHUB_OWNER=myorg
|
|
76
|
-
# GITHUB_REPO=main-product
|
|
77
|
-
# GITHUB_TEAMS=frontend-team,backend-team,mobile-team,qa-team
|
|
78
|
-
|
|
79
|
-
# Strategy 3: Team-multi-repo (COMPLEX)
|
|
80
|
-
# Use when: Teams own multiple repositories (platform teams)
|
|
81
|
-
# Example: Platform team owns api-gateway + auth-service
|
|
82
|
-
# GITHUB_STRATEGY=team-multi-repo
|
|
83
|
-
# GITHUB_OWNER=myorg
|
|
84
|
-
# GITHUB_TEAM_REPO_MAPPING='{"platform-team":["api-gateway","auth-service"],"frontend-team":["web-app","mobile-app"]}'
|
|
65
|
+
GITHUB_TOKEN=ghp_your-github-token-40-chars
|
|
66
|
+
|
|
67
|
+
# All other GitHub configuration is in .specweave/config.json
|
|
85
68
|
|
|
86
69
|
# ----------------------------------------------------------------------------
|
|
87
70
|
# Jira - 3 Strategies for Team Organization
|