specweave 1.0.319 → 1.0.321
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/package.json +1 -1
- package/plugins/specweave/hooks/v2/handlers/universal-auto-create-dispatcher.sh +55 -13
- package/plugins/specweave/lib/vendor/core/universal-auto-create.d.ts +64 -0
- package/plugins/specweave/lib/vendor/core/universal-auto-create.js +228 -0
- package/plugins/specweave/lib/vendor/core/universal-auto-create.js.map +1 -0
- package/plugins/specweave/lib/vendor/utils/feature-id-derivation.d.ts +63 -0
- package/plugins/specweave/lib/vendor/utils/feature-id-derivation.js +85 -0
- package/plugins/specweave/lib/vendor/utils/feature-id-derivation.js.map +1 -0
- package/plugins/specweave/skills/increment/SKILL.md +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specweave",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.321",
|
|
4
4
|
"description": "Spec-driven development framework for AI coding agents. Works with Claude Code, Codex, Antigravity, Cursor, Copilot & more. 100+ skills, 49 CLI commands, verified skill certification, autonomous execution, and living documentation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -95,6 +95,9 @@ command -v jq >/dev/null 2>&1 || exit 0
|
|
|
95
95
|
|
|
96
96
|
# Use shared provider detection (supports PROFILES, LEGACY DIRECT, LEGACY PROVIDER formats)
|
|
97
97
|
HANDLER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
98
|
+
# Derive package root from script location (works for both npm install and dev):
|
|
99
|
+
# handlers/ → v2/ → hooks/ → specweave/ → plugins/ → package root (5 levels up)
|
|
100
|
+
PKG_ROOT="$(cd "$HANDLER_DIR/../../../../.." 2>/dev/null && pwd)"
|
|
98
101
|
SHARED_LIB="$HANDLER_DIR/../lib/check-provider-enabled.sh"
|
|
99
102
|
if [[ -f "$SHARED_LIB" ]]; then
|
|
100
103
|
source "$SHARED_LIB"
|
|
@@ -134,19 +137,26 @@ log "Creating external items for $INC_ID (github=$GH_ENABLED jira=$JIRA_ENABLED
|
|
|
134
137
|
# ============================================================================
|
|
135
138
|
|
|
136
139
|
if [[ "$GH_ENABLED" == "true" ]]; then
|
|
137
|
-
GITHUB_HANDLER="
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
GITHUB_HANDLER=""
|
|
141
|
+
# 1. Sibling plugin dir — handlers/../../../../ = plugins/ (works for both npm and dev)
|
|
142
|
+
CANDIDATE="$HANDLER_DIR/../../../../specweave-github/hooks/github-auto-create-handler.sh"
|
|
143
|
+
[[ -f "$CANDIDATE" ]] && GITHUB_HANDLER="$CANDIDATE"
|
|
144
|
+
# 2. Fallback: PROJECT_ROOT/plugins/ (dev-only, when running from source repo)
|
|
145
|
+
if [[ -z "$GITHUB_HANDLER" ]]; then
|
|
146
|
+
CANDIDATE="${PROJECT_ROOT}/plugins/specweave-github/hooks/github-auto-create-handler.sh"
|
|
147
|
+
[[ -f "$CANDIDATE" ]] && GITHUB_HANDLER="$CANDIDATE"
|
|
143
148
|
fi
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
# 3. Fallback: node_modules/specweave/ (direct npm install)
|
|
150
|
+
if [[ -z "$GITHUB_HANDLER" ]]; then
|
|
151
|
+
CANDIDATE="${PROJECT_ROOT}/node_modules/specweave/plugins/specweave-github/hooks/github-auto-create-handler.sh"
|
|
152
|
+
[[ -f "$CANDIDATE" ]] && GITHUB_HANDLER="$CANDIDATE"
|
|
153
|
+
fi
|
|
154
|
+
if [[ -n "$GITHUB_HANDLER" ]]; then
|
|
155
|
+
log "Delegating GitHub auto-create to handler at $GITHUB_HANDLER"
|
|
146
156
|
bash "$GITHUB_HANDLER" "$INC_ID" &
|
|
147
157
|
GH_PID=$!
|
|
148
158
|
else
|
|
149
|
-
log "GitHub handler not found at any known path"
|
|
159
|
+
log "GitHub handler not found at any known path (checked sibling, PROJECT_ROOT, node_modules)"
|
|
150
160
|
fi
|
|
151
161
|
fi
|
|
152
162
|
|
|
@@ -157,9 +167,39 @@ fi
|
|
|
157
167
|
if [[ "$JIRA_ENABLED" == "true" || "$ADO_ENABLED" == "true" ]]; then
|
|
158
168
|
command -v node &>/dev/null || { log "Node.js not found. Skipping JIRA/ADO."; exit 0; }
|
|
159
169
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
# Resolve universal-auto-create.js from multiple locations
|
|
171
|
+
CREATE_MODULE=""
|
|
172
|
+
# 0. SPECWEAVE_PKG (set by Claude Code hook infrastructure, most reliable)
|
|
173
|
+
if [[ -n "${SPECWEAVE_PKG:-}" ]]; then
|
|
174
|
+
CANDIDATE="${SPECWEAVE_PKG}/dist/src/core/universal-auto-create.js"
|
|
175
|
+
[[ -f "$CANDIDATE" ]] && CREATE_MODULE="$CANDIDATE"
|
|
176
|
+
fi
|
|
177
|
+
# 1. Package dist/ (derived from script location — works for both npm and dev)
|
|
178
|
+
if [[ -z "$CREATE_MODULE" ]]; then
|
|
179
|
+
CANDIDATE="${PKG_ROOT:-$PROJECT_ROOT}/dist/src/core/universal-auto-create.js"
|
|
180
|
+
[[ -f "$CANDIDATE" ]] && CREATE_MODULE="$CANDIDATE"
|
|
181
|
+
fi
|
|
182
|
+
# 2. Vendor directory (self-contained plugin)
|
|
183
|
+
# handlers/../../../ = plugins/specweave/ (3 levels up), then lib/vendor/
|
|
184
|
+
# NOTE: vendor path only works for GitHub-only users; JIRA/ADO dynamic imports
|
|
185
|
+
# (jira-client.js, ado-client.js) are NOT vendored and will fail if this path is used.
|
|
186
|
+
if [[ -z "$CREATE_MODULE" ]]; then
|
|
187
|
+
CANDIDATE="$HANDLER_DIR/../../../lib/vendor/core/universal-auto-create.js"
|
|
188
|
+
[[ -f "$CANDIDATE" ]] && CREATE_MODULE="$(cd "$(dirname "$CANDIDATE")" && pwd)/$(basename "$CANDIDATE")"
|
|
189
|
+
fi
|
|
190
|
+
# 3. node_modules/specweave/ (direct npm install)
|
|
191
|
+
if [[ -z "$CREATE_MODULE" ]]; then
|
|
192
|
+
CANDIDATE="${PROJECT_ROOT}/node_modules/specweave/dist/src/core/universal-auto-create.js"
|
|
193
|
+
[[ -f "$CANDIDATE" ]] && CREATE_MODULE="$CANDIDATE"
|
|
194
|
+
fi
|
|
195
|
+
# 4. Legacy: PROJECT_ROOT/dist/ (dev-only, running from source repo)
|
|
196
|
+
if [[ -z "$CREATE_MODULE" ]]; then
|
|
197
|
+
CANDIDATE="${PROJECT_ROOT}/dist/src/core/universal-auto-create.js"
|
|
198
|
+
[[ -f "$CANDIDATE" ]] && CREATE_MODULE="$CANDIDATE"
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
if [[ -z "$CREATE_MODULE" ]]; then
|
|
202
|
+
log "Module not found at any known path. Skipping JIRA/ADO. Checked: PKG_ROOT=${PKG_ROOT:-unset}, node_modules, vendor, PROJECT_ROOT"
|
|
163
203
|
else
|
|
164
204
|
# Build config JSON for the TypeScript module
|
|
165
205
|
JIRA_DOMAIN=$(jq -r '.sync.jira.domain // .issueTracker.domain // ""' "$CONFIG_PATH" 2>/dev/null)
|
|
@@ -215,7 +255,9 @@ fi
|
|
|
215
255
|
|
|
216
256
|
# Wait for GitHub handler if started
|
|
217
257
|
if [[ -n "${GH_PID:-}" ]]; then
|
|
218
|
-
wait "$GH_PID" 2>/dev/null
|
|
258
|
+
wait "$GH_PID" 2>/dev/null
|
|
259
|
+
GH_EXIT=$?
|
|
260
|
+
[[ $GH_EXIT -ne 0 ]] && log "GitHub handler exited with code $GH_EXIT (check github-auto-create.log for details)"
|
|
219
261
|
fi
|
|
220
262
|
|
|
221
263
|
log "Universal auto-create finished for $INC_ID"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal Auto-Create for External Tools
|
|
3
|
+
*
|
|
4
|
+
* Creates per-user-story items in ALL enabled providers (JIRA, ADO).
|
|
5
|
+
* GitHub is handled separately by the existing github-auto-create-handler.sh.
|
|
6
|
+
*
|
|
7
|
+
* Called by universal-auto-create-dispatcher.sh when spec.md is written.
|
|
8
|
+
*
|
|
9
|
+
* @module universal-auto-create
|
|
10
|
+
*/
|
|
11
|
+
export interface UniversalAutoCreateConfig {
|
|
12
|
+
sync: {
|
|
13
|
+
jira?: {
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
};
|
|
16
|
+
ado?: {
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
jira?: {
|
|
21
|
+
domain?: string;
|
|
22
|
+
projectKey?: string;
|
|
23
|
+
};
|
|
24
|
+
ado?: {
|
|
25
|
+
organization?: string;
|
|
26
|
+
project?: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export interface UserStoryInfo {
|
|
30
|
+
id: string;
|
|
31
|
+
title: string;
|
|
32
|
+
}
|
|
33
|
+
export interface ProviderCreateResult {
|
|
34
|
+
created: Array<{
|
|
35
|
+
usId: string;
|
|
36
|
+
ref: string;
|
|
37
|
+
url: string;
|
|
38
|
+
}>;
|
|
39
|
+
skipped: Array<{
|
|
40
|
+
usId: string;
|
|
41
|
+
reason: string;
|
|
42
|
+
}>;
|
|
43
|
+
errors: Array<{
|
|
44
|
+
usId: string;
|
|
45
|
+
error: string;
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
export type UniversalAutoCreateResult = {
|
|
49
|
+
jira?: ProviderCreateResult;
|
|
50
|
+
ado?: ProviderCreateResult;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Parse user stories (ID + title) from spec.md content.
|
|
54
|
+
* Matches patterns like "### US-001: User story title"
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseUserStories(content: string): UserStoryInfo[];
|
|
57
|
+
/**
|
|
58
|
+
* Create per-user-story items in JIRA and/or ADO for an increment.
|
|
59
|
+
*
|
|
60
|
+
* GitHub is NOT handled here (delegated to existing bash handler).
|
|
61
|
+
* This function is called by the universal-auto-create-dispatcher.sh.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createExternalIssuesForIncrement(incrementId: string, specPath: string, metadataPath: string, config: UniversalAutoCreateConfig): Promise<UniversalAutoCreateResult>;
|
|
64
|
+
//# sourceMappingURL=universal-auto-create.d.ts.map
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal Auto-Create for External Tools
|
|
3
|
+
*
|
|
4
|
+
* Creates per-user-story items in ALL enabled providers (JIRA, ADO).
|
|
5
|
+
* GitHub is handled separately by the existing github-auto-create-handler.sh.
|
|
6
|
+
*
|
|
7
|
+
* Called by universal-auto-create-dispatcher.sh when spec.md is written.
|
|
8
|
+
*
|
|
9
|
+
* @module universal-auto-create
|
|
10
|
+
*/
|
|
11
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
|
+
import { deriveFeatureId } from '../utils/feature-id-derivation.js';
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Helpers
|
|
16
|
+
// ============================================================================
|
|
17
|
+
function emptyResult() {
|
|
18
|
+
return { created: [], skipped: [], errors: [] };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse user stories (ID + title) from spec.md content.
|
|
22
|
+
* Matches patterns like "### US-001: User story title"
|
|
23
|
+
*/
|
|
24
|
+
export function parseUserStories(content) {
|
|
25
|
+
const stories = [];
|
|
26
|
+
const pattern = /###\s+(US-\d+):\s*(.+)/g;
|
|
27
|
+
let match;
|
|
28
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
29
|
+
stories.push({ id: match[1], title: match[2].trim() });
|
|
30
|
+
}
|
|
31
|
+
return stories;
|
|
32
|
+
}
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Provider Functions
|
|
35
|
+
// ============================================================================
|
|
36
|
+
async function createJiraUserStories(incrementId, featureId, userStories, existingLinks, config) {
|
|
37
|
+
const result = emptyResult();
|
|
38
|
+
const { JiraClient } = await import('../integrations/jira/jira-client.js');
|
|
39
|
+
const domain = config.jira?.domain || '';
|
|
40
|
+
const projectKey = config.jira?.projectKey || '';
|
|
41
|
+
if (!domain || !projectKey) {
|
|
42
|
+
result.errors.push({ usId: 'config', error: 'JIRA domain or projectKey not configured' });
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
const client = new JiraClient();
|
|
46
|
+
for (const us of userStories) {
|
|
47
|
+
// Idempotency: skip if already linked
|
|
48
|
+
if (existingLinks[us.id]) {
|
|
49
|
+
result.skipped.push({ usId: us.id, reason: 'already-linked' });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const summary = `[${featureId}][${us.id}] ${us.title}`;
|
|
54
|
+
const issue = await client.createIssue({
|
|
55
|
+
issueType: 'Story',
|
|
56
|
+
summary,
|
|
57
|
+
labels: ['specweave', 'auto-created'],
|
|
58
|
+
}, projectKey);
|
|
59
|
+
const issueUrl = `https://${domain}/browse/${issue.key}`;
|
|
60
|
+
result.created.push({ usId: us.id, ref: issue.key, url: issueUrl });
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
64
|
+
result.errors.push({ usId: us.id, error: msg });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
async function createAdoUserStories(incrementId, featureId, userStories, existingLinks, config) {
|
|
70
|
+
const result = emptyResult();
|
|
71
|
+
const { AdoClient } = await import('../integrations/ado/ado-client.js');
|
|
72
|
+
const { getAdoPat } = await import('../integrations/ado/ado-pat-provider.js');
|
|
73
|
+
const organization = config.ado?.organization || '';
|
|
74
|
+
const project = config.ado?.project || '';
|
|
75
|
+
if (!organization || !project) {
|
|
76
|
+
result.errors.push({ usId: 'config', error: 'ADO organization or project not configured' });
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
const pat = getAdoPat(organization);
|
|
80
|
+
if (!pat) {
|
|
81
|
+
result.errors.push({ usId: 'config', error: 'AZURE_DEVOPS_PAT not set' });
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
const client = new AdoClient({ pat, organization, project });
|
|
85
|
+
for (const us of userStories) {
|
|
86
|
+
// Idempotency: skip if already linked
|
|
87
|
+
if (existingLinks[us.id]) {
|
|
88
|
+
result.skipped.push({ usId: us.id, reason: 'already-linked' });
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const title = `[${featureId}][${us.id}] ${us.title}`;
|
|
93
|
+
const workItem = await client.createWorkItem({
|
|
94
|
+
workItemType: 'User Story',
|
|
95
|
+
title,
|
|
96
|
+
tags: ['specweave', 'auto-created'],
|
|
97
|
+
});
|
|
98
|
+
result.created.push({
|
|
99
|
+
usId: us.id,
|
|
100
|
+
ref: String(workItem.id),
|
|
101
|
+
url: workItem.url,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
106
|
+
result.errors.push({ usId: us.id, error: msg });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// Metadata Update
|
|
113
|
+
// ============================================================================
|
|
114
|
+
async function updateMetadata(metadataPath, results) {
|
|
115
|
+
if (!existsSync(metadataPath))
|
|
116
|
+
return;
|
|
117
|
+
const raw = await readFile(metadataPath, 'utf-8');
|
|
118
|
+
const metadata = JSON.parse(raw);
|
|
119
|
+
if (!metadata.externalLinks) {
|
|
120
|
+
metadata.externalLinks = {};
|
|
121
|
+
}
|
|
122
|
+
const now = new Date().toISOString();
|
|
123
|
+
// Update JIRA links
|
|
124
|
+
if (results.jira && results.jira.created.length > 0) {
|
|
125
|
+
if (!metadata.externalLinks.jira) {
|
|
126
|
+
metadata.externalLinks.jira = { userStories: {} };
|
|
127
|
+
}
|
|
128
|
+
if (!metadata.externalLinks.jira.userStories) {
|
|
129
|
+
metadata.externalLinks.jira.userStories = {};
|
|
130
|
+
}
|
|
131
|
+
for (const item of results.jira.created) {
|
|
132
|
+
metadata.externalLinks.jira.userStories[item.usId] = {
|
|
133
|
+
issueKey: item.ref,
|
|
134
|
+
issueUrl: item.url,
|
|
135
|
+
syncedAt: now,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Update ADO links
|
|
140
|
+
if (results.ado && results.ado.created.length > 0) {
|
|
141
|
+
if (!metadata.externalLinks.ado) {
|
|
142
|
+
metadata.externalLinks.ado = { userStories: {} };
|
|
143
|
+
}
|
|
144
|
+
if (!metadata.externalLinks.ado.userStories) {
|
|
145
|
+
metadata.externalLinks.ado.userStories = {};
|
|
146
|
+
}
|
|
147
|
+
for (const item of results.ado.created) {
|
|
148
|
+
metadata.externalLinks.ado.userStories[item.usId] = {
|
|
149
|
+
workItemId: parseInt(item.ref, 10),
|
|
150
|
+
workItemUrl: item.url,
|
|
151
|
+
syncedAt: now,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
await writeFile(metadataPath, JSON.stringify(metadata, null, 2) + '\n', 'utf-8');
|
|
156
|
+
}
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Main Entry Point
|
|
159
|
+
// ============================================================================
|
|
160
|
+
/**
|
|
161
|
+
* Create per-user-story items in JIRA and/or ADO for an increment.
|
|
162
|
+
*
|
|
163
|
+
* GitHub is NOT handled here (delegated to existing bash handler).
|
|
164
|
+
* This function is called by the universal-auto-create-dispatcher.sh.
|
|
165
|
+
*/
|
|
166
|
+
export async function createExternalIssuesForIncrement(incrementId, specPath, metadataPath, config) {
|
|
167
|
+
const result = {};
|
|
168
|
+
// Parse user stories from spec.md
|
|
169
|
+
const content = await readFile(specPath, 'utf-8');
|
|
170
|
+
const userStories = parseUserStories(content);
|
|
171
|
+
if (userStories.length === 0) {
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
// Derive feature ID
|
|
175
|
+
let featureId;
|
|
176
|
+
try {
|
|
177
|
+
featureId = deriveFeatureId(incrementId);
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
const numMatch = incrementId.match(/^(\d+)/);
|
|
181
|
+
featureId = numMatch ? `FS-${parseInt(numMatch[1], 10)}` : `FS-${incrementId.substring(0, 3)}`;
|
|
182
|
+
}
|
|
183
|
+
// Load existing external links from metadata (for idempotency)
|
|
184
|
+
let existingJiraLinks = {};
|
|
185
|
+
let existingAdoLinks = {};
|
|
186
|
+
if (existsSync(metadataPath)) {
|
|
187
|
+
try {
|
|
188
|
+
const metaRaw = await readFile(metadataPath, 'utf-8');
|
|
189
|
+
const metadata = JSON.parse(metaRaw);
|
|
190
|
+
existingJiraLinks = metadata.externalLinks?.jira?.userStories || {};
|
|
191
|
+
existingAdoLinks = metadata.externalLinks?.ado?.userStories || {};
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// Metadata parse error — proceed without existing links
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Create in each enabled provider (isolated errors)
|
|
198
|
+
if (config.sync?.jira?.enabled) {
|
|
199
|
+
try {
|
|
200
|
+
result.jira = await createJiraUserStories(incrementId, featureId, userStories, existingJiraLinks, config);
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
const providerResult = emptyResult();
|
|
204
|
+
providerResult.errors.push({
|
|
205
|
+
usId: 'unknown',
|
|
206
|
+
error: err instanceof Error ? err.message : String(err),
|
|
207
|
+
});
|
|
208
|
+
result.jira = providerResult;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (config.sync?.ado?.enabled) {
|
|
212
|
+
try {
|
|
213
|
+
result.ado = await createAdoUserStories(incrementId, featureId, userStories, existingAdoLinks, config);
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
const providerResult = emptyResult();
|
|
217
|
+
providerResult.errors.push({
|
|
218
|
+
usId: 'unknown',
|
|
219
|
+
error: err instanceof Error ? err.message : String(err),
|
|
220
|
+
});
|
|
221
|
+
result.ado = providerResult;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Update metadata with new links
|
|
225
|
+
await updateMetadata(metadataPath, result);
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=universal-auto-create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"universal-auto-create.js","sourceRoot":"","sources":["../../../src/core/universal-auto-create.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature ID Derivation Utility
|
|
3
|
+
*
|
|
4
|
+
* Derives feature ID from increment ID using the 1:1 mapping principle:
|
|
5
|
+
* - Each increment maps to exactly one feature folder
|
|
6
|
+
* - Feature ID = FS-{increment_number} or FS-{increment_number}E for external
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* deriveFeatureId('0081-ado-repo-cloning') → 'FS-081'
|
|
10
|
+
* deriveFeatureId('0100-some-feature') → 'FS-100'
|
|
11
|
+
* deriveFeatureId('1000-future-feature') → 'FS-1000'
|
|
12
|
+
* deriveFeatureId('0111E-external-issue') → 'FS-111E'
|
|
13
|
+
*
|
|
14
|
+
* @see ADR-0140 for the decision rationale
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Derive feature ID from increment ID
|
|
18
|
+
*
|
|
19
|
+
* The feature ID is derived directly from the increment number:
|
|
20
|
+
* - Extract leading digits from increment ID
|
|
21
|
+
* - Format as FS-XXX (minimum 3 digits, more if needed)
|
|
22
|
+
* - Add 'E' suffix for external increments (e.g., 0111E-...)
|
|
23
|
+
*
|
|
24
|
+
* This eliminates the need to store feature_id in metadata.json,
|
|
25
|
+
* as it's 100% derivable from the increment ID.
|
|
26
|
+
*
|
|
27
|
+
* CRITICAL: External increments (with E suffix like 0111E-...)
|
|
28
|
+
* MUST map to external features (FS-111E), not internal ones (FS-111).
|
|
29
|
+
*
|
|
30
|
+
* @param incrementId - Increment ID (e.g., "0081-ado-repo-cloning" or "0111E-external-issue")
|
|
31
|
+
* @returns Feature ID (e.g., "FS-081" or "FS-111E")
|
|
32
|
+
* @throws Error if increment ID format is invalid
|
|
33
|
+
*/
|
|
34
|
+
export declare function deriveFeatureId(incrementId: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Extract increment number from increment ID
|
|
37
|
+
*
|
|
38
|
+
* @param incrementId - Increment ID (e.g., "0081-ado-repo-cloning")
|
|
39
|
+
* @returns Increment number (e.g., 81)
|
|
40
|
+
* @throws Error if increment ID format is invalid
|
|
41
|
+
*/
|
|
42
|
+
export declare function extractIncrementNumber(incrementId: string): number;
|
|
43
|
+
/**
|
|
44
|
+
* Validate feature ID format
|
|
45
|
+
*
|
|
46
|
+
* Valid formats:
|
|
47
|
+
* - FS-XXX (internal, 3+ digits)
|
|
48
|
+
* - FS-XXXE (external, 3+ digits with E suffix)
|
|
49
|
+
*
|
|
50
|
+
* @param featureId - Feature ID to validate
|
|
51
|
+
* @returns true if valid, false otherwise
|
|
52
|
+
*/
|
|
53
|
+
export declare function isValidFeatureId(featureId: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Check if feature ID is external (imported)
|
|
56
|
+
*
|
|
57
|
+
* External features have an 'E' suffix: FS-042E
|
|
58
|
+
*
|
|
59
|
+
* @param featureId - Feature ID to check
|
|
60
|
+
* @returns true if external, false if internal
|
|
61
|
+
*/
|
|
62
|
+
export declare function isExternalFeatureId(featureId: string): boolean;
|
|
63
|
+
//# sourceMappingURL=feature-id-derivation.d.ts.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature ID Derivation Utility
|
|
3
|
+
*
|
|
4
|
+
* Derives feature ID from increment ID using the 1:1 mapping principle:
|
|
5
|
+
* - Each increment maps to exactly one feature folder
|
|
6
|
+
* - Feature ID = FS-{increment_number} or FS-{increment_number}E for external
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* deriveFeatureId('0081-ado-repo-cloning') → 'FS-081'
|
|
10
|
+
* deriveFeatureId('0100-some-feature') → 'FS-100'
|
|
11
|
+
* deriveFeatureId('1000-future-feature') → 'FS-1000'
|
|
12
|
+
* deriveFeatureId('0111E-external-issue') → 'FS-111E'
|
|
13
|
+
*
|
|
14
|
+
* @see ADR-0140 for the decision rationale
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Derive feature ID from increment ID
|
|
18
|
+
*
|
|
19
|
+
* The feature ID is derived directly from the increment number:
|
|
20
|
+
* - Extract leading digits from increment ID
|
|
21
|
+
* - Format as FS-XXX (minimum 3 digits, more if needed)
|
|
22
|
+
* - Add 'E' suffix for external increments (e.g., 0111E-...)
|
|
23
|
+
*
|
|
24
|
+
* This eliminates the need to store feature_id in metadata.json,
|
|
25
|
+
* as it's 100% derivable from the increment ID.
|
|
26
|
+
*
|
|
27
|
+
* CRITICAL: External increments (with E suffix like 0111E-...)
|
|
28
|
+
* MUST map to external features (FS-111E), not internal ones (FS-111).
|
|
29
|
+
*
|
|
30
|
+
* @param incrementId - Increment ID (e.g., "0081-ado-repo-cloning" or "0111E-external-issue")
|
|
31
|
+
* @returns Feature ID (e.g., "FS-081" or "FS-111E")
|
|
32
|
+
* @throws Error if increment ID format is invalid
|
|
33
|
+
*/
|
|
34
|
+
export function deriveFeatureId(incrementId) {
|
|
35
|
+
// Match number prefix and optional E suffix
|
|
36
|
+
const match = incrementId.match(/^(\d+)(E)?/);
|
|
37
|
+
if (!match) {
|
|
38
|
+
throw new Error(`Invalid increment ID format: ${incrementId}. Expected format: NNNN-name or NNNNE-name`);
|
|
39
|
+
}
|
|
40
|
+
const num = parseInt(match[1], 10);
|
|
41
|
+
const isExternal = match[2] === 'E';
|
|
42
|
+
// padStart(3, '0') ensures minimum 3 digits (FS-001 to FS-999)
|
|
43
|
+
// Numbers 1000+ naturally have 4+ digits (FS-1000, FS-1001, etc.)
|
|
44
|
+
const featureId = `FS-${String(num).padStart(3, '0')}`;
|
|
45
|
+
return isExternal ? `${featureId}E` : featureId;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Extract increment number from increment ID
|
|
49
|
+
*
|
|
50
|
+
* @param incrementId - Increment ID (e.g., "0081-ado-repo-cloning")
|
|
51
|
+
* @returns Increment number (e.g., 81)
|
|
52
|
+
* @throws Error if increment ID format is invalid
|
|
53
|
+
*/
|
|
54
|
+
export function extractIncrementNumber(incrementId) {
|
|
55
|
+
const match = incrementId.match(/^(\d+)/);
|
|
56
|
+
if (!match) {
|
|
57
|
+
throw new Error(`Invalid increment ID format: ${incrementId}. Expected format: NNNN-name`);
|
|
58
|
+
}
|
|
59
|
+
return parseInt(match[1], 10);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Validate feature ID format
|
|
63
|
+
*
|
|
64
|
+
* Valid formats:
|
|
65
|
+
* - FS-XXX (internal, 3+ digits)
|
|
66
|
+
* - FS-XXXE (external, 3+ digits with E suffix)
|
|
67
|
+
*
|
|
68
|
+
* @param featureId - Feature ID to validate
|
|
69
|
+
* @returns true if valid, false otherwise
|
|
70
|
+
*/
|
|
71
|
+
export function isValidFeatureId(featureId) {
|
|
72
|
+
return /^FS-\d{3,}E?$/.test(featureId);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if feature ID is external (imported)
|
|
76
|
+
*
|
|
77
|
+
* External features have an 'E' suffix: FS-042E
|
|
78
|
+
*
|
|
79
|
+
* @param featureId - Feature ID to check
|
|
80
|
+
* @returns true if external, false if internal
|
|
81
|
+
*/
|
|
82
|
+
export function isExternalFeatureId(featureId) {
|
|
83
|
+
return featureId.endsWith('E');
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=feature-id-derivation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-id-derivation.js","sourceRoot":"","sources":["../../../src/utils/feature-id-derivation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB;IACjD,4CAA4C;IAC5C,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,gCAAgC,WAAW,4CAA4C,CAAC,CAAC;IAC3G,CAAC;IAED,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;IAEpC,+DAA+D;IAC/D,kEAAkE;IAClE,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IACvD,OAAO,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,WAAmB;IACxD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,gCAAgC,WAAW,8BAA8B,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,OAAO,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,OAAO,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -22,6 +22,10 @@ hooks:
|
|
|
22
22
|
hooks:
|
|
23
23
|
- type: command
|
|
24
24
|
command: bash plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh
|
|
25
|
+
- matcher: Skill|Task
|
|
26
|
+
hooks:
|
|
27
|
+
- type: command
|
|
28
|
+
command: bash plugins/specweave/hooks/v2/dispatchers/post-tool-use-analytics.sh
|
|
25
29
|
---
|
|
26
30
|
|
|
27
31
|
# Plan Product Increment
|