@pixelcraft-tw/spec 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/bin/pxs.d.ts +2 -0
- package/dist/bin/pxs.js +5 -0
- package/dist/bin/pxs.js.map +1 -0
- package/dist/src/backends/claude.d.ts +9 -0
- package/dist/src/backends/claude.js +80 -0
- package/dist/src/backends/claude.js.map +1 -0
- package/dist/src/backends/codex.d.ts +9 -0
- package/dist/src/backends/codex.js +72 -0
- package/dist/src/backends/codex.js.map +1 -0
- package/dist/src/backends/factory.d.ts +2 -0
- package/dist/src/backends/factory.js +14 -0
- package/dist/src/backends/factory.js.map +1 -0
- package/dist/src/backends/interface.d.ts +15 -0
- package/dist/src/backends/interface.js +2 -0
- package/dist/src/backends/interface.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +94 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/clarify.d.ts +5 -0
- package/dist/src/commands/clarify.js +46 -0
- package/dist/src/commands/clarify.js.map +1 -0
- package/dist/src/commands/diff.d.ts +1 -0
- package/dist/src/commands/diff.js +81 -0
- package/dist/src/commands/diff.js.map +1 -0
- package/dist/src/commands/implement.d.ts +9 -0
- package/dist/src/commands/implement.js +247 -0
- package/dist/src/commands/implement.js.map +1 -0
- package/dist/src/commands/init.d.ts +6 -0
- package/dist/src/commands/init.js +183 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/new.d.ts +5 -0
- package/dist/src/commands/new.js +186 -0
- package/dist/src/commands/new.js.map +1 -0
- package/dist/src/commands/refine.d.ts +8 -0
- package/dist/src/commands/refine.js +158 -0
- package/dist/src/commands/refine.js.map +1 -0
- package/dist/src/commands/reset.d.ts +3 -0
- package/dist/src/commands/reset.js +44 -0
- package/dist/src/commands/reset.js.map +1 -0
- package/dist/src/commands/review.d.ts +4 -0
- package/dist/src/commands/review.js +70 -0
- package/dist/src/commands/review.js.map +1 -0
- package/dist/src/commands/status.d.ts +1 -0
- package/dist/src/commands/status.js +53 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/discovery/project.d.ts +7 -0
- package/dist/src/discovery/project.js +135 -0
- package/dist/src/discovery/project.js.map +1 -0
- package/dist/src/git/operations.d.ts +10 -0
- package/dist/src/git/operations.js +56 -0
- package/dist/src/git/operations.js.map +1 -0
- package/dist/src/parsers/arguments.d.ts +18 -0
- package/dist/src/parsers/arguments.js +43 -0
- package/dist/src/parsers/arguments.js.map +1 -0
- package/dist/src/parsers/plan.d.ts +23 -0
- package/dist/src/parsers/plan.js +117 -0
- package/dist/src/parsers/plan.js.map +1 -0
- package/dist/src/parsers/spec.d.ts +10 -0
- package/dist/src/parsers/spec.js +46 -0
- package/dist/src/parsers/spec.js.map +1 -0
- package/dist/src/state/manager.d.ts +24 -0
- package/dist/src/state/manager.js +103 -0
- package/dist/src/state/manager.js.map +1 -0
- package/dist/src/state/types.d.ts +48 -0
- package/dist/src/state/types.js +20 -0
- package/dist/src/state/types.js.map +1 -0
- package/dist/src/utils/display.d.ts +7 -0
- package/dist/src/utils/display.js +42 -0
- package/dist/src/utils/display.js.map +1 -0
- package/dist/src/utils/prompt.d.ts +15 -0
- package/dist/src/utils/prompt.js +139 -0
- package/dist/src/utils/prompt.js.map +1 -0
- package/package.json +52 -0
- package/templates/agents-md-snippet.md +20 -0
- package/templates/architectures/clean/csharp-aspnet.md +56 -0
- package/templates/architectures/clean/dart-flutter.md +73 -0
- package/templates/architectures/clean/go-gin.md +50 -0
- package/templates/architectures/clean/go-std.md +49 -0
- package/templates/architectures/clean/kotlin-android.md +70 -0
- package/templates/architectures/clean/python-fastapi.md +49 -0
- package/templates/architectures/clean/swift-ios.md +69 -0
- package/templates/architectures/clean/typescript-express.md +60 -0
- package/templates/architectures/clean/typescript-nestjs.md +61 -0
- package/templates/architectures/ddd/csharp-aspnet.md +55 -0
- package/templates/architectures/ddd/go-gin.md +53 -0
- package/templates/architectures/ddd/python-fastapi.md +52 -0
- package/templates/architectures/ddd/typescript-nestjs.md +62 -0
- package/templates/architectures/hexagonal/csharp-aspnet.md +45 -0
- package/templates/architectures/hexagonal/go-gin.md +47 -0
- package/templates/architectures/hexagonal/python-fastapi.md +43 -0
- package/templates/architectures/hexagonal/typescript-nestjs.md +44 -0
- package/templates/architectures/layered/csharp-aspnet.md +45 -0
- package/templates/architectures/layered/go-gin.md +41 -0
- package/templates/architectures/layered/python-fastapi.md +42 -0
- package/templates/architectures/layered/typescript-nestjs.md +48 -0
- package/templates/architectures/modular/csharp-aspnet.md +45 -0
- package/templates/architectures/modular/dart-flutter.md +64 -0
- package/templates/architectures/modular/go-gin.md +47 -0
- package/templates/architectures/modular/kotlin-android.md +68 -0
- package/templates/architectures/modular/python-fastapi.md +45 -0
- package/templates/architectures/modular/swift-ios.md +55 -0
- package/templates/architectures/modular/typescript-nestjs.md +48 -0
- package/templates/architectures/mvvm/dart-flutter.md +69 -0
- package/templates/architectures/mvvm/kotlin-android.md +79 -0
- package/templates/architectures/mvvm/swift-ios.md +66 -0
- package/templates/claude-commands/sf.clarify.md +18 -0
- package/templates/claude-commands/sf.implement.md +80 -0
- package/templates/claude-commands/sf.new.md +22 -0
- package/templates/claude-commands/sf.refine.md +47 -0
- package/templates/claude-commands/sf.review.md +17 -0
- package/templates/claude-commands/sf.status.md +12 -0
- package/templates/workflow/config.yaml +17 -0
- package/templates/workflow/prompts/clarify.md +39 -0
- package/templates/workflow/prompts/final-review.md +30 -0
- package/templates/workflow/prompts/implement-tdd.md +35 -0
- package/templates/workflow/prompts/implement.md +43 -0
- package/templates/workflow/prompts/refine.md +52 -0
- package/templates/workflow/prompts/review.md +33 -0
- package/templates/workflow/prompts/test.md +14 -0
- package/templates/workflow/templates/spec-template.md +13 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type Phase = 'spec_created' | 'clarifying' | 'spec_approved' | 'plan_pending_approval' | 'ready_to_implement' | 'implementing' | 'completed' | 'merged';
|
|
2
|
+
export type TaskStatus = 'pending' | 'in_progress' | 'review_pending' | 'complete' | 'skipped';
|
|
3
|
+
export type FeatureType = 'feat' | 'fix' | 'refactor' | 'docs' | 'chore';
|
|
4
|
+
export type TestStrategy = 'tdd' | 'after' | 'none';
|
|
5
|
+
export type TestType = 'unit' | 'intg' | 'both';
|
|
6
|
+
export interface TaskState {
|
|
7
|
+
name: string;
|
|
8
|
+
status: TaskStatus;
|
|
9
|
+
}
|
|
10
|
+
export interface SessionInfo {
|
|
11
|
+
backend: string;
|
|
12
|
+
id: string;
|
|
13
|
+
}
|
|
14
|
+
export interface FeatureState {
|
|
15
|
+
feature: string;
|
|
16
|
+
type: FeatureType;
|
|
17
|
+
branch: string;
|
|
18
|
+
base_branch?: string;
|
|
19
|
+
phase: Phase;
|
|
20
|
+
total_tasks: number;
|
|
21
|
+
current_task: number;
|
|
22
|
+
session?: SessionInfo;
|
|
23
|
+
tasks: TaskState[];
|
|
24
|
+
}
|
|
25
|
+
export interface WorkflowState {
|
|
26
|
+
features: FeatureState[];
|
|
27
|
+
}
|
|
28
|
+
export interface ProjectConfig {
|
|
29
|
+
project: {
|
|
30
|
+
name: string;
|
|
31
|
+
language: string;
|
|
32
|
+
framework: string;
|
|
33
|
+
architecture: string;
|
|
34
|
+
lang_framework: string;
|
|
35
|
+
};
|
|
36
|
+
git: {
|
|
37
|
+
convention: string;
|
|
38
|
+
};
|
|
39
|
+
backend: {
|
|
40
|
+
default: string;
|
|
41
|
+
};
|
|
42
|
+
test: {
|
|
43
|
+
strategy: TestStrategy;
|
|
44
|
+
type: TestType;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export declare const PHASE_GUARDS: Record<string, Phase[]>;
|
|
48
|
+
export declare const TASK_TRANSITIONS: Record<TaskStatus, TaskStatus[]>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Phase guard: which phases each command is allowed in
|
|
2
|
+
export const PHASE_GUARDS = {
|
|
3
|
+
new: [], // any phase (creates new feature)
|
|
4
|
+
refine: ['spec_created', 'clarifying'],
|
|
5
|
+
clarify: ['spec_created', 'spec_approved', 'plan_pending_approval'],
|
|
6
|
+
implement: ['ready_to_implement', 'implementing'],
|
|
7
|
+
review: ['implementing', 'completed'],
|
|
8
|
+
status: [], // any phase
|
|
9
|
+
reset: [], // any phase
|
|
10
|
+
diff: ['ready_to_implement', 'implementing', 'completed', 'merged'],
|
|
11
|
+
};
|
|
12
|
+
// Valid task status transitions
|
|
13
|
+
export const TASK_TRANSITIONS = {
|
|
14
|
+
pending: ['in_progress', 'skipped'],
|
|
15
|
+
in_progress: ['review_pending'],
|
|
16
|
+
review_pending: ['complete', 'in_progress'], // in_progress = request-change
|
|
17
|
+
complete: [],
|
|
18
|
+
skipped: [],
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/state/types.ts"],"names":[],"mappings":"AA+DA,uDAAuD;AACvD,MAAM,CAAC,MAAM,YAAY,GAA4B;IACnD,GAAG,EAAE,EAAE,EAAE,kCAAkC;IAC3C,MAAM,EAAE,CAAC,cAAc,EAAE,YAAY,CAAC;IACtC,OAAO,EAAE,CAAC,cAAc,EAAE,eAAe,EAAE,uBAAuB,CAAC;IACnE,SAAS,EAAE,CAAC,oBAAoB,EAAE,cAAc,CAAC;IACjD,MAAM,EAAE,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,MAAM,EAAE,EAAE,EAAE,YAAY;IACxB,KAAK,EAAE,EAAE,EAAE,YAAY;IACvB,IAAI,EAAE,CAAC,oBAAoB,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,CAAC;CACpE,CAAC;AAEF,gCAAgC;AAChC,MAAM,CAAC,MAAM,gBAAgB,GAAqC;IAChE,OAAO,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC;IACnC,WAAW,EAAE,CAAC,gBAAgB,CAAC;IAC/B,cAAc,EAAE,CAAC,UAAU,EAAE,aAAa,CAAC,EAAE,+BAA+B;IAC5E,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,EAAE;CACZ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function heading(text: string): void;
|
|
2
|
+
export declare function success(text: string): void;
|
|
3
|
+
export declare function error(text: string): void;
|
|
4
|
+
export declare function warn(text: string): void;
|
|
5
|
+
export declare function info(text: string): void;
|
|
6
|
+
export declare function taskIcon(status: string): string;
|
|
7
|
+
export declare function table(headers: string[], rows: string[][]): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export function heading(text) {
|
|
3
|
+
console.log(chalk.bold.cyan(`\n━━━ ${text} ━━━\n`));
|
|
4
|
+
}
|
|
5
|
+
export function success(text) {
|
|
6
|
+
console.log(chalk.green(` ✓ ${text}`));
|
|
7
|
+
}
|
|
8
|
+
export function error(text) {
|
|
9
|
+
console.log(chalk.red(` ✗ ${text}`));
|
|
10
|
+
}
|
|
11
|
+
export function warn(text) {
|
|
12
|
+
console.log(chalk.yellow(` ⚠ ${text}`));
|
|
13
|
+
}
|
|
14
|
+
export function info(text) {
|
|
15
|
+
console.log(chalk.gray(` ℹ ${text}`));
|
|
16
|
+
}
|
|
17
|
+
export function taskIcon(status) {
|
|
18
|
+
switch (status) {
|
|
19
|
+
case 'complete': return '✅';
|
|
20
|
+
case 'in_progress': return '⏳';
|
|
21
|
+
case 'skipped': return '⏭️';
|
|
22
|
+
case 'review_pending': return '🔍';
|
|
23
|
+
case 'pending':
|
|
24
|
+
default: return '⬜';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function table(headers, rows) {
|
|
28
|
+
// Calculate column widths
|
|
29
|
+
const widths = headers.map((h, i) => {
|
|
30
|
+
const colValues = [h, ...rows.map((r) => r[i] ?? '')];
|
|
31
|
+
return Math.max(...colValues.map((v) => v.length));
|
|
32
|
+
});
|
|
33
|
+
// Print header
|
|
34
|
+
const headerLine = headers.map((h, i) => h.padEnd(widths[i])).join(' ');
|
|
35
|
+
console.log(chalk.bold(headerLine));
|
|
36
|
+
// Print rows
|
|
37
|
+
for (const row of rows) {
|
|
38
|
+
const line = row.map((cell, i) => (cell ?? '').padEnd(widths[i])).join(' ');
|
|
39
|
+
console.log(line);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=display.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"display.js","sourceRoot":"","sources":["../../../src/utils/display.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,UAAU,CAAC,CAAC,OAAO,GAAG,CAAC;QAC5B,KAAK,aAAa,CAAC,CAAC,OAAO,GAAG,CAAC;QAC/B,KAAK,SAAS,CAAC,CAAC,OAAO,IAAI,CAAC;QAC5B,KAAK,gBAAgB,CAAC,CAAC,OAAO,IAAI,CAAC;QACnC,KAAK,SAAS,CAAC;QACf,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAiB,EAAE,IAAgB;IACvD,0BAA0B;IAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,SAAS,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,eAAe;IACf,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAEpC,aAAa;IACb,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load a prompt template from .workflow/prompts/<name>.md
|
|
3
|
+
*/
|
|
4
|
+
export declare function loadPrompt(name: string, cwd?: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Assemble a full prompt from template name + variables + agents/skills + extra text.
|
|
7
|
+
*/
|
|
8
|
+
export declare function assemblePrompt(opts: {
|
|
9
|
+
templateName: string;
|
|
10
|
+
vars: Record<string, string>;
|
|
11
|
+
agents?: string[];
|
|
12
|
+
skills?: string[];
|
|
13
|
+
extraText?: string;
|
|
14
|
+
cwd?: string;
|
|
15
|
+
}): string;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const WORKFLOW_DIR = '.workflow';
|
|
4
|
+
/**
|
|
5
|
+
* Load a prompt template from .workflow/prompts/<name>.md
|
|
6
|
+
*/
|
|
7
|
+
export function loadPrompt(name, cwd = process.cwd()) {
|
|
8
|
+
const promptPath = path.join(cwd, WORKFLOW_DIR, 'prompts', `${name}.md`);
|
|
9
|
+
if (!fs.existsSync(promptPath)) {
|
|
10
|
+
throw new Error(`Prompt template "${name}" not found at ${promptPath}`);
|
|
11
|
+
}
|
|
12
|
+
return fs.readFileSync(promptPath, 'utf-8');
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Substitute template variables in a prompt string.
|
|
16
|
+
* Variables use {varName} syntax.
|
|
17
|
+
*/
|
|
18
|
+
function renderPrompt(template, vars) {
|
|
19
|
+
let result = template;
|
|
20
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
21
|
+
result = result.replaceAll(`{${key}}`, value);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the agent/skill instruction block for prompt injection.
|
|
27
|
+
*/
|
|
28
|
+
function buildAgentSkillBlock(agents, skills) {
|
|
29
|
+
const parts = [];
|
|
30
|
+
if (agents.length > 0) {
|
|
31
|
+
parts.push(`\n## Agent Instructions\nInvolve the following agents in analysis: ${agents.map(a => `@${a}`).join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
if (skills.length > 0) {
|
|
34
|
+
parts.push(`\n## Skill/MCP Instructions\nUse the following skills/MCPs to assist: ${skills.map(s => `/${s}`).join(', ')}`);
|
|
35
|
+
}
|
|
36
|
+
return parts.join('\n');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Assemble a full prompt from template name + variables + agents/skills + extra text.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Load architecture constraints from .workflow/architecture.md if it exists.
|
|
43
|
+
*/
|
|
44
|
+
function loadArchitectureConstraint(cwd = process.cwd()) {
|
|
45
|
+
const archPath = path.join(cwd, WORKFLOW_DIR, 'architecture.md');
|
|
46
|
+
if (!fs.existsSync(archPath))
|
|
47
|
+
return '';
|
|
48
|
+
const content = fs.readFileSync(archPath, 'utf-8');
|
|
49
|
+
return `\n## Architecture Constraint
|
|
50
|
+
This project follows the architecture specification below. All implementations must comply:
|
|
51
|
+
${content}
|
|
52
|
+
|
|
53
|
+
<HARD-GATE>
|
|
54
|
+
- New files must be placed in the correct directory as defined by the architecture
|
|
55
|
+
- Dependency direction must not be violated (e.g., domain must not reference infrastructure)
|
|
56
|
+
- Follow the architecture's naming conventions and file naming rules
|
|
57
|
+
- If requirements cross layers, use Port/Adapter or the corresponding architectural pattern
|
|
58
|
+
- NestJS projects: new features must create independent Modules; do not stuff logic into unrelated Modules
|
|
59
|
+
</HARD-GATE>`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Discover review-related slash commands and skills in the project.
|
|
63
|
+
* Scans .claude/commands/ for review-related files.
|
|
64
|
+
*/
|
|
65
|
+
function discoverReviewTools(cwd = process.cwd()) {
|
|
66
|
+
const commands = [];
|
|
67
|
+
const skills = [];
|
|
68
|
+
// Scan .claude/commands/ for review-related slash commands
|
|
69
|
+
const commandsDir = path.join(cwd, '.claude', 'commands');
|
|
70
|
+
if (fs.existsSync(commandsDir)) {
|
|
71
|
+
try {
|
|
72
|
+
const files = fs.readdirSync(commandsDir);
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
if (!file.endsWith('.md'))
|
|
75
|
+
continue;
|
|
76
|
+
const name = file.replace('.md', '');
|
|
77
|
+
const lower = name.toLowerCase();
|
|
78
|
+
if (lower.includes('review') || lower.includes('simplify') || lower.includes('lint')
|
|
79
|
+
|| lower.includes('audit') || lower.includes('check')) {
|
|
80
|
+
commands.push(`/${name}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// ignore read errors
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Well-known review skills available in Claude Code
|
|
89
|
+
const knownReviewSkills = ['simplify', 'code-review'];
|
|
90
|
+
skills.push(...knownReviewSkills);
|
|
91
|
+
return { commands, skills };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Build auto-discovery block for review prompts when no agents/skills are provided.
|
|
95
|
+
*/
|
|
96
|
+
function buildReviewAutoDiscoveryBlock(cwd = process.cwd()) {
|
|
97
|
+
const { commands, skills } = discoverReviewTools(cwd);
|
|
98
|
+
const parts = [];
|
|
99
|
+
parts.push('\n## Review Tool Instructions');
|
|
100
|
+
parts.push('You MUST perform a thorough code review. Do NOT just summarize the git diff.');
|
|
101
|
+
parts.push('Analyze the actual code changes for correctness, security, performance, and quality.');
|
|
102
|
+
if (commands.length > 0) {
|
|
103
|
+
parts.push(`\nAvailable project review commands: ${commands.join(', ')}`);
|
|
104
|
+
parts.push('Consider using these to assist the review.');
|
|
105
|
+
}
|
|
106
|
+
if (skills.length > 0) {
|
|
107
|
+
parts.push(`\nAvailable review skills: ${skills.map(s => `/${s}`).join(', ')}`);
|
|
108
|
+
parts.push('Use /simplify or /code-review if available to perform deeper analysis.');
|
|
109
|
+
}
|
|
110
|
+
return parts.join('\n');
|
|
111
|
+
}
|
|
112
|
+
const REVIEW_TEMPLATES = ['review', 'final-review'];
|
|
113
|
+
/**
|
|
114
|
+
* Assemble a full prompt from template name + variables + agents/skills + extra text.
|
|
115
|
+
*/
|
|
116
|
+
export function assemblePrompt(opts) {
|
|
117
|
+
const template = loadPrompt(opts.templateName, opts.cwd);
|
|
118
|
+
let prompt = renderPrompt(template, opts.vars);
|
|
119
|
+
// Auto-append architecture constraints when architecture.md exists
|
|
120
|
+
const archConstraint = loadArchitectureConstraint(opts.cwd);
|
|
121
|
+
if (archConstraint) {
|
|
122
|
+
prompt += archConstraint;
|
|
123
|
+
}
|
|
124
|
+
const agents = opts.agents ?? [];
|
|
125
|
+
const skills = opts.skills ?? [];
|
|
126
|
+
const agentSkillBlock = buildAgentSkillBlock(agents, skills);
|
|
127
|
+
if (agentSkillBlock) {
|
|
128
|
+
prompt += '\n' + agentSkillBlock;
|
|
129
|
+
}
|
|
130
|
+
// For review templates: inject auto-discovery when no agents/skills are provided
|
|
131
|
+
if (REVIEW_TEMPLATES.includes(opts.templateName) && agents.length === 0 && skills.length === 0) {
|
|
132
|
+
prompt += buildReviewAutoDiscoveryBlock(opts.cwd);
|
|
133
|
+
}
|
|
134
|
+
if (opts.extraText) {
|
|
135
|
+
prompt += `\n\n## Additional Instructions\n${opts.extraText}`;
|
|
136
|
+
}
|
|
137
|
+
return prompt;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../../src/utils/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,YAAY,GAAG,WAAW,CAAC;AAEjC;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IAClE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;IACzE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,kBAAkB,UAAU,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,QAAgB,EAAE,IAA4B;IAClE,IAAI,MAAM,GAAG,QAAQ,CAAC;IACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,MAAgB,EAAE,MAAgB;IAC9D,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,sEAAsE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1H,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,yEAAyE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7H,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH;;GAEG;AACH,SAAS,0BAA0B,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,EAAE,iBAAiB,CAAC,CAAC;IACjE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,OAAO;;EAEP,OAAO;;;;;;;;aAQI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACtD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,2DAA2D;IAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC1D,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;oBAAE,SAAS;gBACpC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACrC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;uBAC/E,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBACxD,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IAED,oDAAoD;IACpD,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IACtD,MAAM,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,CAAC;IAElC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,SAAS,6BAA6B,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAChE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAC5C,KAAK,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;IAC3F,KAAK,CAAC,IAAI,CAAC,sFAAsF,CAAC,CAAC;IAEnG,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,wCAAwC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,8BAA8B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChF,KAAK,CAAC,IAAI,CAAC,wEAAwE,CAAC,CAAC;IACvF,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AAEpD;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAO9B;IACC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,IAAI,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAE/C,mEAAmE;IACnE,MAAM,cAAc,GAAG,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5D,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,IAAI,cAAc,CAAC;IAC3B,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;IAEjC,MAAM,eAAe,GAAG,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7D,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,IAAI,IAAI,GAAG,eAAe,CAAC;IACnC,CAAC;IAED,iFAAiF;IACjF,IAAI,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/F,MAAM,IAAI,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,MAAM,IAAI,mCAAmC,IAAI,CAAC,SAAS,EAAE,CAAC;IAChE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pixelcraft-tw/spec",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lightweight spec-driven development CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"spec-driven",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"ai",
|
|
14
|
+
"cli",
|
|
15
|
+
"workflow"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/pixelcraft-tw/spec-cli.git"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"pxs": "./dist/bin/pxs.js"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"dev": "tsx watch bin/pxs.ts",
|
|
31
|
+
"test": "vitest",
|
|
32
|
+
"prepublishOnly": "npm run build && npm test"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"chalk": "^5.4.1",
|
|
36
|
+
"commander": "^13.1.0",
|
|
37
|
+
"inquirer": "^12.6.0",
|
|
38
|
+
"js-yaml": "^4.1.0"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist/",
|
|
42
|
+
"templates/"
|
|
43
|
+
],
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/inquirer": "^9.0.7",
|
|
46
|
+
"@types/js-yaml": "^4.0.9",
|
|
47
|
+
"@types/node": "^22.15.0",
|
|
48
|
+
"tsx": "^4.19.4",
|
|
49
|
+
"typescript": "^5.8.3",
|
|
50
|
+
"vitest": "^3.1.4"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## SF Spec-Driven Workflow
|
|
2
|
+
|
|
3
|
+
This project uses sf for spec-driven development.
|
|
4
|
+
|
|
5
|
+
### Workflow State
|
|
6
|
+
- Read `.workflow/state.yaml` for current progress
|
|
7
|
+
- Read `.workflow/plans/<feature>.md` for implementation plan
|
|
8
|
+
|
|
9
|
+
### Rules
|
|
10
|
+
- Implement tasks in the order specified in the plan; do not skip
|
|
11
|
+
- After completing each task, run `git add -A && git commit`
|
|
12
|
+
- Commit messages follow conventional commits or the project's custom convention
|
|
13
|
+
- Do not implement beyond task spec scope
|
|
14
|
+
- After each task completion, wait for user confirmation before continuing
|
|
15
|
+
|
|
16
|
+
### Prompt Templates
|
|
17
|
+
- Implementation guide: `.workflow/prompts/implement.md`
|
|
18
|
+
- TDD guide: `.workflow/prompts/implement-tdd.md`
|
|
19
|
+
- Review guide: `.workflow/prompts/review.md`
|
|
20
|
+
- Test guide: `.workflow/prompts/test.md`
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Architecture: Clean Architecture (C# + ASP.NET Core)
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
```
|
|
5
|
+
src/
|
|
6
|
+
├── Domain/
|
|
7
|
+
│ ├── Entities/Order.cs
|
|
8
|
+
│ ├── ValueObjects/Money.cs
|
|
9
|
+
│ ├── Events/OrderCreatedEvent.cs
|
|
10
|
+
│ ├── Errors/DomainException.cs
|
|
11
|
+
│ └── Interfaces/IOrderRepository.cs
|
|
12
|
+
├── Application/
|
|
13
|
+
│ ├── UseCases/CreateOrder/
|
|
14
|
+
│ │ ├── CreateOrderCommand.cs
|
|
15
|
+
│ │ ├── CreateOrderHandler.cs
|
|
16
|
+
│ │ └── CreateOrderValidator.cs
|
|
17
|
+
│ ├── Common/
|
|
18
|
+
│ │ └── Behaviors/ValidationBehavior.cs
|
|
19
|
+
│ └── DependencyInjection.cs
|
|
20
|
+
├── Infrastructure/
|
|
21
|
+
│ ├── Persistence/
|
|
22
|
+
│ │ ├── ApplicationDbContext.cs
|
|
23
|
+
│ │ ├── Repositories/OrderRepository.cs
|
|
24
|
+
│ │ └── Configurations/OrderConfiguration.cs
|
|
25
|
+
│ ├── Services/
|
|
26
|
+
│ │ └── PaymentGateway.cs
|
|
27
|
+
│ └── DependencyInjection.cs
|
|
28
|
+
└── WebApi/
|
|
29
|
+
├── Controllers/OrderController.cs
|
|
30
|
+
├── Middleware/ExceptionHandlerMiddleware.cs
|
|
31
|
+
├── Filters/
|
|
32
|
+
└── Program.cs
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Dependency Rule
|
|
36
|
+
- Domain → depends on nothing outside, pure C# classes
|
|
37
|
+
- Application → depends only on Domain; uses MediatR for CQRS
|
|
38
|
+
- Infrastructure → implements Domain interfaces, EF Core DbContext
|
|
39
|
+
- WebApi → depends on Application; ASP.NET Controllers + minimal APIs
|
|
40
|
+
|
|
41
|
+
## Conventions
|
|
42
|
+
- MediatR for Command/Query handling
|
|
43
|
+
- FluentValidation for input validation
|
|
44
|
+
- EF Core with Fluent API configuration (no data annotations on Domain)
|
|
45
|
+
- DI registration in each layer's DependencyInjection.cs
|
|
46
|
+
|
|
47
|
+
## File Naming
|
|
48
|
+
- PascalCase: CreateOrderCommand.cs
|
|
49
|
+
- One class per file
|
|
50
|
+
- Folder-per-feature in Application (e.g., UseCases/CreateOrder/)
|
|
51
|
+
|
|
52
|
+
## Testing
|
|
53
|
+
- Domain: pure unit tests (xUnit)
|
|
54
|
+
- Application: mock repositories (NSubstitute/Moq)
|
|
55
|
+
- Infrastructure: integration tests (WebApplicationFactory, TestContainers)
|
|
56
|
+
- WebApi: E2E tests (WebApplicationFactory)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Architecture: Clean Architecture (Flutter)
|
|
2
|
+
|
|
3
|
+
## Layer-Based Structure
|
|
4
|
+
Flutter app organized into domain, application, infrastructure, and presentation layers under `lib/`.
|
|
5
|
+
|
|
6
|
+
## Directory Structure
|
|
7
|
+
```
|
|
8
|
+
lib/
|
|
9
|
+
├── domain/
|
|
10
|
+
│ ├── entities/order.dart
|
|
11
|
+
│ ├── value_objects/money.dart
|
|
12
|
+
│ └── errors/domain_error.dart
|
|
13
|
+
├── application/
|
|
14
|
+
│ ├── use_cases/create_order_use_case.dart
|
|
15
|
+
│ ├── ports/
|
|
16
|
+
│ │ ├── order_repository_port.dart
|
|
17
|
+
│ │ └── payment_gateway_port.dart
|
|
18
|
+
│ └── dtos/create_order_dto.dart
|
|
19
|
+
├── infrastructure/
|
|
20
|
+
│ ├── repositories/order_repository.dart
|
|
21
|
+
│ ├── services/payment_gateway.dart
|
|
22
|
+
│ ├── database/
|
|
23
|
+
│ │ └── app_database.dart
|
|
24
|
+
│ └── config/env.dart
|
|
25
|
+
├── presentation/
|
|
26
|
+
│ ├── pages/order_page.dart
|
|
27
|
+
│ ├── widgets/order_card.dart
|
|
28
|
+
│ ├── providers/order_provider.dart
|
|
29
|
+
│ └── router/app_router.dart
|
|
30
|
+
└── main.dart
|
|
31
|
+
test/
|
|
32
|
+
├── domain/
|
|
33
|
+
├── application/
|
|
34
|
+
├── infrastructure/
|
|
35
|
+
└── presentation/
|
|
36
|
+
integration_test/
|
|
37
|
+
└── app_test.dart
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Dependency Rule
|
|
41
|
+
- domain → depends on nothing outside, pure Dart classes
|
|
42
|
+
- application → depends only on domain; use cases call externals via port abstract classes
|
|
43
|
+
- infrastructure → implements application ports
|
|
44
|
+
- presentation → depends on application; widgets + providers
|
|
45
|
+
|
|
46
|
+
## Recommended Dependencies
|
|
47
|
+
- State Management: Riverpod + flutter_hooks
|
|
48
|
+
- Networking: dio + retrofit
|
|
49
|
+
- Navigation: go_router
|
|
50
|
+
- Local Storage: drift (SQL), shared_preferences (KV)
|
|
51
|
+
- DI: Riverpod (self-contained)
|
|
52
|
+
|
|
53
|
+
## Conventions
|
|
54
|
+
- Use Cases are single-purpose classes with a `call()` method
|
|
55
|
+
- Providers (Riverpod) expose use cases and state to UI
|
|
56
|
+
- Presentation layer never imports infrastructure directly
|
|
57
|
+
- Error handling via Either pattern or sealed result classes
|
|
58
|
+
|
|
59
|
+
## File Naming
|
|
60
|
+
- snake_case: create_order_use_case.dart
|
|
61
|
+
- Entity: *_entity.dart or just *.dart under entities/
|
|
62
|
+
- Port: *_port.dart
|
|
63
|
+
- Repository: *_repository.dart
|
|
64
|
+
- Provider: *_provider.dart
|
|
65
|
+
- Page: *_page.dart
|
|
66
|
+
- Widget: *_widget.dart or descriptive name
|
|
67
|
+
|
|
68
|
+
## Testing
|
|
69
|
+
- domain: pure unit tests, no mocks
|
|
70
|
+
- application: mock ports
|
|
71
|
+
- infrastructure: integration tests
|
|
72
|
+
- presentation: widget tests with WidgetTester
|
|
73
|
+
- integration_test/: full app flow tests
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Architecture: Clean Architecture (Go + Gin)
|
|
2
|
+
|
|
3
|
+
## Directory Structure
|
|
4
|
+
```
|
|
5
|
+
internal/
|
|
6
|
+
├── domain/
|
|
7
|
+
│ ├── entity/order.go # Entities, Value Objects
|
|
8
|
+
│ └── event/order_created.go # Domain Events
|
|
9
|
+
├── usecase/
|
|
10
|
+
│ ├── create_order.go # Use Cases (one file per use case)
|
|
11
|
+
│ └── interfaces.go # Port interfaces
|
|
12
|
+
├── infrastructure/
|
|
13
|
+
│ ├── postgres/
|
|
14
|
+
│ │ └── order_repo.go # Repository implementation
|
|
15
|
+
│ ├── redis/
|
|
16
|
+
│ │ └── cache.go # Cache implementation (if any)
|
|
17
|
+
│ └── config/
|
|
18
|
+
│ └── env.go # Environment configuration
|
|
19
|
+
├── handler/
|
|
20
|
+
│ └── http/
|
|
21
|
+
│ ├── order_handler.go # Gin handler
|
|
22
|
+
│ ├── middleware/
|
|
23
|
+
│ │ └── auth.go # Middleware
|
|
24
|
+
│ └── router.go # Route definitions
|
|
25
|
+
└── pkg/ # Cross-project reusable packages
|
|
26
|
+
└── logger/
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Dependency Rule
|
|
30
|
+
- domain → depends on nothing outside, pure Go structs and interfaces
|
|
31
|
+
- usecase → depends only on domain; calls infrastructure through interfaces
|
|
32
|
+
- infrastructure → implements usecase interfaces
|
|
33
|
+
- handler → depends on usecase; handles HTTP layer
|
|
34
|
+
|
|
35
|
+
## Go Conventions
|
|
36
|
+
- Interfaces defined at the consumer side (usecase/), not implementation side (infrastructure/)
|
|
37
|
+
- Error handling uses custom error types defined in domain/
|
|
38
|
+
- Struct initialization uses New functions: NewOrderUseCase(), NewOrderRepo()
|
|
39
|
+
- Context passed throughout: handler → usecase → repository
|
|
40
|
+
|
|
41
|
+
## File Naming
|
|
42
|
+
- snake_case: create_order.go
|
|
43
|
+
- Test files: create_order_test.go
|
|
44
|
+
- One type per file within same package
|
|
45
|
+
|
|
46
|
+
## Testing
|
|
47
|
+
- domain: pure unit tests
|
|
48
|
+
- usecase: mock interfaces (using mockgen or hand-written mocks)
|
|
49
|
+
- infrastructure: integration tests (using testcontainers or test DB)
|
|
50
|
+
- handler: HTTP tests (using httptest)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Architecture: Clean Architecture (Go Standard Library)
|
|
2
|
+
|
|
3
|
+
## Directory Structure
|
|
4
|
+
```
|
|
5
|
+
internal/
|
|
6
|
+
├── domain/
|
|
7
|
+
│ ├── entity/order.go # Entities, Value Objects
|
|
8
|
+
│ └── errors.go # Domain errors
|
|
9
|
+
├── usecase/
|
|
10
|
+
│ ├── create_order.go # Use Cases
|
|
11
|
+
│ └── interfaces.go # Port interfaces
|
|
12
|
+
├── infrastructure/
|
|
13
|
+
│ ├── postgres/
|
|
14
|
+
│ │ └── order_repo.go # Repository implementation
|
|
15
|
+
│ └── config/
|
|
16
|
+
│ └── env.go # Environment configuration
|
|
17
|
+
├── handler/
|
|
18
|
+
│ └── http/
|
|
19
|
+
│ ├── order_handler.go # net/http handler
|
|
20
|
+
│ ├── middleware/
|
|
21
|
+
│ │ └── auth.go # Middleware
|
|
22
|
+
│ └── router.go # http.ServeMux routes
|
|
23
|
+
└── pkg/
|
|
24
|
+
└── logger/
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Dependency Rule
|
|
28
|
+
- domain → depends on nothing outside, pure Go structs and interfaces
|
|
29
|
+
- usecase → depends only on domain; calls infrastructure through interfaces
|
|
30
|
+
- infrastructure → implements usecase interfaces
|
|
31
|
+
- handler → depends on usecase; handles HTTP layer using net/http
|
|
32
|
+
|
|
33
|
+
## Go Conventions
|
|
34
|
+
- Use net/http ServeMux (Go 1.22+ enhanced routing) instead of external routers
|
|
35
|
+
- Interfaces defined at the consumer side (usecase/)
|
|
36
|
+
- Error handling uses custom error types defined in domain/
|
|
37
|
+
- Struct initialization uses New functions
|
|
38
|
+
- Context passed throughout: handler → usecase → repository
|
|
39
|
+
|
|
40
|
+
## File Naming
|
|
41
|
+
- snake_case: create_order.go
|
|
42
|
+
- Test files: create_order_test.go
|
|
43
|
+
- One type per file within same package
|
|
44
|
+
|
|
45
|
+
## Testing
|
|
46
|
+
- domain: pure unit tests
|
|
47
|
+
- usecase: mock interfaces
|
|
48
|
+
- infrastructure: integration tests (using testcontainers)
|
|
49
|
+
- handler: HTTP tests (using httptest)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Architecture: Clean Architecture (Android / Kotlin)
|
|
2
|
+
|
|
3
|
+
## Layer-Based Structure
|
|
4
|
+
Android app organized into domain, data, and presentation packages following Clean Architecture with Jetpack Compose.
|
|
5
|
+
|
|
6
|
+
## Directory Structure
|
|
7
|
+
```
|
|
8
|
+
app/src/main/java/com/example/app/
|
|
9
|
+
├── domain/
|
|
10
|
+
│ ├── model/Order.kt
|
|
11
|
+
│ ├── repository/OrderRepository.kt # interface
|
|
12
|
+
│ ├── usecase/CreateOrderUseCase.kt
|
|
13
|
+
│ └── error/DomainError.kt
|
|
14
|
+
├── data/
|
|
15
|
+
│ ├── repository/OrderRepositoryImpl.kt
|
|
16
|
+
│ ├── remote/
|
|
17
|
+
│ │ ├── api/OrderApi.kt # Retrofit interface
|
|
18
|
+
│ │ └── dto/OrderDto.kt
|
|
19
|
+
│ ├── local/
|
|
20
|
+
│ │ ├── dao/OrderDao.kt # Room DAO
|
|
21
|
+
│ │ └── entity/OrderEntity.kt
|
|
22
|
+
│ └── mapper/OrderMapper.kt
|
|
23
|
+
├── presentation/
|
|
24
|
+
│ ├── order/
|
|
25
|
+
│ │ ├── OrderScreen.kt
|
|
26
|
+
│ │ └── OrderViewModel.kt
|
|
27
|
+
│ ├── navigation/AppNavigation.kt
|
|
28
|
+
│ └── theme/Theme.kt
|
|
29
|
+
├── di/
|
|
30
|
+
│ ├── AppModule.kt
|
|
31
|
+
│ ├── NetworkModule.kt
|
|
32
|
+
│ └── DatabaseModule.kt
|
|
33
|
+
└── App.kt # Application class
|
|
34
|
+
app/src/test/ # Unit tests
|
|
35
|
+
app/src/androidTest/ # Instrumented tests
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Dependency Rule
|
|
39
|
+
- domain → depends on nothing outside, pure Kotlin classes + interfaces
|
|
40
|
+
- data → implements domain repository interfaces
|
|
41
|
+
- presentation → depends on domain; Compose screens + ViewModels
|
|
42
|
+
- di → wires everything together via Hilt modules
|
|
43
|
+
|
|
44
|
+
## Recommended Dependencies
|
|
45
|
+
- State Management: Hilt + StateFlow + Compose State
|
|
46
|
+
- Networking: Retrofit + OkHttp
|
|
47
|
+
- DI: Hilt
|
|
48
|
+
- Navigation: Navigation Compose
|
|
49
|
+
- Local Storage: Room
|
|
50
|
+
- Async: Kotlin Coroutines + Flow
|
|
51
|
+
|
|
52
|
+
## Conventions
|
|
53
|
+
- Use Cases are single-purpose classes with an `operator fun invoke()` or `execute()` method
|
|
54
|
+
- ViewModels extend `ViewModel()`, expose `StateFlow` to Compose
|
|
55
|
+
- Repository interfaces live in domain, implementations in data
|
|
56
|
+
- Hilt @Module classes in di/ package
|
|
57
|
+
|
|
58
|
+
## File Naming
|
|
59
|
+
- PascalCase: CreateOrderUseCase.kt
|
|
60
|
+
- Interface: OrderRepository.kt (domain), OrderRepositoryImpl.kt (data)
|
|
61
|
+
- DAO: *Dao.kt
|
|
62
|
+
- API: *Api.kt
|
|
63
|
+
- Screen: *Screen.kt
|
|
64
|
+
- ViewModel: *ViewModel.kt
|
|
65
|
+
|
|
66
|
+
## Testing
|
|
67
|
+
- domain: pure unit tests, JUnit
|
|
68
|
+
- data: integration tests with Room in-memory DB
|
|
69
|
+
- presentation: Compose UI tests with composeTestRule
|
|
70
|
+
- Use Cases: unit test with mock repositories
|