@thiagodiogo/pscode 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/cli/index.js +3 -0
  2. package/dist/core/completions/command-registry.js +8 -0
  3. package/dist/core/config-prompts.d.ts +1 -1
  4. package/dist/core/config-prompts.js +8 -1
  5. package/dist/core/init.d.ts +3 -0
  6. package/dist/core/init.js +83 -4
  7. package/dist/core/pr-init-prompt.d.ts +13 -0
  8. package/dist/core/pr-init-prompt.js +76 -0
  9. package/dist/core/presets/dixi.d.ts +1 -0
  10. package/dist/core/presets/dixi.js +15 -25
  11. package/dist/core/profile-sync-drift.js +2 -0
  12. package/dist/core/profiles.d.ts +4 -4
  13. package/dist/core/profiles.js +4 -3
  14. package/dist/core/project-config.d.ts +31 -0
  15. package/dist/core/project-config.js +24 -0
  16. package/dist/core/shared/skill-generation.js +5 -2
  17. package/dist/core/templates/skill-templates.d.ts +1 -0
  18. package/dist/core/templates/skill-templates.js +2 -0
  19. package/dist/core/templates/types.d.ts +0 -1
  20. package/dist/core/templates/workflows/apply-change.js +27 -3
  21. package/dist/core/templates/workflows/archive-change.js +0 -1
  22. package/dist/core/templates/workflows/bulk-archive-change.js +0 -1
  23. package/dist/core/templates/workflows/continue-change.js +0 -1
  24. package/dist/core/templates/workflows/explore.js +0 -1
  25. package/dist/core/templates/workflows/feedback.js +0 -1
  26. package/dist/core/templates/workflows/ff-change.js +0 -1
  27. package/dist/core/templates/workflows/handoff.d.ts +10 -0
  28. package/dist/core/templates/workflows/handoff.js +31 -0
  29. package/dist/core/templates/workflows/new-change.js +0 -1
  30. package/dist/core/templates/workflows/onboard.js +0 -1
  31. package/dist/core/templates/workflows/propose.js +45 -31
  32. package/dist/core/templates/workflows/trello-draft.js +4 -16
  33. package/dist/core/templates/workflows/trello-next-step-comment.d.ts +22 -15
  34. package/dist/core/templates/workflows/trello-next-step-comment.js +69 -31
  35. package/dist/core/templates/workflows/trello-setup.js +0 -1
  36. package/dist/core/templates/workflows/verify-change.js +0 -1
  37. package/package.json +1 -1
  38. package/pscode/content/dixi/commands/ps/apply.md +15 -0
  39. package/pscode/content/dixi/commands/ps/archive.md +14 -0
  40. package/pscode/content/dixi/commands/ps/complete.md +14 -0
  41. package/pscode/content/dixi/commands/ps/draft.md +15 -0
  42. package/pscode/content/dixi/commands/ps/explore.md +14 -0
  43. package/pscode/content/dixi/commands/ps/propose.md +14 -0
  44. package/pscode/content/dixi/commands/ps/trello-setup.md +14 -0
  45. package/pscode/content/dixi/commands/pstld/adr.md +75 -0
  46. package/pscode/content/dixi/commands/pstld/arch-check.md +64 -0
  47. package/pscode/content/dixi/commands/pstld/dod.md +66 -0
  48. package/pscode/content/dixi/commands/pstld/jira-draft.md +80 -0
package/dist/cli/index.js CHANGED
@@ -76,6 +76,8 @@ program
76
76
  .option('--tools <tools>', toolsOptionDescription)
77
77
  .option('--force', 'Auto-cleanup legacy files without prompting')
78
78
  .option('--profile <profile>', 'Workflow profile to use (core, full, trello)')
79
+ .option('--pr', 'Enable PR workflow config without interactive prompts (uses defaults: branch=feat/{change-name}, title=[{type}] {change-name})')
80
+ .option('--no-pr', 'Disable PR workflow config without interactive prompts')
79
81
  .action(async (targetPath = '.', options) => {
80
82
  try {
81
83
  // Validate that the path is a valid directory
@@ -103,6 +105,7 @@ program
103
105
  tools: options?.tools,
104
106
  force: options?.force,
105
107
  profile: options?.profile,
108
+ pr: options?.pr,
106
109
  });
107
110
  await initCommand.execute(targetPath);
108
111
  }
@@ -22,6 +22,14 @@ export const COMMAND_REGISTRY = [
22
22
  takesValue: true,
23
23
  values: ['standard', 'dixi'],
24
24
  },
25
+ {
26
+ name: 'pr',
27
+ description: 'Enable PR workflow config without interactive prompts',
28
+ },
29
+ {
30
+ name: 'no-pr',
31
+ description: 'Disable PR workflow config without interactive prompts',
32
+ },
25
33
  ],
26
34
  },
27
35
  {
@@ -2,7 +2,7 @@ import type { ProjectConfig } from './project-config.js';
2
2
  /**
3
3
  * Serialize config to YAML string with helpful comments.
4
4
  *
5
- * @param config - Partial config object (schema required, context/rules optional)
5
+ * @param config - Partial config object (schema required, context/rules/pr optional)
6
6
  * @returns YAML string ready to write to file
7
7
  */
8
8
  export declare function serializeConfig(config: Partial<ProjectConfig>): string;
@@ -1,7 +1,8 @@
1
+ import { stringify as stringifyYaml } from 'yaml';
1
2
  /**
2
3
  * Serialize config to YAML string with helpful comments.
3
4
  *
4
- * @param config - Partial config object (schema required, context/rules optional)
5
+ * @param config - Partial config object (schema required, context/rules/pr optional)
5
6
  * @returns YAML string ready to write to file
6
7
  */
7
8
  export function serializeConfig(config) {
@@ -9,6 +10,12 @@ export function serializeConfig(config) {
9
10
  // Schema (required)
10
11
  lines.push(`schema: ${config.schema}`);
11
12
  lines.push('');
13
+ // PR workflow config (if present)
14
+ if (config.pr !== undefined) {
15
+ const prYaml = stringifyYaml({ pr: config.pr }, { lineWidth: 0 });
16
+ lines.push(prYaml.trimEnd());
17
+ lines.push('');
18
+ }
12
19
  // Context section with comments
13
20
  lines.push('# Project context (optional)');
14
21
  lines.push('# This is shown to AI when creating artifacts.');
@@ -9,12 +9,14 @@ type InitCommandOptions = {
9
9
  force?: boolean;
10
10
  interactive?: boolean;
11
11
  profile?: string;
12
+ pr?: boolean;
12
13
  };
13
14
  export declare class InitCommand {
14
15
  private readonly toolsArg?;
15
16
  private readonly force;
16
17
  private readonly interactiveOption?;
17
18
  private readonly profileOverride?;
19
+ private readonly prFlag?;
18
20
  constructor(options?: InitCommandOptions);
19
21
  execute(targetPath: string): Promise<void>;
20
22
  private validate;
@@ -28,6 +30,7 @@ export declare class InitCommand {
28
30
  private resolveToolsArg;
29
31
  private validateTools;
30
32
  private handleTrelloSetup;
33
+ private handlePrSetup;
31
34
  private createDirectoryStructure;
32
35
  private generateSkillsAndCommands;
33
36
  private createConfig;
package/dist/core/init.js CHANGED
@@ -24,8 +24,10 @@ import { getGlobalConfig } from './global-config.js';
24
24
  import { getProfileWorkflows, isValidProfile, DEFAULT_PROFILE, PROFILES, ALL_WORKFLOWS } from './profiles.js';
25
25
  import { detectDixiStack, getDixiStackFamily, getDixiStackLabel, installDixiExtras } from './presets/dixi.js';
26
26
  import { stringify as stringifyYaml } from 'yaml';
27
+ import { parse as parseYaml } from 'yaml';
27
28
  import { getAvailableTools } from './available-tools.js';
28
29
  import { migrateIfNeeded } from './migration.js';
30
+ import { runPrInitPrompt } from './pr-init-prompt.js';
29
31
  const require = createRequire(import.meta.url);
30
32
  const { version: PSCODE_VERSION } = require('../../package.json');
31
33
  // -----------------------------------------------------------------------------
@@ -50,6 +52,8 @@ const WORKFLOW_TO_SKILL_DIR = {
50
52
  // Trello-specific workflows
51
53
  'trello-setup': 'pscode-trello-setup',
52
54
  'draft': 'pscode-trello-draft',
55
+ // Productivity workflows
56
+ 'handoff': 'pscode-handoff',
53
57
  // Dixi-specific workflows
54
58
  'rfc': 'pscode-dixi-rfc',
55
59
  'design': 'pscode-dixi-design',
@@ -67,11 +71,13 @@ export class InitCommand {
67
71
  force;
68
72
  interactiveOption;
69
73
  profileOverride;
74
+ prFlag;
70
75
  constructor(options = {}) {
71
76
  this.toolsArg = options.tools;
72
77
  this.force = options.force ?? false;
73
78
  this.interactiveOption = options.interactive;
74
79
  this.profileOverride = options.profile;
80
+ this.prFlag = options.pr;
75
81
  }
76
82
  async execute(targetPath) {
77
83
  const projectPath = path.resolve(targetPath);
@@ -108,6 +114,8 @@ export class InitCommand {
108
114
  await this.createDirectoryStructure(pscodePath, extendMode);
109
115
  // Trello integration setup (interactive mode only)
110
116
  const trelloConfigured = await this.handleTrelloSetup(pscodePath);
117
+ // PR workflow setup (interactive or via flags)
118
+ const prConfig = await this.handlePrSetup(pscodePath, extendMode);
111
119
  // Generate skills and commands for each tool
112
120
  const results = await this.generateSkillsAndCommands(projectPath, validatedTools);
113
121
  // Dixi profile extras: stack detection and .pscode-dixi.yaml
@@ -118,7 +126,7 @@ export class InitCommand {
118
126
  await this.generateJiraFiles(projectPath);
119
127
  }
120
128
  // Create config.yaml if needed
121
- const configStatus = await this.createConfig(pscodePath, extendMode);
129
+ const configStatus = await this.createConfig(pscodePath, extendMode, prConfig ?? undefined);
122
130
  // Display success message
123
131
  this.displaySuccessMessage(projectPath, validatedTools, results, configStatus, trelloConfigured);
124
132
  }
@@ -405,6 +413,51 @@ export class InitCommand {
405
413
  }
406
414
  }
407
415
  // ═══════════════════════════════════════════════════════════
416
+ // PR WORKFLOW SETUP
417
+ // ═══════════════════════════════════════════════════════════
418
+ async handlePrSetup(pscodePath, extendMode) {
419
+ // --pr flag: enable PR without prompts (default values)
420
+ if (this.prFlag === true) {
421
+ return {
422
+ enabled: true,
423
+ branch: { pattern: 'feat/{change-name}' },
424
+ title: { template: '[{type}] {change-name}' },
425
+ description: { template: '## O que foi feito\n\n\n## Como testar\n\n\n## Referências\n' },
426
+ comments: { linkInTask: true },
427
+ };
428
+ }
429
+ // --no-pr flag: disable PR without prompts
430
+ if (this.prFlag === false) {
431
+ return { enabled: false };
432
+ }
433
+ // Non-interactive mode with no flag: skip PR config
434
+ if (!this.canPromptInteractively()) {
435
+ return null;
436
+ }
437
+ // Interactive mode: if config exists, ask if user wants to reconfigure PR
438
+ if (extendMode) {
439
+ const configPath = path.join(pscodePath, 'config.yaml');
440
+ const configYmlPath = path.join(pscodePath, 'config.yml');
441
+ const configExists = fs.existsSync(configPath) || fs.existsSync(configYmlPath);
442
+ if (configExists) {
443
+ const { confirm } = await import('@inquirer/prompts');
444
+ const wantsReconfigure = await confirm({
445
+ message: 'Reconfigurar preferências de PR?',
446
+ default: false,
447
+ });
448
+ if (!wantsReconfigure) {
449
+ return null; // Preserve existing PR config
450
+ }
451
+ }
452
+ }
453
+ try {
454
+ return await runPrInitPrompt();
455
+ }
456
+ catch {
457
+ return null;
458
+ }
459
+ }
460
+ // ═══════════════════════════════════════════════════════════
408
461
  // DIRECTORY STRUCTURE
409
462
  // ═══════════════════════════════════════════════════════════
410
463
  async createDirectoryStructure(pscodePath, extendMode) {
@@ -522,16 +575,36 @@ export class InitCommand {
522
575
  // ═══════════════════════════════════════════════════════════
523
576
  // CONFIG FILE
524
577
  // ═══════════════════════════════════════════════════════════
525
- async createConfig(pscodePath, extendMode) {
578
+ async createConfig(pscodePath, extendMode, prConfig) {
526
579
  const configPath = path.join(pscodePath, 'config.yaml');
527
580
  const configYmlPath = path.join(pscodePath, 'config.yml');
528
581
  const configYamlExists = fs.existsSync(configPath);
529
582
  const configYmlExists = fs.existsSync(configYmlPath);
530
583
  if (configYamlExists || configYmlExists) {
584
+ // If PR config was provided, merge it into the existing file
585
+ if (prConfig !== undefined) {
586
+ try {
587
+ const existingPath = configYamlExists ? configPath : configYmlPath;
588
+ const raw = parseYaml(fs.readFileSync(existingPath, 'utf-8'));
589
+ const existingSchema = (raw && typeof raw.schema === 'string') ? raw.schema : DEFAULT_SCHEMA;
590
+ const globalConfig = getGlobalConfig();
591
+ const resolvedProfile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
592
+ const schema = existingSchema || (resolvedProfile === 'dixi' ? 'pstld-workflow' : DEFAULT_SCHEMA);
593
+ const yamlContent = serializeConfig({ schema, pr: prConfig });
594
+ await FileSystemUtils.writeFile(configPath, yamlContent);
595
+ return 'updated';
596
+ }
597
+ catch {
598
+ return 'exists';
599
+ }
600
+ }
531
601
  return 'exists';
532
602
  }
533
603
  try {
534
- const yamlContent = serializeConfig({ schema: DEFAULT_SCHEMA });
604
+ const globalConfig = getGlobalConfig();
605
+ const resolvedProfile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
606
+ const schema = resolvedProfile === 'dixi' ? 'pstld-workflow' : DEFAULT_SCHEMA;
607
+ const yamlContent = serializeConfig({ schema, pr: prConfig });
535
608
  await FileSystemUtils.writeFile(configPath, yamlContent);
536
609
  return 'created';
537
610
  }
@@ -589,7 +662,13 @@ export class InitCommand {
589
662
  }
590
663
  // Config status
591
664
  if (configStatus === 'created') {
592
- console.log(`Config: pscode/config.yaml (schema: ${DEFAULT_SCHEMA})`);
665
+ const globalCfgForSchema = getGlobalConfig();
666
+ const profileForSchema = this.resolveProfileOverride() ?? (isValidProfile(globalCfgForSchema.profile ?? '') ? globalCfgForSchema.profile : DEFAULT_PROFILE);
667
+ const createdSchema = profileForSchema === 'dixi' ? 'pstld-workflow' : DEFAULT_SCHEMA;
668
+ console.log(`Config: pscode/config.yaml (schema: ${createdSchema})`);
669
+ }
670
+ else if (configStatus === 'updated') {
671
+ console.log(`Config: pscode/config.yaml (updated with PR config)`);
593
672
  }
594
673
  else if (configStatus === 'exists') {
595
674
  // Show actual filename (config.yaml or config.yml)
@@ -0,0 +1,13 @@
1
+ /**
2
+ * PR Init Prompt
3
+ *
4
+ * Handles the interactive PR workflow configuration questions during `pscode init`.
5
+ * Collects branch pattern, PR title/description templates, and tracker comment settings.
6
+ */
7
+ import type { PrConfig } from './project-config.js';
8
+ /**
9
+ * Runs the interactive PR configuration questions during `pscode init`.
10
+ * Returns the collected PrConfig, or null if the user opts out.
11
+ */
12
+ export declare function runPrInitPrompt(): Promise<PrConfig | null>;
13
+ //# sourceMappingURL=pr-init-prompt.d.ts.map
@@ -0,0 +1,76 @@
1
+ /**
2
+ * PR Init Prompt
3
+ *
4
+ * Handles the interactive PR workflow configuration questions during `pscode init`.
5
+ * Collects branch pattern, PR title/description templates, and tracker comment settings.
6
+ */
7
+ import chalk from 'chalk';
8
+ const DEFAULT_BRANCH_PATTERN = 'feat/{change-name}';
9
+ const DEFAULT_TITLE_TEMPLATE = '[{type}] {change-name}';
10
+ const DEFAULT_DESCRIPTION_TEMPLATE = '## O que foi feito\n\n\n## Como testar\n\n\n## Referências\n';
11
+ /**
12
+ * Runs the interactive PR configuration questions during `pscode init`.
13
+ * Returns the collected PrConfig, or null if the user opts out.
14
+ */
15
+ export async function runPrInitPrompt() {
16
+ const { confirm, input } = await import('@inquirer/prompts');
17
+ console.log();
18
+ console.log(chalk.bold('Workflow de PR'));
19
+ console.log(chalk.dim(' Configure padrões de branch, título e descrição para Pull Requests.'));
20
+ console.log(chalk.dim(' Template variables: {change-name}, {type}, {ticket}'));
21
+ console.log();
22
+ const wantsPr = await confirm({
23
+ message: 'Usar workflow de PR neste projeto? (branch dedicada + PR obrigatório)',
24
+ default: false,
25
+ });
26
+ if (!wantsPr) {
27
+ return { enabled: false };
28
+ }
29
+ const branchPattern = await input({
30
+ message: 'Padrão de nome de branch:',
31
+ default: DEFAULT_BRANCH_PATTERN,
32
+ });
33
+ const titleTemplate = await input({
34
+ message: 'Template de título do PR:',
35
+ default: DEFAULT_TITLE_TEMPLATE,
36
+ });
37
+ const useDefaultDescription = await confirm({
38
+ message: 'Usar template padrão de descrição de PR?',
39
+ default: true,
40
+ });
41
+ let descriptionTemplate;
42
+ if (useDefaultDescription) {
43
+ descriptionTemplate = DEFAULT_DESCRIPTION_TEMPLATE;
44
+ }
45
+ else {
46
+ const customDesc = await input({
47
+ message: 'Caminho para arquivo .md com template de descrição (ou deixe vazio para usar o padrão):',
48
+ default: '',
49
+ });
50
+ if (customDesc.trim()) {
51
+ try {
52
+ const { readFileSync } = await import('fs');
53
+ descriptionTemplate = readFileSync(customDesc.trim(), 'utf-8');
54
+ }
55
+ catch {
56
+ console.log(chalk.yellow(` Arquivo não encontrado — usando template padrão.`));
57
+ descriptionTemplate = DEFAULT_DESCRIPTION_TEMPLATE;
58
+ }
59
+ }
60
+ else {
61
+ descriptionTemplate = DEFAULT_DESCRIPTION_TEMPLATE;
62
+ }
63
+ }
64
+ const linkInTask = await confirm({
65
+ message: 'Comentar link do PR na tarefa (Trello/Jira)?',
66
+ default: true,
67
+ });
68
+ return {
69
+ enabled: true,
70
+ branch: { pattern: branchPattern.trim() || DEFAULT_BRANCH_PATTERN },
71
+ title: { template: titleTemplate.trim() || DEFAULT_TITLE_TEMPLATE },
72
+ description: { template: descriptionTemplate },
73
+ comments: { linkInTask },
74
+ };
75
+ }
76
+ //# sourceMappingURL=pr-init-prompt.js.map
@@ -28,5 +28,6 @@ export declare function applyFeatureSlicedSkeleton(projectRoot: string): {
28
28
  skipped: number;
29
29
  };
30
30
  export declare function installEslintArchitectureTemplate(projectRoot: string): void;
31
+ export declare function copyDixiCommands(destRoot: string, srcDir: string, subdir: string): void;
31
32
  export declare function installDixiExtras(projectDir: string, stack: DixiStack | null): void;
32
33
  //# sourceMappingURL=dixi.d.ts.map
@@ -141,8 +141,6 @@ export function installDixiClaudeMd(projectDir, family) {
141
141
  fs.writeFileSync(claudeMdPath, templateContent, { encoding: 'utf-8' });
142
142
  }
143
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
144
  const HOOKS_SRC = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'pscode', 'content', 'dixi', 'claude-runtime', 'hooks');
147
145
  const DIXI_HOOK_ENTRIES = [
148
146
  { event: 'PreToolUse', matcher: 'Edit|Write', command: 'node .claude/hooks/arch-guard.mjs' },
@@ -296,6 +294,18 @@ export function installEslintArchitectureTemplate(projectRoot) {
296
294
  ' import architectureRules from \'./eslint-architecture.mjs\';\n' +
297
295
  ' export default [...existingConfig, ...architectureRules];\n');
298
296
  }
297
+ export function copyDixiCommands(destRoot, srcDir, subdir) {
298
+ if (!fs.existsSync(srcDir))
299
+ return;
300
+ const destDir = path.join(destRoot, '.claude', 'commands', subdir);
301
+ if (!fs.existsSync(destDir)) {
302
+ fs.mkdirSync(destDir, { recursive: true });
303
+ }
304
+ const files = fs.readdirSync(srcDir);
305
+ for (const file of files) {
306
+ fs.copyFileSync(path.join(srcDir, file), path.join(destDir, file));
307
+ }
308
+ }
299
309
  export function installDixiExtras(projectDir, stack) {
300
310
  const family = getDixiStackFamily(stack);
301
311
  // Resolve package content root: dist/core/presets/ → package root → pscode/content/dixi/
@@ -362,29 +372,6 @@ export function installDixiExtras(projectDir, stack) {
362
372
  else {
363
373
  console.log('[dixi] skeleton arquitetural: stack não reconhecida — skeleton não disponível para esta stack');
364
374
  }
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
375
  // Copy hooks to .claude/hooks/ (brownfield-safe: skip if file already exists)
389
376
  const hooksDestDir = path.join(projectDir, '.claude', 'hooks');
390
377
  if (!fs.existsSync(hooksDestDir)) {
@@ -401,5 +388,8 @@ export function installDixiExtras(projectDir, stack) {
401
388
  }
402
389
  // Merge .claude/settings.json with hook registrations (never overwrite existing config)
403
390
  mergeSettingsHooks(path.join(projectDir, '.claude', 'settings.json'));
391
+ // Copy Dixi-aware /ps:* overrides and exclusive /pstld:* commands (always overwrite)
392
+ const commandsBase = path.join(packageRoot, 'pscode', 'content', 'dixi', 'commands');
393
+ copyDixiCommands(projectDir, path.join(commandsBase, 'ps'), 'ps');
404
394
  }
405
395
  //# sourceMappingURL=dixi.js.map
@@ -21,6 +21,8 @@ export const WORKFLOW_TO_SKILL_DIR = {
21
21
  // Trello-specific workflows
22
22
  'trello-setup': 'pscode-trello-setup',
23
23
  'draft': 'pscode-trello-draft',
24
+ // Productivity workflows
25
+ 'handoff': 'pscode-handoff',
24
26
  // Dixi-specific workflows
25
27
  'rfc': 'pscode-dixi-rfc',
26
28
  'design': 'pscode-dixi-design',
@@ -5,7 +5,7 @@
5
5
  * `pscode init --profile <name>` or `pscode config profile <name>`.
6
6
  * The workflow lists are fixed in code — users cannot customise them.
7
7
  */
8
- export declare const ALL_WORKFLOWS: readonly ["propose", "explore", "new", "continue", "apply", "ff", "complete", "bulk-archive", "verify", "onboard", "trello-setup", "draft", "rfc", "design", "tasks", "arch-check", "adr", "jira-sync", "dod"];
8
+ export declare const ALL_WORKFLOWS: readonly ["propose", "explore", "new", "continue", "apply", "ff", "complete", "bulk-archive", "verify", "onboard", "trello-setup", "draft", "rfc", "design", "tasks", "arch-check", "adr", "jira-sync", "dod", "handoff"];
9
9
  export type WorkflowId = (typeof ALL_WORKFLOWS)[number];
10
10
  export interface ProfileDefinition {
11
11
  description: string;
@@ -14,11 +14,11 @@ export interface ProfileDefinition {
14
14
  export declare const PROFILES: {
15
15
  readonly standard: {
16
16
  readonly description: "Padrão — propose, explore, apply, complete";
17
- readonly workflows: readonly ["propose", "explore", "apply", "complete", "trello-setup", "draft"];
17
+ readonly workflows: readonly ["propose", "explore", "apply", "complete", "trello-setup", "draft", "handoff"];
18
18
  };
19
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"];
20
+ readonly description: "Dixi — propose, explore, apply, complete com guardrails para Java/Spring e React/Next.js";
21
+ readonly workflows: readonly ["propose", "explore", "apply", "complete", "trello-setup", "draft", "handoff"];
22
22
  };
23
23
  };
24
24
  export type ProfileName = keyof typeof PROFILES;
@@ -25,15 +25,16 @@ export const ALL_WORKFLOWS = [
25
25
  'adr',
26
26
  'jira-sync',
27
27
  'dod',
28
+ 'handoff',
28
29
  ];
29
30
  export const PROFILES = {
30
31
  standard: {
31
32
  description: 'Padrão — propose, explore, apply, complete',
32
- workflows: ['propose', 'explore', 'apply', 'complete', 'trello-setup', 'draft'],
33
+ workflows: ['propose', 'explore', 'apply', 'complete', 'trello-setup', 'draft', 'handoff'],
33
34
  },
34
35
  dixi: {
35
- description: 'Dixi — RFC→Design→Tasks→Apply com guardrails para Java/Spring e React/Next.js',
36
- workflows: ['rfc', 'design', 'tasks', 'apply', 'arch-check', 'adr', 'jira-sync', 'dod'],
36
+ description: 'Dixi — propose, explore, apply, complete com guardrails para Java/Spring e React/Next.js',
37
+ workflows: ['propose', 'explore', 'apply', 'complete', 'trello-setup', 'draft', 'handoff'],
37
38
  },
38
39
  };
39
40
  export const DEFAULT_PROFILE = 'standard';
@@ -1,4 +1,20 @@
1
1
  import { z } from 'zod';
2
+ export declare const PrConfigSchema: z.ZodObject<{
3
+ enabled: z.ZodBoolean;
4
+ branch: z.ZodOptional<z.ZodObject<{
5
+ pattern: z.ZodString;
6
+ }, z.core.$strip>>;
7
+ title: z.ZodOptional<z.ZodObject<{
8
+ template: z.ZodString;
9
+ }, z.core.$strip>>;
10
+ description: z.ZodOptional<z.ZodObject<{
11
+ template: z.ZodString;
12
+ }, z.core.$strip>>;
13
+ comments: z.ZodOptional<z.ZodObject<{
14
+ linkInTask: z.ZodBoolean;
15
+ }, z.core.$strip>>;
16
+ }, z.core.$strip>;
17
+ export type PrConfig = z.infer<typeof PrConfigSchema>;
2
18
  /**
3
19
  * Zod schema for project configuration.
4
20
  *
@@ -16,6 +32,21 @@ export declare const ProjectConfigSchema: z.ZodObject<{
16
32
  schema: z.ZodString;
17
33
  context: z.ZodOptional<z.ZodString>;
18
34
  rules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
35
+ pr: z.ZodOptional<z.ZodObject<{
36
+ enabled: z.ZodBoolean;
37
+ branch: z.ZodOptional<z.ZodObject<{
38
+ pattern: z.ZodString;
39
+ }, z.core.$strip>>;
40
+ title: z.ZodOptional<z.ZodObject<{
41
+ template: z.ZodString;
42
+ }, z.core.$strip>>;
43
+ description: z.ZodOptional<z.ZodObject<{
44
+ template: z.ZodString;
45
+ }, z.core.$strip>>;
46
+ comments: z.ZodOptional<z.ZodObject<{
47
+ linkInTask: z.ZodBoolean;
48
+ }, z.core.$strip>>;
49
+ }, z.core.$strip>>;
19
50
  }, z.core.$strip>;
20
51
  export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;
21
52
  /**
@@ -2,6 +2,13 @@ import { existsSync, readFileSync } from 'fs';
2
2
  import path from 'path';
3
3
  import { parse as parseYaml } from 'yaml';
4
4
  import { z } from 'zod';
5
+ export const PrConfigSchema = z.object({
6
+ enabled: z.boolean(),
7
+ branch: z.object({ pattern: z.string() }).optional(),
8
+ title: z.object({ template: z.string() }).optional(),
9
+ description: z.object({ template: z.string() }).optional(),
10
+ comments: z.object({ linkInTask: z.boolean() }).optional(),
11
+ });
5
12
  /**
6
13
  * Zod schema for project configuration.
7
14
  *
@@ -34,6 +41,8 @@ export const ProjectConfigSchema = z.object({
34
41
  )
35
42
  .optional()
36
43
  .describe('Per-artifact rules, keyed by artifact ID'),
44
+ // Optional: PR workflow configuration
45
+ pr: PrConfigSchema.optional().describe('PR workflow configuration'),
37
46
  });
38
47
  const MAX_CONTEXT_SIZE = 50 * 1024; // 50KB hard limit
39
48
  /**
@@ -99,6 +108,21 @@ export function readProjectConfig(projectRoot) {
99
108
  console.warn(`Invalid 'context' field in config (must be string)`);
100
109
  }
101
110
  }
111
+ // Parse pr field using Zod safeParse
112
+ if (raw.pr !== undefined) {
113
+ if (typeof raw.pr === 'object' && raw.pr !== null) {
114
+ const prResult = PrConfigSchema.safeParse(raw.pr);
115
+ if (prResult.success) {
116
+ config.pr = prResult.data;
117
+ }
118
+ else {
119
+ console.warn(`Invalid 'pr' field in config: ${prResult.error.issues.map((i) => i.message).join(', ')}`);
120
+ }
121
+ }
122
+ else {
123
+ console.warn(`Invalid 'pr' field in config (must be object)`);
124
+ }
125
+ }
102
126
  // Parse rules field using Zod
103
127
  if (raw.rules !== undefined) {
104
128
  const rulesField = z.record(z.string(), z.array(z.string()));
@@ -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, getCompleteChangeSkillTemplate, getBulkArchiveChangeSkillTemplate, getVerifyChangeSkillTemplate, getOnboardSkillTemplate, getProposeSkillTemplate, getTrelloSetupSkillTemplate, getTrelloDraftSkillTemplate, getPsExploreCommandTemplate, getPsNewCommandTemplate, getPsContinueCommandTemplate, getPsApplyCommandTemplate, getPsFfCommandTemplate, getPsCompleteCommandTemplate, getPsBulkArchiveCommandTemplate, getPsVerifyCommandTemplate, getPsOnboardCommandTemplate, getPsProposeCommandTemplate, getTrelloSetupCommandTemplate, getTrelloDraftCommandTemplate, } from '../templates/skill-templates.js';
6
+ import { getExploreSkillTemplate, getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getFfChangeSkillTemplate, getCompleteChangeSkillTemplate, getBulkArchiveChangeSkillTemplate, getVerifyChangeSkillTemplate, getOnboardSkillTemplate, getProposeSkillTemplate, getTrelloSetupSkillTemplate, getTrelloDraftSkillTemplate, getHandoffSkillTemplate, getPsExploreCommandTemplate, getPsNewCommandTemplate, getPsContinueCommandTemplate, getPsApplyCommandTemplate, getPsFfCommandTemplate, getPsCompleteCommandTemplate, getPsBulkArchiveCommandTemplate, getPsVerifyCommandTemplate, getPsOnboardCommandTemplate, getPsProposeCommandTemplate, getTrelloSetupCommandTemplate, getTrelloDraftCommandTemplate, getHandoffCommandTemplate, } from '../templates/skill-templates.js';
7
7
  /**
8
8
  * Gets skill templates with their directory names, optionally filtered by workflow IDs.
9
9
  *
@@ -24,6 +24,8 @@ export function getSkillTemplates(workflowFilter) {
24
24
  // Trello-specific workflows
25
25
  { template: getTrelloSetupSkillTemplate(), dirName: 'pscode-trello-setup', workflowId: 'trello-setup' },
26
26
  { template: getTrelloDraftSkillTemplate(), dirName: 'pscode-trello-draft', workflowId: 'draft' },
27
+ // Productivity workflows
28
+ { template: getHandoffSkillTemplate(), dirName: 'pscode-handoff', workflowId: 'handoff' },
27
29
  ];
28
30
  if (!workflowFilter)
29
31
  return all;
@@ -50,6 +52,8 @@ export function getCommandTemplates(workflowFilter) {
50
52
  // Trello-specific workflows
51
53
  { template: getTrelloSetupCommandTemplate(), id: 'trello-setup' },
52
54
  { template: getTrelloDraftCommandTemplate(), id: 'draft' },
55
+ // Productivity workflows
56
+ { template: getHandoffCommandTemplate(), id: 'handoff' },
53
57
  ];
54
58
  if (!workflowFilter)
55
59
  return all;
@@ -86,7 +90,6 @@ export function generateSkillContent(template, generatedByVersion, transformInst
86
90
  return `---
87
91
  name: ${template.name}
88
92
  description: ${template.description}
89
- license: ${template.license || 'MIT'}
90
93
  compatibility: ${template.compatibility || 'Requires pscode CLI.'}
91
94
  metadata:
92
95
  author: ${template.metadata?.author || 'pscode'}
@@ -17,4 +17,5 @@ export { getProposeSkillTemplate, getPsProposeCommandTemplate } from './workflow
17
17
  export { getFeedbackSkillTemplate } from './workflows/feedback.js';
18
18
  export { getTrelloSetupSkillTemplate, getTrelloSetupCommandTemplate } from './workflows/trello-setup.js';
19
19
  export { getTrelloDraftSkillTemplate, getTrelloDraftCommandTemplate } from './workflows/trello-draft.js';
20
+ export { getHandoffSkillTemplate, getHandoffCommandTemplate } from './workflows/handoff.js';
20
21
  //# sourceMappingURL=skill-templates.d.ts.map
@@ -17,4 +17,6 @@ export { getFeedbackSkillTemplate } from './workflows/feedback.js';
17
17
  // Trello-specific workflows
18
18
  export { getTrelloSetupSkillTemplate, getTrelloSetupCommandTemplate } from './workflows/trello-setup.js';
19
19
  export { getTrelloDraftSkillTemplate, getTrelloDraftCommandTemplate } from './workflows/trello-draft.js';
20
+ // Productivity workflows
21
+ export { getHandoffSkillTemplate, getHandoffCommandTemplate } from './workflows/handoff.js';
20
22
  //# sourceMappingURL=skill-templates.js.map
@@ -5,7 +5,6 @@ export interface SkillTemplate {
5
5
  name: string;
6
6
  description: string;
7
7
  instructions: string;
8
- license?: string;
9
8
  compatibility?: string;
10
9
  metadata?: Record<string, string>;
11
10
  }