devlyn-cli 3.3.1 → 4.0.1
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 +8 -8
- package/CLAUDE.md +17 -17
- package/README.md +43 -30
- package/bin/devlyn.js +123 -105
- package/bin/instruction-templates.json +26 -0
- package/config/skills/_shared/adapters/README.md +1 -1
- package/config/skills/_shared/adapters/omp.md +2 -2
- package/config/skills/_shared/archive_run.py +2 -2
- package/config/skills/_shared/codex-config.md +15 -15
- package/config/skills/_shared/engine-doctor.sh +1 -1
- package/config/skills/_shared/engine-preflight.md +5 -5
- package/config/skills/_shared/finish-gate.py +1 -1
- package/config/skills/_shared/resolve-bootstrap.py +2 -2
- package/config/skills/_shared/runtime-principles.md +6 -6
- package/config/skills/_shared/spec-verify-check.py +3 -3
- package/config/skills/_shared/task-complete.py +1 -1
- package/config/skills/{devlyn:design-ui → devlyn-design-ui}/SKILL.md +1 -1
- package/config/skills/{devlyn:engines → devlyn-engines}/SKILL.md +37 -11
- package/config/skills/{devlyn:ideate → devlyn-ideate}/SKILL.md +42 -24
- package/config/skills/{devlyn:ideate → devlyn-ideate}/references/elicitation.md +2 -2
- package/config/skills/{devlyn:ideate → devlyn-ideate}/references/from-spec-mode.md +2 -2
- package/config/skills/{devlyn:ideate → devlyn-ideate}/references/project-mode.md +5 -5
- package/config/skills/{devlyn:ideate → devlyn-ideate}/references/spec-template.md +1 -1
- package/config/skills/{devlyn:queue → devlyn-queue}/SKILL.md +39 -16
- package/config/skills/devlyn-queue/scripts/append.py +94 -0
- package/config/skills/{devlyn:resolve → devlyn-resolve}/SKILL.md +38 -19
- package/config/skills/{devlyn:resolve → devlyn-resolve}/references/free-form-mode.md +3 -3
- package/config/skills/{devlyn:resolve → devlyn-resolve}/references/outer-loop.md +1 -1
- package/config/skills/{devlyn:resolve → devlyn-resolve}/references/state-schema.md +1 -1
- package/config/skills/{devlyn:resolve → devlyn-resolve}/references/task-completion.md +9 -2
- package/config/templates/prompt-templates.md +2 -2
- package/optional-skills/better-auth-setup/SKILL.md +24 -18
- package/optional-skills/{devlyn:pencil-pull → devlyn-pencil-pull}/SKILL.md +1 -1
- package/optional-skills/{devlyn:pencil-push → devlyn-pencil-push}/SKILL.md +1 -1
- package/optional-skills/{devlyn:reap → devlyn-reap}/SKILL.md +36 -5
- package/optional-skills/{devlyn:reap → devlyn-reap}/scripts/reap.sh +1 -1
- package/optional-skills/{devlyn:reap → devlyn-reap}/scripts/scan.sh +1 -1
- package/optional-skills/dokkit/COMMANDS.md +4 -4
- package/optional-skills/dokkit/EXPORT.md +3 -3
- package/optional-skills/dokkit/FILLING.md +4 -4
- package/optional-skills/dokkit/IMAGE-SOURCING.md +2 -2
- package/optional-skills/dokkit/INGESTION.md +3 -3
- package/optional-skills/dokkit/PIPELINE.md +5 -4
- package/optional-skills/dokkit/SKILL.md +35 -5
- package/optional-skills/dokkit/STATE.md +1 -1
- package/optional-skills/dokkit/references/image-opportunity-heuristics.md +1 -1
- package/optional-skills/generate-skill/REFERENCE.md +10 -13
- package/optional-skills/generate-skill/SKILL.md +13 -5
- package/package.json +3 -1
- /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/decision.md +0 -0
- /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/roadmap.md +0 -0
- /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/vision.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/build-gate.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/cleanup.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/implement.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/plan.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/probe-derive.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/surface-close.md +0 -0
- /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/verify.md +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Append one normalized pending intent to the project queue."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
QUEUE_PATH = Path("docs/specs/queue.md")
|
|
13
|
+
LOCK_PATH = Path(".devlyn/queue.lock")
|
|
14
|
+
# Each `add` writes its own handoff file, so two adds never consume each other's intent.
|
|
15
|
+
HANDOFF = re.compile(r"\.devlyn/queue-intent-[A-Za-z0-9._-]+\.txt")
|
|
16
|
+
MINIMAL_HEADER = b"# Intent Queue\n\n"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@contextmanager
|
|
20
|
+
def queue_lock():
|
|
21
|
+
"""One writer at a time across processes, from creating the queue to the append."""
|
|
22
|
+
LOCK_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
fd = os.open(LOCK_PATH, os.O_RDWR | os.O_CREAT, 0o644)
|
|
24
|
+
try:
|
|
25
|
+
if os.name == "nt":
|
|
26
|
+
import msvcrt
|
|
27
|
+
|
|
28
|
+
# The CRT locks bytes that exist; give a new lock file its one byte.
|
|
29
|
+
if os.fstat(fd).st_size == 0:
|
|
30
|
+
os.write(fd, b"\0")
|
|
31
|
+
os.lseek(fd, 0, os.SEEK_SET)
|
|
32
|
+
msvcrt.locking(fd, msvcrt.LK_LOCK, 1)
|
|
33
|
+
try:
|
|
34
|
+
yield
|
|
35
|
+
finally:
|
|
36
|
+
os.lseek(fd, 0, os.SEEK_SET)
|
|
37
|
+
msvcrt.locking(fd, msvcrt.LK_UNLCK, 1)
|
|
38
|
+
else:
|
|
39
|
+
import fcntl
|
|
40
|
+
|
|
41
|
+
fcntl.flock(fd, fcntl.LOCK_EX)
|
|
42
|
+
yield
|
|
43
|
+
finally:
|
|
44
|
+
os.close(fd)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def append_intent(queue_path, intent):
|
|
48
|
+
normalized = " ".join(intent.split())
|
|
49
|
+
if not normalized:
|
|
50
|
+
raise ValueError("intent must contain non-whitespace text")
|
|
51
|
+
item = f"- [ ] {normalized}\n".encode("utf-8")
|
|
52
|
+
|
|
53
|
+
with queue_lock():
|
|
54
|
+
if not queue_path.exists():
|
|
55
|
+
queue_path.parent.mkdir(parents=True, exist_ok=True)
|
|
56
|
+
queue_path.write_bytes(MINIMAL_HEADER + item)
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
with queue_path.open("rb") as queue:
|
|
60
|
+
queue.seek(0, 2)
|
|
61
|
+
size = queue.tell()
|
|
62
|
+
if size:
|
|
63
|
+
queue.seek(-1, 2)
|
|
64
|
+
ends_with_newline = queue.read(1) == b"\n"
|
|
65
|
+
else:
|
|
66
|
+
ends_with_newline = True
|
|
67
|
+
|
|
68
|
+
prefix = b"" if ends_with_newline else b"\n"
|
|
69
|
+
with queue_path.open("ab") as queue:
|
|
70
|
+
queue.write(prefix + item)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def main(argv=None):
|
|
74
|
+
parser = argparse.ArgumentParser(
|
|
75
|
+
description="Append one pending intent at the physical end of docs/specs/queue.md."
|
|
76
|
+
)
|
|
77
|
+
parser.add_argument("handoff", type=Path, help=".devlyn/queue-intent-<unique>.txt, relative to the project root")
|
|
78
|
+
args = parser.parse_args(argv)
|
|
79
|
+
if not HANDOFF.fullmatch(args.handoff.as_posix()):
|
|
80
|
+
parser.error(f"handoff must be .devlyn/queue-intent-<unique>.txt: {args.handoff}")
|
|
81
|
+
try:
|
|
82
|
+
try:
|
|
83
|
+
intent = args.handoff.read_text(encoding="utf-8")
|
|
84
|
+
finally:
|
|
85
|
+
args.handoff.unlink(missing_ok=True)
|
|
86
|
+
append_intent(QUEUE_PATH, intent)
|
|
87
|
+
except (OSError, UnicodeError, ValueError) as error:
|
|
88
|
+
print(f"queue add failed: {error}", file=sys.stderr)
|
|
89
|
+
return 1
|
|
90
|
+
return 0
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if __name__ == "__main__":
|
|
94
|
+
raise SystemExit(main())
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: devlyn
|
|
2
|
+
name: devlyn-resolve
|
|
3
3
|
description: Default to direct work after inspecting scope and verification, including bounded multi-file changes. Use full resolve for explicit resolve/spec-mode requests, queue drains, or concrete interacting requirements or verification too complex for the current context. Domain labels, file count, spec documents and quoted skill paths alone do not trigger it. Investigate or clarify missing intent/access first. Explicit small resolve retains all phases and independent verification. Verify dual-judge is default-when-available.
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -28,23 +28,42 @@ Every phase applies Subtractive-first / Goal-locked / No-workaround / Evidence,
|
|
|
28
28
|
</harness_principles>
|
|
29
29
|
|
|
30
30
|
<runtime_paths>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
fi
|
|
39
|
-
DEVLYN_SHARED_DIR="$(cd "$DEVLYN_SKILL_DIR/../_shared" && pwd)"
|
|
40
|
-
CODEX_MONITORED_PATH="$DEVLYN_SHARED_DIR/codex-monitored.sh"
|
|
41
|
-
if [ ! -f "$CODEX_MONITORED_PATH" ]; then
|
|
42
|
-
echo "BLOCKED:shared-dir-unresolved: $CODEX_MONITORED_PATH" >&2
|
|
43
|
-
exit 1
|
|
44
|
-
fi
|
|
31
|
+
Before PHASE 0 or any phase command, establish the bindings below.
|
|
32
|
+
|
|
33
|
+
Resolve bundled resources from the SKILL.md loaded for this invocation.
|
|
34
|
+
|
|
35
|
+
Reader-rendered directory hint:
|
|
36
|
+
```text
|
|
37
|
+
${CLAUDE_SKILL_DIR}
|
|
45
38
|
```
|
|
46
39
|
|
|
47
|
-
|
|
40
|
+
Treat the hint as literal path data, never shell code. If the reader
|
|
41
|
+
replaced it with an absolute directory, use that directory. Otherwise
|
|
42
|
+
use the filesystem path or base directory reported for this loaded
|
|
43
|
+
SKILL.md. Resolve virtual URIs through the reader's native filesystem
|
|
44
|
+
mapping. If available source locations name different directories,
|
|
45
|
+
stop.
|
|
46
|
+
|
|
47
|
+
Bind DEVLYN_SKILL_DIR to that absolute directory. Do not obtain this
|
|
48
|
+
binding from an environment variable, cwd, or another installation.
|
|
49
|
+
Verify its SKILL.md before proceeding. Missing or conflicting source
|
|
50
|
+
identity is BLOCKED:skill-source-unresolved; include the failed path
|
|
51
|
+
when known.
|
|
52
|
+
|
|
53
|
+
Resolve bundled references against this directory. These bindings are
|
|
54
|
+
workflow values: establish them explicitly using each tool or shell's
|
|
55
|
+
literal-path rules, and include their absolute values in every fresh
|
|
56
|
+
worker's prompt, telling it to set them from those values, never from
|
|
57
|
+
its inherited environment. Do not rely on shell state surviving between
|
|
58
|
+
calls.
|
|
59
|
+
|
|
60
|
+
Resolve directory symlinks on DEVLYN_SKILL_DIR before deriving its
|
|
61
|
+
sibling _shared. Bind that directory as DEVLYN_SHARED_DIR. References
|
|
62
|
+
written as _shared/... use this binding. Verify the directory and each
|
|
63
|
+
required resource before use; failure is BLOCKED:shared-dir-unresolved
|
|
64
|
+
with the failed path. Never search another installation.
|
|
65
|
+
|
|
66
|
+
Bind CODEX_MONITORED_PATH to DEVLYN_SHARED_DIR/codex-monitored.sh and verify that file before proceeding. Pass DEVLYN_SKILL_DIR, DEVLYN_SHARED_DIR, and CODEX_MONITORED_PATH explicitly to every fresh phase worker. In omp, when this skill was selected by name, resolve it with `printf '%s\n' skill://devlyn-resolve` in its Bash tool.
|
|
48
67
|
</runtime_paths>
|
|
49
68
|
|
|
50
69
|
<engine_routing>
|
|
@@ -62,9 +81,9 @@ Each model-invoked phase routes to an engine and prepends the per-engine adapter
|
|
|
62
81
|
<modes>
|
|
63
82
|
Three input shapes:
|
|
64
83
|
|
|
65
|
-
1. **Free-form**: `/devlyn
|
|
66
|
-
2. **Spec**: `/devlyn
|
|
67
|
-
3. **Verify-only**: `/devlyn
|
|
84
|
+
1. **Free-form**: `/devlyn-resolve "fix the login bug"` (inline goal) or `/devlyn-resolve --goal-file <path>` (goal text read from a file — the devlynd `ResolveAdapter` launcher path, injection-safe). PHASE 0 runs the complexity classifier and either proceeds with an internal mini-spec (trivial), drafts focused questions for in-prompt resolution (medium), or synthesizes a best-effort spec with a logged `## Assumptions` block (large; zero-scope-signal goals halt). No mid-pipeline prompts in any branch.
|
|
85
|
+
2. **Spec**: `/devlyn-resolve --spec docs/roadmap/phase-N/X.md`. Supplied specs and expected files are read-only inside resolve. Stage verification commands from sibling `spec.expected.json`; if absent, use the legacy `## Verification` JSON block.
|
|
86
|
+
3. **Verify-only**: `/devlyn-resolve --verify-only <diff-or-PR-ref> --spec <path>`. Skips PHASE 1-4. Runs PHASE 5 (VERIFY) on the supplied diff against the spec.
|
|
68
87
|
</modes>
|
|
69
88
|
|
|
70
89
|
<post_implement_invariant>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Free-form mode — complexity classifier
|
|
2
2
|
|
|
3
|
-
When `/devlyn
|
|
3
|
+
When `/devlyn-resolve` is invoked with a free-form goal (no `--spec`) — whether the goal is the inline positional text or the content of `--goal-file <path>` (PHASE 0 resolves `goal_text` from either source before classifying) — PHASE 0 runs this classifier to set `state.complexity ∈ {trivial, medium, large}` and either proceeds with an internal mini-spec, drafts focused questions for in-prompt resolution, synthesizes a best-effort spec with logged assumptions, or halts zero-scope-signal goals with `/devlyn-ideate` guidance.
|
|
4
4
|
|
|
5
5
|
The classifier is rules-based / deterministic — not an LLM judgment call. Decision rules below.
|
|
6
6
|
|
|
@@ -49,7 +49,7 @@ Conditions (any one):
|
|
|
49
49
|
- The goal mentions a new feature whose surface area requires design decisions the harness cannot make from a one-shot prompt.
|
|
50
50
|
|
|
51
51
|
Action:
|
|
52
|
-
- Default: synthesize a best-effort spec from the goal with an explicit `## Assumptions` block (every assumption scope-narrowing and reversible — when in doubt, narrower); log `recommend: /devlyn
|
|
52
|
+
- Default: synthesize a best-effort spec from the goal with an explicit `## Assumptions` block (every assumption scope-narrowing and reversible — when in doubt, narrower); log `recommend: /devlyn-ideate first` in `.devlyn/criteria.generated.md` AND the final report; proceed to PHASE 1; the final report flags every assumption for user review.
|
|
53
53
|
- Zero-signal exception: if the large classification includes `file_scope_signals == 0` (classifier cannot pick scope), halt with terminal verdict `BLOCKED:large-needs-ideation` — assumptions there would be scope-invention, not narrowing.
|
|
54
54
|
|
|
55
55
|
## Anti-pattern: drift to LLM judgment
|
|
@@ -63,7 +63,7 @@ When the rules are silent (rare — pathological goal text), default to `medium`
|
|
|
63
63
|
The internal mini-spec written for trivial / medium / large-assumptions paths must satisfy:
|
|
64
64
|
|
|
65
65
|
- `## Requirements` non-empty, each bullet testable (CLI command, test command, observable file change).
|
|
66
|
-
- For executable generated verification, put one fenced `json` object with `verification_commands` inside the sentinel-marked `## Verification` section. Generated mode reads this inline carrier, not a sibling file. Follow `../../devlyn
|
|
66
|
+
- For executable generated verification, put one fenced `json` object with `verification_commands` inside the sentinel-marked `## Verification` section. Generated mode reads this inline carrier, not a sibling file. Follow `../../devlyn-ideate/references/spec-template.md` § "Sibling file: `spec.expected.json`" for constraint coverage, diff scope and violating/allowed controls, but express those checks as executable `verification_commands`; sibling-only fields such as `required_files` and `forbidden_patterns` are rejected inline. Preserve uncovered semantics as source-review obligations.
|
|
67
67
|
- `## Verification` is preceded by a `<!-- devlyn:verification -->` sentinel on its own line directly above the heading — the machine locator `spec-verify-check.py` uses; the heading text itself is decorative and may be any language. When all Requirements are pure-design with no runnable acceptance check, put `{"pure_design": true, "verification_commands": []}` in the fenced `json` block and retain semantic source-review obligations. This explicit declaration is required; a missing block, an unmarked empty list, or `pure_design: true` with commands is invalid.
|
|
68
68
|
- Free-form mode mini-specs are written to `.devlyn/criteria.generated.md` (not to a roadmap path) — this is run-scoped artifact, not a documented spec.
|
|
69
69
|
- After writing `.devlyn/criteria.generated.md`, set `state.source.type = "generated"`, `state.source.spec_path = null`, `state.source.spec_sha256 = null`, `state.source.criteria_path = ".devlyn/criteria.generated.md"`, and `state.source.criteria_sha256` to the raw-byte SHA-256 of the generated criteria file. Downstream PLAN/IMPLEMENT/VERIFY phases and `spec-verify-check.py --include-risk-probes` depend on this pointer; do not rely on the file existing by convention.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Outer-loop commit boundary
|
|
2
2
|
|
|
3
|
-
A full `/devlyn
|
|
3
|
+
A full `/devlyn-resolve` run starts only from committed owner inputs. Keep each
|
|
4
4
|
commit scoped: no unrelated paths or queue-item changes.
|
|
5
5
|
|
|
6
6
|
1. Before task writes, the outer owner allocates an absent task branch per
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pipeline.state.json schema
|
|
2
2
|
|
|
3
|
-
Single authoritative verdict source for `/devlyn
|
|
3
|
+
Single authoritative verdict source for `/devlyn-resolve`. The orchestrator branches on `state.phases.<name>.verdict` directly — never parses `.devlyn/*.findings.jsonl` for routing. Living document; bump `version` on a breaking change.
|
|
4
4
|
|
|
5
5
|
## Top-level shape
|
|
6
6
|
|
|
@@ -7,8 +7,15 @@ authorized by the task scope; no additional approval ceremony is required.
|
|
|
7
7
|
|
|
8
8
|
## Allocate before work
|
|
9
9
|
|
|
10
|
-
Use the
|
|
11
|
-
|
|
10
|
+
Use the caller's DEVLYN_SHARED_DIR. When this reference is opened
|
|
11
|
+
directly, derive that binding from ../../_shared relative to this
|
|
12
|
+
reference's reader-supplied containing directory, resolving directory
|
|
13
|
+
symlinks first. Missing source identity is
|
|
14
|
+
BLOCKED:skill-source-unresolved; a missing task-complete.py is
|
|
15
|
+
BLOCKED:shared-dir-unresolved. Never select another installation.
|
|
16
|
+
|
|
17
|
+
Run the bound task-complete.py before committing owner inputs or
|
|
18
|
+
starting direct/full work:
|
|
12
19
|
|
|
13
20
|
```sh
|
|
14
21
|
python3 "$DEVLYN_SHARED_DIR/task-complete.py" allocate --repo . \
|
|
@@ -23,7 +23,7 @@ Start with the minimum viable solution, then improve if time allows.
|
|
|
23
23
|
|
|
24
24
|
## Debugging
|
|
25
25
|
|
|
26
|
-
For systematic debugging, use `/devlyn
|
|
26
|
+
For systematic debugging, use `/devlyn-resolve [issue description]` which includes:
|
|
27
27
|
- Code path mapping
|
|
28
28
|
- Hypothesis-driven analysis
|
|
29
29
|
- Test-driven fix validation
|
|
@@ -61,7 +61,7 @@ have working, tested code ready for PR.
|
|
|
61
61
|
For complex bugs that need autonomous resolution:
|
|
62
62
|
|
|
63
63
|
```
|
|
64
|
-
/devlyn
|
|
64
|
+
/devlyn-resolve [bug description]
|
|
65
65
|
|
|
66
66
|
After analysis, implement the fix autonomously:
|
|
67
67
|
- Write failing test first
|
|
@@ -30,13 +30,19 @@ The setup produces a dual-auth system: session cookies for browser users and API
|
|
|
30
30
|
|
|
31
31
|
Read these when each step directs you to them:
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
-
|
|
33
|
+
Resolve the bundled references/... paths below against the directory
|
|
34
|
+
containing the SKILL.md loaded for this invocation, using its opened
|
|
35
|
+
path, reader-reported base directory, or native URI mapping. Never
|
|
36
|
+
resolve them against cwd or another installation. Missing source
|
|
37
|
+
identity is BLOCKED:skill-source-unresolved.
|
|
38
|
+
|
|
39
|
+
- `references/schema.md` — Complete Drizzle schema (auth, org, API key tables)
|
|
40
|
+
- `references/middleware.md` — Auth middleware, tenant context, types, error handler
|
|
41
|
+
- `references/api-keys.md` — Key generation, CRUD routes, security patterns
|
|
42
|
+
- `references/config-and-entry.md` — Env config, error types, entry point wiring
|
|
43
|
+
- `references/testing.md` — Test preload, seed factory, integration patterns
|
|
44
|
+
- `references/proxy-setup.md` — Reverse proxy architecture, forwarding headers, OAuth callback routing
|
|
45
|
+
- `references/proxy-gotchas.md` — Proxy-specific troubleshooting (redirect_uri_mismatch, state_mismatch, cookie prefix issues)
|
|
40
46
|
|
|
41
47
|
## Handling Input
|
|
42
48
|
|
|
@@ -86,7 +92,7 @@ Generate the auth secret immediately (do not use a placeholder):
|
|
|
86
92
|
bunx @better-auth/cli@latest secret
|
|
87
93
|
```
|
|
88
94
|
|
|
89
|
-
Read
|
|
95
|
+
Read `references/config-and-entry.md` for the complete Zod config implementation. Key env vars:
|
|
90
96
|
|
|
91
97
|
| Variable | Required | Purpose |
|
|
92
98
|
|----------|----------|---------|
|
|
@@ -104,7 +110,7 @@ Read `${CLAUDE_SKILL_DIR}/references/config-and-entry.md` for the complete Zod c
|
|
|
104
110
|
**Entry:** Config validated at startup.
|
|
105
111
|
**Exit:** Migration applied, tables created in PostgreSQL.
|
|
106
112
|
|
|
107
|
-
Read
|
|
113
|
+
Read `references/schema.md` for the complete schema. Key decisions:
|
|
108
114
|
|
|
109
115
|
- Use **plural table names** (`users`, `sessions`) — mapped to Better Auth's singular models in Step 3
|
|
110
116
|
- Use **UUIDs** for all primary keys
|
|
@@ -284,7 +290,7 @@ authRoutes.on(["POST", "GET"], "/*", (c) => auth.handler(c.req.raw));
|
|
|
284
290
|
**Entry:** Auth routes mounted.
|
|
285
291
|
**Exit:** `src/middleware/auth.ts` validates API keys and session cookies.
|
|
286
292
|
|
|
287
|
-
Read
|
|
293
|
+
Read `references/middleware.md` for the complete implementation. The middleware:
|
|
288
294
|
- Detects API keys via `Authorization: Bearer pyx_...` prefix
|
|
289
295
|
- Validates via SHA-256 hash lookup + revocation + expiration checks
|
|
290
296
|
- Falls back to Better Auth session cookie validation
|
|
@@ -299,7 +305,7 @@ Read `${CLAUDE_SKILL_DIR}/references/middleware.md` for the complete implementat
|
|
|
299
305
|
**Entry:** Auth middleware populates `AuthContext` on the request.
|
|
300
306
|
**Exit:** `src/middleware/tenant-context.ts` resolves org and plan.
|
|
301
307
|
|
|
302
|
-
Read
|
|
308
|
+
Read `references/middleware.md` for the implementation. Key behaviors:
|
|
303
309
|
- API key auth: org already resolved from the key's project, just fetches plan
|
|
304
310
|
- Session auth: looks up user's most recent org membership
|
|
305
311
|
- Uses distinct error code `no_organization` (not generic `forbidden`) so the frontend can show "Create your first organization" instead of "Access denied"
|
|
@@ -311,7 +317,7 @@ Read `${CLAUDE_SKILL_DIR}/references/middleware.md` for the implementation. Key
|
|
|
311
317
|
**Entry:** Middleware needs error classes to throw.
|
|
312
318
|
**Exit:** `src/lib/errors.ts` with `AppError` hierarchy, `src/middleware/error-handler.ts`.
|
|
313
319
|
|
|
314
|
-
Read
|
|
320
|
+
Read `references/config-and-entry.md` for the complete error hierarchy and handler. Every error has a distinct `code` field for frontend differentiation:
|
|
315
321
|
|
|
316
322
|
| Error Class | Code | Status |
|
|
317
323
|
|-------------|------|--------|
|
|
@@ -328,7 +334,7 @@ Read `${CLAUDE_SKILL_DIR}/references/config-and-entry.md` for the complete error
|
|
|
328
334
|
**Entry:** All middleware and routes implemented.
|
|
329
335
|
**Exit:** `src/index.ts` with correct middleware ordering.
|
|
330
336
|
|
|
331
|
-
Read
|
|
337
|
+
Read `references/config-and-entry.md` for the complete entry point. The middleware order is:
|
|
332
338
|
|
|
333
339
|
1. **Request ID** — first, so error handler can include it in responses
|
|
334
340
|
2. **Security Headers** — HSTS (production), X-Frame-Options
|
|
@@ -348,7 +354,7 @@ CORS before auth routes is non-negotiable. If registered after, preflight OPTION
|
|
|
348
354
|
**Entry:** Application fully functional.
|
|
349
355
|
**Exit:** Test preload, seed factory, integration app, cleanup utilities.
|
|
350
356
|
|
|
351
|
-
Read
|
|
357
|
+
Read `references/testing.md` for the complete test setup. Essential components:
|
|
352
358
|
|
|
353
359
|
1. **Test preload** (`bunfig.toml` + `setup.ts`) — clears `RESEND_API_KEY`, bridges `TEST_DATABASE_URL`
|
|
354
360
|
2. **Seed factory** (`seedTestData()`) — creates full tenant hierarchy with unique slugs
|
|
@@ -413,7 +419,7 @@ If any item fails, fix the root cause before proceeding.
|
|
|
413
419
|
|
|
414
420
|
## API Key System
|
|
415
421
|
|
|
416
|
-
For the complete custom API key implementation (generation, validation, CRUD routes), read
|
|
422
|
+
For the complete custom API key implementation (generation, validation, CRUD routes), read `references/api-keys.md`.
|
|
417
423
|
|
|
418
424
|
Summary:
|
|
419
425
|
- **Format**: `pyx_` prefix + 32 random bytes base62-encoded (~50 chars)
|
|
@@ -447,7 +453,7 @@ Always append a unique suffix (UUID slice or timestamp) to prevent collisions.
|
|
|
447
453
|
|
|
448
454
|
## Proxy Architecture (Steps 12-14)
|
|
449
455
|
|
|
450
|
-
These steps apply when the frontend is a separate application that proxies auth requests to the backend. Read
|
|
456
|
+
These steps apply when the frontend is a separate application that proxies auth requests to the backend. Read `references/proxy-setup.md` for the complete guide with code examples.
|
|
451
457
|
|
|
452
458
|
### Step 12: Configure Dynamic baseURL on Backend
|
|
453
459
|
|
|
@@ -499,7 +505,7 @@ socialProviders: {
|
|
|
499
505
|
**Entry:** Backend uses `allowedHosts` with `trustedProxyHeaders`.
|
|
500
506
|
**Exit:** Frontend proxies auth requests and OAuth callbacks correctly.
|
|
501
507
|
|
|
502
|
-
The frontend proxy must do three things. Read
|
|
508
|
+
The frontend proxy must do three things. Read `references/proxy-setup.md` for complete code.
|
|
503
509
|
|
|
504
510
|
#### 13a. Create two proxy routes
|
|
505
511
|
|
|
@@ -592,7 +598,7 @@ curl -s -X POST "https://your-frontend.com/api/auth/sign-in/social" \
|
|
|
592
598
|
|
|
593
599
|
Expected: `redirect_uri: https://your-frontend.com/auth/callback/google`
|
|
594
600
|
|
|
595
|
-
For detailed troubleshooting of proxy-specific failures, read
|
|
601
|
+
For detailed troubleshooting of proxy-specific failures, read `references/proxy-gotchas.md`.
|
|
596
602
|
|
|
597
603
|
---
|
|
598
604
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: devlyn
|
|
2
|
+
name: devlyn-reap
|
|
3
3
|
description: Safely count and kill orphaned child processes (PPID=1) left behind by Claude Code MCP plugins, Superset terminal tabs, and codex wrappers. Use this whenever the user says "too many processes", "can't open terminals", "pty/process limit", "hundreds of bun/codex/workerd piling up", "clean up orphans", "reap processes", or reports new terminals failing to spawn on macOS. Also use proactively after long Claude sessions to prevent hitting kern.maxprocperuid or kern.tty.ptmx_max limits. ONLY touches a conservative whitelist of known leaks — never guesses on unknown processes.
|
|
4
4
|
allowed-tools: Read, Bash(ps:*), Bash(lsof:*), Bash(pgrep:*), Bash(awk:*), Bash(id:*), Bash(sysctl:*), Bash(bash:*)
|
|
5
5
|
argument-hint: [scan | kill | kill --force | kill --include workerd | kill --only telegram-bun]
|
|
@@ -38,10 +38,41 @@ If the user's intent is ambiguous (e.g., they say "지워줘" but didn't specify
|
|
|
38
38
|
|
|
39
39
|
Always run scan first — even in KILL mode — so the user sees what is about to happen.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Resolve bundled resources from the SKILL.md loaded for this invocation.
|
|
42
|
+
|
|
43
|
+
Reader-rendered directory hint:
|
|
44
|
+
```text
|
|
45
|
+
${CLAUDE_SKILL_DIR}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Treat the hint as literal path data, never shell code. If the reader
|
|
49
|
+
replaced it with an absolute directory, use that directory. Otherwise
|
|
50
|
+
use the filesystem path or base directory reported for this loaded
|
|
51
|
+
SKILL.md. Resolve virtual URIs through the reader's native filesystem
|
|
52
|
+
mapping. If available source locations name different directories,
|
|
53
|
+
stop.
|
|
54
|
+
|
|
55
|
+
Bind DEVLYN_SKILL_DIR to that absolute directory. Do not obtain this
|
|
56
|
+
binding from an environment variable, cwd, or another installation.
|
|
57
|
+
Verify its SKILL.md before proceeding. Missing or conflicting source
|
|
58
|
+
identity is BLOCKED:skill-source-unresolved; include the failed path
|
|
59
|
+
when known.
|
|
60
|
+
|
|
61
|
+
Resolve bundled references against this directory. These bindings are
|
|
62
|
+
workflow values: establish them explicitly using each tool or shell's
|
|
63
|
+
literal-path rules, and include their absolute values in every fresh
|
|
64
|
+
worker's prompt, telling it to set them from those values, never from
|
|
65
|
+
its inherited environment. Do not rely on shell state surviving between
|
|
66
|
+
calls.
|
|
67
|
+
|
|
68
|
+
In omp, use: printf '%s\n' skill://devlyn-reap
|
|
69
|
+
Verify the requested script under this bound directory before running
|
|
70
|
+
it; a missing script is BLOCKED:skill-source-unresolved with its path.
|
|
71
|
+
|
|
72
|
+
Run the bundled scanner:
|
|
42
73
|
|
|
43
74
|
```bash
|
|
44
|
-
bash
|
|
75
|
+
bash "$DEVLYN_SKILL_DIR/scripts/scan.sh"
|
|
45
76
|
```
|
|
46
77
|
|
|
47
78
|
Report the output verbatim to the user. Then add your own 2-line summary:
|
|
@@ -60,7 +91,7 @@ sysctl kern.maxprocperuid kern.tty.ptmx_max 2>/dev/null
|
|
|
60
91
|
Run the reap script with the parsed flags:
|
|
61
92
|
|
|
62
93
|
```bash
|
|
63
|
-
bash
|
|
94
|
+
bash "$DEVLYN_SKILL_DIR/scripts/reap.sh" [flags]
|
|
64
95
|
```
|
|
65
96
|
|
|
66
97
|
Show the output verbatim. The script re-verifies `PPID==1 && user==current` for every PID right before signaling — a process that was legitimately adopted since the scan will be skipped, not killed.
|
|
@@ -72,7 +103,7 @@ After kill, re-run scan to confirm the counts dropped. If any whitelisted PIDs a
|
|
|
72
103
|
If `telegram-bun` count > 10 OR oldest whitelisted orphan > 24h, tell the user this is a recurring leak and suggest one of:
|
|
73
104
|
|
|
74
105
|
1. **Patch the telegram plugin** — add `process.stdin.on('end', () => process.exit(0))` to `server.ts` so the child dies when Claude Code exits.
|
|
75
|
-
2. **Schedule this skill** — run `/devlyn
|
|
106
|
+
2. **Schedule this skill** — run `/devlyn-reap kill` periodically (e.g., via the `/loop` skill or a launchd agent).
|
|
76
107
|
3. **Update Superset** — newer versions may SIGTERM process groups on tab close.
|
|
77
108
|
|
|
78
109
|
Do NOT apply these automatically. Recommend and let the user choose.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# devlyn
|
|
2
|
+
# devlyn-reap — kill orphan processes from safe whitelist categories.
|
|
3
3
|
# Verifies PPID==1 and user-ownership AGAIN at kill time to avoid racing a
|
|
4
4
|
# legitimately-reparented process. Unknown orphans are never killed.
|
|
5
5
|
#
|
|
@@ -187,12 +187,12 @@ File path to the template document (DOCX or HWPX).
|
|
|
187
187
|
|
|
188
188
|
**Phase 3 — Source Images**:
|
|
189
189
|
7. **Cell-level images**: For each `field_type: "image"` with `image_file: null` and `image_type: "figure"`:
|
|
190
|
-
- Run: `python
|
|
190
|
+
- Run: `python "$DEVLYN_SKILL_DIR/scripts/source_images.py" generate --prompt "<prompt>" --preset technical_illustration --output-dir .dokkit/images/ --project-dir . --lang ko`
|
|
191
191
|
- Parse `__RESULT__` JSON, update `analysis.json`
|
|
192
192
|
- Skip photo/signature types (require user-provided files)
|
|
193
193
|
- Default `--lang ko` (Korean only). Override with user instruction if needed.
|
|
194
194
|
8. **Section content images**: For each `image_opportunities` entry with `status: "pending"`:
|
|
195
|
-
- Run: `python
|
|
195
|
+
- Run: `python "$DEVLYN_SKILL_DIR/scripts/source_images.py" generate --prompt "<generation_prompt>" --preset <preset> --output-dir .dokkit/images/ --project-dir . --lang ko`
|
|
196
196
|
- On failure: set `status: "skipped"`, log reason
|
|
197
197
|
- Use `--lang ko+en` if the content contains technical terms that benefit from English (e.g., architecture diagrams with API names).
|
|
198
198
|
9. Report: "Sourced X/Y images"
|
|
@@ -272,8 +272,8 @@ File path to the template document (DOCX or HWPX).
|
|
|
272
272
|
> "Analyze the template at `<path>`. Detect all fillable fields INCLUDING image fields. Map to sources. Write `analysis.json`."
|
|
273
273
|
|
|
274
274
|
**Image sourcing** (inline, between agents):
|
|
275
|
-
- **Pass A — Cell-level**: For `field_type: "image"` with `image_file: null` and `image_type: "figure"`, run `python
|
|
276
|
-
- **Pass B — Section content**: For `image_opportunities` with `status: "pending"`, run `python
|
|
275
|
+
- **Pass A — Cell-level**: For `field_type: "image"` with `image_file: null` and `image_type: "figure"`, run `python "$DEVLYN_SKILL_DIR/scripts/source_images.py" generate --prompt "..." --preset ... --output-dir .dokkit/images/ --project-dir . --lang ko`
|
|
276
|
+
- **Pass B — Section content**: For `image_opportunities` with `status: "pending"`, run `python "$DEVLYN_SKILL_DIR/scripts/source_images.py" generate --prompt "..." --preset ... --output-dir .dokkit/images/ --project-dir . --lang ko`
|
|
277
277
|
- Default language is `ko` (Korean only). Use `--lang ko+en` for mixed content, or `--lang en` for English-only.
|
|
278
278
|
|
|
279
279
|
**Then**: Spawn the dokkit-filler agent in fill mode:
|
|
@@ -41,8 +41,8 @@ def compile_hwpx(work_dir: str, output_path: str):
|
|
|
41
41
|
|
|
42
42
|
### Scripts
|
|
43
43
|
```bash
|
|
44
|
-
python
|
|
45
|
-
python
|
|
44
|
+
python "$DEVLYN_SKILL_DIR/scripts/compile_hwpx.py" <work_dir> <output.hwpx>
|
|
45
|
+
python "$DEVLYN_SKILL_DIR/scripts/export_pdf.py" <input> <output.pdf>
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
## PDF Conversion
|
|
@@ -54,7 +54,7 @@ soffice --headless --convert-to pdf --outdir <output_dir> <input_file>
|
|
|
54
54
|
|
|
55
55
|
### Using Python Script
|
|
56
56
|
```bash
|
|
57
|
-
python
|
|
57
|
+
python "$DEVLYN_SKILL_DIR/scripts/export_pdf.py" <input> <output.pdf>
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
## Cross-Format Conversion
|
|
@@ -15,24 +15,24 @@ Field detection, matching strategies, and surgical XML editing rules for the dok
|
|
|
15
15
|
|
|
16
16
|
### DOCX Field Detection
|
|
17
17
|
```bash
|
|
18
|
-
python
|
|
18
|
+
python "$DEVLYN_SKILL_DIR/scripts/detect_fields.py" <document.xml>
|
|
19
19
|
```
|
|
20
20
|
Outputs JSON array of detected fields with labels, types, and XML paths.
|
|
21
21
|
|
|
22
22
|
### HWPX Field Detection
|
|
23
23
|
```bash
|
|
24
|
-
python
|
|
24
|
+
python "$DEVLYN_SKILL_DIR/scripts/detect_fields_hwpx.py" <section.xml>
|
|
25
25
|
```
|
|
26
26
|
Same output format, adapted for HWPX XML structure.
|
|
27
27
|
|
|
28
28
|
### DOCX Validation
|
|
29
29
|
```bash
|
|
30
|
-
python
|
|
30
|
+
python "$DEVLYN_SKILL_DIR/scripts/validate_docx.py" <work_dir>
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
### HWPX Validation
|
|
34
34
|
```bash
|
|
35
|
-
python
|
|
35
|
+
python "$DEVLYN_SKILL_DIR/scripts/validate_hwpx.py" <work_dir>
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## Matching Strategy
|
|
@@ -25,7 +25,7 @@ Via `/dokkit modify "use <file>"`:
|
|
|
25
25
|
|
|
26
26
|
### 3. AI Generation
|
|
27
27
|
```bash
|
|
28
|
-
python
|
|
28
|
+
python "$DEVLYN_SKILL_DIR/scripts/source_images.py" generate \
|
|
29
29
|
--prompt "인포그래픽: AI 감정 케어 플랫폼 4단계 로드맵" \
|
|
30
30
|
--preset infographic \
|
|
31
31
|
--output-dir .dokkit/images/ \
|
|
@@ -61,7 +61,7 @@ Use `--aspect-ratio 16:9` to override. Use `--no-enhance` to skip preset style i
|
|
|
61
61
|
|
|
62
62
|
### 4. Web Search
|
|
63
63
|
```bash
|
|
64
|
-
python
|
|
64
|
+
python "$DEVLYN_SKILL_DIR/scripts/source_images.py" search \
|
|
65
65
|
--query "company logo example" \
|
|
66
66
|
--output-dir .dokkit/images/
|
|
67
67
|
```
|
|
@@ -12,11 +12,11 @@ Parsing strategies and format routing for converting source documents into the d
|
|
|
12
12
|
| HTML | Docling | `python -m docling <file> --to md` |
|
|
13
13
|
| CSV | Docling | `python -m docling <file> --to md` |
|
|
14
14
|
| MD | Direct copy | Read and process as-is |
|
|
15
|
-
| XLSX | Custom | `python
|
|
16
|
-
| HWPX | Custom | `python
|
|
15
|
+
| XLSX | Custom | `python "$DEVLYN_SKILL_DIR/scripts/parse_xlsx.py"` |
|
|
16
|
+
| HWPX | Custom | `python "$DEVLYN_SKILL_DIR/scripts/parse_hwpx.py"` |
|
|
17
17
|
| JSON | Custom | Read, format as structured markdown |
|
|
18
18
|
| TXT | Custom | Read, wrap as markdown |
|
|
19
|
-
| PNG/JPG | Gemini Vision | `python
|
|
19
|
+
| PNG/JPG | Gemini Vision | `python "$DEVLYN_SKILL_DIR/scripts/parse_image_with_gemini.py"` |
|
|
20
20
|
|
|
21
21
|
## Docling Usage
|
|
22
22
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Dokkit Pipeline Reference
|
|
2
2
|
|
|
3
3
|
Detailed step-by-step procedure for the one-command document filling pipeline.
|
|
4
|
-
|
|
4
|
+
Read by the invoking skill before execution; bundled paths use the
|
|
5
|
+
DEVLYN_SKILL_DIR binding it passes.
|
|
5
6
|
|
|
6
7
|
## Table of Contents
|
|
7
8
|
|
|
@@ -119,7 +120,7 @@ Report: `Phase 3/6: Generated content for N fields`
|
|
|
119
120
|
Run the python-docx assembly script (NOT raw XML surgery):
|
|
120
121
|
|
|
121
122
|
```bash
|
|
122
|
-
python
|
|
123
|
+
python "$DEVLYN_SKILL_DIR/scripts/fill_docx.py" \
|
|
123
124
|
<template.docx> \
|
|
124
125
|
.dokkit/analysis.json \
|
|
125
126
|
.dokkit/fill_content.json \
|
|
@@ -150,7 +151,7 @@ For each image field and image opportunity in `analysis.json`, run in parallel.
|
|
|
150
151
|
**CRITICAL: Always pass `--field-id` and `--purpose`** so the image manifest tracks which image was generated for which slot. Without this, images get randomly assigned to the wrong slots.
|
|
151
152
|
|
|
152
153
|
```bash
|
|
153
|
-
python
|
|
154
|
+
python "$DEVLYN_SKILL_DIR/scripts/source_images.py" generate \
|
|
154
155
|
--prompt "<prompt>" \
|
|
155
156
|
--preset <preset> \
|
|
156
157
|
--output-dir .dokkit/images/ \
|
|
@@ -345,4 +346,4 @@ Phase 5 → quality_gates results, iterations_count
|
|
|
345
346
|
Phase 6 → exports[] entry added
|
|
346
347
|
```
|
|
347
348
|
|
|
348
|
-
Validate after every write: `python $
|
|
349
|
+
Validate after every write: `python "$DEVLYN_SKILL_DIR/scripts/validate_state.py" .dokkit/state.json`
|