@dzhechkov/harness-cli 0.3.233 → 0.3.235
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/.dz-manifest.json +16 -16
- package/README.md +43 -3
- package/dist/cli.d.ts +17 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +319 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/sbom.json +15 -15
- package/src/cli.ts +334 -1
- package/src/index.ts +1 -1
package/.dz-manifest.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": ".agentic-qe/brain.rvf.lock",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "99fa5178a48b6953b8f3a6f1690204a3b5449004b7dd105e7a3155ad3658266c"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": ".agentic-qe/memory.db",
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": ".agentic-qe/memory.db-shm",
|
|
20
|
-
"sha256": "
|
|
20
|
+
"sha256": "cab99dd5d4068013a0c5f4f1c2d29dbec07729dc47c1d77adba6a53b773d0026"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": ".agentic-qe/memory.db-wal",
|
|
24
|
-
"sha256": "
|
|
24
|
+
"sha256": "2cc80ec378c1f9cd2588676588ee1b73bfe4f2f7ef3b96879c295eb86dccd2c2"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"path": ".agentic-qe/patterns.rvf",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": ".agentic-qe/patterns.rvf.lock",
|
|
36
|
-
"sha256": "
|
|
36
|
+
"sha256": "84842f3916c558b1f4f2da2a5b0236587f146dda1f6ab4ba4c31ed7b8b17f943"
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"path": "CHANGELOG.md",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"path": "README.md",
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "378ef9c8fcde4e77594b29a5f043b57f880f5df2b40fce679addfae756670154"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": "coverage/coverage-final.json",
|
|
@@ -69,27 +69,27 @@
|
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
"path": "dist/cli.d.ts",
|
|
72
|
-
"sha256": "
|
|
72
|
+
"sha256": "628c55bb864dd8a8f337a9cb3fe4271f0df7c0bf9237b1150427171f0577fd57"
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"path": "dist/cli.d.ts.map",
|
|
76
|
-
"sha256": "
|
|
76
|
+
"sha256": "45c0c1328829a56cfe0ea0ebc74c15477a00bb683b496404688ab4b7b7a88ef8"
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
"path": "dist/cli.js",
|
|
80
|
-
"sha256": "
|
|
80
|
+
"sha256": "20974a159438ac73a4c308cdb3710154dd9fe0450ed5184f6f3938792380fece"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"path": "dist/cli.js.map",
|
|
84
|
-
"sha256": "
|
|
84
|
+
"sha256": "bfb9ecebeb1c3cc631f2c8e7f46af4318e3698789c9dda49aaebbe26fe1c7fa7"
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
"path": "dist/index.d.ts",
|
|
88
|
-
"sha256": "
|
|
88
|
+
"sha256": "aaac0134a074fd06e9b5e09daa952ef6353510e481885da51a6e4f2e92beba35"
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
"path": "dist/index.d.ts.map",
|
|
92
|
-
"sha256": "
|
|
92
|
+
"sha256": "459c3381a9fbcd3378b623f9fe103a8d3337e4025a2f546b6d40e4d9dd4e0f70"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"path": "dist/index.js",
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
"path": "package.json",
|
|
108
|
-
"sha256": "
|
|
108
|
+
"sha256": "c03aafaeaf663bb47b756608049bf77f6460615f1e806054644ec830c524cf72"
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
"path": "src/bin.ts",
|
|
@@ -113,15 +113,15 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"path": "src/cli.ts",
|
|
116
|
-
"sha256": "
|
|
116
|
+
"sha256": "6b8d21416576a2744b5d9bf973d7b878c255495829fcf3679b183a977abc55a2"
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
"path": "src/index.ts",
|
|
120
|
-
"sha256": "
|
|
120
|
+
"sha256": "395b27b317e575b94a1049cdb284db185e0ec382fbae1e588c78ff2398b2563f"
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
"path": "test/cli.test.ts",
|
|
124
|
-
"sha256": "
|
|
124
|
+
"sha256": "09472c195254c359f14256c39e8c7afd08c0b71aadb57cd077534f0d7c482005"
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
"path": "tsconfig.json",
|
|
@@ -133,5 +133,5 @@
|
|
|
133
133
|
}
|
|
134
134
|
]
|
|
135
135
|
},
|
|
136
|
-
"signature": "
|
|
136
|
+
"signature": "lQbeQtlApDnUL/w9FcwEmMPS6jGGZ5eTBN+VVlDt0+Hh3QKOsk9G3SK9kOpiCAqTzt/2/54p8lFSiP0FwNuzCQ=="
|
|
137
137
|
}
|
package/README.md
CHANGED
|
@@ -241,6 +241,11 @@ dz roam --apply # sync agent state across machin
|
|
|
241
241
|
### Phase 6: Share (publish to the world)
|
|
242
242
|
|
|
243
243
|
```bash
|
|
244
|
+
# Verified release — 4 HARD gates (tests, audit, syntax, smoke-boot) in FRONT of dz publish:
|
|
245
|
+
dz release --dry-run # print the full gate plan, execute nothing
|
|
246
|
+
dz release --filter skills-devops --tag # gates for one release set; on green: git tag + notes
|
|
247
|
+
dz release # gates for the whole workspace; green prints the ready dz publish command
|
|
248
|
+
|
|
244
249
|
# Publish updated packages to npm:
|
|
245
250
|
dz publish --dry-run # preview
|
|
246
251
|
dz publish --filter skills-devops # publish specific package
|
|
@@ -367,6 +372,37 @@ the release job gets a perfectly attested malicious package.
|
|
|
367
372
|
A ready-to-install workflow is at `features/publish-provenance/07_code_changes/publish.yml`; copy it to
|
|
368
373
|
`.github/workflows/` and add an `NPM_TOKEN` secret.
|
|
369
374
|
|
|
375
|
+
### Verified release — `dz release` (4 HARD gates in front of `dz publish`)
|
|
376
|
+
|
|
377
|
+
`dz publish` gates on guard / claim-check / signatures / provenance / files-whitelist — none of which
|
|
378
|
+
prove the code **works**. `dz release` is the opt-in VERIFY phase in front of it: (1) each package's full
|
|
379
|
+
test suite, (2) `pnpm audit --prod --audit-level high` (production deps by default — a dev-only advisory
|
|
380
|
+
is not a false gate; `--audit-dev` widens; an audit that *cannot run* also blocks, classified
|
|
381
|
+
`AUDIT_ERROR`), (3) `node --check` of every `dist/**/*.js` + bin file (a dist older than `src/` is
|
|
382
|
+
`STALE_DIST`, never checked as-is; a package that declares a `build` script but has **no** dist JS is
|
|
383
|
+
`MISSING_DIST` — an unbuilt package cannot ship), (4) smoke-boot every bin via `node <bin> --help` in a
|
|
384
|
+
throwaway cwd with a timeout. Packages without a `test` script are **named skips** in the report — never
|
|
385
|
+
silent passes; a template-only pack (no build, no artifacts, no bin) is a named `SKIP_NO_ARTIFACTS`.
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
dz release --dry-run # full gate plan, zero commands executed
|
|
389
|
+
dz release --filter harness-core # gates for one release set
|
|
390
|
+
dz release --affected # narrow to packages touched by working tree + last commit (git down ⇒ FAIL-OPEN to full set)
|
|
391
|
+
dz release --tag # on green: annotated git tag + short notes from recent commits
|
|
392
|
+
dz release --publish # on green: chain in-process into dz publish (its own dry-run protocol)
|
|
393
|
+
dz release --json # CI-parseable verdict; valid JSON on error paths too (with --publish, publish output rides INSIDE the envelope)
|
|
394
|
+
dz release --audit-dev # widen the audit gate to dev dependencies too
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
Expected flow: gate table (`✓/✗/○` per gate with per-package failures + named skips) → on any red gate:
|
|
398
|
+
release STOPPED, exit 1, best-effort `gh issue create` (no `gh`/network ⇒ loud log, never a block;
|
|
399
|
+
note: the issue body embeds first lines of failing output — pass `--no-issue` on public repos when
|
|
400
|
+
test/audit output may carry paths or environment details) → on
|
|
401
|
+
green: re-sign reminder (`dz sign …` — the publish signature gate is refuse-unsigned), then the ready
|
|
402
|
+
`dz publish` command printed **without** `--yes`: live publish stays your explicit act. **When to use:**
|
|
403
|
+
before a multi-package npm release, or whenever a broken dist/bin must be impossible to ship; keep plain
|
|
404
|
+
`dz publish` for routine pushes. `dz publish` itself is byte-identical whether or not release exists.
|
|
405
|
+
|
|
370
406
|
### Skill Packs (23 packs · 179 skills)
|
|
371
407
|
|
|
372
408
|
Each pack is an npm package — click through for the **full per-skill documentation** (what each skill does + how to trigger it). Install a whole pack with `dz install <pkg>`, or pick skills with `dz init --select` / a `--preset`.
|
|
@@ -445,7 +481,7 @@ Get the whole set with `dz init --target claude-code --preset meta`, or pick one
|
|
|
445
481
|
|
|
446
482
|
> **A skill and its npx toolkit are not duplicates — they're a graduation.** Several skills (e.g. `feature-adr`, `design-thinking`) exist BOTH as a skill inside a `dz` preset AND as a standalone `npx` package. The preset's SKILL.md is **fully functional on its own** (the whole methodology — modules + references — travels with it, and it auto-activates by description), and it's the only way to compile that capability to the **non-Claude platforms** (Codex/OpenCode/Hermes/OpenClaude) via `dz`. The npx package adds **project-level runtime governance** around the same skill: a slash command, governance rules, a context shard, and (for feature-adr) reward-learning + `/harvest`. So: pick the **skill/preset** for a working capability across platforms; pick the **npx toolkit** when you want it as a governed, command-driven fixture of one project.
|
|
447
483
|
|
|
448
|
-
## All Commands (
|
|
484
|
+
## All Commands (55)
|
|
449
485
|
|
|
450
486
|
```
|
|
451
487
|
dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force]
|
|
@@ -505,6 +541,7 @@ dz sbom --pack <dir> [--out <file>] # emit the CycloneDX 1.5 SBOM for a pack s
|
|
|
505
541
|
dz guard check --op <publish|teach|consolidate|reindex> [--text <s>] [--json] [--force <reason>] # declarative constraint layer before self-mutating ops: HARD violation → block (exit 1), SOFT → warn; zero-config defaults, .dz/guard.json to customise; dz guard --init | dz guard log (append-only audit). dz publish runs it automatically (--no-guard "<reason>" = logged escape hatch)
|
|
506
542
|
dz feature-adr-setup --guards [--loc-cap <n>] [--apply] # P3: scaffold DETERMINISTIC guard tests into the project — guards.config.json + a zero-dependency check.mjs runner (LOC cap, secret scan, frozen-file sha256 pins, waivers-with-reasons); wire `node architecture/guards/check.mjs` into CI
|
|
507
543
|
dz publish [--filter <name>] [--bump-only] [--claim-check <off|warn|error>] (dry-run by default; pass --yes/--confirm to go live; claim-check gate defaults to warn — surfaces README claim findings, never blocks)
|
|
544
|
+
dz release [--filter <name>] [--affected] [--audit-dev] [--tag] [--publish] [--json] [--dry-run] [--no-issue] # VERIFIED release: 4 HARD gates in front of dz publish — package test suites, pnpm audit --prod >=high (--audit-dev widens), node --check of every dist/bin file (unbuilt package with a build script ⇒ MISSING_DIST fail), bin smoke-boot "node <bin> --help" (temp cwd + timeout); --affected narrows to git-touched packages (fail-open); any red gate STOPS the release (exit 1) + best-effort gh issue; green ⇒ re-sign reminder, then the ready dz publish command (never with --yes injected)
|
|
508
545
|
dz auto-canonicalize --source <github-url> --pack <skills-pack>
|
|
509
546
|
dz sync-upstream [--package <dir>] [--list] [--all]
|
|
510
547
|
dz drift-check [--all] [--json] [--project <dir>] # CI gate: exit 1 on NEW shared-skill drift (baseline: .dz/drift-allowlist.json; --all incl .claude dogfood)
|
|
@@ -1620,7 +1657,10 @@ dz guard (teach): ✗ BLOCK [checked: no-secrets, store-bloat-cap]
|
|
|
1620
1657
|
hatch `--no-guard "<reason>"` requires a reason and is logged to `.dz/guard-audit.jsonl` — an override is
|
|
1621
1658
|
visible, never silent. Calibrated against false gates: in a pnpm workspace, `workspace:*` in source is
|
|
1622
1659
|
*correct* (pnpm rewrites it at publish), so the rule resolves each workspace dep to the version it would
|
|
1623
|
-
ship as and blocks only a dep that would ship raw.
|
|
1660
|
+
ship as and blocks only a dep that would ship raw. The `readme-consistency` rule also holds the **docs
|
|
1661
|
+
site** in the contour: the command count on the Starlight CLI overview and the front-page tagline must
|
|
1662
|
+
match the cli README (the site once drifted 35→55 silently; now a mismatch is a warning here and a red
|
|
1663
|
+
test in CI). In a repo without the docs site those pairs simply aren't gathered — no false gate.
|
|
1624
1664
|
|
|
1625
1665
|
### `dz feature-adr-setup --guards` — when a project rule should be a TEST, not a reviewer's memory
|
|
1626
1666
|
|
|
@@ -2061,7 +2101,7 @@ npx @dzhechkov/p-replicator init
|
|
|
2061
2101
|
|
|
2062
2102
|
## Status
|
|
2063
2103
|
|
|
2064
|
-
`v0.3.
|
|
2104
|
+
`v0.3.235` — published on npm. Also available as [Claude Plugin](#claude-plugin). Part of [DZ Harness Hub](https://github.com/djd1m/dz-harness-hub).
|
|
2065
2105
|
|
|
2066
2106
|
## Claude Plugin
|
|
2067
2107
|
|
package/dist/cli.d.ts
CHANGED
|
@@ -13,6 +13,23 @@ export interface CliIo {
|
|
|
13
13
|
* command that needs it (`brain ground`), and never when stdin is a TTY (nothing piped).
|
|
14
14
|
*/
|
|
15
15
|
readonly stdin?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Test seam for `dz release`: overrides subprocess execution for gate steps and the
|
|
18
|
+
* gh/git side channels (production leaves it unset → real `execSync`, stdio piped).
|
|
19
|
+
* A scripted runner makes failing gates, gh outages, and git-tag failures testable
|
|
20
|
+
* without spawning anything.
|
|
21
|
+
*/
|
|
22
|
+
readonly releaseRunner?: ReleaseExecRunner;
|
|
16
23
|
}
|
|
24
|
+
/** Injected subprocess runner used by `dz release` (see {@link CliIo.releaseRunner}). */
|
|
25
|
+
export type ReleaseExecRunner = (cmd: string, opts: {
|
|
26
|
+
readonly cwd: string;
|
|
27
|
+
readonly timeoutMs: number;
|
|
28
|
+
}) => {
|
|
29
|
+
exitCode: number;
|
|
30
|
+
stdout: string;
|
|
31
|
+
stderr: string;
|
|
32
|
+
timedOut?: boolean;
|
|
33
|
+
};
|
|
17
34
|
export declare function runCli(argv: string[], io?: CliIo): Promise<number>;
|
|
18
35
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA4PH,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KACvD;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAovK9E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAE,KAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CA8I5E"}
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
import { execSync } from 'node:child_process';
|
|
10
10
|
import { homedir, tmpdir } from 'node:os';
|
|
11
11
|
import { createRequire } from 'node:module';
|
|
12
|
-
import { createSkill, getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, claimCheck, summarize, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, RECALL_USAGE_LOG_RELATIVE, RECALL_USAGE_LOG_MAX_BYTES, parseRecallUsageLog, buildRecallUsageReport, buildManifest, buildSbom, resolveTrustRoot, decideVerifyPolicy, generateSigningKeypair, evaluateGuard, resolveRules, auditRecord, guardExitCode, DEFAULT_RULES, decideProvenance, isInsideTree, signManifest, verifyManifest, listSignablePackFiles, assertKeyOutsideTree, decidePublishGate, MANIFEST_NAME, SBOM_NAME, buildArchitectureMap, renderMapHuman, findArchitectureDrift, renderDriftReport, scanWorkspacePackages, loadSubsystemManifest, loadProductVision, checkFeatureAgainstArchitecture, renderArchCheck, planProjectSkills, guidanceForStage, renderInjectionReport, analyzeCorpus, renderRakeReport, renderCriticSection, rakeAsLesson, rakeReward, DEFAULT_RAKE_THRESHOLDS, streamSessionEvents, findLatestTranscript, detectProcessRakes, buildRetro, renderRetro, retroLessonText, PROCESS_SIGNATURES, RETRO_DOMAIN, scanForSetup, buildSetupPlan, scaffoldFromSpec, renderScaffoldPreview, readExistingForScaffold, assembleChallengeContext, buildChallengeBrief, planDiscriminationCheck, classifyDiscrimination, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, } from '@dzhechkov/harness-core';
|
|
12
|
+
import { createSkill, getSkillInfo, getWorkflow, isTargetName, listSkills, runDoctor, runInit, benchmarkSkill, benchmarkSkills, scanMcp, reconcileCapabilities, RECONCILE_BANNER, buildRegistry, discoverSkillPackDirs, checkUpstream, compareSkills, checkAllUpstream, sweepSkillDrift, syncCanonicalSkill, checkUpgrades, discoverPackages, discoverSourcePackages, fetchAllDownloads, filterByCategory, pretrain, recommend, generatePlugin, publishPackages, runSetup, runMigrate, searchRegistry, runSync, runVerify, runInitAgentsMd, runInitGeminiMd, TARGET_NAMES, WORKFLOW_NAMES, importEcc, recordPattern, resolveLearningBackend, storeStats, consolidateSessions, pruneNoisePatterns, lessonDeltaReport, removePatternsByIds, snapshotStore, recallHybrid, teachGuard, mirrorPatternsToVector, vectorMirrorEnabled, vectorTierStatus, resolveVectorEngine, reindexVectorStore, harmonizeVectorStore, importRvfCheckpoint, statuslineData, writeFeatureAdrState, computeUsage, deriveUsageCalibration, normalizeClaudeUsageModelKey, readUsageLimits, claimCheck, summarize, queryBookKnowledge, loadStorePatternsSync, patternRecordId, loadStoreRecords, recordToPattern, bundleSkills, brainHome, listBrain, promoteProjectToBrain, updateBrainSource, queryBrain, groundPrompt, expandKu, reindexBrainVectors, buildPrimer, exportBrainSlice, importBrainSlice, registerKusToBrain, RECALL_USAGE_LOG_RELATIVE, RECALL_USAGE_LOG_MAX_BYTES, parseRecallUsageLog, buildRecallUsageReport, buildManifest, buildSbom, resolveTrustRoot, decideVerifyPolicy, generateSigningKeypair, evaluateGuard, resolveRules, auditRecord, guardExitCode, DEFAULT_RULES, decideProvenance, isInsideTree, signManifest, verifyManifest, listSignablePackFiles, assertKeyOutsideTree, decidePublishGate, collectPackageFacts, planReleaseGates, selectAffectedPackages, classifyGateExecutions, buildFailureIssue, buildReleaseNotes, releaseTagName, firstOutputLine, formatPublishError, MANIFEST_NAME, SBOM_NAME, buildArchitectureMap, renderMapHuman, findArchitectureDrift, renderDriftReport, scanWorkspacePackages, loadSubsystemManifest, loadProductVision, checkFeatureAgainstArchitecture, renderArchCheck, planProjectSkills, guidanceForStage, renderInjectionReport, analyzeCorpus, renderRakeReport, renderCriticSection, rakeAsLesson, rakeReward, DEFAULT_RAKE_THRESHOLDS, streamSessionEvents, findLatestTranscript, detectProcessRakes, buildRetro, renderRetro, retroLessonText, PROCESS_SIGNATURES, RETRO_DOMAIN, scanForSetup, buildSetupPlan, scaffoldFromSpec, renderScaffoldPreview, readExistingForScaffold, assembleChallengeContext, buildChallengeBrief, planDiscriminationCheck, classifyDiscrimination, pickAdversaryModel, CHALLENGE_QUESTIONS, loadOutcomes, renderOutcomes, statsForKey, selectAutoCost, recordProvisional, finalizeOutcome, COST_LADDER, splitScenarios, budgetPlan, selectWinner, proseScopeOk, renderProseDiff, readScenarioIds, DEFAULT_MAX_JUDGE_RUNS, } from '@dzhechkov/harness-core';
|
|
13
13
|
import { getPreset, PRESET_NAMES } from '@dzhechkov/harness-presets';
|
|
14
14
|
import { scanGitHub, analyzeRepo, generateReport, deepAnalyze, scanAllSources, ScoutMemory } from '@dzhechkov/scout';
|
|
15
15
|
const USAGE = `dz - DZ cross-platform harness CLI
|
|
@@ -32,6 +32,7 @@ Usage:
|
|
|
32
32
|
dz sign --pack <dir> --key <path-outside-repo> (Ed25519 manifest + CycloneDX SBOM for a pack)
|
|
33
33
|
dz verify-pack --pack <dir> [--pubkey <path>] (signature check; fail-closed; key from the repo, never the pack)
|
|
34
34
|
dz publish [--filter <name>] [--bump-only] [--claim-check <off|warn|error>] [--require-signing] [--provenance|--no-provenance] (dry-run by default; pass --yes/--confirm/--no-dry-run to go live; claim-check gate default warn — surfaces README claim findings, never blocks; error fails an offending package)
|
|
35
|
+
dz release [--filter <name>] [--tag] [--publish] [--json] [--dry-run] [--no-issue] (VERIFIED release: 4 HARD gates in FRONT of dz publish — full package test suites, audit >=high, node --check of every dist/bin file, bin smoke-boot via "node <bin> --help" — any red gate STOPS the release (exit 1) + best-effort gh issue; all green ⇒ re-sign reminder, then prints the ready dz publish command (or chains with --publish); never duplicates publish's own gates)
|
|
35
36
|
dz setup --target <name> [--preset <name>] [--select id,id,...] [--skills-dir <dir>] [--project <dir>] [--memory agentdb] [--no-memory] [--no-hooks] [--install-driver] [--force] [--enrich]
|
|
36
37
|
dz teach "<pattern>" [--reward <0-1>] [--domain <name>] [--type rule|success-pattern|lesson-learned] [--project <dir>] [--no-mirror] (--project pins the learned store to <dir>/.dz, not the cwd — pin to a canonical brain)
|
|
37
38
|
dz teach --from-json <file> [--project <dir>] [--no-mirror] (bulk-import a 'dz recall --all --json' export — share a learned store across machines)
|
|
@@ -3260,6 +3261,308 @@ function cmdPublish(options, flags, cwd, write) {
|
|
|
3260
3261
|
}
|
|
3261
3262
|
return report.errors > 0 ? 1 : 0;
|
|
3262
3263
|
}
|
|
3264
|
+
/* ------------------------------------------------------------------ */
|
|
3265
|
+
/* dz release — verified-release conveyor (feature release-verified, */
|
|
3266
|
+
/* ADR-001): 4 HARD gates in FRONT of the untouched dz publish. */
|
|
3267
|
+
/* ------------------------------------------------------------------ */
|
|
3268
|
+
/**
|
|
3269
|
+
* The single executor of the pure engine's {@link GateStep} plan. ALL subprocess side effects
|
|
3270
|
+
* of the release live here (engine purity, NFR-1). Contract highlights:
|
|
3271
|
+
*
|
|
3272
|
+
* - ALL four gates execute even after an earlier gate fails (AM-9/FR-6 — no fail-fast hiding);
|
|
3273
|
+
* one red gate still reddens the release.
|
|
3274
|
+
* - Smoke steps run in a THROWAWAY cwd (AM-4): skills bins are installers that mutate
|
|
3275
|
+
* `.claude/` on default action.
|
|
3276
|
+
* - gh issue + git tag are best-effort periphery: loud on failure, NEVER verdict- or
|
|
3277
|
+
* exit-code-affecting (FR-7/FR-8, AM-6).
|
|
3278
|
+
* - On green it never injects `--yes/--confirm/--no-dry-run` into the printed/chained publish
|
|
3279
|
+
* (AM-5): live publish stays the operator's explicit act, and `--publish` chains into
|
|
3280
|
+
* publish's own default (dry-run) protocol with ALL its gates untouched.
|
|
3281
|
+
*/
|
|
3282
|
+
function cmdRelease(options, flags, cwd, write, runner) {
|
|
3283
|
+
const json = flags.has('json');
|
|
3284
|
+
const warnings = [];
|
|
3285
|
+
const say = (line) => {
|
|
3286
|
+
if (!json)
|
|
3287
|
+
write(line);
|
|
3288
|
+
};
|
|
3289
|
+
const loud = (line) => {
|
|
3290
|
+
warnings.push(line);
|
|
3291
|
+
if (!json)
|
|
3292
|
+
write(line);
|
|
3293
|
+
};
|
|
3294
|
+
const emitJson = (obj) => {
|
|
3295
|
+
if (json)
|
|
3296
|
+
write(JSON.stringify({ ...obj, warnings }, null, 2));
|
|
3297
|
+
};
|
|
3298
|
+
// G9 reuse-never-copy: the engine's firstOutputLine, not a byte-parallel local copy.
|
|
3299
|
+
const oneLine = firstOutputLine;
|
|
3300
|
+
// Strict allowlist (cmdPublish's typo-rejection pattern) — a mistyped flag is NEVER
|
|
3301
|
+
// swallowed, and the rejection honors the --json contract too (AC-7).
|
|
3302
|
+
// `--verified` is accepted but not read: verified is the DEFAULT and only mode of
|
|
3303
|
+
// `dz release` in this slice — the flag exists so the branded invocation is not a typo error.
|
|
3304
|
+
const allowedFlags = new Set(['verified', 'tag', 'publish', 'json', 'dry-run', 'no-issue', 'affected', 'audit-dev', 'help']);
|
|
3305
|
+
const allowedOptions = new Set(['filter']);
|
|
3306
|
+
const allowedHelp = ' allowed: --verified, --filter <substr>, --affected, --audit-dev, --tag, --publish, --json, --dry-run, --no-issue';
|
|
3307
|
+
const rejectUnknown = (key) => {
|
|
3308
|
+
if (json)
|
|
3309
|
+
write(JSON.stringify({ error: `unknown option --${key}`, allowed: allowedHelp.trim(), publishAction: 'blocked', exitCode: 1 }, null, 2));
|
|
3310
|
+
else {
|
|
3311
|
+
write(`dz release: unknown option --${key}`);
|
|
3312
|
+
write(allowedHelp);
|
|
3313
|
+
}
|
|
3314
|
+
return 1;
|
|
3315
|
+
};
|
|
3316
|
+
for (const flag of flags)
|
|
3317
|
+
if (!allowedFlags.has(flag))
|
|
3318
|
+
return rejectUnknown(flag);
|
|
3319
|
+
for (const key of options.keys()) {
|
|
3320
|
+
if (key.startsWith('_positional_'))
|
|
3321
|
+
continue;
|
|
3322
|
+
if (!allowedOptions.has(key))
|
|
3323
|
+
return rejectUnknown(key);
|
|
3324
|
+
}
|
|
3325
|
+
// --filter mirrors publish semantics: trim + drop empties; empty result is an ERROR,
|
|
3326
|
+
// never "match all" (the publish P0 regression this deliberately copies).
|
|
3327
|
+
const filterStr = options.get('filter');
|
|
3328
|
+
let filter;
|
|
3329
|
+
if (filterStr !== undefined) {
|
|
3330
|
+
filter = filterStr.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3331
|
+
if (filter.length === 0) {
|
|
3332
|
+
if (json)
|
|
3333
|
+
write(JSON.stringify({ error: '--filter requires a non-empty comma-separated list of package-name substrings', publishAction: 'blocked', exitCode: 1 }, null, 2));
|
|
3334
|
+
else
|
|
3335
|
+
write('dz release: --filter requires a non-empty comma-separated list of package-name substrings');
|
|
3336
|
+
return 1;
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
// DETECT — real facts via the engine's one fs seam (discoverPackages underneath).
|
|
3340
|
+
let factsList;
|
|
3341
|
+
try {
|
|
3342
|
+
factsList = collectPackageFacts(cwd, filter);
|
|
3343
|
+
}
|
|
3344
|
+
catch (err) {
|
|
3345
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3346
|
+
if (json)
|
|
3347
|
+
write(JSON.stringify({ error: msg, publishAction: 'blocked', exitCode: 1 }, null, 2));
|
|
3348
|
+
else
|
|
3349
|
+
write(`dz release: ${msg}`);
|
|
3350
|
+
return 1;
|
|
3351
|
+
}
|
|
3352
|
+
if (factsList.length === 0) {
|
|
3353
|
+
// C-6: outside a workspace (or an over-narrow filter) — explain and exit non-zero, never throw.
|
|
3354
|
+
const msg = 'no publishable packages found under packages/@dzhechkov — run from the monorepo root (or widen --filter)';
|
|
3355
|
+
if (json)
|
|
3356
|
+
write(JSON.stringify({ error: msg, packages: [], publishAction: 'blocked', exitCode: 1 }, null, 2));
|
|
3357
|
+
else
|
|
3358
|
+
write(`dz release: ${msg}`);
|
|
3359
|
+
return 1;
|
|
3360
|
+
}
|
|
3361
|
+
// Executor: default = real execSync with piped stdio + timeout; injectable for tests.
|
|
3362
|
+
// Defined BEFORE planning because --affected needs read-only git diffs at DETECT time.
|
|
3363
|
+
const run = runner ??
|
|
3364
|
+
((cmd, opts) => {
|
|
3365
|
+
try {
|
|
3366
|
+
const stdout = execSync(cmd, { cwd: opts.cwd, stdio: 'pipe', encoding: 'utf-8', timeout: opts.timeoutMs });
|
|
3367
|
+
return { exitCode: 0, stdout: stdout == null ? '' : String(stdout), stderr: '' };
|
|
3368
|
+
}
|
|
3369
|
+
catch (err) {
|
|
3370
|
+
const e = err;
|
|
3371
|
+
// execSync's timeout kills via signal and leaves status null — that IS the timeout shape.
|
|
3372
|
+
const timedOut = (e.status === null || e.status === undefined) && (e.signal != null || e.killed === true);
|
|
3373
|
+
return {
|
|
3374
|
+
exitCode: typeof e.status === 'number' ? e.status : 1,
|
|
3375
|
+
stdout: e.stdout == null ? '' : String(e.stdout),
|
|
3376
|
+
stderr: e.stderr == null || String(e.stderr).trim() === '' ? formatPublishError(e) : String(e.stderr),
|
|
3377
|
+
timedOut,
|
|
3378
|
+
};
|
|
3379
|
+
}
|
|
3380
|
+
});
|
|
3381
|
+
// AM-8/--affected: narrow the release set to packages touched by the working tree + last
|
|
3382
|
+
// commit. Selection is the PURE selectAffectedPackages; the two read-only git diffs are the
|
|
3383
|
+
// injected fact source. Any git failure ⇒ null ⇒ FAIL-OPEN to the full set (never zero).
|
|
3384
|
+
if (flags.has('affected')) {
|
|
3385
|
+
const dWork = run('git diff --name-only HEAD', { cwd, timeoutMs: 10_000 });
|
|
3386
|
+
const dLast = run('git diff --name-only HEAD~1..HEAD', { cwd, timeoutMs: 10_000 });
|
|
3387
|
+
const anyOk = dWork.exitCode === 0 || dLast.exitCode === 0;
|
|
3388
|
+
const changed = anyOk
|
|
3389
|
+
? [dWork, dLast]
|
|
3390
|
+
.filter((r) => r.exitCode === 0)
|
|
3391
|
+
.flatMap((r) => r.stdout.split('\n'))
|
|
3392
|
+
.map((s) => s.trim())
|
|
3393
|
+
.filter((s) => s.length > 0)
|
|
3394
|
+
: null;
|
|
3395
|
+
const selected = selectAffectedPackages(changed, factsList);
|
|
3396
|
+
if (changed === null)
|
|
3397
|
+
loud('dz release --affected: ⚠ git diff unavailable — FAIL-OPEN to the full package set');
|
|
3398
|
+
else
|
|
3399
|
+
say(`dz release --affected: ${selected.length}/${factsList.length} package(s) selected from ${changed.length} changed file(s)${selected.length === factsList.length ? ' (no narrowing — full set)' : ''}`);
|
|
3400
|
+
factsList = selected;
|
|
3401
|
+
}
|
|
3402
|
+
const plan = planReleaseGates(factsList, {
|
|
3403
|
+
monorepoRoot: cwd,
|
|
3404
|
+
pnpmLockPresent: existsSync(join(cwd, 'pnpm-lock.yaml')),
|
|
3405
|
+
includeDevDeps: flags.has('audit-dev'),
|
|
3406
|
+
});
|
|
3407
|
+
// --dry-run: print the full plan, execute NOTHING (deterministic, byte-testable preview).
|
|
3408
|
+
if (flags.has('dry-run')) {
|
|
3409
|
+
if (json) {
|
|
3410
|
+
write(JSON.stringify({ dryRun: true, packages: plan.packages, steps: plan.steps, skips: plan.skips, warnings }, null, 2));
|
|
3411
|
+
return 0;
|
|
3412
|
+
}
|
|
3413
|
+
write(`\ndz release --dry-run — plan only, zero commands executed (${plan.packages.length} package(s))`);
|
|
3414
|
+
for (const gate of ['tests', 'audit', 'syntax', 'smoke']) {
|
|
3415
|
+
const steps = plan.steps.filter((s) => s.gate === gate);
|
|
3416
|
+
write(` ${gate} (${steps.length} step(s)):`);
|
|
3417
|
+
for (const s of steps) {
|
|
3418
|
+
if (s.kind === 'synthetic-fail')
|
|
3419
|
+
write(` ✗ [planned ${s.failClass ?? 'FAIL'}] ${s.pkg ?? ''}: ${s.reason}`);
|
|
3420
|
+
else
|
|
3421
|
+
write(` · ${s.cmd} (cwd=${s.tempCwd === true ? '<temp>' : s.cwd}, timeout=${s.timeoutMs}ms)`);
|
|
3422
|
+
}
|
|
3423
|
+
if (gate === 'tests')
|
|
3424
|
+
for (const sk of plan.skips)
|
|
3425
|
+
write(` ○ [${sk.class}] ${sk.pkg}: ${sk.reason}`);
|
|
3426
|
+
}
|
|
3427
|
+
write(' → run without --dry-run to execute the gates');
|
|
3428
|
+
return 0;
|
|
3429
|
+
}
|
|
3430
|
+
// VERIFY — execute EVERY exec step (AM-9: all 4 gates run and report even after a failure).
|
|
3431
|
+
const executions = [];
|
|
3432
|
+
let smokeTmp;
|
|
3433
|
+
const execSteps = plan.steps.filter((s) => s.kind !== 'synthetic-fail');
|
|
3434
|
+
say(`\ndz release — executing ${execSteps.length} gate step(s) across ${plan.packages.length} package(s)…`);
|
|
3435
|
+
for (const step of execSteps) {
|
|
3436
|
+
let stepCwd = step.cwd;
|
|
3437
|
+
if (step.tempCwd === true) {
|
|
3438
|
+
// AM-4: boot bins in a throwaway cwd so an installer-style bin cannot mutate the workspace.
|
|
3439
|
+
if (smokeTmp === undefined)
|
|
3440
|
+
smokeTmp = mkdtempSync(join(tmpdir(), 'dz-release-smoke-'));
|
|
3441
|
+
stepCwd = smokeTmp;
|
|
3442
|
+
}
|
|
3443
|
+
const started = Date.now();
|
|
3444
|
+
const r = run(step.cmd, { cwd: stepCwd, timeoutMs: step.timeoutMs });
|
|
3445
|
+
executions.push({
|
|
3446
|
+
stepId: step.id,
|
|
3447
|
+
exitCode: r.exitCode,
|
|
3448
|
+
stdout: r.stdout,
|
|
3449
|
+
stderr: r.stderr,
|
|
3450
|
+
durationMs: Date.now() - started,
|
|
3451
|
+
timedOut: r.timedOut,
|
|
3452
|
+
});
|
|
3453
|
+
}
|
|
3454
|
+
if (smokeTmp !== undefined) {
|
|
3455
|
+
try {
|
|
3456
|
+
rmSync(smokeTmp, { recursive: true, force: true });
|
|
3457
|
+
}
|
|
3458
|
+
catch { /* best-effort cleanup */ }
|
|
3459
|
+
}
|
|
3460
|
+
const verdict = classifyGateExecutions(plan, executions);
|
|
3461
|
+
// Report: per-gate ✓/✗/○ with package granularity + timestamp (NFR-5). Skips are named,
|
|
3462
|
+
// never folded into pass wording (AM-2: "N passed, M skipped", not "all tests passed").
|
|
3463
|
+
for (const g of verdict.gates) {
|
|
3464
|
+
const icon = g.status === 'pass' ? '✓' : g.status === 'fail' ? '✗' : '○';
|
|
3465
|
+
say(` ${icon} ${g.gate.padEnd(7)} ${g.status.toUpperCase().padEnd(4)} ${g.passed} passed, ${g.failures.length} failed, ${g.skips.length} skipped`);
|
|
3466
|
+
for (const f of g.failures)
|
|
3467
|
+
say(` [${f.class}] ${f.pkg !== undefined ? `${f.pkg}: ` : ''}${f.reason}`);
|
|
3468
|
+
for (const sk of g.skips)
|
|
3469
|
+
say(` [${sk.class}] ${sk.pkg}: ${sk.reason}`);
|
|
3470
|
+
}
|
|
3471
|
+
say(` verdict at ${verdict.timestamp}: ${verdict.publishAction === 'proceed' ? '✓ all gates green' : '✗ RELEASE BLOCKED'}`);
|
|
3472
|
+
const invocation = `dz release${filterStr !== undefined ? ` --filter ${filterStr}` : ''}`;
|
|
3473
|
+
const shq = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
|
|
3474
|
+
if (verdict.publishAction === 'blocked') {
|
|
3475
|
+
say(`dz release: ✗ release STOPPED — ${verdict.blockedBy.join('; ')}`);
|
|
3476
|
+
// FR-7 / AM-6: best-effort issue — a courier, never a judge. Loud on any failure;
|
|
3477
|
+
// the verdict and exit code are ALREADY decided and cannot change here.
|
|
3478
|
+
let issueUrl;
|
|
3479
|
+
if (!flags.has('no-issue')) {
|
|
3480
|
+
const issue = buildFailureIssue(verdict, { invocation });
|
|
3481
|
+
const probe = run('command -v gh', { cwd, timeoutMs: 10_000 });
|
|
3482
|
+
if (probe.exitCode !== 0) {
|
|
3483
|
+
loud(`dz release: ⚠ gh unavailable — file the issue manually: ${issue.title}`);
|
|
3484
|
+
}
|
|
3485
|
+
else {
|
|
3486
|
+
const res = run(`gh issue create --title ${shq(issue.title)} --body ${shq(issue.body)}`, { cwd, timeoutMs: 30_000 });
|
|
3487
|
+
if (res.exitCode === 0) {
|
|
3488
|
+
issueUrl = oneLine(res.stdout) || undefined;
|
|
3489
|
+
say(`dz release: gh issue created${issueUrl !== undefined ? `: ${issueUrl}` : ''}`);
|
|
3490
|
+
}
|
|
3491
|
+
else {
|
|
3492
|
+
loud(`dz release: ⚠ gh issue creation failed (${oneLine(res.stderr, res.stdout) || 'unknown error'}) — file the issue manually: ${issue.title}`);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
emitJson({
|
|
3497
|
+
gates: verdict.gates,
|
|
3498
|
+
ok: verdict.ok,
|
|
3499
|
+
blockedBy: verdict.blockedBy,
|
|
3500
|
+
skipped: verdict.skipped,
|
|
3501
|
+
publishAction: verdict.publishAction,
|
|
3502
|
+
timestamp: verdict.timestamp,
|
|
3503
|
+
...(issueUrl !== undefined ? { issueUrl } : {}),
|
|
3504
|
+
exitCode: 1,
|
|
3505
|
+
});
|
|
3506
|
+
return 1;
|
|
3507
|
+
}
|
|
3508
|
+
// Success path. FR-8: --tag is best-effort trimming — loud on failure, never gate-affecting.
|
|
3509
|
+
let tagName;
|
|
3510
|
+
if (flags.has('tag')) {
|
|
3511
|
+
const sha = run('git rev-parse --short HEAD', { cwd, timeoutMs: 10_000 });
|
|
3512
|
+
const log = run('git log --oneline -n 15 --no-decorate', { cwd, timeoutMs: 10_000 });
|
|
3513
|
+
const notes = buildReleaseNotes(log.exitCode === 0 ? log.stdout.split('\n') : []);
|
|
3514
|
+
tagName = releaseTagName(new Date(), sha.exitCode === 0 ? sha.stdout.trim() : '');
|
|
3515
|
+
const tag = run(`git tag -a ${tagName} -m ${shq(notes)}`, { cwd, timeoutMs: 10_000 });
|
|
3516
|
+
if (tag.exitCode === 0) {
|
|
3517
|
+
say(`dz release: tagged ${tagName} (annotated with short release notes from recent commits)`);
|
|
3518
|
+
say('dz release: note — the tag attests a VERIFIED tree, not a completed publish');
|
|
3519
|
+
}
|
|
3520
|
+
else {
|
|
3521
|
+
loud(`dz release: ⚠ git tag failed (${oneLine(tag.stderr, tag.stdout) || 'unknown error'}) — non-blocking; tag manually: git tag -a ${tagName}`);
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
// FR-10 / AC-8: re-sign reminder BEFORE the handoff — publish's signature gate is
|
|
3525
|
+
// refuse-unsigned, and gates 3–4 often follow a rebuild.
|
|
3526
|
+
say('dz release: reminder — re-sign any rebuilt packs BEFORE publishing (dz sign --pack <dir> --key <path-outside-repo>); the publish signature gate is refuse-unsigned');
|
|
3527
|
+
// FR-9 / AM-5: the handoff NEVER injects --yes/--confirm/--no-dry-run — live publish stays
|
|
3528
|
+
// the operator's explicit, loud-bannered act inside dz publish itself.
|
|
3529
|
+
const publishCommand = `dz publish${filterStr !== undefined ? ` --filter ${filterStr}` : ''}`;
|
|
3530
|
+
let publishExit;
|
|
3531
|
+
let publishOutput;
|
|
3532
|
+
if (flags.has('publish')) {
|
|
3533
|
+
say(`dz release: ✓ gates green — chaining into ${publishCommand} in-process (ALL publish gates + its dry-run-by-default protocol run untouched)`);
|
|
3534
|
+
const pubOptions = new Map(filterStr !== undefined ? [['filter', filterStr]] : []);
|
|
3535
|
+
// --json contract: the chained publish must NOT print prose before the JSON envelope —
|
|
3536
|
+
// capture its lines and carry them INSIDE the envelope instead (stdout stays one JSON doc).
|
|
3537
|
+
if (json) {
|
|
3538
|
+
publishOutput = [];
|
|
3539
|
+
const captured = publishOutput;
|
|
3540
|
+
publishExit = cmdPublish(pubOptions, new Set(), cwd, (line) => {
|
|
3541
|
+
captured.push(line);
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
else {
|
|
3545
|
+
publishExit = cmdPublish(pubOptions, new Set(), cwd, write);
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
else {
|
|
3549
|
+
say(`dz release: ✓ all gates green — publish when ready: ${publishCommand}`);
|
|
3550
|
+
}
|
|
3551
|
+
emitJson({
|
|
3552
|
+
gates: verdict.gates,
|
|
3553
|
+
ok: verdict.ok,
|
|
3554
|
+
blockedBy: verdict.blockedBy,
|
|
3555
|
+
skipped: verdict.skipped,
|
|
3556
|
+
publishAction: verdict.publishAction,
|
|
3557
|
+
timestamp: verdict.timestamp,
|
|
3558
|
+
...(tagName !== undefined ? { tag: tagName } : {}),
|
|
3559
|
+
publishCommand,
|
|
3560
|
+
...(publishExit !== undefined ? { publishExit } : {}),
|
|
3561
|
+
...(publishOutput !== undefined ? { publishOutput } : {}),
|
|
3562
|
+
exitCode: publishExit ?? 0,
|
|
3563
|
+
});
|
|
3564
|
+
return publishExit ?? 0;
|
|
3565
|
+
}
|
|
3263
3566
|
function cmdRegistry(options, cwd, write) {
|
|
3264
3567
|
const registry = buildRegistry(cwd);
|
|
3265
3568
|
if (registry.totalSkills === 0) {
|
|
@@ -3937,6 +4240,19 @@ function gatherReadmeCounts(root) {
|
|
|
3937
4240
|
pairs.push({ label: 'commands (root CJM header vs cli All Commands)', a: cjm, b: cliAll });
|
|
3938
4241
|
if (rootAll !== null && cliAll !== null)
|
|
3939
4242
|
pairs.push({ label: 'All Commands (root vs cli)', a: rootAll, b: cliAll });
|
|
4243
|
+
// sitedoc in the readme-first contour (feature sitedoc-readme-first, ADR-001 + Codex QE AM-3/AM-4):
|
|
4244
|
+
// the docs SITE carries the same command count as the cli README. A MISSING file skips its pair
|
|
4245
|
+
// (target repo without sitedoc — missing-evidence contract). But a file that EXISTS and no longer
|
|
4246
|
+
// matches its anchored pattern emits a MISMATCH pair (a: -1) — silent non-extraction is the exact
|
|
4247
|
+
// disease this contour cures (AM-1 applies to the guard path too, not only the CI test).
|
|
4248
|
+
const sitePair = (rel, re, label) => {
|
|
4249
|
+
if (cliAll === null || !existsSync(join(root, rel)))
|
|
4250
|
+
return;
|
|
4251
|
+
const found = num(read(rel), re);
|
|
4252
|
+
pairs.push({ label, a: found ?? -1, b: cliAll });
|
|
4253
|
+
};
|
|
4254
|
+
sitePair('packages/@dzhechkov/sitedoc/src/content/docs/cli/overview.md', /^description: All (\d+) dz commands/m, 'commands (docs-site CLI overview description vs cli All Commands)');
|
|
4255
|
+
sitePair('packages/@dzhechkov/sitedoc/src/content/docs/index.mdx', /^\s*tagline: "[^"]*?(\d+) commands/m, 'commands (docs-site index hero tagline vs cli All Commands)');
|
|
3940
4256
|
return pairs;
|
|
3941
4257
|
}
|
|
3942
4258
|
/** Gather the facts one op needs. All I/O is best-effort — a missing signal skips its rule, never crashes. */
|
|
@@ -5187,6 +5503,8 @@ export async function runCli(argv, io = {}) {
|
|
|
5187
5503
|
return await cmdAutoCanonicalize(options, cwd, write);
|
|
5188
5504
|
case 'publish':
|
|
5189
5505
|
return cmdPublish(options, flags, cwd, write);
|
|
5506
|
+
case 'release':
|
|
5507
|
+
return cmdRelease(options, flags, cwd, write, io.releaseRunner);
|
|
5190
5508
|
case 'registry':
|
|
5191
5509
|
return cmdRegistry(options, cwd, write);
|
|
5192
5510
|
case 'benchmark':
|