@thiagodiogo/pscode 1.0.0 → 1.0.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.
Files changed (84) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/index.js +6 -6
  3. package/dist/commands/config.d.ts +4 -12
  4. package/dist/commands/config.js +69 -242
  5. package/dist/core/change-metadata/schema.d.ts +1 -0
  6. package/dist/core/change-metadata/schema.js +1 -0
  7. package/dist/core/{archive.d.ts → complete.d.ts} +2 -2
  8. package/dist/core/{archive.js → complete.js} +28 -5
  9. package/dist/core/completions/command-registry.js +5 -5
  10. package/dist/core/config-schema.d.ts +1 -5
  11. package/dist/core/config-schema.js +2 -5
  12. package/dist/core/global-config.d.ts +1 -3
  13. package/dist/core/global-config.js +1 -1
  14. package/dist/core/init.d.ts +2 -0
  15. package/dist/core/init.js +81 -20
  16. package/dist/core/jira-transition.d.ts +16 -0
  17. package/dist/core/jira-transition.js +29 -0
  18. package/dist/core/migration.d.ts +3 -12
  19. package/dist/core/migration.js +10 -72
  20. package/dist/core/presets/dixi.d.ts +32 -0
  21. package/dist/core/presets/dixi.js +405 -0
  22. package/dist/core/profile-sync-drift.js +9 -1
  23. package/dist/core/profiles.d.ts +23 -21
  24. package/dist/core/profiles.js +28 -24
  25. package/dist/core/shared/skill-generation.js +3 -3
  26. package/dist/core/shared/tool-detection.d.ts +1 -1
  27. package/dist/core/shared/tool-detection.js +1 -1
  28. package/dist/core/templates/skill-templates.d.ts +1 -1
  29. package/dist/core/templates/skill-templates.js +1 -1
  30. package/dist/core/templates/workflows/apply-change.js +3 -3
  31. package/dist/core/templates/workflows/archive-change.d.ts +2 -2
  32. package/dist/core/templates/workflows/archive-change.js +10 -10
  33. package/dist/core/templates/workflows/onboard.js +9 -9
  34. package/dist/core/update.d.ts +1 -6
  35. package/dist/core/update.js +5 -29
  36. package/dist/core/workspace/foundation.d.ts +1 -1
  37. package/dist/core/workspace/foundation.js +1 -1
  38. package/dist/core/workspace/legacy-state.js +1 -1
  39. package/dist/core/workspace/skills.d.ts +4 -3
  40. package/dist/core/workspace/skills.js +3 -3
  41. package/package.json +4 -3
  42. package/pscode/content/dixi/architectures/feature-sliced-react/eslint-architecture.mjs.template +44 -0
  43. package/pscode/content/dixi/architectures/feature-sliced-react/features/README.md.template +30 -0
  44. package/pscode/content/dixi/architectures/feature-sliced-react/skeleton.yaml +8 -0
  45. package/pscode/content/dixi/architectures/hexagonal-spring/ArchitectureTest.java.template +41 -0
  46. package/pscode/content/dixi/architectures/hexagonal-spring/skeleton.yaml +11 -0
  47. package/pscode/content/dixi/claude-runtime/CLAUDE.md.java.template +62 -0
  48. package/pscode/content/dixi/claude-runtime/CLAUDE.md.react.template +74 -0
  49. package/pscode/content/dixi/claude-runtime/commands/adr.md +75 -0
  50. package/pscode/content/dixi/claude-runtime/commands/arch-check.md +64 -0
  51. package/pscode/content/dixi/claude-runtime/commands/dod.md +66 -0
  52. package/pscode/content/dixi/claude-runtime/commands/jira-draft.md +80 -0
  53. package/pscode/content/dixi/claude-runtime/commands/jira-setup.md +105 -0
  54. package/pscode/content/dixi/claude-runtime/commands/jira-sync.md +69 -0
  55. package/pscode/content/dixi/claude-runtime/commands/rfc.md +73 -0
  56. package/pscode/content/dixi/claude-runtime/hooks/arch-guard.mjs +101 -0
  57. package/pscode/content/dixi/claude-runtime/hooks/jira-context.mjs +60 -0
  58. package/pscode/content/dixi/claude-runtime/skills/pstld-arch-guardian.md +101 -0
  59. package/pscode/content/dixi/claude-runtime/skills/pstld-commit-crafter.md +98 -0
  60. package/pscode/content/dixi/claude-runtime/skills/pstld-jira-context.md +64 -0
  61. package/pscode/content/dixi/context/java/architecture.md +143 -0
  62. package/pscode/content/dixi/context/java/naming.md +62 -0
  63. package/pscode/content/dixi/context/java/testing.md +162 -0
  64. package/pscode/content/dixi/context/react/architecture.md +119 -0
  65. package/pscode/content/dixi/context/react/naming.md +129 -0
  66. package/pscode/content/dixi/context/react/testing.md +141 -0
  67. package/pscode/content/dixi/context/shared/commits.md +47 -0
  68. package/pscode/content/dixi/context/shared/dev-flow.md +53 -0
  69. package/pscode/content/dixi/context/shared/dod.md +38 -0
  70. package/pscode/content/dixi/context/shared/pr-flow.md +53 -0
  71. package/pscode/content/dixi/kit/java/.editorconfig +25 -0
  72. package/pscode/content/dixi/kit/java/.github/workflows/ci-java.yml +68 -0
  73. package/pscode/content/dixi/kit/java/.husky/commit-msg +2 -0
  74. package/pscode/content/dixi/kit/react/.editorconfig +20 -0
  75. package/pscode/content/dixi/kit/react/.github/workflows/ci-react.yml +80 -0
  76. package/pscode/content/dixi/kit/react/.husky/commit-msg +2 -0
  77. package/pscode/content/dixi/kit/react/.husky/pre-commit +2 -0
  78. package/pscode/content/dixi/kit/react/lint-staged.config.mjs +4 -0
  79. package/pscode/content/dixi/kit/shared/.commitlintrc.yml +15 -0
  80. package/pscode/content/dixi/kit/shared/.github/pull_request_template.md +24 -0
  81. package/schemas/pstld-workflow/schema.yaml +67 -0
  82. package/schemas/pstld-workflow/templates/design.md +15 -0
  83. package/schemas/pstld-workflow/templates/rfc.md +26 -0
  84. package/schemas/pstld-workflow/templates/tasks.md +15 -0
@@ -0,0 +1,405 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { parse as parseYaml } from 'yaml';
5
+ export function detectDixiStack(projectDir) {
6
+ if (fs.existsSync(path.join(projectDir, 'pom.xml')))
7
+ return 'java-maven';
8
+ if (fs.existsSync(path.join(projectDir, 'build.gradle')))
9
+ return 'java-gradle';
10
+ if (fs.existsSync(path.join(projectDir, 'next.config.js')) ||
11
+ fs.existsSync(path.join(projectDir, 'next.config.ts')) ||
12
+ fs.existsSync(path.join(projectDir, 'next.config.mjs')))
13
+ return 'next';
14
+ const pkgPath = path.join(projectDir, 'package.json');
15
+ if (fs.existsSync(pkgPath)) {
16
+ try {
17
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
18
+ const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
19
+ if ('next' in deps)
20
+ return 'next';
21
+ if ('react' in deps)
22
+ return 'react';
23
+ }
24
+ catch {
25
+ // fallback to 'node' when package.json has invalid JSON
26
+ }
27
+ return 'node';
28
+ }
29
+ if (fs.existsSync(path.join(projectDir, 'pyproject.toml')))
30
+ return 'python';
31
+ return null;
32
+ }
33
+ export function getDixiStackFamily(stack) {
34
+ if (stack === null)
35
+ return null;
36
+ if (stack === 'java-maven' || stack === 'java-gradle')
37
+ return 'java';
38
+ if (stack === 'next')
39
+ return 'react';
40
+ return stack;
41
+ }
42
+ export function getDixiStackLabel(stack) {
43
+ if (stack === null)
44
+ return 'desconhecida';
45
+ const labels = {
46
+ 'java-maven': 'Java/Maven',
47
+ 'java-gradle': 'Java/Gradle',
48
+ 'next': 'Next.js',
49
+ 'react': 'React',
50
+ 'node': 'Node.js',
51
+ 'python': 'Python',
52
+ };
53
+ return labels[stack];
54
+ }
55
+ /**
56
+ * Copies files recursively from sourceDir to targetDir.
57
+ * By default skips files that already exist (brownfield-safe).
58
+ * Files whose basename appears in options.overwrite are always overwritten.
59
+ */
60
+ export function copyKitFiles(sourceDir, targetDir, options = {}) {
61
+ if (!fs.existsSync(sourceDir))
62
+ return;
63
+ const overwriteSet = new Set(options.overwrite ?? []);
64
+ function copyDir(src, dest) {
65
+ if (!fs.existsSync(dest)) {
66
+ fs.mkdirSync(dest, { recursive: true });
67
+ }
68
+ const entries = fs.readdirSync(src, { withFileTypes: true });
69
+ for (const entry of entries) {
70
+ const srcPath = path.join(src, entry.name);
71
+ const destPath = path.join(dest, entry.name);
72
+ if (entry.isDirectory()) {
73
+ copyDir(srcPath, destPath);
74
+ }
75
+ else {
76
+ if (fs.existsSync(destPath) && !overwriteSet.has(entry.name)) {
77
+ console.log(` ${entry.name} já existe — pulado`);
78
+ continue;
79
+ }
80
+ const destDirPath = path.dirname(destPath);
81
+ if (!fs.existsSync(destDirPath)) {
82
+ fs.mkdirSync(destDirPath, { recursive: true });
83
+ }
84
+ fs.copyFileSync(srcPath, destPath);
85
+ }
86
+ }
87
+ }
88
+ copyDir(sourceDir, targetDir);
89
+ }
90
+ /**
91
+ * Copies all files from srcDir into <destRoot>/pastelsdd/context/, skipping files that already exist.
92
+ * Creates the destination directory if needed.
93
+ */
94
+ export function copyContextDocs(destRoot, srcDir) {
95
+ if (!fs.existsSync(srcDir))
96
+ return;
97
+ const contextDir = path.join(destRoot, 'pastelsdd', 'context');
98
+ if (!fs.existsSync(contextDir)) {
99
+ fs.mkdirSync(contextDir, { recursive: true });
100
+ }
101
+ const files = fs.readdirSync(srcDir);
102
+ for (const file of files) {
103
+ const src = path.join(srcDir, file);
104
+ const dest = path.join(contextDir, file);
105
+ if (fs.existsSync(dest)) {
106
+ console.log(` ${file} já existe — pulado`);
107
+ continue;
108
+ }
109
+ fs.copyFileSync(src, dest);
110
+ }
111
+ }
112
+ export function installDixiClaudeMd(projectDir, family) {
113
+ const currentFile = fileURLToPath(import.meta.url);
114
+ const packageRoot = path.join(path.dirname(currentFile), '..', '..', '..');
115
+ const claudeRuntimeDir = path.join(packageRoot, 'pscode', 'content', 'dixi', 'claude-runtime');
116
+ let templateName;
117
+ if (family === 'java') {
118
+ templateName = 'CLAUDE.md.java.template';
119
+ }
120
+ else if (family === 'react') {
121
+ templateName = 'CLAUDE.md.react.template';
122
+ }
123
+ else {
124
+ templateName = 'CLAUDE.md.java.template';
125
+ console.log('Dixi: stack não detectada, instalando CLAUDE.md genérico (baseado em Java). ' +
126
+ 'Edite .pscode-dixi.yaml para corrigir.');
127
+ }
128
+ const templatePath = path.join(claudeRuntimeDir, templateName);
129
+ const templateContent = fs.readFileSync(templatePath, 'utf-8');
130
+ const claudeMdPath = path.join(projectDir, 'CLAUDE.md');
131
+ const marker = '<!-- dixi-constitutional -->';
132
+ if (fs.existsSync(claudeMdPath)) {
133
+ const existing = fs.readFileSync(claudeMdPath, 'utf-8');
134
+ if (existing.includes(marker)) {
135
+ console.log('Dixi: CLAUDE.md já contém seção constitucional — pulando.');
136
+ return;
137
+ }
138
+ fs.writeFileSync(claudeMdPath, existing + '\n' + templateContent, { encoding: 'utf-8' });
139
+ }
140
+ else {
141
+ fs.writeFileSync(claudeMdPath, templateContent, { encoding: 'utf-8' });
142
+ }
143
+ }
144
+ const PSTLD_COMMANDS_SRC = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'pscode', 'content', 'dixi', 'claude-runtime', 'commands');
145
+ const PSTLD_SKILLS_SRC = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'pscode', 'content', 'dixi', 'claude-runtime', 'skills');
146
+ const HOOKS_SRC = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'pscode', 'content', 'dixi', 'claude-runtime', 'hooks');
147
+ const DIXI_HOOK_ENTRIES = [
148
+ { event: 'PreToolUse', matcher: 'Edit|Write', command: 'node .claude/hooks/arch-guard.mjs' },
149
+ { event: 'UserPromptSubmit', matcher: null, command: 'node .claude/hooks/jira-context.mjs' },
150
+ ];
151
+ function mergeSettingsHooks(settingsPath) {
152
+ let settings = {};
153
+ if (fs.existsSync(settingsPath)) {
154
+ try {
155
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
156
+ }
157
+ catch (e) {
158
+ console.log(`Dixi: settings.json inválido — criando novo arquivo. (${e instanceof Error ? e.message : String(e)})`);
159
+ settings = {};
160
+ }
161
+ }
162
+ if (!settings.hooks || typeof settings.hooks !== 'object' || Array.isArray(settings.hooks)) {
163
+ settings.hooks = {};
164
+ }
165
+ const hooks = settings.hooks;
166
+ for (const { event, matcher, command } of DIXI_HOOK_ENTRIES) {
167
+ if (!Array.isArray(hooks[event])) {
168
+ hooks[event] = [];
169
+ }
170
+ const eventHooks = hooks[event];
171
+ const alreadyExists = eventHooks.some(entry => Array.isArray(entry.hooks) && entry.hooks.some(h => h.command === command));
172
+ if (!alreadyExists) {
173
+ const entry = {
174
+ hooks: [{ type: 'command', command }],
175
+ };
176
+ if (matcher)
177
+ entry.matcher = matcher;
178
+ eventHooks.push(entry);
179
+ }
180
+ }
181
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), { encoding: 'utf-8' });
182
+ }
183
+ const ARCHITECTURES_BASE = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'pscode', 'content', 'dixi', 'architectures');
184
+ // Task 3.2
185
+ export function detectBasePackage(projectRoot) {
186
+ const pomPath = path.join(projectRoot, 'pom.xml');
187
+ if (!fs.existsSync(pomPath)) {
188
+ console.log('[dixi] detectBasePackage: pom.xml não encontrado — usando fallback com.example.app. Ajuste manualmente o basePackage no ArchitectureTest.java gerado.');
189
+ return 'com.example.app';
190
+ }
191
+ const pomContent = fs.readFileSync(pomPath, 'utf-8');
192
+ const groupIdMatch = pomContent.match(/<groupId>([^<]+)<\/groupId>/);
193
+ const artifactIdMatch = pomContent.match(/<artifactId>([^<]+)<\/artifactId>/);
194
+ if (!groupIdMatch || !artifactIdMatch) {
195
+ console.log('[dixi] detectBasePackage: groupId ou artifactId não encontrado no pom.xml — usando fallback com.example.app.');
196
+ return 'com.example.app';
197
+ }
198
+ const groupId = groupIdMatch[1].trim();
199
+ const artifactId = artifactIdMatch[1].trim().replace(/-/g, '');
200
+ return `${groupId}.${artifactId}`;
201
+ }
202
+ // Task 3.1
203
+ export function applyHexagonalSkeleton(projectRoot, basePackage) {
204
+ const skeletonPath = path.join(ARCHITECTURES_BASE, 'hexagonal-spring', 'skeleton.yaml');
205
+ if (!fs.existsSync(skeletonPath))
206
+ return { created: 0, skipped: 0 };
207
+ const skeletonData = parseYaml(fs.readFileSync(skeletonPath, 'utf-8'));
208
+ const basePackageDir = basePackage.replace(/\./g, '/');
209
+ let created = 0;
210
+ let skipped = 0;
211
+ for (const dir of skeletonData.dirs) {
212
+ const resolvedDir = dir.replace('{basePackageDir}', basePackageDir);
213
+ const dirPath = path.join(projectRoot, resolvedDir);
214
+ const gitkeepPath = path.join(dirPath, '.gitkeep');
215
+ if (!fs.existsSync(dirPath)) {
216
+ fs.mkdirSync(dirPath, { recursive: true });
217
+ }
218
+ if (!fs.existsSync(gitkeepPath)) {
219
+ fs.writeFileSync(gitkeepPath, '');
220
+ created++;
221
+ }
222
+ else {
223
+ skipped++;
224
+ }
225
+ }
226
+ return { created, skipped };
227
+ }
228
+ // Task 3.3
229
+ export function generateArchitectureTest(projectRoot, basePackage) {
230
+ const templatePath = path.join(ARCHITECTURES_BASE, 'hexagonal-spring', 'ArchitectureTest.java.template');
231
+ if (!fs.existsSync(templatePath))
232
+ return false;
233
+ const basePackageDir = basePackage.replace(/\./g, '/');
234
+ const destDir = path.join(projectRoot, 'src', 'test', 'java', basePackageDir);
235
+ const destPath = path.join(destDir, 'ArchitectureTest.java');
236
+ if (fs.existsSync(destPath)) {
237
+ console.log('[dixi] ArchitectureTest.java: arquivo existente — ignorado');
238
+ return false;
239
+ }
240
+ const template = fs.readFileSync(templatePath, 'utf-8');
241
+ const content = template.replace(/\{basePackage\}/g, basePackage);
242
+ if (!fs.existsSync(destDir)) {
243
+ fs.mkdirSync(destDir, { recursive: true });
244
+ }
245
+ fs.writeFileSync(destPath, content, { encoding: 'utf-8' });
246
+ return true;
247
+ }
248
+ // Task 3.4
249
+ export function applyFeatureSlicedSkeleton(projectRoot) {
250
+ const skeletonPath = path.join(ARCHITECTURES_BASE, 'feature-sliced-react', 'skeleton.yaml');
251
+ if (!fs.existsSync(skeletonPath))
252
+ return { created: 0, skipped: 0 };
253
+ const skeletonData = parseYaml(fs.readFileSync(skeletonPath, 'utf-8'));
254
+ const readmeTemplatePath = path.join(ARCHITECTURES_BASE, 'feature-sliced-react', 'features', 'README.md.template');
255
+ let created = 0;
256
+ let skipped = 0;
257
+ for (const dir of skeletonData.dirs) {
258
+ const dirPath = path.join(projectRoot, dir);
259
+ if (!fs.existsSync(dirPath)) {
260
+ fs.mkdirSync(dirPath, { recursive: true });
261
+ }
262
+ if (dir.endsWith('features')) {
263
+ const readmePath = path.join(dirPath, 'README.md');
264
+ if (!fs.existsSync(readmePath) && fs.existsSync(readmeTemplatePath)) {
265
+ fs.copyFileSync(readmeTemplatePath, readmePath);
266
+ created++;
267
+ }
268
+ else {
269
+ skipped++;
270
+ }
271
+ }
272
+ else {
273
+ const gitkeepPath = path.join(dirPath, '.gitkeep');
274
+ if (!fs.existsSync(gitkeepPath)) {
275
+ fs.writeFileSync(gitkeepPath, '');
276
+ created++;
277
+ }
278
+ else {
279
+ skipped++;
280
+ }
281
+ }
282
+ }
283
+ return { created, skipped };
284
+ }
285
+ // Task 3.5
286
+ export function installEslintArchitectureTemplate(projectRoot) {
287
+ const templatePath = path.join(ARCHITECTURES_BASE, 'feature-sliced-react', 'eslint-architecture.mjs.template');
288
+ if (!fs.existsSync(templatePath))
289
+ return;
290
+ const destPath = path.join(projectRoot, 'eslint-architecture.mjs');
291
+ if (!fs.existsSync(destPath)) {
292
+ fs.copyFileSync(templatePath, destPath);
293
+ }
294
+ console.log('\n[dixi] eslint-architecture.mjs instalado.\n' +
295
+ ' Para ativar as regras arquiteturais, adicione ao seu eslint.config.js:\n\n' +
296
+ ' import architectureRules from \'./eslint-architecture.mjs\';\n' +
297
+ ' export default [...existingConfig, ...architectureRules];\n');
298
+ }
299
+ export function installDixiExtras(projectDir, stack) {
300
+ const family = getDixiStackFamily(stack);
301
+ // Resolve package content root: dist/core/presets/ → package root → pscode/content/dixi/
302
+ const currentFile = fileURLToPath(import.meta.url);
303
+ const packageRoot = path.join(path.dirname(currentFile), '..', '..', '..');
304
+ const contentBase = path.join(packageRoot, 'pscode', 'content', 'dixi', 'context');
305
+ const kitBase = path.join(packageRoot, 'pscode', 'content', 'dixi', 'kit');
306
+ // Task 4.5: Ensure pastelsdd/context/ exists in the client repo
307
+ const contextDir = path.join(projectDir, 'pastelsdd', 'context');
308
+ if (!fs.existsSync(contextDir)) {
309
+ fs.mkdirSync(contextDir, { recursive: true });
310
+ }
311
+ // Task 4.2: Always copy shared/ docs
312
+ copyContextDocs(projectDir, path.join(contentBase, 'shared'));
313
+ if (family === null || family === 'node') {
314
+ console.log('Dixi: Stack não detectada — apenas docs compartilhados instalados. ' +
315
+ 'Configure `family` em `.pscode-dixi.yaml` para instalar docs específicos de stack.');
316
+ }
317
+ // Task 4.3: Copy java/ only for Java projects
318
+ if (family === 'java') {
319
+ copyContextDocs(projectDir, path.join(contentBase, 'java'));
320
+ }
321
+ // Task 4.4: Copy react/ only for React projects
322
+ if (family === 'react') {
323
+ copyContextDocs(projectDir, path.join(contentBase, 'react'));
324
+ }
325
+ // Task 4.2: Copy shared SDLC kit (always) — PR template always overwritten
326
+ copyKitFiles(path.join(kitBase, 'shared'), projectDir, {
327
+ overwrite: ['pull_request_template.md'],
328
+ });
329
+ // Task 4.3: Copy java SDLC kit
330
+ if (family === 'java') {
331
+ copyKitFiles(path.join(kitBase, 'java'), projectDir);
332
+ console.log('\nDixi [Java kit instalado]:\n' +
333
+ ' Adicione ao pom.xml:\n' +
334
+ ' • plugin commitlint: npm install --save-dev @commitlint/cli @commitlint/config-conventional commitlint-plugin-jira-rules\n' +
335
+ ' • plugin Jacoco: <groupId>org.jacoco</groupId><artifactId>jacoco-maven-plugin</artifactId>\n' +
336
+ ' • plugin Husky (se gerenciado via frontend): npx husky install\n' +
337
+ ' Atenção: o job archunit requer ArchitectureTest.java (gerado pelo pscode init skeleton).');
338
+ }
339
+ // Task 4.4: Copy react SDLC kit
340
+ if (family === 'react') {
341
+ copyKitFiles(path.join(kitBase, 'react'), projectDir);
342
+ console.log('\nDixi [React kit instalado]:\n' +
343
+ ' Execute:\n' +
344
+ ' npm install --save-dev @commitlint/cli @commitlint/config-conventional commitlint-plugin-jira-rules husky lint-staged prettier eslint\n' +
345
+ ' npx husky install\n' +
346
+ ' Adicione ao package.json: "prepare": "husky install"');
347
+ }
348
+ installDixiClaudeMd(projectDir, family);
349
+ // Task 3.6 + 3.7: Apply architectural skeleton by stack
350
+ if (family === 'java') {
351
+ const basePackage = detectBasePackage(projectDir);
352
+ const skeletonResult = applyHexagonalSkeleton(projectDir, basePackage);
353
+ console.log(`[dixi] skeleton hexagonal: ${skeletonResult.created} diretórios criados, ${skeletonResult.skipped} ignorados`);
354
+ const archTestCreated = generateArchitectureTest(projectDir, basePackage);
355
+ console.log(`[dixi] ArchitectureTest.java: ${archTestCreated ? 'criado' : 'ignorado (já existe)'}`);
356
+ }
357
+ else if (family === 'react') {
358
+ const skeletonResult = applyFeatureSlicedSkeleton(projectDir);
359
+ console.log(`[dixi] skeleton feature-sliced: ${skeletonResult.created} arquivos criados, ${skeletonResult.skipped} ignorados`);
360
+ installEslintArchitectureTemplate(projectDir);
361
+ }
362
+ else {
363
+ console.log('[dixi] skeleton arquitetural: stack não reconhecida — skeleton não disponível para esta stack');
364
+ }
365
+ // Copy /pstld:* slash commands to .claude/commands/pstld/ (idempotent — overwrites)
366
+ const pstldCommandsDir = path.join(projectDir, '.claude', 'commands', 'pstld');
367
+ if (!fs.existsSync(pstldCommandsDir)) {
368
+ fs.mkdirSync(pstldCommandsDir, { recursive: true });
369
+ }
370
+ if (fs.existsSync(PSTLD_COMMANDS_SRC)) {
371
+ const files = fs.readdirSync(PSTLD_COMMANDS_SRC);
372
+ for (const file of files) {
373
+ fs.copyFileSync(path.join(PSTLD_COMMANDS_SRC, file), path.join(pstldCommandsDir, file));
374
+ }
375
+ }
376
+ // Copy pstld-* skills to .claude/skills/<name>/SKILL.md (idempotent — overwrites)
377
+ if (fs.existsSync(PSTLD_SKILLS_SRC)) {
378
+ const skillFiles = fs.readdirSync(PSTLD_SKILLS_SRC).filter(f => f.endsWith('.md'));
379
+ for (const file of skillFiles) {
380
+ const skillName = file.replace(/\.md$/, '');
381
+ const skillDir = path.join(projectDir, '.claude', 'skills', skillName);
382
+ if (!fs.existsSync(skillDir)) {
383
+ fs.mkdirSync(skillDir, { recursive: true });
384
+ }
385
+ fs.copyFileSync(path.join(PSTLD_SKILLS_SRC, file), path.join(skillDir, 'SKILL.md'));
386
+ }
387
+ }
388
+ // Copy hooks to .claude/hooks/ (brownfield-safe: skip if file already exists)
389
+ const hooksDestDir = path.join(projectDir, '.claude', 'hooks');
390
+ if (!fs.existsSync(hooksDestDir)) {
391
+ fs.mkdirSync(hooksDestDir, { recursive: true });
392
+ }
393
+ if (fs.existsSync(HOOKS_SRC)) {
394
+ const hookFiles = fs.readdirSync(HOOKS_SRC).filter(f => f.endsWith('.mjs'));
395
+ for (const file of hookFiles) {
396
+ const dest = path.join(hooksDestDir, file);
397
+ if (!fs.existsSync(dest)) {
398
+ fs.copyFileSync(path.join(HOOKS_SRC, file), dest);
399
+ }
400
+ }
401
+ }
402
+ // Merge .claude/settings.json with hook registrations (never overwrite existing config)
403
+ mergeSettingsHooks(path.join(projectDir, '.claude', 'settings.json'));
404
+ }
405
+ //# sourceMappingURL=dixi.js.map
@@ -14,7 +14,7 @@ export const WORKFLOW_TO_SKILL_DIR = {
14
14
  'apply': 'pscode-apply-change',
15
15
  'ff': 'pscode-ff-change',
16
16
  'sync': 'pscode-sync-specs',
17
- 'archive': 'pscode-archive-change',
17
+ 'complete': 'pscode-archive-change',
18
18
  'bulk-archive': 'pscode-bulk-archive-change',
19
19
  'verify': 'pscode-verify-change',
20
20
  'onboard': 'pscode-onboard',
@@ -22,6 +22,14 @@ export const WORKFLOW_TO_SKILL_DIR = {
22
22
  // Trello-specific workflows
23
23
  'trello-setup': 'pscode-trello-setup',
24
24
  'draft': 'pscode-trello-draft',
25
+ // Dixi-specific workflows
26
+ 'rfc': 'pscode-dixi-rfc',
27
+ 'design': 'pscode-dixi-design',
28
+ 'tasks': 'pscode-dixi-tasks',
29
+ 'arch-check': 'pscode-dixi-arch-check',
30
+ 'adr': 'pscode-dixi-adr',
31
+ 'jira-sync': 'pscode-dixi-jira-sync',
32
+ 'dod': 'pscode-dixi-dod',
25
33
  };
26
34
  function toKnownWorkflows(workflows) {
27
35
  return workflows.filter((workflow) => ALL_WORKFLOWS.includes(workflow));
@@ -1,26 +1,28 @@
1
1
  /**
2
- * Profile System
2
+ * Predefined Workflow Profiles
3
3
  *
4
- * Defines workflow profiles that control which workflows are installed.
5
- * Profiles determine WHICH workflows; delivery (in global config) determines HOW.
4
+ * Add, remove or edit profiles here. Users select a profile via
5
+ * `pscode init --profile <name>` or `pscode config profile <name>`.
6
+ * The workflow lists are fixed in code — users cannot customise them.
6
7
  */
7
- import type { Profile } from './global-config.js';
8
- /**
9
- * Core workflows included in the 'core' profile.
10
- * These provide the streamlined experience for new users.
11
- */
12
- export declare const CORE_WORKFLOWS: readonly ["propose", "explore", "apply", "sync", "archive"];
13
- /**
14
- * All available workflows in the system.
15
- */
16
- export declare const ALL_WORKFLOWS: readonly ["propose", "explore", "new", "continue", "apply", "ff", "sync", "archive", "bulk-archive", "verify", "onboard", "trello-setup", "draft"];
8
+ export declare const ALL_WORKFLOWS: readonly ["propose", "explore", "new", "continue", "apply", "ff", "sync", "complete", "bulk-archive", "verify", "onboard", "trello-setup", "draft", "rfc", "design", "tasks", "arch-check", "adr", "jira-sync", "dod"];
17
9
  export type WorkflowId = (typeof ALL_WORKFLOWS)[number];
18
- export type CoreWorkflowId = (typeof CORE_WORKFLOWS)[number];
19
- /**
20
- * Resolves which workflows should be active for a given profile configuration.
21
- *
22
- * - 'core' profile always returns CORE_WORKFLOWS
23
- * - 'custom' profile returns the provided customWorkflows, or empty array if not provided
24
- */
25
- export declare function getProfileWorkflows(profile: Profile, customWorkflows?: string[]): readonly string[];
10
+ export interface ProfileDefinition {
11
+ description: string;
12
+ workflows: readonly WorkflowId[];
13
+ }
14
+ export declare const PROFILES: {
15
+ readonly standard: {
16
+ readonly description: "Padrão — propose, explore, apply, sync, complete";
17
+ readonly workflows: readonly ["propose", "explore", "apply", "sync", "complete"];
18
+ };
19
+ readonly dixi: {
20
+ readonly description: "Dixi — RFC→Design→Tasks→Apply com guardrails para Java/Spring e React/Next.js";
21
+ readonly workflows: readonly ["rfc", "design", "tasks", "apply", "arch-check", "adr", "jira-sync", "dod"];
22
+ };
23
+ };
24
+ export type ProfileName = keyof typeof PROFILES;
25
+ export declare const DEFAULT_PROFILE: ProfileName;
26
+ export declare function getProfileWorkflows(profile: ProfileName): readonly WorkflowId[];
27
+ export declare function isValidProfile(name: string): name is ProfileName;
26
28
  //# sourceMappingURL=profiles.d.ts.map
@@ -1,16 +1,9 @@
1
1
  /**
2
- * Profile System
2
+ * Predefined Workflow Profiles
3
3
  *
4
- * Defines workflow profiles that control which workflows are installed.
5
- * Profiles determine WHICH workflows; delivery (in global config) determines HOW.
6
- */
7
- /**
8
- * Core workflows included in the 'core' profile.
9
- * These provide the streamlined experience for new users.
10
- */
11
- export const CORE_WORKFLOWS = ['propose', 'explore', 'apply', 'sync', 'archive'];
12
- /**
13
- * All available workflows in the system.
4
+ * Add, remove or edit profiles here. Users select a profile via
5
+ * `pscode init --profile <name>` or `pscode config profile <name>`.
6
+ * The workflow lists are fixed in code — users cannot customise them.
14
7
  */
15
8
  export const ALL_WORKFLOWS = [
16
9
  'propose',
@@ -20,24 +13,35 @@ export const ALL_WORKFLOWS = [
20
13
  'apply',
21
14
  'ff',
22
15
  'sync',
23
- 'archive',
16
+ 'complete',
24
17
  'bulk-archive',
25
18
  'verify',
26
19
  'onboard',
27
- // Trello-specific workflows
28
20
  'trello-setup',
29
21
  'draft',
22
+ 'rfc',
23
+ 'design',
24
+ 'tasks',
25
+ 'arch-check',
26
+ 'adr',
27
+ 'jira-sync',
28
+ 'dod',
30
29
  ];
31
- /**
32
- * Resolves which workflows should be active for a given profile configuration.
33
- *
34
- * - 'core' profile always returns CORE_WORKFLOWS
35
- * - 'custom' profile returns the provided customWorkflows, or empty array if not provided
36
- */
37
- export function getProfileWorkflows(profile, customWorkflows) {
38
- if (profile === 'custom') {
39
- return customWorkflows ?? [];
40
- }
41
- return CORE_WORKFLOWS;
30
+ export const PROFILES = {
31
+ standard: {
32
+ description: 'Padrão — propose, explore, apply, sync, complete',
33
+ workflows: ['propose', 'explore', 'apply', 'sync', 'complete'],
34
+ },
35
+ dixi: {
36
+ description: 'Dixi RFC→Design→Tasks→Apply com guardrails para Java/Spring e React/Next.js',
37
+ workflows: ['rfc', 'design', 'tasks', 'apply', 'arch-check', 'adr', 'jira-sync', 'dod'],
38
+ },
39
+ };
40
+ export const DEFAULT_PROFILE = 'standard';
41
+ export function getProfileWorkflows(profile) {
42
+ return PROFILES[profile].workflows;
43
+ }
44
+ export function isValidProfile(name) {
45
+ return name in PROFILES;
42
46
  }
43
47
  //# sourceMappingURL=profiles.js.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Shared utilities for generating skill and command files.
5
5
  */
6
- import { getExploreSkillTemplate, getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getFfChangeSkillTemplate, getSyncSpecsSkillTemplate, getArchiveChangeSkillTemplate, getBulkArchiveChangeSkillTemplate, getVerifyChangeSkillTemplate, getOnboardSkillTemplate, getProposeSkillTemplate, getTrelloSetupSkillTemplate, getTrelloDraftSkillTemplate, getPsExploreCommandTemplate, getPsNewCommandTemplate, getPsContinueCommandTemplate, getPsApplyCommandTemplate, getPsFfCommandTemplate, getPsSyncCommandTemplate, getPsArchiveCommandTemplate, getPsBulkArchiveCommandTemplate, getPsVerifyCommandTemplate, getPsOnboardCommandTemplate, getPsProposeCommandTemplate, getTrelloSetupCommandTemplate, getTrelloDraftCommandTemplate, } from '../templates/skill-templates.js';
6
+ import { getExploreSkillTemplate, getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getFfChangeSkillTemplate, getSyncSpecsSkillTemplate, getCompleteChangeSkillTemplate, getBulkArchiveChangeSkillTemplate, getVerifyChangeSkillTemplate, getOnboardSkillTemplate, getProposeSkillTemplate, getTrelloSetupSkillTemplate, getTrelloDraftSkillTemplate, getPsExploreCommandTemplate, getPsNewCommandTemplate, getPsContinueCommandTemplate, getPsApplyCommandTemplate, getPsFfCommandTemplate, getPsSyncCommandTemplate, getPsCompleteCommandTemplate, getPsBulkArchiveCommandTemplate, getPsVerifyCommandTemplate, getPsOnboardCommandTemplate, getPsProposeCommandTemplate, getTrelloSetupCommandTemplate, getTrelloDraftCommandTemplate, } from '../templates/skill-templates.js';
7
7
  /**
8
8
  * Gets skill templates with their directory names, optionally filtered by workflow IDs.
9
9
  *
@@ -17,7 +17,7 @@ export function getSkillTemplates(workflowFilter) {
17
17
  { template: getApplyChangeSkillTemplate(), dirName: 'pscode-apply-change', workflowId: 'apply' },
18
18
  { template: getFfChangeSkillTemplate(), dirName: 'pscode-ff-change', workflowId: 'ff' },
19
19
  { template: getSyncSpecsSkillTemplate(), dirName: 'pscode-sync-specs', workflowId: 'sync' },
20
- { template: getArchiveChangeSkillTemplate(), dirName: 'pscode-archive-change', workflowId: 'archive' },
20
+ { template: getCompleteChangeSkillTemplate(), dirName: 'pscode-archive-change', workflowId: 'complete' },
21
21
  { template: getBulkArchiveChangeSkillTemplate(), dirName: 'pscode-bulk-archive-change', workflowId: 'bulk-archive' },
22
22
  { template: getVerifyChangeSkillTemplate(), dirName: 'pscode-verify-change', workflowId: 'verify' },
23
23
  { template: getOnboardSkillTemplate(), dirName: 'pscode-onboard', workflowId: 'onboard' },
@@ -44,7 +44,7 @@ export function getCommandTemplates(workflowFilter) {
44
44
  { template: getPsApplyCommandTemplate(), id: 'apply' },
45
45
  { template: getPsFfCommandTemplate(), id: 'ff' },
46
46
  { template: getPsSyncCommandTemplate(), id: 'sync' },
47
- { template: getPsArchiveCommandTemplate(), id: 'archive' },
47
+ { template: getPsCompleteCommandTemplate(), id: 'complete' },
48
48
  { template: getPsBulkArchiveCommandTemplate(), id: 'bulk-archive' },
49
49
  { template: getPsVerifyCommandTemplate(), id: 'verify' },
50
50
  { template: getPsOnboardCommandTemplate(), id: 'onboard' },
@@ -11,7 +11,7 @@ export type SkillName = (typeof SKILL_NAMES)[number];
11
11
  /**
12
12
  * IDs of command templates created by pscode init.
13
13
  */
14
- export declare const COMMAND_IDS: readonly ["explore", "new", "continue", "apply", "ff", "sync", "archive", "bulk-archive", "verify", "onboard", "propose"];
14
+ export declare const COMMAND_IDS: readonly ["explore", "new", "continue", "apply", "ff", "sync", "complete", "bulk-archive", "verify", "onboard", "propose"];
15
15
  export type CommandId = (typeof COMMAND_IDS)[number];
16
16
  /**
17
17
  * Status of skill configuration for a tool.
@@ -32,7 +32,7 @@ export const COMMAND_IDS = [
32
32
  'apply',
33
33
  'ff',
34
34
  'sync',
35
- 'archive',
35
+ 'complete',
36
36
  'bulk-archive',
37
37
  'verify',
38
38
  'onboard',
@@ -10,7 +10,7 @@ export { getContinueChangeSkillTemplate, getPsContinueCommandTemplate } from './
10
10
  export { getApplyChangeSkillTemplate, getPsApplyCommandTemplate } from './workflows/apply-change.js';
11
11
  export { getFfChangeSkillTemplate, getPsFfCommandTemplate } from './workflows/ff-change.js';
12
12
  export { getSyncSpecsSkillTemplate, getPsSyncCommandTemplate } from './workflows/sync-specs.js';
13
- export { getArchiveChangeSkillTemplate, getPsArchiveCommandTemplate } from './workflows/archive-change.js';
13
+ export { getCompleteChangeSkillTemplate, getPsCompleteCommandTemplate } from './workflows/archive-change.js';
14
14
  export { getBulkArchiveChangeSkillTemplate, getPsBulkArchiveCommandTemplate } from './workflows/bulk-archive-change.js';
15
15
  export { getVerifyChangeSkillTemplate, getPsVerifyCommandTemplate } from './workflows/verify-change.js';
16
16
  export { getOnboardSkillTemplate, getPsOnboardCommandTemplate } from './workflows/onboard.js';
@@ -9,7 +9,7 @@ export { getContinueChangeSkillTemplate, getPsContinueCommandTemplate } from './
9
9
  export { getApplyChangeSkillTemplate, getPsApplyCommandTemplate } from './workflows/apply-change.js';
10
10
  export { getFfChangeSkillTemplate, getPsFfCommandTemplate } from './workflows/ff-change.js';
11
11
  export { getSyncSpecsSkillTemplate, getPsSyncCommandTemplate } from './workflows/sync-specs.js';
12
- export { getArchiveChangeSkillTemplate, getPsArchiveCommandTemplate } from './workflows/archive-change.js';
12
+ export { getCompleteChangeSkillTemplate, getPsCompleteCommandTemplate } from './workflows/archive-change.js';
13
13
  export { getBulkArchiveChangeSkillTemplate, getPsBulkArchiveCommandTemplate } from './workflows/bulk-archive-change.js';
14
14
  export { getVerifyChangeSkillTemplate, getPsVerifyCommandTemplate } from './workflows/verify-change.js';
15
15
  export { getOnboardSkillTemplate, getPsOnboardCommandTemplate } from './workflows/onboard.js';
@@ -182,7 +182,7 @@ function getApplyInstructions() {
182
182
  Testado por: <usuario / Claude>
183
183
  Status: Funcionando
184
184
 
185
- Proximo passo: /ps:archive <name> para arquivar a change.
185
+ Proximo passo: /ps:complete <name> para arquivar a change.
186
186
  \`\`\`
187
187
 
188
188
  If any Trello call fails, continue — Trello is auxiliary, never blocking.
@@ -197,7 +197,7 @@ function getApplyInstructions() {
197
197
  Display:
198
198
  - Tasks completed this session
199
199
  - Overall progress: "N/M tasks complete"
200
- - If all done and approved: mention Trello stage (Em Teste or Ready to Deploy) and suggest \`/ps:archive\`
200
+ - If all done and approved: mention Trello stage (Em Teste or Ready to Deploy) and suggest \`/ps:complete\`
201
201
  - If paused: explain why and wait for guidance
202
202
 
203
203
  **Output During Implementation**
@@ -240,7 +240,7 @@ All tasks complete! How would you like to validate the implementation?
240
240
  **Change:** <change-name>
241
241
  **Trello:** Card moved to 🚀 Ready to Deploy ← only shown if lists.deploy is configured
242
242
 
243
- Ready to archive with \`/ps:archive\`.
243
+ Ready to archive with \`/ps:complete\`.
244
244
  \`\`\`
245
245
 
246
246
  **Output On Pause (Issue Encountered)**
@@ -5,6 +5,6 @@
5
5
  * templates file into workflow-focused modules.
6
6
  */
7
7
  import type { SkillTemplate, CommandTemplate } from '../types.js';
8
- export declare function getArchiveChangeSkillTemplate(): SkillTemplate;
9
- export declare function getPsArchiveCommandTemplate(): CommandTemplate;
8
+ export declare function getCompleteChangeSkillTemplate(): SkillTemplate;
9
+ export declare function getPsCompleteCommandTemplate(): CommandTemplate;
10
10
  //# sourceMappingURL=archive-change.d.ts.map