@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
@@ -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);
@@ -41,7 +43,7 @@ const WORKFLOW_TO_SKILL_DIR = {
41
43
  'apply': 'pscode-apply-change',
42
44
  'ff': 'pscode-ff-change',
43
45
  'sync': 'pscode-sync-specs',
44
- 'archive': 'pscode-archive-change',
46
+ 'complete': 'pscode-archive-change',
45
47
  'bulk-archive': 'pscode-bulk-archive-change',
46
48
  'verify': 'pscode-verify-change',
47
49
  'onboard': 'pscode-onboard',
@@ -49,6 +51,14 @@ const WORKFLOW_TO_SKILL_DIR = {
49
51
  // Trello-specific workflows
50
52
  'trello-setup': 'pscode-trello-setup',
51
53
  'draft': 'pscode-trello-draft',
54
+ // Dixi-specific workflows
55
+ 'rfc': 'pscode-dixi-rfc',
56
+ 'design': 'pscode-dixi-design',
57
+ 'tasks': 'pscode-dixi-tasks',
58
+ 'arch-check': 'pscode-dixi-arch-check',
59
+ 'adr': 'pscode-dixi-adr',
60
+ 'jira-sync': 'pscode-dixi-jira-sync',
61
+ 'dod': 'pscode-dixi-dod',
52
62
  };
53
63
  // -----------------------------------------------------------------------------
54
64
  // Init Command Class
@@ -101,6 +111,13 @@ export class InitCommand {
101
111
  const trelloConfigured = await this.handleTrelloSetup(pscodePath);
102
112
  // Generate skills and commands for each tool
103
113
  const results = await this.generateSkillsAndCommands(projectPath, validatedTools);
114
+ // Dixi profile extras: stack detection and .pscode-dixi.yaml
115
+ await this.handleDixiExtras(projectPath);
116
+ // Dixi profile: generate JIRA integration files
117
+ const resolvedProfile = this.resolveProfileOverride() ?? (isValidProfile(getGlobalConfig().profile ?? '') ? getGlobalConfig().profile : DEFAULT_PROFILE);
118
+ if (resolvedProfile === 'dixi') {
119
+ await this.generateJiraFiles(projectPath);
120
+ }
104
121
  // Create config.yaml if needed
105
122
  const configStatus = await this.createConfig(pscodePath, extendMode);
106
123
  // Display success message
@@ -128,10 +145,11 @@ export class InitCommand {
128
145
  if (this.profileOverride === undefined) {
129
146
  return undefined;
130
147
  }
131
- if (this.profileOverride === 'core' || this.profileOverride === 'custom') {
148
+ if (isValidProfile(this.profileOverride)) {
132
149
  return this.profileOverride;
133
150
  }
134
- throw new Error(`Invalid profile "${this.profileOverride}". Available profiles: core, custom`);
151
+ const available = Object.keys(PROFILES).join(', ');
152
+ throw new Error(`Invalid profile "${this.profileOverride}". Available profiles: ${available}`);
135
153
  }
136
154
  // ═══════════════════════════════════════════════════════════
137
155
  // LEGACY TOOL MIGRATION
@@ -431,14 +449,9 @@ export class InitCommand {
431
449
  let removedSkillCount = 0;
432
450
  // Read global config for profile and delivery settings (use --profile override if set)
433
451
  const globalConfig = getGlobalConfig();
434
- const profile = this.resolveProfileOverride() ?? globalConfig.profile ?? 'core';
452
+ const profile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
435
453
  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];
454
+ const workflows = [...getProfileWorkflows(profile)];
442
455
  // Get skill and command templates filtered by profile workflows
443
456
  const shouldGenerateSkills = delivery !== 'commands';
444
457
  const shouldGenerateCommands = delivery !== 'skills';
@@ -541,15 +554,13 @@ export class InitCommand {
541
554
  if (results.refreshedTools.length > 0) {
542
555
  console.log(`Refreshed: ${results.refreshedTools.map((t) => t.name).join(', ')}`);
543
556
  }
544
- // Show counts (respecting profile filter + trello workflows always included)
557
+ // Show counts
545
558
  const successfulTools = [...results.createdTools, ...results.refreshedTools];
546
559
  if (successfulTools.length > 0) {
547
560
  const globalConfig = getGlobalConfig();
548
- const profile = this.profileOverride ?? globalConfig.profile ?? 'core';
561
+ const profile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
549
562
  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])];
563
+ const workflows = [...getProfileWorkflows(profile)];
553
564
  const toolDirs = [...new Set(successfulTools.map((t) => t.skillsDir))].join(', ');
554
565
  const skillCount = delivery !== 'commands' ? getSkillTemplates(workflows).length : 0;
555
566
  const commandCount = delivery !== 'skills' ? getCommandContents(workflows).length : 0;
@@ -591,10 +602,10 @@ export class InitCommand {
591
602
  else {
592
603
  console.log(chalk.dim(`Config: skipped (non-interactive mode)`));
593
604
  }
594
- // Getting started (task 7.6: show propose if in profile)
605
+ // Getting started
595
606
  const globalCfg = getGlobalConfig();
596
- const activeProfile = this.profileOverride ?? globalCfg.profile ?? 'core';
597
- const activeWorkflows = [...getProfileWorkflows(activeProfile, globalCfg.workflows)];
607
+ const activeProfile = this.resolveProfileOverride() ?? (isValidProfile(globalCfg.profile ?? '') ? globalCfg.profile : DEFAULT_PROFILE);
608
+ const activeWorkflows = [...getProfileWorkflows(activeProfile)];
598
609
  console.log();
599
610
  if (activeWorkflows.includes('propose')) {
600
611
  console.log(chalk.bold('Getting started:'));
@@ -605,7 +616,7 @@ export class InitCommand {
605
616
  console.log(' Start your first change: /ps:new "your idea"');
606
617
  }
607
618
  else {
608
- console.log("Done. Run 'pscode config profile' to configure your workflows.");
619
+ console.log("Done. Run 'pscode config profile' to switch profiles.");
609
620
  }
610
621
  // Trello status
611
622
  if (trelloConfigured) {
@@ -625,6 +636,56 @@ export class InitCommand {
625
636
  }
626
637
  console.log();
627
638
  }
639
+ async generateJiraFiles(projectPath) {
640
+ const pastelsddPath = path.join(projectPath, 'pastelsdd');
641
+ await FileSystemUtils.createDirectory(pastelsddPath);
642
+ const jiraYamlPath = path.join(pastelsddPath, 'jira.yaml');
643
+ if (!fs.existsSync(jiraYamlPath)) {
644
+ const content = `project_key: ""\nboard_url: ""\nconfigured: false\ntransitions:\n done: ""\n`;
645
+ await FileSystemUtils.writeFile(jiraYamlPath, content);
646
+ }
647
+ const mcpJsonPath = path.join(projectPath, '.mcp.json');
648
+ let mcpConfig = {};
649
+ if (fs.existsSync(mcpJsonPath)) {
650
+ try {
651
+ const raw = fs.readFileSync(mcpJsonPath, 'utf-8');
652
+ mcpConfig = JSON.parse(raw);
653
+ }
654
+ catch {
655
+ console.log('Aviso: .mcp.json inválido — recriando com entrada Atlassian.');
656
+ mcpConfig = {};
657
+ }
658
+ }
659
+ const mcpServers = (mcpConfig.mcpServers ?? {});
660
+ if (!mcpServers['atlassian']) {
661
+ mcpServers['atlassian'] = {
662
+ command: 'npx',
663
+ args: ['-y', 'mcp-remote', 'https://mcp.atlassian.com/v1/sse'],
664
+ };
665
+ mcpConfig.mcpServers = mcpServers;
666
+ await FileSystemUtils.writeFile(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
667
+ }
668
+ console.log('JIRA: edite pastelsdd/jira.yaml com project_key e board_url, depois use /pstld:jira-sync para testar a conexão.');
669
+ }
670
+ async handleDixiExtras(projectPath) {
671
+ const globalConfig = getGlobalConfig();
672
+ const profile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
673
+ if (profile !== 'dixi')
674
+ return;
675
+ const stack = detectDixiStack(projectPath);
676
+ const label = getDixiStackLabel(stack);
677
+ if (stack) {
678
+ console.log(`Dixi: stack detectada — ${label}`);
679
+ }
680
+ else {
681
+ console.log('Dixi: stack não detectada, usando configuração genérica');
682
+ }
683
+ installDixiExtras(projectPath, stack);
684
+ const family = getDixiStackFamily(stack);
685
+ const yamlContent = stringifyYaml({ stack, family, detectedAt: new Date().toISOString() });
686
+ const dixiYamlPath = path.join(projectPath, '.pscode-dixi.yaml');
687
+ await FileSystemUtils.writeFile(dixiYamlPath, yamlContent);
688
+ }
628
689
  startSpinner(text) {
629
690
  return ora({
630
691
  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
@@ -1,19 +1,20 @@
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
- import { getGlobalConfig, getGlobalConfigPath, saveGlobalConfig } from './global-config.js';
8
7
  import { CommandAdapterRegistry } from './command-generation/index.js';
9
8
  import { WORKFLOW_TO_SKILL_DIR } from './profile-sync-drift.js';
10
9
  import { ALL_WORKFLOWS } from './profiles.js';
11
10
  import path from 'path';
12
11
  import * as fs from 'fs';
13
- function scanInstalledWorkflowArtifacts(projectPath, tools) {
12
+ /**
13
+ * Scans installed workflow files across all detected tools and returns
14
+ * the union of installed workflow IDs.
15
+ */
16
+ export function scanInstalledWorkflows(projectPath, tools) {
14
17
  const installed = new Set();
15
- let hasSkills = false;
16
- let hasCommands = false;
17
18
  for (const tool of tools) {
18
19
  if (!tool.skillsDir)
19
20
  continue;
@@ -23,7 +24,6 @@ function scanInstalledWorkflowArtifacts(projectPath, tools) {
23
24
  const skillFile = path.join(skillsDir, skillDirName, 'SKILL.md');
24
25
  if (fs.existsSync(skillFile)) {
25
26
  installed.add(workflowId);
26
- hasSkills = true;
27
27
  }
28
28
  }
29
29
  const adapter = CommandAdapterRegistry.get(tool.value);
@@ -36,73 +36,11 @@ function scanInstalledWorkflowArtifacts(projectPath, tools) {
36
36
  : path.join(projectPath, commandPath);
37
37
  if (fs.existsSync(fullPath)) {
38
38
  installed.add(workflowId);
39
- hasCommands = true;
40
39
  }
41
40
  }
42
41
  }
43
- return {
44
- workflows: ALL_WORKFLOWS.filter((workflowId) => installed.has(workflowId)),
45
- hasSkills,
46
- hasCommands,
47
- };
48
- }
49
- /**
50
- * Scans installed workflow files across all detected tools and returns
51
- * the union of installed workflow IDs.
52
- */
53
- export function scanInstalledWorkflows(projectPath, tools) {
54
- return scanInstalledWorkflowArtifacts(projectPath, tools).workflows;
55
- }
56
- function inferDelivery(artifacts) {
57
- if (artifacts.hasSkills && artifacts.hasCommands) {
58
- return 'both';
59
- }
60
- if (artifacts.hasCommands) {
61
- return 'commands';
62
- }
63
- return 'skills';
64
- }
65
- /**
66
- * Performs one-time migration if the global config does not yet have a profile field.
67
- * Called by both init and update before profile resolution.
68
- *
69
- * - If no profile field exists and workflows are installed: sets profile to 'custom'
70
- * with the detected workflows, preserving the user's existing setup.
71
- * - If no profile field exists and no workflows are installed: no-op (defaults apply).
72
- * - If profile field already exists: no-op.
73
- */
74
- export function migrateIfNeeded(projectPath, tools) {
75
- const config = getGlobalConfig();
76
- // Check raw config file for profile field presence
77
- const configPath = getGlobalConfigPath();
78
- let rawConfig = {};
79
- try {
80
- if (fs.existsSync(configPath)) {
81
- rawConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
82
- }
83
- }
84
- catch {
85
- return; // Can't read config, skip migration
86
- }
87
- // If profile is already explicitly set, no migration needed
88
- if (rawConfig.profile !== undefined) {
89
- return;
90
- }
91
- // Scan for installed workflows
92
- const artifacts = scanInstalledWorkflowArtifacts(projectPath, tools);
93
- const installedWorkflows = artifacts.workflows;
94
- if (installedWorkflows.length === 0) {
95
- // No workflows installed, new user — defaults will apply
96
- return;
97
- }
98
- // Migrate: set profile to custom with detected workflows
99
- config.profile = 'custom';
100
- config.workflows = installedWorkflows;
101
- if (rawConfig.delivery === undefined) {
102
- config.delivery = inferDelivery(artifacts);
103
- }
104
- saveGlobalConfig(config);
105
- console.log(`Migrated: custom profile with ${installedWorkflows.length} workflows`);
106
- console.log("New in this version: /ps:propose. Try 'pscode config profile core' for the streamlined experience.");
42
+ return ALL_WORKFLOWS.filter((id) => installed.has(id));
107
43
  }
44
+ // No-op kept for call-site compatibility during transition.
45
+ export function migrateIfNeeded(_projectPath, _tools) { }
108
46
  //# sourceMappingURL=migration.js.map
@@ -0,0 +1,32 @@
1
+ export type DixiStack = 'java-maven' | 'java-gradle' | 'next' | 'react' | 'node' | 'python';
2
+ export type DixiStackFamily = 'java' | 'react' | 'node' | 'python';
3
+ export declare function detectDixiStack(projectDir: string): DixiStack | null;
4
+ export declare function getDixiStackFamily(stack: DixiStack | null): DixiStackFamily | null;
5
+ export declare function getDixiStackLabel(stack: DixiStack | null): string;
6
+ /**
7
+ * Copies files recursively from sourceDir to targetDir.
8
+ * By default skips files that already exist (brownfield-safe).
9
+ * Files whose basename appears in options.overwrite are always overwritten.
10
+ */
11
+ export declare function copyKitFiles(sourceDir: string, targetDir: string, options?: {
12
+ overwrite?: string[];
13
+ }): void;
14
+ /**
15
+ * Copies all files from srcDir into <destRoot>/pastelsdd/context/, skipping files that already exist.
16
+ * Creates the destination directory if needed.
17
+ */
18
+ export declare function copyContextDocs(destRoot: string, srcDir: string): void;
19
+ export declare function installDixiClaudeMd(projectDir: string, family: DixiStackFamily | null): void;
20
+ export declare function detectBasePackage(projectRoot: string): string;
21
+ export declare function applyHexagonalSkeleton(projectRoot: string, basePackage: string): {
22
+ created: number;
23
+ skipped: number;
24
+ };
25
+ export declare function generateArchitectureTest(projectRoot: string, basePackage: string): boolean;
26
+ export declare function applyFeatureSlicedSkeleton(projectRoot: string): {
27
+ created: number;
28
+ skipped: number;
29
+ };
30
+ export declare function installEslintArchitectureTemplate(projectRoot: string): void;
31
+ export declare function installDixiExtras(projectDir: string, stack: DixiStack | null): void;
32
+ //# sourceMappingURL=dixi.d.ts.map