@jigging/jig 0.1.0-alpha.13 → 0.1.0-alpha.15

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/README.md CHANGED
@@ -1,21 +1,7 @@
1
1
  # `@jigging/jig`
2
2
 
3
- Jig puts reusable Agent methods to work with powers you approve. Choose your
4
- Agents, combine specialists, inspect their results, and stop owned work.
5
- The developer alpha has one finite command surface:
6
-
7
- ```text
8
- jig init --bare <directory>
9
- jig review [project] [--yes]
10
- jig run <flow:path|binding:id> [--input JSON|@FILE] [--attach NAME=DIR] [--out DIR] [--timeout DURATION]
11
- jig --version
12
- ```
13
-
14
- `jig --version` prints the built package version without opening a project or
15
- acquiring a sandbox.
16
-
17
- There is no `jig setup`. `review` and `run` transparently acquire the rootless
18
- authority they need or fail closed.
3
+ Put reusable Agent methods to work with powers you approve. Choose your Agents,
4
+ combine specialists, inspect their results, and stop owned work.
19
5
 
20
6
  ## Install
21
7
 
@@ -23,183 +9,41 @@ authority they need or fail closed.
23
9
  npm install --global @jigging/jig@alpha
24
10
  ```
25
11
 
26
- Installing Jig also installs `@oven/bun-linux-x64-baseline@1.3.3` as an exact
27
- external runtime dependency. Bun is not embedded in or bundled with the
28
- `@jigging/jig` archive. npm verifies the installed package; Jig selects only
29
- that closed package-local path, authenticates its version, revision, and digest
30
- before evaluator or Flow bytes execute, and revalidates it before each launch.
31
-
32
- For a useful application, try [an issue becoming a tested patch](https://jig.md/guide/tested-patch).
33
- It selects local source files and returns a patch with executed checks, without
34
- changing the original repository.
35
-
36
- ## Supported host
37
-
38
- The alpha has been independently tested on a provisioned Ubuntu 24.04 x86_64
39
- host. Other Linux x86_64 hosts meeting the requirements below have not yet been
40
- independently validated; Jig fails closed when a required capability is absent.
41
-
42
- The alpha requires:
43
-
44
- - Linux x86_64 with glibc 2.17 or newer and an SSE4.2-capable baseline CPU;
45
- - Bubblewrap 0.12 or newer and GNU `readlink -f`, located as described below;
46
- - cgroup v2 with delegated `cpu`, `memory`, and `pids` controllers;
47
- - a systemd user manager with transient delegated scopes; and
48
- - unprivileged user, mount, PID, network, IPC, UTS, and cgroup namespaces.
49
-
50
- The glibc and SSE4.2 floors come from the selected Bun baseline runtime.
51
-
52
- Host tools are resolved through `/usr/bin`, `/bin`, or the NixOS system profile
53
- `/run/current-system/sw/bin`, never ambient `PATH`. An operator may select
54
- Bubblewrap using an absolute `JIG_BWRAP_PATH`, including a shell-local Nix
55
- dependency. It receives the same validation and never falls back on failure.
56
- On NixOS, enable
57
- `programs.nix-ld.enable` for the unmodified npm runtime. Jig uses the real glibc
58
- loader at the system-managed `share/nix-ld/lib/ld.so` link, mounting only that
59
- loader and its required libraries into Runs. It does not mount nix-ld or the
60
- whole Nix store. Independent NixOS host conformance is not yet established.
61
-
62
- Jig commands do not use `sudo`, download runtimes, or expose host control to
63
- Flow code. Runtime installation is handled once by npm with the Jig package.
64
-
65
- Flow Runs default to 30 seconds. `jig run --timeout DURATION` accepts a
66
- positive integer followed by `ms`, `s`, `m`, or `h`, up to 24 hours. Flow
67
- execution scopes remain fixed at 256 MiB aggregate memory, 64 aggregate PIDs,
68
- and 50% of one CPU. Project evaluation is fixed at 3 seconds, 256 MiB, 64
69
- PIDs, and 50% CPU. Locked dependency preparation is fixed at 60 seconds, 512
70
- MiB, 64 PIDs, and one CPU. See the repository
71
- [`SECURITY.md`](https://github.com/jiggy/jig/blob/main/SECURITY.md) for the
72
- threat boundary and private reporting channel.
73
-
74
- ## Use
75
-
76
- ```console
77
- jig init --bare my-project
78
- cd my-project
79
- ```
80
-
81
- Place packages under `flows/<name>/`. Each package has exact-case `FLOW.md`
82
- and, for this alpha, one `flow.ts`. The generated `jig.ts`
83
- explicitly discovers `./flows` and `./bindings`.
12
+ The developer alpha requires Linux x86_64/glibc, Bubblewrap, and a systemd user
13
+ manager with delegated cgroup v2 controllers. See the
14
+ [complete supported-host requirements](https://jig.md/guide/#supported-host).
15
+ npm installs Jig's exact Bun runtime dependency alongside the package.
84
16
 
85
- The published `@jigging/flow@0.1.0-alpha.7` package provides the small Run/1
86
- authoring API. Declare it exactly in the Flow's `package.json`, generate a text
87
- `bun.lock` with Bun 1.3.3 and `bun install --lockfile-only`, then handle one
88
- finite Run:
17
+ ## Get started
89
18
 
90
- ```ts
91
- import { handle } from "@jigging/flow";
19
+ [Create your first Flow](https://jig.md/guide/#your-first-flow), or try
20
+ [an issue becoming a tested patch](https://jig.md/guide/tested-patch): selected
21
+ local source becomes a reviewable patch with executed checks, while the original
22
+ repository stays unchanged.
92
23
 
93
- await handle(async (run) => ({
94
- outcome: "done",
95
- output: { received: run.input },
96
- }));
97
- ```
98
-
99
- ```console
100
- jig review
101
- jig run flow:flows/example --input '{}'
102
- ```
103
-
104
- For a longer Run, add a bounded duration such as `--timeout 2m`. The timeout
105
- starts when Jig accepts the root Run. Project acquisition happens before it;
106
- mandatory fencing and cleanup may finish afterward.
107
-
108
- `jig review` shows the complete proposed project change, including exact
109
- current and proposed Package/1 content digests, and asks for approval. It is
110
- not a source-file diff; inspect editable source with your normal tools. When
111
- standard input and output are both terminals, Jig prompts for approval.
112
- Otherwise it prints the review and exits with `JIG_APPROVAL_REQUIRED`; `--yes`
113
- records an already explicit approval. The CLI keeps review and admission
114
- mechanics internal.
115
-
116
- A target is marked changed when its package identity or exact execution
117
- evidence changes. Package digests appear once in the package section;
118
- host-specific evidence remains private.
119
-
120
- For ordinary dependencies, place `package.json` and text `bun.lock` beside
121
- `flow.ts`; do not include `node_modules`. `bun install --lockfile-only` is the
122
- author-side lock-generation step: it creates no project-local `node_modules`,
123
- but may use the network and Bun's author-side cache. On the first `jig review`
124
- after those inputs change, Jig fetches the frozen production artifacts and
125
- materializes a private execution snapshot in a contained trusted preparation
126
- process, using the fixed Bun runtime, the default npm registry, and no
127
- lifecycle scripts. It reuses the Run containment and ownership mechanism but
128
- deliberately has registry network access. A declined review may therefore have
129
- materialized inert evidence without granting authority. Unsupported or
130
- unlocked sources fail before admission.
131
-
132
- Unreleased code can remain ordinary readable files inside the Flow package and
133
- be imported relatively. Code shared from elsewhere must be copied or bundled
134
- into the finished package before `jig review`; Jig does not follow symlinks or
135
- resolve `file:`, `workspace:`, or Git dependencies and does not own that build
136
- step.
137
-
138
- A dependency-free package needs no `bun.lock`; omit it rather than preserving
139
- an empty or stale lock. Optional package schemas are FLOW Schema/1 files and
140
- must begin with the exact root declaration
141
- `"$schema": "https://flow.jig.md/schemas/schema-1.json"`.
24
+ Jig has three project commands:
142
25
 
143
- The admitted Run uses only the retained prepared bytes. It has no network,
144
- ambient `PATH`, package installation, or lifecycle scripts. Packages with no
145
- runtime dependencies continue to run directly, and already bundled
146
- package-local code remains valid. An unchanged `jig review` reuses the admitted
147
- prepared tree only while its source and host evidence still match.
148
-
149
- Bindings use an explicit target. A declaration may also give its package exact
150
- child-Flow slots:
151
-
152
- ```ts
153
- import { defineBinding } from "@jigging/jig";
154
-
155
- export default defineBinding({
156
- package: "./flows/reviewer",
157
- slots: { research: "flow:flows/research" },
158
- });
159
- ```
160
-
161
- ```console
162
- jig run binding:reviewer --input '{}'
26
+ ```text
27
+ jig init --bare <directory>
28
+ jig review [project] [--allow-resolution-network] [--yes]
29
+ jig run <flow:path|binding:id> [options]
163
30
  ```
164
31
 
165
- `slots` is optional and omission normalizes to `{}`. It maps at most 256
166
- LocalName keys to explicit `flow:<path>` or `binding:<id>` targets in the same
167
- admitted generation. Slots belong to the Binding; a direct `flow:` Run has none.
168
- Selected child Bindings must have no child slots of their own.
169
-
170
- Each `flow/call` carries JSON/1 input into a fresh child context and returns a
171
- complete JSON/1 Run result. The child receives its selected target's admitted
172
- settings and Agent capability, empty attachments, no inherited slots, and a
173
- deadline no later than its parent. It selects its own package-local Skills per
174
- Agent call; providers and credentials remain host choices. Run/1 controls
175
- operation identity, duplicate joins, conflicting reuse, cancellation, and
176
- uncertainty; Jig does not replay uncertain dispatch automatically. There is no
177
- separate child history, administration, scheduler, catalogue, or resolver. Jig
178
- also never guesses an unprefixed target.
179
-
180
- Each Run context admits one active operation; a child Flow can own one Agent
181
- operation while its parent awaits it. Excess distinct concurrent calls receive
182
- `RESOURCE_EXHAUSTED`, while sequential calls remain available. Cancellation and
183
- cleanup cover the specialist and its locally owned Agent worker.
32
+ `review` asks you to approve the proposed project changes. `run` executes the
33
+ admitted revision and returns JSON after settling owned work. Use
34
+ `jig --version` to inspect the installed version.
184
35
 
185
- One experimental [Agent Run capability](https://jig.md/spec/agent-run) is
186
- available through ordinary Run/1 `effect/call`. The host may use the official
187
- OpenAI JavaScript SDK against an operator-selected OpenAI-compatible endpoint,
188
- or run native Codex, Claude Code, or Pi through one private ACP mechanism.
189
- Direct API configuration uses `OPENAI_API_KEY` and `OPENAI_MODEL`; optional
190
- `OPENAI_BASE_URL` and `OPENAI_API` select the HTTPS endpoint and either the
191
- `responses` (default) or `chat-completions` wire shape. Jig supplies no default
192
- model. Client, API, endpoint, model, executable path, and credentials are
193
- trusted host configuration, not FLOW or Binding inputs, and Jig exposes no
194
- provider registry.
36
+ ## Guides
195
37
 
196
- The complete runnable example and current specification map are in the
197
- [repository README](https://github.com/jiggy/jig#readme) and
198
- [`docs/jig/guide/index.md`](https://github.com/jiggy/jig/blob/main/docs/jig/guide/index.md).
38
+ - [Choose an Agent](https://jig.md/guide/agents)
39
+ - [Working with files](https://jig.md/guide/files)
40
+ - [Dependencies](https://jig.md/guide/dependencies)
41
+ - [Workflow design](https://jig.md/guide/workflow-design)
42
+ - [Project authoring](https://jig.md/spec/project-sdk)
43
+ - [Execution policy](https://jig.md/spec/project-policy)
44
+ - [Security boundary](https://github.com/jiggy/jig/blob/main/SECURITY.md)
199
45
 
200
- This is a prerelease alpha. It does not expose Services, Hooks, event sources,
201
- a provider registry or package-selected provider configuration, Agent sessions,
202
- Semantic Choice, Jig Graph, schedulers, catalogues, runtime registries, or
203
- sandbox registries.
46
+ Jig is prerelease software. [FLOW](https://flow.jig.md/) remains an independent
47
+ standard for portable methods.
204
48
 
205
49
  Copyright © 2026 Victor Duarte <zvictor> and contributors.
package/dist/index.js CHANGED
@@ -105,6 +105,43 @@ function validateString(value, byteLimit) {
105
105
  }
106
106
  }
107
107
 
108
+ // src/project/commands.ts
109
+ function projectCommandPath(value) {
110
+ if (typeof value !== "string" || value.length > 256 || !/^[A-Za-z0-9_][A-Za-z0-9_./-]*$/.test(value) || value.split("/").some((part) => part === "" || part === "." || part === "..") || value.split("/").length > 16)
111
+ throw new TypeError("command file paths must be bounded relative paths without traversal");
112
+ return value;
113
+ }
114
+ function normalizeProjectCommands(value) {
115
+ if (value === null || typeof value !== "object" || Array.isArray(value))
116
+ throw new TypeError("commands must be an object");
117
+ const entries = Object.entries(value);
118
+ if (entries.length > 8)
119
+ throw new TypeError("commands exceed eight entries");
120
+ const output = Object.create(null);
121
+ for (const [name, item] of entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
122
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) || name.length > 64)
123
+ throw new TypeError("command name must be a LocalName");
124
+ if (item === null || typeof item !== "object" || Array.isArray(item) || Object.keys(item).length !== 1)
125
+ throw new TypeError(`command ${name} must contain run or test`);
126
+ const command = item;
127
+ if (Object.hasOwn(command, "run")) {
128
+ const path = projectCommandPath(command.run);
129
+ if (!path.endsWith(".ts") && !path.endsWith(".js"))
130
+ throw new TypeError("command run must name a .ts or .js entrypoint");
131
+ output[name] = Object.freeze({ run: path });
132
+ } else if (Object.hasOwn(command, "test") && Array.isArray(command.test)) {
133
+ if (command.test.length === 0 || command.test.length > 16)
134
+ throw new TypeError("command test must select one to sixteen test files");
135
+ const paths = command.test.map(projectCommandPath);
136
+ if (new Set(paths).size !== paths.length || paths.some((path) => !/\.(test|spec)\.(ts|js)$/.test(path)))
137
+ throw new TypeError("command test must name distinct .test or .spec files");
138
+ output[name] = Object.freeze({ test: Object.freeze(paths) });
139
+ } else
140
+ throw new TypeError(`command ${name} must contain run or test`);
141
+ }
142
+ return Object.freeze(output);
143
+ }
144
+
108
145
  // src/package/case-fold-15.1.ts
109
146
  var CASE_FOLD_15_1 = new Map([
110
147
  [65, "a"],
@@ -1797,7 +1834,7 @@ function bindingRef(id) {
1797
1834
  }
1798
1835
  function normalizeBinding(input, canonical) {
1799
1836
  const captured = snapshotJsonObject(input, "Binding definition");
1800
- assertClosedObject(captured, canonical ? ["kind", "package", "settings", "slots"] : ["package", "settings", "slots"], "Binding definition");
1837
+ assertClosedObject(captured, canonical ? ["kind", "package", "settings", "slots", "commands"] : ["package", "settings", "slots", "commands"], "Binding definition");
1801
1838
  if (canonical && captured.kind !== "package") {
1802
1839
  throw new TypeError("Binding kind must be package");
1803
1840
  }
@@ -1806,11 +1843,13 @@ function normalizeBinding(input, canonical) {
1806
1843
  const packagePath = normalizeProjectPath(captured.package, "package");
1807
1844
  const settings = Object.hasOwn(captured, "settings") ? expectJsonObject(captured.settings, "settings") : emptyRecord();
1808
1845
  const slots = Object.hasOwn(captured, "slots") ? normalizeFlowSlots(captured.slots) : emptyRecord();
1846
+ const commands = normalizeProjectCommands(Object.hasOwn(captured, "commands") ? captured.commands : {});
1809
1847
  return record({
1810
1848
  kind: "package",
1811
1849
  package: packagePath,
1812
1850
  settings,
1813
- slots
1851
+ slots,
1852
+ ...Object.keys(commands).length === 0 ? {} : { commands }
1814
1853
  });
1815
1854
  }
1816
1855
  function normalizeFlowSlots(value) {
@@ -1,4 +1,5 @@
1
1
  import type { JsonObject } from '../json.js';
2
+ import { type ProjectCommands } from './commands.js';
2
3
  export interface DiscoverySource {
3
4
  readonly kind: 'discover';
4
5
  readonly roots: readonly string[];
@@ -31,11 +32,13 @@ export interface PackageBindingDefinition {
31
32
  readonly package: string;
32
33
  readonly settings: JsonObject;
33
34
  readonly slots: Readonly<Record<string, string>>;
35
+ readonly commands?: ProjectCommands;
34
36
  }
35
37
  export interface PackageBindingInput {
36
38
  readonly package: string;
37
39
  readonly settings?: JsonObject;
38
40
  readonly slots?: Readonly<Record<string, string>>;
41
+ readonly commands?: ProjectCommands;
39
42
  }
40
43
  export type BindingDefinition = PackageBindingDefinition;
41
44
  export declare function discover(roots: string | readonly string[]): DiscoverySource;
@@ -0,0 +1,10 @@
1
+ /** Reviewed invocation policy; runtime and containment remain host choices. */
2
+ export type ProjectCommand = {
3
+ readonly run: string;
4
+ } | {
5
+ readonly test: readonly string[];
6
+ };
7
+ export type ProjectCommands = Readonly<Record<string, ProjectCommand>>;
8
+ export declare function projectCommandPath(value: unknown): string;
9
+ /** Input must already be an ordinary JSON snapshot at the owning boundary. */
10
+ export declare function normalizeProjectCommands(value: unknown): ProjectCommands;
@@ -102,6 +102,9 @@
102
102
  "$ref": "#/$defs/targetSelector"
103
103
  },
104
104
  "maxProperties": 256
105
+ },
106
+ "commands": {
107
+ "$ref": "#/$defs/commands"
105
108
  }
106
109
  },
107
110
  "required": ["kind", "package", "settings", "slots"],
@@ -109,6 +112,43 @@
109
112
  },
110
113
  "bindingDefinition": {
111
114
  "$ref": "#/$defs/packageBinding"
115
+ },
116
+ "commands": {
117
+ "type": "object",
118
+ "maxProperties": 8,
119
+ "additionalProperties": {
120
+ "oneOf": [
121
+ {
122
+ "type": "object",
123
+ "properties": {
124
+ "run": {
125
+ "type": "string",
126
+ "minLength": 1,
127
+ "maxLength": 256
128
+ }
129
+ },
130
+ "required": ["run"],
131
+ "additionalProperties": false
132
+ },
133
+ {
134
+ "type": "object",
135
+ "properties": {
136
+ "test": {
137
+ "type": "array",
138
+ "minItems": 1,
139
+ "maxItems": 16,
140
+ "items": {
141
+ "type": "string",
142
+ "minLength": 1,
143
+ "maxLength": 256
144
+ }
145
+ }
146
+ },
147
+ "required": ["test"],
148
+ "additionalProperties": false
149
+ }
150
+ ]
151
+ }
112
152
  }
113
153
  }
114
154
  }
@@ -105,6 +105,43 @@ function validateString(value, byteLimit) {
105
105
  }
106
106
  }
107
107
 
108
+ // src/project/commands.ts
109
+ function projectCommandPath(value) {
110
+ if (typeof value !== "string" || value.length > 256 || !/^[A-Za-z0-9_][A-Za-z0-9_./-]*$/.test(value) || value.split("/").some((part) => part === "" || part === "." || part === "..") || value.split("/").length > 16)
111
+ throw new TypeError("command file paths must be bounded relative paths without traversal");
112
+ return value;
113
+ }
114
+ function normalizeProjectCommands(value) {
115
+ if (value === null || typeof value !== "object" || Array.isArray(value))
116
+ throw new TypeError("commands must be an object");
117
+ const entries = Object.entries(value);
118
+ if (entries.length > 8)
119
+ throw new TypeError("commands exceed eight entries");
120
+ const output = Object.create(null);
121
+ for (const [name, item] of entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
122
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) || name.length > 64)
123
+ throw new TypeError("command name must be a LocalName");
124
+ if (item === null || typeof item !== "object" || Array.isArray(item) || Object.keys(item).length !== 1)
125
+ throw new TypeError(`command ${name} must contain run or test`);
126
+ const command = item;
127
+ if (Object.hasOwn(command, "run")) {
128
+ const path = projectCommandPath(command.run);
129
+ if (!path.endsWith(".ts") && !path.endsWith(".js"))
130
+ throw new TypeError("command run must name a .ts or .js entrypoint");
131
+ output[name] = Object.freeze({ run: path });
132
+ } else if (Object.hasOwn(command, "test") && Array.isArray(command.test)) {
133
+ if (command.test.length === 0 || command.test.length > 16)
134
+ throw new TypeError("command test must select one to sixteen test files");
135
+ const paths = command.test.map(projectCommandPath);
136
+ if (new Set(paths).size !== paths.length || paths.some((path) => !/\.(test|spec)\.(ts|js)$/.test(path)))
137
+ throw new TypeError("command test must name distinct .test or .spec files");
138
+ output[name] = Object.freeze({ test: Object.freeze(paths) });
139
+ } else
140
+ throw new TypeError(`command ${name} must contain run or test`);
141
+ }
142
+ return Object.freeze(output);
143
+ }
144
+
108
145
  // src/package/case-fold-15.1.ts
109
146
  var CASE_FOLD_15_1 = new Map([
110
147
  [65, "a"],
@@ -1797,7 +1834,7 @@ function bindingRef(id) {
1797
1834
  }
1798
1835
  function normalizeBinding(input, canonical) {
1799
1836
  const captured = snapshotJsonObject(input, "Binding definition");
1800
- assertClosedObject(captured, canonical ? ["kind", "package", "settings", "slots"] : ["package", "settings", "slots"], "Binding definition");
1837
+ assertClosedObject(captured, canonical ? ["kind", "package", "settings", "slots", "commands"] : ["package", "settings", "slots", "commands"], "Binding definition");
1801
1838
  if (canonical && captured.kind !== "package") {
1802
1839
  throw new TypeError("Binding kind must be package");
1803
1840
  }
@@ -1806,11 +1843,13 @@ function normalizeBinding(input, canonical) {
1806
1843
  const packagePath = normalizeProjectPath(captured.package, "package");
1807
1844
  const settings = Object.hasOwn(captured, "settings") ? expectJsonObject(captured.settings, "settings") : emptyRecord();
1808
1845
  const slots = Object.hasOwn(captured, "slots") ? normalizeFlowSlots(captured.slots) : emptyRecord();
1846
+ const commands = normalizeProjectCommands(Object.hasOwn(captured, "commands") ? captured.commands : {});
1809
1847
  return record({
1810
1848
  kind: "package",
1811
1849
  package: packagePath,
1812
1850
  settings,
1813
- slots
1851
+ slots,
1852
+ ...Object.keys(commands).length === 0 ? {} : { commands }
1814
1853
  });
1815
1854
  }
1816
1855
  function normalizeFlowSlots(value) {