whykit 0.3.1__py3-none-any.whl
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.
- whykit/__init__.py +21 -0
- whykit/adopt.py +776 -0
- whykit/backlinks.py +106 -0
- whykit/check.py +320 -0
- whykit/ci_formats.py +283 -0
- whykit/cli.py +1719 -0
- whykit/completion.py +193 -0
- whykit/config.py +233 -0
- whykit/console.py +217 -0
- whykit/context.py +204 -0
- whykit/contract.py +210 -0
- whykit/contract_schemas/backlinks-report.schema.json +61 -0
- whykit/contract_schemas/context-bundle.schema.json +27 -0
- whykit/contract_schemas/context-pack.schema.json +29 -0
- whykit/contract_schemas/impact-report.schema.json +122 -0
- whykit/contract_schemas/query-result.schema.json +105 -0
- whykit/contract_schemas/status-report.schema.json +176 -0
- whykit/contract_schemas/trace-report.schema.json +211 -0
- whykit/diff.py +823 -0
- whykit/evidence.py +178 -0
- whykit/explorer_index.py +554 -0
- whykit/graph.py +318 -0
- whykit/immutability.py +624 -0
- whykit/impact.py +272 -0
- whykit/init_layout.py +205 -0
- whykit/io.py +563 -0
- whykit/lint.py +2190 -0
- whykit/lsp.py +1241 -0
- whykit/markdown_code.py +107 -0
- whykit/mcp_server.py +2289 -0
- whykit/messages.py +46 -0
- whykit/pack.py +294 -0
- whykit/parse_cache.py +752 -0
- whykit/placeholders.py +84 -0
- whykit/promote.py +651 -0
- whykit/py.typed +0 -0
- whykit/query.py +174 -0
- whykit/review.py +527 -0
- whykit/rule_policy.py +1063 -0
- whykit/rules.py +274 -0
- whykit/scaffold.py +784 -0
- whykit/sensitivity.py +95 -0
- whykit/snapshot.py +319 -0
- whykit/status.py +167 -0
- whykit/tables.py +120 -0
- whykit/template/00-context/README.md +17 -0
- whykit/template/00-context/company.md +52 -0
- whykit/template/00-context/evidence-register.md +34 -0
- whykit/template/00-context/goals.md +38 -0
- whykit/template/00-context/review-log.md +22 -0
- whykit/template/00-context/terminology.md +34 -0
- whykit/template/01-strategy/README.md +7 -0
- whykit/template/02-discoverability/README.md +10 -0
- whykit/template/03-website/README.md +7 -0
- whykit/template/04-automation/README.md +10 -0
- whykit/template/05-operations/README.md +19 -0
- whykit/template/06-decisions/README.md +11 -0
- whykit/template/06-decisions/decision-log.md +38 -0
- whykit/template/07-research/README.md +10 -0
- whykit/template/AGENTS.md +163 -0
- whykit/template/Home.md +57 -0
- whykit/template/INTEROP.md +117 -0
- whykit/template/OBSIDIAN.md +92 -0
- whykit/template/README.md +51 -0
- whykit/template/assets/README.md +6 -0
- whykit/template/reports/README.md +7 -0
- whykit/template/templates/README.md +7 -0
- whykit/template/templates/automation-spec-template.md +70 -0
- whykit/template/templates/decision-record-template.md +65 -0
- whykit/template/templates/document-template.md +55 -0
- whykit/template/templates/experiment-template.md +62 -0
- whykit/template/templates/meeting-note-template.md +47 -0
- whykit/template/templates/page-brief-template.md +63 -0
- whykit/template/templates/research-note-template.md +53 -0
- whykit/template/templates/source-ingestion-template.md +43 -0
- whykit/template/whykit.toml +39 -0
- whykit/trace.py +272 -0
- whykit/vault_index.py +190 -0
- whykit-0.3.1.dist-info/METADATA +162 -0
- whykit-0.3.1.dist-info/RECORD +84 -0
- whykit-0.3.1.dist-info/WHEEL +4 -0
- whykit-0.3.1.dist-info/entry_points.txt +3 -0
- whykit-0.3.1.dist-info/licenses/LICENSE +201 -0
- whykit-0.3.1.dist-info/licenses/NOTICE +13 -0
whykit/placeholders.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Placeholder prose that WhyKit itself writes into new decision records.
|
|
2
|
+
|
|
3
|
+
`whykit new decision` renders its skeleton from the constants below, and the
|
|
4
|
+
`decision.placeholder` lint rule reads the same constants, so the two cannot
|
|
5
|
+
drift apart. The hand-copied template in ``template/templates/`` is parsed at
|
|
6
|
+
lint time for the same reason: whatever prose ships there is what an adopter
|
|
7
|
+
forgets to replace.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import functools
|
|
12
|
+
import re
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# Section heading -> the paragraph `whykit new decision` writes under it.
|
|
16
|
+
SCAFFOLD_SECTION_PROMPTS: dict[str, str] = {
|
|
17
|
+
"Context": "What is true now, and what forces a choice?",
|
|
18
|
+
"Decision": "State the choice in one sentence.",
|
|
19
|
+
"Rationale": "Why this option, given the evidence and constraints?",
|
|
20
|
+
}
|
|
21
|
+
SCAFFOLD_EVIDENCE_TODO = "- TODO — add E-NNN references or explain why none apply."
|
|
22
|
+
ALTERNATIVES_HEADER = "| Alternative | Upside | Risk | Why rejected |"
|
|
23
|
+
ALTERNATIVES_EMPTY_ROW = "| | | | |"
|
|
24
|
+
|
|
25
|
+
# Sections whose prose is the decision itself: a placeholder or nothing at all
|
|
26
|
+
# here means the record does not say what was decided or why.
|
|
27
|
+
PROSE_SECTIONS = ("Context", "Decision", "Rationale")
|
|
28
|
+
|
|
29
|
+
TEMPLATE_PATH = Path(__file__).resolve().parent / "template" / "templates" / "decision-record-template.md"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def normalize(text: str) -> str:
|
|
33
|
+
"""Collapse whitespace so a re-wrapped placeholder still matches."""
|
|
34
|
+
return " ".join(text.split())
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def split_sections(lines: list[str]) -> list[tuple[str, int, int]]:
|
|
38
|
+
"""``(heading, first_body_line, end_line)`` for every ``## `` section.
|
|
39
|
+
|
|
40
|
+
Line indexes are 0-based and half-open; ``### `` subsections stay inside
|
|
41
|
+
their parent section.
|
|
42
|
+
"""
|
|
43
|
+
heads = [(index, line[3:].strip()) for index, line in enumerate(lines) if line.startswith("## ")]
|
|
44
|
+
sections = []
|
|
45
|
+
for position, (index, heading) in enumerate(heads):
|
|
46
|
+
end = heads[position + 1][0] if position + 1 < len(heads) else len(lines)
|
|
47
|
+
sections.append((heading, index + 1, end))
|
|
48
|
+
return sections
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def paragraphs(lines: list[str]) -> list[tuple[int, str]]:
|
|
52
|
+
"""Plain prose paragraphs as ``(first_line_index, normalized_text)``.
|
|
53
|
+
|
|
54
|
+
Headings, list items, table rows and quotes end a paragraph and are not
|
|
55
|
+
part of one.
|
|
56
|
+
"""
|
|
57
|
+
found: list[tuple[int, str]] = []
|
|
58
|
+
start: int | None = None
|
|
59
|
+
chunk: list[str] = []
|
|
60
|
+
for index, line in enumerate([*lines, ""]):
|
|
61
|
+
stripped = line.strip()
|
|
62
|
+
prose = bool(stripped) and not re.match(r"^(#|[-*+] |\d+[.)] |\||>)", stripped) and stripped not in {"-", "*", "+"}
|
|
63
|
+
if prose:
|
|
64
|
+
if start is None:
|
|
65
|
+
start = index
|
|
66
|
+
chunk.append(stripped)
|
|
67
|
+
elif chunk:
|
|
68
|
+
found.append((start or 0, normalize(" ".join(chunk))))
|
|
69
|
+
start, chunk = None, []
|
|
70
|
+
return found
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@functools.lru_cache(maxsize=1)
|
|
74
|
+
def template_section_prompts() -> dict[str, frozenset[str]]:
|
|
75
|
+
"""Placeholder paragraphs per section, from the scaffold and the shipped template."""
|
|
76
|
+
prompts: dict[str, set[str]] = {name: {normalize(text)} for name, text in SCAFFOLD_SECTION_PROMPTS.items()}
|
|
77
|
+
try:
|
|
78
|
+
lines = TEMPLATE_PATH.read_text(encoding="utf-8").splitlines()
|
|
79
|
+
except OSError:
|
|
80
|
+
lines = []
|
|
81
|
+
for heading, start, end in split_sections(lines):
|
|
82
|
+
for _, text in paragraphs(lines[start:end]):
|
|
83
|
+
prompts.setdefault(heading, set()).add(text)
|
|
84
|
+
return {name: frozenset(values) for name, values in prompts.items()}
|