@toist/in 0.5.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,50 @@
2
2
 
3
3
  All notable changes to `@toist/in` are recorded here.
4
4
 
5
+ ## 0.7.1 — 2026-05-06
6
+
7
+ Fix @toist/aja cross-package dependency versions not being bumped by release script
8
+
9
+ Scaffold template pins `@toist/aja` and `@toist/spec` to `^0.7.1`.
10
+
11
+ ## 0.7.0 — 2026-05-06
12
+
13
+ Add @toist/aja CLI (bunx @toist/aja --config toist.yml) and @toist/in interactive setup wizard (bunx @toist/in). Establishes aja=runner, in=adoption tool split. No forced directory layout — user chooses paths via wizard, config drives the runner.
14
+
15
+ Scaffold template pins `@toist/aja` and `@toist/spec` to `^0.7.0`.
16
+
17
+ ## Unreleased
18
+
19
+ Clarify `@toist/in` as the Toist adoption tool, not only a scaffold CLI.
20
+
21
+ - `bunx @toist/in` scaffolds `.toist/` in the current repo — one clear
22
+ action, no silent branching.
23
+ - `bunx @toist/in upgrade` is now the explicit upgrade subcommand.
24
+ - `bunx @toist/in <path>` scaffolds a dedicated Toist home at `<path>`.
25
+ - Added a dedicated embedded template for repo-owned `.toist/` layout
26
+ (no nested `package.json`, kinds import examples pointing to host
27
+ source tree, `instance.json` stub, `TOIST_PORT` env var).
28
+ - `bunx @toist/in upgrade` from inside `.toist/` resolves the host
29
+ repo's `package.json` automatically.
30
+ - Template/docs language now distinguishes dedicated instance
31
+ directories from embedded existing-project mode.
32
+ - Added package README documenting the two adoption modes.
33
+
34
+ ## 0.6.1 — 2026-05-05
35
+
36
+ Fix published @toist/aja dependency resolution for embedded runners and include the typed runner client/smoke-test surface.
37
+
38
+ Scaffold template pins `@toist/aja` and `@toist/spec` to `^0.6.1`.
39
+
40
+ ## 0.6.0 — 2026-05-05
41
+
42
+ **`bunx @toist/in` now upgrades existing instances.** Running without
43
+ arguments in a directory that has a `package.json` with `@toist/*` deps
44
+ bumps them all to the latest version — the same behaviour as the now-removed
45
+ `@toist/up` package. `@toist/up` is deprecated; migrate to `bunx @toist/in`.
46
+
47
+ Scaffold template pins `@toist/aja` and `@toist/spec` to `^0.6.0`.
48
+
5
49
  ## 0.5.0 — 2026-05-05
6
50
 
7
51
  Lockstep version bump. Scaffold template pins `@toist/aja` and `@toist/spec` to `^0.5.0`.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # `@toist/in`
2
+
3
+ Interactive setup wizard for Toist.
4
+
5
+ ```sh
6
+ bunx @toist/in
7
+ ```
8
+
9
+ Asks a few questions, writes `toist.yml`, creates your chosen directories.
10
+ Then start the runner with:
11
+
12
+ ```sh
13
+ bunx @toist/aja
14
+ ```
15
+
16
+ ## What the wizard asks
17
+
18
+ ```
19
+ Instance name? [my-project]
20
+ Pipelines dir? [pipelines]
21
+ Resources dir? [resources]
22
+ Data dir? [data]
23
+ Port? [3000]
24
+ Register custom TypeScript kinds? [y/N]
25
+ Kind file path? [src/kinds/index.ts]
26
+ Add another? [y/N]
27
+ ```
28
+
29
+ ## What it produces
30
+
31
+ `toist.yml` in the current directory — that's it. All paths are yours
32
+ to choose. There is no enforced layout.
33
+
34
+ ## Upgrade
35
+
36
+ ```sh
37
+ bunx @toist/in upgrade
38
+ ```
39
+
40
+ Finds `package.json` and bumps every `@toist/*` dep to latest.
41
+
42
+ ## See also
43
+
44
+ - `@toist/aja` — the runner: `bunx @toist/aja --config toist.yml`
45
+ - `context/instance-spec.md`
46
+ - https://toist.in
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@toist/in",
3
- "version": "0.5.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
- "description": "Scaffold a new toist instance: bunx @toist/in <path>",
5
+ "description": "Adopt Toist into a repo: scaffold a Toist home or embedded .toist layout",
6
6
  "main": "./src/index.ts",
7
7
  "types": "./src/index.ts",
8
8
  "bin": {
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "files": [
12
12
  "src/",
13
- "templates/",
14
- "CHANGELOG.md"
13
+ "CHANGELOG.md",
14
+ "README.md"
15
15
  ]
16
16
  }
package/src/index.ts CHANGED
@@ -1,23 +1,37 @@
1
1
  #!/usr/bin/env bun
2
2
  // 2121 toist
3
- // @toist/in — scaffold a new toist instance.
3
+ // @toist/in — interactive setup wizard for Toist.
4
4
  //
5
- // Usage: bunx @toist/in <path>
6
- //
7
- // Writes a fresh instance directory at <path> with start.ts, kinds/,
8
- // pipelines/, resources/, data/, README.md, and .gitignore. Prints a
9
- // suggested package.json snippet to stdout for the host to merge.
5
+ // Usage:
6
+ // bunx @toist/in run wizard → writes toist.yml + creates dirs
7
+ // bunx @toist/in upgrade upgrade @toist/* deps in current directory
10
8
 
11
- import { scaffold } from "./scaffold.ts"
9
+ import { wizard } from "./wizard.ts"
10
+ import { upgrade } from "./upgrade.ts"
12
11
 
13
12
  const args = process.argv.slice(2)
14
- const target = args[0]
13
+ const arg = args[0]
15
14
 
16
- if (!target || target === "--help" || target === "-h") {
17
- console.log("bunx @toist/in <path> scaffold a new toist instance at <path>")
15
+ if (arg === "--help" || arg === "-h") {
16
+ console.log("bunx @toist/in run setup wizard writes toist.yml")
17
+ console.log("bunx @toist/in upgrade upgrade @toist/* deps")
18
+ console.log("")
19
+ console.log("To start the runner after setup:")
20
+ console.log(" bunx @toist/aja")
18
21
  console.log("")
19
22
  console.log("Full docs: https://toist.in")
20
- process.exit(target ? 0 : 1)
23
+ process.exit(0)
21
24
  }
22
25
 
23
- await scaffold(target)
26
+ if (!arg) {
27
+ await wizard()
28
+ } else if (arg === "upgrade") {
29
+ await upgrade()
30
+ } else {
31
+ console.error(`Unknown command: ${arg}`)
32
+ console.error("")
33
+ console.error("Usage:")
34
+ console.error(" bunx @toist/in run setup wizard")
35
+ console.error(" bunx @toist/in upgrade upgrade @toist/* deps")
36
+ process.exit(1)
37
+ }
package/src/upgrade.ts ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bun
2
+ // 2121 toist
3
+ // Upgrade @toist/* deps in the nearest package.json to latest.
4
+ // Called by @toist/in when run without a path argument in an existing instance.
5
+
6
+ import { readFile } from "node:fs/promises"
7
+ import { existsSync } from "node:fs"
8
+ import { basename, dirname, resolve } from "node:path"
9
+ import { spawnSync } from "node:child_process"
10
+
11
+ function resolvePackageDir(cwd: string): string | null {
12
+ const localPkg = resolve(cwd, "package.json")
13
+ if (existsSync(localPkg)) return cwd
14
+
15
+ if (basename(cwd) === ".toist") {
16
+ const parent = dirname(cwd)
17
+ if (existsSync(resolve(parent, "package.json"))) return parent
18
+ }
19
+
20
+ return null
21
+ }
22
+
23
+ export async function upgrade(cwd = process.cwd()): Promise<void> {
24
+ const packageDir = resolvePackageDir(cwd)
25
+ if (!packageDir) {
26
+ console.error(`No package.json in ${cwd}.`)
27
+ console.error("Run from your host repo root or .toist/ directory.")
28
+ process.exit(1)
29
+ }
30
+
31
+ const pkgPath = resolve(packageDir, "package.json")
32
+
33
+ const pkg = JSON.parse(await readFile(pkgPath, "utf8")) as {
34
+ dependencies?: Record<string, string>
35
+ devDependencies?: Record<string, string>
36
+ }
37
+
38
+ const all: Record<string, string> = {
39
+ ...(pkg.dependencies ?? {}),
40
+ ...(pkg.devDependencies ?? {}),
41
+ }
42
+
43
+ // @toist/in itself is excluded — it runs via bunx, no local copy needed.
44
+ const targets = Object.keys(all)
45
+ .filter((n) => n.startsWith("@toist/") && n !== "@toist/in")
46
+ .sort()
47
+
48
+ if (targets.length === 0) {
49
+ console.log("No @toist/* deps in package.json — nothing to upgrade.")
50
+ process.exit(0)
51
+ }
52
+
53
+ console.log(`Upgrading: ${targets.join(", ")}\n`)
54
+ const result = spawnSync("bun", ["update", "--latest", ...targets], {
55
+ stdio: "inherit",
56
+ cwd: packageDir,
57
+ })
58
+ process.exit(result.status ?? 0)
59
+ }
package/src/wizard.ts ADDED
@@ -0,0 +1,144 @@
1
+ // 2121 toist
2
+ // Interactive setup wizard for @toist/in.
3
+ // Asks a few questions, writes toist.yml, creates directories.
4
+
5
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs"
6
+ import { createInterface } from "node:readline"
7
+ import { basename, resolve } from "node:path"
8
+
9
+ // Line-by-line reader that works with both TTY and piped stdin.
10
+ function makeReader() {
11
+ const iface = createInterface({ input: process.stdin, output: process.stdout, terminal: false })
12
+ const lines: string[] = []
13
+ let resolve: ((v: string) => void) | null = null
14
+
15
+ iface.on("line", (line) => {
16
+ if (resolve) {
17
+ const r = resolve
18
+ resolve = null
19
+ r(line)
20
+ } else {
21
+ lines.push(line)
22
+ }
23
+ })
24
+
25
+ iface.on("close", () => {
26
+ if (resolve) resolve("")
27
+ })
28
+
29
+ return {
30
+ read(): Promise<string> {
31
+ if (lines.length > 0) return Promise.resolve(lines.shift()!)
32
+ return new Promise((r) => { resolve = r })
33
+ },
34
+ close() { iface.close() },
35
+ }
36
+ }
37
+
38
+ async function prompt(reader: ReturnType<typeof makeReader>, label: string, def: string): Promise<string> {
39
+ process.stdout.write(`${label} [${def}] `)
40
+ const v = (await reader.read()).trim()
41
+ return v || def
42
+ }
43
+
44
+ async function yn(reader: ReturnType<typeof makeReader>, label: string, def = false): Promise<boolean> {
45
+ const hint = def ? "Y/n" : "y/N"
46
+ process.stdout.write(`${label} [${hint}] `)
47
+ const v = (await reader.read()).trim().toLowerCase()
48
+ if (!v) return def
49
+ return v === "y" || v === "yes"
50
+ }
51
+
52
+ export async function wizard(cwd = process.cwd()): Promise<void> {
53
+ const reader = makeReader()
54
+
55
+ console.log("")
56
+ console.log("Welcome to Toist. Let's set up your project.")
57
+ console.log("")
58
+
59
+ const defaultName = basename(cwd)
60
+ const name = await prompt(reader, "Instance name? ", defaultName)
61
+ const pipelines = await prompt(reader, "Pipelines dir? ", "pipelines")
62
+ const resources = await prompt(reader, "Resources dir? ", "resources")
63
+ const data = await prompt(reader, "Data dir? ", "data")
64
+ const port = await prompt(reader, "Port? ", "3000")
65
+
66
+ console.log("")
67
+ const hasKinds = await yn(reader, "Register custom TypeScript kinds?")
68
+
69
+ const kinds: string[] = []
70
+ if (hasKinds) {
71
+ console.log("")
72
+ let more = true
73
+ while (more) {
74
+ const kindFile = await prompt(reader, " Kind file path? ", "src/kinds/index.ts")
75
+ kinds.push(kindFile)
76
+ more = await yn(reader, " Add another? ")
77
+ }
78
+ }
79
+
80
+ reader.close()
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Write toist.yml
84
+ // ---------------------------------------------------------------------------
85
+
86
+ const kindsBlock = kinds.length > 0
87
+ ? `\nkinds:\n${kinds.map((k) => ` - ${k}`).join("\n")}`
88
+ : ""
89
+
90
+ const configContent = [
91
+ `# toist.yml — generated by bunx @toist/in`,
92
+ `# Run with: bunx @toist/aja`,
93
+ ``,
94
+ `instance: ${name}`,
95
+ `port: ${port}`,
96
+ `pipelines: ./${pipelines}`,
97
+ `resources: ./${resources}`,
98
+ `data: ./${data}`,
99
+ ...(kinds.length > 0 ? [``, `kinds:`, ...kinds.map((k) => ` - ${k}`)] : []),
100
+ ``,
101
+ ].join("\n")
102
+
103
+ const configPath = resolve(cwd, "toist.yml")
104
+ const alreadyExists = existsSync(configPath)
105
+
106
+ console.log("")
107
+
108
+ if (alreadyExists) {
109
+ console.log("toist.yml already exists — skipping (remove it to re-run wizard).")
110
+ } else {
111
+ writeFileSync(configPath, configContent, "utf8")
112
+ console.log("Wrote toist.yml")
113
+ }
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // Create directories
117
+ // ---------------------------------------------------------------------------
118
+
119
+ for (const dir of [pipelines, resources, data]) {
120
+ const abs = resolve(cwd, dir)
121
+ if (!existsSync(abs)) {
122
+ mkdirSync(abs, { recursive: true })
123
+ // Keep data/ out of git, commit the rest
124
+ if (dir === data) {
125
+ writeFileSync(resolve(abs, ".gitignore"), "*.db\n*.db-shm\n*.db-wal\n.lock/\n")
126
+ } else {
127
+ writeFileSync(resolve(abs, ".gitkeep"), "")
128
+ }
129
+ console.log(`Created ${dir}/`)
130
+ }
131
+ }
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // Done
135
+ // ---------------------------------------------------------------------------
136
+
137
+ console.log("")
138
+ console.log("Done. Start with:")
139
+ console.log(" bunx @toist/aja")
140
+ console.log("")
141
+ console.log("Or add a script to package.json:")
142
+ console.log(' "toist": "bunx @toist/aja"')
143
+ console.log("")
144
+ }
package/src/scaffold.ts DELETED
@@ -1,67 +0,0 @@
1
- // 2121 toist
2
- // Scaffold logic for @toist/in. Recursively copies templates/default/* into
3
- // the target directory, with two `_`-prefixed filenames renamed at copy time:
4
- //
5
- // - `_gitignore` -> `.gitignore`
6
- // - `_package.json` -> `package.json`
7
- //
8
- // The underscore prefix exists because npm strips files like `.gitignore` and
9
- // (in some configurations) `package.json` from publishable subdirs. Prefixing
10
- // dodges those rules; the runtime rename produces the real names in the host.
11
- //
12
- // Templates live in `<package>/templates/default/`, relative to this file's
13
- // runtime location. Works whether the package is consumed as a workspace
14
- // link (in this repo) or from node_modules (in a host repo).
15
-
16
- import { mkdir, copyFile, readdir, stat } from "node:fs/promises"
17
- import { existsSync } from "node:fs"
18
- import { dirname, join, resolve } from "node:path"
19
- import { fileURLToPath } from "node:url"
20
-
21
- const __dir = dirname(fileURLToPath(import.meta.url))
22
- const TEMPLATE_DIR = resolve(__dir, "..", "templates", "default")
23
-
24
- const RENAMES: Record<string, string> = {
25
- _gitignore: ".gitignore",
26
- "_package.json": "package.json",
27
- }
28
-
29
- export async function scaffold(target: string): Promise<void> {
30
- const dest = resolve(process.cwd(), target)
31
-
32
- if (existsSync(dest)) {
33
- const entries = await readdir(dest)
34
- if (entries.length > 0) {
35
- console.error(`Target directory '${dest}' is not empty. Aborting.`)
36
- process.exit(1)
37
- }
38
- } else {
39
- await mkdir(dest, { recursive: true })
40
- }
41
-
42
- await copyTree(TEMPLATE_DIR, dest)
43
-
44
- console.log(`\nScaffolded toist instance at ${dest}\n`)
45
- console.log("Next:")
46
- console.log(` cd ${target}`)
47
- console.log(` bun install`)
48
- console.log(` bun start.ts`)
49
- console.log("")
50
- console.log(`Full docs: https://toist.in`)
51
- console.log("")
52
- }
53
-
54
- async function copyTree(src: string, dest: string): Promise<void> {
55
- for (const entry of await readdir(src)) {
56
- const srcPath = join(src, entry)
57
- const destName = RENAMES[entry] ?? entry
58
- const destPath = join(dest, destName)
59
- const info = await stat(srcPath)
60
- if (info.isDirectory()) {
61
- await mkdir(destPath, { recursive: true })
62
- await copyTree(srcPath, destPath)
63
- } else {
64
- await copyFile(srcPath, destPath)
65
- }
66
- }
67
- }
@@ -1,31 +0,0 @@
1
- # my-toist-instance
2
-
3
- Generated with [`bunx @toist/in`](https://toist.in).
4
-
5
- ## First run
6
-
7
- 1. `bun install`
8
- 2. `bun start.ts` — runner boots on `http://localhost:3000`.
9
-
10
- Open `http://localhost:3000` to use the UI.
11
-
12
- ## Layout
13
-
14
- - `start.ts` — bootstrap; calls `startRunner` from `@toist/aja`
15
- - `kinds/custom.ts` — register your domain-specific kinds here
16
- - `pipelines/*.yaml` — pipeline definitions (per pipeline-spec)
17
- - `resources/*.yaml` — resource definitions (per resource-spec)
18
- - `data/` — runtime SQLite databases (gitignored)
19
-
20
- ## Upgrading
21
-
22
- ```sh
23
- bunx @toist/up
24
- ```
25
-
26
- Auto-detects every `@toist/*` dep in this directory's `package.json`
27
- and bumps them to the latest published version.
28
-
29
- ## More
30
-
31
- Full quickstart and docs: https://toist.in
@@ -1,7 +0,0 @@
1
- node_modules/
2
- bun.lock
3
- data/*.db
4
- data/*.db-shm
5
- data/*.db-wal
6
- data/.lock/
7
- .platform/
@@ -1,13 +0,0 @@
1
- {
2
- "name": "my-toist-instance",
3
- "version": "0.1.0",
4
- "type": "module",
5
- "scripts": {
6
- "start": "bun start.ts",
7
- "dev": "bun --watch start.ts"
8
- },
9
- "dependencies": {
10
- "@toist/aja": "^0.5.0",
11
- "@toist/spec": "^0.5.0"
12
- }
13
- }
File without changes
@@ -1,23 +0,0 @@
1
- // 2121 toist — register your domain-specific kinds here.
2
- //
3
- // Each export should be a NodeKind<P, I>. See the @toist/aja built-ins for
4
- // reference shapes, and pipeline-spec / kind-spec at https://toist.in for
5
- // the full contract.
6
- //
7
- // Example:
8
- //
9
- // import type { NodeKind } from "@toist/spec"
10
- //
11
- // export const greet: NodeKind<{ name: string }, { msg: string }> = {
12
- // id: "demo.greet",
13
- // params: { name: { type: "string", required: true } },
14
- // async execute(ctx) {
15
- // return { msg: `Hello, ${ctx.params.name}!` }
16
- // },
17
- // }
18
- //
19
- // Then in start.ts:
20
- // import * as customKinds from "./kinds/custom.ts"
21
- // register(...Object.values(customKinds))
22
-
23
- export {}
File without changes
File without changes
@@ -1,14 +0,0 @@
1
- // 2121 toist instance bootstrap.
2
- // See https://toist.in for the full quickstart.
3
-
4
- import { startRunner } from "@toist/aja"
5
-
6
- // Uncomment to register domain-specific kinds:
7
- // import { register } from "@toist/aja"
8
- // import * as customKinds from "./kinds/custom.ts"
9
- // register(...Object.values(customKinds))
10
-
11
- await startRunner({
12
- port: Number(process.env.PORT ?? 3000),
13
- rootDir: import.meta.dir,
14
- })