rvlib-shipkit 0.38.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +367 -16
- package/README.md +26 -14
- package/package.json +2 -2
- package/src/assets/gen/comfyts-gen.ts +27 -10
- package/src/assets/gen/generate-roll.ts +1 -1
- package/src/assets/kit-config.ts +6 -6
- package/src/assets/run-assets.ts +14 -10
- package/src/assets/server/studio-server.ts +6 -5
- package/src/check.ts +343 -0
- package/src/{rules/SK001-package-json-shape.ts → checks/SK001-package-json-shape.check.ts} +44 -46
- package/src/checks/SK002-contracts-fulfillness.check.ts +24 -0
- package/src/checks/SK003-claude-md-shape.check.ts +66 -0
- package/src/checks/SK004-gitignore-policy.check.ts +309 -0
- package/src/{rules/SK005-rvlib-versions.ts → checks/SK005-rvlib-versions.check.ts} +46 -32
- package/src/{rules/SK006-npm-publish-safety.ts → checks/SK006-npm-publish-safety.check.ts} +25 -24
- package/src/{rules/SK007-github-visibility.ts → checks/SK007-github-visibility.check.ts} +20 -14
- package/src/{rules/SK008-reviewer-agent.ts → checks/SK008-reviewer-agent.check.ts} +29 -14
- package/src/{rules/SK009-commit-ai-tells.ts → checks/SK009-commit-ai-tells.check.ts} +24 -21
- package/src/checks/SK010-undeclared-unit.check.ts +111 -0
- package/src/{rules/SK011-sibling-unit-ranges.ts → checks/SK011-sibling-unit-ranges.check.ts} +21 -20
- package/src/{rules/SK012-config-barrel-import.ts → checks/SK012-config-barrel-import.check.ts} +18 -23
- package/src/{rules/SK013-one-way-in-policy.ts → checks/SK013-one-way-in-policy.check.ts} +58 -50
- package/src/{rules/SK014-prose-ste.ts → checks/SK014-prose-ste.check.ts} +27 -13
- package/src/{rules/SK015-nested-repo.ts → checks/SK015-nested-repo.check.ts} +15 -12
- package/src/checks/SK016-legacy-check-shapes.check.ts +182 -0
- package/src/checks/SK017-vscode-hub-task.check.ts +197 -0
- package/src/checks/SK018-test-files-run.check.ts +28 -0
- package/src/checks/SK019-tests-run-in-units.check.ts +76 -0
- package/src/checks/SK020-self-spawning-check.check.ts +147 -0
- package/src/checks/contracts.check.ts +20 -0
- package/src/cli/cmd_Asks.ts +3 -2
- package/src/cli/cmd_Assets.ts +2 -2
- package/src/cli/cmd_Bump.ts +1 -1
- package/src/cli/cmd_Check.ts +6 -6
- package/src/cli/cmd_Checks.ts +95 -0
- package/src/cli/cmd_Ci.ts +48 -13
- package/src/cli/cmd_Commands.ts +2 -6
- package/src/cli/cmd_ConfigKeys.ts +69 -0
- package/src/cli/cmd_Contracts.ts +1 -1
- package/src/cli/cmd_Fleet.ts +6 -2
- package/src/cli/cmd_Hooks.ts +1 -1
- package/src/cli/cmd_Keys.ts +43 -18
- package/src/cli/cmd_Mv.ts +2 -2
- package/src/cli/cmd_Prose.ts +10 -1
- package/src/cli/cmd_Releases.ts +2 -2
- package/src/cli/cmd_Serve.ts +1 -1
- package/src/cli/cmd_Start.ts +6 -6
- package/src/cli/cmd_Status.ts +25 -0
- package/src/cli/cmd_Tui.ts +2 -2
- package/src/cli.ts +7 -4
- package/src/collections/actions.ts +4 -4
- package/src/collections/checks.ts +258 -0
- package/src/collections/collection-types.ts +254 -1
- package/src/collections/comfy.ts +23 -0
- package/src/collections/md-body.ts +14 -0
- package/src/collections/pings.ts +38 -12
- package/src/collections/posts.ts +19 -3
- package/src/collections/providers.ts +198 -91
- package/src/collections/reflections.ts +41 -0
- package/src/collections/state-rules.ts +73 -0
- package/src/collections/test-time.ts +288 -0
- package/src/collections/tests.ts +181 -0
- package/src/collections/wikis.ts +66 -0
- package/src/comfy/comfy-panel.ts +292 -0
- package/src/contracts/apply-contracts.ts +17 -12
- package/src/contracts/contract-types.ts +16 -4
- package/src/contracts/contracts-engine.ts +78 -42
- package/src/contracts/manifests/discord.contract.ts +6 -6
- package/src/contracts/manifests/identity.contract.ts +3 -3
- package/src/contracts/manifests/itch.contract.ts +12 -15
- package/src/contracts/manifests/social.contract.ts +5 -5
- package/src/contracts/manifests/steam.contract.ts +16 -16
- package/src/contracts/run-contracts.ts +4 -3
- package/src/core/attach.ts +96 -0
- package/src/core/canonical-scripts.ts +25 -0
- package/src/core/check-ctx.ts +204 -14
- package/src/core/check-file-names.ts +112 -0
- package/src/core/check-pass.ts +84 -0
- package/src/core/check-runner.ts +796 -0
- package/src/core/config-keys.ts +256 -0
- package/src/core/config-rewrite.ts +77 -0
- package/src/core/discover-checks.ts +166 -90
- package/src/core/discover-legacy-checks.ts +26 -0
- package/src/core/doc-block.ts +13 -0
- package/src/core/floor-resolve.ts +264 -0
- package/src/core/floor.ts +20 -137
- package/src/core/git-bin.ts +24 -0
- package/src/core/git-root.ts +108 -0
- package/src/core/instance.ts +195 -0
- package/src/core/jobs.ts +118 -0
- package/src/core/layout.ts +52 -0
- package/src/core/legacy-shapes.ts +207 -0
- package/src/core/lib-root.ts +5 -0
- package/src/core/locked-write.ts +110 -39
- package/src/core/machine-commit.ts +5 -4
- package/src/core/migrate-layout.ts +557 -0
- package/src/core/motherhome.ts +2 -8
- package/src/core/nesting.ts +4 -12
- package/src/core/one-way-in-policy.ts +73 -17
- package/src/core/outcome-words.ts +45 -0
- package/src/core/package-manager.ts +86 -0
- package/src/core/parallel.ts +21 -0
- package/src/core/process-tree.ts +44 -0
- package/src/core/raw-access.ts +93 -0
- package/src/core/read-set.ts +152 -0
- package/src/core/run-check.ts +155 -213
- package/src/core/run-ci.ts +153 -189
- package/src/core/run-fix.ts +96 -0
- package/src/core/run-fixers.ts +53 -30
- package/src/core/self-update.ts +9 -3
- package/src/core/slots.ts +83 -0
- package/src/core/spawn-timeout.ts +34 -3
- package/src/core/when-block.ts +401 -0
- package/src/core/when.ts +135 -0
- package/src/core/write-status.ts +5 -2
- package/src/hooks/leak-guard.ts +10 -8
- package/src/hooks/one-way-in-guard.ts +13 -5
- package/src/hooks/root-identity.ts +2 -1
- package/src/hooks/run-hooks.ts +35 -23
- package/src/init-repo.ts +39 -23
- package/src/ledger/check-stats.ts +155 -0
- package/src/ledger/check-table.ts +179 -0
- package/src/ledger/db.ts +255 -0
- package/src/ledger/gate-checks.ts +433 -0
- package/src/ledger/git-key.ts +116 -77
- package/src/ledger/git-scan.ts +96 -35
- package/src/ledger/ledger.ts +116 -42
- package/src/ledger/live-runs.ts +111 -0
- package/src/ledger/live-timeline.ts +97 -0
- package/src/ledger/repo-view.ts +73 -0
- package/src/ledger/run-log.ts +448 -0
- package/src/ledger/run-timeline.ts +173 -0
- package/src/ledger/store.ts +23 -12
- package/src/ledger/suite-log.ts +161 -0
- package/src/ledger/toolchain-identity.ts +7 -1
- package/src/ledger/world-cache.ts +111 -0
- package/src/mv/mv-md.ts +16 -6
- package/src/problems/extract.ts +182 -0
- package/src/problems/problem-board.ts +37 -0
- package/src/problems/problem-line.ts +77 -0
- package/src/prose/prose-view.ts +90 -0
- package/src/prose/run-prose.ts +60 -19
- package/src/prose/ste.ts +140 -24
- package/src/reflections/reflections-queue.ts +81 -0
- package/src/release/git-facts.ts +15 -9
- package/src/release/run-bump.ts +6 -5
- package/src/release/run-release.ts +25 -11
- package/src/release/run-releases.ts +2 -2
- package/src/release/unit-gate.ts +7 -54
- package/src/release/unit-resolve.ts +282 -0
- package/src/release/units.ts +33 -217
- package/src/repo-config.ts +155 -85
- package/src/rules/rule.ts +36 -96
- package/src/rules/untrack-generated.ts +5 -4
- package/src/scaffold/files/vscode-tasks.json +10 -31
- package/src/scaffold/jsonc-spans.ts +113 -0
- package/src/scaffold/project-scaffold.ts +16 -43
- package/src/scan.ts +98 -0
- package/src/serve/gate-trigger.ts +148 -0
- package/src/serve/health-schedule.ts +102 -0
- package/src/serve/hub-app/chart-view.ts +179 -0
- package/src/serve/hub-app/check-view.tsx +345 -0
- package/src/serve/hub-app/check-words.ts +164 -0
- package/src/serve/hub-app/checks-svc.ts +280 -0
- package/src/serve/hub-app/clock-svc.ts +33 -0
- package/src/serve/hub-app/col-card.tsx +23 -39
- package/src/serve/hub-app/cols-svc.ts +44 -20
- package/src/serve/hub-app/comfy-svc.ts +171 -0
- package/src/serve/hub-app/comfy-view.tsx +81 -0
- package/src/serve/hub-app/dur.ts +41 -0
- package/src/serve/hub-app/hash-route.ts +121 -0
- package/src/serve/hub-app/header-bar.tsx +117 -0
- package/src/serve/hub-app/health-svc.ts +23 -28
- package/src/serve/hub-app/help.tsx +29 -0
- package/src/serve/hub-app/hub-app.tsx +122 -119
- package/src/serve/hub-app/hub-st.ts +115 -42
- package/src/serve/hub-app/hub-types.ts +117 -2
- package/src/serve/hub-app/hub.css +534 -192
- package/src/serve/hub-app/index.html +2 -0
- package/src/serve/hub-app/live-svc.ts +174 -0
- package/src/serve/hub-app/md.ts +15 -0
- package/src/serve/hub-app/media-svc.ts +4 -1
- package/src/serve/hub-app/media-view.tsx +15 -0
- package/src/serve/hub-app/next-run.ts +68 -0
- package/src/serve/hub-app/project-gutter.css +65 -0
- package/src/serve/hub-app/project-gutter.tsx +128 -0
- package/src/serve/hub-app/projects-svc.ts +226 -0
- package/src/serve/hub-app/prose-hits-st.ts +124 -0
- package/src/serve/hub-app/prose-hits-view.tsx +176 -0
- package/src/serve/hub-app/prose-hits.css +21 -0
- package/src/serve/hub-app/run-strip.ts +47 -0
- package/src/serve/hub-app/runs-tabs.ts +24 -0
- package/src/serve/hub-app/test-view.tsx +107 -0
- package/src/serve/hub-app/tests-svc.ts +62 -0
- package/src/serve/hub-app/tests-view.tsx +238 -0
- package/src/serve/hub-app/time-ago.tsx +34 -0
- package/src/serve/hub-app/timeline-chart.tsx +274 -0
- package/src/serve/hub-app/timeline-geometry.ts +314 -0
- package/src/serve/hub-app/timeline-svc.ts +62 -0
- package/src/serve/hub-app/timeline-view.tsx +245 -0
- package/src/serve/hub-app/tree-build.ts +410 -0
- package/src/serve/hub-app/tree-row.tsx +42 -0
- package/src/serve/hub-app/tree-svc.ts +174 -0
- package/src/serve/hub-app/wikis-view.tsx +53 -0
- package/src/serve/other-repos.ts +79 -0
- package/src/serve/project-gutter-route.ts +350 -0
- package/src/serve/project-gutter.ts +449 -0
- package/src/serve/run-serve.ts +245 -16
- package/src/serve/same-origin.ts +14 -0
- package/src/serve/serve-hub.ts +213 -18
- package/src/shipkit.ts +19 -12
- package/src/smoke/run-smoke.ts +3 -2
- package/src/start-repo.ts +75 -36
- package/src/suite/judge-suites.ts +209 -15
- package/src/suite/kept-timings.ts +58 -0
- package/src/suite/runner-shapes.ts +88 -0
- package/src/suite/suite-cli.ts +147 -24
- package/src/suite/suite-cpu-preload.ts +92 -0
- package/src/suite/test-units.ts +243 -0
- package/src/ts/config.ts +4 -69
- package/src/ts/jsonc.ts +69 -0
- package/src/ts/lex.ts +143 -0
- package/src/ts/rename.ts +73 -10
- package/src/ts/source-graph.ts +28 -8
- package/src/ts/tsconfig-projects.ts +6 -97
- package/src/ts/tsconfig-resolve.ts +108 -0
- package/src/tui/build-steps-panel.ts +61 -0
- package/src/tui/checks-panel.ts +24 -16
- package/src/tui/run-tui.ts +2 -2
- package/src/tui/tui-panel.ts +5 -4
- package/src/wiki/graph-colors.ts +68 -0
- package/src/wiki/graph-mount.ts +7 -9
- package/src/wiki/graph-page.ts +42 -25
- package/src/wiki/graph-positions.ts +105 -0
- package/src/wiki/run-wiki.ts +1 -1
- package/src/wiki/serve-graph.ts +33 -3
- package/src/cli/cmd_Rules.ts +0 -51
- package/src/cli/cmd_Units.ts +0 -82
- package/src/collections/graphs.ts +0 -32
- package/src/contracts/contracts-once.ts +0 -43
- package/src/core/check-suite.ts +0 -302
- package/src/core/repair-contract-dirs.ts +0 -136
- package/src/core/task-runner.ts +0 -339
- package/src/ledger/gate-units.ts +0 -138
- package/src/ledger/unit-table.ts +0 -105
- package/src/rules/SK002-contracts-fulfillness.ts +0 -24
- package/src/rules/SK003-claude-md-shape.ts +0 -70
- package/src/rules/SK004-gitignore-policy.ts +0 -238
- package/src/rules/SK010-undeclared-release-unit.ts +0 -127
- package/src/rules/rule-registry.ts +0 -64
- package/src/rules/run-fix.ts +0 -72
- package/src/tui/tasks-panel.ts +0 -60
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# rvlib-shipkit
|
|
2
2
|
|
|
3
|
-
Repo toolkit for shipping: one config file gives a repo a commit gate (
|
|
3
|
+
Repo toolkit for shipping: one config file gives a repo a commit gate (one DAG over every check), release-contract fulfillness checks, a human ask queue, markdown-wiki tooling, and the asset atelier (generate, curate, and ship assets — favicon/logo/OG included).
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
6
|
bun add -d rvlib-shipkit
|
|
@@ -14,12 +14,23 @@ import { defineRepo } from 'rvlib-shipkit'
|
|
|
14
14
|
export default defineRepo({
|
|
15
15
|
name: 'my-game',
|
|
16
16
|
venues: ['itch'], // which release contracts are REQUIRED
|
|
17
|
-
social: ['x', 'discord'], // .
|
|
18
|
-
tasks: [{ id: 'e2e', cmd: ['bun', 'run', 'e2e'], deps: ['lint'] }],
|
|
17
|
+
social: ['x', 'discord'], // .shipkit/social/<channel>/ post folders (+ .shipkit/discord/)
|
|
19
18
|
})
|
|
20
19
|
```
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
Everything the gate runs beyond the floor is a CHECK, and a check needs no config at all: drop a `<name>.check.ts` file anywhere and `shipkit ci`, `shipkit check`, `shipkit checks` and the hub discover it.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// e2e.check.ts
|
|
25
|
+
import { check } from 'rvlib-shipkit/src/check.ts'
|
|
26
|
+
export default check({
|
|
27
|
+
description: 'the end to end suite passes against a built app',
|
|
28
|
+
blocking: true,
|
|
29
|
+
build: true,
|
|
30
|
+
cmd: ['bun', 'run', 'e2e'],
|
|
31
|
+
deps: ['lint'],
|
|
32
|
+
})
|
|
33
|
+
```
|
|
23
34
|
|
|
24
35
|
## CLI
|
|
25
36
|
|
|
@@ -27,17 +38,18 @@ Health checks need no config at all: drop a `<name>.check.ts` file anywhere and
|
|
|
27
38
|
shipkit ci commit gate: floor (lint/typecheck/test/imports/cycles —
|
|
28
39
|
scripts win, imports/cycles/typecheck fill from bundled
|
|
29
40
|
tools with zero config, gaps print visibly) + contracts
|
|
30
|
-
+ your
|
|
41
|
+
+ your checks, parallel DAG; refreshes STATUS.md
|
|
31
42
|
[--bail --skip g --only id --json]
|
|
32
43
|
shipkit contracts release-contract fulfillness alone [--venue v --json]
|
|
33
|
-
shipkit check periodic health: built-in
|
|
44
|
+
shipkit check periodic health: the built-in checks + every *.check.ts in
|
|
34
45
|
the repo, auto-discovered + TTL-cached
|
|
35
46
|
[--json --fresh --frozen --fix [SK### …]]
|
|
36
|
-
--fix applies
|
|
37
|
-
self-maintaining
|
|
38
|
-
shipkit
|
|
39
|
-
fixable
|
|
40
|
-
|
|
47
|
+
--fix applies check autofixes then re-runs them; --frozen =
|
|
48
|
+
self-maintaining checks (rvlib self-update) report only
|
|
49
|
+
shipkit checks the inventory: every check, where it runs, blocks or warns,
|
|
50
|
+
and how it caches. --fixable = what repairs itself,
|
|
51
|
+
with its owning doc [--json --md --out]
|
|
52
|
+
shipkit tui live dashboard: build steps + every other check + panels
|
|
41
53
|
the repo injects via repo.config tui.panels
|
|
42
54
|
shipkit asks human action queue: list · ask "<title>" · asks check (auto-close)
|
|
43
55
|
shipkit needs everything blocked on a human, red/yellow by collection
|
|
@@ -45,7 +57,7 @@ shipkit needs everything blocked on a human, red/yellow by collection
|
|
|
45
57
|
shipkit wiki serve|lint — live md link-graph + orphan/dangling-ref lint
|
|
46
58
|
shipkit mv rename an md page + its sibling dir, sweeping every ref
|
|
47
59
|
shipkit assets serve|fill|add|gen|identity|check — the asset atelier
|
|
48
|
-
(declare kinds in .
|
|
60
|
+
(declare kinds in .shipkit/assets/kit.ts, roll, curate, ship)
|
|
49
61
|
shipkit pb collection <name> — scaffold a PocketBase collection's UI
|
|
50
62
|
folder (columns/card/grid/form/index) from the app's
|
|
51
63
|
generated pb types, create-only [--dry-run]
|
|
@@ -55,7 +67,7 @@ shipkit smoke browser pass over a DEPLOYED web app, read only: login
|
|
|
55
67
|
quota headers, a detail page per `open` link. Exit code =
|
|
56
68
|
failed routes. Needs playwright in the repo
|
|
57
69
|
[--base <url> --routes /a,/b]
|
|
58
|
-
shipkit serve the repo's ONE server: hub page,
|
|
70
|
+
shipkit serve the repo's ONE server: hub page, wikis, studio, status
|
|
59
71
|
shipkit reflect your prompts across every Claude Code session of a repo:
|
|
60
72
|
all config roots, queued-mid-run ones included, harness
|
|
61
73
|
noise out, chronological md corpus for a retrospective
|
|
@@ -75,6 +87,6 @@ Ship your own contracts via `contracts: { extra: [defineContract({...})] }` —
|
|
|
75
87
|
|
|
76
88
|
## Library use
|
|
77
89
|
|
|
78
|
-
Everything the CLI does is importable: `
|
|
90
|
+
Everything the CLI does is importable: `runChecks`, `evaluateContracts`, the asks queue, `scanMdGraph`/`lintMd`/`serveMdGraph`, and the assets surface (`defineKit`/`defineKind`, `identityKinds`, `studioRoutes`, `saveEndpoint` via `rvlib-shipkit/vite`). Ships TypeScript source. Bun ≥ 1.0.
|
|
79
91
|
|
|
80
92
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rvlib-shipkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"funding": "https://github.com/sponsors/rvion",
|
|
5
5
|
"description": "All-in-one repo companion: ci gate (task DAG), auto-discovered health checks, release-contract fulfillness, asks queue, md-wiki tools, asset atelier, live tui",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@napi-rs/canvas": "^1.0.2",
|
|
37
37
|
"clipanion": "^4.0.0-rc.4",
|
|
38
|
-
"comfy-ts": "^2.
|
|
38
|
+
"comfy-ts": "^2.9.0",
|
|
39
39
|
"ink": "^7.1.1",
|
|
40
40
|
"marked": "^18.0.7",
|
|
41
41
|
"mobx": "^7.0.0",
|
|
@@ -96,15 +96,22 @@ export function applyVarValues(wf: DefinedWorkflow, vars: Record<string, unknown
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
// ──
|
|
100
|
-
//
|
|
99
|
+
// ── drafts — comfy-ts disk format, path rule taken from the ComfyTS instance
|
|
100
|
+
// so it cannot drift: .comfy-ts/drafts/<wfId>/<draftId>.json.
|
|
101
|
+
//
|
|
102
|
+
// The draft id is the KIND, never the asset: ONE `creature` draft behind all
|
|
103
|
+
// 107 creatures, so tuning the loras once tunes them for the whole kind. The
|
|
104
|
+
// draft is the BASE and the asset's own prompt is injected ON TOP of it at run
|
|
105
|
+
// time, so a prompt left sitting in a draft can never reach an asset, and
|
|
106
|
+
// playing with a draft cannot break the assets that share it.
|
|
107
|
+
//
|
|
101
108
|
// Shipkit does its own IO here because comfy-ts exports no drafts API yet;
|
|
102
109
|
// swap when it lands.
|
|
103
110
|
const draftDir = (lw: LoadedWorkflow): string => ComfyTS.create().resolveFromDrafts(lw.id)
|
|
104
111
|
export const draftPath = (lw: LoadedWorkflow, name: string): string => join(draftDir(lw), `${name}.json`)
|
|
105
112
|
|
|
106
|
-
export function readAssetDraft(lw: LoadedWorkflow,
|
|
107
|
-
const p = draftPath(lw,
|
|
113
|
+
export function readAssetDraft(lw: LoadedWorkflow, draftId: string): Record<string, unknown> | null {
|
|
114
|
+
const p = draftPath(lw, draftId)
|
|
108
115
|
if (!existsSync(p)) return null
|
|
109
116
|
try {
|
|
110
117
|
return JSON.parse(readFileSync(p, 'utf8')) as Record<string, unknown>
|
|
@@ -116,8 +123,8 @@ export function readAssetDraft(lw: LoadedWorkflow, assetId: string): Record<stri
|
|
|
116
123
|
/** Seed `{ prompt }` ONLY when the draft is absent (hand-tuned state is never
|
|
117
124
|
* overwritten). The stored prompt is TUI context; generation overrides it.
|
|
118
125
|
* Returns true when just seeded. */
|
|
119
|
-
export function ensureAssetDraft(lw: LoadedWorkflow,
|
|
120
|
-
const p = draftPath(lw,
|
|
126
|
+
export function ensureAssetDraft(lw: LoadedWorkflow, draftId: string, prompt: string): boolean {
|
|
127
|
+
const p = draftPath(lw, draftId)
|
|
121
128
|
if (existsSync(p)) return false
|
|
122
129
|
mkdirSync(draftDir(lw), { recursive: true })
|
|
123
130
|
writeFileSync(p, JSON.stringify({ prompt }, null, 2) + '\n')
|
|
@@ -133,22 +140,32 @@ export function ensureAssetDraft(lw: LoadedWorkflow, assetId: string, prompt: st
|
|
|
133
140
|
export async function generateComfyTs(
|
|
134
141
|
rt: ComfyTsRuntime,
|
|
135
142
|
lw: LoadedWorkflow,
|
|
136
|
-
args: { prompt: string; seed: number; assetId: string; destDir: string; name: string; vars?: Record<string, unknown>; onProgress?: OnProgress },
|
|
143
|
+
args: { prompt: string; seed: number; assetId: string; draftId?: string; destDir: string; name: string; vars?: Record<string, unknown>; onProgress?: OnProgress },
|
|
137
144
|
): Promise<string[]> {
|
|
138
|
-
|
|
145
|
+
// the KIND's shared draft, falling back to the asset for a caller that
|
|
146
|
+
// names no kind. It is what the tune button edits
|
|
147
|
+
const draftId = args.draftId ?? args.assetId
|
|
148
|
+
ensureAssetDraft(lw, draftId, args.prompt)
|
|
139
149
|
const defs = varsOf(lw.wf)
|
|
140
150
|
// modules are import-cached: a previous asset's values must not leak
|
|
141
151
|
for (const [, varDef] of lw.wf.entries()) varDef.reset()
|
|
142
152
|
if (args.vars != null) applyVarValues(lw.wf, args.vars, `asset '${args.assetId}' kit vars`)
|
|
143
|
-
const draft = readAssetDraft(lw,
|
|
153
|
+
const draft = readAssetDraft(lw, draftId)
|
|
144
154
|
if (draft != null) {
|
|
145
155
|
for (const [key, value] of Object.entries(draft)) {
|
|
146
156
|
const varDef = defs[key]
|
|
147
|
-
if (varDef == null) { console.log(`WARN draft '${
|
|
157
|
+
if (varDef == null) { console.log(`WARN draft '${draftId}': stale var '${key}' not on workflow '${lw.id}' — ignored`); continue }
|
|
148
158
|
varDef.loadJSON(value)
|
|
149
159
|
}
|
|
150
160
|
}
|
|
161
|
+
// LAST, and unconditionally, the three the KIT owns go ON TOP of the shared
|
|
162
|
+
// draft: the asset's prompt, the kind's declared size, and the roll's seed.
|
|
163
|
+
// Everything else (loras, steps, cfg, removeBg) is the draft's to own. Size
|
|
164
|
+
// is here because the kind ships its rolls into a fixed box, so a size tried
|
|
165
|
+
// in the panel must not silently resize every asset that shares the draft
|
|
151
166
|
defs.prompt!.parse(args.prompt)
|
|
167
|
+
const kitSize = (args.vars ?? {})['size']
|
|
168
|
+
if (kitSize != null) defs['size']?.loadJSON(kitSize)
|
|
152
169
|
// '= N' pins mode AND value, neutralizing any ?/+ mode a draft restored,
|
|
153
170
|
// so the v<seed> file name stays truthful
|
|
154
171
|
defs.seed!.parse(`= ${args.seed}`)
|
|
@@ -45,7 +45,7 @@ export async function generateRoll(
|
|
|
45
45
|
}
|
|
46
46
|
const rt = await resolveComfy()
|
|
47
47
|
const lw = workflowFor(rt.workflows, { workflow: gen.workflow, assetId: found.asset.id })
|
|
48
|
-
await generateComfyTs(rt, lw, { prompt: args.prompt, seed: args.seed, assetId: found.asset.id, destDir: args.destDir, name: `v${args.seed}`, vars: gen.vars, onProgress: args.onProgress })
|
|
48
|
+
await generateComfyTs(rt, lw, { prompt: args.prompt, seed: args.seed, assetId: found.asset.id, draftId: found.kind.id, destDir: args.destDir, name: `v${args.seed}`, vars: gen.vars, onProgress: args.onProgress })
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/** Memoized lazy comfy runtime for a resolved kit — loads the cflow modules on
|
package/src/assets/kit-config.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// defineKit — the repo-side surface: .
|
|
1
|
+
// defineKit — the repo-side surface: .shipkit/assets/kit.ts declares asset kinds,
|
|
2
2
|
// generators, ship targets and metadata panels; the studio/CLI derive
|
|
3
3
|
// everything else. STRUCTURE lives here (git-reviewed TS); studio-edited
|
|
4
4
|
// state lives in the workspace overlays (pick.json, prompt.json, *.rect.json).
|
|
@@ -64,7 +64,7 @@ export type CustomPanelSpec = {
|
|
|
64
64
|
panel: 'custom'
|
|
65
65
|
/** url-safe kebab-case, unique per kind — becomes the served module name */
|
|
66
66
|
id: string
|
|
67
|
-
/** repo-root-relative ES module path (e.g. .
|
|
67
|
+
/** repo-root-relative ES module path (e.g. .shipkit/assets/panels/legs-editor.js) */
|
|
68
68
|
script: string
|
|
69
69
|
/** default corner of the floating card on the canvas (drag overrides) */
|
|
70
70
|
anchor?: 'tl' | 'bl'
|
|
@@ -113,7 +113,7 @@ export type Kind = {
|
|
|
113
113
|
media?: Media
|
|
114
114
|
/** icon kinds get no geometry, thumbnails only */
|
|
115
115
|
mode?: 'sprite' | 'icon'
|
|
116
|
-
/** via = repo hook module path (default .
|
|
116
|
+
/** via = repo hook module path (default .shipkit/assets/ship.ts) · fn = a
|
|
117
117
|
* built-in lib hook (e.g. identityKinds re-rendering the set) — never
|
|
118
118
|
* both. With NO hook, `dir` alone is the zero-config ship: the picked
|
|
119
119
|
* roll is copied to <dir>/<assetId>.<ext>; hooks read dir as data. */
|
|
@@ -224,14 +224,14 @@ export function missingPanelScripts(kit: KitConfig, root: string): string[] {
|
|
|
224
224
|
export function defineKit(k: KitConfig): ResolvedKit {
|
|
225
225
|
const errs = validateKit(k)
|
|
226
226
|
if (errs.length > 0) throw new Error(`invalid kit:\n ${errs.join('\n ')}`)
|
|
227
|
-
return { ...k, workspace: k.workspace ?? '.
|
|
227
|
+
return { ...k, workspace: k.workspace ?? '.shipkit/assets/out' }
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
export const defineKind = (k: Kind): Kind => k
|
|
231
231
|
|
|
232
232
|
export async function loadKit(root: string): Promise<ResolvedKit> {
|
|
233
|
-
const path = join(root, '.
|
|
234
|
-
if (!existsSync(path)) throw new Error(`no .
|
|
233
|
+
const path = join(root, '.shipkit/assets/kit.ts')
|
|
234
|
+
if (!existsSync(path)) throw new Error(`no .shipkit/assets/kit.ts in ${root} — create one (defineKit) or run the init snippet from the docs`)
|
|
235
235
|
const mod = (await import(path)) as { default?: ResolvedKit }
|
|
236
236
|
if (mod.default == null) throw new Error(`${path}: must default-export defineKit({...})`)
|
|
237
237
|
const missing = missingPanelScripts(mod.default, root)
|
package/src/assets/run-assets.ts
CHANGED
|
@@ -107,19 +107,19 @@ export const runAssetsGen = (root: string, io: AssetsIo, o: { assetId: string; n
|
|
|
107
107
|
// ── identity: ONE render path + a brand.ts content hash so drift is
|
|
108
108
|
// machine-detectable — and machine-CLEARED (auto re-render), never a nag.
|
|
109
109
|
// Only a missing PICK still asks a human (taste).
|
|
110
|
-
const brandHashPath = (root: string): string => join(root, '.
|
|
110
|
+
const brandHashPath = (root: string): string => join(root, '.shipkit/identity/brand.hash')
|
|
111
111
|
const brandHash = async (root: string): Promise<string> =>
|
|
112
|
-
new Bun.CryptoHasher('sha256').update(await Bun.file(join(root, '.
|
|
112
|
+
new Bun.CryptoHasher('sha256').update(await Bun.file(join(root, '.shipkit/identity/brand.ts')).text()).digest('hex').slice(0, 16)
|
|
113
113
|
|
|
114
114
|
export type IdentityRender = { written: string[] } | { blocked: string }
|
|
115
115
|
|
|
116
116
|
export async function renderIdentityNow(root: string): Promise<IdentityRender> {
|
|
117
|
-
const brandPath = join(root, '.
|
|
118
|
-
if (!existsSync(brandPath)) return { blocked: 'no .
|
|
117
|
+
const brandPath = join(root, '.shipkit/identity/brand.ts')
|
|
118
|
+
if (!existsSync(brandPath)) return { blocked: 'no .shipkit/identity/brand.ts — create one exporting a Brand (name, palette, mark)' }
|
|
119
119
|
// cache-bust: bun caches dynamic imports for the process lifetime — a
|
|
120
120
|
// long-running hub must re-render the EDITED brand, not the cached one
|
|
121
121
|
const mod = (await import(`${brandPath}?v=${await brandHash(root)}`)) as { default?: Brand }
|
|
122
|
-
if (mod.default == null) return { blocked: '.
|
|
122
|
+
if (mod.default == null) return { blocked: '.shipkit/identity/brand.ts must default-export the Brand' }
|
|
123
123
|
const brand = mod.default
|
|
124
124
|
let markFile: string | undefined
|
|
125
125
|
if (brand.mark.backend === 'comfy') {
|
|
@@ -141,7 +141,7 @@ export async function renderIdentityNow(root: string): Promise<IdentityRender> {
|
|
|
141
141
|
/** brand.ts drifted since the last render → re-render NOW (autonomous).
|
|
142
142
|
* Returns what happened; 'blocked' = only a human pick can clear it. */
|
|
143
143
|
export async function healIdentity(root: string): Promise<{ state: 'fresh' | 'rendered' | 'blocked' | 'absent'; detail?: string }> {
|
|
144
|
-
if (!existsSync(join(root, '.
|
|
144
|
+
if (!existsSync(join(root, '.shipkit/identity/brand.ts'))) return { state: 'absent' }
|
|
145
145
|
const outputsMissing = ['public/favicon.png', 'public/og.png', 'public/logo.png'].some((p) => !existsSync(join(root, p)))
|
|
146
146
|
const stored = existsSync(brandHashPath(root)) ? (await Bun.file(brandHashPath(root)).text()).trim() : null
|
|
147
147
|
if (!outputsMissing && stored === (await brandHash(root))) return { state: 'fresh' }
|
|
@@ -197,10 +197,11 @@ export const runAssetsCheck = (root: string, io: AssetsIo): Promise<number> =>
|
|
|
197
197
|
bad++
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
|
-
// one draft per comfy
|
|
201
|
-
// ONE aggregate line: per-asset rows would drown the check
|
|
200
|
+
// one draft per comfy KIND, shared by its assets — the tweakable
|
|
201
|
+
// entry. ONE aggregate line: per-asset rows would drown the check
|
|
202
202
|
let draftCount = 0
|
|
203
203
|
let draftSeeded = 0
|
|
204
|
+
const draftKinds = new Set<string>()
|
|
204
205
|
for (const f of allAssets(kit).filter((x) => x.kind.generate.backend === 'comfy')) {
|
|
205
206
|
let lw: LoadedWorkflow
|
|
206
207
|
try {
|
|
@@ -209,10 +210,13 @@ export const runAssetsCheck = (root: string, io: AssetsIo): Promise<number> =>
|
|
|
209
210
|
continue // an unresolvable workflow already FAILed on its kind above
|
|
210
211
|
}
|
|
211
212
|
const prompt = (await readPromptOverlay(assetDir(join(root, kit.workspace), f.asset.id))).prompt ?? f.asset.prompt
|
|
212
|
-
|
|
213
|
+
// the first asset of a kind seeds it and the rest share it, so
|
|
214
|
+
// a lora tuned once holds for every asset of that kind
|
|
215
|
+
if (ensureAssetDraft(lw, f.kind.id, prompt)) draftSeeded++
|
|
216
|
+
draftKinds.add(`${lw.id}/${f.kind.id}`)
|
|
213
217
|
draftCount++
|
|
214
218
|
}
|
|
215
|
-
if (draftCount > 0) io.print(`ok:
|
|
219
|
+
if (draftCount > 0) io.print(`ok: ${draftKinds.size} draft(s) behind ${draftCount} comfy asset(s) in .comfy-ts/drafts/${draftSeeded > 0 ? ` (${draftSeeded} seeded now)` : ''}`)
|
|
216
220
|
// offline graph validation when the host has a schema cache;
|
|
217
221
|
// a missing cache is world state → WARN, never a repo FAIL
|
|
218
222
|
const hostsWarned = new Set<string>()
|
|
@@ -83,10 +83,10 @@ export async function studioRoutes(deps: StudioDeps): Promise<StudioRoutes> {
|
|
|
83
83
|
let ship: AssetInfo['ship']
|
|
84
84
|
if (f.kind.ship?.fn != null) ship = { mode: 'built-in', note: 're-renders the set on pick' }
|
|
85
85
|
else {
|
|
86
|
-
const via = f.kind.ship?.via ?? '.
|
|
86
|
+
const via = f.kind.ship?.via ?? '.shipkit/assets/ship.ts'
|
|
87
87
|
ship = existsSync(join(root, via)) ? { mode: 'hook', path: via } : { mode: 'record', note: 'pick.json only — no ship hook' }
|
|
88
88
|
}
|
|
89
|
-
return { backend, deterministic: backend === 'canvas', workflow, ship, metaFile: f.kind.meta?.file ?? null, kindId: f.kind.id, kitSource: '.
|
|
89
|
+
return { backend, deterministic: backend === 'canvas', workflow, ship, metaFile: f.kind.meta?.file ?? null, kindId: f.kind.id, kitSource: '.shipkit/assets/kit.ts' }
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// effective prompt: workspace overlay wins over the kit's builder output
|
|
@@ -100,7 +100,8 @@ export async function studioRoutes(deps: StudioDeps): Promise<StudioRoutes> {
|
|
|
100
100
|
try {
|
|
101
101
|
const gen = f.kind.generate as { workflow?: string }
|
|
102
102
|
const lw = workflowFor(workflows, { workflow: gen.workflow, assetId: f.asset.id })
|
|
103
|
-
|
|
103
|
+
// the KIND's draft, shared by every asset of that kind
|
|
104
|
+
if (ensureAssetDraft(lw, f.kind.id, await promptOf(f))) console.log(`ok: draft '${f.kind.id}' seeded → .comfy-ts/drafts/${lw.id}/${f.kind.id}.json`)
|
|
104
105
|
} catch (e) {
|
|
105
106
|
console.log(`WARN draft for '${f.asset.id}' not seeded — ${(e as Error).message}`)
|
|
106
107
|
}
|
|
@@ -176,12 +177,12 @@ export async function studioRoutes(deps: StudioDeps): Promise<StudioRoutes> {
|
|
|
176
177
|
|
|
177
178
|
// ── ship hooks: a kind's BUILT-IN ship.fn (e.g. identityKinds re-rendering
|
|
178
179
|
// the set from the pick) wins; otherwise the repo module is probed once
|
|
179
|
-
// PER PATH (ship.via, default .
|
|
180
|
+
// PER PATH (ship.via, default .shipkit/assets/ship.ts; fixing a hook needs a
|
|
180
181
|
// studio restart) ───────────────────────────────────────────────────────
|
|
181
182
|
const shipFns = new Map<string, ShipFn | null>()
|
|
182
183
|
const resolveShipFn = async (f: FoundAsset): Promise<ShipFn | null> => {
|
|
183
184
|
if (f.kind.ship?.fn != null) return f.kind.ship.fn
|
|
184
|
-
const shipPath = join(root, f.kind.ship?.via ?? '.
|
|
185
|
+
const shipPath = join(root, f.kind.ship?.via ?? '.shipkit/assets/ship.ts')
|
|
185
186
|
let fn = shipFns.get(shipPath)
|
|
186
187
|
if (fn === undefined) {
|
|
187
188
|
if (existsSync(shipPath)) {
|