devlyn-cli 4.0.1 → 4.2.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.
- package/AGENTS.md +11 -95
- package/CLAUDE.md +7 -160
- package/LICENSE +21 -0
- package/README.md +83 -114
- package/bin/devlyn.js +330 -472
- package/bin/instruction-templates.json +120 -2
- package/bin/instructions.js +35 -8
- package/config/skills/_shared/adapters/claude.md +8 -85
- package/config/skills/_shared/adapters/codex.md +14 -34
- package/config/skills/_shared/adapters/omp.md +1 -11
- package/config/skills/_shared/codex-monitored.sh +4 -54
- package/config/skills/_shared/engine-doctor.sh +18 -58
- package/config/skills/_shared/engine-preflight.md +15 -47
- package/config/skills/_shared/expected-contract.py +242 -0
- package/config/skills/_shared/expected.schema.json +10 -95
- package/config/skills/_shared/invocation-receipt.py +26 -757
- package/config/skills/_shared/platform-support.py +37 -28
- package/config/skills/_shared/role-config.py +100 -421
- package/config/skills/_shared/runtime-principles.md +17 -93
- package/config/skills/_shared/task-complete.py +490 -285
- package/config/skills/_shared/task-completion.md +235 -0
- package/config/skills/devlyn-engines/SKILL.md +9 -11
- package/config/skills/devlyn-ideate/SKILL.md +57 -107
- package/config/skills/devlyn-ideate/references/elicitation.md +18 -99
- package/config/skills/devlyn-ideate/references/loop.md +110 -0
- package/config/skills/devlyn-ideate/references/package-format.md +128 -0
- package/config/skills/devlyn-ideate/scripts/acceptance.py +795 -0
- package/config/skills/devlyn-ideate/scripts/queue.py +1780 -0
- package/package.json +2 -3
- package/config/commit-conventions.md +0 -28
- package/config/skills/_shared/adapters/README.md +0 -74
- package/config/skills/_shared/adapters/grok.md +0 -100
- package/config/skills/_shared/archive_run.py +0 -969
- package/config/skills/_shared/codex-config.md +0 -81
- package/config/skills/_shared/collect-codex-findings.py +0 -505
- package/config/skills/_shared/finish-gate.py +0 -521
- package/config/skills/_shared/grok-anchor-guard.py +0 -78
- package/config/skills/_shared/judge-output-parser.py +0 -200
- package/config/skills/_shared/judge-role-evidence.py +0 -320
- package/config/skills/_shared/phase-prompt-render.py +0 -183
- package/config/skills/_shared/process-evidence.py +0 -926
- package/config/skills/_shared/resolve-bootstrap.py +0 -1410
- package/config/skills/_shared/resolve-stop-hook.py +0 -359
- package/config/skills/_shared/run-bounded.py +0 -58
- package/config/skills/_shared/spec-verify-check.py +0 -5083
- package/config/skills/_shared/state-phase-write.py +0 -5989
- package/config/skills/_shared/terminal-claim-check.py +0 -777
- package/config/skills/_shared/verify-merge-findings.py +0 -3528
- package/config/skills/devlyn-design-ui/SKILL.md +0 -650
- package/config/skills/devlyn-ideate/references/from-spec-mode.md +0 -61
- package/config/skills/devlyn-ideate/references/project-mode.md +0 -80
- package/config/skills/devlyn-ideate/references/spec-template.md +0 -121
- package/config/skills/devlyn-ideate/references/templates/decision.md +0 -48
- package/config/skills/devlyn-ideate/references/templates/roadmap.md +0 -50
- package/config/skills/devlyn-ideate/references/templates/vision.md +0 -44
- package/config/skills/devlyn-queue/SKILL.md +0 -66
- package/config/skills/devlyn-queue/scripts/append.py +0 -94
- package/config/skills/devlyn-resolve/SKILL.md +0 -379
- package/config/skills/devlyn-resolve/references/free-form-mode.md +0 -71
- package/config/skills/devlyn-resolve/references/outer-loop.md +0 -23
- package/config/skills/devlyn-resolve/references/phases/build-gate.md +0 -75
- package/config/skills/devlyn-resolve/references/phases/cleanup.md +0 -33
- package/config/skills/devlyn-resolve/references/phases/implement.md +0 -44
- package/config/skills/devlyn-resolve/references/phases/plan.md +0 -39
- package/config/skills/devlyn-resolve/references/phases/probe-derive.md +0 -261
- package/config/skills/devlyn-resolve/references/phases/surface-close.md +0 -34
- package/config/skills/devlyn-resolve/references/phases/verify.md +0 -310
- package/config/skills/devlyn-resolve/references/state-schema.md +0 -152
- package/config/skills/devlyn-resolve/references/task-completion.md +0 -160
- package/config/templates/prompt-templates.md +0 -71
- package/config/templates/template-feature.spec.md +0 -255
- package/config/templates/template-product-spec.md +0 -680
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import errno
|
|
7
|
-
import json
|
|
8
7
|
import os
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
import re
|
|
@@ -62,8 +61,15 @@ def file_lock(path, *, blocking=False):
|
|
|
62
61
|
fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
|
|
63
62
|
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
SHIM_NODE = re.compile(r'IF EXIST "%(?:~dp0|dp0%)\\node\.exe"')
|
|
65
|
+
SHIM_RUN = re.compile(r'(?:"%_prog%"|"%~dp0\\node\.exe"|\bnode)\s+"%(?:~dp0|dp0%)\\([^"\r\n]+)"\s+%\*')
|
|
66
|
+
SHIM_SET = re.compile(r'(?im)^[ \t]*@?SET[ \t]+"?([^=" \t]+)=')
|
|
67
|
+
NPM_CLI = re.compile(r'SET "(NP[MX]_CLI_JS)=%~dp0\\([^"\r\n]+)"')
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def native_argv(argv, cwd=None):
|
|
71
|
+
"""Resolve argv as Windows does, for a command that will run in cwd; a Node command shim runs its Node and script
|
|
72
|
+
directly, never cmd /c."""
|
|
67
73
|
if os.name != "nt":
|
|
68
74
|
return list(argv)
|
|
69
75
|
binary = shutil.which(argv[0])
|
|
@@ -72,33 +78,36 @@ def native_argv(argv):
|
|
|
72
78
|
shim = Path(binary)
|
|
73
79
|
if shim.suffix.lower() not in {".cmd", ".bat"}:
|
|
74
80
|
return [binary, *argv[1:]]
|
|
75
|
-
packages = {"codex": "@openai/codex", "claude": "@anthropic-ai/claude-code"}
|
|
76
|
-
package = packages.get(shim.stem.lower())
|
|
77
|
-
if shim.suffix.lower() != ".cmd" or package is None:
|
|
78
|
-
raise OSError(f"unsupported native command shim: {shim}")
|
|
79
|
-
root = (shim.parent.parent if shim.parent.name == ".bin" else shim.parent / "node_modules") / package
|
|
80
|
-
try:
|
|
81
|
-
manifest = json.loads((root / "package.json").read_text(encoding="utf-8"))
|
|
82
|
-
except (ValueError, UnicodeError) as exc:
|
|
83
|
-
raise OSError(f"invalid npm engine manifest: {shim}") from exc
|
|
84
|
-
if not isinstance(manifest, dict):
|
|
85
|
-
raise OSError(f"invalid npm engine manifest: {shim}")
|
|
86
|
-
entry = manifest.get("bin")
|
|
87
|
-
entry = entry.get(shim.stem.lower()) if isinstance(entry, dict) else entry
|
|
88
|
-
if manifest.get("name") != package or not isinstance(entry, str):
|
|
89
|
-
raise OSError(f"invalid npm engine bin: {shim}")
|
|
90
|
-
target = (root / entry).resolve(strict=True)
|
|
91
|
-
if not target.is_relative_to(root.resolve()) or target.suffix not in {".js", ".cjs", ".mjs"}:
|
|
92
|
-
raise OSError(f"unsupported npm engine bin: {target}")
|
|
93
|
-
raw = shim.read_text(encoding="utf-8")
|
|
94
|
-
matches = re.findall(r'"%_prog%"\s+"%dp0%\\([^"\r\n]+)"\s+%\*', raw)
|
|
95
|
-
if len(matches) != 1 or (shim.parent / matches[0]).resolve() != target:
|
|
96
|
-
raise OSError(f"unrecognized npm engine shim: {shim}")
|
|
97
81
|
node = shim.parent / "node.exe"
|
|
98
|
-
|
|
99
|
-
|
|
82
|
+
node = str(node) if node.is_file() else shutil.which("node.exe")
|
|
83
|
+
script = shim_script(shim, shim.read_text(encoding="utf-8", errors="replace"), node, cwd)
|
|
84
|
+
if script is None:
|
|
85
|
+
raise OSError(f"unsupported native command shim: {shim}; use argv with an explicit interpreter, such as node and the script")
|
|
86
|
+
if node is None:
|
|
100
87
|
raise FileNotFoundError(f"node.exe unavailable for {shim}")
|
|
101
|
-
return [
|
|
88
|
+
return [node, str(script.resolve(strict=True)), *argv[1:]]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def shim_script(shim, raw, node, cwd=None):
|
|
92
|
+
"""The script a Node command shim runs, as cmd.exe would resolve it, else None: the one target of an npm, pnpm
|
|
93
|
+
or corepack cmd-shim that sets no environment, or for Node.js's own npm.cmd and npx.cmd the global prefix's
|
|
94
|
+
npm when installed there, else the bundled one."""
|
|
95
|
+
targets = set(SHIM_RUN.findall(raw))
|
|
96
|
+
if SHIM_NODE.search(raw) and len(targets) == 1 and set(SHIM_SET.findall(raw)) <= {"dp0", "_prog", "PATHEXT"}:
|
|
97
|
+
return shim.parent / targets.pop().replace("\\", "/")
|
|
98
|
+
cli = NPM_CLI.search(raw)
|
|
99
|
+
if not cli or f'"%NODE_EXE%" "%{cli[1]}%" %*' not in raw:
|
|
100
|
+
return None
|
|
101
|
+
prefix_js = re.search(r'SET "NPM_PREFIX_JS=%~dp0\\([^"\r\n]+)"', raw)
|
|
102
|
+
global_cli = re.search(rf'SET "NPM_PREFIX_{cli[1]}=%%F\\([^"\r\n]+)"', raw)
|
|
103
|
+
if prefix_js and global_cli and node:
|
|
104
|
+
# As the shim's FOR /F does: the last nonempty line npm-prefix.js prints names the global prefix.
|
|
105
|
+
output = subprocess.run([node, str(shim.parent / prefix_js[1].replace("\\", "/"))], cwd=cwd, capture_output=True,
|
|
106
|
+
text=True, encoding="utf-8").stdout
|
|
107
|
+
prefix = [line for line in output.splitlines() if line]
|
|
108
|
+
if prefix and (installed := Path(prefix[-1]) / global_cli[1].replace("\\", "/")).is_file():
|
|
109
|
+
return installed
|
|
110
|
+
return shim.parent / cli[2].replace("\\", "/")
|
|
102
111
|
|
|
103
112
|
|
|
104
113
|
def system_exit_code(code):
|