Skip to content

Commit 5d42ebd

Browse files
committed
Refactor rendering and validation result handling
- Updated the Renderer class to improve section extraction and added detailed JSDoc comments for better documentation. - Refactored ValidationResultRenderer to utilize an output writer interface for consistent message formatting and improved readability. - Enhanced DefaultTaskStore with additional JSDoc comments for clarity on method functionalities. - Improved FileManager with detailed JSDoc comments for file operations. - Updated GitHub types and issues provider to use a unified type helper for type checks. - Refactored ProjectsProvider to streamline task creation and filtering logic. - Enhanced TaskManager with new methods for changelog management and added JSDoc comments for better understanding. - Improved TaskProvider with detailed filtering logic for eligible tasks and added JSDoc comments. - Added unit tests for ObservabilityLogger to ensure logging functionalities work as expected. - Updated UID resolution and audit services with detailed JSDoc comments for clarity on method purposes. - Introduced IOutputWriter interface for consistent CLI output handling and added OutputFormat enum for structured output types.
1 parent 7faae79 commit 5d42ebd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1624
-381
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ dist
33
.docusaurus
44
.coverage
55
test/validator.test.js
6+
__mocks__
67

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Format and process
77
- The `Unreleased` section contains completed tasks that have been removed from `TODO.md` and are targeted for the next release.
88
- integration.test.task.003 — undefined — Test cleanup
99
- integration.test.task.002 — undefined — Integration test completion
10+
- integration.test.task.003 — undefined — Test cleanup
11+
- integration.test.task.002 — undefined — Integration test completion
1012
- Each entry should include: task id, summary, author, PR or commit link, and a one-line description of the change.
1113
- When creating a release, move the entries from `Unreleased` to a new versioned section (e.g. `## [1.0.0] - 2025-09-20`) and include release notes.
1214

__mocks__/chalk.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const chalk = {
2+
green: (text) => text,
3+
red: (text) => text,
4+
yellow: (text) => text,
5+
blue: (text) => text,
6+
cyan: (text) => text,
7+
magenta: (text) => text,
8+
white: (text) => text,
9+
black: (text) => text,
10+
gray: (text) => text,
11+
grey: (text) => text,
12+
redBright: (text) => text,
13+
greenBright: (text) => text,
14+
yellowBright: (text) => text,
15+
blueBright: (text) => text,
16+
magentaBright: (text) => text,
17+
cyanBright: (text) => text,
18+
whiteBright: (text) => text,
19+
bgRed: (text) => text,
20+
bgGreen: (text) => text,
21+
bgYellow: (text) => text,
22+
bgBlue: (text) => text,
23+
bgMagenta: (text) => text,
24+
bgCyan: (text) => text,
25+
bgWhite: (text) => text,
26+
bgBlack: (text) => text,
27+
bgRedBright: (text) => text,
28+
bgGreenBright: (text) => text,
29+
bgYellowBright: (text) => text,
30+
bgBlueBright: (text) => text,
31+
bgMagentaBright: (text) => text,
32+
bgCyanBright: (text) => text,
33+
bgWhiteBright: (text) => text,
34+
bold: (text) => text,
35+
dim: (text) => text,
36+
italic: (text) => text,
37+
underline: (text) => text,
38+
inverse: (text) => text,
39+
strikethrough: (text) => text,
40+
reset: (text) => text,
41+
};
42+
43+
module.exports = chalk;

jest.config.cjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@ module.exports = {
3535
clearMocks: true,
3636
restoreMocks: true,
3737
resetMocks: true,
38+
transformIgnorePatterns: ['node_modules/(?!chalk)'],
39+
moduleNameMapper: {
40+
'^chalk$': '<rootDir>/__mocks__/chalk.js',
41+
},
3842
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Code Quality Standards
2+
3+
## Architectural Rules
4+
5+
### Index File Encapsulation
6+
7+
**NEVER** allow index files to export from outside their directory tree.
8+
9+
**❌ WRONG - Breaks encapsulation:**
10+
11+
```typescript
12+
// src/types/rendering/index.ts
13+
export * from './IRenderer';
14+
export * from './OutputFormat';
15+
export { ConsoleOutputWriter } from '../../core/rendering/console-output.writer'; // ❌ BAD
16+
```
17+
18+
**✅ CORRECT - Maintains encapsulation:**
19+
20+
```typescript
21+
// src/types/rendering/index.ts
22+
export * from './IRenderer';
23+
export * from './OutputFormat';
24+
25+
// Add exports to the appropriate domain index file instead:
26+
// src/core/rendering/index.ts
27+
export { ConsoleOutputWriter } from './console-output.writer';
28+
```
29+
30+
**Rationale:**
31+
32+
- Maintains clear module boundaries and encapsulation
33+
- Prevents circular dependencies
34+
- Makes dependencies explicit and traceable
35+
- Follows domain-driven design principles
36+
- Improves maintainability and refactoring safety
37+
38+
**Enforcement:**
39+
40+
- Code reviews must flag any index file reaching outside its directory
41+
- Automated linting rules should be added to prevent this pattern
42+
- Refactoring should move cross-domain exports to appropriate domain boundaries

src/cli.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getLogger } from './core/system/logger';
66
import { ObservabilityLogger } from './core/system/observability.logger';
77
import { CommandFactory } from './commands/shared/command.factory';
88
import { EXIT_CODES } from './constants/exit-codes';
9+
import { ConsoleOutputWriter } from './core/rendering/console-output.writer';
910

1011
/**
1112
* Main CLI entry point for the Documentation-Driven Development toolkit.
@@ -36,8 +37,11 @@ const observabilityLogger = new ObservabilityLogger(pinoLogger);
3637
const program = new Command();
3738
program.name('dddctl').description('Documentation-Driven Development CLI').version('1.0.0');
3839

40+
// Create output writer for CLI messaging
41+
const outputWriter = new ConsoleOutputWriter();
42+
3943
// Configure all commands through the factory
40-
CommandFactory.configureProgram(program, baseLogger);
44+
CommandFactory.configureProgram(program, baseLogger, outputWriter);
4145

4246
// Helper function to get safe command name
4347
function getCommandName(): string {

src/commands/audit/ref-audit.command.test.ts

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,40 +38,48 @@ describe('RefAuditCommand', () => {
3838
jest.clearAllMocks();
3939
});
4040

41-
it('should have correct name and description', () => {
42-
const cmd = new RefAuditCommand(mockLogger);
43-
expect(cmd.name).toBe(CommandName.AUDIT);
44-
expect(cmd.description).toBe('Audit references across repo & tasks');
41+
describe('execute', () => {
42+
it('should execute the command successfully', async () => {
43+
const cmd = new RefAuditCommand(mockLogger);
44+
await cmd.execute();
45+
46+
expect(mockLogger.info).toHaveBeenCalledWith('Executing ref audit command');
47+
expect(container.resolve).toHaveBeenCalledWith(SERVICE_KEYS.REFERENCE_AUDIT);
48+
expect(mockService.execute).toHaveBeenCalled();
49+
expect(mockLogger.info).toHaveBeenCalledWith('Ref audit command executed');
50+
});
51+
52+
it('should handle service execution error', async () => {
53+
const error = new Error('Service error');
54+
mockService.execute.mockRejectedValue(error);
55+
56+
const cmd = new RefAuditCommand(mockLogger);
57+
58+
await expect(cmd.execute()).rejects.toThrow('Service error');
59+
expect(mockLogger.info).toHaveBeenCalledWith('Executing ref audit command');
60+
expect(mockLogger.info).not.toHaveBeenCalledWith('Ref audit command executed');
61+
});
4562
});
4663

47-
it('should execute the command successfully', async () => {
48-
const cmd = new RefAuditCommand(mockLogger);
49-
await cmd.execute();
64+
describe('configure', () => {
65+
it('should configure the command on parent', () => {
66+
RefAuditCommand.configure(mockParentCommand, mockLogger);
5067

51-
expect(mockLogger.info).toHaveBeenCalledWith('Executing ref audit command');
52-
expect(container.resolve).toHaveBeenCalledWith(SERVICE_KEYS.REFERENCE_AUDIT);
53-
expect(mockService.execute).toHaveBeenCalled();
54-
expect(mockLogger.info).toHaveBeenCalledWith('Ref audit command executed');
68+
expect(mockParentCommand.command).toHaveBeenCalledWith(CommandName.AUDIT);
69+
expect(mockParentCommand.description).toHaveBeenCalledWith(
70+
'Audit references across repo & tasks',
71+
);
72+
expect(mockParentCommand.action).toHaveBeenCalledWith(expect.any(Function));
73+
});
5574
});
5675

57-
it('should configure the command on parent', () => {
58-
RefAuditCommand.configure(mockParentCommand, mockLogger);
59-
60-
expect(mockParentCommand.command).toHaveBeenCalledWith(CommandName.AUDIT);
61-
expect(mockParentCommand.description).toHaveBeenCalledWith(
62-
'Audit references across repo & tasks',
63-
);
64-
expect(mockParentCommand.action).toHaveBeenCalledWith(expect.any(Function));
65-
});
66-
67-
it('should handle service execution error', async () => {
68-
const error = new Error('Service error');
69-
mockService.execute.mockRejectedValue(error);
70-
71-
const cmd = new RefAuditCommand(mockLogger);
72-
73-
await expect(cmd.execute()).rejects.toThrow('Service error');
74-
expect(mockLogger.info).toHaveBeenCalledWith('Executing ref audit command');
75-
expect(mockLogger.info).not.toHaveBeenCalledWith('Ref audit command executed');
76+
describe('properties', () => {
77+
it.each([
78+
{ property: 'name', expected: CommandName.AUDIT },
79+
{ property: 'description', expected: 'Audit references across repo & tasks' },
80+
])('should have correct $property', ({ property, expected }) => {
81+
const cmd = new RefAuditCommand(mockLogger);
82+
expect(cmd[property as keyof RefAuditCommand]).toBe(expected);
83+
});
7684
});
7785
});

src/commands/audit/ref-audit.command.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ export class RefAuditCommand extends BaseCommand {
1515

1616
/**
1717
* Executes the ref audit command.
18+
* @returns Promise that resolves when the operation is complete
19+
*
20+
* @example
21+
* ```typescript
22+
* await command.execute();
23+
* ```
1824
*/
1925
async execute(): Promise<void> {
2026
this.logger.info('Executing ref audit command');
@@ -23,6 +29,11 @@ export class RefAuditCommand extends BaseCommand {
2329
this.logger.info('Ref audit command executed');
2430
}
2531

32+
/**
33+
* Configures the command within the parent command.
34+
* @param parent - The parent Command instance
35+
* @param logger - Logger instance for logging
36+
*/
2637
static configure(parent: Command, logger: ILogger): void {
2738
parent
2839
.command(CommandName.AUDIT)

src/commands/audit/supersede.command.test.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,23 @@ describe('SupersedeCommand', () => {
3838
});
3939

4040
describe('execute', () => {
41-
it('should execute supersede and log correctly', async () => {
42-
const oldUid = 'OLD123';
43-
const newUid = 'NEW456';
44-
41+
it.each([
42+
{
43+
oldUid: 'OLD123',
44+
newUid: 'NEW456',
45+
description: 'valid UIDs',
46+
},
47+
{
48+
oldUid: 'ABC',
49+
newUid: 'XYZ',
50+
description: 'short UIDs',
51+
},
52+
{
53+
oldUid: 'old-uid-123',
54+
newUid: 'new-uid-456',
55+
description: 'dashed UIDs',
56+
},
57+
])('should execute supersede successfully with $description', async ({ oldUid, newUid }) => {
4558
await command.execute({ oldUid, newUid });
4659

4760
expect(mockLogger.info).toHaveBeenCalledWith('Executing supersede command', {
@@ -69,7 +82,6 @@ describe('SupersedeCommand', () => {
6982
oldUid: 'OLD123',
7083
});
7184
expect(mockService.execute).toHaveBeenCalledWith('OLD123', 'NEW456');
72-
// Note: In real scenario, error logging might be handled by BaseCommand or elsewhere
7385
});
7486
});
7587

src/commands/audit/supersede.command.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ export class SupersedeCommand extends BaseCommand {
3535
this.logger.info(`Supersede command executed: ${oldUid} -> ${newUid}`, { newUid, oldUid });
3636
}
3737

38+
/**
39+
* Configures the command within the parent command.
40+
* @param parent - The parent Command instance
41+
* @param logger - Logger instance for logging
42+
*/
3843
static configure(parent: Command, logger: ILogger): void {
3944
parent
4045
.command(CommandName.SUPERSEDE)

0 commit comments

Comments
 (0)