@thiagodiogo/pscode 1.0.0 → 2.0.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 (87) hide show
  1. package/README.md +2 -2
  2. package/bin/pscode.js +0 -0
  3. package/dist/cli/index.js +6 -7
  4. package/dist/commands/config.d.ts +4 -12
  5. package/dist/commands/config.js +69 -242
  6. package/dist/core/change-metadata/schema.d.ts +1 -0
  7. package/dist/core/change-metadata/schema.js +1 -0
  8. package/dist/core/{archive.d.ts → complete.d.ts} +2 -3
  9. package/dist/core/{archive.js → complete.js} +63 -64
  10. package/dist/core/completions/command-registry.js +5 -9
  11. package/dist/core/config-schema.d.ts +1 -5
  12. package/dist/core/config-schema.js +2 -5
  13. package/dist/core/global-config.d.ts +1 -3
  14. package/dist/core/global-config.js +1 -1
  15. package/dist/core/init.d.ts +2 -0
  16. package/dist/core/init.js +81 -21
  17. package/dist/core/jira-transition.d.ts +16 -0
  18. package/dist/core/jira-transition.js +29 -0
  19. package/dist/core/migration.d.ts +3 -12
  20. package/dist/core/migration.js +10 -72
  21. package/dist/core/presets/dixi.d.ts +32 -0
  22. package/dist/core/presets/dixi.js +405 -0
  23. package/dist/core/profile-sync-drift.js +9 -2
  24. package/dist/core/profiles.d.ts +23 -21
  25. package/dist/core/profiles.js +28 -25
  26. package/dist/core/shared/skill-generation.js +3 -5
  27. package/dist/core/shared/tool-detection.d.ts +2 -2
  28. package/dist/core/shared/tool-detection.js +1 -3
  29. package/dist/core/templates/skill-templates.d.ts +1 -2
  30. package/dist/core/templates/skill-templates.js +1 -2
  31. package/dist/core/templates/workflows/apply-change.js +3 -3
  32. package/dist/core/templates/workflows/archive-change.d.ts +2 -2
  33. package/dist/core/templates/workflows/archive-change.js +10 -10
  34. package/dist/core/templates/workflows/onboard.js +9 -9
  35. package/dist/core/update.d.ts +1 -6
  36. package/dist/core/update.js +5 -29
  37. package/dist/core/workspace/foundation.d.ts +1 -1
  38. package/dist/core/workspace/foundation.js +1 -1
  39. package/dist/core/workspace/legacy-state.js +1 -1
  40. package/dist/core/workspace/skills.d.ts +4 -3
  41. package/dist/core/workspace/skills.js +3 -3
  42. package/package.json +21 -22
  43. package/pscode/content/dixi/architectures/feature-sliced-react/eslint-architecture.mjs.template +44 -0
  44. package/pscode/content/dixi/architectures/feature-sliced-react/features/README.md.template +30 -0
  45. package/pscode/content/dixi/architectures/feature-sliced-react/skeleton.yaml +8 -0
  46. package/pscode/content/dixi/architectures/hexagonal-spring/ArchitectureTest.java.template +41 -0
  47. package/pscode/content/dixi/architectures/hexagonal-spring/skeleton.yaml +11 -0
  48. package/pscode/content/dixi/claude-runtime/CLAUDE.md.java.template +62 -0
  49. package/pscode/content/dixi/claude-runtime/CLAUDE.md.react.template +74 -0
  50. package/pscode/content/dixi/claude-runtime/commands/adr.md +75 -0
  51. package/pscode/content/dixi/claude-runtime/commands/arch-check.md +64 -0
  52. package/pscode/content/dixi/claude-runtime/commands/dod.md +66 -0
  53. package/pscode/content/dixi/claude-runtime/commands/jira-draft.md +80 -0
  54. package/pscode/content/dixi/claude-runtime/commands/jira-setup.md +105 -0
  55. package/pscode/content/dixi/claude-runtime/commands/jira-sync.md +69 -0
  56. package/pscode/content/dixi/claude-runtime/commands/rfc.md +73 -0
  57. package/pscode/content/dixi/claude-runtime/hooks/arch-guard.mjs +101 -0
  58. package/pscode/content/dixi/claude-runtime/hooks/jira-context.mjs +60 -0
  59. package/pscode/content/dixi/claude-runtime/skills/pstld-arch-guardian.md +101 -0
  60. package/pscode/content/dixi/claude-runtime/skills/pstld-commit-crafter.md +98 -0
  61. package/pscode/content/dixi/claude-runtime/skills/pstld-jira-context.md +64 -0
  62. package/pscode/content/dixi/context/java/architecture.md +143 -0
  63. package/pscode/content/dixi/context/java/naming.md +62 -0
  64. package/pscode/content/dixi/context/java/testing.md +162 -0
  65. package/pscode/content/dixi/context/react/architecture.md +119 -0
  66. package/pscode/content/dixi/context/react/naming.md +129 -0
  67. package/pscode/content/dixi/context/react/testing.md +141 -0
  68. package/pscode/content/dixi/context/shared/commits.md +47 -0
  69. package/pscode/content/dixi/context/shared/dev-flow.md +53 -0
  70. package/pscode/content/dixi/context/shared/dod.md +38 -0
  71. package/pscode/content/dixi/context/shared/pr-flow.md +53 -0
  72. package/pscode/content/dixi/kit/java/.editorconfig +25 -0
  73. package/pscode/content/dixi/kit/java/.github/workflows/ci-java.yml +68 -0
  74. package/pscode/content/dixi/kit/java/.husky/commit-msg +2 -0
  75. package/pscode/content/dixi/kit/react/.editorconfig +20 -0
  76. package/pscode/content/dixi/kit/react/.github/workflows/ci-react.yml +80 -0
  77. package/pscode/content/dixi/kit/react/.husky/commit-msg +2 -0
  78. package/pscode/content/dixi/kit/react/.husky/pre-commit +2 -0
  79. package/pscode/content/dixi/kit/react/lint-staged.config.mjs +4 -0
  80. package/pscode/content/dixi/kit/shared/.commitlintrc.yml +15 -0
  81. package/pscode/content/dixi/kit/shared/.github/pull_request_template.md +24 -0
  82. package/schemas/pstld-workflow/schema.yaml +67 -0
  83. package/schemas/pstld-workflow/templates/design.md +15 -0
  84. package/schemas/pstld-workflow/templates/rfc.md +26 -0
  85. package/schemas/pstld-workflow/templates/tasks.md +15 -0
  86. package/dist/core/templates/workflows/sync-specs.d.ts +0 -10
  87. package/dist/core/templates/workflows/sync-specs.js +0 -290
@@ -4,6 +4,8 @@ import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progre
4
4
  import { Validator } from './validation/validator.js';
5
5
  import chalk from 'chalk';
6
6
  import { findSpecUpdates, buildUpdatedSpec, writeUpdatedSpec, } from './specs-apply.js';
7
+ import { readChangeMetadata } from '../utils/change-metadata.js';
8
+ import { readJiraConfig, tryTransitionJiraIssue } from './jira-transition.js';
7
9
  /**
8
10
  * Recursively copy a directory. Used when fs.rename fails (e.g. EPERM on Windows).
9
11
  */
@@ -42,7 +44,7 @@ async function moveDirectory(src, dest) {
42
44
  }
43
45
  }
44
46
  }
45
- export class ArchiveCommand {
47
+ export class CompleteCommand {
46
48
  async execute(changeName, options = {}) {
47
49
  const targetPath = '.';
48
50
  const changesDir = path.join(targetPath, 'pscode', 'changes');
@@ -149,7 +151,7 @@ export class ArchiveCommand {
149
151
  default: false
150
152
  });
151
153
  if (!proceed) {
152
- console.log('Archive cancelled.');
154
+ console.log('Complete cancelled.');
153
155
  return;
154
156
  }
155
157
  }
@@ -172,7 +174,7 @@ export class ArchiveCommand {
172
174
  default: false
173
175
  });
174
176
  if (!proceed) {
175
- console.log('Archive cancelled.');
177
+ console.log('Complete cancelled.');
176
178
  return;
177
179
  }
178
180
  }
@@ -180,73 +182,49 @@ export class ArchiveCommand {
180
182
  console.log(`Warning: ${incompleteTasks} incomplete task(s) found. Continuing due to --yes flag.`);
181
183
  }
182
184
  }
183
- // Handle spec updates unless skipSpecs flag is set
184
- if (options.skipSpecs) {
185
- console.log('Skipping spec updates (--skip-specs flag provided).');
186
- }
187
- else {
188
- // Find specs to update
189
- const specUpdates = await findSpecUpdates(changeDir, mainSpecsDir);
190
- if (specUpdates.length > 0) {
191
- console.log('\nSpecs to update:');
185
+ // Sync delta specs to main specs
186
+ const specUpdates = await findSpecUpdates(changeDir, mainSpecsDir);
187
+ if (specUpdates.length > 0) {
188
+ console.log('\nSincronizando specs...');
189
+ // Prepare all updates first (validation pass, no writes)
190
+ const prepared = [];
191
+ try {
192
192
  for (const update of specUpdates) {
193
- const status = update.exists ? 'update' : 'create';
194
- const capability = path.basename(path.dirname(update.target));
195
- console.log(` ${capability}: ${status}`);
196
- }
197
- let shouldUpdateSpecs = true;
198
- if (!options.yes) {
199
- const { confirm } = await import('@inquirer/prompts');
200
- shouldUpdateSpecs = await confirm({
201
- message: 'Proceed with spec updates?',
202
- default: true
203
- });
204
- if (!shouldUpdateSpecs) {
205
- console.log('Skipping spec updates. Proceeding with archive.');
206
- }
193
+ const built = await buildUpdatedSpec(update, changeName);
194
+ prepared.push({ update, rebuilt: built.rebuilt, counts: built.counts });
207
195
  }
208
- if (shouldUpdateSpecs) {
209
- // Prepare all updates first (validation pass, no writes)
210
- const prepared = [];
211
- try {
212
- for (const update of specUpdates) {
213
- const built = await buildUpdatedSpec(update, changeName);
214
- prepared.push({ update, rebuilt: built.rebuilt, counts: built.counts });
196
+ }
197
+ catch (err) {
198
+ console.log(String(err.message || err));
199
+ console.log('Aborted. No files were changed.');
200
+ return;
201
+ }
202
+ // All validations passed; pre-validate rebuilt full spec and then write files and display counts
203
+ let totals = { added: 0, modified: 0, removed: 0, renamed: 0 };
204
+ for (const p of prepared) {
205
+ const specName = path.basename(path.dirname(p.update.target));
206
+ if (!skipValidation) {
207
+ const report = await new Validator().validateSpecContent(specName, p.rebuilt);
208
+ if (!report.valid) {
209
+ console.log(chalk.red(`\nValidation errors in rebuilt spec for ${specName} (will not write changes):`));
210
+ for (const issue of report.issues) {
211
+ if (issue.level === 'ERROR')
212
+ console.log(chalk.red(` ✗ ${issue.message}`));
213
+ else if (issue.level === 'WARNING')
214
+ console.log(chalk.yellow(` ⚠ ${issue.message}`));
215
215
  }
216
- }
217
- catch (err) {
218
- console.log(String(err.message || err));
219
216
  console.log('Aborted. No files were changed.');
220
217
  return;
221
218
  }
222
- // All validations passed; pre-validate rebuilt full spec and then write files and display counts
223
- let totals = { added: 0, modified: 0, removed: 0, renamed: 0 };
224
- for (const p of prepared) {
225
- const specName = path.basename(path.dirname(p.update.target));
226
- if (!skipValidation) {
227
- const report = await new Validator().validateSpecContent(specName, p.rebuilt);
228
- if (!report.valid) {
229
- console.log(chalk.red(`\nValidation errors in rebuilt spec for ${specName} (will not write changes):`));
230
- for (const issue of report.issues) {
231
- if (issue.level === 'ERROR')
232
- console.log(chalk.red(` ✗ ${issue.message}`));
233
- else if (issue.level === 'WARNING')
234
- console.log(chalk.yellow(` ⚠ ${issue.message}`));
235
- }
236
- console.log('Aborted. No files were changed.');
237
- return;
238
- }
239
- }
240
- await writeUpdatedSpec(p.update, p.rebuilt, p.counts);
241
- totals.added += p.counts.added;
242
- totals.modified += p.counts.modified;
243
- totals.removed += p.counts.removed;
244
- totals.renamed += p.counts.renamed;
245
- }
246
- console.log(`Totals: + ${totals.added}, ~ ${totals.modified}, - ${totals.removed}, → ${totals.renamed}`);
247
- console.log('Specs updated successfully.');
248
219
  }
220
+ await writeUpdatedSpec(p.update, p.rebuilt, p.counts);
221
+ totals.added += p.counts.added;
222
+ totals.modified += p.counts.modified;
223
+ totals.removed += p.counts.removed;
224
+ totals.renamed += p.counts.renamed;
249
225
  }
226
+ console.log(`Totals: + ${totals.added}, ~ ${totals.modified}, - ${totals.removed}, → ${totals.renamed}`);
227
+ console.log('Specs updated successfully.');
250
228
  }
251
229
  // Create archive directory with date prefix
252
230
  const archiveName = `${this.getArchiveDate()}-${changeName}`;
@@ -263,6 +241,27 @@ export class ArchiveCommand {
263
241
  }
264
242
  // Create archive directory if needed
265
243
  await fs.mkdir(archiveDir, { recursive: true });
244
+ // Attempt JIRA transition before moving (non-fatal)
245
+ try {
246
+ const changeMetadata = readChangeMetadata(changeDir, targetPath);
247
+ if (changeMetadata?.jiraIssueKey) {
248
+ const jiraConfig = await readJiraConfig(targetPath);
249
+ const transitionId = jiraConfig?.transitions?.done;
250
+ if (transitionId) {
251
+ const result = await tryTransitionJiraIssue(changeMetadata.jiraIssueKey, transitionId);
252
+ if (result.warning) {
253
+ console.log(chalk.yellow(`JIRA: ${result.warning}`));
254
+ }
255
+ }
256
+ else {
257
+ console.log(chalk.yellow(`JIRA: jiraIssueKey "${changeMetadata.jiraIssueKey}" encontrado, mas transitions.done não está configurado em pastelsdd/jira.yaml. ` +
258
+ `Execute /pstld:jira-setup para configurar.`));
259
+ }
260
+ }
261
+ }
262
+ catch {
263
+ // JIRA transition is non-fatal — never block the archive
264
+ }
266
265
  // Move change to archive (uses copy+remove on EPERM/EXDEV, e.g. Windows)
267
266
  await moveDirectory(changeDir, archivePath);
268
267
  console.log(`Change '${changeName}' archived as '${archiveName}'.`);
@@ -300,7 +299,7 @@ export class ArchiveCommand {
300
299
  }
301
300
  try {
302
301
  const answer = await select({
303
- message: 'Select a change to archive',
302
+ message: 'Select a change to complete',
304
303
  choices
305
304
  });
306
305
  return answer;
@@ -315,4 +314,4 @@ export class ArchiveCommand {
315
314
  return new Date().toISOString().split('T')[0];
316
315
  }
317
316
  }
318
- //# sourceMappingURL=archive.js.map
317
+ //# sourceMappingURL=complete.js.map
@@ -18,9 +18,9 @@ export const COMMAND_REGISTRY = [
18
18
  },
19
19
  {
20
20
  name: 'profile',
21
- description: 'Override global config profile (core or custom)',
21
+ description: 'Override global config profile (standard or dixi)',
22
22
  takesValue: true,
23
- values: ['core', 'custom'],
23
+ values: ['standard', 'dixi'],
24
24
  },
25
25
  ],
26
26
  },
@@ -128,8 +128,8 @@ export const COMMAND_REGISTRY = [
128
128
  ],
129
129
  },
130
130
  {
131
- name: 'archive',
132
- description: 'Archive a completed change and update main specs',
131
+ name: 'complete',
132
+ description: 'Complete a change and update main specs',
133
133
  acceptsPositional: true,
134
134
  positionalType: 'change-id',
135
135
  positionals: [{ name: 'change-name', type: 'change-id', optional: true }],
@@ -139,10 +139,6 @@ export const COMMAND_REGISTRY = [
139
139
  short: 'y',
140
140
  description: 'Skip confirmation prompts',
141
141
  },
142
- {
143
- name: 'skip-specs',
144
- description: 'Skip spec update operations',
145
- },
146
142
  {
147
143
  name: 'no-validate',
148
144
  description: 'Skip validation (not recommended)',
@@ -847,7 +843,7 @@ export const COMMAND_REGISTRY = [
847
843
  name: 'profile',
848
844
  description: 'Configure workflow profile (interactive picker or preset shortcut)',
849
845
  acceptsPositional: true,
850
- positionals: [{ name: 'preset', optional: true }],
846
+ positionals: [{ name: 'name', optional: true }],
851
847
  flags: [],
852
848
  },
853
849
  ],
@@ -5,16 +5,12 @@ import { z } from 'zod';
5
5
  */
6
6
  export declare const GlobalConfigSchema: z.ZodObject<{
7
7
  featureFlags: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>>;
8
- profile: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
9
- core: "core";
10
- custom: "custom";
11
- }>>>;
8
+ profile: z.ZodDefault<z.ZodOptional<z.ZodString>>;
12
9
  delivery: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
13
10
  commands: "commands";
14
11
  skills: "skills";
15
12
  both: "both";
16
13
  }>>>;
17
- workflows: z.ZodOptional<z.ZodArray<z.ZodString>>;
18
14
  }, z.core.$loose>;
19
15
  export type GlobalConfigType = z.infer<typeof GlobalConfigSchema>;
20
16
  /**
@@ -10,16 +10,13 @@ export const GlobalConfigSchema = z
10
10
  .optional()
11
11
  .default({}),
12
12
  profile: z
13
- .enum(['core', 'custom'])
13
+ .string()
14
14
  .optional()
15
15
  .default('core'),
16
16
  delivery: z
17
17
  .enum(['both', 'skills', 'commands'])
18
18
  .optional()
19
19
  .default('both'),
20
- workflows: z
21
- .array(z.string())
22
- .optional(),
23
20
  })
24
21
  .passthrough();
25
22
  /**
@@ -30,7 +27,7 @@ export const DEFAULT_CONFIG = {
30
27
  profile: 'core',
31
28
  delivery: 'both',
32
29
  };
33
- const KNOWN_TOP_LEVEL_KEYS = new Set([...Object.keys(DEFAULT_CONFIG), 'workflows']);
30
+ const KNOWN_TOP_LEVEL_KEYS = new Set([...Object.keys(DEFAULT_CONFIG)]);
34
31
  /**
35
32
  * Validate a config key path for CLI set operations.
36
33
  * Unknown top-level keys are rejected unless explicitly allowed by the caller.
@@ -1,13 +1,11 @@
1
1
  export declare const GLOBAL_CONFIG_DIR_NAME = "pscode";
2
2
  export declare const GLOBAL_CONFIG_FILE_NAME = "config.json";
3
3
  export declare const GLOBAL_DATA_DIR_NAME = "pscode";
4
- export type Profile = 'core' | 'custom';
5
4
  export type Delivery = 'both' | 'skills' | 'commands';
6
5
  export interface GlobalConfig {
7
6
  featureFlags?: Record<string, boolean>;
8
- profile?: Profile;
7
+ profile?: string;
9
8
  delivery?: Delivery;
10
- workflows?: string[];
11
9
  }
12
10
  /**
13
11
  * Gets the global configuration directory path following XDG Base Directory Specification.
@@ -7,7 +7,7 @@ export const GLOBAL_CONFIG_FILE_NAME = 'config.json';
7
7
  export const GLOBAL_DATA_DIR_NAME = 'pscode';
8
8
  const DEFAULT_CONFIG = {
9
9
  featureFlags: {},
10
- profile: 'core',
10
+ profile: 'standard',
11
11
  delivery: 'both',
12
12
  };
13
13
  /**
@@ -32,6 +32,8 @@ export declare class InitCommand {
32
32
  private generateSkillsAndCommands;
33
33
  private createConfig;
34
34
  private displaySuccessMessage;
35
+ private generateJiraFiles;
36
+ private handleDixiExtras;
35
37
  private startSpinner;
36
38
  private removeSkillDirs;
37
39
  private removeCommandFiles;
package/dist/core/init.js CHANGED
@@ -21,7 +21,9 @@ import { detectLegacyToolArtifacts, runLegacyToolMigration, formatLegacyToolDete
21
21
  import { runTrelloInitPrompt } from './trello-init-prompt.js';
22
22
  import { getToolsWithSkillsDir, getToolStates, getSkillTemplates, getCommandContents, generateSkillContent, } from './shared/index.js';
23
23
  import { getGlobalConfig } from './global-config.js';
24
- import { getProfileWorkflows, ALL_WORKFLOWS } from './profiles.js';
24
+ import { getProfileWorkflows, isValidProfile, DEFAULT_PROFILE, PROFILES, ALL_WORKFLOWS } from './profiles.js';
25
+ import { detectDixiStack, getDixiStackFamily, getDixiStackLabel, installDixiExtras } from './presets/dixi.js';
26
+ import { stringify as stringifyYaml } from 'yaml';
25
27
  import { getAvailableTools } from './available-tools.js';
26
28
  import { migrateIfNeeded } from './migration.js';
27
29
  const require = createRequire(import.meta.url);
@@ -40,8 +42,7 @@ const WORKFLOW_TO_SKILL_DIR = {
40
42
  'continue': 'pscode-continue-change',
41
43
  'apply': 'pscode-apply-change',
42
44
  'ff': 'pscode-ff-change',
43
- 'sync': 'pscode-sync-specs',
44
- 'archive': 'pscode-archive-change',
45
+ 'complete': 'pscode-archive-change',
45
46
  'bulk-archive': 'pscode-bulk-archive-change',
46
47
  'verify': 'pscode-verify-change',
47
48
  'onboard': 'pscode-onboard',
@@ -49,6 +50,14 @@ const WORKFLOW_TO_SKILL_DIR = {
49
50
  // Trello-specific workflows
50
51
  'trello-setup': 'pscode-trello-setup',
51
52
  'draft': 'pscode-trello-draft',
53
+ // Dixi-specific workflows
54
+ 'rfc': 'pscode-dixi-rfc',
55
+ 'design': 'pscode-dixi-design',
56
+ 'tasks': 'pscode-dixi-tasks',
57
+ 'arch-check': 'pscode-dixi-arch-check',
58
+ 'adr': 'pscode-dixi-adr',
59
+ 'jira-sync': 'pscode-dixi-jira-sync',
60
+ 'dod': 'pscode-dixi-dod',
52
61
  };
53
62
  // -----------------------------------------------------------------------------
54
63
  // Init Command Class
@@ -101,6 +110,13 @@ export class InitCommand {
101
110
  const trelloConfigured = await this.handleTrelloSetup(pscodePath);
102
111
  // Generate skills and commands for each tool
103
112
  const results = await this.generateSkillsAndCommands(projectPath, validatedTools);
113
+ // Dixi profile extras: stack detection and .pscode-dixi.yaml
114
+ await this.handleDixiExtras(projectPath);
115
+ // Dixi profile: generate JIRA integration files
116
+ const resolvedProfile = this.resolveProfileOverride() ?? (isValidProfile(getGlobalConfig().profile ?? '') ? getGlobalConfig().profile : DEFAULT_PROFILE);
117
+ if (resolvedProfile === 'dixi') {
118
+ await this.generateJiraFiles(projectPath);
119
+ }
104
120
  // Create config.yaml if needed
105
121
  const configStatus = await this.createConfig(pscodePath, extendMode);
106
122
  // Display success message
@@ -128,10 +144,11 @@ export class InitCommand {
128
144
  if (this.profileOverride === undefined) {
129
145
  return undefined;
130
146
  }
131
- if (this.profileOverride === 'core' || this.profileOverride === 'custom') {
147
+ if (isValidProfile(this.profileOverride)) {
132
148
  return this.profileOverride;
133
149
  }
134
- throw new Error(`Invalid profile "${this.profileOverride}". Available profiles: core, custom`);
150
+ const available = Object.keys(PROFILES).join(', ');
151
+ throw new Error(`Invalid profile "${this.profileOverride}". Available profiles: ${available}`);
135
152
  }
136
153
  // ═══════════════════════════════════════════════════════════
137
154
  // LEGACY TOOL MIGRATION
@@ -431,14 +448,9 @@ export class InitCommand {
431
448
  let removedSkillCount = 0;
432
449
  // Read global config for profile and delivery settings (use --profile override if set)
433
450
  const globalConfig = getGlobalConfig();
434
- const profile = this.resolveProfileOverride() ?? globalConfig.profile ?? 'core';
451
+ const profile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
435
452
  const delivery = globalConfig.delivery ?? 'both';
436
- const profileWorkflows = getProfileWorkflows(profile, globalConfig.workflows);
437
- // Trello workflows are always generated regardless of profile, so users can
438
- // run /ps:trello-setup to configure the integration at any time.
439
- const TRELLO_WORKFLOWS = ['trello-setup', 'task', 'draft'];
440
- const workflowsSet = new Set([...profileWorkflows, ...TRELLO_WORKFLOWS]);
441
- const workflows = [...workflowsSet];
453
+ const workflows = [...getProfileWorkflows(profile)];
442
454
  // Get skill and command templates filtered by profile workflows
443
455
  const shouldGenerateSkills = delivery !== 'commands';
444
456
  const shouldGenerateCommands = delivery !== 'skills';
@@ -541,15 +553,13 @@ export class InitCommand {
541
553
  if (results.refreshedTools.length > 0) {
542
554
  console.log(`Refreshed: ${results.refreshedTools.map((t) => t.name).join(', ')}`);
543
555
  }
544
- // Show counts (respecting profile filter + trello workflows always included)
556
+ // Show counts
545
557
  const successfulTools = [...results.createdTools, ...results.refreshedTools];
546
558
  if (successfulTools.length > 0) {
547
559
  const globalConfig = getGlobalConfig();
548
- const profile = this.profileOverride ?? globalConfig.profile ?? 'core';
560
+ const profile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
549
561
  const delivery = globalConfig.delivery ?? 'both';
550
- const profileWorkflows = getProfileWorkflows(profile, globalConfig.workflows);
551
- const TRELLO_WORKFLOWS = ['trello-setup', 'task', 'draft'];
552
- const workflows = [...new Set([...profileWorkflows, ...TRELLO_WORKFLOWS])];
562
+ const workflows = [...getProfileWorkflows(profile)];
553
563
  const toolDirs = [...new Set(successfulTools.map((t) => t.skillsDir))].join(', ');
554
564
  const skillCount = delivery !== 'commands' ? getSkillTemplates(workflows).length : 0;
555
565
  const commandCount = delivery !== 'skills' ? getCommandContents(workflows).length : 0;
@@ -591,10 +601,10 @@ export class InitCommand {
591
601
  else {
592
602
  console.log(chalk.dim(`Config: skipped (non-interactive mode)`));
593
603
  }
594
- // Getting started (task 7.6: show propose if in profile)
604
+ // Getting started
595
605
  const globalCfg = getGlobalConfig();
596
- const activeProfile = this.profileOverride ?? globalCfg.profile ?? 'core';
597
- const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
606
+ const activeProfile = this.resolveProfileOverride() ?? (isValidProfile(globalCfg.profile ?? '') ? globalCfg.profile : DEFAULT_PROFILE);
607
+ const activeWorkflows = [...getProfileWorkflows(activeProfile)];
598
608
  console.log();
599
609
  if (activeWorkflows.includes('propose')) {
600
610
  console.log(chalk.bold('Getting started:'));
@@ -605,7 +615,7 @@ export class InitCommand {
605
615
  console.log(' Start your first change: /ps:new "your idea"');
606
616
  }
607
617
  else {
608
- console.log("Done. Run 'pscode config profile' to configure your workflows.");
618
+ console.log("Done. Run 'pscode config profile' to switch profiles.");
609
619
  }
610
620
  // Trello status
611
621
  if (trelloConfigured) {
@@ -625,6 +635,56 @@ export class InitCommand {
625
635
  }
626
636
  console.log();
627
637
  }
638
+ async generateJiraFiles(projectPath) {
639
+ const pastelsddPath = path.join(projectPath, 'pastelsdd');
640
+ await FileSystemUtils.createDirectory(pastelsddPath);
641
+ const jiraYamlPath = path.join(pastelsddPath, 'jira.yaml');
642
+ if (!fs.existsSync(jiraYamlPath)) {
643
+ const content = `project_key: ""\nboard_url: ""\nconfigured: false\ntransitions:\n done: ""\n`;
644
+ await FileSystemUtils.writeFile(jiraYamlPath, content);
645
+ }
646
+ const mcpJsonPath = path.join(projectPath, '.mcp.json');
647
+ let mcpConfig = {};
648
+ if (fs.existsSync(mcpJsonPath)) {
649
+ try {
650
+ const raw = fs.readFileSync(mcpJsonPath, 'utf-8');
651
+ mcpConfig = JSON.parse(raw);
652
+ }
653
+ catch {
654
+ console.log('Aviso: .mcp.json inválido — recriando com entrada Atlassian.');
655
+ mcpConfig = {};
656
+ }
657
+ }
658
+ const mcpServers = (mcpConfig.mcpServers ?? {});
659
+ if (!mcpServers['atlassian']) {
660
+ mcpServers['atlassian'] = {
661
+ command: 'npx',
662
+ args: ['-y', 'mcp-remote', 'https://mcp.atlassian.com/v1/sse'],
663
+ };
664
+ mcpConfig.mcpServers = mcpServers;
665
+ await FileSystemUtils.writeFile(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
666
+ }
667
+ console.log('JIRA: edite pastelsdd/jira.yaml com project_key e board_url, depois use /pstld:jira-sync para testar a conexão.');
668
+ }
669
+ async handleDixiExtras(projectPath) {
670
+ const globalConfig = getGlobalConfig();
671
+ const profile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
672
+ if (profile !== 'dixi')
673
+ return;
674
+ const stack = detectDixiStack(projectPath);
675
+ const label = getDixiStackLabel(stack);
676
+ if (stack) {
677
+ console.log(`Dixi: stack detectada — ${label}`);
678
+ }
679
+ else {
680
+ console.log('Dixi: stack não detectada, usando configuração genérica');
681
+ }
682
+ installDixiExtras(projectPath, stack);
683
+ const family = getDixiStackFamily(stack);
684
+ const yamlContent = stringifyYaml({ stack, family, detectedAt: new Date().toISOString() });
685
+ const dixiYamlPath = path.join(projectPath, '.pscode-dixi.yaml');
686
+ await FileSystemUtils.writeFile(dixiYamlPath, yamlContent);
687
+ }
628
688
  startSpinner(text) {
629
689
  return ora({
630
690
  text,
@@ -0,0 +1,16 @@
1
+ interface JiraConfig {
2
+ project_key?: string;
3
+ configured?: boolean;
4
+ transitions?: {
5
+ done?: string;
6
+ };
7
+ }
8
+ export interface JiraTransitionResult {
9
+ attempted: boolean;
10
+ success: boolean;
11
+ warning?: string;
12
+ }
13
+ export declare function readJiraConfig(projectDir: string): Promise<JiraConfig | null>;
14
+ export declare function tryTransitionJiraIssue(issueKey: string, transitionId: string): Promise<JiraTransitionResult>;
15
+ export {};
16
+ //# sourceMappingURL=jira-transition.d.ts.map
@@ -0,0 +1,29 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ export async function readJiraConfig(projectDir) {
5
+ const jiraYamlPath = path.join(projectDir, 'pastelsdd', 'jira.yaml');
6
+ try {
7
+ const content = await fs.readFile(jiraYamlPath, 'utf-8');
8
+ return parseYaml(content);
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
14
+ export async function tryTransitionJiraIssue(issueKey, transitionId) {
15
+ // Validate issueKey format
16
+ if (!/^[A-Z]+-[0-9]+$/.test(issueKey)) {
17
+ return {
18
+ attempted: false,
19
+ success: false,
20
+ warning: `jiraIssueKey "${issueKey}" não corresponde ao formato esperado [A-Z]+-[0-9]+. Transição JIRA ignorada.`,
21
+ };
22
+ }
23
+ // The CLI cannot call the Atlassian MCP server directly.
24
+ // Log a message informing the user to transition manually or via /pstld:jira-sync.
25
+ console.log(`JIRA: issue ${issueKey} vinculada (transição "${transitionId}" pendente). ` +
26
+ `Use /pstld:jira-sync para confirmar o status ou transite manualmente.`);
27
+ return { attempted: true, success: true };
28
+ }
29
+ //# sourceMappingURL=jira-transition.js.map
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Migration Utilities
3
3
  *
4
- * One-time migration logic for existing projects when profile system is introduced.
5
- * Called by both init and update commands before profile resolution.
4
+ * Scans installed workflow artifacts across tools.
5
+ * Called by init and update commands.
6
6
  */
7
7
  import type { AIToolOption } from './config.js';
8
8
  /**
@@ -10,14 +10,5 @@ import type { AIToolOption } from './config.js';
10
10
  * the union of installed workflow IDs.
11
11
  */
12
12
  export declare function scanInstalledWorkflows(projectPath: string, tools: AIToolOption[]): string[];
13
- /**
14
- * Performs one-time migration if the global config does not yet have a profile field.
15
- * Called by both init and update before profile resolution.
16
- *
17
- * - If no profile field exists and workflows are installed: sets profile to 'custom'
18
- * with the detected workflows, preserving the user's existing setup.
19
- * - If no profile field exists and no workflows are installed: no-op (defaults apply).
20
- * - If profile field already exists: no-op.
21
- */
22
- export declare function migrateIfNeeded(projectPath: string, tools: AIToolOption[]): void;
13
+ export declare function migrateIfNeeded(_projectPath: string, _tools: AIToolOption[]): void;
23
14
  //# sourceMappingURL=migration.d.ts.map