@yeison.restrepo.r/code-conductor 1.23.1 → 1.23.3
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/bin/code-conductor.mjs +1 -1
- package/global/commands/cc-checkpoint.md +5 -5
- package/global/commands/cc-compact.md +5 -5
- package/lib/installer/deploy.mjs +21 -1
- package/package.json +2 -1
- package/project-template/.claude/commands/cc-implement.md +4 -4
- package/project-template/.claude/commands/cc-plan.md +3 -3
- package/project-template/.claude/commands/cc-spec.md +3 -3
- package/scripts/conductor-db.mjs +450 -0
- package/scripts/detect-stack.mjs +679 -0
- package/scripts/resume-read.mjs +160 -0
- package/scripts/session-id.mjs +67 -0
- package/scripts/snap-build.mjs +90 -0
- package/scripts/snap-validate.mjs +32 -0
package/bin/code-conductor.mjs
CHANGED
|
@@ -87,7 +87,7 @@ export function run(argv, env = process.env, { cwd = process.cwd(), log } = {})
|
|
|
87
87
|
// and the environment codes (EROFS/EACCES/EPERM), which are exit 1 regardless.
|
|
88
88
|
let writing = false;
|
|
89
89
|
try {
|
|
90
|
-
assertAssets(assetRoot, ['global', 'skills', 'project-template']); // pre-flight
|
|
90
|
+
assertAssets(assetRoot, ['global', 'skills', 'scripts', 'project-template']); // pre-flight
|
|
91
91
|
writing = true; // copy phase begins
|
|
92
92
|
const claudeDir = deployGlobal(assetRoot, home);
|
|
93
93
|
chmodHooks(claudeDir);
|
|
@@ -32,16 +32,16 @@ Run **only after** the `project.md` append succeeded (authoritative). Any failur
|
|
|
32
32
|
|
|
33
33
|
1. Ensure `.conductor/` exists (`mkdir -p .conductor`, best-effort); if that fails, skip the tail.
|
|
34
34
|
2. Derive `c` — the **full-40** `git rev-parse HEAD` (lowercased, `/^[0-9a-f]{7,40}$/`, `"0000000"` fallback on non-zero/timeout/git-absent/format-mismatch; shell `timeout` only if the binary exists).
|
|
35
|
-
3. Resolve the id: `id="$(node scripts/session-id.mjs 2>>.conductor/last-write.log)"`.
|
|
36
|
-
4. Derive `ph` by carry-forward: with a bounded ~5 s outer timeout (only if `timeout` exists; `conductor-db`'s internal 2 s `busy_timeout` bounds real contention otherwise), run `node <probe-flags> scripts/conductor-db.mjs get-session "$id"`; if it prints a row whose `phase` is a non-empty enum member, reuse it. On no row, empty phase, DB unavailable, or timeout, default to `"impl"`.
|
|
35
|
+
3. Resolve the id: `id="$(node .claude/scripts/session-id.mjs 2>>.conductor/last-write.log)"`.
|
|
36
|
+
4. Derive `ph` by carry-forward: with a bounded ~5 s outer timeout (only if `timeout` exists; `conductor-db`'s internal 2 s `busy_timeout` bounds real contention otherwise), run `node <probe-flags> .claude/scripts/conductor-db.mjs get-session "$id"`; if it prints a row whose `phase` is a non-empty enum member, reuse it. On no row, empty phase, DB unavailable, or timeout, default to `"impl"`.
|
|
37
37
|
5. Set `pr` to the **verbatim `## Checkpoint …` section body** just appended to `project.md` (the same in-context string) — so `project.md` and the snapshot's `pr` are byte-identical. `pr` is always non-empty here, so the blob is always **v2**.
|
|
38
38
|
6. Project `d` / `x` from that section (best-effort): match headings `/^#{2,4}[ \t]+(.+?)[ \t]*$/`; normalize the heading text by removing all `*`, `_`, `` ` `` then `.trim()` + lowercase; a heading containing `decision` or `convention` opens a `d` block, one containing `debt`, `workaround`, or `limitation` opens an `x` block. **The `d` set is tested first and wins** (a heading matching both opens one `d` block). The parent `## Checkpoint …` heading matches no keyword, so it opens no block and closes any open one. Within a block, bullet lines `/^[ \t]*[-*][ \t]+(\S.*?)[ \t]*$/` contribute their trimmed capture (line-based; continuation lines ignored — the full text survives in `pr`). If unstructured, `d`/`x` may be empty (valid).
|
|
39
39
|
7. Defaults: `n=[]`, `f=[]`, `s`= the active spec stem or `"none"`.
|
|
40
|
-
8. Build the v2 blob: pipe `{ph, c, s, n, f, d, x, pr}` on **stdin** to `node scripts/snap-build.mjs`; capture its stdout. If it exits non-zero, skip the DB write (fail-open).
|
|
40
|
+
8. Build the v2 blob: pipe `{ph, c, s, n, f, d, x, pr}` on **stdin** to `node .claude/scripts/snap-build.mjs`; capture its stdout. If it exits non-zero, skip the DB write (fail-open).
|
|
41
41
|
9. Write the rows (both argv double-quoted; blob on stdin; all output to the log):
|
|
42
42
|
|
|
43
|
-
`node <probe-flags> scripts/conductor-db.mjs session "$id" "$ph" "$s" "$c" >> .conductor/last-write.log 2>&1`
|
|
44
|
-
`printf '%s' "$snap_json" | node <probe-flags> scripts/conductor-db.mjs snapshot "$c" >> .conductor/last-write.log 2>&1`
|
|
43
|
+
`node <probe-flags> .claude/scripts/conductor-db.mjs session "$id" "$ph" "$s" "$c" >> .conductor/last-write.log 2>&1`
|
|
44
|
+
`printf '%s' "$snap_json" | node <probe-flags> .claude/scripts/conductor-db.mjs snapshot "$c" >> .conductor/last-write.log 2>&1`
|
|
45
45
|
|
|
46
46
|
All DB-tail redirects use **append mode (`>>`)** so concurrent runs never truncate a preceding trace. **Cross-platform note:** the forms above are Unix-canonical; on Windows/PowerShell realize the same semantics — set `$OutputEncoding = [System.Text.UTF8Encoding]::new($false)` before piping, capture `$id = node …`, pipe `$snap_json | node … snapshot "$c"`, append the log via `… 2>&1 | Out-File -Append -Encoding utf8 .conductor/last-write.log` (not `*>>`), and `New-Item -ItemType Directory -Force .conductor`.
|
|
47
47
|
|
|
@@ -15,7 +15,7 @@ Collect from the current conversation context:
|
|
|
15
15
|
|
|
16
16
|
For all four array fields: filter out empty/whitespace-only strings, deduplicate by exact case-sensitive string equality (first occurrence wins), then truncate each surviving element to its per-element cap (Unicode-safe: `Array.from(str).slice(0, cap).join('')`), then if the array still exceeds its cap, drop from the **head** (oldest first) until it fits. Empty arrays are valid and must still be serialized (never omitted).
|
|
17
17
|
|
|
18
|
-
Serialize by piping a flat JSON object `{ph, c, s, n, f, d, x}` (no `pr`) on **stdin** to `node scripts/snap-build.mjs`; its stdout is the canonical single-line **v1** SNAP JSON. `snap-build` performs all array normalization (filter/dedup/per-element cap/head-drop) and the 4096-char size trim internally — do not pre-serialize. If `snap-build` exits non-zero (malformed field object), report the error and stop; do not write the handoff file, do not run the DB tail, do not print the compact prompt.
|
|
18
|
+
Serialize by piping a flat JSON object `{ph, c, s, n, f, d, x}` (no `pr`) on **stdin** to `node .claude/scripts/snap-build.mjs`; its stdout is the canonical single-line **v1** SNAP JSON. `snap-build` performs all array normalization (filter/dedup/per-element cap/head-drop) and the 4096-char size trim internally — do not pre-serialize. If `snap-build` exits non-zero (malformed field object), report the error and stop; do not write the handoff file, do not run the DB tail, do not print the compact prompt.
|
|
19
19
|
|
|
20
20
|
Write the single JSON line, followed by exactly one trailing newline, to `.claude/memory/session-snapshot.json`.
|
|
21
21
|
|
|
@@ -36,14 +36,14 @@ Once the snapshot is written successfully, output exactly:
|
|
|
36
36
|
After the handoff file is written and the compact prompt is ready — and **only** if the authoritative write succeeded — run this synchronous, fail-open tail. Any failure here is non-fatal: never revert the handoff file, never suppress the compact prompt.
|
|
37
37
|
|
|
38
38
|
1. Ensure `.conductor/` exists (`mkdir -p .conductor`, best-effort). If that fails, skip the tail entirely.
|
|
39
|
-
2. Resolve the session id: `id="$(node scripts/session-id.mjs 2>>.conductor/last-write.log)"`.
|
|
39
|
+
2. Resolve the session id: `id="$(node .claude/scripts/session-id.mjs 2>>.conductor/last-write.log)"`.
|
|
40
40
|
3. Upsert the session row (Node-flag probe applies — same as the cc-implement Step 6 hook: no-flag-first, else `--experimental-sqlite --no-warnings`, else skip):
|
|
41
41
|
|
|
42
|
-
`node <probe-flags> scripts/conductor-db.mjs session "$id" "$ph" "$s" "$c" >> .conductor/last-write.log 2>&1`
|
|
42
|
+
`node <probe-flags> .claude/scripts/conductor-db.mjs session "$id" "$ph" "$s" "$c" >> .conductor/last-write.log 2>&1`
|
|
43
43
|
4. Insert the snapshot row, piping the v1 blob on **stdin** (never argv):
|
|
44
44
|
|
|
45
|
-
`printf '%s' "$snap_json" | node <probe-flags> scripts/conductor-db.mjs snapshot "$c" >> .conductor/last-write.log 2>&1`
|
|
45
|
+
`printf '%s' "$snap_json" | node <probe-flags> .claude/scripts/conductor-db.mjs snapshot "$c" >> .conductor/last-write.log 2>&1`
|
|
46
46
|
|
|
47
47
|
All argv scalars are double-quoted. **Every** DB-tail redirect uses **append mode (`>>`)** — never `>` — so a rapid or parallel second run never truncates a preceding trace; the log is gitignored, best-effort, and per-run growth is one line at most (rotation is out of scope). The lone `CONDUCTOR_DB:` degradation line (Node < 22.5 / `node:sqlite` absent) lands only in `.conductor/last-write.log`, never the UI. Finally print `> Snapshot written. Run /compact now to clear history.` regardless of the tail's outcome.
|
|
48
48
|
|
|
49
|
-
**Cross-platform note:** the incantations above are the **Unix (bash) canonical form**; the `.md` file is an agent instruction, not a literal script — realize the same semantics on the host shell. On Windows/PowerShell: **first set `$OutputEncoding = [System.Text.UTF8Encoding]::new($false)`** (no-BOM UTF-8, so the piped snapshot string reaches Node's UTF-8 `TextDecoder` byte-clean); capture the id via `$id = node scripts/session-id.mjs`; pipe the blob with `$snap_json | node … snapshot "$c"`; append the log with `… 2>&1 | Out-File -Append -Encoding utf8 .conductor/last-write.log` (explicit UTF-8 append — never the bare `*>>`, whose default encoding is UTF-16LE on PS 5.1 and would corrupt the trace); ensure the dir with `New-Item -ItemType Directory -Force .conductor`. The stdin-only-for-payloads, double-quoted-argv, append-log, and fail-open rules are identical on both platforms.
|
|
49
|
+
**Cross-platform note:** the incantations above are the **Unix (bash) canonical form**; the `.md` file is an agent instruction, not a literal script — realize the same semantics on the host shell. On Windows/PowerShell: **first set `$OutputEncoding = [System.Text.UTF8Encoding]::new($false)`** (no-BOM UTF-8, so the piped snapshot string reaches Node's UTF-8 `TextDecoder` byte-clean); capture the id via `$id = node .claude/scripts/session-id.mjs`; pipe the blob with `$snap_json | node … snapshot "$c"`; append the log with `… 2>&1 | Out-File -Append -Encoding utf8 .conductor/last-write.log` (explicit UTF-8 append — never the bare `*>>`, whose default encoding is UTF-16LE on PS 5.1 and would corrupt the trace); ensure the dir with `New-Item -ItemType Directory -Force .conductor`. The stdin-only-for-payloads, double-quoted-argv, append-log, and fail-open rules are identical on both platforms.
|
package/lib/installer/deploy.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cpSync, mkdirSync, chmodSync, existsSync, readdirSync, statSync } from 'node:fs';
|
|
1
|
+
import { cpSync, mkdirSync, chmodSync, existsSync, readdirSync, statSync, rmSync } from 'node:fs';
|
|
2
2
|
import { join, relative, sep } from 'node:path';
|
|
3
3
|
|
|
4
4
|
// Verified at plan time: `find global skills project-template -type l` returns
|
|
@@ -47,6 +47,20 @@ export function deployGlobal(assetRoot, home) {
|
|
|
47
47
|
return target;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// 1.23.2 deployed scripts/ as a sibling of .claude at <cwd>/scripts instead of
|
|
51
|
+
// nesting it under .claude, colliding with any scripts/ the host project already
|
|
52
|
+
// owned. Only remove the stale dir when its contents are EXACTLY the bundled
|
|
53
|
+
// script file set (no extras) - anything else is presumed to be the host's own
|
|
54
|
+
// scripts/ and is left untouched.
|
|
55
|
+
function sweepStaleRootScripts(assetRoot, cwd) {
|
|
56
|
+
const staleDir = join(cwd, 'scripts');
|
|
57
|
+
if (!existsSync(staleDir) || !statSync(staleDir).isDirectory()) return;
|
|
58
|
+
const bundled = readdirSync(join(assetRoot, 'scripts')).sort();
|
|
59
|
+
const present = readdirSync(staleDir).sort();
|
|
60
|
+
const matches = bundled.length === present.length && bundled.every((f, i) => f === present[i]);
|
|
61
|
+
if (matches) rmSync(staleDir, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
export function deployProject(assetRoot, cwd) {
|
|
51
65
|
const target = join(cwd, '.claude');
|
|
52
66
|
const templateRoot = join(assetRoot, 'project-template');
|
|
@@ -67,6 +81,12 @@ export function deployProject(assetRoot, cwd) {
|
|
|
67
81
|
if (name === '.claude') continue;
|
|
68
82
|
cpSync(join(templateRoot, name), join(cwd, name), CP_OPTS);
|
|
69
83
|
}
|
|
84
|
+
// scripts/ is a sibling of project-template/ under assetRoot (source layout),
|
|
85
|
+
// but is deployed as a CHILD of .claude/ (target) - it must stay inside the
|
|
86
|
+
// project's self-contained .claude footprint, not spill into the host's own
|
|
87
|
+
// project root where it could collide with a scripts/ dir the host already owns.
|
|
88
|
+
sweepStaleRootScripts(assetRoot, cwd);
|
|
89
|
+
cpSync(join(assetRoot, 'scripts'), join(target, 'scripts'), CP_OPTS);
|
|
70
90
|
return target;
|
|
71
91
|
}
|
|
72
92
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yeison.restrepo.r/code-conductor",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.3",
|
|
4
4
|
"description": "A spec-first, token-efficient Claude Code configuration and installer CLI.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"lib/",
|
|
16
16
|
"global/",
|
|
17
17
|
"skills/",
|
|
18
|
+
"scripts/",
|
|
18
19
|
"project-template/"
|
|
19
20
|
],
|
|
20
21
|
"engines": {
|
|
@@ -4,12 +4,12 @@ description: "(Conductor) Execute implementation tasks from an approved plan"
|
|
|
4
4
|
|
|
5
5
|
## Phase entry - Resume Read
|
|
6
6
|
|
|
7
|
-
Before doing anything else, restore any stored context for the current commit by running
|
|
7
|
+
Before doing anything else, restore any stored context for the current commit by running `.claude/scripts/resume-read.mjs`. It resolves the current git hash, prefers a valid DB snapshot (`conductor-db get-snapshot`), falls back to the `.claude/memory/session-snapshot.json` handoff file, and prints a `RESUME_HIT` block on a hit / nothing on a miss. Capture its stdout **and** its exit code with the canonical per-platform form (each first probes for `node` and treats its absence as a clean miss, never an error):
|
|
8
8
|
|
|
9
9
|
- **Unix / Git Bash:**
|
|
10
10
|
```sh
|
|
11
11
|
if command -v node >/dev/null 2>&1; then
|
|
12
|
-
resume_out="$(node scripts/resume-read.mjs 2>>.conductor/last-write.log)"; resume_rc=$?
|
|
12
|
+
resume_out="$(node .claude/scripts/resume-read.mjs 2>>.conductor/last-write.log)"; resume_rc=$?
|
|
13
13
|
else resume_rc=3; resume_out=""; fi
|
|
14
14
|
```
|
|
15
15
|
- **PowerShell:**
|
|
@@ -20,7 +20,7 @@ Before doing anything else, restore any stored context for the current commit by
|
|
|
20
20
|
$__nap = $PSNativeCommandUseErrorActionPreference; $PSNativeCommandUseErrorActionPreference = $false
|
|
21
21
|
}
|
|
22
22
|
try {
|
|
23
|
-
$resume_out = node scripts/resume-read.mjs 2>> .conductor/last-write.log; $resume_rc = $LASTEXITCODE
|
|
23
|
+
$resume_out = node .claude/scripts/resume-read.mjs 2>> .conductor/last-write.log; $resume_rc = $LASTEXITCODE
|
|
24
24
|
} catch { $resume_rc = 3; $resume_out = "" }
|
|
25
25
|
finally {
|
|
26
26
|
$ErrorActionPreference = $__eap
|
|
@@ -133,7 +133,7 @@ Runs after both success (`[X]`) and failure (`[!]`) paths. Record the task's fin
|
|
|
133
133
|
Each probe is a disposable child; a non-zero exit — including a fatal `bad option: --experimental-sqlite` from a Node that does not recognize the flag — is caught and simply advances to the next branch. The fatal startup error is therefore always contained inside a probe whose failure is expected; it never propagates and never aborts the hook.
|
|
134
134
|
3. Launch the chosen form, from the repo root, with the active plan file path, the task ID, and the just-written state character (`X` or `!`):
|
|
135
135
|
|
|
136
|
-
`node <chosen-flags> scripts/conductor-db.mjs record "<plan_file>" "<task_id>" "<state>"`
|
|
136
|
+
`node <chosen-flags> .claude/scripts/conductor-db.mjs record "<plan_file>" "<task_id>" "<state>"`
|
|
137
137
|
|
|
138
138
|
All three arguments **must** be wrapped in double quotes exactly as shown. A repository path can contain spaces (e.g. `/Users/me/My Projects/repo/docs/plan.md`); unquoted, the shell word-splits it into several argv entries and the recorder sees `!== 3` positionals, silently rejecting a legitimate write. `--no-warnings` (in both probe and launch) suppresses Node's `ExperimentalWarning: SQLite is an experimental feature` line so it never pollutes hook stderr; it does not affect the recorder's own `CONDUCTOR_DB:` diagnostics (those are direct `process.stderr` writes, not process warnings).
|
|
139
139
|
|
|
@@ -4,12 +4,12 @@ description: "(Conductor) Map implementation steps from an approved spec"
|
|
|
4
4
|
|
|
5
5
|
## Phase entry - Resume Read
|
|
6
6
|
|
|
7
|
-
Before doing anything else, restore any stored context for the current commit by running
|
|
7
|
+
Before doing anything else, restore any stored context for the current commit by running `.claude/scripts/resume-read.mjs`. It resolves the current git hash, prefers a valid DB snapshot (`conductor-db get-snapshot`), falls back to the `.claude/memory/session-snapshot.json` handoff file, and prints a `RESUME_HIT` block on a hit / nothing on a miss. Capture its stdout **and** its exit code with the canonical per-platform form (each first probes for `node` and treats its absence as a clean miss, never an error):
|
|
8
8
|
|
|
9
9
|
- **Unix / Git Bash:**
|
|
10
10
|
```sh
|
|
11
11
|
if command -v node >/dev/null 2>&1; then
|
|
12
|
-
resume_out="$(node scripts/resume-read.mjs 2>>.conductor/last-write.log)"; resume_rc=$?
|
|
12
|
+
resume_out="$(node .claude/scripts/resume-read.mjs 2>>.conductor/last-write.log)"; resume_rc=$?
|
|
13
13
|
else resume_rc=3; resume_out=""; fi
|
|
14
14
|
```
|
|
15
15
|
- **PowerShell:**
|
|
@@ -20,7 +20,7 @@ Before doing anything else, restore any stored context for the current commit by
|
|
|
20
20
|
$__nap = $PSNativeCommandUseErrorActionPreference; $PSNativeCommandUseErrorActionPreference = $false
|
|
21
21
|
}
|
|
22
22
|
try {
|
|
23
|
-
$resume_out = node scripts/resume-read.mjs 2>> .conductor/last-write.log; $resume_rc = $LASTEXITCODE
|
|
23
|
+
$resume_out = node .claude/scripts/resume-read.mjs 2>> .conductor/last-write.log; $resume_rc = $LASTEXITCODE
|
|
24
24
|
} catch { $resume_rc = 3; $resume_out = "" }
|
|
25
25
|
finally {
|
|
26
26
|
$ErrorActionPreference = $__eap
|
|
@@ -4,12 +4,12 @@ description: "(Conductor) Define the problem and generate an approved spec"
|
|
|
4
4
|
|
|
5
5
|
## Phase entry - Resume Read
|
|
6
6
|
|
|
7
|
-
Before doing anything else, restore any stored context for the current commit by running
|
|
7
|
+
Before doing anything else, restore any stored context for the current commit by running `.claude/scripts/resume-read.mjs`. It resolves the current git hash, prefers a valid DB snapshot (`conductor-db get-snapshot`), falls back to the `.claude/memory/session-snapshot.json` handoff file, and prints a `RESUME_HIT` block on a hit / nothing on a miss. Capture its stdout **and** its exit code with the canonical per-platform form (each first probes for `node` and treats its absence as a clean miss, never an error):
|
|
8
8
|
|
|
9
9
|
- **Unix / Git Bash:**
|
|
10
10
|
```sh
|
|
11
11
|
if command -v node >/dev/null 2>&1; then
|
|
12
|
-
resume_out="$(node scripts/resume-read.mjs 2>>.conductor/last-write.log)"; resume_rc=$?
|
|
12
|
+
resume_out="$(node .claude/scripts/resume-read.mjs 2>>.conductor/last-write.log)"; resume_rc=$?
|
|
13
13
|
else resume_rc=3; resume_out=""; fi
|
|
14
14
|
```
|
|
15
15
|
- **PowerShell:**
|
|
@@ -20,7 +20,7 @@ Before doing anything else, restore any stored context for the current commit by
|
|
|
20
20
|
$__nap = $PSNativeCommandUseErrorActionPreference; $PSNativeCommandUseErrorActionPreference = $false
|
|
21
21
|
}
|
|
22
22
|
try {
|
|
23
|
-
$resume_out = node scripts/resume-read.mjs 2>> .conductor/last-write.log; $resume_rc = $LASTEXITCODE
|
|
23
|
+
$resume_out = node .claude/scripts/resume-read.mjs 2>> .conductor/last-write.log; $resume_rc = $LASTEXITCODE
|
|
24
24
|
} catch { $resume_rc = 3; $resume_out = "" }
|
|
25
25
|
finally {
|
|
26
26
|
$ErrorActionPreference = $__eap
|