@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
package/README.md CHANGED
@@ -50,7 +50,7 @@ Once initialized, use slash commands in your AI agent:
50
50
  /ps:propose "add dark mode" ← creates a new change
51
51
  /ps:continue ← advances to the next artifact
52
52
  /ps:apply ← applies pending tasks
53
- /ps:archive archives a completed change
53
+ /ps:complete completes a change
54
54
  ```
55
55
 
56
56
  ---
@@ -84,7 +84,7 @@ The AI agent reads these files at each step and generates the next artifact usin
84
84
  | `pscode validate [name]` | Validate a change or spec |
85
85
  | `pscode validate --all` | Validate everything |
86
86
  | `pscode show [name]` | Display a change or spec |
87
- | `pscode archive [name]` | Archive a completed change |
87
+ | `pscode complete [name]` | Complete a change |
88
88
  | `pscode new change <name>` | Create a new change directory |
89
89
  | `pscode schemas` | List available workflow schemas |
90
90
  | `pscode view` | Interactive dashboard |
package/dist/cli/index.js CHANGED
@@ -7,7 +7,7 @@ import { promises as fs } from 'fs';
7
7
  import { AI_TOOLS } from '../core/config.js';
8
8
  import { UpdateCommand } from '../core/update.js';
9
9
  import { ListCommand } from '../core/list.js';
10
- import { ArchiveCommand } from '../core/archive.js';
10
+ import { CompleteCommand } from '../core/complete.js';
11
11
  import { ViewCommand } from '../core/view.js';
12
12
  import { registerSpecCommand } from '../commands/spec.js';
13
13
  import { ChangeCommand } from '../commands/change.js';
@@ -75,7 +75,7 @@ program
75
75
  .description('Initialize Pscode in your project')
76
76
  .option('--tools <tools>', toolsOptionDescription)
77
77
  .option('--force', 'Auto-cleanup legacy files without prompting')
78
- .option('--profile <profile>', 'Override global config profile (core or custom)')
78
+ .option('--profile <profile>', 'Workflow profile to use (core, full, trello)')
79
79
  .action(async (targetPath = '.', options) => {
80
80
  try {
81
81
  // Validate that the path is a valid directory
@@ -250,15 +250,15 @@ changeCmd
250
250
  }
251
251
  });
252
252
  program
253
- .command('archive [change-name]')
254
- .description('Archive a completed change and update main specs')
253
+ .command('complete [change-name]')
254
+ .description('Complete a change and update main specs')
255
255
  .option('-y, --yes', 'Skip confirmation prompts')
256
256
  .option('--skip-specs', 'Skip spec update operations (useful for infrastructure, tooling, or doc-only changes)')
257
257
  .option('--no-validate', 'Skip validation (not recommended, requires confirmation)')
258
258
  .action(async (changeName, options) => {
259
259
  try {
260
- const archiveCommand = new ArchiveCommand();
261
- await archiveCommand.execute(changeName, options);
260
+ const completeCommand = new CompleteCommand();
261
+ await completeCommand.execute(changeName, options);
262
262
  }
263
263
  catch (error) {
264
264
  console.log(); // Empty line for spacing
@@ -1,27 +1,19 @@
1
1
  import { Command } from 'commander';
2
2
  import { GlobalConfig } from '../core/global-config.js';
3
- import type { Profile, Delivery } from '../core/global-config.js';
3
+ import type { Delivery } from '../core/global-config.js';
4
+ import { type ProfileName } from '../core/profiles.js';
4
5
  interface ProfileState {
5
- profile: Profile;
6
+ profile: ProfileName;
6
7
  delivery: Delivery;
7
- workflows: string[];
8
8
  }
9
9
  interface ProfileStateDiff {
10
10
  hasChanges: boolean;
11
11
  lines: string[];
12
12
  }
13
13
  /**
14
- * Resolve the effective current profile state from global config defaults.
14
+ * Resolve the effective current profile state from global config.
15
15
  */
16
16
  export declare function resolveCurrentProfileState(config: GlobalConfig): ProfileState;
17
- /**
18
- * Derive profile type from selected workflows.
19
- */
20
- export declare function deriveProfileFromWorkflowSelection(selectedWorkflows: string[]): Profile;
21
- /**
22
- * Format a compact workflow summary for the profile header.
23
- */
24
- export declare function formatWorkflowSummary(workflows: readonly string[], profile: Profile): string;
25
17
  /**
26
18
  * Build a user-facing diff summary between two profile states.
27
19
  */
@@ -3,135 +3,34 @@ import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
4
  import { getGlobalConfigPath, getGlobalConfig, saveGlobalConfig, } from '../core/global-config.js';
5
5
  import { getNestedValue, setNestedValue, deleteNestedValue, coerceValue, formatValueYaml, validateConfigKeyPath, validateConfig, DEFAULT_CONFIG, } from '../core/config-schema.js';
6
- import { CORE_WORKFLOWS, ALL_WORKFLOWS, getProfileWorkflows } from '../core/profiles.js';
6
+ import { PROFILES, getProfileWorkflows, isValidProfile, DEFAULT_PROFILE } from '../core/profiles.js';
7
7
  import { PSCODE_DIR_NAME } from '../core/config.js';
8
8
  import { hasProjectConfigDrift } from '../core/profile-sync-drift.js';
9
9
  import { findWorkspaceRoot, hasWorkspaceSkillProfileDrift, readOptionalWorkspaceViewState, } from '../core/workspace/index.js';
10
- const WORKFLOW_PROMPT_META = {
11
- propose: {
12
- name: 'Propose change',
13
- description: 'Create proposal, design, and tasks from a request',
14
- },
15
- explore: {
16
- name: 'Explore ideas',
17
- description: 'Investigate a problem before implementation',
18
- },
19
- new: {
20
- name: 'New change',
21
- description: 'Create a new change scaffold quickly',
22
- },
23
- continue: {
24
- name: 'Continue change',
25
- description: 'Resume work on an existing change',
26
- },
27
- apply: {
28
- name: 'Apply tasks',
29
- description: 'Implement tasks from the current change',
30
- },
31
- ff: {
32
- name: 'Fast-forward',
33
- description: 'Run a faster implementation workflow',
34
- },
35
- sync: {
36
- name: 'Sync specs',
37
- description: 'Sync change artifacts with specs',
38
- },
39
- archive: {
40
- name: 'Archive change',
41
- description: 'Finalize and archive a completed change',
42
- },
43
- 'bulk-archive': {
44
- name: 'Bulk archive',
45
- description: 'Archive multiple completed changes together',
46
- },
47
- verify: {
48
- name: 'Verify change',
49
- description: 'Run verification checks against a change',
50
- },
51
- onboard: {
52
- name: 'Onboard',
53
- description: 'Guided onboarding flow for Pscode',
54
- },
55
- };
56
10
  function isPromptCancellationError(error) {
57
11
  return (error instanceof Error &&
58
12
  (error.name === 'ExitPromptError' || error.message.includes('force closed the prompt with SIGINT')));
59
13
  }
60
14
  /**
61
- * Resolve the effective current profile state from global config defaults.
15
+ * Resolve the effective current profile state from global config.
62
16
  */
63
17
  export function resolveCurrentProfileState(config) {
64
- const profile = config.profile || 'core';
65
- const delivery = config.delivery || 'both';
66
- const workflows = [
67
- ...getProfileWorkflows(profile, config.workflows ? [...config.workflows] : undefined),
68
- ];
69
- return { profile, delivery, workflows };
70
- }
71
- /**
72
- * Derive profile type from selected workflows.
73
- */
74
- export function deriveProfileFromWorkflowSelection(selectedWorkflows) {
75
- const isCoreMatch = selectedWorkflows.length === CORE_WORKFLOWS.length &&
76
- CORE_WORKFLOWS.every((w) => selectedWorkflows.includes(w));
77
- return isCoreMatch ? 'core' : 'custom';
78
- }
79
- /**
80
- * Format a compact workflow summary for the profile header.
81
- */
82
- export function formatWorkflowSummary(workflows, profile) {
83
- return `${workflows.length} selected (${profile})`;
84
- }
85
- function stableWorkflowOrder(workflows) {
86
- const seen = new Set();
87
- const ordered = [];
88
- for (const workflow of ALL_WORKFLOWS) {
89
- if (workflows.includes(workflow) && !seen.has(workflow)) {
90
- ordered.push(workflow);
91
- seen.add(workflow);
92
- }
93
- }
94
- const extras = workflows.filter((w) => !ALL_WORKFLOWS.includes(w));
95
- extras.sort();
96
- for (const extra of extras) {
97
- if (!seen.has(extra)) {
98
- ordered.push(extra);
99
- seen.add(extra);
100
- }
101
- }
102
- return ordered;
18
+ const profile = isValidProfile(config.profile ?? '') ? config.profile : DEFAULT_PROFILE;
19
+ const delivery = config.delivery ?? 'both';
20
+ return { profile, delivery };
103
21
  }
104
22
  /**
105
23
  * Build a user-facing diff summary between two profile states.
106
24
  */
107
25
  export function diffProfileState(before, after) {
108
26
  const lines = [];
109
- if (before.delivery !== after.delivery) {
110
- lines.push(`delivery: ${before.delivery} -> ${after.delivery}`);
111
- }
112
27
  if (before.profile !== after.profile) {
113
28
  lines.push(`profile: ${before.profile} -> ${after.profile}`);
114
29
  }
115
- const beforeOrdered = stableWorkflowOrder(before.workflows);
116
- const afterOrdered = stableWorkflowOrder(after.workflows);
117
- const beforeSet = new Set(beforeOrdered);
118
- const afterSet = new Set(afterOrdered);
119
- const added = afterOrdered.filter((w) => !beforeSet.has(w));
120
- const removed = beforeOrdered.filter((w) => !afterSet.has(w));
121
- if (added.length > 0 || removed.length > 0) {
122
- const tokens = [];
123
- if (added.length > 0) {
124
- tokens.push(`added ${added.join(', ')}`);
125
- }
126
- if (removed.length > 0) {
127
- tokens.push(`removed ${removed.join(', ')}`);
128
- }
129
- lines.push(`workflows: ${tokens.join('; ')}`);
30
+ if (before.delivery !== after.delivery) {
31
+ lines.push(`delivery: ${before.delivery} -> ${after.delivery}`);
130
32
  }
131
- return {
132
- hasChanges: lines.length > 0,
133
- lines,
134
- };
33
+ return { hasChanges: lines.length > 0, lines };
135
34
  }
136
35
  async function resolveWorkspaceConfigProfileContext(cwd = process.cwd()) {
137
36
  const workspaceRoot = await findWorkspaceRoot(cwd);
@@ -148,7 +47,7 @@ function maybeWarnProjectConfigDrift(projectDir, state, colorize) {
148
47
  if (!fs.existsSync(pscodeDir)) {
149
48
  return;
150
49
  }
151
- if (!hasProjectConfigDrift(projectDir, state.workflows, state.delivery)) {
50
+ if (!hasProjectConfigDrift(projectDir, [...getProfileWorkflows(state.profile)], state.delivery)) {
152
51
  return;
153
52
  }
154
53
  console.log(colorize('Warning: Global config is not applied to this project. Run `pscode update` to sync.'));
@@ -225,20 +124,13 @@ export function registerConfigCommand(program) {
225
124
  }
226
125
  console.log(formatValueYaml(config));
227
126
  // Annotate profile settings
127
+ const profileName = isValidProfile(config.profile ?? '') ? config.profile : DEFAULT_PROFILE;
228
128
  const profileSource = rawConfig.profile !== undefined ? '(explicit)' : '(default)';
229
129
  const deliverySource = rawConfig.delivery !== undefined ? '(explicit)' : '(default)';
230
130
  console.log(`\nProfile settings:`);
231
- console.log(` profile: ${config.profile} ${profileSource}`);
232
- console.log(` delivery: ${config.delivery} ${deliverySource}`);
233
- if (config.profile === 'core') {
234
- console.log(` workflows: ${CORE_WORKFLOWS.join(', ')} (from core profile)`);
235
- }
236
- else if (config.workflows && config.workflows.length > 0) {
237
- console.log(` workflows: ${config.workflows.join(', ')} (explicit)`);
238
- }
239
- else {
240
- console.log(` workflows: (none)`);
241
- }
131
+ console.log(` profile: ${profileName} ${profileSource} — ${PROFILES[profileName].description}`);
132
+ console.log(` delivery: ${config.delivery ?? 'both'} ${deliverySource}`);
133
+ console.log(` workflows: ${getProfileWorkflows(profileName).join(', ')}`);
242
134
  }
243
135
  });
244
136
  // config get
@@ -406,169 +298,109 @@ export function registerConfigCommand(program) {
406
298
  process.exitCode = 1;
407
299
  }
408
300
  });
409
- // config profile [preset]
301
+ // config profile [name]
410
302
  configCmd
411
- .command('profile [preset]')
412
- .description('Configure workflow profile (interactive picker or preset shortcut)')
413
- .action(async (preset) => {
414
- // Preset shortcut: `pscode config profile core`
415
- if (preset === 'core') {
303
+ .command('profile [name]')
304
+ .description(`Switch workflow profile. Available: ${Object.keys(PROFILES).join(', ')}`)
305
+ .action(async (name) => {
306
+ const availableNames = Object.keys(PROFILES).join(', ');
307
+ // Direct selection via argument
308
+ if (name) {
309
+ if (!isValidProfile(name)) {
310
+ console.error(`Error: Unknown profile "${name}". Available: ${availableNames}`);
311
+ process.exitCode = 1;
312
+ return;
313
+ }
416
314
  const config = getGlobalConfig();
417
- config.profile = 'core';
418
- config.workflows = [...CORE_WORKFLOWS];
419
- // Preserve delivery setting
315
+ config.profile = name;
316
+ delete config.workflows;
420
317
  saveGlobalConfig(config);
318
+ const workflows = getProfileWorkflows(name);
319
+ console.log(`Profile set to "${name}" (${PROFILES[name].description})`);
320
+ console.log(`Workflows: ${workflows.join(', ')}`);
421
321
  const workspaceContext = await resolveWorkspaceConfigProfileContext();
422
322
  printConfigProfileApplyGuidance(workspaceContext);
423
323
  return;
424
324
  }
425
- if (preset) {
426
- console.error(`Error: Unknown profile preset "${preset}". Available presets: core`);
427
- process.exitCode = 1;
428
- return;
429
- }
430
- // Non-interactive check
325
+ // Non-interactive fallback
431
326
  if (!process.stdout.isTTY) {
432
- console.error('Interactive mode required. Use `pscode config profile core` or set config via environment/flags.');
327
+ console.error(`Interactive mode required. Use: pscode config profile <name>`);
328
+ console.error(`Available profiles: ${availableNames}`);
433
329
  process.exitCode = 1;
434
330
  return;
435
331
  }
436
332
  // Interactive picker
437
- const { select, checkbox, confirm } = await import('@inquirer/prompts');
333
+ const { select, confirm } = await import('@inquirer/prompts');
438
334
  const chalk = (await import('chalk')).default;
439
335
  try {
440
336
  const config = getGlobalConfig();
441
337
  const currentState = resolveCurrentProfileState(config);
442
- console.log(chalk.bold('\nCurrent profile settings'));
443
- console.log(` Delivery: ${currentState.delivery}`);
444
- console.log(` Workflows: ${formatWorkflowSummary(currentState.workflows, currentState.profile)}`);
445
- console.log(chalk.dim(' Delivery = where workflows are installed (skills, commands, or both)'));
446
- console.log(chalk.dim(' Workflows = which actions are available (propose, explore, apply, etc.)'));
338
+ console.log(chalk.bold('\nCurrent profile:'), `${currentState.profile} — ${PROFILES[currentState.profile].description}`);
339
+ console.log(chalk.dim(`Workflows: ${getProfileWorkflows(currentState.profile).join(', ')}`));
447
340
  console.log();
448
341
  const action = await select({
449
342
  message: 'What do you want to configure?',
450
343
  choices: [
451
- {
452
- value: 'both',
453
- name: 'Delivery and workflows',
454
- description: 'Update install mode and available actions together',
455
- },
456
- {
457
- value: 'delivery',
458
- name: 'Delivery only',
459
- description: 'Change where workflows are installed',
460
- },
461
- {
462
- value: 'workflows',
463
- name: 'Workflows only',
464
- description: 'Change which workflow actions are available',
465
- },
466
- {
467
- value: 'keep',
468
- name: 'Keep current settings (exit)',
469
- description: 'Leave configuration unchanged and exit',
470
- },
344
+ { value: 'profile', name: 'Switch profile', description: 'Choose a different workflow set' },
345
+ { value: 'delivery', name: 'Change delivery', description: 'Skills, commands, or both' },
346
+ { value: 'both', name: 'Profile + delivery', description: 'Change both at once' },
347
+ { value: 'keep', name: 'Keep current (exit)', description: 'Leave unchanged' },
471
348
  ],
472
349
  });
473
350
  if (action === 'keep') {
474
- console.log('No config changes.');
351
+ console.log('No changes.');
475
352
  await maybeWarnConfigDrift(currentState, chalk.yellow);
476
353
  return;
477
354
  }
478
- const nextState = {
479
- profile: currentState.profile,
480
- delivery: currentState.delivery,
481
- workflows: [...currentState.workflows],
482
- };
483
- if (action === 'both' || action === 'delivery') {
355
+ const nextState = { ...currentState };
356
+ if (action === 'profile' || action === 'both') {
357
+ const profileChoices = Object.entries(PROFILES).map(([key, def]) => ({
358
+ value: key,
359
+ name: key,
360
+ description: `${def.description} | workflows: ${def.workflows.join(', ')}`,
361
+ }));
362
+ nextState.profile = await select({
363
+ message: 'Select profile:',
364
+ choices: profileChoices,
365
+ default: currentState.profile,
366
+ });
367
+ }
368
+ if (action === 'delivery' || action === 'both') {
484
369
  const deliveryChoices = [
485
- {
486
- value: 'both',
487
- name: 'Both (skills + commands)',
488
- description: 'Install workflows as both skills and slash commands',
489
- },
490
- {
491
- value: 'skills',
492
- name: 'Skills only',
493
- description: 'Install workflows only as skills',
494
- },
495
- {
496
- value: 'commands',
497
- name: 'Commands only',
498
- description: 'Install workflows only as slash commands',
499
- },
370
+ { value: 'both', name: 'Both (skills + commands)', description: 'Install as both skills and slash commands' },
371
+ { value: 'skills', name: 'Skills only', description: 'Install only as agent skills' },
372
+ { value: 'commands', name: 'Commands only', description: 'Install only as slash commands' },
500
373
  ];
501
- for (const choice of deliveryChoices) {
502
- if (choice.value === currentState.delivery) {
503
- choice.name += ' [current]';
504
- }
505
- }
506
374
  nextState.delivery = await select({
507
- message: 'Delivery mode (how workflows are installed):',
375
+ message: 'Delivery mode:',
508
376
  choices: deliveryChoices,
509
377
  default: currentState.delivery,
510
378
  });
511
379
  }
512
- if (action === 'both' || action === 'workflows') {
513
- const formatWorkflowChoice = (workflow) => {
514
- const metadata = WORKFLOW_PROMPT_META[workflow] ?? {
515
- name: workflow,
516
- description: `Workflow: ${workflow}`,
517
- };
518
- return {
519
- value: workflow,
520
- name: metadata.name,
521
- description: metadata.description,
522
- short: metadata.name,
523
- checked: currentState.workflows.includes(workflow),
524
- };
525
- };
526
- const selectedWorkflows = await checkbox({
527
- message: 'Select workflows to make available:',
528
- instructions: 'Space to toggle, Enter to confirm',
529
- pageSize: ALL_WORKFLOWS.length,
530
- theme: {
531
- icon: {
532
- checked: '[x]',
533
- unchecked: '[ ]',
534
- },
535
- },
536
- choices: ALL_WORKFLOWS.map(formatWorkflowChoice),
537
- });
538
- nextState.workflows = selectedWorkflows;
539
- nextState.profile = deriveProfileFromWorkflowSelection(selectedWorkflows);
540
- }
541
380
  const diff = diffProfileState(currentState, nextState);
542
381
  if (!diff.hasChanges) {
543
- console.log('No config changes.');
544
- await maybeWarnConfigDrift(nextState, chalk.yellow);
382
+ console.log('No changes.');
383
+ await maybeWarnConfigDrift(currentState, chalk.yellow);
545
384
  return;
546
385
  }
547
- console.log(chalk.bold('\nConfig changes:'));
386
+ console.log(chalk.bold('\nChanges:'));
548
387
  for (const line of diff.lines) {
549
388
  console.log(` ${line}`);
550
389
  }
551
390
  console.log();
552
391
  config.profile = nextState.profile;
553
392
  config.delivery = nextState.delivery;
554
- config.workflows = nextState.workflows;
393
+ delete config.workflows;
555
394
  saveGlobalConfig(config);
556
395
  const workspaceContext = await resolveWorkspaceConfigProfileContext();
557
396
  if (workspaceContext) {
558
- const applyNow = await confirm({
559
- message: 'Apply changes to this workspace now?',
560
- default: true,
561
- });
397
+ const applyNow = await confirm({ message: 'Apply to this workspace now?', default: true });
562
398
  if (applyNow) {
563
399
  try {
564
- execSync('npx pscode workspace update', {
565
- stdio: 'inherit',
566
- cwd: workspaceContext.commandCwd,
567
- });
568
- console.log('Run `pscode workspace update` in your other workspaces to apply.');
400
+ execSync('npx pscode workspace update', { stdio: 'inherit', cwd: workspaceContext.commandCwd });
569
401
  }
570
402
  catch {
571
- console.error('`pscode workspace update` failed. Please run it manually to apply the profile changes.');
403
+ console.error('`pscode workspace update` failed. Run it manually.');
572
404
  process.exitCode = 1;
573
405
  }
574
406
  return;
@@ -576,21 +408,16 @@ export function registerConfigCommand(program) {
576
408
  printConfigProfileApplyGuidance(workspaceContext);
577
409
  return;
578
410
  }
579
- // Check if inside an Pscode project
580
411
  const projectDir = process.cwd();
581
412
  const pscodeDir = path.join(projectDir, PSCODE_DIR_NAME);
582
413
  if (fs.existsSync(pscodeDir)) {
583
- const applyNow = await confirm({
584
- message: 'Apply changes to this project now?',
585
- default: true,
586
- });
414
+ const applyNow = await confirm({ message: 'Apply to this project now?', default: true });
587
415
  if (applyNow) {
588
416
  try {
589
417
  execSync('npx pscode update', { stdio: 'inherit', cwd: projectDir });
590
- console.log('Run `pscode update` in your other projects to apply.');
591
418
  }
592
419
  catch {
593
- console.error('`pscode update` failed. Please run it manually to apply the profile changes.');
420
+ console.error('`pscode update` failed. Run it manually.');
594
421
  process.exitCode = 1;
595
422
  }
596
423
  return;
@@ -600,7 +427,7 @@ export function registerConfigCommand(program) {
600
427
  }
601
428
  catch (error) {
602
429
  if (isPromptCancellationError(error)) {
603
- console.log('Config profile cancelled.');
430
+ console.log('Cancelled.');
604
431
  process.exitCode = 130;
605
432
  return;
606
433
  }
@@ -13,6 +13,7 @@ export declare const ChangeMetadataSchema: z.ZodObject<{
13
13
  store: z.ZodString;
14
14
  id: z.ZodString;
15
15
  }, z.core.$strict>>;
16
+ jiraIssueKey: z.ZodOptional<z.ZodString>;
16
17
  }, z.core.$strip>;
17
18
  export type ChangeMetadata = z.infer<typeof ChangeMetadataSchema>;
18
19
  //# sourceMappingURL=schema.d.ts.map
@@ -24,5 +24,6 @@ export const ChangeMetadataSchema = z.object({
24
24
  goal: z.string().min(1).optional(),
25
25
  affected_areas: z.array(z.string().min(1)).optional(),
26
26
  initiative: InitiativeLinkSchema.optional(),
27
+ jiraIssueKey: z.string().optional(),
27
28
  });
28
29
  //# sourceMappingURL=schema.js.map
@@ -1,4 +1,4 @@
1
- export declare class ArchiveCommand {
1
+ export declare class CompleteCommand {
2
2
  execute(changeName?: string, options?: {
3
3
  yes?: boolean;
4
4
  skipSpecs?: boolean;
@@ -8,4 +8,4 @@ export declare class ArchiveCommand {
8
8
  private selectChange;
9
9
  private getArchiveDate;
10
10
  }
11
- //# sourceMappingURL=archive.d.ts.map
11
+ //# sourceMappingURL=complete.d.ts.map
@@ -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
  }
@@ -263,6 +265,27 @@ export class ArchiveCommand {
263
265
  }
264
266
  // Create archive directory if needed
265
267
  await fs.mkdir(archiveDir, { recursive: true });
268
+ // Attempt JIRA transition before moving (non-fatal)
269
+ try {
270
+ const changeMetadata = readChangeMetadata(changeDir, targetPath);
271
+ if (changeMetadata?.jiraIssueKey) {
272
+ const jiraConfig = await readJiraConfig(targetPath);
273
+ const transitionId = jiraConfig?.transitions?.done;
274
+ if (transitionId) {
275
+ const result = await tryTransitionJiraIssue(changeMetadata.jiraIssueKey, transitionId);
276
+ if (result.warning) {
277
+ console.log(chalk.yellow(`JIRA: ${result.warning}`));
278
+ }
279
+ }
280
+ else {
281
+ console.log(chalk.yellow(`JIRA: jiraIssueKey "${changeMetadata.jiraIssueKey}" encontrado, mas transitions.done não está configurado em pastelsdd/jira.yaml. ` +
282
+ `Execute /pstld:jira-setup para configurar.`));
283
+ }
284
+ }
285
+ }
286
+ catch {
287
+ // JIRA transition is non-fatal — never block the archive
288
+ }
266
289
  // Move change to archive (uses copy+remove on EPERM/EXDEV, e.g. Windows)
267
290
  await moveDirectory(changeDir, archivePath);
268
291
  console.log(`Change '${changeName}' archived as '${archiveName}'.`);
@@ -300,7 +323,7 @@ export class ArchiveCommand {
300
323
  }
301
324
  try {
302
325
  const answer = await select({
303
- message: 'Select a change to archive',
326
+ message: 'Select a change to complete',
304
327
  choices
305
328
  });
306
329
  return answer;
@@ -315,4 +338,4 @@ export class ArchiveCommand {
315
338
  return new Date().toISOString().split('T')[0];
316
339
  }
317
340
  }
318
- //# sourceMappingURL=archive.js.map
341
+ //# 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 }],
@@ -847,7 +847,7 @@ export const COMMAND_REGISTRY = [
847
847
  name: 'profile',
848
848
  description: 'Configure workflow profile (interactive picker or preset shortcut)',
849
849
  acceptsPositional: true,
850
- positionals: [{ name: 'preset', optional: true }],
850
+ positionals: [{ name: 'name', optional: true }],
851
851
  flags: [],
852
852
  },
853
853
  ],