devlyn-cli 4.0.0 → 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 +1 -1
- package/CLAUDE.md +1 -1
- package/bin/devlyn.js +25 -44
- package/bin/instruction-templates.json +2 -0
- package/config/skills/_shared/codex-config.md +12 -12
- package/config/skills/devlyn-engines/SKILL.md +35 -9
- package/config/skills/devlyn-ideate/SKILL.md +32 -14
- package/config/skills/devlyn-queue/SKILL.md +36 -13
- package/config/skills/devlyn-queue/scripts/append.py +94 -0
- package/config/skills/devlyn-resolve/SKILL.md +34 -15
- package/config/skills/devlyn-resolve/references/task-completion.md +9 -2
- package/optional-skills/better-auth-setup/SKILL.md +24 -18
- package/optional-skills/devlyn-reap/SKILL.md +34 -3
- 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 +1 -1
package/AGENTS.md
CHANGED
|
@@ -42,7 +42,7 @@ Conversational entry: Before loading a workflow or writing files, inspect the re
|
|
|
42
42
|
|
|
43
43
|
Use full `/devlyn-resolve` automatically only when inspection identifies interacting requirements or verification work too complex to manage reliably in the current context, such as coupled durable state, concurrent ownership and failure recovery across boundaries. Name the concrete interaction or verification gap. Domain labels (security/auth/payment/persistence/concurrency/API), file count and a spec document alone do not trigger it. Investigate or clarify missing intent/access first; a pipeline cannot supply them. Direct work still requires risk-proportionate checks and independent review for consequential changes. Explicit resolve (even small), explicit spec-mode workflows and queue drains retain all canonical phases, independent verification, pins and failure handling. If the full-route condition emerges mid-edit, carry the existing delta into its scope/evidence; do not reset the baseline or reconfirm existing authorization.
|
|
44
44
|
|
|
45
|
-
Full-route handoff + loop engineering: the orchestrating model — not the user — invokes the skills. `/devlyn-queue` fronts the intent queue (`docs/specs/queue.md`): no args = status, `add <intent>`, `drain` = serial unattended drain per the contract below. Work selected for full resolve is written to `docs/specs/<id>/spec.md` (always a spec file — the user's reviewed contract; free-form large assumes-and-logs instead of halting, zero-scope-signal goals still halt to ideate), summarized once for user review, then run hands-free via `--spec`. Outer loop per task: before the first full run, commit the scoped queue-item delta (when applicable) and linked spec bundle; findings-backed verdicts (NEEDS_WORK, BLOCKED:repair-budget-exhausted) get a committed spec amendment before re-run, max 3 iterations; infrastructure / invalid-input / engine-availability / implement-empty BLOCKED verdicts surface immediately. Unattended queue drain (`docs/specs/queue.md`, strictly serial): assumptions may only take scope-narrowing, reversible, non-user-visible defaults — material ambiguity marks the item `[F] needs-review`; commit every terminal `[x]` / `[F]` transition before advancing. The canonical commit order is `
|
|
45
|
+
Full-route handoff + loop engineering: the orchestrating model — not the user — invokes the skills. `/devlyn-queue` fronts the intent queue (`docs/specs/queue.md`): no args = status, `add <intent>`, `drain` = serial unattended drain per the contract below. Work selected for full resolve is written to `docs/specs/<id>/spec.md` (always a spec file — the user's reviewed contract; free-form large assumes-and-logs instead of halting, zero-scope-signal goals still halt to ideate), summarized once for user review, then run hands-free via `--spec`. Outer loop per task: before the first full run, commit the scoped queue-item delta (when applicable) and linked spec bundle; findings-backed verdicts (NEEDS_WORK, BLOCKED:repair-budget-exhausted) get a committed spec amendment before re-run, max 3 iterations; infrastructure / invalid-input / engine-availability / implement-empty BLOCKED verdicts surface immediately. Unattended queue drain (`docs/specs/queue.md`, strictly serial): assumptions may only take scope-narrowing, reversible, non-user-visible defaults — material ambiguity marks the item `[F] needs-review`; commit every terminal `[x]` / `[F]` transition before advancing. The canonical commit order is `references/outer-loop.md` in the installed `devlyn-resolve` skill.
|
|
46
46
|
|
|
47
47
|
## Subtractive-First Editing
|
|
48
48
|
|
package/CLAUDE.md
CHANGED
|
@@ -62,7 +62,7 @@ The user does not invoke skills manually; the orchestrating model does. For work
|
|
|
62
62
|
3. On go-ahead, commit the scoped queue-item delta (when applicable) and linked spec bundle, then invoke `/devlyn-resolve --spec <path>` hands-free to completion.
|
|
63
63
|
4. **Per-task outer loop**: read the terminal verdict. Successful normal-run archive → outer-owner completion per the canonical task-completion reference, after any terminal queue commit. Verdicts backed by spec/verification findings (NEEDS_WORK, BLOCKED:repair-budget-exhausted) → adjudicate the findings, amend the spec (recorded in the spec file; spec stays read-only inside a run), commit that scoped amendment, then re-invoke — at most 3 outer iterations, then surface with the findings trail. Infrastructure, invalid-input, engine-availability, and implement-empty BLOCKED verdicts are not spec-amendable: surface immediately. Every iteration re-enters through committed durable artifacts (spec, findings, run archive), never conversation memory.
|
|
64
64
|
|
|
65
|
-
**Intent queue (unattended drain)**: the user stacks intents in `docs/specs/queue.md` (ordered checklist; `/devlyn-queue` is the front-end — no args shows status, `add <intent>` appends, `drain` starts the serial drain). Drain strictly serially — per item: spec it, commit the scoped owner baseline, run the outer loop, mark `[x]` done or `[F]` blocked with reason, commit that terminal transition, then complete eligible delivery once. Retain PR/pending workspaces; unavailable separate safe placement stops the drain with remaining items pending. A product-blocked item otherwise never halts the queue. The queue entry is the user's go-ahead, so assume-and-log replaces the interactive checkpoint — but unattended assumptions may only take scope-narrowing, reversible, non-user-visible defaults. Material ambiguity (user-visible behavior, data/state semantics, new files/scripts/flags, implementation surface) → mark `[F] needs-review`, commit it, and move on. End the drain with a per-item product verdict, separate delivery status/URL/resume and assumptions report. The canonical commit order is `
|
|
65
|
+
**Intent queue (unattended drain)**: the user stacks intents in `docs/specs/queue.md` (ordered checklist; `/devlyn-queue` is the front-end — no args shows status, `add <intent>` appends, `drain` starts the serial drain). Drain strictly serially — per item: spec it, commit the scoped owner baseline, run the outer loop, mark `[x]` done or `[F]` blocked with reason, commit that terminal transition, then complete eligible delivery once. Retain PR/pending workspaces; unavailable separate safe placement stops the drain with remaining items pending. A product-blocked item otherwise never halts the queue. The queue entry is the user's go-ahead, so assume-and-log replaces the interactive checkpoint — but unattended assumptions may only take scope-narrowing, reversible, non-user-visible defaults. Material ambiguity (user-visible behavior, data/state semantics, new files/scripts/flags, implementation surface) → mark `[F] needs-review`, commit it, and move on. End the drain with a per-item product verdict, separate delivery status/URL/resume and assumptions report. The canonical commit order is `references/outer-loop.md` in the installed `devlyn-resolve` skill.
|
|
66
66
|
|
|
67
67
|
## Subtractive-first editing — perfection = nothing left to remove
|
|
68
68
|
<!-- runtime-principles:section=subtractive-first:begin -->
|
package/bin/devlyn.js
CHANGED
|
@@ -31,7 +31,6 @@ const RENAMED_SKILLS = {
|
|
|
31
31
|
'devlyn:pencil-push': 'devlyn-pencil-push',
|
|
32
32
|
'devlyn:reap': 'devlyn-reap',
|
|
33
33
|
};
|
|
34
|
-
const DEVLYN_SKILL_DIR_STAMP = '__DEVLYN_SKILL_DIR__';
|
|
35
34
|
const DEVLYN_INSTALL_MARKER = '.devlyn-install.json';
|
|
36
35
|
|
|
37
36
|
// Every spelling a colon name can have on disk: its own and npm's U+F03A extraction alias.
|
|
@@ -47,11 +46,25 @@ const PRE_STANDARD_SKILL_MD_SHA256 = new Set([
|
|
|
47
46
|
'5f2e8b29609cbb2af72941579823bec598265ebf29cd3a375d6aa47d95b74354', // devlyn-pencil-push
|
|
48
47
|
]);
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
// Skills devlyn-cli shipped and later deleted without listing them, by SHA-256 of their only
|
|
50
|
+
// file, SKILL.md (LF endings): a user's own folder of the same name is never removed.
|
|
51
|
+
const RETIRED_SKILL_MD_SHA256 = {
|
|
52
|
+
// 0.2.0-1.15.0; deleted in iter-0034, it still routes agents to retired commands.
|
|
53
|
+
'workflow-routing': new Set([
|
|
54
|
+
'7fec2bb20d808a873a975d64a6223e404bc5b328c800760bb77257ae6b2f467a',
|
|
55
|
+
'8bfdaeff2ce48d5a79b5653de3bbe0d56a61a7fd750ed3ad11149fec5a502ecf',
|
|
56
|
+
]),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Whether `dir` is a real folder whose only file (dotfiles such as .DS_Store aside) is a SKILL.md
|
|
60
|
+
// with an LF-normalized SHA-256 in `hashes`: an unedited copy devlyn-cli shipped, not something
|
|
61
|
+
// the user added to or wrote.
|
|
62
|
+
function isShippedCopy(dir, hashes) {
|
|
51
63
|
const skill = path.join(dir, 'SKILL.md');
|
|
52
64
|
return fs.lstatSync(dir, { throwIfNoEntry: false })?.isDirectory() === true
|
|
65
|
+
&& fs.readdirSync(dir).filter((name) => !name.startsWith('.')).length === 1
|
|
53
66
|
&& fs.lstatSync(skill, { throwIfNoEntry: false })?.isFile() === true
|
|
54
|
-
&&
|
|
67
|
+
&& hashes.has(crypto.createHash('sha256')
|
|
55
68
|
.update(fs.readFileSync(skill, 'utf8').replace(/\r\n/g, '\n')).digest('hex'));
|
|
56
69
|
}
|
|
57
70
|
|
|
@@ -368,6 +381,14 @@ function cleanupDeprecated(targetDir) {
|
|
|
368
381
|
removed++;
|
|
369
382
|
}
|
|
370
383
|
}
|
|
384
|
+
for (const [name, hashes] of Object.entries(RETIRED_SKILL_MD_SHA256)) {
|
|
385
|
+
const fullPath = path.join(targetDir, 'skills', name);
|
|
386
|
+
if (isShippedCopy(fullPath, hashes)) {
|
|
387
|
+
fs.rmSync(fullPath, { recursive: true });
|
|
388
|
+
log(` ✕ skills/${name}/ (removed)`, 'dim');
|
|
389
|
+
removed++;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
371
392
|
return removed;
|
|
372
393
|
}
|
|
373
394
|
|
|
@@ -380,7 +401,7 @@ function retireRenamedSkills(skillsDir) {
|
|
|
380
401
|
for (const [oldName, newName] of Object.entries(RENAMED_SKILLS)) {
|
|
381
402
|
const found = legacySkillPaths(skillsDir, oldName).filter((fullPath) => fs.existsSync(fullPath));
|
|
382
403
|
if (found.length > 0 && optional.has(newName)) installOptionalSkillInto(skillsDir, newName);
|
|
383
|
-
else if (
|
|
404
|
+
else if (isShippedCopy(path.join(skillsDir, newName), PRE_STANDARD_SKILL_MD_SHA256)) refreshPreStandardCopy(skillsDir, newName);
|
|
384
405
|
for (const fullPath of found) {
|
|
385
406
|
fs.rmSync(fullPath, { recursive: true, force: true });
|
|
386
407
|
log(` ✕ ${path.basename(fullPath)}/ (renamed to ${newName})`, 'dim');
|
|
@@ -454,40 +475,6 @@ function writeInstallMarker(skillsDir) {
|
|
|
454
475
|
}
|
|
455
476
|
}
|
|
456
477
|
|
|
457
|
-
function shellDoubleQuoteDefault(value) {
|
|
458
|
-
if (value.includes('\n')) {
|
|
459
|
-
throw new Error(`Cannot stamp skill path with newline: ${value}`);
|
|
460
|
-
}
|
|
461
|
-
return value.replace(/[\\$"`}]/g, '\\$&');
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function stampInstalledSkillDir(rootDir, skillDir) {
|
|
465
|
-
// Stamp ONLY the assignment-default occurrence. The sentinel comparison
|
|
466
|
-
// literal on the guard line must stay intact: stamping it makes the guard
|
|
467
|
-
// compare the stamped default to itself and false-positive
|
|
468
|
-
// BLOCKED:shared-dir-unresolved on every codex/omp run that executes the
|
|
469
|
-
// block (CLAUDE_SKILL_DIR unset) — iter-0040 R3 latent finding.
|
|
470
|
-
const assignmentStamp = '${CLAUDE_SKILL_DIR:-' + DEVLYN_SKILL_DIR_STAMP + '}';
|
|
471
|
-
const stampedAssignment = '${CLAUDE_SKILL_DIR:-' + shellDoubleQuoteDefault(skillDir) + '}';
|
|
472
|
-
|
|
473
|
-
function visit(current) {
|
|
474
|
-
const stats = fs.statSync(current);
|
|
475
|
-
if (stats.isDirectory()) {
|
|
476
|
-
for (const item of fs.readdirSync(current)) {
|
|
477
|
-
visit(path.join(current, item));
|
|
478
|
-
}
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
if (path.extname(current) !== '.md') return;
|
|
482
|
-
|
|
483
|
-
const content = fs.readFileSync(current, 'utf8');
|
|
484
|
-
if (!content.includes(assignmentStamp)) return;
|
|
485
|
-
fs.writeFileSync(current, content.split(assignmentStamp).join(stampedAssignment));
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
visit(rootDir);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
478
|
// Dev artifacts that live under config/skills/ but must never ship or install.
|
|
492
479
|
// Mirrors the `!` exclusions in package.json files[].
|
|
493
480
|
const UNSHIPPED_SKILL_DIRS = new Set([
|
|
@@ -642,7 +629,6 @@ function installOptionalSkillInto(target, skillName) {
|
|
|
642
629
|
const dest = path.join(target, skillName);
|
|
643
630
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
644
631
|
copyRecursive(path.join(OPTIONAL_SKILLS_SOURCE, skillName), dest, target);
|
|
645
|
-
stampInstalledSkillDir(dest, dest);
|
|
646
632
|
assertCompleteSkillInstall(OPTIONAL_SKILLS_SOURCE, target, [skillName]);
|
|
647
633
|
const oldName = Object.keys(RENAMED_SKILLS).find((name) => RENAMED_SKILLS[name] === skillName);
|
|
648
634
|
for (const fullPath of oldName ? legacySkillPaths(target, oldName) : []) {
|
|
@@ -737,7 +723,6 @@ function installSkillsForCLI(cliKey) {
|
|
|
737
723
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
738
724
|
}
|
|
739
725
|
copyRecursive(src, dest, cli.skillsDir);
|
|
740
|
-
stampInstalledSkillDir(dest, dest);
|
|
741
726
|
copied++;
|
|
742
727
|
log(` → ${cli.skillsDir.replace(os.homedir(), '~')}/${skillName}`, 'dim');
|
|
743
728
|
}
|
|
@@ -854,10 +839,6 @@ function installClaudeCore() {
|
|
|
854
839
|
}
|
|
855
840
|
copyRecursive(CONFIG_SOURCE, targetDir, targetDir);
|
|
856
841
|
assertCompleteSkillInstall(path.join(CONFIG_SOURCE, 'skills'), skillsDir, DEVLYN_CORE_SKILLS);
|
|
857
|
-
for (const name of DEVLYN_CORE_SKILLS) {
|
|
858
|
-
const dest = path.join(skillsDir, name);
|
|
859
|
-
stampInstalledSkillDir(dest, dest);
|
|
860
|
-
}
|
|
861
842
|
retireRenamedSkills(skillsDir);
|
|
862
843
|
writeInstallMarker(skillsDir);
|
|
863
844
|
|
|
@@ -196,6 +196,7 @@
|
|
|
196
196
|
"76517a747d47a3986b7181db4bbe5898ad3d6d82644ce39d8bf62edc998cecb9",
|
|
197
197
|
"766c776b750cb84f084c49679fa377899693acf5f8d4e00751ca0fab97b4f030",
|
|
198
198
|
"78690fc75b54ee634413fd0d5f6892692b2557e8838fbb8453f7bdd4b6519cc5",
|
|
199
|
+
"7a0d897737f705b0276b829df5b62fd9541e3897b872b2a2668f54e08b88dfff",
|
|
199
200
|
"7aab816bfa43d73b4eb8bd9136242e8a4938f3610e7ad3c32e033b7ee3d28a1e",
|
|
200
201
|
"7fde023e25658ff48c67b6708c5b6f383a6497a509946cb3bdc5da7d35e4f3f6",
|
|
201
202
|
"8211bb3dcc9d7df40c393a4ef8e620526d84ded9faec25655cfa74801458515d",
|
|
@@ -455,6 +456,7 @@
|
|
|
455
456
|
"3ab5f6021b62f21624e3eed505cc6849c66972d01ac67337533cff0e81dc9dc8",
|
|
456
457
|
"3afe3870751373b6059ebddfb6c179ab70b7a1f3a9299555ecf5ea47c2970422",
|
|
457
458
|
"3b5bf36be5ad797443dc5c1c407684c6f86b28894ca9a975a3fb5c18b3066fbb",
|
|
459
|
+
"3ba1747fbe0662a0aa9c42e6e442748f013a8e90febc330e2854ab9ad841dd77",
|
|
458
460
|
"3cfc73fecd13c54fd400f5b04b2c4113cc776c102f6f09eaa401252276818e17",
|
|
459
461
|
"3d321af4f48efc92735cc8772cf092f15789ac63e1e20f857ba20c0532453eac",
|
|
460
462
|
"3def30362c96b6238021ab82e6f2ebd2974005ca94a84008f43528aaa7eb435f",
|
|
@@ -6,20 +6,20 @@ Single source of truth for how every skill calls Codex. **MCP is not used.** Ski
|
|
|
6
6
|
|
|
7
7
|
All long-running Codex calls go through `codex-monitored.sh`. It streams full stdout and emits a `[codex-monitored] heartbeat` every 30s on stderr. Write multiline prompts as exact UTF-8 bytes to a task-local file; set `DEVLYN_CODEX_PROMPT_FILE` and pass the sole prompt argument `-`. The wrapper snapshots those bytes before dispatch and seals actual argv, prompt digest and completion in `<prompt-file>.transport.json`. Missing/unreadable files, competing prompt arguments and receipt/prompt mismatches fail before launch. Without file transport, stdin remains DEVNULL (avoiding the open-stdin plus argument-prompt hang).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
Use the DEVLYN_SHARED_DIR established by the invoking skill; if it was
|
|
10
|
+
lost, re-establish it by that skill's rules. When this document is
|
|
11
|
+
opened directly, bind DEVLYN_SHARED_DIR to its reader-supplied
|
|
12
|
+
containing directory, resolving directory symlinks. Never take it from
|
|
13
|
+
an environment variable, cwd or another installation; without either
|
|
14
|
+
source, stop with BLOCKED:skill-source-unresolved.
|
|
15
|
+
|
|
16
|
+
Bind CODEX_MONITORED_PATH to DEVLYN_SHARED_DIR/codex-monitored.sh and
|
|
17
|
+
verify the file before calling it; failure is
|
|
18
|
+
BLOCKED:shared-dir-unresolved with the failed path.
|
|
19
|
+
|
|
20
|
+
After establishing DEVLYN_SHARED_DIR in a Bash invocation:
|
|
11
21
|
```bash
|
|
12
|
-
DEVLYN_SKILL_DIR="${CLAUDE_SKILL_DIR:-__DEVLYN_SKILL_DIR__}"
|
|
13
|
-
if [ "$DEVLYN_SKILL_DIR" = "__DEVLYN_SKILL_DIR__" ] || [ ! -d "$DEVLYN_SKILL_DIR/../_shared" ]; then
|
|
14
|
-
echo "BLOCKED:shared-dir-unresolved: $DEVLYN_SKILL_DIR/../_shared" >&2
|
|
15
|
-
exit 1
|
|
16
|
-
fi
|
|
17
|
-
DEVLYN_SHARED_DIR="$(cd "$DEVLYN_SKILL_DIR/../_shared" && pwd)"
|
|
18
22
|
CODEX_MONITORED_PATH="$DEVLYN_SHARED_DIR/codex-monitored.sh"
|
|
19
|
-
if [ ! -f "$CODEX_MONITORED_PATH" ]; then
|
|
20
|
-
echo "BLOCKED:shared-dir-unresolved: $CODEX_MONITORED_PATH" >&2
|
|
21
|
-
exit 1
|
|
22
|
-
fi
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
**Read-only critique / adversarial review / debate** (`/devlyn-resolve` VERIFY pair-mode, plus any future ideate read-only critique). Codex returns findings on stdout; the orchestrator writes files.
|
|
@@ -10,16 +10,42 @@ $ARGUMENTS
|
|
|
10
10
|
</args>
|
|
11
11
|
|
|
12
12
|
<runtime_paths>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
fi
|
|
21
|
-
DEVLYN_SHARED_DIR="$(cd "$DEVLYN_SKILL_DIR/../_shared" && pwd)"
|
|
13
|
+
Before the first command, establish the bindings below.
|
|
14
|
+
|
|
15
|
+
Resolve bundled resources from the SKILL.md loaded for this invocation.
|
|
16
|
+
|
|
17
|
+
Reader-rendered directory hint:
|
|
18
|
+
```text
|
|
19
|
+
${CLAUDE_SKILL_DIR}
|
|
22
20
|
```
|
|
21
|
+
|
|
22
|
+
Treat the hint as literal path data, never shell code. If the reader
|
|
23
|
+
replaced it with an absolute directory, use that directory. Otherwise
|
|
24
|
+
use the filesystem path or base directory reported for this loaded
|
|
25
|
+
SKILL.md. Resolve virtual URIs through the reader's native filesystem
|
|
26
|
+
mapping. If available source locations name different directories,
|
|
27
|
+
stop.
|
|
28
|
+
|
|
29
|
+
Bind DEVLYN_SKILL_DIR to that absolute directory. Do not obtain this
|
|
30
|
+
binding from an environment variable, cwd, or another installation.
|
|
31
|
+
Verify its SKILL.md before proceeding. Missing or conflicting source
|
|
32
|
+
identity is BLOCKED:skill-source-unresolved; include the failed path
|
|
33
|
+
when known.
|
|
34
|
+
|
|
35
|
+
Resolve bundled references against this directory. These bindings are
|
|
36
|
+
workflow values: establish them explicitly using each tool or shell's
|
|
37
|
+
literal-path rules, and include their absolute values in every fresh
|
|
38
|
+
worker's prompt, telling it to set them from those values, never from
|
|
39
|
+
its inherited environment. Do not rely on shell state surviving between
|
|
40
|
+
calls.
|
|
41
|
+
|
|
42
|
+
Resolve directory symlinks on DEVLYN_SKILL_DIR before deriving its
|
|
43
|
+
sibling _shared. Bind that directory as DEVLYN_SHARED_DIR. References
|
|
44
|
+
written as _shared/... use this binding. Verify the directory and each
|
|
45
|
+
required resource before use; failure is BLOCKED:shared-dir-unresolved
|
|
46
|
+
with the failed path. Never search another installation.
|
|
47
|
+
|
|
48
|
+
Verify DEVLYN_SHARED_DIR/role-config.py and DEVLYN_SHARED_DIR/engine-doctor.sh before their respective commands. In omp, use `printf '%s\n' skill://devlyn-engines`.
|
|
23
49
|
</runtime_paths>
|
|
24
50
|
|
|
25
51
|
## No args — status + how to choose
|
|
@@ -28,22 +28,40 @@ Read `_shared/runtime-principles.md` (Subtractive-first / Goal-locked / Evidence
|
|
|
28
28
|
</harness_principles>
|
|
29
29
|
|
|
30
30
|
<runtime_paths>
|
|
31
|
-
Resolve
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
echo "BLOCKED:shared-dir-unresolved: $DEVLYN_SKILL_DIR/../_shared" >&2
|
|
37
|
-
exit 1
|
|
38
|
-
fi
|
|
39
|
-
DEVLYN_SHARED_DIR="$(cd "$DEVLYN_SKILL_DIR/../_shared" && pwd)"
|
|
40
|
-
if [ ! -f "$DEVLYN_SHARED_DIR/spec-verify-check.py" ]; then
|
|
41
|
-
echo "BLOCKED:shared-dir-unresolved: $DEVLYN_SHARED_DIR/spec-verify-check.py" >&2
|
|
42
|
-
exit 1
|
|
43
|
-
fi
|
|
31
|
+
Resolve bundled resources from the SKILL.md loaded for this invocation.
|
|
32
|
+
|
|
33
|
+
Reader-rendered directory hint:
|
|
34
|
+
```text
|
|
35
|
+
${CLAUDE_SKILL_DIR}
|
|
44
36
|
```
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
Treat the hint as literal path data, never shell code. If the reader
|
|
39
|
+
replaced it with an absolute directory, use that directory. Otherwise
|
|
40
|
+
use the filesystem path or base directory reported for this loaded
|
|
41
|
+
SKILL.md. Resolve virtual URIs through the reader's native filesystem
|
|
42
|
+
mapping. If available source locations name different directories,
|
|
43
|
+
stop.
|
|
44
|
+
|
|
45
|
+
Bind DEVLYN_SKILL_DIR to that absolute directory. Do not obtain this
|
|
46
|
+
binding from an environment variable, cwd, or another installation.
|
|
47
|
+
Verify its SKILL.md before proceeding. Missing or conflicting source
|
|
48
|
+
identity is BLOCKED:skill-source-unresolved; include the failed path
|
|
49
|
+
when known.
|
|
50
|
+
|
|
51
|
+
Resolve bundled references against this directory. These bindings are
|
|
52
|
+
workflow values: establish them explicitly using each tool or shell's
|
|
53
|
+
literal-path rules, and include their absolute values in every fresh
|
|
54
|
+
worker's prompt, telling it to set them from those values, never from
|
|
55
|
+
its inherited environment. Do not rely on shell state surviving between
|
|
56
|
+
calls.
|
|
57
|
+
|
|
58
|
+
Resolve directory symlinks on DEVLYN_SKILL_DIR before deriving its
|
|
59
|
+
sibling _shared. Bind that directory as DEVLYN_SHARED_DIR. References
|
|
60
|
+
written as _shared/... use this binding. Verify the directory and each
|
|
61
|
+
required resource before use; failure is BLOCKED:shared-dir-unresolved
|
|
62
|
+
with the failed path. Never search another installation.
|
|
63
|
+
|
|
64
|
+
Verify DEVLYN_SHARED_DIR/spec-verify-check.py before validation. Use `python3 "$DEVLYN_SHARED_DIR/spec-verify-check.py"` for spec validation commands after binding the path in that shell. In omp, use `printf '%s\n' skill://devlyn-ideate`.
|
|
47
65
|
</runtime_paths>
|
|
48
66
|
|
|
49
67
|
<engine_routing>
|
|
@@ -9,19 +9,42 @@ Utility front-end for the intent-queue contract in the project instructions ("In
|
|
|
9
9
|
$ARGUMENTS
|
|
10
10
|
</args>
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
fi
|
|
20
|
-
DEVLYN_SHARED_DIR="$(cd "$DEVLYN_SKILL_DIR/../_shared" && pwd)"
|
|
12
|
+
Before `add` or `drain`, establish the bindings below.
|
|
13
|
+
|
|
14
|
+
Resolve bundled resources from the SKILL.md loaded for this invocation.
|
|
15
|
+
|
|
16
|
+
Reader-rendered directory hint:
|
|
17
|
+
```text
|
|
18
|
+
${CLAUDE_SKILL_DIR}
|
|
21
19
|
```
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
Treat the hint as literal path data, never shell code. If the reader
|
|
22
|
+
replaced it with an absolute directory, use that directory. Otherwise
|
|
23
|
+
use the filesystem path or base directory reported for this loaded
|
|
24
|
+
SKILL.md. Resolve virtual URIs through the reader's native filesystem
|
|
25
|
+
mapping. If available source locations name different directories,
|
|
26
|
+
stop.
|
|
27
|
+
|
|
28
|
+
Bind DEVLYN_SKILL_DIR to that absolute directory. Do not obtain this
|
|
29
|
+
binding from an environment variable, cwd, or another installation.
|
|
30
|
+
Verify its SKILL.md before proceeding. Missing or conflicting source
|
|
31
|
+
identity is BLOCKED:skill-source-unresolved; include the failed path
|
|
32
|
+
when known.
|
|
33
|
+
|
|
34
|
+
Resolve bundled references against this directory. These bindings are
|
|
35
|
+
workflow values: establish them explicitly using each tool or shell's
|
|
36
|
+
literal-path rules, and include their absolute values in every fresh
|
|
37
|
+
worker's prompt, telling it to set them from those values, never from
|
|
38
|
+
its inherited environment. Do not rely on shell state surviving between
|
|
39
|
+
calls.
|
|
40
|
+
|
|
41
|
+
Resolve directory symlinks on DEVLYN_SKILL_DIR before deriving its
|
|
42
|
+
sibling _shared. Bind that directory as DEVLYN_SHARED_DIR. References
|
|
43
|
+
written as _shared/... use this binding. Verify the directory and each
|
|
44
|
+
required resource before use; failure is BLOCKED:shared-dir-unresolved
|
|
45
|
+
with the failed path. Never search another installation.
|
|
46
|
+
|
|
47
|
+
For `add`, verify DEVLYN_SKILL_DIR/scripts/append.py before invoking it. For `drain`, before processing the first item, read and obey ../devlyn-resolve/references/outer-loop.md (its scoped-commit order is binding) and read ../devlyn-resolve/references/task-completion.md, both relative to this bound skill directory. Load the sibling devlyn-resolve/SKILL.md by its absolute path and preserve this bundle's DEVLYN_SHARED_DIR when handing off; do not rediscover resolve by its bare command name. In omp, use `printf '%s\n' skill://devlyn-queue`.
|
|
25
48
|
|
|
26
49
|
Queue state is base's `docs/specs/queue.md` plus owned receipts: for each `$(git rev-parse --git-common-dir)/devlyn-completion/*/receipt.json`, read `git show <ref>:docs/specs/queue.md` from its `recovery_ref` if that ref exists (it is never deleted), else its `branch` if it still exists; an item unmarked on base but `[x]`/`[F]` there counts as that mark. Two different terminal marks for one item stop for inspection. Status and drain selection both use this view.
|
|
27
50
|
|
|
@@ -31,10 +54,10 @@ Read the queue state above (absent `docs/specs/queue.md` → report "queue empty
|
|
|
31
54
|
|
|
32
55
|
## Subcommands
|
|
33
56
|
|
|
34
|
-
- `add <intent text>` —
|
|
57
|
+
- `add <intent text>` — use the Write tool to place the exact full intent in a new file `.devlyn/queue-intent-<unique>.txt` (a name no other add uses, made of letters, digits, `.`, `_` and `-`, e.g. `queue-intent-20261001-1230-k3x9.txt`); do not create it with shell syntax. If the conversation already produced a spec, the file must start with `(spec: docs/specs/<id>/spec.md)`. Then, from the project root, run exactly once `python3 "$DEVLYN_SKILL_DIR/scripts/append.py" .devlyn/queue-intent-<unique>.txt`. The helper consumes that file, appends one `- [ ] <intent>` line at the physical end of `docs/specs/queue.md` under a lock (creating it with its header if missing), and is the sole queue writer. Never edit `docs/specs/queue.md` directly for `add` and never use a direct-edit fallback; a nonzero helper exit is shown to the user and stops the `add`.
|
|
35
58
|
- `drain` — serial drain per the project-instructions contract. For each pending item, in order:
|
|
36
59
|
1. Allocate the owner's absent task branch with `--worktree <absent path>` per `../devlyn-resolve/references/task-completion.md`, and work in that worktree; then spec it if unspecced (the queue entry is the user's go-ahead). Unattended assumptions may only take scope-narrowing, reversible, non-user-visible defaults; material ambiguity (user-visible behavior, data/state semantics, new files/scripts/flags, implementation surface) → mark `[F] needs-review: <question>`, commit that queue transition, and continue.
|
|
37
|
-
2. Bring only the current queue-item delta and linked spec bundle from the queue view into the task worktree, which starts from the fetched remote base with no other local commits, commit that scoped owner baseline, then run
|
|
60
|
+
2. Bring only the current queue-item delta and linked spec bundle from the queue view into the task worktree, which starts from the fetched remote base with no other local commits, commit that scoped owner baseline, then run the sibling devlyn-resolve loaded above with `--spec <path>` hands-free.
|
|
38
61
|
After every resolve invocation, run `python3 "$DEVLYN_SHARED_DIR/terminal-claim-check.py" .`; exit 79 marks `[F] FAILED-INCOMPLETE` from the predicate, never from the session self-report.
|
|
39
62
|
3. Outer loop on the terminal verdict: PASS → mark `[x]`. Findings-backed verdicts (NEEDS_WORK, BLOCKED:repair-budget-exhausted) → amend the spec, commit that scoped amendment, then re-run — at most 3 outer iterations. Infrastructure / invalid-input / engine-availability / implement-empty BLOCKED verdicts are not spec-amendable → mark `[F] <verdict>` immediately.
|
|
40
63
|
4. Commit each terminal `[x]` / `[F]` queue transition before advancing. For successfully archived normal runs, invoke owner completion once after that declared queue-file-only commit; bind it separately from the verified source. Honor local-only/no-push. Failed products never publish. Retain PR/pending workspaces and use a separate owned branch from base for the next item; unavailable safe placement stops the drain with remaining items pending. A product-blocked item otherwise never halts the queue.
|
|
@@ -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())
|
|
@@ -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>
|
|
@@ -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 . \
|
|
@@ -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
|
|
|
@@ -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.
|
|
@@ -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`
|
|
@@ -13,15 +13,45 @@ description: >
|
|
|
13
13
|
user-invocable: true
|
|
14
14
|
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Agent
|
|
15
15
|
argument-hint: "<template_path> <sources_folder> | improve [instruction]"
|
|
16
|
-
context:
|
|
17
|
-
- type: file
|
|
18
|
-
path: ${CLAUDE_SKILL_DIR}/PIPELINE.md
|
|
19
16
|
---
|
|
20
17
|
|
|
21
18
|
# Dokkit — One-Command Document Filling
|
|
22
19
|
|
|
23
20
|
Source folder + template → finished document. Fully automatic, iterates until perfect.
|
|
24
21
|
|
|
22
|
+
Resolve bundled resources from the SKILL.md loaded for this invocation.
|
|
23
|
+
|
|
24
|
+
Reader-rendered directory hint:
|
|
25
|
+
```text
|
|
26
|
+
${CLAUDE_SKILL_DIR}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Treat the hint as literal path data, never shell code. If the reader
|
|
30
|
+
replaced it with an absolute directory, use that directory. Otherwise
|
|
31
|
+
use the filesystem path or base directory reported for this loaded
|
|
32
|
+
SKILL.md. Resolve virtual URIs through the reader's native filesystem
|
|
33
|
+
mapping. If available source locations name different directories,
|
|
34
|
+
stop.
|
|
35
|
+
|
|
36
|
+
Bind DEVLYN_SKILL_DIR to that absolute directory. Do not obtain this
|
|
37
|
+
binding from an environment variable, cwd, or another installation.
|
|
38
|
+
Verify its SKILL.md before proceeding. Missing or conflicting source
|
|
39
|
+
identity is BLOCKED:skill-source-unresolved; include the failed path
|
|
40
|
+
when known.
|
|
41
|
+
|
|
42
|
+
Resolve bundled references against this directory. These bindings are
|
|
43
|
+
workflow values: establish them explicitly using each tool or shell's
|
|
44
|
+
literal-path rules, and include their absolute values in every fresh
|
|
45
|
+
worker's prompt, telling it to set them from those values, never from
|
|
46
|
+
its inherited environment. Do not rely on shell state surviving between
|
|
47
|
+
calls.
|
|
48
|
+
|
|
49
|
+
Read PIPELINE.md from this bound directory before executing the
|
|
50
|
+
workflow. Bundled Markdown, references, and scripts use this same
|
|
51
|
+
directory. Project data under .dokkit remains project-relative.
|
|
52
|
+
Pass the absolute binding and required resource paths explicitly
|
|
53
|
+
to every worker.
|
|
54
|
+
|
|
25
55
|
## Usage
|
|
26
56
|
|
|
27
57
|
```
|
|
@@ -113,7 +143,7 @@ These rules prevent the font corruption issues seen in previous versions:
|
|
|
113
143
|
|
|
114
144
|
**Never write inline Gemini API calls for image generation.** Always use the provided script:
|
|
115
145
|
```bash
|
|
116
|
-
python $
|
|
146
|
+
python "$DEVLYN_SKILL_DIR/scripts/source_images.py" generate \
|
|
117
147
|
--prompt "<prompt>" --preset <preset> --output-dir .dokkit/images/ \
|
|
118
148
|
--project-dir . --lang ko
|
|
119
149
|
```
|
|
@@ -173,7 +203,7 @@ display_h = int(actual_h * scale)
|
|
|
173
203
|
|
|
174
204
|
| File | Purpose | Used By |
|
|
175
205
|
|------|---------|---------|
|
|
176
|
-
| `PIPELINE.md` | Detailed pipeline steps (
|
|
206
|
+
| `PIPELINE.md` | Detailed pipeline steps (read before execution) | Orchestrator |
|
|
177
207
|
| `STATE.md` | State schema and management | All agents |
|
|
178
208
|
| `INGESTION.md` | Source file parsing | Ingestor |
|
|
179
209
|
| `ANALYSIS.md` | Field detection, structure mapping | Analyzer |
|
|
@@ -33,7 +33,7 @@ After any mutation:
|
|
|
33
33
|
1. Read current state.json (avoid overwriting concurrent changes)
|
|
34
34
|
2. Update only the relevant fields
|
|
35
35
|
3. Write the full state back
|
|
36
|
-
4. Validate: `python
|
|
36
|
+
4. Validate: `python "$DEVLYN_SKILL_DIR/scripts/validate_state.py" .dokkit/state.json`
|
|
37
37
|
|
|
38
38
|
## State Transitions
|
|
39
39
|
|
|
@@ -113,7 +113,7 @@ Each opportunity is added to the field's `image_opportunities` array:
|
|
|
113
113
|
- `insertion_point.strategy`: Always `"after_paragraph"` for section content
|
|
114
114
|
- `insertion_point.anchor_text`: Distinctive Korean phrase from the paragraph (used by filler to locate insertion point)
|
|
115
115
|
- `generation_prompt`: English prompt for AI image generation
|
|
116
|
-
- `preset`: Maps to
|
|
116
|
+
- `preset`: Maps to the bundled `scripts/source_images.py` preset parameter
|
|
117
117
|
- `content_type`: One of `flowchart`, `diagram`, `data`, `concept`, `infographic`
|
|
118
118
|
- `rationale`: Brief explanation of why an image helps here
|
|
119
119
|
- `dimensions`: Default size — filler may adjust based on content_type
|
|
@@ -46,7 +46,6 @@ Complete catalog of YAML frontmatter fields for Claude Code skill files (`SKILL.
|
|
|
46
46
|
| `argument-hint` | string | none | Placeholder shown to user (e.g., `"[file path]"`) |
|
|
47
47
|
| `user-invocable` | boolean | `true` | Whether users can trigger via `/skill-name` |
|
|
48
48
|
| `disable-model-invocation` | boolean | `false` | If `true`, Claude cannot proactively trigger the skill |
|
|
49
|
-
| `context` | list | none | Files to inject into context when skill activates |
|
|
50
49
|
| `agent` | object | none | Run skill as a subagent with its own context |
|
|
51
50
|
| `model` | string | inherited | Override model (`opus`, `sonnet`, `haiku`) |
|
|
52
51
|
| `hooks` | object | none | Shell commands to run on skill lifecycle events |
|
|
@@ -62,20 +61,15 @@ allowed-tools: Read, Bash(npm test *), Bash(npx *)
|
|
|
62
61
|
|
|
63
62
|
Wildcard `*` matches any arguments. Without a glob, Bash runs any command.
|
|
64
63
|
|
|
65
|
-
###
|
|
64
|
+
### Supporting files
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
Read supporting files explicitly from the skill body:
|
|
68
67
|
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
path: ${CLAUDE_SKILL_DIR}/PATTERNS.md
|
|
73
|
-
- type: file
|
|
74
|
-
path: ./CLAUDE.md
|
|
68
|
+
```text
|
|
69
|
+
Before proceeding, read PATTERNS.md relative to the directory
|
|
70
|
+
containing this loaded SKILL.md.
|
|
75
71
|
```
|
|
76
72
|
|
|
77
|
-
Use `${CLAUDE_SKILL_DIR}` for paths relative to the skill directory.
|
|
78
|
-
|
|
79
73
|
### `agent` Field
|
|
80
74
|
|
|
81
75
|
Run the skill as an isolated subagent:
|
|
@@ -102,7 +96,10 @@ hooks:
|
|
|
102
96
|
|
|
103
97
|
## String Substitutions
|
|
104
98
|
|
|
105
|
-
|
|
99
|
+
Claude Code replaces these placeholders when rendering a skill.
|
|
100
|
+
They are reader substitutions, not portable shell environment
|
|
101
|
+
variables. Portable resource references use the selected skill
|
|
102
|
+
directory and relative paths.
|
|
106
103
|
|
|
107
104
|
| Placeholder | Replaced With |
|
|
108
105
|
|---|---|
|
|
@@ -116,7 +113,7 @@ These placeholders are replaced at runtime:
|
|
|
116
113
|
```markdown
|
|
117
114
|
Parse `$ARGUMENTS` for the file path.
|
|
118
115
|
If `$1` is empty, ask the user for a target file.
|
|
119
|
-
Read
|
|
116
|
+
Read config.yaml relative to this loaded skill's directory.
|
|
120
117
|
```
|
|
121
118
|
|
|
122
119
|
---
|
|
@@ -15,6 +15,13 @@ argument-hint: "[skill description or name]"
|
|
|
15
15
|
Create production-quality Claude Code skills with correct frontmatter, structured workflows, and Claude 4.6 prompt patterns.
|
|
16
16
|
|
|
17
17
|
Reference files in this skill directory:
|
|
18
|
+
|
|
19
|
+
Resolve the bundled reference paths below against the directory
|
|
20
|
+
containing the SKILL.md loaded for this invocation, using its opened
|
|
21
|
+
path, reader-reported base directory, or native URI mapping. Never
|
|
22
|
+
resolve them against cwd or another installation. Missing source
|
|
23
|
+
identity is BLOCKED:skill-source-unresolved.
|
|
24
|
+
|
|
18
25
|
- `REFERENCE.md` — Complete frontmatter field catalog and validation rules
|
|
19
26
|
- `PROMPT-PATTERNS.md` — Claude 4.6 prompt engineering patterns for skill bodies
|
|
20
27
|
- `CHECKLIST.md` — Quality verification checklist
|
|
@@ -50,11 +57,13 @@ Estimate the total line count for the skill content:
|
|
|
50
57
|
Rules for reference files:
|
|
51
58
|
- One level deep only (no nested references)
|
|
52
59
|
- Add a table of contents if a reference file exceeds 100 lines
|
|
53
|
-
- Reference files use
|
|
60
|
+
- Reference files use paths relative to the generated SKILL.md, with
|
|
61
|
+
an explicit instruction to resolve them against the loaded skill
|
|
62
|
+
directory.
|
|
54
63
|
|
|
55
64
|
### Step 3: Write Frontmatter
|
|
56
65
|
|
|
57
|
-
Read
|
|
66
|
+
Read `REFERENCE.md` for the complete field catalog.
|
|
58
67
|
|
|
59
68
|
Apply these rules:
|
|
60
69
|
- `name`: lowercase, hyphens only, max 64 chars, no "anthropic" or "claude"
|
|
@@ -64,7 +73,7 @@ Apply these rules:
|
|
|
64
73
|
|
|
65
74
|
### Step 4: Write Skill Body
|
|
66
75
|
|
|
67
|
-
Read
|
|
76
|
+
Read `PROMPT-PATTERNS.md` for Claude 4.6 patterns.
|
|
68
77
|
|
|
69
78
|
Structure the body in this order:
|
|
70
79
|
1. **Title and purpose** — One-line summary of what the skill does and why
|
|
@@ -111,7 +120,7 @@ For multi-file skills, create reference files:
|
|
|
111
120
|
|
|
112
121
|
### Step 7: Validate
|
|
113
122
|
|
|
114
|
-
Read
|
|
123
|
+
Read `CHECKLIST.md` and verify the generated skill against every item.
|
|
115
124
|
|
|
116
125
|
Fix any issues before presenting the final output.
|
|
117
126
|
|
|
@@ -124,7 +133,6 @@ After generating the skill, present:
|
|
|
124
133
|
3. **Test invocation** — Example command to test the skill
|
|
125
134
|
|
|
126
135
|
Offer to:
|
|
127
|
-
- Run `/devlyn:review` on the generated skill for quality assurance
|
|
128
136
|
- Run `pyx-scan` if the skill will be published
|
|
129
137
|
|
|
130
138
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devlyn-cli",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "Loop & harness engineering for AI coding agents (Claude Code, Codex, and any AGENTS.md-driven agent) — take intent to shipped, engineer-quality software hands-free with phase-gated pipelines and agent orchestration",
|
|
5
5
|
"homepage": "https://github.com/fysoul17/devlyn-cli#readme",
|
|
6
6
|
"bin": {
|