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