@polymorphism-tech/morph-spec 9.0.0 → 9.2.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 +8 -9
- package/README.md +38 -36
- package/bin/morph-spec.js +11 -63
- package/bin/validate.js +1 -39
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -1
- package/framework/MORPH.md +3 -3
- package/framework/agents.json +19 -11
- package/framework/commands/morph-apply.md +1 -1
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +22 -31
- package/framework/evals/review-checklist.md +12 -2
- package/framework/hooks/README.md +8 -44
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +2 -16
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
- package/framework/hooks/claude-code/core/gate-guard.js +7 -67
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
- package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
- package/framework/hooks/claude-code/core/state-sync.js +12 -68
- package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
- package/framework/hooks/claude-code/core/validate-completion.js +11 -57
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
- package/framework/hooks/claude-code/core/worktree-context.js +10 -110
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +6 -75
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +6 -51
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +6 -25
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +2 -15
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +1 -3
- package/framework/hooks/shared/gate-question.js +1 -7
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +1 -42
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +4 -23
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/state-reader.js +13 -183
- package/framework/hooks/shared/state-writer.js +8 -52
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/rules/nextjs-standards.md +1 -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 +4 -4
- package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +9 -7
- package/framework/skills/core/morph-review/SKILL.md +9 -6
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +29 -12
- package/framework/standards/STANDARDS.json +36 -14
- package/framework/standards/STANDARDS.meta.json +16 -2
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +2 -2
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +1 -1
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +10 -4
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/frontend/nextjs/testing.md +10 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +9 -9
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- 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/plan.md +3 -1
- package/framework/templates/docs/ui-mockups.md +4 -0
- 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/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/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +17 -104
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +5 -57
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor.js +48 -302
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +3 -32
- package/src/commands/project/init.js +4 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/standards.js +1 -62
- package/src/commands/project/status.js +0 -53
- package/src/commands/project/update.js +31 -239
- package/src/commands/project/worktree.js +108 -882
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +14 -85
- package/src/commands/state/approve.js +15 -97
- package/src/commands/state/archive.js +12 -64
- package/src/commands/state/auto-approval.js +2 -5
- package/src/commands/state/create.js +22 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +98 -475
- package/src/commands/state/gate-check.js +22 -132
- package/src/commands/state/gate-decision.js +3 -39
- package/src/commands/state/handoff.js +3 -30
- package/src/commands/state/list.js +14 -84
- package/src/commands/state/report-check.js +8 -58
- package/src/commands/state/retype.js +4 -26
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +11 -11
- package/src/commands/validation/plan-check.js +4 -2
- package/src/commands/validation/tests-audit.js +7 -0
- package/src/commands/validation/validate-feature.js +10 -97
- package/src/commands/validation/verify.js +30 -113
- package/src/core/paths/output-schema.js +2 -101
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +0 -27
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +22 -304
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +0 -1
- package/src/core/work-types.js +1 -36
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
- package/src/dashboard/app/components/CommandPalette.jsx +0 -7
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +1 -5
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
- package/src/dashboard/app/pages/Features.jsx +1 -8
- package/src/dashboard/app/pages/Guide.jsx +0 -1
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +10 -69
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +0 -6
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +10 -66
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +2 -6
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +4 -19
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +14 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +4 -50
- package/src/lib/e2e/applicability.js +37 -0
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +9 -53
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +4 -37
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +1 -0
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +3 -15
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +1 -1
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +4 -27
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/adapters.js +6 -17
- package/src/lib/mutation/dotnet-introspection.js +20 -106
- package/src/lib/mutation/runner.js +23 -75
- package/src/lib/mutation/subset.js +1 -4
- package/src/lib/mutation/trx.js +0 -1
- package/src/lib/plan/artifact-vocabulary.js +5 -25
- package/src/lib/plan/checks.js +34 -33
- package/src/lib/plan/plan-parser.js +7 -8
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +11 -26
- package/src/lib/risk-detector.js +41 -173
- package/src/lib/specs/adr-index.js +6 -34
- package/src/lib/specs/frontmatter.js +2 -15
- package/src/lib/specs/gfm-table.js +1 -7
- package/src/lib/specs/revocations.js +1 -14
- package/src/lib/specs/screen-checklist.js +47 -124
- package/src/lib/stack/dotnet-build-ignores.js +2 -6
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +1 -8
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +1 -7
- package/src/lib/standards/sections.js +2 -13
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +21 -169
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -240
- package/src/lib/tasks/test-runner.js +43 -341
- package/src/lib/telemetry/metrics.js +7 -36
- package/src/lib/telemetry/telemetry-reader.js +13 -125
- package/src/lib/telemetry/verify-event.js +2 -30
- package/src/lib/tests-audit/audit.js +96 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +11 -101
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +5 -63
- package/src/lib/validators/validation-runner.js +21 -158
- package/src/lib/validators/verify-runner.js +85 -376
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -136
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +13 -95
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -147
- package/src/utils/agents-installer.js +29 -276
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -52
- package/src/utils/file-copier.js +11 -95
- package/src/utils/hooks-installer.js +21 -298
- package/src/utils/skills-installer.js +4 -101
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -9
- package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
- package/framework/hooks/git/pre-commit/agents.sh +0 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
- package/framework/hooks/git/pre-push/run-tests.sh +0 -44
- package/src/commands/utils/index.js +0 -3
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/old-format.js +0 -68
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* every case, judged only on the comments an edit ADDED. Whether a short
|
|
4
|
-
* comment restates the code is not decidable here; that stays with the
|
|
5
|
-
* Gate 3 reviewer and the `code-budget` density.
|
|
6
|
-
*
|
|
7
|
-
* @module comment-rules
|
|
8
|
-
*/
|
|
1
|
+
// Judges only the comments an edit ADDED, and only their shape: whether a
|
|
2
|
+
// comment restates the code is not decidable here.
|
|
9
3
|
|
|
10
4
|
import { existsSync, readdirSync, readFileSync } from 'fs';
|
|
11
5
|
import { dirname, join } from 'path';
|
|
@@ -58,13 +52,7 @@ function nearest(startDir, test) {
|
|
|
58
52
|
}
|
|
59
53
|
}
|
|
60
54
|
|
|
61
|
-
/**
|
|
62
|
-
* Doc-comments are allowed only on a package consumed outside the repo. When
|
|
63
|
-
* that cannot be read from the nearest project file, the rule stays quiet.
|
|
64
|
-
*
|
|
65
|
-
* @param {string} file - absolute path of the edited file
|
|
66
|
-
* @returns {boolean}
|
|
67
|
-
*/
|
|
55
|
+
/** Doc-comments are allowed only on a package consumed outside the repo; unreadable = quiet. */
|
|
68
56
|
export function docCommentsForbidden(file) {
|
|
69
57
|
const dir = dirname(file);
|
|
70
58
|
if (/\.cs$/i.test(file)) {
|
|
@@ -85,7 +73,7 @@ export function docCommentsForbidden(file) {
|
|
|
85
73
|
return JSON.parse(readFileSync(pkg, 'utf8')).private === true;
|
|
86
74
|
}
|
|
87
75
|
|
|
88
|
-
|
|
76
|
+
// Moved comments do not count as added.
|
|
89
77
|
function addedCommentLines(before, after, file) {
|
|
90
78
|
const remaining = new Map();
|
|
91
79
|
for (const text of commentLines(before, file).values()) {
|
|
@@ -115,10 +103,7 @@ function addedRegionLines(before, after) {
|
|
|
115
103
|
return out;
|
|
116
104
|
}
|
|
117
105
|
|
|
118
|
-
/**
|
|
119
|
-
* @param {{ file: string, before: string, after: string, docCommentsForbidden?: boolean }} input
|
|
120
|
-
* @returns {Array<{ line: number, rule: 'process'|'banner'|'doc-comment'|'long-block', text: string }>}
|
|
121
|
-
*/
|
|
106
|
+
/** @returns {Array<{ line: number, rule: 'process'|'banner'|'doc-comment'|'long-block', text: string }>} */
|
|
122
107
|
export function commentFindings({ file, before, after, docCommentsForbidden: docForbidden = false }) {
|
|
123
108
|
const isCs = /\.cs$/i.test(file);
|
|
124
109
|
const all = commentLines(after, file);
|
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* O comentário é o que `stripComments` apaga: o mesmo varredor que já separa
|
|
5
|
-
* comentário de string para o `doctor --ai`, com a sintaxe de TS/JS ligada
|
|
6
|
-
* fora do C#. Um regex por linha confundiria `https://` com comentário.
|
|
7
|
-
*
|
|
8
|
-
* @module comment-scanner
|
|
9
|
-
*/
|
|
1
|
+
// Comentário é o que `stripComments` apaga: um regex por linha confundiria
|
|
2
|
+
// `https://` com comentário.
|
|
10
3
|
|
|
11
4
|
import { stripComments } from './comment-stripper.js';
|
|
12
5
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* tecla de função, métrica, célula de planilha ou o modelo C-quatro: esses
|
|
16
|
-
* vizinhos não contam (casos em comment-scanner.test.js).
|
|
17
|
-
*/
|
|
6
|
+
// A mesma grafia de `C{n}`/`F{n}` é tecla, métrica, célula ou o modelo C-quatro:
|
|
7
|
+
// esses vizinhos não contam.
|
|
18
8
|
const PLAN_ID = new RegExp(String.raw`(?<!\b(?:key|tecla|press|aperte|cell|c[eé]lula|column|coluna)\s+)(?<![\w.#$/-])[CF]\d{1,2}(?![\w.(-]|\s+(?:key|tecla|score|model|modelo|diagram|diagrama)\b)`, 'i');
|
|
19
9
|
|
|
20
10
|
const PROCESS_CITATION = [
|
|
@@ -27,19 +17,11 @@ const PROCESS_CITATION = [
|
|
|
27
17
|
/\bmockups?\b/i,
|
|
28
18
|
];
|
|
29
19
|
|
|
30
|
-
/**
|
|
31
|
-
* @param {string} commentText
|
|
32
|
-
* @returns {boolean} o comentário usa o vocabulário de processo acima
|
|
33
|
-
*/
|
|
34
20
|
export function citesProcess(commentText) {
|
|
35
21
|
return PROCESS_CITATION.some((re) => re.test(commentText)) || PLAN_ID.test(commentText);
|
|
36
22
|
}
|
|
37
23
|
|
|
38
|
-
/**
|
|
39
|
-
* @param {string} content - conteúdo do arquivo
|
|
40
|
-
* @param {string} file - path, só para escolher a sintaxe
|
|
41
|
-
* @returns {Map<number, string>} nº da linha (1-based) → texto do comentário nela
|
|
42
|
-
*/
|
|
24
|
+
/** @returns {Map<number, string>} linha 1-based → texto do comentário; `file` só escolhe a sintaxe */
|
|
43
25
|
export function commentLines(content, file) {
|
|
44
26
|
const text = String(content || '');
|
|
45
27
|
const stripped = stripComments(text, { js: !/\.cs$/i.test(file) });
|
|
@@ -53,8 +35,7 @@ export function commentLines(content, file) {
|
|
|
53
35
|
if (first < 0) first = k;
|
|
54
36
|
last = k;
|
|
55
37
|
}
|
|
56
|
-
//
|
|
57
|
-
// primeiro ao último caractere apagado.
|
|
38
|
+
// Espaço dentro do comentário não muda ao ser apagado.
|
|
58
39
|
if (first >= 0) out.set(index + 1, row.slice(first, last + 1).trim());
|
|
59
40
|
start += row.length + 1;
|
|
60
41
|
});
|
|
@@ -1,55 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* string, preservando comprimento e quebras de linha.
|
|
4
|
-
*
|
|
5
|
-
* Existe uma vez só, e serve a consumidores que fariam a MESMA coisa de
|
|
6
|
-
* jeitos ligeiramente diferentes se cada um escrevesse o seu:
|
|
7
|
-
*
|
|
8
|
-
* - `code-audit.js` — não pode acusar `AgentThread` que só aparece num
|
|
9
|
-
* comentário ou numa mensagem de log;
|
|
10
|
-
* - `registry-audit.js` — precisa ler **JSONC**, a forma que o exemplar
|
|
11
|
-
* canônico `model-registry.v2.json` usa e que o leitor C# do kit aceita de
|
|
12
|
-
* propósito (`JsonCommentHandling.Skip`, `AllowTrailingCommas`);
|
|
13
|
-
* - `mutation/dotnet-introspection.js` — lê o `global.json` por `jsoncToJson`;
|
|
14
|
-
* - `comment-scanner.js` (ao lado; lido pelo `code-budget` e pelo hook
|
|
15
|
-
* `comment-guard`) — acha as linhas de
|
|
16
|
-
* comentário de `.cs`/`.ts`/`.js`, e é o único que liga a opção `js`.
|
|
17
|
-
*
|
|
18
|
-
* **Por que não um regex.** O exemplar tem `"http://localhost:11434/v1"` e uma
|
|
19
|
-
* `provenance` com URL. Um `replace(/\/\/.*$/gm, '')` decapitaria as duas —
|
|
20
|
-
* apagando dado e chamando isso de comentário. Um varredor que atravessa as
|
|
21
|
-
* strings primeiro é a única forma correta, e é o que está aqui.
|
|
22
|
-
*
|
|
23
|
-
* O comprimento é preservado (os removidos viram espaço, `\n` e `\r` ficam) para
|
|
24
|
-
* que índices e números de linha do texto original continuem válidos no texto
|
|
25
|
-
* limpo. É o que permite `code-audit.js` reportar `arquivo.cs:34` sem um segundo
|
|
26
|
-
* mapeamento.
|
|
27
|
-
*/
|
|
1
|
+
// Atravessa as strings antes: um regex apagaria o `//` de `"http://localhost"`.
|
|
2
|
+
// O comprimento é preservado para índices e números de linha continuarem válidos.
|
|
28
3
|
|
|
29
4
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* Apaga também o CONTEÚDO das strings. **As aspas delimitadoras ficam.**
|
|
34
|
-
*
|
|
35
|
-
* Apagá-las junto era um defeito com consequência: um elemento-string virava
|
|
36
|
-
* espaço puro, e o detector de vírgula sobrando de `jsoncToJson` passava a
|
|
37
|
-
* enxergar `[ , , ]` — separadores REAIS entre delimitadores que sumiram —
|
|
38
|
-
* e apagava uma vírgula que era sintaxe. Medido: `{"tags":["a","b"]}` saía
|
|
39
|
-
* `{"tags":["a","b" ]}`, JSON inválido, e o registry voltava a ser
|
|
40
|
-
* `○ não verificado`. O B1 reintroduzido pela correção do B1.
|
|
41
|
-
* @param {{ unterminated?: string|null }} [options.report]
|
|
42
|
-
* Se um objeto for passado, recebe `unterminated` = `'block-comment'`,
|
|
43
|
-
* `'string'`, `'verbatim-string'`, `'raw-string'`, `'template'` ou `null`. É o canal de
|
|
44
|
-
* diagnóstico que `jsoncToJson` usa para ficar tão estrito quanto o leitor
|
|
45
|
-
* de produção, sem que este lexer precise lançar (ele também varre `.cs`,
|
|
46
|
-
* onde um comentário aberto é problema do arquivo, não da varredura).
|
|
47
|
-
* @param {boolean} [options.js=false]
|
|
48
|
-
* Lê também template literal e regex literal de TS/JS. Opt-in porque só
|
|
49
|
-
* quem varre `.ts`/`.js` precisa: sem template, `https://${h}` vira
|
|
50
|
-
* comentário; sem regex, uma crase dentro de `/[`']/` abre um template que
|
|
51
|
-
* engole o resto do arquivo.
|
|
52
|
-
* @returns {string} texto do MESMO comprimento
|
|
5
|
+
* `blankStrings` apaga o conteúdo das strings, mas as ASPAS FICAM: sem elas um
|
|
6
|
+
* separador real vira vírgula sobrando. `report.unterminated` recebe o literal não
|
|
7
|
+
* fechado; `js` lê template e regex literal, sem os quais `https://${h}` vira comentário.
|
|
53
8
|
*/
|
|
54
9
|
export function stripComments(text, { blankStrings = false, report = null, js = false } = {}) {
|
|
55
10
|
const out = text.split('');
|
|
@@ -95,7 +50,7 @@ export function stripComments(text, { blankStrings = false, report = null, js =
|
|
|
95
50
|
if (end !== -1) { i = end; continue; }
|
|
96
51
|
}
|
|
97
52
|
|
|
98
|
-
//
|
|
53
|
+
// C# raw string literal
|
|
99
54
|
if (c === '"' && next === '"' && text[i + 2] === '"') {
|
|
100
55
|
const end = text.indexOf('"""', i + 3);
|
|
101
56
|
if (end === -1) flag('raw-string');
|
|
@@ -105,7 +60,7 @@ export function stripComments(text, { blankStrings = false, report = null, js =
|
|
|
105
60
|
continue;
|
|
106
61
|
}
|
|
107
62
|
|
|
108
|
-
//
|
|
63
|
+
// C# verbatim string: doubled quotes escape
|
|
109
64
|
if (c === '@' && next === '"') {
|
|
110
65
|
let j = i + 2;
|
|
111
66
|
let closed = false;
|
|
@@ -159,7 +114,6 @@ export function stripComments(text, { blankStrings = false, report = null, js =
|
|
|
159
114
|
return out.join('');
|
|
160
115
|
}
|
|
161
116
|
|
|
162
|
-
/** Índice logo após a crase que fecha o template aberto em `start`, ou -1. */
|
|
163
117
|
function templateEnd(text, start) {
|
|
164
118
|
let j = start + 1;
|
|
165
119
|
while (j < text.length) {
|
|
@@ -176,11 +130,7 @@ const REGEX_AFTER_WORD = new Set([
|
|
|
176
130
|
'throw', 'instanceof', 'yield', 'await',
|
|
177
131
|
]);
|
|
178
132
|
|
|
179
|
-
|
|
180
|
-
* Uma `/` abre regex, e não divisão, quando o que vem antes não é um valor: o
|
|
181
|
-
* mesmo critério de contexto dos tokenizers de JS. `out` já tem os comentários
|
|
182
|
-
* apagados, então o caractere anterior é código. Sem `<`: em TSX, `</b>` é tag.
|
|
183
|
-
*/
|
|
133
|
+
// `/` abre regex quando o que vem antes não é um valor. Sem `<`: em TSX, `</b>` é tag.
|
|
184
134
|
function regexMayStart(out, i) {
|
|
185
135
|
let k = i - 1;
|
|
186
136
|
while (k >= 0 && /\s/.test(out[k])) k--;
|
|
@@ -192,7 +142,6 @@ function regexMayStart(out, i) {
|
|
|
192
142
|
return REGEX_AFTER_WORD.has(out.slice(w, k + 1).join(''));
|
|
193
143
|
}
|
|
194
144
|
|
|
195
|
-
/** Índice logo após a `/` (e as flags) que fecha a regex aberta em `start`, ou -1 se a linha acaba antes. */
|
|
196
145
|
function regexEnd(text, start) {
|
|
197
146
|
let inClass = false;
|
|
198
147
|
for (let j = start + 1; j < text.length; j++) {
|
|
@@ -210,7 +159,6 @@ function regexEnd(text, start) {
|
|
|
210
159
|
return -1;
|
|
211
160
|
}
|
|
212
161
|
|
|
213
|
-
/** Índice logo após o `}` que fecha um `${`: o código do buraco aninha chaves, strings e templates. */
|
|
214
162
|
function holeEnd(text, start) {
|
|
215
163
|
let depth = 1;
|
|
216
164
|
let j = start;
|
|
@@ -235,31 +183,9 @@ function holeEnd(text, start) {
|
|
|
235
183
|
return text.length;
|
|
236
184
|
}
|
|
237
185
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
* As duas tolerâncias são **exatamente** as que `ModelRegistry.FromJson` do
|
|
242
|
-
* ai-kit declara (`CommentHandling = Skip`, `AllowTrailingCommas = true`). A
|
|
243
|
-
* ferramenta que audita o registry tem de enxergar a mesma forma que o leitor
|
|
244
|
-
* de produção enxerga — senão o framework passa a recomendar uma forma que a
|
|
245
|
-
* sua própria auditoria não lê, e as checagens ficam inertes em silêncio.
|
|
246
|
-
*
|
|
247
|
-
* A vírgula sobrando é localizada num texto onde o conteúdo das strings também
|
|
248
|
-
* foi apagado — **com as aspas preservadas**, senão um elemento-string vira
|
|
249
|
-
* espaço puro e um separador REAL passa por vírgula sobrando (o defeito B4) —
|
|
250
|
-
* e só então removida do texto real: uma vírgula dentro de `"a,}"` é dado.
|
|
251
|
-
*
|
|
252
|
-
* **Um comentário de bloco não fechado LANÇA**, e a razão é a mesma que fez
|
|
253
|
-
* esta função existir: paridade com o leitor de produção. `JsonCommentHandling.Skip`
|
|
254
|
-
* do .NET lança nesse caso, e ser mais frouxo que o kit é o defeito INVERSO do
|
|
255
|
-
* B1 — auditar, e aprovar, um documento que a aplicação recusa carregar.
|
|
256
|
-
* `readJsonSafe` captura e devolve `null`, que o relatório traduz em
|
|
257
|
-
* "não verificado" com razão.
|
|
258
|
-
*
|
|
259
|
-
* @param {string} raw
|
|
260
|
-
* @returns {string} JSON estrito, do mesmo comprimento do original
|
|
261
|
-
* @throws {SyntaxError} comentário de bloco aberto e nunca fechado
|
|
262
|
-
*/
|
|
186
|
+
// Paridade com `ModelRegistry.FromJson` do ai-kit, nos dois sentidos: comentário de
|
|
187
|
+
// bloco não fechado LANÇA, como `JsonCommentHandling.Skip`, porque ser mais frouxo
|
|
188
|
+
// que o kit é aprovar um documento que a aplicação recusa carregar.
|
|
263
189
|
export function jsoncToJson(raw) {
|
|
264
190
|
const report = {};
|
|
265
191
|
const noComments = stripComments(raw, { report }).split('');
|
|
Binary file
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
export const ERRATA_HEADING = '## Errata';
|
|
2
2
|
|
|
3
|
-
/**
|
|
3
|
+
/** The one normalization every reader of a frozen markdown applies. */
|
|
4
4
|
export function normalizeMarkdown(text) {
|
|
5
5
|
return String(text ?? '').replace(/^/, '').replace(/\r\n?/g, '\n');
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* Lines with fenced blocks marked, so no heading is read inside ``` or ~~~.
|
|
10
|
-
*
|
|
11
|
-
* @returns {{ raw: string, n: number, fenced: boolean }[]}
|
|
12
|
-
*/
|
|
13
8
|
export function markdownLines(text) {
|
|
14
9
|
const out = [];
|
|
15
10
|
let fence = null;
|
|
@@ -30,7 +25,7 @@ export function markdownLines(text) {
|
|
|
30
25
|
const isErrataHeading = (line) => !line.fenced
|
|
31
26
|
&& /^##\s+errata\s*$/i.test(line.raw);
|
|
32
27
|
|
|
33
|
-
/** Offset
|
|
28
|
+
/** Offset in the NORMALIZED text, or -1. */
|
|
34
29
|
export function errataOffset(text) {
|
|
35
30
|
let offset = 0;
|
|
36
31
|
for (const line of markdownLines(text)) {
|
|
@@ -40,20 +35,12 @@ export function errataOffset(text) {
|
|
|
40
35
|
return -1;
|
|
41
36
|
}
|
|
42
37
|
|
|
43
|
-
/** Everything before the errata block, normalized, without trailing blank. */
|
|
44
38
|
export function frozenBody(text) {
|
|
45
39
|
const norm = normalizeMarkdown(text);
|
|
46
40
|
const at = errataOffset(norm);
|
|
47
41
|
return (at === -1 ? norm : norm.slice(0, at)).trimEnd();
|
|
48
42
|
}
|
|
49
43
|
|
|
50
|
-
/**
|
|
51
|
-
* Is `proposed` the approved text plus an append-only errata? The body stays
|
|
52
|
-
* byte-identical, the result carries an errata block, and what the errata
|
|
53
|
-
* already said is never rewritten.
|
|
54
|
-
*
|
|
55
|
-
* @returns {{ok: true} | {ok: false, why: string}}
|
|
56
|
-
*/
|
|
57
44
|
export function errataViolation(original, proposed) {
|
|
58
45
|
if (frozenBody(proposed) !== frozenBody(original)) {
|
|
59
46
|
return { ok: false, why: 'o corpo aprovado mudou — errata só ACRESCENTA ao fim, nunca altera o texto aprovado' };
|
|
@@ -1,41 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* `resolveMorphRoot` (payload-utils) answers this from a file path. Some hook
|
|
5
|
-
* events carry no path at all — an `AskUserQuestion` gate approval is the one
|
|
6
|
-
* that matters — and those were resolved by "the first in_progress feature of
|
|
7
|
-
* whatever tree the session happens to sit in". That is how a Gate 2 approval
|
|
8
|
-
* for one feature landed on `export-hub`, an unrelated feature that merely
|
|
9
|
-
* happened to be active in the primary root.
|
|
10
|
-
*
|
|
11
|
-
* The honest signal for a pathless event is the question text itself: every
|
|
12
|
-
* gate template in the core skills names its feature — "Gate 1 — Proposta de
|
|
13
|
-
* {feature} aprovada?", "Gate 2 — Plano de {feature} aprovado?", "Gate 3 —
|
|
14
|
-
* Review de {feature} aprovado?", "Gate 3 — Hotfix de {feature} pronto para
|
|
15
|
-
* ship?". Rather than parse those sentences (which would break the moment a
|
|
16
|
-
* template is reworded), match the question against the set of feature slugs
|
|
17
|
-
* that actually exist on disk.
|
|
18
|
-
*
|
|
19
|
-
* When the text names no known feature, the caller must REFUSE TO GUESS.
|
|
20
|
-
* Stamping a gate onto the wrong feature is close to unrecoverable — it inverts
|
|
21
|
-
* gate order and invites a second stamp on the real owner; writing nothing
|
|
22
|
-
* costs one `morph-spec approve`.
|
|
23
|
-
*
|
|
24
|
-
* Fail-open by contract: every function swallows errors and returns an empty
|
|
25
|
-
* result rather than throwing.
|
|
26
|
-
*/
|
|
1
|
+
// A pathless gate question is matched against the slugs on disk, never parsed.
|
|
2
|
+
// When it names no known feature the caller must REFUSE TO GUESS: a gate on the
|
|
3
|
+
// wrong feature is close to unrecoverable, writing nothing costs one `approve`.
|
|
27
4
|
|
|
28
5
|
import { execSync } from 'child_process';
|
|
29
6
|
import { loadState } from './state-reader.js';
|
|
30
7
|
|
|
31
|
-
|
|
32
|
-
* Feature slugs known to `root`.
|
|
33
|
-
*
|
|
34
|
-
* Deliberately `loadState` and not a `readdirSync` of `.morph/features/`: state
|
|
35
|
-
* lives in BOTH shapes — the thin `.morph/state.json` index and the per-feature
|
|
36
|
-
* `feature.json` — and `loadState` is the one place that already merges them.
|
|
37
|
-
* Listing directories alone misses every feature carried only by the index.
|
|
38
|
-
*/
|
|
8
|
+
// `loadState`, not a directory listing: it also sees features only the index carries.
|
|
39
9
|
export function featureNamesIn(root) {
|
|
40
10
|
try {
|
|
41
11
|
const state = loadState(root);
|
|
@@ -45,11 +15,6 @@ export function featureNamesIn(root) {
|
|
|
45
15
|
}
|
|
46
16
|
}
|
|
47
17
|
|
|
48
|
-
/**
|
|
49
|
-
* Every working tree of this repo — the primary root plus every registered
|
|
50
|
-
* secondary worktree. Fail-open to `[sessionRoot]` when git is unavailable or
|
|
51
|
-
* the directory is not a repo.
|
|
52
|
-
*/
|
|
53
18
|
export function allWorkTrees(sessionRoot) {
|
|
54
19
|
const roots = [sessionRoot];
|
|
55
20
|
try {
|
|
@@ -64,26 +29,14 @@ export function allWorkTrees(sessionRoot) {
|
|
|
64
29
|
if (path && !roots.includes(path)) roots.push(path);
|
|
65
30
|
}
|
|
66
31
|
} catch {
|
|
67
|
-
/* fail-open — the session root alone */
|
|
68
32
|
}
|
|
69
33
|
return roots;
|
|
70
34
|
}
|
|
71
35
|
|
|
72
36
|
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
73
37
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
*
|
|
77
|
-
* Slug boundaries are `[^a-z0-9-]` so a longer name never matches inside a
|
|
78
|
-
* shorter one (`export` must not match the text "export-hub"). Zero matches
|
|
79
|
-
* means the text named nothing; two or more means it named two features and is
|
|
80
|
-
* ambiguous. Both are null — "I don't know" and "it's ambiguous" must lead to
|
|
81
|
-
* the same refusal, not to a coin flip.
|
|
82
|
-
*
|
|
83
|
-
* @param {string} text
|
|
84
|
-
* @param {string[]} names - candidate slugs
|
|
85
|
-
* @returns {string|null}
|
|
86
|
-
*/
|
|
38
|
+
// `export` must not match inside "export-hub". Zero and ambiguous matches are
|
|
39
|
+
// both null: they must lead to the same refusal.
|
|
87
40
|
export function featureNamedIn(text, names) {
|
|
88
41
|
if (typeof text !== 'string' || !Array.isArray(names) || names.length === 0) return null;
|
|
89
42
|
const haystack = text.toLowerCase();
|
|
@@ -93,28 +46,8 @@ export function featureNamedIn(text, names) {
|
|
|
93
46
|
return hits.length === 1 ? hits[0] : null;
|
|
94
47
|
}
|
|
95
48
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
* hyphen — `morph-spec create`'s naming convention) that names none of
|
|
99
|
-
* `names`. Signals "the text is talking about a specific feature I don't
|
|
100
|
-
* know" as opposed to "the text names nothing in particular".
|
|
101
|
-
*
|
|
102
|
-
* This is what the single-feature fallback below was missing: a gate question
|
|
103
|
-
* for a feature not yet registered (no `create`, no `plan.md` written
|
|
104
|
-
* yet — a fresh hotfix, say) names NO known feature either, and used to fall
|
|
105
|
-
* through to "the tree's one feature must be it". That is exactly how a Gate 3
|
|
106
|
-
* for `backfill-bool-filter` landed on `export-hub`, the tree's only
|
|
107
|
-
* registered feature at the time.
|
|
108
|
-
*
|
|
109
|
-
* Deliberately narrow (requires a hyphen) to match real feature slugs
|
|
110
|
-
* (`export-hub`, `backfill-bool-filter`) without flagging ordinary prose —
|
|
111
|
-
* the fixed portions of every gate template ("Proposta", "aprovado", "Sinal",
|
|
112
|
-
* "pronto para ship"...) are single words with no internal hyphen.
|
|
113
|
-
*
|
|
114
|
-
* @param {string} text
|
|
115
|
-
* @param {string[]} names - every feature slug known across all trees
|
|
116
|
-
* @returns {string|null}
|
|
117
|
-
*/
|
|
49
|
+
// A slug-shaped token naming no known feature: the question is about a feature
|
|
50
|
+
// not yet registered. Requires a hyphen, so template prose never matches.
|
|
118
51
|
export function unrecognizedSlugLike(text, names) {
|
|
119
52
|
if (typeof text !== 'string') return null;
|
|
120
53
|
const known = new Set((names || []).map((n) => String(n).toLowerCase()));
|
|
@@ -122,17 +55,8 @@ export function unrecognizedSlugLike(text, names) {
|
|
|
122
55
|
return tokens.find((t) => !known.has(t)) ?? null;
|
|
123
56
|
}
|
|
124
57
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
*
|
|
128
|
-
* The session root is consulted first so the common case costs no git call, and
|
|
129
|
-
* so a feature present in BOTH trees resolves to the tree the session is
|
|
130
|
-
* actually in — the same tree `morph-spec approve` would write from that cwd.
|
|
131
|
-
* Only when the session root knows no such feature do we pay for enumerating
|
|
132
|
-
* the worktrees.
|
|
133
|
-
*
|
|
134
|
-
* @returns {{name: string, root: string}|null} null ⇒ caller must not guess
|
|
135
|
-
*/
|
|
58
|
+
// The session root wins a feature present in both trees: it is where
|
|
59
|
+
// `morph-spec approve` would write from that cwd.
|
|
136
60
|
export function locateFeatureForQuestion(text, sessionRoot) {
|
|
137
61
|
const trees = allWorkTrees(sessionRoot);
|
|
138
62
|
const localNames = featureNamesIn(sessionRoot);
|
|
@@ -146,18 +70,10 @@ export function locateFeatureForQuestion(text, sessionRoot) {
|
|
|
146
70
|
if (found) return { name: found, root };
|
|
147
71
|
}
|
|
148
72
|
|
|
149
|
-
// The text names a specific feature none of the trees know — refuse rather
|
|
150
|
-
// than guess it meant the tree's one registered feature (see
|
|
151
|
-
// `unrecognizedSlugLike` above).
|
|
152
73
|
const allNames = trees.flatMap(featureNamesIn);
|
|
153
74
|
if (unrecognizedSlugLike(text, allNames)) return null;
|
|
154
75
|
|
|
155
|
-
//
|
|
156
|
-
// there is no other feature the answer could have been about. This is the
|
|
157
|
-
// single-feature project — the common case — and refusing there would break
|
|
158
|
-
// gate questions phrased without a name ("Gate 1 — Proposta aprovada?").
|
|
159
|
-
// With two or more features the fallback is exactly the guess that put a
|
|
160
|
-
// Gate 2 on `export-hub`, so it stops here.
|
|
76
|
+
// With exactly ONE feature in the tree there is nothing else the answer could be about.
|
|
161
77
|
if (localNames.length === 1) return { name: localNames[0], root: sessionRoot };
|
|
162
78
|
|
|
163
79
|
return null;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
// because a hook cannot import `src/`.
|
|
1
|
+
// `src/lib/detectors` re-exports this list, because a hook cannot import `src/`.
|
|
3
2
|
export const PACKAGE_LOCATIONS = ['.', 'src/frontend', 'src/web', 'frontend', 'apps/web', 'packages/app'];
|
|
4
3
|
|
|
5
|
-
// Where `verify`/`e2e` look for a Playwright setup when `project.frontendPath` is absent.
|
|
6
4
|
export const E2E_SPEC_LOCATIONS = [...PACKAGE_LOCATIONS, 'web'];
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The gate a gate question asks about, or null. Explicit gate names win over
|
|
3
|
-
* words that may also appear in a feature slug ("plan", "design", "review").
|
|
4
|
-
*
|
|
5
|
-
* @param {string} question
|
|
6
|
-
* @returns {'plan'|'uiux'|'review'|null}
|
|
7
|
-
*/
|
|
1
|
+
/** Explicit gate names win over words a feature slug may also carry ("plan", "design", "review"). */
|
|
8
2
|
export function gateFromQuestion(question) {
|
|
9
3
|
if (typeof question !== 'string') return null;
|
|
10
4
|
if (/gate\s+uiux/i.test(question)) return 'uiux';
|
|
@@ -1,31 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* `info/exclude` do git.
|
|
4
|
-
*
|
|
5
|
-
* O arquivo lido é o do COMMON dir (`git rev-parse --git-common-dir`), nunca o
|
|
6
|
-
* `.git/worktrees/<id>/info/exclude`: o git não lê o deste para uma worktree
|
|
7
|
-
* vinculada, e o do common dir vale para toda árvore do repositório — inclusive
|
|
8
|
-
* a raiz primária. Cada chamador passa o seu marcador, e o bloco só ganha as
|
|
9
|
-
* linhas que ainda faltam.
|
|
10
|
-
*
|
|
11
|
-
* Idempotente e fail-open: um exclude que não pôde ser escrito custa um
|
|
12
|
-
* arquivo aparecendo como `??`, nunca um erro.
|
|
13
|
-
*/
|
|
1
|
+
// O exclude do COMMON dir: o git não lê `.git/worktrees/<id>/info/exclude` para
|
|
2
|
+
// uma worktree vinculada. Fail-open: falhar custa um `??`, nunca um erro.
|
|
14
3
|
|
|
15
4
|
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'fs';
|
|
16
5
|
import { join } from 'path';
|
|
17
6
|
import { resolveGitCommonDir } from './git-paths.js';
|
|
18
7
|
|
|
19
|
-
/**
|
|
20
|
-
* @param {string} cwd - qualquer diretório dentro do repositório
|
|
21
|
-
* @param {string[]} patterns - linhas prontas (já ancoradas, ex. `/graphify-out`)
|
|
22
|
-
* @param {string} marker - comentário que identifica o bloco do chamador
|
|
23
|
-
*/
|
|
24
8
|
export function ensureCommonExclude(cwd, patterns, marker) {
|
|
25
9
|
if (!patterns.length) return;
|
|
26
10
|
try {
|
|
27
|
-
// Pelo disco
|
|
28
|
-
// console no win32 quando o chamador era um processo destacado.
|
|
11
|
+
// Pelo disco: um `git` gerado por um processo destacado abre console no win32.
|
|
29
12
|
const common = resolveGitCommonDir(cwd);
|
|
30
13
|
if (!common) return;
|
|
31
14
|
const infoDir = join(common, 'info');
|