@christang/keel 5.2.2 → 5.2.4
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/assets/bootstrap/AGENTS.md +1 -1
- package/bin/keel.js +31 -2
- package/package.json +1 -1
- package/plugins/keel/.claude-plugin/plugin.json +1 -1
- package/plugins/keel/.codex-plugin/plugin.json +1 -1
- package/scripts/install_to_repo.py +45 -1
- package/scripts/validate_plugin.py +1032 -22
- package/src/core/capabilities.js +25 -5
- package/src/core/gates.js +70 -24
- package/src/core/guard.js +8 -0
- package/src/core/task-contract.js +127 -14
package/bin/keel.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
runGate,
|
|
16
16
|
} = require("../src/core/gates");
|
|
17
17
|
const {
|
|
18
|
+
isKeelSourceRepo,
|
|
18
19
|
probeCapabilities,
|
|
19
20
|
renderCapabilities,
|
|
20
21
|
} = require("../src/core/capabilities");
|
|
@@ -1202,7 +1203,12 @@ function printTargetSurface(repo, target) {
|
|
|
1202
1203
|
sourceDetail = `plugin source unreadable at ${manifestRelative}`;
|
|
1203
1204
|
}
|
|
1204
1205
|
}
|
|
1205
|
-
|
|
1206
|
+
// Development-only check: plugins/keel/ exists only in Keel's own source
|
|
1207
|
+
// repository, so in a consuming project it is permanently `missing` and
|
|
1208
|
+
// there is nothing the author can do about it.
|
|
1209
|
+
if (isKeelSourceRepo(repo)) {
|
|
1210
|
+
printDoctorLine("native plugin source", sourceStatus, sourceDetail);
|
|
1211
|
+
}
|
|
1206
1212
|
printDoctorLine(
|
|
1207
1213
|
"native plugin runtime",
|
|
1208
1214
|
"manual",
|
|
@@ -1229,7 +1235,12 @@ function printTargetSurface(repo, target) {
|
|
|
1229
1235
|
printDoctorLine(
|
|
1230
1236
|
"Keel behavioral skills",
|
|
1231
1237
|
"plugin",
|
|
1232
|
-
|
|
1238
|
+
isKeelSourceRepo(repo)
|
|
1239
|
+
? "keel-* skills are delivered by the installed Keel plugin (see native "
|
|
1240
|
+
+ "plugin status above); install the plugin if it is missing"
|
|
1241
|
+
: "keel-* skills are delivered by the installed Keel plugin; verify it "
|
|
1242
|
+
+ "with the runtime's own plugin listing, since Keel cannot observe "
|
|
1243
|
+
+ "installation from this repository"
|
|
1233
1244
|
);
|
|
1234
1245
|
|
|
1235
1246
|
const commands = commandSurfaceForTarget(target, repo);
|
|
@@ -1333,10 +1344,28 @@ function runDoctor(options) {
|
|
|
1333
1344
|
printTargetSurface(repo, options.target);
|
|
1334
1345
|
printLensSurface(repo, options.target);
|
|
1335
1346
|
printFastPrePushSurface(repo);
|
|
1347
|
+
printSourceRepoCliResolution(repo);
|
|
1336
1348
|
|
|
1337
1349
|
return checkStatus;
|
|
1338
1350
|
}
|
|
1339
1351
|
|
|
1352
|
+
// Only meaningful in Keel's own repository: a bare `keel` resolves to the
|
|
1353
|
+
// installed package, so gate commands verify the released CLI rather than the
|
|
1354
|
+
// working tree. The failure mode is a silently stale result, not an error, so
|
|
1355
|
+
// it is worth stating rather than leaving to be discovered.
|
|
1356
|
+
function printSourceRepoCliResolution(repo) {
|
|
1357
|
+
if (!isKeelSourceRepo(repo)) return;
|
|
1358
|
+
process.stdout.write("\nKeel source repository:\n");
|
|
1359
|
+
printDoctorLine(
|
|
1360
|
+
"gate CLI resolution",
|
|
1361
|
+
"advisory",
|
|
1362
|
+
"a bare `keel` runs the installed package, so gate results will not "
|
|
1363
|
+
+ "reflect uncommitted changes to gate, contract, or capability code; "
|
|
1364
|
+
+ "run `node bin/keel.js gate <stage> . --change <c> --task <t>` "
|
|
1365
|
+
+ "instead — the repository argument is required"
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1340
1369
|
function readFastCheck(repo) {
|
|
1341
1370
|
const configPath = path.join(repo, "keel", "config.yaml");
|
|
1342
1371
|
if (!fs.existsSync(configPath)) return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keel",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.4",
|
|
4
4
|
"description": "Keel OpenSpec execution discipline: stateless continuity, task capsules, deterministic gates, and expectation alignment for Codex and Claude Code.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TanglmChris",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keel",
|
|
3
|
-
"version": "5.2.
|
|
3
|
+
"version": "5.2.4",
|
|
4
4
|
"description": "Keel OpenSpec execution discipline: stateless continuity, task capsules, deterministic gates, and expectation alignment for Codex and Claude Code.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "TanglmChris",
|
|
@@ -33,6 +33,14 @@ KEEL_CONFIG_TEMPLATE = (
|
|
|
33
33
|
"# Example:\n"
|
|
34
34
|
"# fast_check: npm test -- --fast\n"
|
|
35
35
|
)
|
|
36
|
+
KEEL_GITIGNORE_PATH = KEEL_ROOT / ".gitignore"
|
|
37
|
+
KEEL_GITIGNORE_TEMPLATE = (
|
|
38
|
+
"# Keel local state. keel/ otherwise holds project content that belongs in\n"
|
|
39
|
+
"# version control (config.yaml, CHANGELOG.md, archive/); the write-guard\n"
|
|
40
|
+
"# manifest is per-clone session state that a gate run writes and\n"
|
|
41
|
+
"# `keel guard clear` removes.\n"
|
|
42
|
+
"guard.json\n"
|
|
43
|
+
)
|
|
36
44
|
GITHOOKS_DIR = ".githooks"
|
|
37
45
|
PRE_PUSH_PATH = Path(GITHOOKS_DIR) / "pre-push"
|
|
38
46
|
OPENSPEC_ROOT = Path("openspec")
|
|
@@ -222,6 +230,14 @@ def keel_config_action() -> InstallAction:
|
|
|
222
230
|
)
|
|
223
231
|
|
|
224
232
|
|
|
233
|
+
def keel_gitignore_action() -> InstallAction:
|
|
234
|
+
return InstallAction(
|
|
235
|
+
relative_path=KEEL_GITIGNORE_PATH,
|
|
236
|
+
content=KEEL_GITIGNORE_TEMPLATE,
|
|
237
|
+
strategy="keel-config-scaffold",
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
225
241
|
def read_fast_check(repo: Path) -> str | None:
|
|
226
242
|
"""Return the project's declared fast inner-loop command, or None.
|
|
227
243
|
|
|
@@ -432,11 +448,38 @@ def unmanaged_keel_content_warning(repo: Path, relative_path: str) -> bool:
|
|
|
432
448
|
return False
|
|
433
449
|
|
|
434
450
|
|
|
451
|
+
KEEL_PACKAGE_NAME = "@christang/keel"
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def is_keel_source_repo(repo: Path) -> bool:
|
|
455
|
+
"""Whether `repo` is Keel's own source repository.
|
|
456
|
+
|
|
457
|
+
Mirrors `isKeelSourceRepo` in src/core/capabilities.js. Both signals are
|
|
458
|
+
required so a project that merely vendors a plugins/keel/ directory is not
|
|
459
|
+
misclassified as Keel's own source.
|
|
460
|
+
"""
|
|
461
|
+
try:
|
|
462
|
+
manifest = json.loads((repo / "package.json").read_text(encoding="utf-8"))
|
|
463
|
+
except (OSError, ValueError):
|
|
464
|
+
return False
|
|
465
|
+
if manifest.get("name") != KEEL_PACKAGE_NAME:
|
|
466
|
+
return False
|
|
467
|
+
return (repo / "plugins" / "keel").is_dir()
|
|
468
|
+
|
|
469
|
+
|
|
435
470
|
def collect_actions(repo: Path, target: str) -> list[InstallAction]:
|
|
436
471
|
actions: list[InstallAction] = []
|
|
437
472
|
targets = target_set(target)
|
|
438
473
|
|
|
439
|
-
|
|
474
|
+
# Keel's own AGENTS.md carries the full protocol that the validation suite
|
|
475
|
+
# asserts on; the packaged bootstrap is the shorter consumer-facing text.
|
|
476
|
+
# Writing it here replaces the protocol and turns the repository red.
|
|
477
|
+
if is_keel_source_repo(repo):
|
|
478
|
+
print(
|
|
479
|
+
"skip AGENTS.md: Keel source repository, whose AGENTS.md carries "
|
|
480
|
+
"the full protocol; the consumer bootstrap is not written here"
|
|
481
|
+
)
|
|
482
|
+
elif not unmanaged_keel_content_warning(repo, "AGENTS.md"):
|
|
440
483
|
actions.append(
|
|
441
484
|
managed_file_action("AGENTS.md", PACKAGE_ROOT / BOOTSTRAP_ASSET)
|
|
442
485
|
)
|
|
@@ -445,6 +488,7 @@ def collect_actions(repo: Path, target: str) -> list[InstallAction]:
|
|
|
445
488
|
|
|
446
489
|
actions.append(openspec_config_action())
|
|
447
490
|
actions.append(keel_config_action())
|
|
491
|
+
actions.append(keel_gitignore_action())
|
|
448
492
|
actions.extend(openspec_schema_actions())
|
|
449
493
|
return actions
|
|
450
494
|
|