@static-var/keystone 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,261 @@
1
+ #!/usr/bin/env python3
2
+ """Validate Keystone canonical source and packaging invariants."""
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ ROOT = Path(__file__).resolve().parents[1]
12
+ SKILL_DIR = ROOT / "skills" / "keystone"
13
+ SKILL = SKILL_DIR / "SKILL.md"
14
+ MODULE_DIR = SKILL_DIR / "modules"
15
+ PI_EXTENSION = ROOT / ".pi" / "extensions" / "keystone.ts"
16
+ ALLOWLIST = ROOT / "packaging.allowlist"
17
+ SUBAGENT_HELPER = MODULE_DIR / "helpers" / "subagents.md"
18
+ TARGET_HARNESSES = ("Pi", "Claude", "Codex", "T3", "OpenCode", "Copilot")
19
+ COMMON_PLAYBOOK_HEADINGS = (
20
+ "## Core principle",
21
+ "## Load when",
22
+ "## Not for",
23
+ "## Outcome contract",
24
+ "## Subagents and reasoning",
25
+ "## Hard rules",
26
+ "## Failure modes",
27
+ "## Output format",
28
+ )
29
+ MODULE_PLAYBOOK_HEADINGS = {
30
+ "build": ("## Modes", "## Process", "## Architecture taste checklist"),
31
+ "review": ("## Review passes", "## Severity rubric", "## Impact tracing", "## Security and regression checklist"),
32
+ "breakdown": ("## Modes", "## Process", "## Requirements inventory", "## Iteration layering", "## Task quality bar"),
33
+ }
34
+ DEFAULT_PLAYBOOK_HEADINGS = ("## Modes", "## Process")
35
+ FORBIDDEN_TRACKED = {
36
+ "docs",
37
+ "plans",
38
+ "index.html",
39
+ "styles.css",
40
+ }
41
+
42
+
43
+ def fail(message: str) -> None:
44
+ print(f"validate-keystone: {message}", file=sys.stderr)
45
+ raise SystemExit(1)
46
+
47
+
48
+ def tracked_files() -> list[str]:
49
+ try:
50
+ result = subprocess.run(
51
+ ["git", "ls-files"], cwd=ROOT, text=True, check=True, capture_output=True
52
+ )
53
+ except Exception:
54
+ return []
55
+ return [line for line in result.stdout.splitlines() if line]
56
+
57
+
58
+ def parse_frontmatter(text: str) -> dict[str, object]:
59
+ if not text.startswith("---\n"):
60
+ return {}
61
+ end = text.find("\n---", 4)
62
+ if end == -1:
63
+ return {}
64
+ data: dict[str, object] = {}
65
+ current_key = None
66
+ for raw in text[4:end].splitlines():
67
+ line = raw.rstrip()
68
+ if not line or line.lstrip().startswith("#"):
69
+ continue
70
+ if line.startswith(" - ") and current_key:
71
+ data.setdefault(current_key, []).append(line[4:].strip().strip('"\''))
72
+ continue
73
+ if ":" in line:
74
+ key, value = line.split(":", 1)
75
+ current_key = key.strip()
76
+ value = value.strip()
77
+ data[current_key] = [] if value == "" else value.strip('"\'')
78
+ return data
79
+
80
+
81
+ def check_skill() -> str:
82
+ if not SKILL_DIR.is_dir():
83
+ fail("missing canonical skill directory skills/keystone/")
84
+ if not SKILL.is_file():
85
+ fail("missing canonical skill file skills/keystone/SKILL.md")
86
+ text = SKILL.read_text()
87
+ metadata = parse_frontmatter(text)
88
+ name = metadata.get("name")
89
+ if name is None:
90
+ heading = re.search(r"^#\s+(.+)$", text, re.M)
91
+ name = heading.group(1).strip().lower() if heading else None
92
+ if str(name).lower() != "keystone":
93
+ fail("skill name must be keystone")
94
+ return text
95
+
96
+
97
+ def check_language(text: str) -> None:
98
+ public_plan = re.compile(r"(?<![\w/.-])/(?:plan)(?![\w/.-])|\bplanner\s+name\s+is\s+plan\b", re.I)
99
+ if public_plan.search(text):
100
+ fail("public /plan mention found; Keystone uses breakdown")
101
+ if not re.search(r"\bbreakdown\b", text, re.I):
102
+ fail("missing required breakdown terminology")
103
+
104
+
105
+ def check_references(text: str) -> None:
106
+ refs = sorted(set(re.findall(r"\(([^)]+\.(?:md|json|yaml|yml|txt|sh|py))\)", text)))
107
+ refs += sorted(set(re.findall(r"(?:^|\s)([A-Za-z0-9_./-]+\.(?:md|json|yaml|yml|txt|sh|py))", text)))
108
+ missing = []
109
+ for ref in refs:
110
+ if ref.startswith(("http://", "https://", "mailto:")) or ref.startswith("/"):
111
+ continue
112
+ candidate = (SKILL_DIR / ref).resolve()
113
+ try:
114
+ candidate.relative_to(ROOT)
115
+ except ValueError:
116
+ continue
117
+ if not candidate.exists():
118
+ missing.append(ref)
119
+ if missing:
120
+ fail("missing referenced module/gate files: " + ", ".join(sorted(set(missing))))
121
+
122
+
123
+ def routing_modules(text: str) -> set[str]:
124
+ return {
125
+ match.group(1)
126
+ for match in re.finditer(r"`modules/([a-z-]+)\.md`", text)
127
+ }
128
+
129
+
130
+ def check_product_modules(skill_text: str) -> None:
131
+ primary_modules = routing_modules(skill_text)
132
+ root_modules = {path.stem for path in MODULE_DIR.glob("*.md")}
133
+ extra = sorted(root_modules - primary_modules)
134
+ missing = sorted(primary_modules - root_modules)
135
+ if extra:
136
+ fail("module files not present in routing table: " + ", ".join(extra))
137
+ if missing:
138
+ fail("routing table modules missing files: " + ", ".join(missing))
139
+
140
+
141
+ def check_module_playbooks(skill_text: str) -> None:
142
+ primary_modules = routing_modules(skill_text)
143
+ pseudo_modules = {"tests", "package", "plan"}
144
+ allowed_terms = {"plan"} # allowed only as a concept; public /plan remains forbidden elsewhere.
145
+ for path in MODULE_DIR.glob("*.md"):
146
+ text = path.read_text()
147
+ invalid = sorted(
148
+ token for token in re.findall(r"`([a-z][a-z-]+)`", text)
149
+ if token in pseudo_modules and token not in primary_modules and token not in allowed_terms
150
+ )
151
+ if invalid:
152
+ fail(f"module {path.stem} references non-shipped pseudo-modules: " + ", ".join(invalid))
153
+ for name in sorted(primary_modules - {"router"}):
154
+ module = MODULE_DIR / f"{name}.md"
155
+ text = module.read_text()
156
+ required = COMMON_PLAYBOOK_HEADINGS + MODULE_PLAYBOOK_HEADINGS.get(
157
+ name,
158
+ DEFAULT_PLAYBOOK_HEADINGS,
159
+ )
160
+ missing = [
161
+ heading for heading in required
162
+ if re.search(rf"^{re.escape(heading)}\s*$", text, re.M) is None
163
+ ]
164
+ if missing:
165
+ fail(f"module {name} missing playbook headings: " + ", ".join(missing))
166
+
167
+
168
+ def check_subagent_helper(skill_text: str) -> None:
169
+ if not SUBAGENT_HELPER.is_file():
170
+ fail("missing subagent reasoning helper modules/helpers/subagents.md")
171
+ helper_text = SUBAGENT_HELPER.read_text()
172
+ missing_harnesses = [name for name in TARGET_HARNESSES if name not in helper_text]
173
+ if missing_harnesses:
174
+ fail("subagent helper missing target harnesses: " + ", ".join(missing_harnesses))
175
+ primary_modules = routing_modules(skill_text)
176
+ if not primary_modules:
177
+ fail("no primary modules found in Keystone routing table")
178
+ missing_modules = [
179
+ name for name in sorted(primary_modules)
180
+ if f"modules/{name}.md" not in helper_text
181
+ ]
182
+ if missing_modules:
183
+ fail("subagent helper missing module reasoning rows: " + ", ".join(missing_modules))
184
+ for name in sorted(primary_modules):
185
+ module = MODULE_DIR / f"{name}.md"
186
+ if not module.is_file():
187
+ fail(f"missing primary module file: modules/{name}.md")
188
+ if "## Subagents and reasoning" not in module.read_text():
189
+ fail(f"module missing subagent reasoning section: modules/{name}.md")
190
+
191
+
192
+ def check_ignored_not_tracked() -> None:
193
+ bad = []
194
+ for path in tracked_files():
195
+ first = path.split("/", 1)[0]
196
+ if path in FORBIDDEN_TRACKED or first in FORBIDDEN_TRACKED:
197
+ bad.append(path)
198
+ if bad:
199
+ fail("ignored artifacts are tracked: " + ", ".join(bad))
200
+
201
+
202
+ def allowlist_entries() -> set[str]:
203
+ if not ALLOWLIST.is_file():
204
+ fail("missing packaging.allowlist")
205
+ return {
206
+ line.strip()
207
+ for line in ALLOWLIST.read_text().splitlines()
208
+ if line.strip() and not line.lstrip().startswith("#")
209
+ }
210
+
211
+
212
+ def check_package_json() -> None:
213
+ package_path = ROOT / "package.json"
214
+ if not package_path.is_file():
215
+ fail("missing package.json")
216
+ package = json.loads(package_path.read_text())
217
+ for key in ("name", "version", "description", "license", "keywords"):
218
+ if key not in package:
219
+ fail(f"package.json missing {key}")
220
+ if package.get("name") != "@static-var/keystone":
221
+ fail('package.json name must be "@static-var/keystone" for npm/pi.dev publishing')
222
+ keywords = package.get("keywords", [])
223
+ if not isinstance(keywords, list) or "pi-package" not in keywords:
224
+ fail('package.json keywords must include "pi-package"')
225
+ files = package.get("files")
226
+ expected_files = allowlist_entries()
227
+ if set(files or []) != expected_files:
228
+ fail("package.json files must exactly match packaging.allowlist entries")
229
+ pi = package.get("pi", {})
230
+ if pi.get("skills") != ["./skills"]:
231
+ fail('package.json must set pi.skills to ["./skills"]')
232
+ if pi.get("extensions") != ["./.pi/extensions/keystone.ts"]:
233
+ fail('package.json must set pi.extensions to ["./.pi/extensions/keystone.ts"]')
234
+ if not PI_EXTENSION.is_file():
235
+ fail("missing Pi extension .pi/extensions/keystone.ts")
236
+ extension = PI_EXTENSION.read_text()
237
+ if 'registerCommand("keystone"' not in extension:
238
+ fail("Pi extension must register public /keystone command")
239
+ leaked = sorted(
240
+ name for name in routing_modules(check_skill())
241
+ if name != "keystone" and f'registerCommand("{name}"' in extension
242
+ )
243
+ if leaked:
244
+ fail("Pi extension exposes internal modules as slash commands: " + ", ".join(leaked))
245
+
246
+
247
+ def main() -> int:
248
+ text = check_skill()
249
+ check_language(text)
250
+ check_references(text)
251
+ check_product_modules(text)
252
+ check_module_playbooks(text)
253
+ check_subagent_helper(text)
254
+ check_ignored_not_tracked()
255
+ check_package_json()
256
+ print("validate-keystone: ok")
257
+ return 0
258
+
259
+
260
+ if __name__ == "__main__":
261
+ raise SystemExit(main())
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env python3
2
+ """Validate the Keystone release archive contents."""
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import zipfile
7
+ from pathlib import Path
8
+
9
+ ROOT = Path(__file__).resolve().parents[1]
10
+ ALLOWLIST = ROOT / "packaging.allowlist"
11
+
12
+ REQUIRED = {
13
+ "README.md",
14
+ "HOW_IT_WORKS.md",
15
+ "package.json",
16
+ "packaging.allowlist",
17
+ "Makefile",
18
+ "scripts/build-metadata.py",
19
+ "scripts/validate-keystone.py",
20
+ "scripts/validate-package.py",
21
+ "scripts/package-keystone.sh",
22
+ ".pi/extensions/keystone.ts",
23
+ "skills/keystone/SKILL.md",
24
+ "skills/keystone/modules/router.md",
25
+ "skills/keystone/modules/research.md",
26
+ "skills/keystone/modules/shape.md",
27
+ "skills/keystone/modules/breakdown.md",
28
+ "skills/keystone/modules/build.md",
29
+ "skills/keystone/modules/debug.md",
30
+ "skills/keystone/modules/review.md",
31
+ "skills/keystone/modules/ship.md",
32
+ "skills/keystone/modules/health.md",
33
+ "skills/keystone/modules/helpers/subagents.md",
34
+ "skills/keystone/modules/gates/isolation.md",
35
+ "skills/keystone/modules/gates/proof.md",
36
+ ".claude-plugin/plugin.json",
37
+ ".claude-plugin/marketplace.json",
38
+ ".codex-plugin/plugin.json",
39
+ ".agents/plugins/marketplace.json",
40
+ }
41
+ FORBIDDEN_NAMES = {
42
+ "index.html",
43
+ "styles.css",
44
+ "skill-engineering.md",
45
+ ".DS_Store",
46
+ }
47
+ FORBIDDEN_PREFIXES = (
48
+ "docs/",
49
+ "plans/",
50
+ "maintainers/",
51
+ "dist/",
52
+ ".git/",
53
+ ".keystone/plans/",
54
+ ".keystone/specs/",
55
+ ".keystone/tmp/",
56
+ "__pycache__/",
57
+ )
58
+ FORBIDDEN_SUFFIXES = (
59
+ ".pyc",
60
+ ".plan.md",
61
+ "-plan.md",
62
+ ".design.md",
63
+ "-design.md",
64
+ )
65
+
66
+
67
+ def fail(message: str) -> None:
68
+ print(f"validate-package: {message}", file=sys.stderr)
69
+ raise SystemExit(1)
70
+
71
+
72
+ def forbidden(path: str) -> bool:
73
+ base = path.rsplit("/", 1)[-1]
74
+ return (
75
+ base in FORBIDDEN_NAMES
76
+ or path.startswith(FORBIDDEN_PREFIXES)
77
+ or path.endswith(FORBIDDEN_SUFFIXES)
78
+ )
79
+
80
+
81
+ def expand_allowlist() -> set[str] | None:
82
+ if not ALLOWLIST.is_file():
83
+ return None
84
+ expanded: set[str] = set()
85
+ for raw in ALLOWLIST.read_text().splitlines():
86
+ path = raw.strip()
87
+ if not path or path.startswith("#"):
88
+ continue
89
+ if forbidden(path) or path in {"dist", "docs", "plans", "maintainers", ".git"}:
90
+ fail(f"forbidden allowlist entry: {path}")
91
+ candidate = ROOT / path
92
+ if not candidate.exists():
93
+ fail(f"allowlisted path missing: {path}")
94
+ if candidate.is_dir():
95
+ for child in candidate.rglob("*"):
96
+ if child.is_file():
97
+ rel = child.relative_to(ROOT).as_posix()
98
+ if not forbidden(rel):
99
+ expanded.add(rel)
100
+ else:
101
+ expanded.add(path)
102
+ return expanded
103
+
104
+
105
+ def main(argv: list[str]) -> int:
106
+ archive = Path(argv[1]) if len(argv) > 1 else Path("dist/keystone.zip")
107
+ if not archive.is_file():
108
+ fail(f"archive not found: {archive}")
109
+ with zipfile.ZipFile(archive) as zf:
110
+ names = {name for name in zf.namelist() if not name.endswith("/")}
111
+ missing = sorted(REQUIRED - names)
112
+ if missing:
113
+ fail("missing required files: " + ", ".join(missing))
114
+ forbidden_names = sorted(name for name in names if forbidden(name))
115
+ if forbidden_names:
116
+ fail("forbidden files present: " + ", ".join(forbidden_names))
117
+
118
+ expected = expand_allowlist()
119
+ if expected is None:
120
+ expected_list = archive.parent / "keystone.files"
121
+ if expected_list.is_file():
122
+ expected = {
123
+ line.strip()
124
+ for line in expected_list.read_text().splitlines()
125
+ if line.strip()
126
+ }
127
+ if expected is not None:
128
+ extra = sorted(names - expected)
129
+ absent = sorted(expected - names)
130
+ if extra:
131
+ fail("archive contains files outside expanded allowlist: " + ", ".join(extra))
132
+ if absent:
133
+ fail("expanded allowlist files missing from archive: " + ", ".join(absent))
134
+
135
+ print("validate-package: ok")
136
+ return 0
137
+
138
+
139
+ if __name__ == "__main__":
140
+ raise SystemExit(main(sys.argv))
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: keystone
3
+ description: Use when the user invokes /keystone or asks Keystone to route product, research, writing, UI, design, planning, implementation, debugging, review, shipping, or health work through its canonical orchestrator.
4
+ ---
5
+
6
+ # Keystone
7
+
8
+ Keystone is a router/orchestrator skill. It selects the right internal module for the user's current request, carries handoffs between modules, and keeps only one public entrypoint: `/keystone`.
9
+
10
+ ## Routing rule
11
+
12
+ Load exactly one primary module for the current task. Load gates or helper modules only when the primary module explicitly needs them. Do not expose internal modules as public slash commands. Actively route the work: name the selected module, preserve the current handoff packet, and state the next gate or check before taking irreversible action.
13
+
14
+ ## Orchestration sequences
15
+
16
+ Use these common paths unless the user's immediate next action clearly points elsewhere:
17
+
18
+ - Product or feature work: `research -> shape -> breakdown -> build -> review -> ship`.
19
+ - Existing plan or approved design: `breakdown -> build -> review -> ship`.
20
+ - Direct implementation request: `build -> review -> ship`, loading `breakdown` first only when execution order or verification is unclear.
21
+ - Debug loop: `debug -> build -> proof gate -> review`; return to `debug` if proof fails or new symptoms appear.
22
+ - Health finding that needs repair: `health -> build` for contained fixes, or `health -> review` when the finding needs independent validation before mutation.
23
+
24
+ Do not add these as shipped modules; they are routing sequences over the existing nine modules.
25
+
26
+ ## Handoff packet contract
27
+
28
+ Whenever one module hands work to another, carry a concise packet:
29
+
30
+ - `source module`: module producing the handoff
31
+ - `target module`: module to load next
32
+ - `goal`: one-sentence outcome for the next module
33
+ - `evidence`: facts, command output, user decisions, or citations already established
34
+ - `files`: relevant paths and whether they are read-only, mutable, or protected
35
+ - `risks`: known uncertainty, skipped checks, safety constraints, or rollback concerns
36
+ - `next check`: the gate, command, question, or review the target should run first
37
+
38
+ If the user overrides a gate, review, or sequence step, record the explicit override in the packet with the risk accepted. Never silently bypass hard safety constraints such as protected files, secret handling, isolation requirements, destructive operations, or policy restrictions; stop and ask or refuse as appropriate.
39
+
40
+ ## Re-entry contract
41
+
42
+ When a module temporarily routes to another module and then resumes, the returning module must receive the handoff packet plus the result.
43
+
44
+ - Build -> Debug -> Build: carry the failing symptom, reproduction command, suspected root cause, files inspected, fix recommendation, and proof command. On return to Build, rerun isolation if mutation scope changed, rerun proof for the original symptom, and rerun review if the fix touches behavior, public API, security, data, or release surfaces.
45
+ - Build -> Research/Shape/Breakdown -> Build: carry the clarified decision or plan delta. On return, rerun the relevant Build preflight only for changed scope; do not redo unchanged gates without reason.
46
+ - Review -> Debug/Build -> Review: carry review findings and changed files. On return, review the addressed findings and any newly touched risk area.
47
+ - Health -> Build/Review: carry health evidence, severity, and recommended first check. Build may fix only contained, well-evidenced issues; uncertain or broad health findings go to Review first.
48
+
49
+ ## Subagents and reasoning
50
+
51
+ When a module may delegate work, consult `modules/helpers/subagents.md` for host capability, safe delegation rules, and the recommended reasoning level for each Keystone module. If the host cannot enforce per-subagent reasoning, include the desired level in the subagent prompt or work inline.
52
+
53
+ ## Routing table
54
+
55
+ | User intent | Primary module |
56
+ |---|---|
57
+ | Choose the right Keystone path, classify intent, resolve module ambiguity | `modules/router.md` |
58
+ | Read, inspect, summarize, extract, research, compare sources, investigate options | `modules/research.md` |
59
+ | Draft, rewrite, UI/UX, visual direction, product decisions, architecture, feature scope | `modules/shape.md` |
60
+ | Breakdown approved design into executable tasks and verification steps | `modules/breakdown.md` |
61
+ | Implement or change files/code/content | `modules/build.md` |
62
+ | Diagnose failures, failing behavior, errors, bug reports, regressions, unexpected behavior | `modules/debug.md` |
63
+ | Review work without changing it | `modules/review.md` |
64
+ | Finalize completed work for delivery or integration | `modules/ship.md` |
65
+ | Agent/tooling health, repository condition, skill drift, context rot, risks | `modules/health.md` |
66
+
67
+ ## Ambiguity rule
68
+
69
+ If several modules could apply, choose the module matching the next irreversible action. If still ambiguous, ask one concise clarifying question before loading a primary module.