@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.
- package/dist/cli/index.js +3 -0
- package/dist/core/completions/command-registry.js +8 -0
- package/dist/core/config-prompts.d.ts +1 -1
- package/dist/core/config-prompts.js +8 -1
- package/dist/core/init.d.ts +3 -0
- package/dist/core/init.js +83 -4
- package/dist/core/pr-init-prompt.d.ts +13 -0
- package/dist/core/pr-init-prompt.js +76 -0
- package/dist/core/presets/dixi.d.ts +1 -0
- package/dist/core/presets/dixi.js +15 -25
- package/dist/core/profile-sync-drift.js +2 -0
- package/dist/core/profiles.d.ts +4 -4
- package/dist/core/profiles.js +4 -3
- package/dist/core/project-config.d.ts +31 -0
- package/dist/core/project-config.js +24 -0
- package/dist/core/shared/skill-generation.js +5 -2
- package/dist/core/templates/skill-templates.d.ts +1 -0
- package/dist/core/templates/skill-templates.js +2 -0
- package/dist/core/templates/types.d.ts +0 -1
- package/dist/core/templates/workflows/apply-change.js +27 -3
- package/dist/core/templates/workflows/archive-change.js +0 -1
- package/dist/core/templates/workflows/bulk-archive-change.js +0 -1
- package/dist/core/templates/workflows/continue-change.js +0 -1
- package/dist/core/templates/workflows/explore.js +0 -1
- package/dist/core/templates/workflows/feedback.js +0 -1
- package/dist/core/templates/workflows/ff-change.js +0 -1
- package/dist/core/templates/workflows/handoff.d.ts +10 -0
- package/dist/core/templates/workflows/handoff.js +31 -0
- package/dist/core/templates/workflows/new-change.js +0 -1
- package/dist/core/templates/workflows/onboard.js +0 -1
- package/dist/core/templates/workflows/propose.js +45 -31
- package/dist/core/templates/workflows/trello-draft.js +4 -16
- package/dist/core/templates/workflows/trello-next-step-comment.d.ts +22 -15
- package/dist/core/templates/workflows/trello-next-step-comment.js +69 -31
- package/dist/core/templates/workflows/trello-setup.js +0 -1
- package/dist/core/templates/workflows/verify-change.js +0 -1
- package/package.json +1 -1
- package/pscode/content/dixi/commands/ps/apply.md +15 -0
- package/pscode/content/dixi/commands/ps/archive.md +14 -0
- package/pscode/content/dixi/commands/ps/complete.md +14 -0
- package/pscode/content/dixi/commands/ps/draft.md +15 -0
- package/pscode/content/dixi/commands/ps/explore.md +14 -0
- package/pscode/content/dixi/commands/ps/propose.md +14 -0
- package/pscode/content/dixi/commands/ps/trello-setup.md +14 -0
- package/pscode/content/dixi/commands/pstld/adr.md +75 -0
- package/pscode/content/dixi/commands/pstld/arch-check.md +64 -0
- package/pscode/content/dixi/commands/pstld/dod.md +66 -0
- 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.');
|
package/dist/core/init.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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',
|
package/dist/core/profiles.d.ts
CHANGED
|
@@ -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 —
|
|
21
|
-
readonly workflows: readonly ["
|
|
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;
|
package/dist/core/profiles.js
CHANGED
|
@@ -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 —
|
|
36
|
-
workflows: ['
|
|
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
|