@polymorphism-tech/morph-spec 7.7.3 → 7.8.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/README.md +2 -2
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +1 -1
- package/framework/MORPH.md +3 -2
- package/framework/agents.json +1 -1
- package/framework/evals/scenarios/dotnet-crud.yaml +1 -1
- package/framework/rules/dotnet-templates.md +0 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +1 -1
- package/framework/skills/core/morph-brainstorming/SKILL.md +2 -2
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +4 -3
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +5 -5
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +1 -1
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +34 -35
- package/framework/standards/backend/api/validation.md +2 -2
- package/framework/standards/backend/dotnet/scrutor.md +1 -3
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +42 -37
- package/framework/standards/data/nosql/cache/redis.md +2 -2
- package/framework/templates/CLAUDE.md.core.template +1 -1
- package/framework/templates/README.md +0 -1
- package/framework/templates/REGISTRY.json +0 -21
- package/framework/templates/code/dotnet/abstractions/handler.cs +0 -3
- package/framework/templates/code/dotnet/constants/api-tags.cs +1 -1
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +4 -5
- package/framework/templates/code/dotnet/feature/handler.cs.hbs +23 -24
- package/framework/templates/code/dotnet/jobs/job.cs +15 -15
- package/framework/templates/code/dotnet/test.cs +72 -101
- package/package.json +1 -1
- package/src/utils/banner.js +53 -43
- package/framework/templates/code/dotnet/backend/repository.cs +0 -141
package/src/utils/banner.js
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
// src/utils/banner.js
|
|
2
|
-
import chalk from
|
|
3
|
-
import { type as osType, release as osRelease } from
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { type as osType, release as osRelease } from "os";
|
|
4
4
|
|
|
5
|
-
const BRAND = chalk.hex(
|
|
5
|
+
const BRAND = chalk.hex("#7C3AED");
|
|
6
6
|
const DIM = chalk.dim;
|
|
7
7
|
|
|
8
8
|
/** Purple gradient, one tone per logo line (top → bottom). */
|
|
9
|
-
const GRADIENT = [
|
|
9
|
+
const GRADIENT = [
|
|
10
|
+
"#6D28D9",
|
|
11
|
+
"#7C3AED",
|
|
12
|
+
"#8B5CF6",
|
|
13
|
+
"#A78BFA",
|
|
14
|
+
"#C4B5FD",
|
|
15
|
+
"#DDD6FE",
|
|
16
|
+
];
|
|
10
17
|
|
|
11
18
|
const LOGO_LINES = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
"███╗ ███╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗",
|
|
20
|
+
"████╗ ████║██╔═══██╗██╔══██╗██╔══██╗██║ ██║",
|
|
21
|
+
"██╔████╔██║██║ ██║██████╔╝██████╔╝███████║",
|
|
22
|
+
"██║╚██╔╝██║██║ ██║██╔══██╗██╔═══╝ ██╔══██║",
|
|
23
|
+
"██║ ╚═╝ ██║╚██████╔╝██║ ██║██║ ██║ ██║",
|
|
24
|
+
"╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝",
|
|
18
25
|
];
|
|
19
26
|
|
|
20
27
|
const LOGO_WIDTH = LOGO_LINES[0].length; // 44
|
|
@@ -28,59 +35,62 @@ const LOGO_WIDTH = LOGO_LINES[0].length; // 44
|
|
|
28
35
|
* @returns {string}
|
|
29
36
|
*/
|
|
30
37
|
export function renderLogo(version) {
|
|
31
|
-
const art = LOGO_LINES
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
const art = LOGO_LINES.map(
|
|
39
|
+
(line, i) => " " + chalk.hex(GRADIENT[i])(line),
|
|
40
|
+
).join("\n");
|
|
34
41
|
|
|
35
42
|
const label = ` S P E C `;
|
|
36
43
|
const ver = ` v${version} `;
|
|
37
44
|
const plain = `${label}·${ver}`;
|
|
38
45
|
const side = Math.max(2, Math.floor((LOGO_WIDTH - plain.length) / 2));
|
|
39
46
|
const rule =
|
|
40
|
-
|
|
41
|
-
DIM(
|
|
47
|
+
" " +
|
|
48
|
+
DIM("─".repeat(side)) +
|
|
42
49
|
BRAND.bold(label) +
|
|
43
|
-
DIM(
|
|
50
|
+
DIM("·") +
|
|
44
51
|
chalk.bold.white(ver) +
|
|
45
|
-
DIM(
|
|
52
|
+
DIM("─".repeat(Math.max(2, LOGO_WIDTH - side - plain.length)));
|
|
46
53
|
|
|
47
54
|
return `\n${art}\n${rule}\n`;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
function platformLabel() {
|
|
51
58
|
const t = osType();
|
|
52
|
-
const r = osRelease().split(
|
|
53
|
-
if (t ===
|
|
54
|
-
if (t ===
|
|
59
|
+
const r = osRelease().split(".").slice(0, 2).join(".");
|
|
60
|
+
if (t === "Windows_NT") return `Windows ${r}`;
|
|
61
|
+
if (t === "Darwin") return `macOS ${r}`;
|
|
55
62
|
return `Linux ${r}`;
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
export function printInstallBanner({ version, nodeVersion, results = [] }) {
|
|
59
|
-
const sep = (label) =>
|
|
66
|
+
const sep = (label) =>
|
|
67
|
+
DIM(` ── ${label} ${"─".repeat(Math.max(0, 38 - label.length))}`);
|
|
60
68
|
|
|
61
|
-
const itemLines = results.map(r =>
|
|
69
|
+
const itemLines = results.map((r) =>
|
|
62
70
|
r.ok
|
|
63
|
-
? ` ${chalk.green(
|
|
64
|
-
: ` ${chalk.red(
|
|
71
|
+
? ` ${chalk.green("✔")} ${r.label.padEnd(20)} ${DIM(r.path)}`
|
|
72
|
+
: ` ${chalk.red("✖")} ${r.label.padEnd(20)} ${chalk.red(r.error ?? "failed")}`,
|
|
65
73
|
);
|
|
66
74
|
|
|
67
|
-
console.log(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
console.log(
|
|
76
|
+
[
|
|
77
|
+
renderLogo(version),
|
|
78
|
+
` ${DIM("Spec-driven development harness for Claude Code")}`,
|
|
79
|
+
` ${DIM("by Polymorphism Tech")} ${DIM("·")} ${DIM("Node")} ${chalk.white(nodeVersion)} ${DIM("·")} ${chalk.white(platformLabel())}`,
|
|
80
|
+
"",
|
|
81
|
+
sep("Installed"),
|
|
82
|
+
...itemLines,
|
|
83
|
+
"",
|
|
84
|
+
sep("Next · new project"),
|
|
85
|
+
` ${chalk.cyan("1.")} ${DIM("cd")} ${chalk.white("your-project")} ${DIM("(git repo)")}`,
|
|
86
|
+
` ${chalk.cyan("2.")} Run ${chalk.bold.white("morph-spec init")}`,
|
|
87
|
+
` ${chalk.cyan("3.")} Open it in Claude Code and run ${chalk.bold.white("/morph-init")}`,
|
|
88
|
+
"",
|
|
89
|
+
sep("Next · project already on morph-spec"),
|
|
90
|
+
` ${chalk.cyan("1.")} Run ${chalk.bold.white("morph-spec update")} ${DIM("— sync everything")}`,
|
|
91
|
+
` ${chalk.cyan("2.")} Run ${chalk.bold.white("morph-spec doctor")} ${DIM("— validate installation")}`,
|
|
92
|
+
` ${chalk.cyan("3.")} ${DIM("Check")} ${chalk.white(".morph/config/config.json → project.stack")} ${DIM("(drives which tools install)")}`,
|
|
93
|
+
"",
|
|
94
|
+
].join("\n"),
|
|
95
|
+
);
|
|
86
96
|
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// REPOSITORY + UNIT OF WORK TEMPLATE (VSA Pattern)
|
|
3
|
-
// Generated by MORPH Framework
|
|
4
|
-
// ============================================================
|
|
5
|
-
|
|
6
|
-
using Microsoft.EntityFrameworkCore;
|
|
7
|
-
using {{pascalCase PROJECT_NAME}}.Database;
|
|
8
|
-
|
|
9
|
-
namespace {{pascalCase PROJECT_NAME}}.Repository;
|
|
10
|
-
|
|
11
|
-
// ============================================================
|
|
12
|
-
// IRepository<T> — Generic repository contract
|
|
13
|
-
// ============================================================
|
|
14
|
-
|
|
15
|
-
// Generic CRUD by key. For slice-specific queries (joins, projection, pagination),
|
|
16
|
-
// inject ApplicationDbContext directly into the handler and write LINQ in the slice —
|
|
17
|
-
// do NOT add query methods here, and do NOT use raw SQL.
|
|
18
|
-
public interface IRepository<T> where T : class
|
|
19
|
-
{
|
|
20
|
-
Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
|
|
21
|
-
Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default);
|
|
22
|
-
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
|
|
23
|
-
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
|
|
24
|
-
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
|
|
25
|
-
Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
|
|
26
|
-
void RemoveRange(IEnumerable<T> entities);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ============================================================
|
|
30
|
-
// IUnitOfWork — Transaction boundary contract
|
|
31
|
-
// ============================================================
|
|
32
|
-
|
|
33
|
-
public interface IUnitOfWork
|
|
34
|
-
{
|
|
35
|
-
int Commit();
|
|
36
|
-
void Rollback();
|
|
37
|
-
Task<int> CommitAsync(CancellationToken cancellationToken = default);
|
|
38
|
-
Task RollbackAsync(CancellationToken cancellationToken = default);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// ============================================================
|
|
42
|
-
// Repository<T> — EF Core generic repository implementation
|
|
43
|
-
// ============================================================
|
|
44
|
-
|
|
45
|
-
public class Repository<T> : IRepository<T> where T : class
|
|
46
|
-
{
|
|
47
|
-
private readonly ApplicationDbContext _context;
|
|
48
|
-
private readonly DbSet<T> _dbSet;
|
|
49
|
-
|
|
50
|
-
public Repository(ApplicationDbContext context)
|
|
51
|
-
{
|
|
52
|
-
_context = context;
|
|
53
|
-
_dbSet = _context.Set<T>();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
public async Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default)
|
|
57
|
-
{
|
|
58
|
-
return await _dbSet.ToListAsync(cancellationToken);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public async Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default)
|
|
62
|
-
{
|
|
63
|
-
return await _dbSet.FindAsync(new[] { id }, cancellationToken);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public async Task<T> AddAsync(T entity, CancellationToken cancellationToken = default)
|
|
67
|
-
{
|
|
68
|
-
await _dbSet.AddAsync(entity, cancellationToken);
|
|
69
|
-
return entity;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
public Task UpdateAsync(T entity, CancellationToken cancellationToken = default)
|
|
73
|
-
{
|
|
74
|
-
_dbSet.Update(entity);
|
|
75
|
-
return Task.CompletedTask;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
public Task DeleteAsync(T entity, CancellationToken cancellationToken = default)
|
|
79
|
-
{
|
|
80
|
-
_dbSet.Remove(entity);
|
|
81
|
-
return Task.CompletedTask;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
public async Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default)
|
|
85
|
-
{
|
|
86
|
-
await _dbSet.AddRangeAsync(entities, cancellationToken);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
public void RemoveRange(IEnumerable<T> entities)
|
|
90
|
-
{
|
|
91
|
-
_dbSet.RemoveRange(entities);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ============================================================
|
|
96
|
-
// UnitOfWork — EF Core transaction management
|
|
97
|
-
// ============================================================
|
|
98
|
-
|
|
99
|
-
public class UnitOfWork : IUnitOfWork
|
|
100
|
-
{
|
|
101
|
-
private readonly ApplicationDbContext _dataContext;
|
|
102
|
-
|
|
103
|
-
public UnitOfWork(ApplicationDbContext dataContext)
|
|
104
|
-
{
|
|
105
|
-
_dataContext = dataContext;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
public int Commit()
|
|
109
|
-
{
|
|
110
|
-
return _dataContext.SaveChanges();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
public async Task<int> CommitAsync(CancellationToken cancellationToken = default)
|
|
114
|
-
{
|
|
115
|
-
return await _dataContext.SaveChangesAsync(cancellationToken);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public void Rollback()
|
|
119
|
-
{
|
|
120
|
-
_dataContext.Dispose();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
public async Task RollbackAsync(CancellationToken cancellationToken = default)
|
|
124
|
-
{
|
|
125
|
-
await _dataContext.DisposeAsync();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// ============================================================
|
|
130
|
-
// DI REGISTRATION
|
|
131
|
-
// ============================================================
|
|
132
|
-
//
|
|
133
|
-
// In Program.cs or a DI extension method:
|
|
134
|
-
//
|
|
135
|
-
// builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
136
|
-
// options.UseSqlServer(connectionString));
|
|
137
|
-
//
|
|
138
|
-
// builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
|
139
|
-
// builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
|
140
|
-
//
|
|
141
|
-
// ============================================================
|