universal-dev-standards 4.1.0 → 4.2.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.
Files changed (46) hide show
  1. package/bin/uds.js +75 -0
  2. package/bundled/core/ai-friendly-architecture.md +542 -0
  3. package/bundled/locales/zh-CN/README.md +210 -509
  4. package/bundled/locales/zh-CN/core/ai-friendly-architecture.md +306 -0
  5. package/bundled/locales/zh-CN/docs/AI-AGENT-ROADMAP.md +82 -22
  6. package/bundled/locales/zh-CN/integrations/gemini-cli/GEMINI.md +35 -3
  7. package/bundled/locales/zh-CN/integrations/github-copilot/COPILOT-CHAT-REFERENCE.md +89 -3
  8. package/bundled/locales/zh-CN/integrations/github-copilot/skills-mapping.md +8 -4
  9. package/bundled/locales/zh-TW/README.md +211 -490
  10. package/bundled/locales/zh-TW/core/ai-friendly-architecture.md +306 -0
  11. package/bundled/locales/zh-TW/docs/AI-AGENT-ROADMAP.md +82 -22
  12. package/bundled/locales/zh-TW/integrations/gemini-cli/GEMINI.md +35 -3
  13. package/bundled/locales/zh-TW/integrations/github-copilot/COPILOT-CHAT-REFERENCE.md +89 -3
  14. package/bundled/locales/zh-TW/integrations/github-copilot/skills-mapping.md +8 -4
  15. package/bundled/skills/claude-code/README.md +8 -0
  16. package/bundled/skills/claude-code/agents/README.md +305 -0
  17. package/bundled/skills/claude-code/agents/code-architect.md +259 -0
  18. package/bundled/skills/claude-code/agents/doc-writer.md +406 -0
  19. package/bundled/skills/claude-code/agents/reviewer.md +353 -0
  20. package/bundled/skills/claude-code/agents/spec-analyst.md +374 -0
  21. package/bundled/skills/claude-code/agents/test-specialist.md +364 -0
  22. package/bundled/skills/claude-code/workflows/README.md +303 -0
  23. package/bundled/skills/claude-code/workflows/code-review.workflow.yaml +186 -0
  24. package/bundled/skills/claude-code/workflows/feature-dev.workflow.yaml +174 -0
  25. package/bundled/skills/claude-code/workflows/integrated-flow.workflow.yaml +238 -0
  26. package/bundled/skills/claude-code/workflows/large-codebase-analysis.workflow.yaml +226 -0
  27. package/package.json +11 -1
  28. package/src/commands/agent.js +417 -0
  29. package/src/commands/ai-context.js +552 -0
  30. package/src/commands/check.js +3 -3
  31. package/src/commands/init.js +6 -3
  32. package/src/commands/workflow.js +425 -0
  33. package/src/config/ai-agent-paths.js +217 -13
  34. package/src/core/constants.js +514 -0
  35. package/src/core/errors.js +398 -0
  36. package/src/core/manifest.js +473 -0
  37. package/src/core/paths.js +398 -0
  38. package/src/prompts/init.js +7 -5
  39. package/src/utils/agent-adapter.js +320 -0
  40. package/src/utils/agents-installer.js +393 -0
  41. package/src/utils/context-chunker.js +467 -0
  42. package/src/utils/copier.js +59 -99
  43. package/src/utils/hasher.js +2 -16
  44. package/src/utils/integration-generator.js +28 -52
  45. package/src/utils/workflows-installer.js +545 -0
  46. package/standards-registry.json +166 -20
@@ -0,0 +1,398 @@
1
+ import { existsSync } from 'fs';
2
+ import { join, basename, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import {
5
+ DIRECTORIES,
6
+ FILE_PATTERNS,
7
+ FILE_EXTENSIONS as EXTENSIONS
8
+ } from './constants.js';
9
+
10
+ /**
11
+ * UDS Path Resolution System
12
+ * Centralized path management for all UDS operations
13
+ *
14
+ * Note: DIRECTORIES, FILE_PATTERNS, and EXTENSIONS are imported from constants.js
15
+ * to maintain a single source of truth for these values.
16
+ */
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+
21
+ // CLI package root (where package.json is located)
22
+ const CLI_ROOT = join(__dirname, '../..');
23
+
24
+ // Bundled files directory (for npm-installed CLI)
25
+ const BUNDLED_ROOT = join(CLI_ROOT, 'bundled');
26
+
27
+ // Root of universal-dev-standards repository (for local development)
28
+ const REPO_ROOT = join(CLI_ROOT, '..');
29
+
30
+ /**
31
+ * Path priority order for source files
32
+ */
33
+ export const PATH_PRIORITY = {
34
+ BUNDLED: 1, // npm-installed bundled files
35
+ REPO: 2, // local development repository
36
+ GITHUB: 3 // fallback download from GitHub
37
+ };
38
+
39
+ // Re-export DIRECTORIES and FILE_PATTERNS for backward compatibility
40
+ export { DIRECTORIES, FILE_PATTERNS, EXTENSIONS };
41
+
42
+ /**
43
+ * PathResolver class for centralized path management
44
+ */
45
+ export class PathResolver {
46
+ /**
47
+ * Get CLI root directory
48
+ * @returns {string} CLI root path
49
+ */
50
+ static getCliRoot() {
51
+ return CLI_ROOT;
52
+ }
53
+
54
+ /**
55
+ * Get repository root directory
56
+ * @returns {string} Repository root path
57
+ */
58
+ static getRepoRoot() {
59
+ return REPO_ROOT;
60
+ }
61
+
62
+ /**
63
+ * Get bundled files directory
64
+ * @returns {string} Bundled root path
65
+ */
66
+ static getBundledRoot() {
67
+ return BUNDLED_ROOT;
68
+ }
69
+
70
+ /**
71
+ * Get standard source file path with priority resolution
72
+ * @param {string} relativePath - Relative path from repo root (e.g., 'core/anti-hallucination.md')
73
+ * @returns {string|null} Absolute path to source file, or null if not found locally
74
+ */
75
+ static getStandardSource(relativePath) {
76
+ // Priority 1: Bundled files (for npm-installed CLI)
77
+ const bundledPath = join(BUNDLED_ROOT, relativePath);
78
+ if (existsSync(bundledPath)) {
79
+ return bundledPath;
80
+ }
81
+
82
+ // Priority 2: Repository root (for local development)
83
+ const repoPath = join(REPO_ROOT, relativePath);
84
+ if (existsSync(repoPath)) {
85
+ return repoPath;
86
+ }
87
+
88
+ return null; // Not found locally, will need to download
89
+ }
90
+
91
+ /**
92
+ * Get integration source file path
93
+ * @param {string} tool - AI tool name (e.g., 'claude-code')
94
+ * @param {string} relativePath - Relative path within tool directory
95
+ * @returns {string|null} Absolute path to integration file, or null if not found
96
+ */
97
+ static getIntegrationSource(tool, relativePath) {
98
+ const toolPath = join(DIRECTORIES.INTEGRATIONS, tool, relativePath);
99
+ return this.getStandardSource(toolPath);
100
+ }
101
+
102
+ /**
103
+ * Get skill source file path
104
+ * @param {string} agent - AI agent name (e.g., 'claude-code')
105
+ * @param {string} category - Skill category (e.g., 'agents', 'workflows')
106
+ * @param {string} fileName - File name
107
+ * @returns {string|null} Absolute path to skill file, or null if not found
108
+ */
109
+ static getSkillSource(agent, category, fileName) {
110
+ const skillPath = join(DIRECTORIES.SKILLS, agent, category, fileName);
111
+ return this.getStandardSource(skillPath);
112
+ }
113
+
114
+ /**
115
+ * Get locale source file path
116
+ * @param {string} locale - Locale code (e.g., 'zh-TW', 'zh-CN')
117
+ * @param {string} relativePath - Relative path within locale directory
118
+ * @returns {string|null} Absolute path to locale file, or null if not found
119
+ */
120
+ static getLocaleSource(locale, relativePath) {
121
+ const localePath = join(DIRECTORIES.LOCALES, locale, relativePath);
122
+ return this.getStandardSource(localePath);
123
+ }
124
+
125
+ /**
126
+ * Get target path for standard file in project
127
+ * @param {string} projectPath - Project root path
128
+ * @param {string} relativePath - Relative path from standards directory
129
+ * @returns {string} Absolute target path
130
+ */
131
+ static getStandardTarget(projectPath, relativePath) {
132
+ return join(projectPath, DIRECTORIES.STANDARDS, relativePath);
133
+ }
134
+
135
+ /**
136
+ * Get target path for integration file
137
+ * @param {string} projectPath - Project root path
138
+ * @param {string} tool - AI tool name
139
+ * @param {string} fileName - File name
140
+ * @returns {string} Absolute target path
141
+ */
142
+ static getIntegrationTarget(projectPath, tool, fileName) {
143
+ // Different tools use different target locations
144
+ const toolTargets = {
145
+ 'claude-code': 'CLAUDE.md',
146
+ 'cursor': '.cursorrules',
147
+ 'cline': '.clinerules',
148
+ 'windsurf': '.windsurfrules',
149
+ 'copilot': 'copilot-instructions.md',
150
+ 'aider': '.aider.conf.yml',
151
+ 'opencode': 'AGENTS.md',
152
+ 'roo': 'ROO.md',
153
+ 'antigravity': 'INSTRUCTIONS.md'
154
+ };
155
+
156
+ const targetFile = toolTargets[tool] || fileName;
157
+ return join(projectPath, targetFile);
158
+ }
159
+
160
+ /**
161
+ * Get target path for skill installation
162
+ * @param {string} projectPath - Project root path
163
+ * @param {string} agent - AI agent name
164
+ * @param {string} level - Installation level ('project' or 'user')
165
+ * @param {string} skillPath - Skill relative path
166
+ * @returns {string} Absolute target path
167
+ */
168
+ static getSkillTarget(projectPath, agent, level, skillPath) {
169
+ if (level === 'project') {
170
+ return join(projectPath, '.uds', 'skills', agent, skillPath);
171
+ } else {
172
+ // User-level installation (home directory)
173
+ const userHome = require('os').homedir();
174
+ return join(userHome, '.uds', 'skills', agent, skillPath);
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Get manifest file path for project
180
+ * @param {string} projectPath - Project root path
181
+ * @returns {string} Absolute path to manifest.json
182
+ */
183
+ static getManifestPath(projectPath) {
184
+ return join(projectPath, DIRECTORIES.STANDARDS, 'manifest.json');
185
+ }
186
+
187
+ /**
188
+ * Get standards directory path for project
189
+ * @param {string} projectPath - Project root path
190
+ * @returns {string} Absolute path to standards directory
191
+ */
192
+ static getStandardsDir(projectPath) {
193
+ return join(projectPath, DIRECTORIES.STANDARDS);
194
+ }
195
+
196
+ /**
197
+ * Get UDS working directory for project
198
+ * @param {string} projectPath - Project root path
199
+ * @returns {string} Absolute path to .uds directory
200
+ */
201
+ static getUDSDir(projectPath) {
202
+ return join(projectPath, '.uds');
203
+ }
204
+
205
+ /**
206
+ * Check if path exists locally (not counting GitHub downloads)
207
+ * @param {string} relativePath - Relative path to check
208
+ * @returns {boolean} True if file exists locally
209
+ */
210
+ static existsLocally(relativePath) {
211
+ return this.getStandardSource(relativePath) !== null;
212
+ }
213
+
214
+ /**
215
+ * Get relative path from project root
216
+ * @param {string} projectPath - Project root path
217
+ * @param {string} fullPath - Full path
218
+ * @returns {string} Relative path
219
+ */
220
+ static getRelativePath(projectPath, fullPath) {
221
+ return fullPath.replace(projectPath + '/', '');
222
+ }
223
+
224
+ /**
225
+ * Normalize path separators for current platform
226
+ * @param {string} path - Path to normalize
227
+ * @returns {string} Normalized path
228
+ */
229
+ static normalizePath(path) {
230
+ return path.replace(/\\/g, '/');
231
+ }
232
+
233
+ /**
234
+ * Join path segments safely
235
+ * @param {...string} segments - Path segments
236
+ * @returns {string} Joined path
237
+ */
238
+ static join(...segments) {
239
+ return join(...segments);
240
+ }
241
+
242
+ /**
243
+ * Get file extension from path
244
+ * @param {string} filePath - File path
245
+ * @returns {string} File extension (with dot)
246
+ */
247
+ static getExtension(filePath) {
248
+ return filePath.substring(filePath.lastIndexOf('.'));
249
+ }
250
+
251
+ /**
252
+ * Get file name without extension
253
+ * @param {string} filePath - File path
254
+ * @returns {string} File name without extension
255
+ */
256
+ static getBaseName(filePath) {
257
+ const name = basename(filePath);
258
+ const ext = this.getExtension(name);
259
+ return name.slice(0, -ext.length);
260
+ }
261
+
262
+ /**
263
+ * Check if file path indicates a standard file
264
+ * @param {string} filePath - File path
265
+ * @returns {boolean} True if it's a standard file
266
+ */
267
+ static isStandardFile(filePath) {
268
+ return filePath.includes(DIRECTORIES.CORE) ||
269
+ filePath.includes(DIRECTORIES.AI + '/standards');
270
+ }
271
+
272
+ /**
273
+ * Check if file path indicates a skill file
274
+ * @param {string} filePath - File path
275
+ * @returns {boolean} True if it's a skill file
276
+ */
277
+ static isSkillFile(filePath) {
278
+ return filePath.includes(DIRECTORIES.SKILLS);
279
+ }
280
+
281
+ /**
282
+ * Check if file path indicates an integration file
283
+ * @param {string} filePath - File path
284
+ * @returns {boolean} True if it's an integration file
285
+ */
286
+ static isIntegrationFile(filePath) {
287
+ return filePath.includes(DIRECTORIES.INTEGRATIONS);
288
+ }
289
+
290
+ /**
291
+ * Check if file path indicates a locale file
292
+ * @param {string} filePath - File path
293
+ * @returns {boolean} True if it's a locale file
294
+ */
295
+ static isLocaleFile(filePath) {
296
+ return filePath.includes(DIRECTORIES.LOCALES);
297
+ }
298
+
299
+ /**
300
+ * Get file type from path
301
+ * @param {string} filePath - File path
302
+ * @returns {string} File type: 'standard', 'skill', 'integration', 'locale', or 'unknown'
303
+ */
304
+ static getFileType(filePath) {
305
+ if (this.isStandardFile(filePath)) return 'standard';
306
+ if (this.isSkillFile(filePath)) return 'skill';
307
+ if (this.isIntegrationFile(filePath)) return 'integration';
308
+ if (this.isLocaleFile(filePath)) return 'locale';
309
+ return 'unknown';
310
+ }
311
+
312
+ /**
313
+ * Convert relative path to canonical form
314
+ * @param {string} relativePath - Relative path from repo root
315
+ * @returns {string} Canonical path
316
+ */
317
+ static canonicalizePath(relativePath) {
318
+ // Remove leading ./ if present
319
+ let canonical = relativePath.replace(/^\.\//, '');
320
+
321
+ // Ensure forward slashes
322
+ canonical = canonical.replace(/\\/g, '/');
323
+
324
+ // Remove trailing slash
325
+ canonical = canonical.replace(/\/$/, '');
326
+
327
+ return canonical;
328
+ }
329
+
330
+ /**
331
+ * Get all possible source locations for a file
332
+ * @param {string} relativePath - Relative path
333
+ * @returns {Array} Array of possible locations with priorities
334
+ */
335
+ static getAllPossibleLocations(relativePath) {
336
+ return [
337
+ {
338
+ path: join(BUNDLED_ROOT, relativePath),
339
+ priority: PATH_PRIORITY.BUNDLED,
340
+ type: 'bundled'
341
+ },
342
+ {
343
+ path: join(REPO_ROOT, relativePath),
344
+ priority: PATH_PRIORITY.REPO,
345
+ type: 'repo'
346
+ }
347
+ ];
348
+ }
349
+
350
+ /**
351
+ * Find first existing local file for a relative path
352
+ * @param {string} relativePath - Relative path
353
+ * @returns {Object|null} First existing location or null
354
+ */
355
+ static findFirstExisting(relativePath) {
356
+ const locations = this.getAllPossibleLocations(relativePath);
357
+
358
+ for (const location of locations) {
359
+ if (existsSync(location.path)) {
360
+ return location;
361
+ }
362
+ }
363
+
364
+ return null;
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Legacy compatibility functions
370
+ * These maintain compatibility with existing code
371
+ */
372
+
373
+ /**
374
+ * Get source file path (legacy compatibility)
375
+ * @param {string} sourcePath - Relative path
376
+ * @returns {string|null} Absolute path or null
377
+ */
378
+ export function getSourcePath(sourcePath) {
379
+ return PathResolver.getStandardSource(sourcePath);
380
+ }
381
+
382
+ /**
383
+ * Get repository root path (legacy compatibility)
384
+ * @returns {string} Repository root path
385
+ */
386
+ export function getRepoRoot() {
387
+ return PathResolver.getRepoRoot();
388
+ }
389
+
390
+ /**
391
+ * Get bundled files directory path (legacy compatibility)
392
+ * @returns {string} Bundled root path
393
+ */
394
+ export function getBundledRoot() {
395
+ return PathResolver.getBundledRoot();
396
+ }
397
+
398
+ export default PathResolver;
@@ -237,11 +237,13 @@ export async function promptCommandsInstallation(selectedTools = []) {
237
237
  const config = getAgentConfig(tool);
238
238
  const displayName = getAgentDisplayName(tool);
239
239
 
240
- // User level option
241
- choices.push({
242
- name: `${chalk.blue(displayName)} - ${msg.choices.userLevel} ${chalk.gray(`(${config.commands.user.replace(os.homedir(), '~')})`)}`,
243
- value: `${tool}:user`
244
- });
240
+ // User level option (skip if user path is null - e.g., Copilot only supports VS Code IDE)
241
+ if (config.commands.user) {
242
+ choices.push({
243
+ name: `${chalk.blue(displayName)} - ${msg.choices.userLevel} ${chalk.gray(`(${config.commands.user.replace(os.homedir(), '~')})`)}`,
244
+ value: `${tool}:user`
245
+ });
246
+ }
245
247
 
246
248
  // Project level option
247
249
  choices.push({