@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,23 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/** The `##` sections of architecture.md, in order, and the element kind each holds. */
|
|
4
|
-
export const ARCHITECTURE_SECTIONS = [
|
|
5
|
-
{ title: "Overview", kind: "VIEW" },
|
|
6
|
-
{ title: "Bounded contexts", kind: "CTX" },
|
|
7
|
-
{ title: "Domain model", kind: "ENT" },
|
|
8
|
-
{ title: "Use cases", kind: "UC" },
|
|
9
|
-
{ title: "Ports", kind: "PORT" },
|
|
10
|
-
{ title: "Adapters", kind: "ADP" },
|
|
11
|
-
{ title: "Flows", kind: "FLOW" },
|
|
12
|
-
{ title: "Modules", kind: "MOD" },
|
|
13
|
-
{ title: "Dependency rules", kind: "RULE" },
|
|
14
|
-
{ title: "Cross-cutting concerns", kind: "XC" },
|
|
15
|
-
{ title: "Decisions", kind: null },
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
export const ARCHITECTURE_KINDS = ["VIEW", "CTX", "ENT", "UC", "PORT", "ADP", "FLOW", "MOD", "RULE", "XC"];
|
|
19
|
-
export const SPEC_KINDS = ["REQ", "SCN"];
|
|
20
|
-
|
|
21
|
-
export function sectionForKind(kind ) {
|
|
22
|
-
return ARCHITECTURE_SECTIONS.find((section) => section.kind === kind)?.title;
|
|
23
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { findDecisionCitations, } from "../decision_citations.js";
|
|
2
|
-
|
|
3
|
-
const HASH_COMMENTS = new Set(["py", "rb", "sh", "bash", "zsh", "pl", "r", "ex", "exs", "jl", "nim", "cr", "ps1"]);
|
|
4
|
-
const DASH_COMMENTS = new Set(["sql", "lua", "hs", "elm", "ada"]);
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Decisions a source file's comments cite by number, such as `(D22)` or `(events D23)`. Each work
|
|
8
|
-
* item numbers its decisions from D1 and is archived when it finishes, so a number in code points
|
|
9
|
-
* at nothing a reader can find.
|
|
10
|
-
*/
|
|
11
|
-
export function decisionCitationsInComments(path , text ) {
|
|
12
|
-
const comments = commentText(path, text.split("\n"));
|
|
13
|
-
return findDecisionCitations(comments, new Set());
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Each line with everything but its comment blanked, so a citation's line number stays right. */
|
|
17
|
-
function commentText(path , lines ) {
|
|
18
|
-
const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
|
|
19
|
-
if (HASH_COMMENTS.has(extension)) return lines.map((line) => after(line, /(^|\s)#/));
|
|
20
|
-
if (DASH_COMMENTS.has(extension)) return lines.map((line) => after(line, /--/));
|
|
21
|
-
let inBlock = false;
|
|
22
|
-
return lines.map((line) => {
|
|
23
|
-
if (inBlock) {
|
|
24
|
-
if (line.includes("*/")) inBlock = false;
|
|
25
|
-
return line;
|
|
26
|
-
}
|
|
27
|
-
const open = line.indexOf("/*");
|
|
28
|
-
if (open !== -1 && !line.includes("*/", open + 2)) inBlock = true;
|
|
29
|
-
const slash = line.indexOf("//");
|
|
30
|
-
const start = [open, slash].filter((at) => at !== -1).sort((a, b) => a - b)[0];
|
|
31
|
-
return start === undefined ? "" : line.slice(start);
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function after(line , marker ) {
|
|
36
|
-
const match = marker.exec(line);
|
|
37
|
-
return match === null ? "" : line.slice(match.index);
|
|
38
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Settled decisions are numbered `D<n>` within the document that records them: the architecture's
|
|
3
|
-
* `## Decisions`, or one feature or change, which is archived when it finishes. A number cited
|
|
4
|
-
* anywhere else either dangles or silently means another document's decision.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/** One citation of decisions by number, such as `D5`, `D5, D19` or `events D17`. */
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const SLUG = "[a-z0-9]+(?:-[a-z0-9]+)*";
|
|
18
|
-
const CITATION = new RegExp(`(?:(?<![\\w-])(${SLUG})\\s+)?(?<![\\w-])(D\\d+(?:\\s*(?:,|and)\\s*D\\d+)*)(?![\\w-])(?:\\s+of\\s+(${SLUG}))?`, "g");
|
|
19
|
-
const DEFINITION = /^\s*- D(\d+):/;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Every citation of decisions by number in the lines given, skipping the `- D<n>:` that defines one
|
|
23
|
-
* and anything inside backticks. `slugs` are the work items a citation may name.
|
|
24
|
-
*/
|
|
25
|
-
export function findDecisionCitations(lines , slugs , skip = () => false) {
|
|
26
|
-
const citations = [];
|
|
27
|
-
lines.forEach((raw, line) => {
|
|
28
|
-
if (skip(line)) return;
|
|
29
|
-
const text = raw.replace(DEFINITION, (m) => " ".repeat(m.length)).replace(/`[^`]*`/g, (m) => " ".repeat(m.length));
|
|
30
|
-
for (const match of text.matchAll(CITATION)) {
|
|
31
|
-
const before = match[1] !== undefined && slugs.has(match[1]) ? match[1] : undefined;
|
|
32
|
-
const after = match[3] !== undefined && slugs.has(match[3]) ? match[3] : undefined;
|
|
33
|
-
const numbers = [...(match[2] ?? "").matchAll(/D(\d+)/g)].map((m) => Number(m[1]));
|
|
34
|
-
const qualifier = before ?? after;
|
|
35
|
-
const cited = qualifier === undefined ? (match[2] ?? "") : (match[0] ?? "").trim();
|
|
36
|
-
citations.push(qualifier === undefined ? { line, text: cited, numbers } : { line, text: cited, numbers, qualifier });
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
return citations;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** The decisions a `## Decisions` section, or any lines, define as `- D<n>: ...`. */
|
|
43
|
-
export function definedDecisions(lines ) {
|
|
44
|
-
return new Set(lines.flatMap((line) => {
|
|
45
|
-
const match = DEFINITION.exec(line);
|
|
46
|
-
return match?.[1] === undefined ? [] : [Number(match[1])];
|
|
47
|
-
}));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* The names a citation may give a work item: a feature's slug with and without its date, and a
|
|
52
|
-
* change's folder name, such as `events`, `2026-10-08-events` and `02-links`.
|
|
53
|
-
*/
|
|
54
|
-
export function workItemSlugs(folderNames ) {
|
|
55
|
-
const slugs = new Set ();
|
|
56
|
-
for (const name of folderNames) {
|
|
57
|
-
slugs.add(name);
|
|
58
|
-
const undated = name.replace(/^\d{4}-\d{2}-\d{2}-/, "");
|
|
59
|
-
if (undated !== "") slugs.add(undated);
|
|
60
|
-
}
|
|
61
|
-
return slugs;
|
|
62
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { factsOf, statusOf } from "../elements.js";
|
|
2
|
-
import { findSection, parseMarkdown } from "../markdown.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* The managed sections of a project's `AGENTS.md`: its commands, from `.cruze/config.yaml`,
|
|
7
|
-
* and its layout, from the built modules in `docs/architecture.md`.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const OPEN = "<!-- cruze:managed -->";
|
|
11
|
-
const CLOSE = "<!-- /cruze:managed -->";
|
|
12
|
-
|
|
13
|
-
/** Rewrites the managed blocks under `## Commands` and `## Layout`; other text is left alone. */
|
|
14
|
-
export function renderAgentsFile(text , view ) {
|
|
15
|
-
let next = text;
|
|
16
|
-
const commands = Object.entries(view.config?.config?.commands ?? {});
|
|
17
|
-
if (commands.length > 0) next = replaceBlock(next, "Commands", commands.map(([name, command]) => `- ${name}: \`${command}\``));
|
|
18
|
-
const modules = builtModules(view);
|
|
19
|
-
if (modules.length > 0) next = replaceBlock(next, "Layout", ["Modules, from `docs/architecture.md`:", "", ...modules]);
|
|
20
|
-
return next;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** The lines under `## Known pitfalls`, one per pitfall, which the feedback export carries back to Cruze. */
|
|
24
|
-
export function knownPitfalls(text ) {
|
|
25
|
-
const doc = parseMarkdown(text);
|
|
26
|
-
const range = findSection(doc, "Known pitfalls");
|
|
27
|
-
if (range === null) return [];
|
|
28
|
-
return doc.lines.slice(range.start, range.end).filter((line) => line.startsWith("- ")).map((line) => line.slice(2).trim());
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The modules whose code exists: those built, and those a land built before a rethink set them
|
|
33
|
-
* back to `planned` for a change to rebuild, since their directories are still there.
|
|
34
|
-
*/
|
|
35
|
-
function builtModules(view ) {
|
|
36
|
-
const everBuilt = new Set(view.journal.filter((e) => e.event === "land").flatMap((e) => (Array.isArray(e["built"]) ? (e["built"] ).map(String) : [])));
|
|
37
|
-
return [...view.living.elements.values()]
|
|
38
|
-
.filter(({ element, doc }) => element.kind === "MOD" && (statusOf(doc, element) === "built" || everBuilt.has(element.id)))
|
|
39
|
-
.map(({ element, doc }) => {
|
|
40
|
-
const facts = factsOf(doc, element);
|
|
41
|
-
const paths = (facts.get("Path") ?? []).flatMap((value) => [...value.matchAll(/`([^`]+)`/g)].map((m) => `\`${m[1] ?? ""}\``));
|
|
42
|
-
return `- ${paths.join(", ")} (${facts.get("Layer")?.[0] ?? "unknown"}): ${element.title}`;
|
|
43
|
-
})
|
|
44
|
-
.sort();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Replaces the body of the first managed block after `## <heading>`, before the next `## ` heading. */
|
|
48
|
-
function replaceBlock(text , heading , body ) {
|
|
49
|
-
const lines = text.split("\n");
|
|
50
|
-
const at = lines.findIndex((line) => line === `## ${heading}`);
|
|
51
|
-
if (at === -1) return text;
|
|
52
|
-
const sectionEnd = lines.findIndex((line, i) => i > at && line.startsWith("## "));
|
|
53
|
-
const end = sectionEnd === -1 ? lines.length : sectionEnd;
|
|
54
|
-
const open = lines.findIndex((line, i) => i > at && i < end && line.trim() === OPEN);
|
|
55
|
-
const close = lines.findIndex((line, i) => i > open && i < end && line.trim() === CLOSE);
|
|
56
|
-
if (open === -1 || close === -1) return text;
|
|
57
|
-
return [...lines.slice(0, open + 1), ...body, ...lines.slice(close)].join("\n");
|
|
58
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { parseMarkdown } from "../markdown.js";
|
|
2
|
-
import { splitRow, tablesIn } from "../markdown_tables.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Edits to the managed tables: the feature map in vision.md and the status table
|
|
6
|
-
* in roadmap.md. Rows are keyed by their first cell.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/** Replaces or appends the row keyed by `cells[0]` in the table under a heading. */
|
|
10
|
-
export function upsertRow(text , heading , cells ) {
|
|
11
|
-
return editTable(text, heading, (rows) => {
|
|
12
|
-
const index = rows.findIndex((row) => row[0] === cells[0]);
|
|
13
|
-
if (index === -1) rows.push(cells);
|
|
14
|
-
else rows[index] = cells;
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function removeRow(text , heading , key ) {
|
|
19
|
-
let removed = null;
|
|
20
|
-
const next = editTable(text, heading, (rows) => {
|
|
21
|
-
const index = rows.findIndex((row) => row[0] === key);
|
|
22
|
-
if (index !== -1) removed = rows.splice(index, 1)[0] ?? null;
|
|
23
|
-
});
|
|
24
|
-
return { text: next, removed };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function readRows(text , heading ) {
|
|
28
|
-
const doc = parseMarkdown(text);
|
|
29
|
-
const at = doc.headings.find((h) => h.text === heading);
|
|
30
|
-
if (at === undefined) return [];
|
|
31
|
-
const end = doc.headings.find((h) => h.line > at.line && h.level <= at.level)?.line ?? doc.lines.length;
|
|
32
|
-
return tablesIn(doc.lines, doc.inFence, at.line + 1, end)[0]?.rows.map((row) => row.cells) ?? [];
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function editTable(text , heading , edit ) {
|
|
36
|
-
const doc = parseMarkdown(text);
|
|
37
|
-
const at = doc.headings.find((h) => h.text === heading);
|
|
38
|
-
if (at === undefined) throw new Error(`No "${heading}" heading to edit`);
|
|
39
|
-
const end = doc.headings.find((h) => h.line > at.line && h.level <= at.level)?.line ?? doc.lines.length;
|
|
40
|
-
let first = -1;
|
|
41
|
-
let last = -1;
|
|
42
|
-
for (let i = at.line + 1; i < end; i++) {
|
|
43
|
-
if (/^\s*\|.*\|\s*$/.test(doc.lines[i] ?? "")) {
|
|
44
|
-
if (first === -1) first = i;
|
|
45
|
-
last = i;
|
|
46
|
-
} else if (first !== -1) break;
|
|
47
|
-
}
|
|
48
|
-
if (first === -1) throw new Error(`No table under "${heading}"`);
|
|
49
|
-
const header = splitRow(doc.lines[first] ?? "");
|
|
50
|
-
const rows = doc.lines.slice(first + 2, last + 1).map(splitRow);
|
|
51
|
-
edit(rows);
|
|
52
|
-
const rendered = [renderRow(header), renderRow(header.map(() => "---")), ...rows.map(renderRow)];
|
|
53
|
-
const lines = [...doc.lines];
|
|
54
|
-
lines.splice(first, last - first + 1, ...rendered);
|
|
55
|
-
return lines.join("\n");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function renderRow(cells ) {
|
|
59
|
-
return `| ${cells.join(" | ")} |`;
|
|
60
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { MANAGED_CLOSE, MANAGED_OPEN, parseMarkdown } from "../markdown.js";
|
|
2
|
-
import { readTasks } from "../project/plan_parts.js";
|
|
3
|
-
import { progressRange, readProgress, } from "../project/progress.js";
|
|
4
|
-
|
|
5
|
-
/** Rewrites the managed `## Progress` block of a feature or change from a changed Progress. */
|
|
6
|
-
export function updateProgress(text , change ) {
|
|
7
|
-
const doc = parseMarkdown(text);
|
|
8
|
-
const progress = readProgress(doc);
|
|
9
|
-
for (const task of readTasks(doc).tasks) if (!progress.tasks.has(task.number)) progress.tasks.set(task.number, null);
|
|
10
|
-
change(progress);
|
|
11
|
-
const block = [MANAGED_OPEN, "## Progress", ...renderProgress(progress), MANAGED_CLOSE];
|
|
12
|
-
const range = progressRange(doc);
|
|
13
|
-
const lines = [...doc.lines];
|
|
14
|
-
if (range === null) {
|
|
15
|
-
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
16
|
-
return [...lines, "", ...block, ""].join("\n");
|
|
17
|
-
}
|
|
18
|
-
lines.splice(range.open, range.end - range.open + 1, ...block);
|
|
19
|
-
return lines.join("\n");
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function renderProgress(progress ) {
|
|
23
|
-
const lines = [];
|
|
24
|
-
if (progress.branch !== undefined) lines.push(`- Branch: ${progress.branch}`);
|
|
25
|
-
for (const [change, state] of progress.changes) lines.push(`- ${change}: ${state}`);
|
|
26
|
-
const ordered = [...progress.tasks].sort(([a], [b]) => Number(a.slice(1)) - Number(b.slice(1)));
|
|
27
|
-
for (const [task, commit] of ordered) {
|
|
28
|
-
lines.push(commit === null ? `- [ ] ${task}` : commit === "" ? `- [x] ${task}` : `- [x] ${task} (${commit})`);
|
|
29
|
-
}
|
|
30
|
-
for (const deviation of progress.deviations) lines.push(`- Deviation ${deviation}`);
|
|
31
|
-
if (progress.landed !== undefined) lines.push(`- Landed: ${progress.landed}`);
|
|
32
|
-
return lines;
|
|
33
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { headingElements, isStatusLine, statusOf, } from "../elements.js";
|
|
2
|
-
import { STATUS_KINDS } from "../ids.js";
|
|
3
|
-
import { parseMarkdown, } from "../markdown.js";
|
|
4
|
-
|
|
5
|
-
/** Managed `- Status:` lines: only the CLI adds or changes them. */
|
|
6
|
-
|
|
7
|
-
/** Sets one element's status, adding the line after its facts when it has none. Returns the new text. */
|
|
8
|
-
export function setStatus(text , id , status ) {
|
|
9
|
-
const doc = parseMarkdown(text);
|
|
10
|
-
const element = headingElements(doc).find((e) => e.id === id);
|
|
11
|
-
if (element === undefined) throw new Error(`${id} is not in this document`);
|
|
12
|
-
const lines = [...doc.lines];
|
|
13
|
-
const at = statusLineIndex(doc, element.start, element.end);
|
|
14
|
-
if (at !== null) lines[at] = `- Status: ${status}`;
|
|
15
|
-
else lines.splice(insertionPoint(doc, element.start, element.end), 0, `- Status: ${status}`);
|
|
16
|
-
return lines.join("\n");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Adds `- Status: planned` to every status-carrying element that has none. */
|
|
20
|
-
export function stampMissingStatuses(text ) {
|
|
21
|
-
let current = text;
|
|
22
|
-
const stamped = [];
|
|
23
|
-
for (const element of headingElements(parseMarkdown(text))) {
|
|
24
|
-
if (!STATUS_KINDS.includes(element.kind)) continue;
|
|
25
|
-
const doc = parseMarkdown(current);
|
|
26
|
-
const fresh = headingElements(doc).find((e) => e.id === element.id);
|
|
27
|
-
if (fresh === undefined || statusOf(doc, fresh) !== undefined) continue;
|
|
28
|
-
current = setStatus(current, element.id, "planned");
|
|
29
|
-
stamped.push(element.id);
|
|
30
|
-
}
|
|
31
|
-
return { text: current, stamped };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function statusLineIndex(doc , start , end ) {
|
|
35
|
-
for (let i = start + 1; i < end; i++) {
|
|
36
|
-
if (!doc.inFence[i] && /^#{1,6} /.test(doc.lines[i] ?? "")) return null;
|
|
37
|
-
if (!doc.inFence[i] && isStatusLine(doc.lines[i] ?? "")) return i;
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** After the element's last top-level fact or step, before any diagram or nested heading. */
|
|
43
|
-
function insertionPoint(doc , start , end ) {
|
|
44
|
-
let point = start + 1;
|
|
45
|
-
for (let i = start + 1; i < end; i++) {
|
|
46
|
-
const line = doc.lines[i] ?? "";
|
|
47
|
-
if (doc.inFence[i] || /^#{1,6} /.test(line)) break;
|
|
48
|
-
if (/^(- |\s{2,}- )/.test(line)) point = i + 1;
|
|
49
|
-
else if (line.trim() !== "" && point > start + 1) break;
|
|
50
|
-
}
|
|
51
|
-
return point;
|
|
52
|
-
}
|
package/dist/domain/elements.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { ID_PATTERN, STATUS_KINDS, kindOf, } from "./ids.js";
|
|
2
|
-
import { sectionEnd, } from "./markdown.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/** One element: a heading `<ID>: <Title>` and its body, or a table row whose first cell is an ID. */
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/** A delta heading that creates a spec file: `### ADDED CAPABILITY <name>: <Title>`. */
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const HEADING = new RegExp(`^(?:(ADDED|MODIFIED|REMOVED) )?(${ID_PATTERN}): (.+)$`);
|
|
30
|
-
const CAPABILITY = /^ADDED CAPABILITY ([a-z0-9]+(?:-[a-z0-9]+)*): (.+)$/;
|
|
31
|
-
const ROW = new RegExp(`^\\|\\s*(${ID_PATTERN})\\s*\\|(.*)\\|\\s*$`);
|
|
32
|
-
const STATUS_LINE = /^- Status: (planned|built)\s*$/;
|
|
33
|
-
|
|
34
|
-
/** Heading elements whose heading lies in [from, to). */
|
|
35
|
-
export function headingElements(doc , from = 0, to = doc.lines.length) {
|
|
36
|
-
const elements = [];
|
|
37
|
-
for (const heading of doc.headings) {
|
|
38
|
-
if (heading.line < from || heading.line >= to) continue;
|
|
39
|
-
const match = HEADING.exec(heading.text);
|
|
40
|
-
if (match?.[2] === undefined || match[3] === undefined) continue;
|
|
41
|
-
const element = {
|
|
42
|
-
id: match[2],
|
|
43
|
-
kind: kindOf(match[2]),
|
|
44
|
-
title: match[3],
|
|
45
|
-
form: "heading",
|
|
46
|
-
level: heading.level,
|
|
47
|
-
start: heading.line,
|
|
48
|
-
end: Math.min(sectionEnd(doc, heading), to),
|
|
49
|
-
};
|
|
50
|
-
if (match[1] !== undefined) element.op = match[1] ;
|
|
51
|
-
elements.push(element);
|
|
52
|
-
}
|
|
53
|
-
return elements;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function capabilityOps(doc , from = 0, to = doc.lines.length) {
|
|
57
|
-
return doc.headings
|
|
58
|
-
.filter((h) => h.line >= from && h.line < to)
|
|
59
|
-
.flatMap((heading) => {
|
|
60
|
-
const match = CAPABILITY.exec(heading.text);
|
|
61
|
-
if (match?.[1] === undefined || match[2] === undefined) return [];
|
|
62
|
-
return [{ capability: match[1], title: match[2], start: heading.line, end: Math.min(sectionEnd(doc, heading), to) }];
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Row elements: table rows, outside code fences, whose first cell is an ID. */
|
|
67
|
-
export function rowElements(doc ) {
|
|
68
|
-
return doc.lines.flatMap((line, index) => {
|
|
69
|
-
if (doc.inFence[index]) return [];
|
|
70
|
-
const match = ROW.exec(line);
|
|
71
|
-
if (match?.[1] === undefined) return [];
|
|
72
|
-
const title = match[2]?.split("|")[0]?.trim() ?? "";
|
|
73
|
-
return [{ id: match[1], kind: kindOf(match[1]), title, form: "row" , level: 0, start: index, end: index + 1 }];
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function elementLines(doc , element ) {
|
|
78
|
-
return doc.lines.slice(element.start, element.end);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function isStatusLine(line ) {
|
|
82
|
-
return STATUS_LINE.test(line);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** The element's own managed status: the first status line before any nested heading. */
|
|
86
|
-
/**
|
|
87
|
-
* Whether an element is still to be built: marked `planned`, or a kind that carries a status
|
|
88
|
-
* but not stamped yet, because the document holding it has never been approved.
|
|
89
|
-
*/
|
|
90
|
-
export function isUnbuilt(doc , element ) {
|
|
91
|
-
const status = statusOf(doc, element);
|
|
92
|
-
return status === "planned" || (status === undefined && STATUS_KINDS.includes(element.kind));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function statusOf(doc , element ) {
|
|
96
|
-
for (let i = element.start + 1; i < element.end; i++) {
|
|
97
|
-
if (/^#{1,6} /.test(doc.lines[i] ?? "") && !doc.inFence[i]) return undefined;
|
|
98
|
-
const match = STATUS_LINE.exec(doc.lines[i] ?? "");
|
|
99
|
-
if (match?.[1] !== undefined) return match[1] ;
|
|
100
|
-
}
|
|
101
|
-
return undefined;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* The element's top-level facts: `- Key: value` items and their indented sub-items.
|
|
106
|
-
* Nested elements' facts are excluded.
|
|
107
|
-
*/
|
|
108
|
-
export function factsOf(doc , element ) {
|
|
109
|
-
const facts = new Map ();
|
|
110
|
-
let current = null;
|
|
111
|
-
for (let i = element.start + 1; i < element.end; i++) {
|
|
112
|
-
const line = doc.lines[i] ?? "";
|
|
113
|
-
if (doc.inFence[i]) continue;
|
|
114
|
-
if (/^#{1,6} /.test(line)) break;
|
|
115
|
-
const fact = /^- ([A-Z][A-Za-z ]*): ?(.*)$/.exec(line);
|
|
116
|
-
if (fact?.[1] !== undefined) {
|
|
117
|
-
current = fact[2] === undefined || fact[2] === "" ? [] : [fact[2]];
|
|
118
|
-
facts.set(fact[1], current);
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
const sub = /^\s{2,}- (.+)$/.exec(line);
|
|
122
|
-
if (sub?.[1] !== undefined && current !== null) current.push(sub[1]);
|
|
123
|
-
else if (line.trim() !== "") current = null;
|
|
124
|
-
}
|
|
125
|
-
return facts;
|
|
126
|
-
}
|
package/dist/domain/journal.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/** The append-only journal: one JSON object per line, written only by the CLI. */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
/** Events other tools and skills record, with the fields each requires. */
|
|
11
|
-
export const RECORDED_EVENTS = {
|
|
12
|
-
rethink: ["level", "kind", "summary", "wrong", "found_by"],
|
|
13
|
-
disposition: ["finding", "disposition", "reason", "review"],
|
|
14
|
-
review: ["review", "round", "blockers", "concerns"],
|
|
15
|
-
override: ["gate", "reason"],
|
|
16
|
-
bug: ["summary", "cause"],
|
|
17
|
-
verification: ["result", "summary"],
|
|
18
|
-
checks: ["commit"],
|
|
19
|
-
lesson: ["summary", "instead", "step"],
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/** Fields an event may carry beyond its required ones, checked when present. A defect rethink must say which step missed it. */
|
|
23
|
-
const OPTIONAL_FIELDS = {
|
|
24
|
-
rethink: ["missed_by"],
|
|
25
|
-
review: ["nits", "checked_by"],
|
|
26
|
-
disposition: ["decided"],
|
|
27
|
-
verification: ["back_to", "commit"],
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/** The workflow steps and checks that find or miss a problem, so a retro can count them. */
|
|
31
|
-
export const STEPS = ["envision", "architect", "research", "design-review", "walkthrough", "plan", "plan-review", "build", "code-review", "verify", "land", "realign", "improve", "validate", "check", "user"];
|
|
32
|
-
|
|
33
|
-
export const FIELD_VALUES = {
|
|
34
|
-
level: ["task", "detail", "change", "feature", "architecture", "vision"],
|
|
35
|
-
kind: ["defect", "discovery"],
|
|
36
|
-
disposition: ["fixed", "waived", "deferred", "rejected", "rethink"],
|
|
37
|
-
decided: ["agent", "user"],
|
|
38
|
-
review: ["design", "plan", "code", "realign", "improve"],
|
|
39
|
-
result: ["accepted", "sent-back"],
|
|
40
|
-
back_to: ["build", "rethink"],
|
|
41
|
-
checked_by: ["agent", "reviewer"],
|
|
42
|
-
found_by: STEPS,
|
|
43
|
-
missed_by: STEPS,
|
|
44
|
-
step: STEPS,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/** A defect rethink must say which step missed it, and a sent-back verification where the change goes back to. */
|
|
48
|
-
function conditionallyRequired(event , fields ) {
|
|
49
|
-
if (event === "rethink" && fields["kind"] === "defect") return ["missed_by"];
|
|
50
|
-
if (event === "verification" && fields["result"] === "sent-back") return ["back_to"];
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const CONDITION_NOTES = { missed_by: " for a defect", back_to: " when the change is sent back" };
|
|
55
|
-
|
|
56
|
-
/** Fields recorded as numbers rather than text. */
|
|
57
|
-
const COUNT_FIELDS = ["round", "blockers", "concerns", "nits"];
|
|
58
|
-
|
|
59
|
-
export function journalPath(folder ) {
|
|
60
|
-
return `${folder}/journal.jsonl`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function parseJournal(text ) {
|
|
64
|
-
const entries = [];
|
|
65
|
-
const badLines = [];
|
|
66
|
-
(text ?? "").split("\n").forEach((line, index) => {
|
|
67
|
-
if (line.trim() === "") return;
|
|
68
|
-
try {
|
|
69
|
-
const entry = JSON.parse(line) ;
|
|
70
|
-
if (typeof entry.event === "string" && typeof entry.at === "string") entries.push(entry);
|
|
71
|
-
else badLines.push(index + 1);
|
|
72
|
-
} catch {
|
|
73
|
-
badLines.push(index + 1);
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
return { entries, badLines };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function serializeEntry(entry ) {
|
|
80
|
-
return `${JSON.stringify(entry)}\n`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Problems with an event a caller asks to record; empty when it can be recorded. */
|
|
84
|
-
export function recordedEventProblems(event , fields ) {
|
|
85
|
-
const required = RECORDED_EVENTS[event];
|
|
86
|
-
if (required === undefined) return [`unknown event "${event}"; recordable events: ${Object.keys(RECORDED_EVENTS).join(", ")}`];
|
|
87
|
-
const needed = [...required, ...conditionallyRequired(event, fields)];
|
|
88
|
-
const problems = needed.filter((field) => (fields[field] ?? "").trim() === "").map((field) => `${event} needs ${field}${CONDITION_NOTES[field] ?? ""}`);
|
|
89
|
-
const known = [...required, ...(OPTIONAL_FIELDS[event] ?? [])];
|
|
90
|
-
for (const field of known) {
|
|
91
|
-
const value = fields[field];
|
|
92
|
-
if (value === undefined) continue;
|
|
93
|
-
const allowed = FIELD_VALUES[field];
|
|
94
|
-
if (allowed !== undefined && !allowed.includes(value)) problems.push(`${field} must be one of ${allowed.join(", ")}`);
|
|
95
|
-
if (COUNT_FIELDS.includes(field) && !/^\d+$/.test(value)) problems.push(`${field} must be a whole number`);
|
|
96
|
-
}
|
|
97
|
-
if ((event === "checks" || event === "verification") && fields["commit"] !== undefined && !/^[0-9a-f]{7,40}$/.test(fields["commit"])) {
|
|
98
|
-
problems.push("commit must be a commit hash, such as the output of git rev-parse --short HEAD");
|
|
99
|
-
}
|
|
100
|
-
if (event === "disposition" && fields["decided"] === "agent" && fields["disposition"] !== "fixed") {
|
|
101
|
-
problems.push("the agent decides only fixes; the user waives, defers or rejects a finding (decided=user)");
|
|
102
|
-
}
|
|
103
|
-
return problems;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** The fields as the journal stores them: counts become numbers. */
|
|
107
|
-
export function recordedFields(fields ) {
|
|
108
|
-
return Object.fromEntries(Object.entries(fields).map(([key, value]) => [key, COUNT_FIELDS.includes(key) ? Number(value) : value]));
|
|
109
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { removeRow, upsertRow } from "../edits/managed_tables.js";
|
|
2
|
-
import { updateProgress } from "../edits/progress_block.js";
|
|
3
|
-
import { parseMarkdown } from "../markdown.js";
|
|
4
|
-
import { PATHS } from "../project/layout.js";
|
|
5
|
-
import { readChangesTable } from "../project/plan_parts.js";
|
|
6
|
-
import { readProgress } from "../project/progress.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/** Records the land in progress blocks, the roadmap status and, when a feature finishes, the feature map. */
|
|
19
|
-
export function applyBookkeeping(view , change , source , landed , files ) {
|
|
20
|
-
const text = (path ) => files.get(path) ?? view.snapshot.get(path);
|
|
21
|
-
files.set(change.path, updateProgress(text(change.path) ?? "", (progress) => (progress.landed = landed)));
|
|
22
|
-
|
|
23
|
-
let finished = true;
|
|
24
|
-
if (source !== change) {
|
|
25
|
-
const featureText = updateProgress(text(source.path) ?? "", (progress) => progress.changes.set(change.folderName, "landed"));
|
|
26
|
-
files.set(source.path, featureText);
|
|
27
|
-
const states = readProgress(parseMarkdown(featureText)).changes;
|
|
28
|
-
finished = readChangesTable(source.doc).every((row) => states.get(row.change) === "landed");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const release = releaseName(text(PATHS.roadmap));
|
|
32
|
-
const slug = typeof source.doc.frontmatter?.["roadmap"] === "string" ? source.doc.frontmatter["roadmap"] : undefined;
|
|
33
|
-
const roadmap = text(PATHS.roadmap);
|
|
34
|
-
if (slug !== undefined && roadmap !== undefined && roadmap.includes("## Status")) {
|
|
35
|
-
files.set(PATHS.roadmap, upsertRow(roadmap, "Status", [slug, source.folderName, finished ? "landed" : "building"]));
|
|
36
|
-
}
|
|
37
|
-
const vision = text(PATHS.vision);
|
|
38
|
-
if (finished && source.kind === "feature" && slug !== undefined && vision !== undefined && vision.includes("### Implemented")) {
|
|
39
|
-
const { text: withoutFuture, removed } = removeRow(vision, "Future", slug);
|
|
40
|
-
const summary = removed?.[1] ?? String(source.doc.frontmatter?.["title"] ?? slug);
|
|
41
|
-
files.set(PATHS.vision, upsertRow(withoutFuture, "Implemented", [slug, release, summary]));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const result = { finished, release };
|
|
45
|
-
if (finished) result.archive = { from: source.folder, to: `${PATHS.archiveDir}/${source.folderName}` };
|
|
46
|
-
return result;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** The release in progress: the roadmap's `## Release` line up to its colon. */
|
|
50
|
-
export function releaseName(roadmap ) {
|
|
51
|
-
if (roadmap === undefined) return "unreleased";
|
|
52
|
-
const line = roadmap.split("## Release")[1]?.split("\n").find((l) => l.trim() !== "");
|
|
53
|
-
return line?.split(":")[0]?.trim() || "unreleased";
|
|
54
|
-
}
|