specweave 0.26.14 → 0.27.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.md +73 -1
- package/README.md +111 -466
- package/dist/plugins/specweave-jira/lib/setup-wizard.d.ts.map +1 -1
- package/dist/plugins/specweave-jira/lib/setup-wizard.js +57 -78
- package/dist/plugins/specweave-jira/lib/setup-wizard.js.map +1 -1
- package/dist/src/cli/commands/import-docs.d.ts.map +1 -1
- package/dist/src/cli/commands/import-docs.js +23 -31
- package/dist/src/cli/commands/import-docs.js.map +1 -1
- package/dist/src/cli/commands/import-external.d.ts.map +1 -1
- package/dist/src/cli/commands/import-external.js +6 -10
- package/dist/src/cli/commands/import-external.js.map +1 -1
- package/dist/src/cli/commands/init-multiproject.d.ts.map +1 -1
- package/dist/src/cli/commands/init-multiproject.js +58 -73
- package/dist/src/cli/commands/init-multiproject.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts +17 -11
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +221 -1874
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/install.d.ts.map +1 -1
- package/dist/src/cli/commands/install.js +14 -22
- package/dist/src/cli/commands/install.js.map +1 -1
- package/dist/src/cli/commands/migrate-config.d.ts.map +1 -1
- package/dist/src/cli/commands/migrate-config.js +6 -10
- package/dist/src/cli/commands/migrate-config.js.map +1 -1
- package/dist/src/cli/commands/switch-project.d.ts.map +1 -1
- package/dist/src/cli/commands/switch-project.js.map +1 -1
- package/dist/src/cli/helpers/ado-area-path-mapper.d.ts.map +1 -1
- package/dist/src/cli/helpers/ado-area-path-mapper.js +36 -49
- package/dist/src/cli/helpers/ado-area-path-mapper.js.map +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.d.ts.map +1 -1
- package/dist/src/cli/helpers/github/increment-profile-selector.js.map +1 -1
- package/dist/src/cli/helpers/github/profile-manager.d.ts.map +1 -1
- package/dist/src/cli/helpers/github/profile-manager.js +8 -11
- package/dist/src/cli/helpers/github/profile-manager.js.map +1 -1
- package/dist/src/cli/helpers/github-repo-selector.d.ts.map +1 -1
- package/dist/src/cli/helpers/github-repo-selector.js +26 -50
- package/dist/src/cli/helpers/github-repo-selector.js.map +1 -1
- package/dist/src/cli/helpers/import-strategy-prompter.d.ts.map +1 -1
- package/dist/src/cli/helpers/import-strategy-prompter.js +39 -52
- package/dist/src/cli/helpers/import-strategy-prompter.js.map +1 -1
- package/dist/src/cli/helpers/init/config-detection.d.ts +40 -0
- package/dist/src/cli/helpers/init/config-detection.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/config-detection.js +125 -0
- package/dist/src/cli/helpers/init/config-detection.js.map +1 -0
- package/dist/src/cli/helpers/init/directory-structure.d.ts +26 -0
- package/dist/src/cli/helpers/init/directory-structure.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/directory-structure.js +190 -0
- package/dist/src/cli/helpers/init/directory-structure.js.map +1 -0
- package/dist/src/cli/helpers/init/external-import.d.ts +15 -0
- package/dist/src/cli/helpers/init/external-import.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/external-import.js +251 -0
- package/dist/src/cli/helpers/init/external-import.js.map +1 -0
- package/dist/src/cli/helpers/init/index.d.ts +15 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/index.js +26 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -0
- package/dist/src/cli/helpers/init/next-steps.d.ts +15 -0
- package/dist/src/cli/helpers/init/next-steps.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/next-steps.js +72 -0
- package/dist/src/cli/helpers/init/next-steps.js.map +1 -0
- package/dist/src/cli/helpers/init/path-utils.d.ts +41 -0
- package/dist/src/cli/helpers/init/path-utils.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/path-utils.js +146 -0
- package/dist/src/cli/helpers/init/path-utils.js.map +1 -0
- package/dist/src/cli/helpers/init/plugin-installer.d.ts +28 -0
- package/dist/src/cli/helpers/init/plugin-installer.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/plugin-installer.js +238 -0
- package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -0
- package/dist/src/cli/helpers/init/repository-setup.d.ts +28 -0
- package/dist/src/cli/helpers/init/repository-setup.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/repository-setup.js +78 -0
- package/dist/src/cli/helpers/init/repository-setup.js.map +1 -0
- package/dist/src/cli/helpers/init/smart-reinit.d.ts +30 -0
- package/dist/src/cli/helpers/init/smart-reinit.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/smart-reinit.js +140 -0
- package/dist/src/cli/helpers/init/smart-reinit.js.map +1 -0
- package/dist/src/cli/helpers/init/testing-config.d.ts +27 -0
- package/dist/src/cli/helpers/init/testing-config.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/testing-config.js +131 -0
- package/dist/src/cli/helpers/init/testing-config.js.map +1 -0
- package/dist/src/cli/helpers/init/types.d.ts +86 -0
- package/dist/src/cli/helpers/init/types.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/types.js +5 -0
- package/dist/src/cli/helpers/init/types.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js +10 -12
- package/dist/src/cli/helpers/issue-tracker/ado-auto-discover.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/ado.js +43 -60
- package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +193 -230
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github.js +43 -54
- package/dist/src/cli/helpers/issue-tracker/github.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +27 -40
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.js +54 -70
- package/dist/src/cli/helpers/issue-tracker/jira.js.map +1 -1
- package/dist/src/cli/helpers/smart-filter.d.ts.map +1 -1
- package/dist/src/cli/helpers/smart-filter.js +62 -85
- package/dist/src/cli/helpers/smart-filter.js.map +1 -1
- package/dist/src/core/increment/auto-transition-manager.d.ts +12 -0
- package/dist/src/core/increment/auto-transition-manager.d.ts.map +1 -1
- package/dist/src/core/increment/auto-transition-manager.js +45 -0
- package/dist/src/core/increment/auto-transition-manager.js.map +1 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +46 -0
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.d.ts +12 -0
- package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -1
- package/dist/src/core/increment/status-change-sync-trigger.js +48 -2
- package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.d.ts +13 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +40 -0
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -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 +63 -83
- package/dist/src/core/repo-structure/repo-bulk-discovery.js.map +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 +339 -424
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/sync/bidirectional-engine.d.ts.map +1 -1
- package/dist/src/core/sync/bidirectional-engine.js +21 -29
- package/dist/src/core/sync/bidirectional-engine.js.map +1 -1
- package/dist/src/init/InitFlow.js +15 -19
- package/dist/src/init/InitFlow.js.map +1 -1
- package/dist/src/init/repo/types.d.ts +1 -1
- package/dist/src/integrations/ado/area-path-mapper.d.ts.map +1 -1
- package/dist/src/integrations/ado/area-path-mapper.js +19 -23
- package/dist/src/integrations/ado/area-path-mapper.js.map +1 -1
- package/dist/src/utils/external-resource-validator.d.ts.map +1 -1
- package/dist/src/utils/external-resource-validator.js +41 -65
- package/dist/src/utils/external-resource-validator.js.map +1 -1
- package/dist/src/utils/project-detection.d.ts.map +1 -1
- package/dist/src/utils/project-detection.js +19 -21
- package/dist/src/utils/project-detection.js.map +1 -1
- package/dist/src/utils/project-validator.d.ts.map +1 -1
- package/dist/src/utils/project-validator.js +5 -7
- package/dist/src/utils/project-validator.js.map +1 -1
- package/package.json +2 -3
- package/plugins/specweave/agents/tech-lead/AGENT.md +9 -0
- package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
- package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
- package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
- package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
- package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
- package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
- package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
- package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
- package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
- package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
- package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
- package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.d.ts +12 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js +45 -0
- package/plugins/specweave/lib/vendor/core/increment/auto-transition-manager.js.map +1 -1
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +46 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/skills/increment-planner/SKILL.md +10 -5
- package/plugins/specweave/skills/specweave-framework/SKILL.md +6 -4
- package/plugins/specweave/templates/coding-standards.md.template +36 -0
- package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
- package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-ado/lib/ado-multi-project-sync.js +1 -0
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-ado/lib/project-selector.js +56 -67
- package/plugins/specweave-ado/lib/project-selector.ts +72 -85
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1104 -0
- package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
- package/plugins/specweave-github/lib/repo-selector.js +55 -66
- package/plugins/specweave-github/lib/repo-selector.ts +73 -84
- package/plugins/specweave-jira/commands/import-projects.js +3 -5
- package/plugins/specweave-jira/commands/import-projects.ts +3 -5
- package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
- package/plugins/specweave-jira/lib/project-selector.js +60 -71
- package/plugins/specweave-jira/lib/project-selector.ts +78 -91
- package/plugins/specweave-jira/lib/setup-wizard.js +51 -72
- package/plugins/specweave-jira/lib/setup-wizard.ts +56 -74
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1017 -0
- package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
- package/src/templates/CLAUDE.md.template +14 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { AdoClientV2 } from "./ado-client-v2.js";
|
|
2
|
+
import { EnhancedContentBuilder } from "../../../src/core/sync/enhanced-content-builder.js";
|
|
3
|
+
import { SpecIncrementMapper } from "../../../src/core/sync/spec-increment-mapper.js";
|
|
4
|
+
import { parseSpecContent } from "../../../src/core/spec-content-sync.js";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import fs from "fs/promises";
|
|
7
|
+
async function syncSpecToAdoWithEnhancedContent(options) {
|
|
8
|
+
const { specPath, organization, project, dryRun = false, verbose = false } = options;
|
|
9
|
+
try {
|
|
10
|
+
const baseSpec = await parseSpecContent(specPath);
|
|
11
|
+
if (!baseSpec) {
|
|
12
|
+
return {
|
|
13
|
+
success: false,
|
|
14
|
+
action: "error",
|
|
15
|
+
error: "Failed to parse spec content"
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (verbose) {
|
|
19
|
+
console.log(`\u{1F4C4} Parsed spec: ${baseSpec.identifier.compact}`);
|
|
20
|
+
}
|
|
21
|
+
const specId = baseSpec.identifier.full || baseSpec.identifier.compact;
|
|
22
|
+
const rootDir = await findSpecWeaveRoot(specPath);
|
|
23
|
+
const mapper = new SpecIncrementMapper(rootDir);
|
|
24
|
+
const mapping = await mapper.mapSpecToIncrements(specId);
|
|
25
|
+
if (verbose) {
|
|
26
|
+
console.log(`\u{1F517} Found ${mapping.increments.length} related increments`);
|
|
27
|
+
}
|
|
28
|
+
const taskMapping = buildTaskMapping(mapping.increments, organization, project);
|
|
29
|
+
const architectureDocs = await findArchitectureDocs(rootDir, specId);
|
|
30
|
+
const enhancedSpec = {
|
|
31
|
+
...baseSpec,
|
|
32
|
+
summary: baseSpec.description,
|
|
33
|
+
taskMapping,
|
|
34
|
+
architectureDocs
|
|
35
|
+
};
|
|
36
|
+
const builder = new EnhancedContentBuilder();
|
|
37
|
+
const description = builder.buildExternalDescription(enhancedSpec);
|
|
38
|
+
if (verbose) {
|
|
39
|
+
console.log(`\u{1F4DD} Generated description: ${description.length} characters`);
|
|
40
|
+
}
|
|
41
|
+
if (dryRun) {
|
|
42
|
+
console.log("\u{1F50D} DRY RUN - Would create/update feature with:");
|
|
43
|
+
console.log(` Title: ${baseSpec.title}`);
|
|
44
|
+
console.log(` Description length: ${description.length}`);
|
|
45
|
+
return {
|
|
46
|
+
success: true,
|
|
47
|
+
action: "no-change",
|
|
48
|
+
tasksLinked: taskMapping?.tasks.length || 0
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (!organization || !project) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
action: "error",
|
|
55
|
+
error: "Azure DevOps organization/project not specified"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const profile = {
|
|
59
|
+
provider: "ado",
|
|
60
|
+
displayName: `${organization}/${project}`,
|
|
61
|
+
config: {
|
|
62
|
+
organization,
|
|
63
|
+
project
|
|
64
|
+
},
|
|
65
|
+
timeRange: { default: "1M", max: "6M" }
|
|
66
|
+
};
|
|
67
|
+
const pat = process.env.AZURE_DEVOPS_PAT || "";
|
|
68
|
+
const client = new AdoClientV2(profile, pat);
|
|
69
|
+
const existingFeature = await findExistingFeature(client, baseSpec.identifier.compact);
|
|
70
|
+
let result;
|
|
71
|
+
if (existingFeature) {
|
|
72
|
+
await client.updateWorkItem(existingFeature.id, {
|
|
73
|
+
title: `[${baseSpec.identifier.compact}] ${baseSpec.title}`,
|
|
74
|
+
description
|
|
75
|
+
});
|
|
76
|
+
result = {
|
|
77
|
+
success: true,
|
|
78
|
+
action: "updated",
|
|
79
|
+
featureId: existingFeature.id,
|
|
80
|
+
featureUrl: `https://dev.azure.com/${organization}/${project}/_workitems/edit/${existingFeature.id}`,
|
|
81
|
+
tasksLinked: taskMapping?.tasks.length || 0
|
|
82
|
+
};
|
|
83
|
+
} else {
|
|
84
|
+
const feature = await client.createEpic({
|
|
85
|
+
title: `[${baseSpec.identifier.compact}] ${baseSpec.title}`,
|
|
86
|
+
description,
|
|
87
|
+
tags: ["spec", "external-tool-sync"]
|
|
88
|
+
});
|
|
89
|
+
result = {
|
|
90
|
+
success: true,
|
|
91
|
+
action: "created",
|
|
92
|
+
featureId: feature.id,
|
|
93
|
+
featureUrl: `https://dev.azure.com/${organization}/${project}/_workitems/edit/${feature.id}`,
|
|
94
|
+
tasksLinked: taskMapping?.tasks.length || 0
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (verbose) {
|
|
98
|
+
console.log(`\u2705 ${result.action === "created" ? "Created" : "Updated"} feature #${result.featureId}`);
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
success: false,
|
|
104
|
+
action: "error",
|
|
105
|
+
error: error.message
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function findSpecWeaveRoot(specPath) {
|
|
110
|
+
let currentDir = path.dirname(specPath);
|
|
111
|
+
while (true) {
|
|
112
|
+
const specweaveDir = path.join(currentDir, ".specweave");
|
|
113
|
+
try {
|
|
114
|
+
await fs.access(specweaveDir);
|
|
115
|
+
return currentDir;
|
|
116
|
+
} catch {
|
|
117
|
+
const parentDir = path.dirname(currentDir);
|
|
118
|
+
if (parentDir === currentDir) {
|
|
119
|
+
throw new Error(".specweave directory not found");
|
|
120
|
+
}
|
|
121
|
+
currentDir = parentDir;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function buildTaskMapping(increments, organization, project) {
|
|
126
|
+
if (increments.length === 0) return void 0;
|
|
127
|
+
const firstIncrement = increments[0];
|
|
128
|
+
const tasks = firstIncrement.tasks.map((task) => ({
|
|
129
|
+
id: task.id,
|
|
130
|
+
title: task.title,
|
|
131
|
+
userStories: task.userStories
|
|
132
|
+
}));
|
|
133
|
+
return {
|
|
134
|
+
incrementId: firstIncrement.id,
|
|
135
|
+
tasks,
|
|
136
|
+
tasksUrl: `https://dev.azure.com/${organization}/${project}/_git/repo?path=/.specweave/increments/${firstIncrement.id}/tasks.md`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function findArchitectureDocs(rootDir, specId) {
|
|
140
|
+
const docs = [];
|
|
141
|
+
const archDir = path.join(rootDir, ".specweave/docs/internal/architecture");
|
|
142
|
+
try {
|
|
143
|
+
const adrDir = path.join(archDir, "adr");
|
|
144
|
+
try {
|
|
145
|
+
const adrs = await fs.readdir(adrDir);
|
|
146
|
+
const relatedAdrs = adrs.filter((file) => file.includes(specId.replace("spec-", "")));
|
|
147
|
+
for (const adr of relatedAdrs) {
|
|
148
|
+
docs.push({
|
|
149
|
+
type: "adr",
|
|
150
|
+
path: path.join(adrDir, adr),
|
|
151
|
+
title: adr.replace(".md", "").replace(/-/g, " ")
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
return docs;
|
|
159
|
+
}
|
|
160
|
+
async function findExistingFeature(client, specId) {
|
|
161
|
+
try {
|
|
162
|
+
const features = await client.queryWorkItems(`[System.Title] Contains '[${specId}]' AND [System.WorkItemType] = 'Feature'`);
|
|
163
|
+
return features[0] || null;
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export {
|
|
169
|
+
syncSpecToAdoWithEnhancedContent
|
|
170
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { select, checkbox, input, Separator } from "@inquirer/prompts";
|
|
2
2
|
async function fetchAllAdoProjects(client) {
|
|
3
3
|
console.log("\u{1F50D} Fetching Azure DevOps projects...");
|
|
4
4
|
try {
|
|
@@ -31,29 +31,25 @@ async function selectAdoProjects(client, options = {}) {
|
|
|
31
31
|
console.log("\u{1F4CB} Available Azure DevOps Projects:\n");
|
|
32
32
|
console.log(` Total: ${allProjects.length} projects
|
|
33
33
|
`);
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
const selectionMethod = await select({
|
|
35
|
+
message: "How would you like to select projects?",
|
|
36
|
+
choices: [
|
|
37
|
+
{
|
|
38
|
+
name: `\u{1F4CB} Interactive (browse and select from ${allProjects.length} projects)`,
|
|
39
|
+
value: "interactive"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "\u270F\uFE0F Manual entry (type project names)",
|
|
43
|
+
value: "manual"
|
|
44
|
+
},
|
|
45
|
+
...allowSelectAll ? [
|
|
44
46
|
{
|
|
45
|
-
name:
|
|
46
|
-
value: "
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
value: "all"
|
|
52
|
-
}
|
|
53
|
-
] : []
|
|
54
|
-
]
|
|
55
|
-
}
|
|
56
|
-
]);
|
|
47
|
+
name: `\u2728 Select all (${allProjects.length} projects)`,
|
|
48
|
+
value: "all"
|
|
49
|
+
}
|
|
50
|
+
] : []
|
|
51
|
+
]
|
|
52
|
+
});
|
|
57
53
|
if (selectionMethod === "all") {
|
|
58
54
|
return {
|
|
59
55
|
selectedNames: allProjects.map((p) => p.name),
|
|
@@ -73,39 +69,36 @@ async function selectAdoProjects(client, options = {}) {
|
|
|
73
69
|
}
|
|
74
70
|
async function interactiveProjectSelection(allProjects, preSelected, minSelection, maxSelection, pageSize) {
|
|
75
71
|
console.log("\u{1F4A1} Use <space> to select, <a> to toggle all, <i> to invert\n");
|
|
76
|
-
const choices =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
const choices = [
|
|
73
|
+
...allProjects.map((p) => ({
|
|
74
|
+
name: formatProjectChoice(p),
|
|
75
|
+
value: p.name,
|
|
76
|
+
checked: preSelected.includes(p.name)
|
|
77
|
+
})),
|
|
78
|
+
// Add separator and manual entry option at the end
|
|
79
|
+
new Separator(),
|
|
83
80
|
{
|
|
84
81
|
name: "\u270F\uFE0F Enter project names manually instead",
|
|
85
82
|
value: "__MANUAL__",
|
|
86
83
|
checked: false
|
|
87
84
|
}
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const actualSelected = selected.filter((k) => k !== "__MANUAL__");
|
|
99
|
-
if (actualSelected.length < minSelection) {
|
|
100
|
-
return `Please select at least ${minSelection} project(s)`;
|
|
101
|
-
}
|
|
102
|
-
if (maxSelection && actualSelected.length > maxSelection) {
|
|
103
|
-
return `Please select at most ${maxSelection} project(s)`;
|
|
104
|
-
}
|
|
105
|
-
return true;
|
|
85
|
+
];
|
|
86
|
+
const selectedNames = await checkbox({
|
|
87
|
+
message: `Select Azure DevOps projects (${minSelection}${maxSelection ? `-${maxSelection}` : "+"} required):`,
|
|
88
|
+
choices,
|
|
89
|
+
pageSize,
|
|
90
|
+
loop: false,
|
|
91
|
+
validate: (selected) => {
|
|
92
|
+
const actualSelected = selected.filter((k) => k !== "__MANUAL__");
|
|
93
|
+
if (actualSelected.length < minSelection) {
|
|
94
|
+
return `Please select at least ${minSelection} project(s)`;
|
|
106
95
|
}
|
|
96
|
+
if (maxSelection && actualSelected.length > maxSelection) {
|
|
97
|
+
return `Please select at most ${maxSelection} project(s)`;
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
107
100
|
}
|
|
108
|
-
|
|
101
|
+
});
|
|
109
102
|
if (selectedNames.includes("__MANUAL__")) {
|
|
110
103
|
return await manualProjectEntry(allProjects, minSelection, maxSelection);
|
|
111
104
|
}
|
|
@@ -127,26 +120,22 @@ async function manualProjectEntry(allProjects, minSelection, maxSelection) {
|
|
|
127
120
|
);
|
|
128
121
|
console.log("");
|
|
129
122
|
}
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
const names = input.split(",").map((n) => n.trim()).filter((n) => n.length > 0);
|
|
140
|
-
if (names.length < minSelection) {
|
|
141
|
-
return `Please enter at least ${minSelection} project name(s)`;
|
|
142
|
-
}
|
|
143
|
-
if (maxSelection && names.length > maxSelection) {
|
|
144
|
-
return `Please enter at most ${maxSelection} project name(s)`;
|
|
145
|
-
}
|
|
146
|
-
return true;
|
|
123
|
+
const manualNames = await input({
|
|
124
|
+
message: "Project names:",
|
|
125
|
+
validate: (inputValue) => {
|
|
126
|
+
if (!inputValue.trim()) {
|
|
127
|
+
return "Please enter at least one project name";
|
|
128
|
+
}
|
|
129
|
+
const names = inputValue.split(",").map((n) => n.trim()).filter((n) => n.length > 0);
|
|
130
|
+
if (names.length < minSelection) {
|
|
131
|
+
return `Please enter at least ${minSelection} project name(s)`;
|
|
147
132
|
}
|
|
133
|
+
if (maxSelection && names.length > maxSelection) {
|
|
134
|
+
return `Please enter at most ${maxSelection} project name(s)`;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
148
137
|
}
|
|
149
|
-
|
|
138
|
+
});
|
|
150
139
|
const selectedNames = manualNames.split(",").map((n) => n.trim()).filter((n) => n.length > 0);
|
|
151
140
|
const knownNames = allProjects.map((p) => p.name);
|
|
152
141
|
const unknownNames = selectedNames.filter((n) => !knownNames.includes(n));
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Validates project names
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import { select, checkbox, input, Separator } from '@inquirer/prompts';
|
|
13
13
|
import { AdoClient } from '../../../src/integrations/ado/ado-client.js';
|
|
14
14
|
|
|
15
15
|
// ============================================================================
|
|
@@ -101,31 +101,27 @@ export async function selectAdoProjects(
|
|
|
101
101
|
console.log(` Total: ${allProjects.length} projects\n`);
|
|
102
102
|
|
|
103
103
|
// Decide selection method
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
: []),
|
|
126
|
-
],
|
|
127
|
-
},
|
|
128
|
-
]);
|
|
104
|
+
const selectionMethod = await select({
|
|
105
|
+
message: 'How would you like to select projects?',
|
|
106
|
+
choices: [
|
|
107
|
+
{
|
|
108
|
+
name: `📋 Interactive (browse and select from ${allProjects.length} projects)`,
|
|
109
|
+
value: 'interactive' as const,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: '✏️ Manual entry (type project names)',
|
|
113
|
+
value: 'manual' as const,
|
|
114
|
+
},
|
|
115
|
+
...(allowSelectAll
|
|
116
|
+
? [
|
|
117
|
+
{
|
|
118
|
+
name: `✨ Select all (${allProjects.length} projects)`,
|
|
119
|
+
value: 'all' as const,
|
|
120
|
+
},
|
|
121
|
+
]
|
|
122
|
+
: []),
|
|
123
|
+
],
|
|
124
|
+
});
|
|
129
125
|
|
|
130
126
|
if (selectionMethod === 'all') {
|
|
131
127
|
return {
|
|
@@ -160,45 +156,40 @@ async function interactiveProjectSelection(
|
|
|
160
156
|
): Promise<ProjectSelectionResult> {
|
|
161
157
|
console.log('💡 Use <space> to select, <a> to toggle all, <i> to invert\n');
|
|
162
158
|
|
|
163
|
-
const choices =
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
new inquirer.Separator(),
|
|
159
|
+
const choices = [
|
|
160
|
+
...allProjects.map((p) => ({
|
|
161
|
+
name: formatProjectChoice(p),
|
|
162
|
+
value: p.name,
|
|
163
|
+
checked: preSelected.includes(p.name),
|
|
164
|
+
})),
|
|
165
|
+
// Add separator and manual entry option at the end
|
|
166
|
+
new Separator(),
|
|
172
167
|
{
|
|
173
168
|
name: '✏️ Enter project names manually instead',
|
|
174
169
|
value: '__MANUAL__',
|
|
175
170
|
checked: false,
|
|
176
|
-
}
|
|
177
|
-
|
|
171
|
+
},
|
|
172
|
+
];
|
|
178
173
|
|
|
179
|
-
const
|
|
180
|
-
{
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return true;
|
|
199
|
-
},
|
|
174
|
+
const selectedNames = await checkbox({
|
|
175
|
+
message: `Select Azure DevOps projects (${minSelection}${maxSelection ? `-${maxSelection}` : '+'} required):`,
|
|
176
|
+
choices,
|
|
177
|
+
pageSize,
|
|
178
|
+
loop: false,
|
|
179
|
+
validate: (selected: readonly string[]) => {
|
|
180
|
+
const actualSelected = selected.filter((k) => k !== '__MANUAL__');
|
|
181
|
+
|
|
182
|
+
if (actualSelected.length < minSelection) {
|
|
183
|
+
return `Please select at least ${minSelection} project(s)`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (maxSelection && actualSelected.length > maxSelection) {
|
|
187
|
+
return `Please select at most ${maxSelection} project(s)`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return true;
|
|
200
191
|
},
|
|
201
|
-
|
|
192
|
+
});
|
|
202
193
|
|
|
203
194
|
// Check if user chose manual entry
|
|
204
195
|
if (selectedNames.includes('__MANUAL__')) {
|
|
@@ -235,33 +226,29 @@ async function manualProjectEntry(
|
|
|
235
226
|
console.log('');
|
|
236
227
|
}
|
|
237
228
|
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return true;
|
|
262
|
-
},
|
|
229
|
+
const manualNames = await input({
|
|
230
|
+
message: 'Project names:',
|
|
231
|
+
validate: (inputValue: string) => {
|
|
232
|
+
if (!inputValue.trim()) {
|
|
233
|
+
return 'Please enter at least one project name';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const names = inputValue
|
|
237
|
+
.split(',')
|
|
238
|
+
.map((n) => n.trim())
|
|
239
|
+
.filter((n) => n.length > 0);
|
|
240
|
+
|
|
241
|
+
if (names.length < minSelection) {
|
|
242
|
+
return `Please enter at least ${minSelection} project name(s)`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (maxSelection && names.length > maxSelection) {
|
|
246
|
+
return `Please enter at most ${maxSelection} project name(s)`;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return true;
|
|
263
250
|
},
|
|
264
|
-
|
|
251
|
+
});
|
|
265
252
|
|
|
266
253
|
const selectedNames = manualNames
|
|
267
254
|
.split(',')
|