@thiagodiogo/pscode 2.1.1 → 2.2.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.
- 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 +76 -3
- package/dist/core/pr-init-prompt.d.ts +13 -0
- package/dist/core/pr-init-prompt.js +76 -0
- package/dist/core/profile-sync-drift.js +2 -0
- package/dist/core/profiles.d.ts +3 -3
- package/dist/core/profiles.js +3 -2
- 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 +29 -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 +48 -31
- package/dist/core/templates/workflows/trello-draft.js +2 -18
- package/dist/core/templates/workflows/trello-next-step-comment.d.ts +32 -15
- package/dist/core/templates/workflows/trello-next-step-comment.js +85 -32
- 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/LICENSE +0 -22
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,19 +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
604
|
const globalConfig = getGlobalConfig();
|
|
535
605
|
const resolvedProfile = this.resolveProfileOverride() ?? (isValidProfile(globalConfig.profile ?? '') ? globalConfig.profile : DEFAULT_PROFILE);
|
|
536
606
|
const schema = resolvedProfile === 'dixi' ? 'pstld-workflow' : DEFAULT_SCHEMA;
|
|
537
|
-
const yamlContent = serializeConfig({ schema });
|
|
607
|
+
const yamlContent = serializeConfig({ schema, pr: prConfig });
|
|
538
608
|
await FileSystemUtils.writeFile(configPath, yamlContent);
|
|
539
609
|
return 'created';
|
|
540
610
|
}
|
|
@@ -597,6 +667,9 @@ export class InitCommand {
|
|
|
597
667
|
const createdSchema = profileForSchema === 'dixi' ? 'pstld-workflow' : DEFAULT_SCHEMA;
|
|
598
668
|
console.log(`Config: pscode/config.yaml (schema: ${createdSchema})`);
|
|
599
669
|
}
|
|
670
|
+
else if (configStatus === 'updated') {
|
|
671
|
+
console.log(`Config: pscode/config.yaml (updated with PR config)`);
|
|
672
|
+
}
|
|
600
673
|
else if (configStatus === 'exists') {
|
|
601
674
|
// Show actual filename (config.yaml or config.yml)
|
|
602
675
|
const configYaml = path.join(projectPath, PSCODE_DIR_NAME, 'config.yaml');
|
|
@@ -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
|
|
@@ -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
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"];
|
|
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
36
|
description: 'Dixi — propose, explore, apply, complete com guardrails para Java/Spring e React/Next.js',
|
|
36
|
-
workflows: ['propose', 'explore', 'apply', 'complete', 'trello-setup', 'draft'],
|
|
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
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { buildNextStepComment, buildNextStepReminder } from './trello-next-step-comment.js';
|
|
1
2
|
export function getApplyChangeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'pscode-apply-change',
|
|
4
5
|
description: 'Implement tasks from an Pscode change. Use when the user wants to start implementing, continue implementation, or work through tasks.',
|
|
5
6
|
instructions: getApplyInstructions(),
|
|
6
|
-
license: 'MIT',
|
|
7
7
|
compatibility: 'Requires pscode CLI.',
|
|
8
8
|
metadata: { author: 'pscode', version: '1.0' },
|
|
9
9
|
};
|
|
@@ -18,6 +18,14 @@ export function getPsApplyCommandTemplate() {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
function getApplyInstructions() {
|
|
21
|
+
// Pre-filled next-step comment (card title interpolated), shared via the
|
|
22
|
+
// trello-next-step-comment utility and indented to sit inside the Trello
|
|
23
|
+
// comment `text: |` block in the testing phase below.
|
|
24
|
+
const completeNextStep = buildNextStepComment('<card title>', '/ps:complete')
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map((line) => (line.length > 0 ? ` ${line}` : ''))
|
|
27
|
+
.join('\n');
|
|
28
|
+
const completeReminder = buildNextStepReminder('<card title>', '/ps:complete');
|
|
21
29
|
return `Implement tasks from a Pscode change.
|
|
22
30
|
|
|
23
31
|
**Input**: Optionally specify a change name (e.g., \`/ps:apply add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
@@ -94,10 +102,27 @@ function getApplyInstructions() {
|
|
|
94
102
|
|
|
95
103
|
**Workspace guard:** If status JSON reports \`actionContext.mode: "workspace-planning"\` and \`allowedEditRoots\` is empty, explain that full workspace apply is not supported in this slice. Treat linked repos and folders as read-only context, ask the user to select an affected area, and STOP before editing files.
|
|
96
104
|
|
|
97
|
-
5. **Read context files**
|
|
105
|
+
5. **Read context files and PR config**
|
|
98
106
|
|
|
99
107
|
Read every file path listed under \`contextFiles\` from the apply instructions output.
|
|
100
108
|
|
|
109
|
+
Additionally, use the **Read tool** to read \`pscode/config.yaml\` from the current working directory.
|
|
110
|
+
|
|
111
|
+
**If \`pscode/config.yaml\` exists and \`pr.enabled: true\`:**
|
|
112
|
+
|
|
113
|
+
Before starting any implementation, inform the user of the PR workflow requirements:
|
|
114
|
+
|
|
115
|
+
> 🔀 **Workflow de PR ativo** — este projeto requer branches dedicadas e Pull Requests.
|
|
116
|
+
> - Branch: crie uma branch com o padrão \`<pr.branch.pattern>\` antes de codificar
|
|
117
|
+
> - Título do PR: \`<pr.title.template>\`
|
|
118
|
+
> - Descrição do PR: use o template definido em \`pr.description.template\`
|
|
119
|
+
> - Ao abrir o PR: \`<"comente o link do PR nesta task" se pr.comments.linkInTask: true, senão omita>\`
|
|
120
|
+
|
|
121
|
+
The agent MUST create the branch with the configured pattern before making any code changes.
|
|
122
|
+
Template variables available: \`{change-name}\` = current change name, \`{type}\` = feat/fix/chore, \`{ticket}\` = ticket ID if available.
|
|
123
|
+
|
|
124
|
+
**If \`pscode/config.yaml\` does not exist, or \`pr.enabled: false\`, or file not found:** continue normally without any PR instructions.
|
|
125
|
+
|
|
101
126
|
6. **Show current progress**
|
|
102
127
|
|
|
103
128
|
Display:
|
|
@@ -173,6 +198,7 @@ function getApplyInstructions() {
|
|
|
173
198
|
mcp__claude_ai_Trello_Custom__update_card { card_id: "<cardId>", list_id: "<lists.deploy.id>" }
|
|
174
199
|
\`\`\`
|
|
175
200
|
b. Add a comment in Portuguese:
|
|
201
|
+
${completeReminder}
|
|
176
202
|
\`\`\`tool
|
|
177
203
|
mcp__claude_ai_Trello_Custom__add_comment
|
|
178
204
|
card_id: "<cardId>"
|
|
@@ -182,7 +208,7 @@ function getApplyInstructions() {
|
|
|
182
208
|
Testado por: <usuario / Claude>
|
|
183
209
|
Status: Funcionando
|
|
184
210
|
|
|
185
|
-
|
|
211
|
+
${completeNextStep}
|
|
186
212
|
\`\`\`
|
|
187
213
|
|
|
188
214
|
If any Trello call fails, continue — Trello is auxiliary, never blocking.
|
|
@@ -3,7 +3,6 @@ export function getCompleteChangeSkillTemplate() {
|
|
|
3
3
|
name: 'pscode-archive-change',
|
|
4
4
|
description: 'Complete a completed change. Use when the user wants to finalize and complete a change after implementation is complete.',
|
|
5
5
|
instructions: getArchiveInstructions(),
|
|
6
|
-
license: 'MIT',
|
|
7
6
|
compatibility: 'Requires pscode CLI.',
|
|
8
7
|
metadata: { author: 'pscode', version: '1.0' },
|
|
9
8
|
};
|
|
@@ -239,7 +239,6 @@ No active changes found. Create a new change to get started.
|
|
|
239
239
|
- Preserve .pscode.yaml when moving to archive
|
|
240
240
|
- Archive directory target uses current date: YYYY-MM-DD-<name>
|
|
241
241
|
- If archive target exists, fail that change but continue with others`,
|
|
242
|
-
license: 'MIT',
|
|
243
242
|
compatibility: 'Requires pscode CLI.',
|
|
244
243
|
metadata: { author: 'pscode', version: '1.0' },
|
|
245
244
|
};
|
|
@@ -110,7 +110,6 @@ For other schemas, follow the \`instruction\` field from the CLI output.
|
|
|
110
110
|
- **IMPORTANT**: \`context\` and \`rules\` are constraints for YOU, not content for the file
|
|
111
111
|
- Do NOT copy \`<context>\`, \`<rules>\`, \`<project_context>\` blocks into the artifact
|
|
112
112
|
- These guide what you write, but should never appear in the output`,
|
|
113
|
-
license: 'MIT',
|
|
114
113
|
compatibility: 'Requires pscode CLI.',
|
|
115
114
|
metadata: { author: 'pscode', version: '1.0' },
|
|
116
115
|
};
|
|
@@ -426,7 +426,6 @@ Do NOT move the card. End the loop.
|
|
|
426
426
|
- **Do question assumptions** - Including the user's and your own
|
|
427
427
|
- **Always run the refinement loop after propose** - When exploration leads to a proposal, the refinement validation loop (Steps RF1–RF3) is mandatory, not optional
|
|
428
428
|
- **Preserve the loop** - Do not exit until the user explicitly approves or cancels`,
|
|
429
|
-
license: 'MIT',
|
|
430
429
|
compatibility: 'Requires pscode CLI.',
|
|
431
430
|
metadata: { author: 'pscode', version: '1.0' },
|
|
432
431
|
};
|
|
@@ -100,7 +100,6 @@ Does this look good? I can modify it if you'd like, or submit it as-is.
|
|
|
100
100
|
\`\`\`
|
|
101
101
|
|
|
102
102
|
Only proceed with submission after user confirms.`,
|
|
103
|
-
license: 'MIT',
|
|
104
103
|
compatibility: 'Requires pscode CLI.',
|
|
105
104
|
metadata: { author: 'pscode', version: '1.0' },
|
|
106
105
|
};
|
|
@@ -93,7 +93,6 @@ After completing all artifacts, summarize:
|
|
|
93
93
|
- If context is critically unclear, ask the user - but prefer making reasonable decisions to keep momentum
|
|
94
94
|
- If a change with that name already exists, suggest continuing that change instead
|
|
95
95
|
- Verify each artifact file exists after writing before proceeding to next`,
|
|
96
|
-
license: 'MIT',
|
|
97
96
|
compatibility: 'Requires pscode CLI.',
|
|
98
97
|
metadata: { author: 'pscode', version: '1.0' },
|
|
99
98
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handoff Skill / Command Template
|
|
3
|
+
*
|
|
4
|
+
* Compacts the current conversation into a handoff document so a fresh agent
|
|
5
|
+
* (or the same user in a new session) can continue the work seamlessly.
|
|
6
|
+
*/
|
|
7
|
+
import type { SkillTemplate, CommandTemplate } from '../types.js';
|
|
8
|
+
export declare function getHandoffSkillTemplate(): SkillTemplate;
|
|
9
|
+
export declare function getHandoffCommandTemplate(): CommandTemplate;
|
|
10
|
+
//# sourceMappingURL=handoff.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function getHandoffSkillTemplate() {
|
|
2
|
+
return {
|
|
3
|
+
name: 'pscode-handoff',
|
|
4
|
+
description: 'Compact the current conversation into a handoff document for another agent to pick up. Use when the user wants to hand off work to a new session or agent.',
|
|
5
|
+
instructions: getHandoffInstructions(),
|
|
6
|
+
compatibility: 'Works with any pscode project.',
|
|
7
|
+
metadata: { author: 'pscode', version: '1.0' },
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function getHandoffInstructions() {
|
|
11
|
+
return `Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS — not the current workspace.
|
|
12
|
+
|
|
13
|
+
Include a "suggested skills" section in the document, which suggests skills that the agent should invoke.
|
|
14
|
+
|
|
15
|
+
Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs, pscode change files). Reference them by path or URL instead.
|
|
16
|
+
|
|
17
|
+
Redact any sensitive information, such as API keys, passwords, or personally identifiable information.
|
|
18
|
+
|
|
19
|
+
If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly.
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
export function getHandoffCommandTemplate() {
|
|
23
|
+
return {
|
|
24
|
+
name: 'PS: Handoff',
|
|
25
|
+
description: 'Compact the current conversation into a handoff document for another agent to pick up',
|
|
26
|
+
category: 'Workflow',
|
|
27
|
+
tags: ['handoff', 'sessao', 'continuacao', 'workflow'],
|
|
28
|
+
content: getHandoffInstructions(),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=handoff.js.map
|
|
@@ -65,7 +65,6 @@ After completing the steps, summarize:
|
|
|
65
65
|
- If the name is invalid (not kebab-case), ask for a valid name
|
|
66
66
|
- If a change with that name already exists, suggest continuing that change instead
|
|
67
67
|
- Pass --schema if using a non-default workflow`,
|
|
68
|
-
license: 'MIT',
|
|
69
68
|
compatibility: 'Requires pscode CLI.',
|
|
70
69
|
metadata: { author: 'pscode', version: '1.0' },
|
|
71
70
|
};
|
|
@@ -3,7 +3,6 @@ export function getOnboardSkillTemplate() {
|
|
|
3
3
|
name: 'pscode-onboard',
|
|
4
4
|
description: 'Guided onboarding for Pscode - walk through a complete workflow cycle with narration and real codebase work.',
|
|
5
5
|
instructions: getOnboardInstructions(),
|
|
6
|
-
license: 'MIT',
|
|
7
6
|
compatibility: 'Requires pscode CLI.',
|
|
8
7
|
metadata: { author: 'pscode', version: '1.0' },
|
|
9
8
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { buildNextStepComment, buildNextStepReminder } from './trello-next-step-comment.js';
|
|
1
2
|
export function getProposeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'pscode-propose',
|
|
4
5
|
description: 'Propose a new change with all artifacts generated in one step. Use when the user wants to quickly describe what they want to build and get a complete proposal with design, specs, and tasks ready for implementation.',
|
|
5
6
|
instructions: getProposeInstructions(),
|
|
6
|
-
license: 'MIT',
|
|
7
7
|
compatibility: 'Requires pscode CLI.',
|
|
8
8
|
metadata: { author: 'pscode', version: '1.0' },
|
|
9
9
|
};
|
|
@@ -18,6 +18,14 @@ export function getPsProposeCommandTemplate() {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
function getProposeInstructions() {
|
|
21
|
+
// Pre-filled next-step comment (card title interpolated), shared via the
|
|
22
|
+
// trello-next-step-comment utility and indented to sit inside the Trello
|
|
23
|
+
// comment `text: |` blocks below.
|
|
24
|
+
const applyNextStep = buildNextStepComment('<card title>', '/ps:apply')
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map((line) => (line.length > 0 ? ` ${line}` : ''))
|
|
27
|
+
.join('\n');
|
|
28
|
+
const applyReminder = buildNextStepReminder('<card title>', '/ps:apply');
|
|
21
29
|
return `Propose a new change - create the change and generate all artifacts in one step.
|
|
22
30
|
|
|
23
31
|
I'll create a change with artifacts:
|
|
@@ -25,7 +33,7 @@ I'll create a change with artifacts:
|
|
|
25
33
|
- design.md (how)
|
|
26
34
|
- tasks.md (implementation steps)
|
|
27
35
|
|
|
28
|
-
After artifacts are created, a **refinement validation loop** runs: the
|
|
36
|
+
After artifacts are created, a **refinement validation loop** runs: the Trello card is updated with the refined plan, the user reviews it, gives feedback, and when satisfied the card is moved to Ready to Dev.
|
|
29
37
|
|
|
30
38
|
When ready to implement, run /ps:apply
|
|
31
39
|
|
|
@@ -203,22 +211,10 @@ Para iniciar a implementação quando aprovado:
|
|
|
203
211
|
|
|
204
212
|
---
|
|
205
213
|
|
|
206
|
-
### Step
|
|
207
|
-
|
|
208
|
-
Use **AskUserQuestion** to ask:
|
|
209
|
-
|
|
210
|
-
> "A implementação e o planejamento estão de acordo com o esperado?"
|
|
214
|
+
### Step R1b — Update Trello card (before asking for approval)
|
|
211
215
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
- 🔄 Não, quero ajustar o plano
|
|
215
|
-
- ❌ Cancelar (manter em refinamento)
|
|
216
|
-
|
|
217
|
-
**Do NOT update the Trello card description or add any comment before the user approves.**
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
### Step R2a — If APPROVED (Sim, está refinada)
|
|
216
|
+
So the user can use the Trello card itself as a visual reference when deciding,
|
|
217
|
+
update the card with the refinement content **before** asking for approval.
|
|
222
218
|
|
|
223
219
|
1. **Update Trello card description** (if \`cardId\` exists):
|
|
224
220
|
Build the description from the artifacts already read in Step R1:
|
|
@@ -237,7 +233,8 @@ Options:
|
|
|
237
233
|
**Artefatos:** pscode/changes/<name>/
|
|
238
234
|
\`\`\`
|
|
239
235
|
|
|
240
|
-
2. **Add a comment** in Portuguese (if \`cardId\` exists):
|
|
236
|
+
2. **Add a refinement comment** in Portuguese (if \`cardId\` exists):
|
|
237
|
+
${applyReminder}
|
|
241
238
|
\`\`\`tool
|
|
242
239
|
mcp__claude_ai_Trello_Custom__add_comment
|
|
243
240
|
card_id: "<cardId>"
|
|
@@ -250,22 +247,45 @@ Options:
|
|
|
250
247
|
### Resumo
|
|
251
248
|
<2-3 line summary of what will be built>
|
|
252
249
|
|
|
253
|
-
|
|
254
|
-
\`\`\`
|
|
255
|
-
/ps:apply <name>
|
|
256
|
-
\`\`\`
|
|
250
|
+
${applyNextStep}
|
|
257
251
|
|
|
258
252
|
_Aguardando aprovação para mover para Ready to Dev._
|
|
259
253
|
\`\`\`
|
|
260
254
|
|
|
261
|
-
|
|
255
|
+
If any Trello call fails, continue — Trello is auxiliary, never blocking.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
### Step R2 — Ask for user approval
|
|
260
|
+
|
|
261
|
+
Use **AskUserQuestion** to ask:
|
|
262
|
+
|
|
263
|
+
> "A implementação e o planejamento estão de acordo com o esperado?"
|
|
264
|
+
|
|
265
|
+
Options:
|
|
266
|
+
- ✅ Sim, está refinada — mover para Ready to Dev
|
|
267
|
+
- 🔄 Não, quero ajustar o plano
|
|
268
|
+
- ❌ Cancelar (manter em refinamento)
|
|
269
|
+
|
|
270
|
+
At this point the Trello card already reflects the current refinement (Step R1b),
|
|
271
|
+
so the user can review it before deciding.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### Step R2a — If APPROVED (Sim, está refinada)
|
|
276
|
+
|
|
277
|
+
The card description and refinement comment were already added in Step R1b.
|
|
278
|
+
Now just move the card and register the explicit approval.
|
|
279
|
+
|
|
280
|
+
1. **Move the Trello card to the ready list** (if \`lists.ready\` is configured and \`cardId\` exists):
|
|
262
281
|
\`\`\`tool
|
|
263
282
|
mcp__claude_ai_Trello_Custom__update_card
|
|
264
283
|
card_id: "<cardId>"
|
|
265
284
|
list_id: "<lists.ready.id>"
|
|
266
285
|
\`\`\`
|
|
267
286
|
|
|
268
|
-
|
|
287
|
+
2. **Add a final Trello comment** (if cardId exists):
|
|
288
|
+
${applyReminder}
|
|
269
289
|
\`\`\`tool
|
|
270
290
|
mcp__claude_ai_Trello_Custom__add_comment
|
|
271
291
|
card_id: "<cardId>"
|
|
@@ -274,13 +294,10 @@ Options:
|
|
|
274
294
|
|
|
275
295
|
O planejamento foi revisado e aprovado.
|
|
276
296
|
|
|
277
|
-
|
|
278
|
-
\`\`\`
|
|
279
|
-
/ps:apply <name>
|
|
280
|
-
\`\`\`
|
|
297
|
+
${applyNextStep}
|
|
281
298
|
\`\`\`
|
|
282
299
|
|
|
283
|
-
|
|
300
|
+
3. **Show success message:**
|
|
284
301
|
\`\`\`markdown
|
|
285
302
|
## ✅ Pronto para desenvolvimento!
|
|
286
303
|
|
|
@@ -305,9 +322,9 @@ Options:
|
|
|
305
322
|
- Changes to technical approach → update \`design.md\`
|
|
306
323
|
- Changes to tasks → update \`tasks.md\`
|
|
307
324
|
|
|
308
|
-
3. **Go back to Step R1** and show the updated refinement summary
|
|
325
|
+
3. **Go back to Step R1** and show the updated refinement summary, then **re-run Step R1b**
|
|
326
|
+
so the Trello card description and comment reflect the adjusted plan before asking again.
|
|
309
327
|
Keep looping until the user approves or cancels.
|
|
310
|
-
**Do NOT update the Trello description or add comments until the user approves.**
|
|
311
328
|
|
|
312
329
|
---
|
|
313
330
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { getNextStepCommentInstructionBlock } from './trello-next-step-comment.js';
|
|
1
2
|
export function getTrelloDraftSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'pscode-trello-draft',
|
|
4
5
|
description: 'Capture a raw idea or concept into the Backlog Trello list. Use when the user wants to quickly record something without refining it into a task yet.',
|
|
5
6
|
instructions: getTrelloDraftInstructions(),
|
|
6
|
-
license: 'MIT',
|
|
7
7
|
compatibility: 'Requires pscode CLI and Trello MCP server configured via /ps:trello-setup.',
|
|
8
8
|
metadata: { author: 'pscode', version: '1.0' },
|
|
9
9
|
};
|
|
@@ -140,23 +140,7 @@ Save the returned card \`id\` as \`cardId\` and \`url\` as \`cardUrl\`.
|
|
|
140
140
|
|
|
141
141
|
## Step 7 — Add next-step comment
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
formatted in Markdown so it is easy to copy and paste.
|
|
145
|
-
|
|
146
|
-
\`\`\`tool
|
|
147
|
-
mcp__claude_ai_Trello_Custom__add_comment
|
|
148
|
-
card_id: "<cardId>"
|
|
149
|
-
text: |
|
|
150
|
-
## Próximo passo
|
|
151
|
-
|
|
152
|
-
Para refinar e gerar os artefatos da change, rode:
|
|
153
|
-
|
|
154
|
-
\`\`\`
|
|
155
|
-
/ps:propose
|
|
156
|
-
\`\`\`
|
|
157
|
-
\`\`\`
|
|
158
|
-
|
|
159
|
-
If this call fails, log the error and continue — the comment is auxiliary, never blocking.
|
|
143
|
+
${getNextStepCommentInstructionBlock('<title>', '/ps:propose')}
|
|
160
144
|
|
|
161
145
|
---
|
|
162
146
|
|
|
@@ -1,26 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trello Next-Step Comment Utility
|
|
3
3
|
*
|
|
4
|
-
* Builds
|
|
5
|
-
*
|
|
6
|
-
* to the next stage.
|
|
4
|
+
* Builds the Trello comment posted at the end of each workflow step, showing
|
|
5
|
+
* the exact command (with the card title pre-filled as a quoted argument) to
|
|
6
|
+
* advance to the next stage — so the dev can copy/paste without typing the name.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Pure functions, no side effects and no Trello API calls: the card title is
|
|
9
|
+
* already available in each skill's flow. Shared across the ps:draft, ps:propose
|
|
10
|
+
* and ps:apply skill templates to avoid drift in the comment format.
|
|
10
11
|
*/
|
|
11
|
-
export type WorkflowStage = 'draft' | 'explore' | 'propose' | 'apply';
|
|
12
12
|
/**
|
|
13
|
-
* Returns the
|
|
14
|
-
*
|
|
13
|
+
* Returns the Markdown text for the next-step comment: a header, a description
|
|
14
|
+
* line, and a code block with the command and pre-filled, quoted card title.
|
|
15
|
+
* Paste the result into mcp__claude_ai_Trello_Custom__add_comment as `text`.
|
|
16
|
+
*
|
|
17
|
+
* @param cardName The card title to interpolate as the argument.
|
|
18
|
+
* @param nextCommand The command to advance the workflow (e.g. "/ps:apply").
|
|
19
|
+
* @param fallbackChangeName Optional kebab-case change name used when `cardName`
|
|
20
|
+
* is null, undefined or empty.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildNextStepComment(cardName: string, nextCommand: string, fallbackChangeName?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Returns a one-line reminder telling the agent to substitute the placeholder
|
|
25
|
+
* title with the real card title before posting, so the embedded next-step
|
|
26
|
+
* command is never left without its quoted argument. Use it right before tool
|
|
27
|
+
* blocks that interpolate `buildNextStepComment` output directly (propose, apply).
|
|
28
|
+
*
|
|
29
|
+
* @param cardName The placeholder shown in the generated command (e.g. "<card title>").
|
|
30
|
+
* @param nextCommand The command to advance the workflow (e.g. "/ps:apply").
|
|
15
31
|
*/
|
|
16
|
-
export declare function
|
|
32
|
+
export declare function buildNextStepReminder(cardName: string, nextCommand: string): string;
|
|
17
33
|
/**
|
|
18
|
-
* Returns the instruction block
|
|
19
|
-
*
|
|
34
|
+
* Returns the instruction block embedded inside a workflow skill template. It
|
|
35
|
+
* tells the AI agent to post a next-step comment built via `buildNextStepComment`,
|
|
36
|
+
* with the card title pre-filled as the command argument.
|
|
20
37
|
*
|
|
21
|
-
* @param
|
|
22
|
-
*
|
|
23
|
-
*
|
|
38
|
+
* @param cardName Placeholder (or literal) card title shown in the generated
|
|
39
|
+
* command (e.g. "<título do card>").
|
|
40
|
+
* @param nextCommand The command to advance the workflow (e.g. "/ps:apply").
|
|
24
41
|
*/
|
|
25
|
-
export declare function getNextStepCommentInstructionBlock(
|
|
42
|
+
export declare function getNextStepCommentInstructionBlock(cardName: string, nextCommand: string): string;
|
|
26
43
|
//# sourceMappingURL=trello-next-step-comment.d.ts.map
|
|
@@ -1,56 +1,109 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Trello Next-Step Comment Utility
|
|
3
3
|
*
|
|
4
|
-
* Builds
|
|
5
|
-
*
|
|
6
|
-
* to the next stage.
|
|
4
|
+
* Builds the Trello comment posted at the end of each workflow step, showing
|
|
5
|
+
* the exact command (with the card title pre-filled as a quoted argument) to
|
|
6
|
+
* advance to the next stage — so the dev can copy/paste without typing the name.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Pure functions, no side effects and no Trello API calls: the card title is
|
|
9
|
+
* already available in each skill's flow. Shared across the ps:draft, ps:propose
|
|
10
|
+
* and ps:apply skill templates to avoid drift in the comment format.
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
/** Human-readable label describing what the next command does. */
|
|
13
|
+
const NEXT_STEP_LABELS = {
|
|
14
|
+
'/ps:propose': 'Para refinar e gerar os artefatos da change',
|
|
15
|
+
'/ps:apply': 'Para implementar as tasks da change',
|
|
16
|
+
'/ps:complete': 'Para finalizar e arquivar a change',
|
|
16
17
|
};
|
|
18
|
+
/** Normalizes a command so it always starts with a single leading slash. */
|
|
19
|
+
function normalizeCommand(nextCommand) {
|
|
20
|
+
const trimmed = (nextCommand ?? '').trim();
|
|
21
|
+
if (trimmed.length === 0)
|
|
22
|
+
return '';
|
|
23
|
+
return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
|
|
24
|
+
}
|
|
25
|
+
/** Converts an arbitrary string into a kebab-case identifier. */
|
|
26
|
+
function toKebabCase(value) {
|
|
27
|
+
return (value ?? '')
|
|
28
|
+
.normalize('NFD')
|
|
29
|
+
.replace(/[̀-ͯ]/g, '')
|
|
30
|
+
.trim()
|
|
31
|
+
.toLowerCase()
|
|
32
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
33
|
+
.replace(/^-+|-+$/g, '');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves the argument placed after the command. Uses the card title when
|
|
37
|
+
* available, otherwise falls back to the kebab-case change identifier. Internal
|
|
38
|
+
* double quotes are escaped so the generated command stays valid.
|
|
39
|
+
*/
|
|
40
|
+
function resolveArgument(cardName, fallbackChangeName) {
|
|
41
|
+
const name = (cardName ?? '').trim();
|
|
42
|
+
const resolved = name.length > 0 ? name : toKebabCase(fallbackChangeName) || 'nova-change';
|
|
43
|
+
return resolved.replace(/"/g, '\\"');
|
|
44
|
+
}
|
|
17
45
|
/**
|
|
18
|
-
* Returns the
|
|
19
|
-
*
|
|
46
|
+
* Returns the Markdown text for the next-step comment: a header, a description
|
|
47
|
+
* line, and a code block with the command and pre-filled, quoted card title.
|
|
48
|
+
* Paste the result into mcp__claude_ai_Trello_Custom__add_comment as `text`.
|
|
49
|
+
*
|
|
50
|
+
* @param cardName The card title to interpolate as the argument.
|
|
51
|
+
* @param nextCommand The command to advance the workflow (e.g. "/ps:apply").
|
|
52
|
+
* @param fallbackChangeName Optional kebab-case change name used when `cardName`
|
|
53
|
+
* is null, undefined or empty.
|
|
20
54
|
*/
|
|
21
|
-
export function buildNextStepComment(
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
55
|
+
export function buildNextStepComment(cardName, nextCommand, fallbackChangeName = '') {
|
|
56
|
+
const command = normalizeCommand(nextCommand);
|
|
57
|
+
const label = NEXT_STEP_LABELS[command] ?? 'Para avançar para a próxima etapa';
|
|
58
|
+
const argument = resolveArgument(cardName, fallbackChangeName);
|
|
59
|
+
return `## Próximo passo
|
|
60
|
+
|
|
61
|
+
${label}, rode:
|
|
25
62
|
|
|
26
63
|
\`\`\`
|
|
27
|
-
|
|
64
|
+
${command} "${argument}"
|
|
28
65
|
\`\`\``;
|
|
29
66
|
}
|
|
30
67
|
/**
|
|
31
|
-
* Returns
|
|
32
|
-
*
|
|
68
|
+
* Returns a one-line reminder telling the agent to substitute the placeholder
|
|
69
|
+
* title with the real card title before posting, so the embedded next-step
|
|
70
|
+
* command is never left without its quoted argument. Use it right before tool
|
|
71
|
+
* blocks that interpolate `buildNextStepComment` output directly (propose, apply).
|
|
72
|
+
*
|
|
73
|
+
* @param cardName The placeholder shown in the generated command (e.g. "<card title>").
|
|
74
|
+
* @param nextCommand The command to advance the workflow (e.g. "/ps:apply").
|
|
75
|
+
*/
|
|
76
|
+
export function buildNextStepReminder(cardName, nextCommand) {
|
|
77
|
+
const command = normalizeCommand(nextCommand);
|
|
78
|
+
return `**IMPORTANT**: Replace \`${cardName}\` below with the actual card title — the command **must always** include the quoted title argument, never post \`${command}\` by itself.`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns the instruction block embedded inside a workflow skill template. It
|
|
82
|
+
* tells the AI agent to post a next-step comment built via `buildNextStepComment`,
|
|
83
|
+
* with the card title pre-filled as the command argument.
|
|
33
84
|
*
|
|
34
|
-
* @param
|
|
35
|
-
*
|
|
36
|
-
*
|
|
85
|
+
* @param cardName Placeholder (or literal) card title shown in the generated
|
|
86
|
+
* command (e.g. "<título do card>").
|
|
87
|
+
* @param nextCommand The command to advance the workflow (e.g. "/ps:apply").
|
|
37
88
|
*/
|
|
38
|
-
export function getNextStepCommentInstructionBlock(
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
89
|
+
export function getNextStepCommentInstructionBlock(cardName, nextCommand) {
|
|
90
|
+
const command = normalizeCommand(nextCommand);
|
|
91
|
+
const comment = buildNextStepComment(cardName, nextCommand);
|
|
92
|
+
const indented = comment
|
|
93
|
+
.split('\n')
|
|
94
|
+
.map((line) => (line.length > 0 ? ` ${line}` : ''))
|
|
95
|
+
.join('\n');
|
|
96
|
+
return `Post a comment on the card with the ready-to-paste command for the next stage,
|
|
97
|
+
using \`buildNextStepComment\` so the card title is pre-filled as the quoted argument.
|
|
42
98
|
|
|
43
|
-
|
|
99
|
+
**IMPORTANT**: Replace \`${cardName}\` with the actual card title from Step 3. The command
|
|
100
|
+
**must always** include the quoted title argument — never post \`${command}\` by itself.
|
|
44
101
|
|
|
45
102
|
\`\`\`tool
|
|
46
103
|
mcp__claude_ai_Trello_Custom__add_comment
|
|
47
104
|
card_id: "<cardId>"
|
|
48
105
|
text: |
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
\`\`\`
|
|
52
|
-
/${next.command} "${escapedTitleVar}"
|
|
53
|
-
\`\`\`
|
|
106
|
+
${indented}
|
|
54
107
|
\`\`\`
|
|
55
108
|
|
|
56
109
|
If this call fails, log the error and continue — the comment is auxiliary, never blocking.`;
|
|
@@ -3,7 +3,6 @@ export function getTrelloSetupSkillTemplate() {
|
|
|
3
3
|
name: 'pscode-trello-setup',
|
|
4
4
|
description: 'Configure Trello integration for your Pscode workflow. Checks MCP availability, reads or creates a Trello board, and writes pscode/trello.yaml with your stage-to-list mapping.',
|
|
5
5
|
instructions: getTrelloSetupInstructions(),
|
|
6
|
-
license: 'MIT',
|
|
7
6
|
compatibility: 'Requires pscode CLI and the Trello MCP server.',
|
|
8
7
|
metadata: { author: 'pscode', version: '1.0' },
|
|
9
8
|
};
|
|
@@ -162,7 +162,6 @@ Use clear markdown with:
|
|
|
162
162
|
- Code references in format: \`file.ts:123\`
|
|
163
163
|
- Specific, actionable recommendations
|
|
164
164
|
- No vague suggestions like "consider reviewing"`,
|
|
165
|
-
license: 'MIT',
|
|
166
165
|
compatibility: 'Requires pscode CLI.',
|
|
167
166
|
metadata: { author: 'pscode', version: '1.0' },
|
|
168
167
|
};
|
package/package.json
CHANGED
package/LICENSE
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 OpenSpec Contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
22
|
-
|