@printpractical/cruze 0.0.13 → 0.0.15
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 +61 -118
- package/dist/adapters/inbound/cli/cli_context.js +0 -27
- package/dist/adapters/inbound/cli/commands/code.js +5 -18
- package/dist/adapters/inbound/cli/commands/documents.js +11 -37
- package/dist/adapters/inbound/cli/commands/init.js +1 -1
- package/dist/adapters/inbound/cli/commands/install.js +1 -0
- package/dist/adapters/inbound/cli/commands/review.js +2 -9
- package/dist/adapters/inbound/cli/commands/status.js +16 -63
- package/dist/adapters/inbound/cli/commands.js +10 -37
- package/dist/adapters/inbound/cli/parse_command.js +0 -54
- package/dist/adapters/outbound/git_repository.js +2 -71
- package/dist/app/ports/repository.js +0 -12
- package/dist/app/project_context.js +15 -25
- package/dist/app/use_cases/approve_document.js +29 -0
- package/dist/app/use_cases/check_code.js +21 -56
- package/dist/app/use_cases/install_skills.js +7 -1
- package/dist/app/use_cases/new_document.js +25 -0
- package/dist/app/use_cases/project_status.js +43 -67
- package/dist/app/use_cases/run_review.js +11 -30
- package/dist/app/use_cases/validate_project.js +8 -5
- package/dist/domain/approvals.js +51 -0
- package/dist/domain/check/check_source.js +3 -32
- package/dist/domain/check/imports.js +3 -3
- package/dist/domain/check/rust_imports.js +25 -3
- package/dist/domain/config.js +8 -84
- package/dist/domain/hashing.js +16 -30
- package/dist/domain/ids.js +13 -27
- package/dist/domain/next_step.js +41 -0
- package/dist/domain/project/features.js +71 -0
- package/dist/domain/project/layout.js +11 -27
- package/dist/domain/project/project_docs.js +48 -0
- package/dist/domain/project/release.js +30 -0
- package/dist/domain/scaffold.js +0 -1
- package/dist/domain/skill_budget.js +4 -4
- package/dist/domain/trace.js +8 -25
- package/dist/domain/validation/document_rules.js +65 -0
- package/dist/domain/validation/release_rules.js +45 -0
- package/dist/domain/validation/review_rules.js +21 -0
- package/dist/domain/validation/validate_project.js +20 -27
- package/dist/domain/versions/cruze_version.js +3 -9
- package/dist/main.js +1 -1
- package/package.json +1 -1
- package/skills/asking/SKILL.md +51 -0
- package/skills/behavioural-testing/SKILL.md +14 -16
- package/skills/behavioural-testing/examples.md +1 -2
- package/skills/behavioural-testing/fakes.md +2 -2
- package/skills/build/SKILL.md +34 -38
- package/skills/dependency-approval/SKILL.md +34 -31
- package/skills/envision/SKILL.md +23 -18
- package/skills/envision/architecture.md +16 -0
- package/skills/envision/breakdown.md +14 -0
- package/skills/envision/possibilities.md +6 -7
- package/skills/fix/SKILL.md +34 -0
- package/skills/formats/SKILL.md +39 -87
- package/skills/formats/config.md +47 -0
- package/skills/formats/templates/architecture.md +18 -74
- package/skills/formats/templates/feature.md +18 -34
- package/skills/formats/templates/vision.md +15 -27
- package/skills/hexagonal-design/SKILL.md +11 -11
- package/skills/hexagonal-design/contracts.md +3 -3
- package/skills/hexagonal-design/fundamentals.md +1 -1
- package/skills/hexagonal-design/languages/cpp.md +1 -1
- package/skills/hexagonal-design/languages/rust.md +1 -1
- package/skills/hexagonal-design/languages/swift.md +2 -2
- package/skills/hexagonal-design/runtime.md +2 -2
- package/skills/research/SKILL.md +4 -4
- package/skills/research/evaluation.md +1 -1
- package/skills/review/SKILL.md +27 -0
- package/skills/roles/SKILL.md +8 -59
- package/skills/roles/code-reviewer.md +22 -32
- package/skills/roles/researcher.md +3 -3
- package/skills/shape/SKILL.md +37 -0
- package/skills/shape/self-check.md +12 -0
- package/src/adapters/inbound/cli/cli_context.ts +0 -27
- package/src/adapters/inbound/cli/commands/code.ts +5 -18
- package/src/adapters/inbound/cli/commands/documents.ts +11 -37
- package/src/adapters/inbound/cli/commands/init.ts +1 -1
- package/src/adapters/inbound/cli/commands/install.ts +1 -0
- package/src/adapters/inbound/cli/commands/review.ts +2 -9
- package/src/adapters/inbound/cli/commands/status.ts +16 -63
- package/src/adapters/inbound/cli/commands.ts +10 -37
- package/src/adapters/inbound/cli/parse_command.ts +0 -54
- package/src/adapters/outbound/git_repository.ts +2 -71
- package/src/app/ports/repository.ts +0 -12
- package/src/app/project_context.ts +15 -25
- package/src/app/use_cases/approve_document.ts +29 -0
- package/src/app/use_cases/check_code.ts +21 -56
- package/src/app/use_cases/install_skills.ts +7 -1
- package/src/app/use_cases/new_document.ts +25 -0
- package/src/app/use_cases/project_status.ts +49 -73
- package/src/app/use_cases/run_review.ts +11 -30
- package/src/app/use_cases/validate_project.ts +8 -5
- package/src/domain/approvals.ts +51 -0
- package/src/domain/check/check_source.ts +3 -32
- package/src/domain/check/imports.ts +3 -3
- package/src/domain/check/rust_imports.ts +25 -3
- package/src/domain/config.ts +9 -85
- package/src/domain/hashing.ts +16 -30
- package/src/domain/ids.ts +13 -27
- package/src/domain/next_step.ts +41 -0
- package/src/domain/project/features.ts +71 -0
- package/src/domain/project/layout.ts +11 -27
- package/src/domain/project/project_docs.ts +48 -0
- package/src/domain/project/release.ts +30 -0
- package/src/domain/scaffold.ts +0 -1
- package/src/domain/skill_budget.ts +4 -4
- package/src/domain/trace.ts +8 -25
- package/src/domain/validation/document_rules.ts +65 -0
- package/src/domain/validation/release_rules.ts +45 -0
- package/src/domain/validation/review_rules.ts +21 -0
- package/src/domain/validation/validate_project.ts +21 -28
- package/src/domain/versions/cruze_version.ts +3 -9
- package/src/main.ts +1 -1
- package/templates/init/AGENTS.md +4 -12
- package/templates/init/ci.yml +3 -3
- package/templates/init/config.yaml +8 -17
- package/templates/init/review/claude.yaml +2 -2
- package/dist/adapters/inbound/cli/argument_files.js +0 -29
- package/dist/adapters/inbound/cli/commands/diff.js +0 -23
- package/dist/adapters/inbound/cli/commands/journal.js +0 -32
- package/dist/adapters/inbound/cli/commands/progress.js +0 -55
- package/dist/adapters/inbound/cli/commands/realign.js +0 -28
- package/dist/adapters/inbound/cli/commands/show.js +0 -96
- package/dist/app/use_cases/abandon_work.js +0 -42
- package/dist/app/use_cases/approve_artifact.js +0 -197
- package/dist/app/use_cases/journal_events.js +0 -64
- package/dist/app/use_cases/land_change.js +0 -134
- package/dist/app/use_cases/new_project_doc.js +0 -48
- package/dist/app/use_cases/new_work_item.js +0 -75
- package/dist/app/use_cases/realign_done.js +0 -45
- package/dist/app/use_cases/realign_status.js +0 -106
- package/dist/app/use_cases/record_progress.js +0 -118
- package/dist/app/use_cases/show_elements.js +0 -18
- package/dist/app/use_cases/show_notes.js +0 -11
- package/dist/app/use_cases/show_scope.js +0 -51
- package/dist/app/use_cases/upstream_diff.js +0 -55
- package/dist/domain/approvals/agent_approval.js +0 -68
- package/dist/domain/approvals/evaluate.js +0 -36
- package/dist/domain/approvals/fingerprint.js +0 -52
- package/dist/domain/approvals/rebase.js +0 -39
- package/dist/domain/approvals/records.js +0 -50
- package/dist/domain/approvals/upstream_text.js +0 -36
- package/dist/domain/architecture_sections.js +0 -23
- package/dist/domain/check/decision_comments.js +0 -38
- package/dist/domain/decision_citations.js +0 -62
- package/dist/domain/edits/agents_file.js +0 -58
- package/dist/domain/edits/managed_tables.js +0 -60
- package/dist/domain/edits/progress_block.js +0 -33
- package/dist/domain/edits/status_lines.js +0 -52
- package/dist/domain/elements.js +0 -126
- package/dist/domain/journal.js +0 -109
- package/dist/domain/land/bookkeeping.js +0 -54
- package/dist/domain/land/merge_plan.js +0 -137
- package/dist/domain/land/text_ops.js +0 -66
- package/dist/domain/line_diff.js +0 -56
- package/dist/domain/project/artifact_ref.js +0 -42
- package/dist/domain/project/deltas.js +0 -49
- package/dist/domain/project/element_lookup.js +0 -124
- package/dist/domain/project/living_model.js +0 -55
- package/dist/domain/project/new_items.js +0 -48
- package/dist/domain/project/notes.js +0 -74
- package/dist/domain/project/plan_parts.js +0 -111
- package/dist/domain/project/progress.js +0 -43
- package/dist/domain/project/project_view.js +0 -72
- package/dist/domain/project/roadmap.js +0 -43
- package/dist/domain/project/scope_view.js +0 -134
- package/dist/domain/project/work_items.js +0 -68
- package/dist/domain/status/next_step.js +0 -181
- package/dist/domain/status/pending_rethink.js +0 -22
- package/dist/domain/status/verification.js +0 -29
- package/dist/domain/status/work_status.js +0 -125
- package/dist/domain/validation/decision_rules.js +0 -76
- package/dist/domain/validation/delta_rules.js +0 -81
- package/dist/domain/validation/element_rules.js +0 -104
- package/dist/domain/validation/future_rules.js +0 -40
- package/dist/domain/validation/living_rules.js +0 -99
- package/dist/domain/validation/plan_rules.js +0 -124
- package/dist/domain/validation/project_rules.js +0 -115
- package/dist/domain/validation/size_rules.js +0 -45
- package/dist/domain/validation/vision_rules.js +0 -41
- package/dist/domain/validation/work_item_rules.js +0 -175
- package/dist/domain/versions/realign_notes.js +0 -72
- package/dist/domain/versions/rule_versions.js +0 -29
- package/dist/domain/versions/scheduled_exceptions.js +0 -41
- package/skills/about/SKILL.md +0 -21
- package/skills/architect/SKILL.md +0 -49
- package/skills/architect/feature.md +0 -40
- package/skills/architect/project.md +0 -71
- package/skills/architect/tweak.md +0 -22
- package/skills/architect/walkthrough.md +0 -35
- package/skills/domain-language/SKILL.md +0 -37
- package/skills/envision/feature.md +0 -25
- package/skills/envision/intake.md +0 -28
- package/skills/envision/project.md +0 -24
- package/skills/explore/SKILL.md +0 -39
- package/skills/formats/reference/architecture.md +0 -84
- package/skills/formats/reference/config.md +0 -80
- package/skills/formats/reference/durable-docs.md +0 -74
- package/skills/formats/reference/specs.md +0 -65
- package/skills/formats/reference/work-items.md +0 -100
- package/skills/formats/templates/adr.md +0 -7
- package/skills/formats/templates/change.md +0 -29
- package/skills/formats/templates/glossary.md +0 -8
- package/skills/formats/templates/roadmap.md +0 -32
- package/skills/formats/templates/spec.md +0 -11
- package/skills/formats/templates/standalone-change.md +0 -46
- package/skills/grilling/SKILL.md +0 -90
- package/skills/grilling/illustrating.md +0 -38
- package/skills/improve/SKILL.md +0 -51
- package/skills/land/SKILL.md +0 -48
- package/skills/next/SKILL.md +0 -31
- package/skills/plan/SKILL.md +0 -62
- package/skills/plan/tasks.md +0 -35
- package/skills/plan/test-plan.md +0 -14
- package/skills/realign/SKILL.md +0 -37
- package/skills/realign/notes/0.0.10.md +0 -131
- package/skills/realign/notes/0.0.11.md +0 -15
- package/skills/realign/notes/0.0.12.md +0 -31
- package/skills/realign/notes/0.0.13.md +0 -11
- package/skills/realign/notes/0.0.2.md +0 -69
- package/skills/realign/notes/0.0.3.md +0 -11
- package/skills/realign/notes/0.0.4.md +0 -27
- package/skills/realign/notes/0.0.5.md +0 -38
- package/skills/realign/notes/0.0.6.md +0 -44
- package/skills/realign/notes/0.0.7.md +0 -11
- package/skills/realign/notes/0.0.8.md +0 -97
- package/skills/realign/notes/0.0.9.md +0 -59
- package/skills/realign/notes.md +0 -40
- package/skills/rethink/SKILL.md +0 -62
- package/skills/rethink/levels.md +0 -54
- package/skills/roadmap/SKILL.md +0 -54
- package/skills/roles/auditor.md +0 -35
- package/skills/roles/design-reviewer.md +0 -43
- package/skills/roles/design-rubric.md +0 -41
- package/skills/roles/plan-rubric.md +0 -11
- package/skills/roles/surveyor.md +0 -41
- package/skills/roles/verifier.md +0 -42
- package/skills/triage/SKILL.md +0 -37
- package/skills/verify/SKILL.md +0 -45
- package/skills/verify/manual-test.md +0 -23
- package/src/adapters/inbound/cli/argument_files.ts +0 -29
- package/src/adapters/inbound/cli/commands/diff.ts +0 -23
- package/src/adapters/inbound/cli/commands/journal.ts +0 -32
- package/src/adapters/inbound/cli/commands/progress.ts +0 -55
- package/src/adapters/inbound/cli/commands/realign.ts +0 -28
- package/src/adapters/inbound/cli/commands/show.ts +0 -96
- package/src/app/use_cases/abandon_work.ts +0 -42
- package/src/app/use_cases/approve_artifact.ts +0 -197
- package/src/app/use_cases/journal_events.ts +0 -64
- package/src/app/use_cases/land_change.ts +0 -134
- package/src/app/use_cases/new_project_doc.ts +0 -48
- package/src/app/use_cases/new_work_item.ts +0 -75
- package/src/app/use_cases/realign_done.ts +0 -45
- package/src/app/use_cases/realign_status.ts +0 -106
- package/src/app/use_cases/record_progress.ts +0 -118
- package/src/app/use_cases/show_elements.ts +0 -18
- package/src/app/use_cases/show_notes.ts +0 -11
- package/src/app/use_cases/show_scope.ts +0 -51
- package/src/app/use_cases/upstream_diff.ts +0 -55
- package/src/domain/approvals/agent_approval.ts +0 -68
- package/src/domain/approvals/evaluate.ts +0 -36
- package/src/domain/approvals/fingerprint.ts +0 -52
- package/src/domain/approvals/rebase.ts +0 -39
- package/src/domain/approvals/records.ts +0 -50
- package/src/domain/approvals/upstream_text.ts +0 -36
- package/src/domain/architecture_sections.ts +0 -23
- package/src/domain/check/decision_comments.ts +0 -38
- package/src/domain/decision_citations.ts +0 -62
- package/src/domain/edits/agents_file.ts +0 -58
- package/src/domain/edits/managed_tables.ts +0 -60
- package/src/domain/edits/progress_block.ts +0 -33
- package/src/domain/edits/status_lines.ts +0 -52
- package/src/domain/elements.ts +0 -126
- package/src/domain/journal.ts +0 -109
- package/src/domain/land/bookkeeping.ts +0 -54
- package/src/domain/land/merge_plan.ts +0 -137
- package/src/domain/land/text_ops.ts +0 -66
- package/src/domain/line_diff.ts +0 -56
- package/src/domain/project/artifact_ref.ts +0 -42
- package/src/domain/project/deltas.ts +0 -49
- package/src/domain/project/element_lookup.ts +0 -124
- package/src/domain/project/living_model.ts +0 -55
- package/src/domain/project/new_items.ts +0 -48
- package/src/domain/project/notes.ts +0 -74
- package/src/domain/project/plan_parts.ts +0 -111
- package/src/domain/project/progress.ts +0 -43
- package/src/domain/project/project_view.ts +0 -72
- package/src/domain/project/roadmap.ts +0 -43
- package/src/domain/project/scope_view.ts +0 -134
- package/src/domain/project/work_items.ts +0 -68
- package/src/domain/status/next_step.ts +0 -181
- package/src/domain/status/pending_rethink.ts +0 -22
- package/src/domain/status/verification.ts +0 -29
- package/src/domain/status/work_status.ts +0 -125
- package/src/domain/validation/decision_rules.ts +0 -76
- package/src/domain/validation/delta_rules.ts +0 -81
- package/src/domain/validation/element_rules.ts +0 -104
- package/src/domain/validation/future_rules.ts +0 -40
- package/src/domain/validation/living_rules.ts +0 -99
- package/src/domain/validation/plan_rules.ts +0 -124
- package/src/domain/validation/project_rules.ts +0 -115
- package/src/domain/validation/size_rules.ts +0 -45
- package/src/domain/validation/vision_rules.ts +0 -41
- package/src/domain/validation/work_item_rules.ts +0 -175
- package/src/domain/versions/realign_notes.ts +0 -72
- package/src/domain/versions/rule_versions.ts +0 -29
- package/src/domain/versions/scheduled_exceptions.ts +0 -41
- package/templates/init/gitattributes +0 -2
|
@@ -1,82 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { approvalState, } from "../../domain/approvals.js";
|
|
2
2
|
import { PATHS } from "../../domain/project/layout.js";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import { activeChange, buildGateReasons, projectStatus, touchedIds, } from "../../domain/status/work_status.js";
|
|
8
|
-
import { appendJournal, loadView, } from "../project_context.js";
|
|
3
|
+
|
|
4
|
+
import { nextStep, } from "../../domain/next_step.js";
|
|
5
|
+
|
|
6
|
+
import { loadDocs, } from "../project_context.js";
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return { branch, ...projectStatus(await loadView(deps.files), branch) };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** The step to run next on this branch, and other work that could proceed. */
|
|
20
|
-
export async function suggestNext(deps ) {
|
|
21
|
-
const branch = await deps.repository.currentBranch();
|
|
22
|
-
return { branch, ...nextSteps(await loadView(deps.files), branch) };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
|
|
14
|
+
|
|
26
15
|
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
27
20
|
|
|
28
21
|
|
|
29
|
-
|
|
30
22
|
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
*/
|
|
36
|
-
export async function checkBuildGate(deps , override ) {
|
|
37
|
-
const view = await loadView(deps.files);
|
|
38
|
-
const branch = await deps.repository.currentBranch();
|
|
39
|
-
const reasons = buildGateReasons(view, branch);
|
|
40
|
-
if (reasons.length === 0) return { gate: "build", passed: true, reasons, overridden: false };
|
|
41
|
-
if (override === undefined) return { gate: "build", passed: false, reasons, overridden: false };
|
|
42
|
-
if (override.trim() === "") throw new CruzeError("override-reason", "an override needs a reason");
|
|
43
|
-
const folder = activeChange(view, branch)?.folder ?? ".cruze";
|
|
44
|
-
await appendJournal(deps, folder, "override", { gate: "build", reason: override, blocked_by: reasons });
|
|
45
|
-
return { gate: "build", passed: true, reasons, overridden: true };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
49
27
|
|
|
50
|
-
|
|
51
|
-
|
|
52
28
|
|
|
53
29
|
|
|
54
|
-
/**
|
|
55
|
-
export async function
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
return { change: change.ref, overlaps };
|
|
30
|
+
/** Each document's approval, and each feature's progress, in release order. */
|
|
31
|
+
export async function showStatus(deps ) {
|
|
32
|
+
const docs = await loadDocs(deps.files);
|
|
33
|
+
const state = (path , doc ) => (doc === undefined ? "missing" : approvalState(docs.approvals, path, doc));
|
|
34
|
+
const documents = [
|
|
35
|
+
{ path: PATHS.vision, state: state(PATHS.vision, docs.vision) },
|
|
36
|
+
{ path: PATHS.architecture, state: state(PATHS.architecture, docs.architecture) },
|
|
37
|
+
];
|
|
38
|
+
const slugs = [...docs.release.map((r) => r.slug), ...docs.features.map((f) => f.slug).filter((s) => !docs.release.some((r) => r.slug === s))];
|
|
39
|
+
const features = slugs.map((slug) => {
|
|
40
|
+
const feature = docs.features.find((f) => f.slug === slug);
|
|
41
|
+
const row = docs.release.find((r) => r.slug === slug);
|
|
42
|
+
return {
|
|
43
|
+
slug,
|
|
44
|
+
...(feature === undefined ? {} : { path: feature.path }),
|
|
45
|
+
status: feature === undefined ? "not-shaped" : (feature.status ?? "unknown"),
|
|
46
|
+
approval: feature === undefined ? "missing" : approvalState(docs.approvals, feature.path, feature.doc),
|
|
47
|
+
inRelease: row !== undefined,
|
|
48
|
+
needs: row?.needs ?? [],
|
|
49
|
+
scenarios: feature?.scenarios.length ?? 0,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
return { documents, features, next: nextStep(docs) };
|
|
78
53
|
}
|
|
79
54
|
|
|
80
|
-
|
|
81
|
-
|
|
55
|
+
/** The step to run next, and why. */
|
|
56
|
+
export async function suggestNext(deps ) {
|
|
57
|
+
return nextStep(await loadDocs(deps.files));
|
|
82
58
|
}
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { CruzeError } from "../../domain/cruze_error.js";
|
|
2
|
-
import { resolveArtifact } from "../../domain/project/artifact_ref.js";
|
|
3
|
-
import { isPlanned } from "../../domain/project/plan_parts.js";
|
|
4
|
-
import { featureOf, } from "../../domain/project/work_items.js";
|
|
5
2
|
|
|
6
|
-
import {
|
|
3
|
+
import { loadDocs, } from "../project_context.js";
|
|
7
4
|
|
|
8
|
-
export const ROLES = ["
|
|
5
|
+
export const ROLES = ["code-reviewer", "researcher"] ;
|
|
9
6
|
|
|
10
7
|
|
|
11
8
|
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
10
|
|
|
17
|
-
|
|
11
|
+
|
|
18
12
|
|
|
19
13
|
|
|
20
14
|
|
|
@@ -30,37 +24,31 @@ export const ROLES = ["design-reviewer", "code-reviewer", "verifier", "researche
|
|
|
30
24
|
|
|
31
25
|
/**
|
|
32
26
|
* Runs a Cruze role in a fresh agent context: the role's prompt, what it reviews, and nothing
|
|
33
|
-
* from the conversation that produced the work.
|
|
34
|
-
* prompt to run in a helper when the configured agent isn't installed.
|
|
27
|
+
* from the conversation that produced the work.
|
|
35
28
|
*/
|
|
36
29
|
export async function runReview(deps , request ) {
|
|
37
30
|
if (!(ROLES ).includes(request.role)) throw new CruzeError("unknown-role", `the roles are ${ROLES.join(", ")}`);
|
|
38
31
|
const role = request.role ;
|
|
39
|
-
const
|
|
32
|
+
const docs = await loadDocs(deps.files);
|
|
40
33
|
const roleText = (await deps.files.readText(`.agents/skills/cruze-roles/${role}.md`)) ?? (await bundledRole(deps, role));
|
|
41
34
|
|
|
42
|
-
const context
|
|
35
|
+
const context = [];
|
|
43
36
|
if (request.item !== undefined) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (feature !== undefined) context.push(`Its feature: \`${feature.path}\``);
|
|
48
|
-
if (role === "design-reviewer" && item !== undefined) context.push(rubricLine(item));
|
|
49
|
-
} else {
|
|
50
|
-
context.push("Under review: the project documents the role lists for project scope");
|
|
37
|
+
const feature = docs.features.find((f) => f.slug === request.item);
|
|
38
|
+
if (feature === undefined) throw new CruzeError("not-found", `no feature ${request.item} in docs/features/`);
|
|
39
|
+
context.push(`Under review: the feature in \`${feature.path}\``);
|
|
51
40
|
}
|
|
52
41
|
if (request.base !== undefined) context.push(`The changes under review: run \`git diff ${request.base}\`.`);
|
|
53
|
-
if (request.blockers !== undefined) context.push(`Round-1 blockers to check:\n\n${request.blockers}`);
|
|
54
42
|
|
|
55
43
|
const prompt = [
|
|
56
44
|
roleText.trim(),
|
|
57
45
|
"## This run",
|
|
58
46
|
"You are running the role above in a fresh context, in the project's repository. Read every input it lists from the files, and run the commands it names. Edit no files. End with the report the role describes.",
|
|
59
|
-
context.map((line) => `- ${line}`).join("\n"),
|
|
47
|
+
...(context.length === 0 ? [] : [context.map((line) => `- ${line}`).join("\n")]),
|
|
60
48
|
].join("\n\n");
|
|
61
49
|
|
|
62
50
|
const subject = { role, ...(request.item === undefined ? {} : { item: request.item }) };
|
|
63
|
-
const command =
|
|
51
|
+
const command = docs.config?.config?.review.command;
|
|
64
52
|
if (command === undefined) return { ...subject, status: "run-in-helper", instruction: helperInstruction(undefined), prompt };
|
|
65
53
|
const run = await deps.runner.run(command, prompt);
|
|
66
54
|
if (run.kind === "missing") return { ...subject, command, status: "run-in-helper", instruction: helperInstruction(command[0]), prompt };
|
|
@@ -84,10 +72,3 @@ async function bundledRole(deps , role ) {
|
|
|
84
72
|
if (file === undefined) throw new CruzeError("role-missing", `the ${role} role is not installed; run cruze install`);
|
|
85
73
|
return file.text;
|
|
86
74
|
}
|
|
87
|
-
|
|
88
|
-
/** A design reviewer reads one rubric: the plan rubric for a planned change, the design rubric otherwise. */
|
|
89
|
-
function rubricLine(item ) {
|
|
90
|
-
return item.kind !== "feature" && isPlanned(item.doc)
|
|
91
|
-
? "This is a change's plan: review it against `.agents/skills/cruze-roles/plan-rubric.md`."
|
|
92
|
-
: "This is a design: review it against `.agents/skills/cruze-roles/design-rubric.md`.";
|
|
93
|
-
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { hasErrors, validateProject } from "../../domain/validation/validate_project.js";
|
|
2
2
|
import { warning, } from "../../domain/validation/problem.js";
|
|
3
3
|
import { PATHS } from "../../domain/project/layout.js";
|
|
4
|
+
import { reviewSettingTemplate } from "../../domain/scaffold.js";
|
|
5
|
+
import { reviewCommandProblems } from "../../domain/validation/review_rules.js";
|
|
4
6
|
import { versionSkew } from "../../domain/versions/cruze_version.js";
|
|
5
7
|
|
|
6
|
-
import { loadView } from "../project_context.js";
|
|
7
8
|
|
|
9
|
+
import { loadDocs } from "../project_context.js";
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
|
|
@@ -13,12 +15,13 @@ import { loadView } from "../project_context.js";
|
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
/** Checks every document in the project against the cruze-formats rules,
|
|
18
|
+
/** Checks every document in the project against the cruze-formats rules, that its skills match this CLI, and that its reviewer can run the tests. */
|
|
17
19
|
export async function validate(deps ) {
|
|
18
|
-
const
|
|
19
|
-
const config =
|
|
20
|
+
const docs = await loadDocs(deps.files);
|
|
21
|
+
const config = docs.config?.config;
|
|
20
22
|
const skew = config === null || config === undefined ? undefined : versionSkew(deps.bundle.version, config.cruze);
|
|
21
|
-
const
|
|
23
|
+
const review = reviewCommandProblems(docs, await deps.bundle.template(reviewSettingTemplate("claude")));
|
|
24
|
+
const problems = [...validateProject(docs), ...(skew === undefined ? [] : [warning(PATHS.config, undefined, "cruze-version", skew.message)]), ...review];
|
|
22
25
|
return {
|
|
23
26
|
valid: !hasErrors(problems),
|
|
24
27
|
errors: problems.filter((p) => p.severity === "error").length,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { designHash } from "./hashing.js";
|
|
2
|
+
|
|
3
|
+
import { slugOfFeaturePath } from "./project/layout.js";
|
|
4
|
+
|
|
5
|
+
/** One approval of one document: the hash of its design content when the user approved it. */
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/** Reads `.cruze/approvals.json`; anything unreadable counts as no approvals, and validate reports it. */
|
|
16
|
+
export function parseApprovals(text ) {
|
|
17
|
+
if (text === undefined) return [];
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(text);
|
|
20
|
+
const list = (parsed ).approvals;
|
|
21
|
+
if (!Array.isArray(list)) return [];
|
|
22
|
+
return list.filter((r) => typeof r?.path === "string" && typeof r?.hash === "string" && typeof r?.approvedAt === "string" && typeof r?.by === "string");
|
|
23
|
+
} catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function serializeApprovals(records ) {
|
|
29
|
+
const sorted = [...records].sort((a, b) => a.path.localeCompare(b.path));
|
|
30
|
+
return `${JSON.stringify({ version: 2, approvals: sorted }, null, 2)}\n`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The records with this one replacing any earlier approval of the same document. */
|
|
34
|
+
export function withApproval(records , record ) {
|
|
35
|
+
return [...records.filter((r) => r.path !== record.path), record];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The hash an approval records. A feature's `## Decisions` is the agent's record of what it chose,
|
|
40
|
+
* which build and review add to, so it stays out: the user's approval covers the rest.
|
|
41
|
+
*/
|
|
42
|
+
export function approvalHash(path , doc ) {
|
|
43
|
+
return designHash(doc, slugOfFeaturePath(path) === undefined ? [] : ["Decisions"]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Whether the document still reads as it did when it was approved. */
|
|
47
|
+
export function approvalState(records , path , doc ) {
|
|
48
|
+
const record = records.find((r) => r.path === path);
|
|
49
|
+
if (record === undefined) return "unapproved";
|
|
50
|
+
return record.hash === approvalHash(path, doc) ? "approved" : "edited";
|
|
51
|
+
}
|
|
@@ -1,36 +1,29 @@
|
|
|
1
1
|
|
|
2
2
|
import { matchesAny } from "../glob.js";
|
|
3
3
|
import { budgetedText } from "./budget_text.js";
|
|
4
|
-
import { decisionCitationsInComments } from "./decision_comments.js";
|
|
5
4
|
import { importsOf, resolveImport } from "./imports.js";
|
|
6
5
|
import { countTypes } from "./type_count.js";
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
14
|
|
|
18
15
|
|
|
19
16
|
|
|
20
17
|
|
|
21
18
|
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
20
|
|
|
28
21
|
|
|
29
22
|
|
|
30
23
|
|
|
31
24
|
/** Enforces the architecture's layer rules and the file budgets on source code. */
|
|
32
25
|
export function checkSource(input ) {
|
|
33
|
-
const { config, sources
|
|
26
|
+
const { config, sources } = input;
|
|
34
27
|
const all = [...sources.keys()];
|
|
35
28
|
const targets = input.only ?? all;
|
|
36
29
|
const layerOf = (path ) => config.layers.find((layer) => matchesAny(path, layer.paths));
|
|
@@ -44,7 +37,7 @@ export function checkSource(input ) {
|
|
|
44
37
|
findings.push({ path, rule: "no-layer", severity: "warning", message: "the file is in no layer, so its imports are unchecked" });
|
|
45
38
|
}
|
|
46
39
|
if (layer !== undefined) {
|
|
47
|
-
for (const found of importsOf(path, text)) {
|
|
40
|
+
for (const found of importsOf(path, text, all)) {
|
|
48
41
|
const target = resolveImport(found, all);
|
|
49
42
|
const targetLayer = target === undefined ? undefined : layerOf(target);
|
|
50
43
|
if (target === undefined || targetLayer === undefined || targetLayer.name === layer.name) continue;
|
|
@@ -67,32 +60,10 @@ export function checkSource(input ) {
|
|
|
67
60
|
if (!excepted && isRustTestFileInSource(path)) {
|
|
68
61
|
findings.push({ path, rule: "test-placement", severity: "warning", message: "Rust tests that leave their inline module go in the crate's tests/ directory, not under src/" });
|
|
69
62
|
}
|
|
70
|
-
if (modulePaths.length > 0 && !insideModuleMap(path, modulePaths)) {
|
|
71
|
-
findings.push({ path, rule: "outside-module-map", severity: "warning", message: "the file is outside every module in the architecture's module map" });
|
|
72
|
-
}
|
|
73
|
-
findings.push(...decisionFindings(path, text));
|
|
74
|
-
}
|
|
75
|
-
for (const [path, text] of input.tests ?? []) {
|
|
76
|
-
if (!sources.has(path) && (input.only === undefined || input.only.includes(path))) findings.push(...decisionFindings(path, text));
|
|
77
63
|
}
|
|
78
64
|
return findings;
|
|
79
65
|
}
|
|
80
66
|
|
|
81
|
-
function decisionFindings(path , text ) {
|
|
82
|
-
return decisionCitationsInComments(path, text).map((citation) => ({
|
|
83
|
-
path,
|
|
84
|
-
line: citation.line + 1,
|
|
85
|
-
rule: "decision-citation",
|
|
86
|
-
severity: "warning",
|
|
87
|
-
message: `the comment cites ${citation.text}, a decision number a reader of the code can't follow; say what was decided, or cite the element or ADR that holds it`,
|
|
88
|
-
}));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Whether a module's Path covers the file: a directory path ending in `/` covers everything under it, any other names one file. */
|
|
92
|
-
export function insideModuleMap(path , modulePaths ) {
|
|
93
|
-
return modulePaths.some((module) => (module.endsWith("/") ? path.startsWith(module) : path === module));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
67
|
/** A Rust file of tests split out of a module: `tests.rs`, `*_tests.rs`, or anything under a `tests/` folder inside `src/`. */
|
|
97
68
|
function isRustTestFileInSource(path ) {
|
|
98
69
|
if (!path.endsWith(".rs")) return false;
|
|
@@ -12,11 +12,11 @@ import { rustImportCandidates } from "./rust_imports.js";
|
|
|
12
12
|
const JS = /\.(?:[cm]?[jt]sx?)$/;
|
|
13
13
|
const JS_EXTENSIONS = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", "/index.ts", "/index.js"];
|
|
14
14
|
|
|
15
|
-
/** Imports that point inside the project
|
|
16
|
-
export function importsOf(path , text ) {
|
|
15
|
+
/** Imports that point inside the project, among `files`. Imports of libraries and the standard library are left out. */
|
|
16
|
+
export function importsOf(path , text , files ) {
|
|
17
17
|
if (JS.test(path)) return jsImports(path, text);
|
|
18
18
|
if (path.endsWith(".py")) return pythonImports(path, text);
|
|
19
|
-
if (path.endsWith(".rs")) return rustImportCandidates(path, text).map((i) => ({ ...i, bySuffix: false }));
|
|
19
|
+
if (path.endsWith(".rs")) return rustImportCandidates(path, text, files).map((i) => ({ ...i, bySuffix: false }));
|
|
20
20
|
if (/\.(?:c|h|cc|cpp|cxx|hh|hpp|hxx)$/.test(path)) return matches(text, /^\s*#\s*include\s+"([^"]+)"/gm, (target) => [target], true);
|
|
21
21
|
if (path.endsWith(".go")) return goImports(text);
|
|
22
22
|
if (/\.(?:java|kt)$/.test(path)) return matches(text, /^import\s+(?:static\s+)?([\w.]+)(?:\.\*)?;?\s*$/gm, (t) => [`${t.replace(/\./g, "/")}.java`, `${t.replace(/\./g, "/")}.kt`], true);
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
/** Resolves Rust `use` paths inside a crate to candidate source files. */
|
|
1
|
+
/** Resolves Rust `use` paths, inside a crate and between a workspace's crates, to candidate source files. */
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Candidate files for each `use crate::…`, `use super::…` and `use self::…` path in a Rust file,
|
|
5
|
+
* and for each `use other_crate::…` that names a library crate among `files`.
|
|
6
|
+
*/
|
|
7
|
+
export function rustImportCandidates(path , text , files ) {
|
|
5
8
|
const root = crateRoot(path);
|
|
6
9
|
if (root === null) return [];
|
|
7
10
|
const own = moduleSegments(path, root);
|
|
11
|
+
const crates = libraryCrates(files);
|
|
8
12
|
const results = [];
|
|
9
13
|
const pattern = /^\s*(?:pub(?:\([^)]*\))?\s+)?use\s+([^;]+);/gms;
|
|
10
14
|
for (const match of text.matchAll(pattern)) {
|
|
@@ -12,11 +16,29 @@ export function rustImportCandidates(path , text )
|
|
|
12
16
|
for (const segments of expandUse((match[1] ?? "").replace(/\s+/g, ""))) {
|
|
13
17
|
const absolute = absoluteSegments(segments, own);
|
|
14
18
|
if (absolute !== null) results.push({ line, candidates: candidatesFor(root, absolute) });
|
|
19
|
+
else {
|
|
20
|
+
const [head, ...rest] = segments;
|
|
21
|
+
const other = head === undefined ? undefined : crates.get(head);
|
|
22
|
+
if (other !== undefined && other !== root) results.push({ line, candidates: candidatesFor(other, rest) });
|
|
23
|
+
}
|
|
15
24
|
}
|
|
16
25
|
}
|
|
17
26
|
return results;
|
|
18
27
|
}
|
|
19
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Each library crate in the project by the name code imports it as: the directory
|
|
31
|
+
* holding `src/lib.rs`, with hyphens as underscores, as Cargo names it by default.
|
|
32
|
+
*/
|
|
33
|
+
function libraryCrates(files ) {
|
|
34
|
+
const crates = new Map ();
|
|
35
|
+
for (const file of files) {
|
|
36
|
+
const match = /^(?:.*\/)?([^/]+)\/src\/lib\.rs$/.exec(file);
|
|
37
|
+
if (match !== null) crates.set((match[1] ?? "").replace(/-/g, "_"), file.slice(0, -"lib.rs".length));
|
|
38
|
+
}
|
|
39
|
+
return crates;
|
|
40
|
+
}
|
|
41
|
+
|
|
20
42
|
/** The directory holding the crate root: everything up to and including `src/`. */
|
|
21
43
|
function crateRoot(path ) {
|
|
22
44
|
const index = path.lastIndexOf("src/");
|
package/dist/domain/config.js
CHANGED
|
@@ -5,10 +5,7 @@ import { parse as parseYaml } from "yaml";
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
8
|
|
|
11
|
-
|
|
12
9
|
|
|
13
10
|
|
|
14
11
|
|
|
@@ -17,34 +14,12 @@ import { parse as parseYaml } from "yaml";
|
|
|
17
14
|
|
|
18
15
|
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
17
|
|
|
23
18
|
|
|
24
19
|
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
/** Which review findings the user decides: only the exceptions, or every one. */
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export const DEFAULT_CHANGE_LIMITS = { minBuilds: 3, maxScenarios: 30, maxTasks: 25, exceptions: [] };
|
|
48
23
|
|
|
49
24
|
|
|
50
25
|
|
|
@@ -62,17 +37,14 @@ export function parseConfig(text ) {
|
|
|
62
37
|
} catch (error) {
|
|
63
38
|
return { config: null, problems: [`not valid YAML: ${error instanceof Error ? error.message : String(error)}`] };
|
|
64
39
|
}
|
|
65
|
-
const problems = [];
|
|
66
40
|
const root = asRecord(raw);
|
|
67
41
|
if (root === null) return { config: null, problems: ["the file must be a YAML mapping"] };
|
|
68
|
-
|
|
69
|
-
|
|
42
|
+
if (root["version"] === 1) return { config: null, problems: ["this config was created by an older Cruze, whose documents this release doesn't read; start the project's Cruze documents again with cruze init in a fresh checkout"] };
|
|
43
|
+
const problems = [];
|
|
44
|
+
if (root["version"] !== 2) problems.push("version must be 2");
|
|
70
45
|
const cruze = root["cruze"];
|
|
71
|
-
if (cruze !== undefined && (typeof cruze !== "string" || cruze.trim() === "")) problems.push("cruze must be a version string, such as \"0.0.
|
|
72
|
-
const standards = root["standards"];
|
|
73
|
-
if (standards !== undefined && (typeof standards !== "string" || standards.trim() === "")) problems.push("standards must be a version string, such as \"0.0.2\"");
|
|
46
|
+
if (cruze !== undefined && (typeof cruze !== "string" || cruze.trim() === "")) problems.push("cruze must be a version string, such as \"0.0.14\"");
|
|
74
47
|
if (typeof root["project"] !== "string" || root["project"] === "") problems.push("project must be a non-empty string");
|
|
75
|
-
if (root["tracker"] !== "markdown") problems.push("tracker must be markdown");
|
|
76
48
|
const source = stringList(root["source"], "source", problems);
|
|
77
49
|
const tests = stringList(root["tests"], "tests", problems);
|
|
78
50
|
|
|
@@ -104,71 +76,23 @@ export function parseConfig(text ) {
|
|
|
104
76
|
}
|
|
105
77
|
}
|
|
106
78
|
|
|
107
|
-
const commands = {};
|
|
108
|
-
const rawCommands = root["commands"];
|
|
109
|
-
if (rawCommands !== undefined && rawCommands !== null) {
|
|
110
|
-
const record = asRecord(rawCommands);
|
|
111
|
-
if (record === null) problems.push("commands must be a mapping of names to commands");
|
|
112
|
-
for (const [name, command] of Object.entries(record ?? {})) {
|
|
113
|
-
if (typeof command === "string" && command.trim() !== "") commands[name] = command;
|
|
114
|
-
else problems.push(`commands.${name} must be a command string`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
79
|
const review = asRecord(root["review"]) ?? {};
|
|
118
80
|
const reviewCommand = review["command"] === undefined ? undefined : stringList(review["command"], "review.command", problems);
|
|
119
81
|
if (reviewCommand?.length === 0) problems.push("review.command needs at least the program to run");
|
|
120
|
-
const decide = review["decide"] ?? "exceptions";
|
|
121
|
-
if (decide !== "exceptions" && decide !== "all") problems.push("review.decide must be exceptions or all");
|
|
122
|
-
const limits = asRecord(root["changes"]) ?? {};
|
|
123
|
-
const changes = {
|
|
124
|
-
minBuilds: positiveInt(limits["min_builds"], "changes.min_builds", problems) ?? DEFAULT_CHANGE_LIMITS.minBuilds,
|
|
125
|
-
maxScenarios: positiveInt(limits["max_scenarios"], "changes.max_scenarios", problems) ?? DEFAULT_CHANGE_LIMITS.maxScenarios,
|
|
126
|
-
maxTasks: positiveInt(limits["max_tasks"], "changes.max_tasks", problems) ?? DEFAULT_CHANGE_LIMITS.maxTasks,
|
|
127
|
-
exceptions: (Array.isArray(limits["exceptions"]) ? limits["exceptions"] : []).flatMap((entry , i ) => {
|
|
128
|
-
const record = asRecord(entry);
|
|
129
|
-
if (typeof record?.["item"] !== "string" || typeof record["reason"] !== "string" || record["reason"].trim() === "") {
|
|
130
|
-
problems.push(`changes.exceptions[${i}] needs an item and a non-empty reason`);
|
|
131
|
-
return [];
|
|
132
|
-
}
|
|
133
|
-
return [{ item: record["item"], reason: record["reason"] }];
|
|
134
|
-
}),
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const references = (Array.isArray(root["references"]) ? root["references"] : root["references"] === undefined ? [] : [null]).flatMap((entry , i ) => {
|
|
138
|
-
const record = asRecord(entry);
|
|
139
|
-
if (typeof record?.["path"] !== "string" || record["path"].trim() === "" || typeof record["use"] !== "string" || record["use"].trim() === "") {
|
|
140
|
-
problems.push(`references[${i}] needs a path and a non-empty use`);
|
|
141
|
-
return [];
|
|
142
|
-
}
|
|
143
|
-
return [{ path: record["path"], use: record["use"] }];
|
|
144
|
-
});
|
|
145
82
|
|
|
146
83
|
const config = {
|
|
147
|
-
version:
|
|
84
|
+
version: 2,
|
|
148
85
|
...(typeof cruze === "string" && cruze.trim() !== "" ? { cruze } : {}),
|
|
149
|
-
...(typeof standards === "string" && standards.trim() !== "" ? { standards } : {}),
|
|
150
86
|
project: typeof root["project"] === "string" ? root["project"] : "",
|
|
151
|
-
tracker: "markdown",
|
|
152
87
|
source,
|
|
153
88
|
tests,
|
|
154
|
-
check: { maxLines: maxLines ??
|
|
89
|
+
check: { maxLines: maxLines ?? 600, maxTypes: maxTypes ?? 12, exceptions },
|
|
155
90
|
layers,
|
|
156
|
-
|
|
157
|
-
review: {
|
|
158
|
-
...(reviewCommand === undefined || reviewCommand.length === 0 ? {} : { command: reviewCommand }),
|
|
159
|
-
decide: decide === "all" ? "all" : "exceptions",
|
|
160
|
-
},
|
|
161
|
-
changes,
|
|
162
|
-
references,
|
|
91
|
+
review: reviewCommand === undefined || reviewCommand.length === 0 ? {} : { command: reviewCommand },
|
|
163
92
|
};
|
|
164
93
|
return { config, problems };
|
|
165
94
|
}
|
|
166
95
|
|
|
167
|
-
/** The first layer whose paths match the file, as the formats define. */
|
|
168
|
-
export function layerOf(config , path , matches ) {
|
|
169
|
-
return config.layers.find((layer) => matches(path, layer.paths));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
96
|
function asRecord(value ) {
|
|
173
97
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? (value ) : null;
|
|
174
98
|
}
|