specweave 0.27.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 +243 -97
- 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-bulk-discovery.d.ts +5 -1
- package/dist/src/core/repo-structure/repo-bulk-discovery.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-bulk-discovery.js +5 -3
- package/dist/src/core/repo-structure/repo-bulk-discovery.js.map +1 -1
- 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 +15 -16
- 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-edit-write-consolidated.sh +87 -0
- 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-ado/lib/ado-multi-project-sync.js +0 -1
- 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/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
- package/src/templates/.env.example +9 -26
- package/plugins/specweave/hooks/docs-changed.sh.backup +0 -79
- package/plugins/specweave/hooks/human-input-required.sh.backup +0 -75
- package/plugins/specweave/hooks/post-first-increment.sh.backup +0 -61
- package/plugins/specweave/hooks/post-increment-change.sh.backup +0 -98
- package/plugins/specweave/hooks/post-increment-completion.sh.backup +0 -231
- package/plugins/specweave/hooks/post-increment-planning.sh.backup +0 -1048
- package/plugins/specweave/hooks/post-increment-status-change.sh.backup +0 -147
- package/plugins/specweave/hooks/post-spec-update.sh.backup +0 -158
- package/plugins/specweave/hooks/post-user-story-complete.sh.backup +0 -179
- package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +0 -83
- package/plugins/specweave/hooks/pre-implementation.sh.backup +0 -67
- package/plugins/specweave/hooks/pre-task-completion.sh.backup +0 -194
- package/plugins/specweave/hooks/pre-tool-use.sh.backup +0 -133
- package/plugins/specweave/hooks/user-prompt-submit.sh.backup +0 -386
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +0 -353
- package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +0 -172
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +0 -170
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +0 -1104
- package/plugins/specweave-github/hooks/post-task-completion.sh.backup +0 -258
- package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +0 -172
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +0 -1017
- package/plugins/specweave-release/hooks/post-task-completion.sh.backup +0 -110
|
@@ -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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { EnhancedContentBuilder } from "../../../
|
|
2
|
-
import { SpecIncrementMapper } from "../../../
|
|
3
|
-
import { parseSpecContent } from "../../../
|
|
1
|
+
import { EnhancedContentBuilder } from "../../../src/core/sync/enhanced-content-builder.js";
|
|
2
|
+
import { SpecIncrementMapper } from "../../../src/core/sync/spec-increment-mapper.js";
|
|
3
|
+
import { parseSpecContent } from "../../../src/core/spec-content-sync.js";
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
import * as fs from "fs/promises";
|
|
6
6
|
async function syncSpecToJiraWithEnhancedContent(options) {
|
|
@@ -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
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# SpecWeave Docs-Changed Hook
|
|
4
|
-
# Runs after file changes are detected
|
|
5
|
-
# Detects if documentation was changed during implementation
|
|
6
|
-
# Triggers review workflow if needed
|
|
7
|
-
|
|
8
|
-
set -e
|
|
9
|
-
|
|
10
|
-
# Find project root by searching upward for .specweave/ directory
|
|
11
|
-
# Works regardless of where hook is installed (source or .claude/hooks/)
|
|
12
|
-
find_project_root() {
|
|
13
|
-
local dir="$1"
|
|
14
|
-
while [ "$dir" != "/" ]; do
|
|
15
|
-
if [ -d "$dir/.specweave" ]; then
|
|
16
|
-
echo "$dir"
|
|
17
|
-
return 0
|
|
18
|
-
fi
|
|
19
|
-
dir="$(dirname "$dir")"
|
|
20
|
-
done
|
|
21
|
-
# Fallback: try current directory
|
|
22
|
-
if [ -d "$(pwd)/.specweave" ]; then
|
|
23
|
-
pwd
|
|
24
|
-
else
|
|
25
|
-
echo "$(pwd)"
|
|
26
|
-
fi
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
|
30
|
-
cd "$PROJECT_ROOT"
|
|
31
|
-
|
|
32
|
-
# Colors
|
|
33
|
-
RED='\033[0;31m'
|
|
34
|
-
YELLOW='\033[1;33m'
|
|
35
|
-
NC='\033[0m'
|
|
36
|
-
|
|
37
|
-
# Get changed files (git)
|
|
38
|
-
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
39
|
-
# Not a git repository, skip
|
|
40
|
-
exit 0
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
CHANGED_FILES=$(git diff --name-only HEAD 2>/dev/null || echo "")
|
|
44
|
-
|
|
45
|
-
if [ -z "$CHANGED_FILES" ]; then
|
|
46
|
-
# No changes
|
|
47
|
-
exit 0
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
# Check if any documentation files changed
|
|
51
|
-
DOC_CHANGES=$(echo "$CHANGED_FILES" | grep -E '\.specweave/(docs|increments/.*/.*\.md)' || true)
|
|
52
|
-
|
|
53
|
-
if [ -n "$DOC_CHANGES" ]; then
|
|
54
|
-
echo -e "${RED}⚠️ Documentation changed during implementation${NC}"
|
|
55
|
-
echo ""
|
|
56
|
-
echo "📋 Files changed:"
|
|
57
|
-
echo "$DOC_CHANGES" | sed 's/^/ /'
|
|
58
|
-
echo ""
|
|
59
|
-
echo -e "${YELLOW}🔔 Recommended actions:${NC}"
|
|
60
|
-
echo " 1. Review documentation changes"
|
|
61
|
-
echo " 2. Update tasks.md if architecture changed"
|
|
62
|
-
echo " 3. Type /review-docs to see full impact"
|
|
63
|
-
echo ""
|
|
64
|
-
|
|
65
|
-
# Play notification sound
|
|
66
|
-
case "$(uname -s)" in
|
|
67
|
-
Darwin)
|
|
68
|
-
afplay /System/Library/Sounds/Ping.aiff 2>/dev/null &
|
|
69
|
-
;;
|
|
70
|
-
Linux)
|
|
71
|
-
paplay /usr/share/sounds/freedesktop/stereo/dialog-warning.oga 2>/dev/null || true
|
|
72
|
-
;;
|
|
73
|
-
esac
|
|
74
|
-
|
|
75
|
-
# Log to hooks log
|
|
76
|
-
LOGS_DIR=".specweave/logs"
|
|
77
|
-
mkdir -p "$LOGS_DIR"
|
|
78
|
-
echo "[$(date)] Documentation changed: $DOC_CHANGES" >> "$LOGS_DIR/hooks.log"
|
|
79
|
-
fi
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# SpecWeave Human-Input-Required Hook
|
|
4
|
-
# Runs when Claude needs clarification or approval
|
|
5
|
-
#
|
|
6
|
-
# Actions:
|
|
7
|
-
# 1. Play notification sound (Ping.aiff)
|
|
8
|
-
# 2. Log the question/requirement
|
|
9
|
-
# 3. Record in current increment's work log (if applicable)
|
|
10
|
-
|
|
11
|
-
set -e
|
|
12
|
-
|
|
13
|
-
# Find project root by searching upward for .specweave/ directory
|
|
14
|
-
# Works regardless of where hook is installed (source or .claude/hooks/)
|
|
15
|
-
find_project_root() {
|
|
16
|
-
local dir="$1"
|
|
17
|
-
while [ "$dir" != "/" ]; do
|
|
18
|
-
if [ -d "$dir/.specweave" ]; then
|
|
19
|
-
echo "$dir"
|
|
20
|
-
return 0
|
|
21
|
-
fi
|
|
22
|
-
dir="$(dirname "$dir")"
|
|
23
|
-
done
|
|
24
|
-
# Fallback: try current directory
|
|
25
|
-
if [ -d "$(pwd)/.specweave" ]; then
|
|
26
|
-
pwd
|
|
27
|
-
else
|
|
28
|
-
echo "$(pwd)"
|
|
29
|
-
fi
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
|
33
|
-
cd "$PROJECT_ROOT"
|
|
34
|
-
|
|
35
|
-
# Get question/requirement (passed as argument or default)
|
|
36
|
-
QUESTION="${1:-User input required}"
|
|
37
|
-
|
|
38
|
-
echo "❓ Human input required"
|
|
39
|
-
|
|
40
|
-
# 1. Play notification sound (cross-platform)
|
|
41
|
-
play_sound() {
|
|
42
|
-
case "$(uname -s)" in
|
|
43
|
-
Darwin)
|
|
44
|
-
afplay /System/Library/Sounds/Ping.aiff 2>/dev/null &
|
|
45
|
-
;;
|
|
46
|
-
Linux)
|
|
47
|
-
paplay /usr/share/sounds/freedesktop/stereo/dialog-question.oga 2>/dev/null || \
|
|
48
|
-
aplay /usr/share/sounds/alsa/Side_Left.wav 2>/dev/null || true
|
|
49
|
-
;;
|
|
50
|
-
MINGW*|MSYS*|CYGWIN*)
|
|
51
|
-
powershell -c "(New-Object Media.SoundPlayer 'C:\Windows\Media\notify.wav').PlaySync();" 2>/dev/null || true
|
|
52
|
-
;;
|
|
53
|
-
esac
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
play_sound &
|
|
57
|
-
|
|
58
|
-
# 2. Log to main hooks log
|
|
59
|
-
LOGS_DIR=".specweave/logs"
|
|
60
|
-
mkdir -p "$LOGS_DIR"
|
|
61
|
-
echo "[$(date)] Human input required: $QUESTION" >> "$LOGS_DIR/hooks.log"
|
|
62
|
-
|
|
63
|
-
# 3. Log to current work context (if exists)
|
|
64
|
-
CURRENT_WORK=$(find .specweave/work -maxdepth 1 -type d -name "current-*" | head -1 || true)
|
|
65
|
-
|
|
66
|
-
if [ -n "$CURRENT_WORK" ] && [ -d "$CURRENT_WORK" ]; then
|
|
67
|
-
echo "" >> "$CURRENT_WORK/notes.md"
|
|
68
|
-
echo "## Input Required ($(date +%Y-%m-%d\ %H:%M))" >> "$CURRENT_WORK/notes.md"
|
|
69
|
-
echo "" >> "$CURRENT_WORK/notes.md"
|
|
70
|
-
echo "$QUESTION" >> "$CURRENT_WORK/notes.md"
|
|
71
|
-
echo "" >> "$CURRENT_WORK/notes.md"
|
|
72
|
-
echo "📝 Logged to $CURRENT_WORK/notes.md"
|
|
73
|
-
fi
|
|
74
|
-
|
|
75
|
-
echo "✅ Hook complete"
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SpecWeave Post-First-Increment Hook
|
|
3
|
-
#
|
|
4
|
-
# Triggers after the first increment is completed
|
|
5
|
-
# Congratulates the user on completing their first increment
|
|
6
|
-
#
|
|
7
|
-
# NON-INTERACTIVE: Just shows a message (hooks run in background)
|
|
8
|
-
|
|
9
|
-
set -euo pipefail
|
|
10
|
-
|
|
11
|
-
# Get project root (where .specweave/ lives)
|
|
12
|
-
PROJECT_ROOT="$(pwd)"
|
|
13
|
-
|
|
14
|
-
# Check if .specweave directory exists
|
|
15
|
-
if [ ! -d ".specweave" ]; then
|
|
16
|
-
# Not in SpecWeave project, skip
|
|
17
|
-
exit 0
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
# Check if this is the first increment completion
|
|
21
|
-
# Count completed increments in .specweave/increments/
|
|
22
|
-
COMPLETED_COUNT=0
|
|
23
|
-
if [ -d ".specweave/increments" ]; then
|
|
24
|
-
# Count directories that have COMPLETION-REPORT.md or completion metadata
|
|
25
|
-
for inc_dir in .specweave/increments/[0-9][0-9][0-9][0-9]-*/; do
|
|
26
|
-
if [ -d "$inc_dir" ]; then
|
|
27
|
-
if [ -f "${inc_dir}reports/COMPLETION-REPORT.md" ] || \
|
|
28
|
-
[ -f "${inc_dir}COMPLETION-SUMMARY.md" ] || \
|
|
29
|
-
([ -f "${inc_dir}metadata.json" ] && grep -q '"status".*"completed"' "${inc_dir}metadata.json" 2>/dev/null); then
|
|
30
|
-
COMPLETED_COUNT=$((COMPLETED_COUNT + 1))
|
|
31
|
-
fi
|
|
32
|
-
fi
|
|
33
|
-
done
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# Only trigger on first completion
|
|
37
|
-
if [ "$COMPLETED_COUNT" -ne 1 ]; then
|
|
38
|
-
exit 0
|
|
39
|
-
fi
|
|
40
|
-
|
|
41
|
-
# Show congratulations message (non-interactive)
|
|
42
|
-
echo ""
|
|
43
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
44
|
-
echo "🎉 Congratulations! You completed your first increment!"
|
|
45
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
46
|
-
echo ""
|
|
47
|
-
echo "✅ Your increment has been documented in:"
|
|
48
|
-
echo " .specweave/increments/[increment-id]/"
|
|
49
|
-
echo ""
|
|
50
|
-
echo "📚 View your documentation:"
|
|
51
|
-
echo " - Specs: .specweave/docs/internal/specs/"
|
|
52
|
-
echo " - Architecture: .specweave/docs/internal/architecture/"
|
|
53
|
-
echo ""
|
|
54
|
-
echo "🚀 Next steps:"
|
|
55
|
-
echo " - Review your increment: /specweave:status"
|
|
56
|
-
echo " - Start next increment: /specweave:increment \"feature name\""
|
|
57
|
-
echo ""
|
|
58
|
-
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
59
|
-
echo ""
|
|
60
|
-
|
|
61
|
-
exit 0
|