@polymorphism-tech/morph-spec 8.43.0 → 8.45.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/CLAUDE.md +16 -13
- package/README.md +10 -10
- package/bin/morph-spec.js +10 -13
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -4
- package/docs/QUICKSTART.md +6 -6
- package/framework/CLAUDE.md +6 -8
- package/framework/MORPH.md +39 -45
- package/framework/agents.json +8 -9
- package/framework/commands/morph-apply.md +1 -3
- package/framework/commands/morph-hotfix.md +1 -1
- package/framework/commands/morph-proposal.md +2 -2
- package/framework/docs/CLI.md +22 -17
- package/framework/evals/review-checklist.md +90 -0
- package/framework/evals/trace-schema.md +2 -12
- package/framework/hooks/README.md +2 -3
- package/framework/hooks/claude-code/core/heavy-gate.js +1 -1
- package/framework/hooks/claude-code/core/protect-spec-files.js +2 -2
- package/framework/hooks/claude-code/core/telemetry-log.js +2 -2
- package/framework/hooks/claude-code/core/trace-autogen.js +1 -22
- package/framework/hooks/claude-code/core/validate-completion.js +3 -56
- package/framework/hooks/claude-code/core/worktree-env-inject.js +1 -4
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/build-check.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +1 -1
- package/framework/hooks/shared/phase-utils.js +0 -5
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/phases.json +4 -6
- package/framework/rubrics/backend.json +1 -1
- package/framework/rubrics/default.json +1 -1
- package/framework/rubrics/frontend.json +1 -1
- package/framework/rules/ai-agents-standards.md +1 -0
- package/framework/rules/morph-workflow.md +6 -6
- package/framework/schemas/feature.schema.json +2 -28
- package/framework/schemas/tasks.schema.json +6 -2
- package/framework/skills/README.md +1 -2
- 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 +145 -246
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +5 -3
- package/framework/skills/core/morph-implement/SKILL.md +101 -201
- package/framework/skills/core/morph-machine/SKILL.md +3 -3
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +171 -287
- package/framework/skills/core/morph-proposal/SKILL.md +4 -5
- package/framework/skills/core/morph-review/SKILL.md +109 -430
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +2 -2
- package/framework/skills/core/morph-uiux/SKILL.md +2 -3
- package/framework/standards/STANDARDS.json +54 -10
- package/framework/standards/ai-agents/agent-archetypes.md +1 -1
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/agent-spec.md +1 -1
- package/framework/standards/ai-agents/batching.md +1 -1
- package/framework/standards/ai-agents/context-providers.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +1 -1
- package/framework/standards/ai-agents/cost-and-budget.md +1 -1
- package/framework/standards/ai-agents/durable-workflows-hitl.md +1 -1
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/mcp-server.md +1 -1
- package/framework/standards/ai-agents/mcp-tools.md +1 -1
- package/framework/standards/ai-agents/media-pipeline.md +1 -1
- package/framework/standards/ai-agents/media-video.md +1 -1
- package/framework/standards/ai-agents/middleware-patterns.md +1 -1
- package/framework/standards/ai-agents/modalities-image-gen.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +1 -1
- package/framework/standards/ai-agents/modalities-text-to-speech.md +1 -1
- package/framework/standards/ai-agents/modalities-vision.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +1 -1
- package/framework/standards/ai-agents/observability-patterns.md +3 -3
- package/framework/standards/ai-agents/production.md +2 -2
- package/framework/standards/ai-agents/prompt-sources.md +1 -1
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +1 -1
- package/framework/standards/ai-agents/service-tier-flex.md +1 -1
- package/framework/standards/ai-agents/setup.md +1 -1
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/ai-agents/testing-ai.md +1 -1
- package/framework/standards/ai-agents/tool-adapters.md +226 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +1 -1
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +11 -1
- package/framework/templates/CLAUDE.md.core.template +1 -1
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/README.md +1 -1
- package/framework/templates/REGISTRY.json +0 -81
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/spec.md +19 -205
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +2 -2
- package/src/commands/e2e/e2e.js +1 -1
- package/src/commands/project/heavy.js +1 -1
- package/src/commands/project/status.js +3 -3
- package/src/commands/project/worktree.js +34 -16
- package/src/commands/state/approve.js +9 -50
- package/src/commands/state/finish.js +4 -0
- package/src/commands/state/gate-check.js +18 -107
- package/src/commands/state/gate-decision.js +1 -1
- package/src/commands/state/report-check.js +42 -17
- package/src/commands/validation/mutate.js +1 -1
- package/src/commands/validation/scout-check.js +28 -0
- package/src/commands/validation/validate-feature.js +5 -32
- package/src/commands/validation/verify.js +16 -25
- package/src/core/paths/output-schema.js +0 -2
- package/src/core/state/state-manager.js +1 -2
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/lib/format.js +5 -0
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/FeatureDetail.jsx +16 -22
- package/src/dashboard/app/pages/Features.jsx +7 -7
- package/src/dashboard/app/pages/PhasePipeline.jsx +1 -1
- package/src/dashboard/app/pages/Rubrics.jsx +7 -11
- package/src/dashboard/scanner/docs-scanner.js +1 -2
- package/src/dashboard/scanner/features-scanner.js +3 -4
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/agents/output-collector.js +0 -1
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/e2e/compose-env.js +5 -2
- package/src/lib/evaluator-report.js +52 -334
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/handoff-generator.js +4 -5
- package/src/lib/git/evidence-staleness.js +39 -0
- package/src/lib/installers/memory-installer.js +2 -2
- package/src/lib/mutation/manifest.js +8 -2
- package/src/lib/mutation/report.js +5 -3
- package/src/lib/mutation/runner.js +9 -7
- package/src/lib/risk-detector.js +63 -125
- package/src/lib/scout/scout-check.js +293 -0
- package/src/lib/tasks/eval-node.js +4 -34
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/tasks-json-parser.js +7 -5
- package/src/lib/tasks/test-nodes.js +1 -1
- package/src/lib/tasks/test-runner.js +1 -1
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -42
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/reuse/reuse-validator.js +6 -8
- package/src/lib/validators/verify-runner.js +1 -1
- package/src/lib/worktree/env-allocator.js +35 -5
- package/src/scripts/setup-infra.js +1 -1
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +5 -9
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/hooks-installer.js +3 -10
- package/src/utils/skills-installer.js +1 -1
- package/framework/evals/proof-rubrics/README.md +0 -76
- package/framework/evals/proof-rubrics/eval-de-modelo.md +0 -80
- package/framework/evals/proof-rubrics/fiacao.md +0 -54
- package/framework/evals/proof-rubrics/guard-cego.md +0 -52
- package/framework/evals/proof-rubrics/media-claims.md +0 -109
- package/framework/evals/proof-rubrics/mutation-proof.md +0 -107
- package/framework/evals/proof-rubrics/observabilidade-genai.md +0 -119
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +0 -48
- package/framework/evals/rubrics/README.md +0 -45
- package/framework/evals/rubrics/architecture.md +0 -103
- package/framework/evals/rubrics/code-quality.md +0 -98
- package/framework/evals/rubrics/condensed.md +0 -57
- package/framework/evals/rubrics/contracts.md +0 -67
- package/framework/evals/rubrics/test-coverage.md +0 -99
- package/framework/evals/scenarios/dotnet-crud.yaml +0 -42
- package/framework/evals/scenarios/nextjs-auth.yaml +0 -38
- package/framework/evals/suites/standard.yaml +0 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +0 -127
- package/framework/skills/core/morph-eval/SKILL.md +0 -64
- package/framework/templates/.idea/morph-templates.xml +0 -92
- package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
- package/framework/templates/docs/clarifications.md +0 -253
- package/framework/templates/docs/onboarding.md +0 -122
- package/framework/templates/docs/schema-analysis.md +0 -119
- package/framework/templates/docs/user-stories.md +0 -34
- package/src/commands/state/score.js +0 -147
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Auto-generates the execution trace
|
|
6
6
|
* `.morph/traces/YYYY-MM-DD-{feature}-implement.json` from tasks.json +
|
|
7
|
-
* recap.md
|
|
7
|
+
* recap.md whenever tasks.json is written during
|
|
8
8
|
* the implement phase.
|
|
9
9
|
*
|
|
10
10
|
* This removes the manual trace-writing step from the skills (friction item
|
|
@@ -17,8 +17,6 @@
|
|
|
17
17
|
* - tasks.json → task set, status (done→completed, skipped→skipped,
|
|
18
18
|
* blocked→failed; pending/in_progress stay out), outputs
|
|
19
19
|
* - recap.md → per-task `**Standard:**` id and `**Persona:**` line
|
|
20
|
-
* - feature.json → taskScores (lastScore → eval_score, dimensions →
|
|
21
|
-
* eval_dimensions, camelCase → snake_case)
|
|
22
20
|
*
|
|
23
21
|
* The most recent existing `*-{feature}-implement.json` is updated in place
|
|
24
22
|
* (session_id/started_at preserved); a new dated file is created otherwise.
|
|
@@ -207,12 +205,6 @@ async function generateTrace(payload) {
|
|
|
207
205
|
try { recap = parseRecap(readFileSync(recapPath, 'utf8')); } catch { recap = {}; }
|
|
208
206
|
}
|
|
209
207
|
|
|
210
|
-
let taskScores = {};
|
|
211
|
-
const featureJsonPath = join(featureDir, 'feature.json');
|
|
212
|
-
if (existsSync(featureJsonPath)) {
|
|
213
|
-
try { taskScores = JSON.parse(readFileSync(featureJsonPath, 'utf8')).taskScores ?? {}; } catch { taskScores = {}; }
|
|
214
|
-
}
|
|
215
|
-
|
|
216
208
|
const tracesDir = join(cwd, '.morph', 'traces');
|
|
217
209
|
mkdirSync(tracesDir, { recursive: true });
|
|
218
210
|
|
|
@@ -235,8 +227,6 @@ async function generateTrace(payload) {
|
|
|
235
227
|
.filter(t => STATUS_MAP[t.status])
|
|
236
228
|
.map(t => {
|
|
237
229
|
const recapEntry = recap[t.id] ?? {};
|
|
238
|
-
const score = taskScores[t.id];
|
|
239
|
-
const dims = score?.dimensions;
|
|
240
230
|
return {
|
|
241
231
|
id: t.id,
|
|
242
232
|
title: t.title ?? null,
|
|
@@ -246,13 +236,6 @@ async function generateTrace(payload) {
|
|
|
246
236
|
sub_agent_dispatched: recapEntry.persona
|
|
247
237
|
? /^morph-|general-purpose/.test(recapEntry.persona)
|
|
248
238
|
: false,
|
|
249
|
-
eval_score: score?.lastScore ?? null,
|
|
250
|
-
eval_dimensions: dims ? {
|
|
251
|
-
architecture: dims.architecture ?? null,
|
|
252
|
-
contracts: dims.contracts ?? null,
|
|
253
|
-
code_quality: dims.codeQuality ?? null,
|
|
254
|
-
test_coverage: dims.testCoverage ?? null,
|
|
255
|
-
} : null,
|
|
256
239
|
files_modified: Array.isArray(t.outputs) ? t.outputs.filter(o => typeof o === 'string') : [],
|
|
257
240
|
duration_seconds: taskDuration(times, t.id),
|
|
258
241
|
};
|
|
@@ -260,7 +243,6 @@ async function generateTrace(payload) {
|
|
|
260
243
|
|
|
261
244
|
const allSettled = tasks.length > 0 &&
|
|
262
245
|
tasks.every(t => t.status !== 'pending' && t.status !== 'in_progress');
|
|
263
|
-
const scores = entries.map(e => e.eval_score).filter(s => typeof s === 'number');
|
|
264
246
|
|
|
265
247
|
const standardCounts = {};
|
|
266
248
|
for (const entry of entries) {
|
|
@@ -284,9 +266,6 @@ async function generateTrace(payload) {
|
|
|
284
266
|
phase_summary: {
|
|
285
267
|
tasks_completed: entries.filter(e => e.status === 'completed').length,
|
|
286
268
|
tasks_failed: entries.filter(e => e.status === 'failed').length,
|
|
287
|
-
avg_eval_score: scores.length
|
|
288
|
-
? Math.round((scores.reduce((a, b) => a + b, 0) / scores.length) * 10) / 10
|
|
289
|
-
: null,
|
|
290
269
|
standards_most_used: mostUsed,
|
|
291
270
|
sub_agents_dispatched: entries.filter(e => e.sub_agent_dispatched).length,
|
|
292
271
|
},
|
|
@@ -1,30 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Stop Hook:
|
|
5
|
-
*
|
|
6
|
-
* Event: Stop
|
|
7
|
-
*
|
|
8
|
-
* 1. Detects transitions into the `implement` phase and recommends /compact.
|
|
9
|
-
* 2. Runs stack-aware build check — dirty-flag gated (R1.2), warns only on
|
|
10
|
-
* failure (silent on pass/skip).
|
|
4
|
+
* Stop Hook: phase-transition alert, recap/tasks audit, session usage.
|
|
11
5
|
*
|
|
12
6
|
* Phase transition tracking: stores last-seen phase per feature in
|
|
13
7
|
* .morph/memory/phase-watch.json so the alert fires exactly once.
|
|
14
8
|
*
|
|
15
|
-
* Build-dirty gate: post-edit-typecheck.js touches
|
|
16
|
-
* .morph/memory/build-dirty.json on every source-file Write/Edit. Without
|
|
17
|
-
* that flag nothing changed since the last verified build, so the (often
|
|
18
|
-
* expensive) full build/typecheck is skipped entirely instead of running on
|
|
19
|
-
* every single Stop.
|
|
20
|
-
*
|
|
21
9
|
* Fail-open: exits 0 on any error.
|
|
22
10
|
*/
|
|
23
11
|
|
|
24
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync
|
|
12
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
25
13
|
import { join } from 'path';
|
|
26
14
|
import { createHash } from 'crypto';
|
|
27
|
-
import { runBuildCheck } from '../../shared/build-check.js';
|
|
28
15
|
import {
|
|
29
16
|
stateExists, getActiveFeature, getMostRecentFeature, derivePhaseForFeature,
|
|
30
17
|
} from '../../shared/state-reader.js';
|
|
@@ -154,9 +141,6 @@ export async function run(payload) {
|
|
|
154
141
|
const cwd = process.cwd();
|
|
155
142
|
const warnings = [];
|
|
156
143
|
|
|
157
|
-
// .morph/memory/ hosts phase-watch.json, build-dirty.json (written by
|
|
158
|
-
// post-edit-typecheck.js) and warned-hashes.json — ensure it exists once,
|
|
159
|
-
// up front, so every write below can assume the dir is there.
|
|
160
144
|
const memDir = join(cwd, '.morph', 'memory');
|
|
161
145
|
try { mkdirSync(memDir, { recursive: true }); } catch { /* non-blocking */ }
|
|
162
146
|
|
|
@@ -187,7 +171,7 @@ export async function run(payload) {
|
|
|
187
171
|
`⚡ Phase advanced: ${lastPhase} → implement`,
|
|
188
172
|
``,
|
|
189
173
|
`💡 Recommended before starting implementation:`,
|
|
190
|
-
` Run /compact to free up context — spec.md
|
|
174
|
+
` Run /compact to free up context — spec.md and tasks.json`,
|
|
191
175
|
` loaded during planning take significant space. A compact now gives`,
|
|
192
176
|
` Claude a clean slate focused on writing code.`,
|
|
193
177
|
``,
|
|
@@ -195,43 +179,6 @@ export async function run(payload) {
|
|
|
195
179
|
);
|
|
196
180
|
}
|
|
197
181
|
|
|
198
|
-
// ── Build/typecheck verification (dirty-flag gated — R1.2) ────────────────
|
|
199
|
-
// Only run the (often expensive) full build/typecheck when something
|
|
200
|
-
// actually changed since the last time it was verified — post-edit-
|
|
201
|
-
// typecheck.js sets the flag on every source-file Write/Edit. No flag →
|
|
202
|
-
// nothing to verify → skip silently, no config/stack read even attempted.
|
|
203
|
-
const BUILD_CHECK_TIMEOUT_MS = 30000; // per-step timeout — build-check.js's own default, made explicit here
|
|
204
|
-
try {
|
|
205
|
-
const dirtyFlagPath = join(memDir, 'build-dirty.json');
|
|
206
|
-
if (existsSync(dirtyFlagPath)) {
|
|
207
|
-
const configPath = join(cwd, '.morph', 'config', 'config.json');
|
|
208
|
-
if (existsSync(configPath)) {
|
|
209
|
-
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
210
|
-
const stack = config?.project?.stack;
|
|
211
|
-
const { frontendPath, backendPath } = config?.project ?? {};
|
|
212
|
-
if (stack && currentPhase === 'implement') {
|
|
213
|
-
const buildResult = runBuildCheck(cwd, stack, BUILD_CHECK_TIMEOUT_MS, { frontendPath, backendPath });
|
|
214
|
-
if (buildResult.status === 'fail') {
|
|
215
|
-
warnings.push('⚠ Build/typecheck failed:');
|
|
216
|
-
warnings.push(buildResult.output);
|
|
217
|
-
warnings.push('Fix build errors before ending session.');
|
|
218
|
-
// keep the dirty flag — still dirty until a build actually passes
|
|
219
|
-
} else {
|
|
220
|
-
// 'pass' → verified clean. 'skip' → no build step was even
|
|
221
|
-
// resolved for this stack (e.g. fresh worktree, no tsconfig) —
|
|
222
|
-
// nothing to verify, so there is nothing left to stay dirty about.
|
|
223
|
-
// Either way, re-arm on the NEXT source edit, not every Stop.
|
|
224
|
-
try { unlinkSync(dirtyFlagPath); } catch { /* non-blocking */ }
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
// No flag → skip the build entirely, silently.
|
|
230
|
-
} catch {
|
|
231
|
-
// Fail-open — build check must never block Stop
|
|
232
|
-
}
|
|
233
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
234
|
-
|
|
235
182
|
// ── Recap + tasks.json audit ──────────────────────────────────────────────
|
|
236
183
|
// Surface drift between recap.md and tasks.json, plus missing standard
|
|
237
184
|
// citations. Advisory warnings only — Stop hook never blocks.
|
|
@@ -42,9 +42,6 @@ const LAUNCHER_RE = /\b(?:(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?(?:dev|start)\b|nex
|
|
|
42
42
|
/** The command already chose its own port/project — respect it. */
|
|
43
43
|
const SELF_CONFIGURED_RE = /(?:^|[\s;&|])(?:PORT|API_PORT|POSTGRES_PORT|COMPOSE_PROJECT_NAME|ASPNETCORE_URLS)=|--port\b|(?:^|\s)-p\s+\d|compose\b[^|;&]*\s-p\s/;
|
|
44
44
|
|
|
45
|
-
/** Keys the layout owns; anything else in the manifest is left alone. */
|
|
46
|
-
const LAYOUT_KEYS = ['COMPOSE_PROJECT_NAME', 'MORPH_PORT_BASE', 'MORPH_PORT_RANGE', 'PORT', 'API_PORT', 'POSTGRES_PORT', 'REDIS_PORT', 'ASPNETCORE_URLS', 'PLAYWRIGHT_BASE_URL'];
|
|
47
|
-
|
|
48
45
|
/** POSIX single-quote, safe for Git Bash on Windows too. */
|
|
49
46
|
function shq(v) {
|
|
50
47
|
return `'${String(v).replace(/'/g, "'\\''")}'`;
|
|
@@ -59,7 +56,7 @@ function shq(v) {
|
|
|
59
56
|
export function rewriteCommand(command, env) {
|
|
60
57
|
if (!command || !LAUNCHER_RE.test(command)) return null;
|
|
61
58
|
if (SELF_CONFIGURED_RE.test(command)) return null;
|
|
62
|
-
const pairs =
|
|
59
|
+
const pairs = Object.keys(env).filter((k) => k !== 'MORPH_FEATURE' && env[k]).map((k) => [k, env[k]]);
|
|
63
60
|
if (!pairs.some(([k]) => k === 'PORT')) return null;
|
|
64
61
|
// The web tier's URL is what the spec suite needs; derive it once here.
|
|
65
62
|
if (!env.PLAYWRIGHT_BASE_URL) pairs.push(['PLAYWRIGHT_BASE_URL', `http://localhost:${env.PORT}`]);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# morph-spec statusline — installed globally to ~/.claude/statusline.sh
|
|
3
|
-
# Claude Code invokes this with JSON via stdin after each response.
|
|
4
|
-
# Requires: Python 3 available as `python3` or `python` on PATH.
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
-
if command -v python3 &>/dev/null; then
|
|
8
|
-
python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
9
|
-
elif command -v python &>/dev/null; then
|
|
10
|
-
python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
11
|
-
fi
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# morph-spec statusline — installed globally to ~/.claude/statusline.sh
|
|
3
|
+
# Claude Code invokes this with JSON via stdin after each response.
|
|
4
|
+
# Requires: Python 3 available as `python3` or `python` on PATH.
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
if command -v python3 &>/dev/null; then
|
|
8
|
+
python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
9
|
+
elif command -v python &>/dev/null; then
|
|
10
|
+
python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
11
|
+
fi
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Scope: universal | Validates commit message format (Conventional Commits)
|
|
3
|
-
# MORPH-SPEC Commit Message Hook
|
|
4
|
-
# Enforces Conventional Commits specification
|
|
5
|
-
|
|
6
|
-
COMMIT_MSG_FILE="$1"
|
|
7
|
-
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
|
|
8
|
-
|
|
9
|
-
# Conventional Commits pattern: type(scope): description
|
|
10
|
-
# Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert
|
|
11
|
-
PATTERN="^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert)(\(.+\))?: .{1,100}"
|
|
12
|
-
|
|
13
|
-
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
|
|
14
|
-
echo "❌ Invalid commit message format!"
|
|
15
|
-
echo ""
|
|
16
|
-
echo "Required format: <type>(<scope>): <description>"
|
|
17
|
-
echo ""
|
|
18
|
-
echo "Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert"
|
|
19
|
-
echo ""
|
|
20
|
-
echo "Examples:"
|
|
21
|
-
echo " feat(auth): add JWT refresh token rotation"
|
|
22
|
-
echo " fix(api): resolve null reference in UserService"
|
|
23
|
-
echo " docs(readme): update installation instructions"
|
|
24
|
-
echo ""
|
|
25
|
-
echo "Your message:"
|
|
26
|
-
echo " $COMMIT_MSG"
|
|
27
|
-
echo ""
|
|
28
|
-
echo "Override with: git commit --no-verify"
|
|
29
|
-
exit 1
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
echo "✅ Commit message format valid"
|
|
33
|
-
exit 0
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scope: universal | Validates commit message format (Conventional Commits)
|
|
3
|
+
# MORPH-SPEC Commit Message Hook
|
|
4
|
+
# Enforces Conventional Commits specification
|
|
5
|
+
|
|
6
|
+
COMMIT_MSG_FILE="$1"
|
|
7
|
+
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
|
|
8
|
+
|
|
9
|
+
# Conventional Commits pattern: type(scope): description
|
|
10
|
+
# Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert
|
|
11
|
+
PATTERN="^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert)(\(.+\))?: .{1,100}"
|
|
12
|
+
|
|
13
|
+
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
|
|
14
|
+
echo "❌ Invalid commit message format!"
|
|
15
|
+
echo ""
|
|
16
|
+
echo "Required format: <type>(<scope>): <description>"
|
|
17
|
+
echo ""
|
|
18
|
+
echo "Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert"
|
|
19
|
+
echo ""
|
|
20
|
+
echo "Examples:"
|
|
21
|
+
echo " feat(auth): add JWT refresh token rotation"
|
|
22
|
+
echo " fix(api): resolve null reference in UserService"
|
|
23
|
+
echo " docs(readme): update installation instructions"
|
|
24
|
+
echo ""
|
|
25
|
+
echo "Your message:"
|
|
26
|
+
echo " $COMMIT_MSG"
|
|
27
|
+
echo ""
|
|
28
|
+
echo "Override with: git commit --no-verify"
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
echo "✅ Commit message format valid"
|
|
33
|
+
exit 0
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Scope: universal | Validates agents.json Schema
|
|
3
|
-
# MORPH-SPEC Pre-Commit Hook: Agent Configuration Validation
|
|
4
|
-
# Uses validate-agents.js to check agents.json
|
|
5
|
-
|
|
6
|
-
echo "🤖 Validating agent configuration..."
|
|
7
|
-
|
|
8
|
-
# Check if agents.json is being modified
|
|
9
|
-
if git diff --cached --name-only | grep -q 'agents\.json$'; then
|
|
10
|
-
echo "Detected changes to agents.json"
|
|
11
|
-
|
|
12
|
-
# Run validator
|
|
13
|
-
if ! morph-spec validate-agents-skills; then
|
|
14
|
-
echo ""
|
|
15
|
-
echo "❌ COMMIT BLOCKED: agents.json validation failed"
|
|
16
|
-
echo " Fix errors above before committing"
|
|
17
|
-
exit 1
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
echo "✓ agents.json is valid"
|
|
21
|
-
else
|
|
22
|
-
echo "✓ No changes to agents.json"
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
exit 0
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Scope: universal | Validates agents.json Schema
|
|
3
|
+
# MORPH-SPEC Pre-Commit Hook: Agent Configuration Validation
|
|
4
|
+
# Uses validate-agents.js to check agents.json
|
|
5
|
+
|
|
6
|
+
echo "🤖 Validating agent configuration..."
|
|
7
|
+
|
|
8
|
+
# Check if agents.json is being modified
|
|
9
|
+
if git diff --cached --name-only | grep -q 'agents\.json$'; then
|
|
10
|
+
echo "Detected changes to agents.json"
|
|
11
|
+
|
|
12
|
+
# Run validator
|
|
13
|
+
if ! morph-spec validate-agents-skills; then
|
|
14
|
+
echo ""
|
|
15
|
+
echo "❌ COMMIT BLOCKED: agents.json validation failed"
|
|
16
|
+
echo " Fix errors above before committing"
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
echo "✓ agents.json is valid"
|
|
21
|
+
else
|
|
22
|
+
echo "✓ No changes to agents.json"
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
exit 0
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Scope: universal | Orchestrates framework + stack-specific pre-commit hooks
|
|
3
|
-
# MORPH-SPEC Master Pre-Commit Hook
|
|
4
|
-
# Runs universal framework hooks + stack-specific hooks in sequence
|
|
5
|
-
|
|
6
|
-
echo "╔════════════════════════════════════════════════╗"
|
|
7
|
-
echo "║ MORPH-SPEC PRE-COMMIT VALIDATION ║"
|
|
8
|
-
echo "╚════════════════════════════════════════════════╝"
|
|
9
|
-
echo ""
|
|
10
|
-
|
|
11
|
-
FRAMEWORK_HOOKS_DIR="$(dirname "$0")"
|
|
12
|
-
STACK_HOOKS_DIR=".morph/hooks/pre-commit"
|
|
13
|
-
|
|
14
|
-
HAS_FAILURES=false
|
|
15
|
-
|
|
16
|
-
# Run framework hooks (universal - all stacks)
|
|
17
|
-
echo "📦 Running framework hooks (universal)..."
|
|
18
|
-
for hook in "$FRAMEWORK_HOOKS_DIR"/*.sh; do
|
|
19
|
-
# Skip orchestrator itself
|
|
20
|
-
if [[ "$hook" == */orchestrator.sh ]]; then
|
|
21
|
-
continue
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
if [[ -f "$hook" ]]; then
|
|
25
|
-
hook_name=$(basename "$hook")
|
|
26
|
-
echo "─────────────────────────────────────────────────"
|
|
27
|
-
if ! bash "$hook"; then
|
|
28
|
-
HAS_FAILURES=true
|
|
29
|
-
echo "❌ Framework hook failed: $hook_name"
|
|
30
|
-
fi
|
|
31
|
-
echo ""
|
|
32
|
-
fi
|
|
33
|
-
done
|
|
34
|
-
|
|
35
|
-
# Run stack-specific hooks (if exist)
|
|
36
|
-
if [[ -d "$STACK_HOOKS_DIR" ]]; then
|
|
37
|
-
echo "🎯 Running stack-specific hooks..."
|
|
38
|
-
for hook in "$STACK_HOOKS_DIR"/*.sh; do
|
|
39
|
-
if [[ -f "$hook" ]]; then
|
|
40
|
-
hook_name=$(basename "$hook")
|
|
41
|
-
echo "─────────────────────────────────────────────────"
|
|
42
|
-
if ! bash "$hook"; then
|
|
43
|
-
HAS_FAILURES=true
|
|
44
|
-
echo "❌ Stack hook failed: $hook_name"
|
|
45
|
-
fi
|
|
46
|
-
echo ""
|
|
47
|
-
fi
|
|
48
|
-
done
|
|
49
|
-
else
|
|
50
|
-
echo "ℹ️ No stack-specific hooks found (optional)"
|
|
51
|
-
echo ""
|
|
52
|
-
fi
|
|
53
|
-
|
|
54
|
-
echo "═════════════════════════════════════════════════"
|
|
55
|
-
|
|
56
|
-
if [[ "$HAS_FAILURES" == "true" ]]; then
|
|
57
|
-
echo "❌ PRE-COMMIT VALIDATION FAILED"
|
|
58
|
-
echo " Fix errors above before committing"
|
|
59
|
-
echo " Or use: git commit --no-verify to skip"
|
|
60
|
-
exit 1
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
echo "✅ ALL PRE-COMMIT VALIDATIONS PASSED"
|
|
64
|
-
exit 0
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scope: universal | Orchestrates framework + stack-specific pre-commit hooks
|
|
3
|
+
# MORPH-SPEC Master Pre-Commit Hook
|
|
4
|
+
# Runs universal framework hooks + stack-specific hooks in sequence
|
|
5
|
+
|
|
6
|
+
echo "╔════════════════════════════════════════════════╗"
|
|
7
|
+
echo "║ MORPH-SPEC PRE-COMMIT VALIDATION ║"
|
|
8
|
+
echo "╚════════════════════════════════════════════════╝"
|
|
9
|
+
echo ""
|
|
10
|
+
|
|
11
|
+
FRAMEWORK_HOOKS_DIR="$(dirname "$0")"
|
|
12
|
+
STACK_HOOKS_DIR=".morph/hooks/pre-commit"
|
|
13
|
+
|
|
14
|
+
HAS_FAILURES=false
|
|
15
|
+
|
|
16
|
+
# Run framework hooks (universal - all stacks)
|
|
17
|
+
echo "📦 Running framework hooks (universal)..."
|
|
18
|
+
for hook in "$FRAMEWORK_HOOKS_DIR"/*.sh; do
|
|
19
|
+
# Skip orchestrator itself
|
|
20
|
+
if [[ "$hook" == */orchestrator.sh ]]; then
|
|
21
|
+
continue
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if [[ -f "$hook" ]]; then
|
|
25
|
+
hook_name=$(basename "$hook")
|
|
26
|
+
echo "─────────────────────────────────────────────────"
|
|
27
|
+
if ! bash "$hook"; then
|
|
28
|
+
HAS_FAILURES=true
|
|
29
|
+
echo "❌ Framework hook failed: $hook_name"
|
|
30
|
+
fi
|
|
31
|
+
echo ""
|
|
32
|
+
fi
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
# Run stack-specific hooks (if exist)
|
|
36
|
+
if [[ -d "$STACK_HOOKS_DIR" ]]; then
|
|
37
|
+
echo "🎯 Running stack-specific hooks..."
|
|
38
|
+
for hook in "$STACK_HOOKS_DIR"/*.sh; do
|
|
39
|
+
if [[ -f "$hook" ]]; then
|
|
40
|
+
hook_name=$(basename "$hook")
|
|
41
|
+
echo "─────────────────────────────────────────────────"
|
|
42
|
+
if ! bash "$hook"; then
|
|
43
|
+
HAS_FAILURES=true
|
|
44
|
+
echo "❌ Stack hook failed: $hook_name"
|
|
45
|
+
fi
|
|
46
|
+
echo ""
|
|
47
|
+
fi
|
|
48
|
+
done
|
|
49
|
+
else
|
|
50
|
+
echo "ℹ️ No stack-specific hooks found (optional)"
|
|
51
|
+
echo ""
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
echo "═════════════════════════════════════════════════"
|
|
55
|
+
|
|
56
|
+
if [[ "$HAS_FAILURES" == "true" ]]; then
|
|
57
|
+
echo "❌ PRE-COMMIT VALIDATION FAILED"
|
|
58
|
+
echo " Fix errors above before committing"
|
|
59
|
+
echo " Or use: git commit --no-verify to skip"
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
echo "✅ ALL PRE-COMMIT VALIDATIONS PASSED"
|
|
64
|
+
exit 0
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Scope: universal | Validates spec.md required sections
|
|
3
|
-
# MORPH-SPEC Pre-Commit Hook: Spec Validation
|
|
4
|
-
# Validates that spec.md files have required sections
|
|
5
|
-
|
|
6
|
-
echo "🔍 Validating spec files..."
|
|
7
|
-
|
|
8
|
-
# Find modified spec.md files
|
|
9
|
-
SPEC_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep 'spec\.md$')
|
|
10
|
-
|
|
11
|
-
if [ -z "$SPEC_FILES" ]; then
|
|
12
|
-
echo "✓ No spec files modified"
|
|
13
|
-
exit 0
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
HAS_ERRORS=false
|
|
17
|
-
|
|
18
|
-
for spec_file in $SPEC_FILES; do
|
|
19
|
-
echo "Checking: $spec_file"
|
|
20
|
-
|
|
21
|
-
# Required sections
|
|
22
|
-
REQUIRED_SECTIONS=(
|
|
23
|
-
"## 📋 Metadata"
|
|
24
|
-
"## 🎯 Overview"
|
|
25
|
-
"## 🏗️ Technical Design"
|
|
26
|
-
"## ✅ Acceptance Criteria"
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
for section in "${REQUIRED_SECTIONS[@]}"; do
|
|
30
|
-
if ! grep -q "$section" "$spec_file"; then
|
|
31
|
-
echo " ❌ Missing section: $section"
|
|
32
|
-
HAS_ERRORS=true
|
|
33
|
-
fi
|
|
34
|
-
done
|
|
35
|
-
|
|
36
|
-
# Check if has at least one user story or requirement
|
|
37
|
-
if ! grep -qi "user story\|requirement\|acceptance criteria" "$spec_file"; then
|
|
38
|
-
echo " ⚠️ Warning: No user stories or requirements found"
|
|
39
|
-
fi
|
|
40
|
-
done
|
|
41
|
-
|
|
42
|
-
if [ "$HAS_ERRORS" = true ]; then
|
|
43
|
-
echo ""
|
|
44
|
-
echo "❌ COMMIT BLOCKED: spec.md files are incomplete"
|
|
45
|
-
echo " Add missing sections before committing"
|
|
46
|
-
exit 1
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
echo "✓ All spec files are valid"
|
|
50
|
-
exit 0
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Scope: universal | Validates spec.md required sections
|
|
3
|
+
# MORPH-SPEC Pre-Commit Hook: Spec Validation
|
|
4
|
+
# Validates that spec.md files have required sections
|
|
5
|
+
|
|
6
|
+
echo "🔍 Validating spec files..."
|
|
7
|
+
|
|
8
|
+
# Find modified spec.md files
|
|
9
|
+
SPEC_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep 'spec\.md$')
|
|
10
|
+
|
|
11
|
+
if [ -z "$SPEC_FILES" ]; then
|
|
12
|
+
echo "✓ No spec files modified"
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
HAS_ERRORS=false
|
|
17
|
+
|
|
18
|
+
for spec_file in $SPEC_FILES; do
|
|
19
|
+
echo "Checking: $spec_file"
|
|
20
|
+
|
|
21
|
+
# Required sections
|
|
22
|
+
REQUIRED_SECTIONS=(
|
|
23
|
+
"## 📋 Metadata"
|
|
24
|
+
"## 🎯 Overview"
|
|
25
|
+
"## 🏗️ Technical Design"
|
|
26
|
+
"## ✅ Acceptance Criteria"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
for section in "${REQUIRED_SECTIONS[@]}"; do
|
|
30
|
+
if ! grep -q "$section" "$spec_file"; then
|
|
31
|
+
echo " ❌ Missing section: $section"
|
|
32
|
+
HAS_ERRORS=true
|
|
33
|
+
fi
|
|
34
|
+
done
|
|
35
|
+
|
|
36
|
+
# Check if has at least one user story or requirement
|
|
37
|
+
if ! grep -qi "user story\|requirement\|acceptance criteria" "$spec_file"; then
|
|
38
|
+
echo " ⚠️ Warning: No user stories or requirements found"
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
if [ "$HAS_ERRORS" = true ]; then
|
|
43
|
+
echo ""
|
|
44
|
+
echo "❌ COMMIT BLOCKED: spec.md files are incomplete"
|
|
45
|
+
echo " Add missing sections before committing"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
echo "✓ All spec files are valid"
|
|
50
|
+
exit 0
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Scope: universal | Runs test suite before push
|
|
3
|
-
# MORPH-SPEC Pre-Push Hook
|
|
4
|
-
# Ensures all tests pass before pushing to remote
|
|
5
|
-
|
|
6
|
-
echo "🧪 Running test suite before push..."
|
|
7
|
-
echo ""
|
|
8
|
-
|
|
9
|
-
# Detect project type and run appropriate tests
|
|
10
|
-
if [[ -f *.csproj ]] || find . -maxdepth 2 -name "*.csproj" 2>/dev/null | grep -q .; then
|
|
11
|
-
# .NET project detected
|
|
12
|
-
echo "📦 Detected .NET project"
|
|
13
|
-
|
|
14
|
-
if ! dotnet test --verbosity minimal --no-build --no-restore 2>/dev/null; then
|
|
15
|
-
echo ""
|
|
16
|
-
echo "❌ .NET tests failed!"
|
|
17
|
-
echo ""
|
|
18
|
-
echo "Fix failing tests before pushing."
|
|
19
|
-
echo "Override with: git push --no-verify"
|
|
20
|
-
exit 1
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
elif [[ -f package.json ]]; then
|
|
24
|
-
# Node.js project detected
|
|
25
|
-
echo "📦 Detected Node.js project"
|
|
26
|
-
|
|
27
|
-
if ! npm test 2>/dev/null; then
|
|
28
|
-
echo ""
|
|
29
|
-
echo "❌ Node.js tests failed!"
|
|
30
|
-
echo ""
|
|
31
|
-
echo "Fix failing tests before pushing."
|
|
32
|
-
echo "Override with: git push --no-verify"
|
|
33
|
-
exit 1
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
else
|
|
37
|
-
echo "⚠️ No recognized test framework found"
|
|
38
|
-
echo " Skipping test execution"
|
|
39
|
-
exit 0
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
echo ""
|
|
43
|
-
echo "✅ All tests passed - safe to push"
|
|
44
|
-
exit 0
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scope: universal | Runs test suite before push
|
|
3
|
+
# MORPH-SPEC Pre-Push Hook
|
|
4
|
+
# Ensures all tests pass before pushing to remote
|
|
5
|
+
|
|
6
|
+
echo "🧪 Running test suite before push..."
|
|
7
|
+
echo ""
|
|
8
|
+
|
|
9
|
+
# Detect project type and run appropriate tests
|
|
10
|
+
if [[ -f *.csproj ]] || find . -maxdepth 2 -name "*.csproj" 2>/dev/null | grep -q .; then
|
|
11
|
+
# .NET project detected
|
|
12
|
+
echo "📦 Detected .NET project"
|
|
13
|
+
|
|
14
|
+
if ! dotnet test --verbosity minimal --no-build --no-restore 2>/dev/null; then
|
|
15
|
+
echo ""
|
|
16
|
+
echo "❌ .NET tests failed!"
|
|
17
|
+
echo ""
|
|
18
|
+
echo "Fix failing tests before pushing."
|
|
19
|
+
echo "Override with: git push --no-verify"
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
elif [[ -f package.json ]]; then
|
|
24
|
+
# Node.js project detected
|
|
25
|
+
echo "📦 Detected Node.js project"
|
|
26
|
+
|
|
27
|
+
if ! npm test 2>/dev/null; then
|
|
28
|
+
echo ""
|
|
29
|
+
echo "❌ Node.js tests failed!"
|
|
30
|
+
echo ""
|
|
31
|
+
echo "Fix failing tests before pushing."
|
|
32
|
+
echo "Override with: git push --no-verify"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
else
|
|
37
|
+
echo "⚠️ No recognized test framework found"
|
|
38
|
+
echo " Skipping test execution"
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
echo ""
|
|
43
|
+
echo "✅ All tests passed - safe to push"
|
|
44
|
+
exit 0
|
|
@@ -84,7 +84,7 @@ export function getBuildSteps(stack, { cwd, frontendPath, backendPath } = {}) {
|
|
|
84
84
|
|
|
85
85
|
if (isFrontendStack(s)) {
|
|
86
86
|
const frontendDir = frontendPath ? join(cwd, frontendPath) : cwd;
|
|
87
|
-
// node_modules guard
|
|
87
|
+
// node_modules guard : a fresh worktree before
|
|
88
88
|
// `morph-spec worktree link` has tsconfig.json (versioned) but no toolchain — running
|
|
89
89
|
// `npx tsc` there fetches an unrelated "tsc" package and fails noisily. No toolchain →
|
|
90
90
|
// no tsc step; the real typecheck runs wherever node_modules exists (root/CI).
|