@polydeukes/sdk-ts 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hyung Soo Seung
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.ko.md ADDED
@@ -0,0 +1,61 @@
1
+ # `@polydeukes/sdk-ts`
2
+
3
+ [English](./README.md) · **한국어**
4
+
5
+ 이 패키지는 TypeScript에서 약속(covenant) 입력 IR을 판정기에 건넵니다. 판정받는 프로젝트의
6
+ `polydeukes` 설치를 찾고, 입력을 표준 입력에 넣어 `pdks covenant check`를 스폰하며, 판정
7
+ 결과를 값으로 돌려줍니다. 판정 코드는 여기에 없고 텔레메트리 행도 여기서 쓰지 않습니다. 행은
8
+ 자식 프로세스가 씁니다.
9
+
10
+ `polydeukes` · `@polydeukes/core`와 함께 설치합니다. 둘 다 이 패키지의
11
+ `peerDependencies`입니다.
12
+
13
+ ```sh
14
+ pnpm add @polydeukes/sdk-ts polydeukes @polydeukes/core
15
+ ```
16
+
17
+ 실행 파일도 설치 단계도 없습니다.
18
+
19
+ <a id="overview"></a>
20
+ ## 개요
21
+
22
+ 공개 계약 심볼은 다음과 같습니다.
23
+
24
+ - `checkCovenant`
25
+ - `CheckCovenantSpec`
26
+ - `CheckCovenantSpawnSpec`
27
+ - `CheckCovenantVerdict`
28
+
29
+ <a id="examples"></a>
30
+ ## 예제
31
+
32
+ ```ts
33
+ import { checkCovenant } from '@polydeukes/sdk-ts';
34
+
35
+ // repoRoot에서 IR을 표준 입력에 넣어 `pdks covenant check --enforce block`을 스폰합니다.
36
+ // IR은 호출자의 것이며 이 패키지는 거기에 아무것도 더하지 않습니다.
37
+ const verdict = await checkCovenant({
38
+ repoRoot: process.cwd(),
39
+ input: {
40
+ toolCalls: [{ name: 'writeFile', args: { path: 'src/index.ts' } }],
41
+ subagentSpawns: [],
42
+ userMessages: [],
43
+ tools: { mutating: ['writeFile', 'rm'], shell: ['exec'], commandArgs: ['command'] },
44
+ },
45
+ });
46
+
47
+ if (verdict.verdict === 'blocked') {
48
+ // `reason`은 판정기 자신의 stderr입니다. 그 텍스트를 어디에 둘지는 호출자가 정합니다.
49
+ console.error(verdict.reason);
50
+ }
51
+ ```
52
+
53
+ `enforce`의 기본값은 `block`입니다. 판정 결과는 `upheld` · `blocked` · `unjudged` 셋이고,
54
+ `unjudged`는 `polydeukes`가 설치되지 않은 프로젝트와 판정 결과가 아닌 모든 자식 상태를
55
+ 포괄합니다.
56
+
57
+ <a id="see-also"></a>
58
+ ## 같이 보기
59
+
60
+ - [`@polydeukes/sdk-ts` 패키지 레퍼런스](../../docs/reference/packages/sdk-ts.ko.md)
61
+ - [판정기(`covenant` 모듈)](../../docs/reference/packages/polydeukes.ko.md#covenant-module)
package/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # `@polydeukes/sdk-ts`
2
+
3
+ **English** · [한국어](./README.ko.md)
4
+
5
+ This package hands a covenant input IR to the judge from TypeScript. It locates the
6
+ `polydeukes` install of the project being judged, spawns `pdks covenant check` with the input
7
+ on stdin, and returns the verdict as a value. No judgment logic lives here, and no telemetry
8
+ row is written here — the child process writes it.
9
+
10
+ Install it next to `polydeukes` and `@polydeukes/core`, which it names as `peerDependencies`:
11
+
12
+ ```sh
13
+ pnpm add @polydeukes/sdk-ts polydeukes @polydeukes/core
14
+ ```
15
+
16
+ There is no bin and no install step.
17
+
18
+ <a id="overview"></a>
19
+ ## Overview
20
+
21
+ Public contract symbols include:
22
+
23
+ - `checkCovenant`
24
+ - `CheckCovenantSpec`
25
+ - `CheckCovenantSpawnSpec`
26
+ - `CheckCovenantVerdict`
27
+
28
+ <a id="examples"></a>
29
+ ## Examples
30
+
31
+ ```ts
32
+ import { checkCovenant } from '@polydeukes/sdk-ts';
33
+
34
+ // Spawns `pdks covenant check --enforce block` in repoRoot with the IR on stdin.
35
+ // The IR is the caller's: this package adds nothing to it.
36
+ const verdict = await checkCovenant({
37
+ repoRoot: process.cwd(),
38
+ input: {
39
+ toolCalls: [{ name: 'writeFile', args: { path: 'src/index.ts' } }],
40
+ subagentSpawns: [],
41
+ userMessages: [],
42
+ tools: { mutating: ['writeFile', 'rm'], shell: ['exec'], commandArgs: ['command'] },
43
+ },
44
+ });
45
+
46
+ if (verdict.verdict === 'blocked') {
47
+ // `reason` is the judge's own stderr — the caller decides where it goes.
48
+ console.error(verdict.reason);
49
+ }
50
+ ```
51
+
52
+ `enforce` defaults to `block`. The three verdicts are `upheld`, `blocked`, and `unjudged`;
53
+ `unjudged` covers a project with no `polydeukes` installed and any child status that is not a
54
+ verdict.
55
+
56
+ <a id="see-also"></a>
57
+ ## See also
58
+
59
+ - [`@polydeukes/sdk-ts` package reference](../../docs/reference/packages/sdk-ts.md)
60
+ - [The judge (`covenant` module)](../../docs/reference/packages/polydeukes.md#covenant-module)
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `checkCovenant` — hand one covenant input to `pdks covenant check` and read the verdict.
3
+ *
4
+ * The whole package: locate the umbrella in the caller's install graph, spawn its bin with
5
+ * the input on stdin, and turn the child's exit status and stderr into a value. Nothing
6
+ * here judges, records a telemetry row, or adds to the input — the branches are whether
7
+ * the umbrella resolved and what status the child left with.
8
+ */
9
+ import type { CovenantInput } from '@polydeukes/core';
10
+ /** What the spawn seam is handed: the executable, its arguments, its cwd, and its stdin. */
11
+ export type CheckCovenantSpawnSpec = {
12
+ command: string;
13
+ args: string[];
14
+ cwd: string;
15
+ stdin: string;
16
+ };
17
+ /** `checkCovenant` input. */
18
+ export type CheckCovenantSpec = {
19
+ /** The project being judged — config discovery, the child's cwd, and the install graph. */
20
+ repoRoot: string;
21
+ /** The caller's own input. The runner refuses one carrying a `world` key. */
22
+ input: CovenantInput;
23
+ /** The observer's posture for the run. ABSENT is `block`. */
24
+ enforce?: 'advise' | 'block';
25
+ /** Injected spawn seam — absent, the child runs under this process's node executable. */
26
+ spawn?: (spec: CheckCovenantSpawnSpec) => Promise<{
27
+ status: number | null;
28
+ stderr: string;
29
+ }>;
30
+ };
31
+ /**
32
+ * What the judge answered.
33
+ *
34
+ * `advisories` and `reason` are the child's stderr verbatim: an unattended caller has no
35
+ * terminal to read it on, so the text comes back as the value and the caller decides where
36
+ * it goes. `unjudged` is every status that is not a verdict — no judgment happened, and
37
+ * reading it as an uphold would let an uninstalled judge pass every call.
38
+ */
39
+ export type CheckCovenantVerdict = {
40
+ verdict: 'upheld';
41
+ advisories: string;
42
+ } | {
43
+ verdict: 'blocked';
44
+ reason: string;
45
+ } | {
46
+ verdict: 'unjudged';
47
+ reason: string;
48
+ };
49
+ /**
50
+ * Judge one input against the covenants of `repoRoot` and return the verdict as a value.
51
+ *
52
+ * The input travels verbatim; this package neither reads nor completes it.
53
+ */
54
+ export declare function checkCovenant(spec: CheckCovenantSpec): Promise<CheckCovenantVerdict>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `checkCovenant` — hand one covenant input to `pdks covenant check` and read the verdict.
3
+ *
4
+ * The whole package: locate the umbrella in the caller's install graph, spawn its bin with
5
+ * the input on stdin, and turn the child's exit status and stderr into a value. Nothing
6
+ * here judges, records a telemetry row, or adds to the input — the branches are whether
7
+ * the umbrella resolved and what status the child left with.
8
+ */
9
+ import { spawn as spawnChild } from 'node:child_process';
10
+ import { findUmbrellaBin, UMBRELLA_PACKAGE } from './resolve-umbrella.js';
11
+ /** The judge's own subcommand — the caller's input goes to its stdin. */
12
+ const CHECK_ARGS = ['covenant', 'check', '--enforce'];
13
+ /**
14
+ * Run the bin under this process's node executable, collect stderr, and discard stdout.
15
+ *
16
+ * No file descriptor is inherited: a caller may hold none of its own, and an inherited
17
+ * stdout that is closed kills the child with EPIPE before it can answer.
18
+ */
19
+ function defaultSpawn(spec) {
20
+ return new Promise((resolve, reject) => {
21
+ const child = spawnChild(spec.command, spec.args, {
22
+ cwd: spec.cwd,
23
+ stdio: ['pipe', 'pipe', 'pipe'],
24
+ });
25
+ let stderr = '';
26
+ child.stderr.setEncoding('utf-8');
27
+ child.stderr.on('data', (chunk) => {
28
+ stderr += chunk;
29
+ });
30
+ // Drained and dropped: the judge writes no verdict to stdout, and an unread pipe
31
+ // fills and stalls the child.
32
+ child.stdout.resume();
33
+ child.on('error', reject);
34
+ child.on('close', (status) => {
35
+ resolve({ status, stderr });
36
+ });
37
+ // A child that exits before draining stdin raises EPIPE on this stream; the exit status
38
+ // is the answer, and an unheard stream error would end the caller's process instead.
39
+ child.stdin.on('error', () => { });
40
+ child.stdin.end(spec.stdin);
41
+ });
42
+ }
43
+ /**
44
+ * Judge one input against the covenants of `repoRoot` and return the verdict as a value.
45
+ *
46
+ * The input travels verbatim; this package neither reads nor completes it.
47
+ */
48
+ export async function checkCovenant(spec) {
49
+ const bin = findUmbrellaBin(spec.repoRoot);
50
+ if (bin === undefined) {
51
+ return {
52
+ verdict: 'unjudged',
53
+ reason: `no ${UMBRELLA_PACKAGE} in the install graph of ${spec.repoRoot}: install it to have this input judged`,
54
+ };
55
+ }
56
+ const spawn = spec.spawn ?? defaultSpawn;
57
+ let status;
58
+ let stderr;
59
+ try {
60
+ ({ status, stderr } = await spawn({
61
+ command: process.execPath,
62
+ args: [bin, ...CHECK_ARGS, spec.enforce ?? 'block'],
63
+ cwd: spec.repoRoot,
64
+ stdin: JSON.stringify(spec.input),
65
+ }));
66
+ }
67
+ catch (error) {
68
+ // No process ran, so no verdict and no row: the failure comes back as the value the
69
+ // caller was promised rather than as an exception it did not sign up for.
70
+ return {
71
+ verdict: 'unjudged',
72
+ reason: `the judge could not be spawned: ${error instanceof Error ? error.message : String(error)}`,
73
+ };
74
+ }
75
+ if (status === 0)
76
+ return { verdict: 'upheld', advisories: stderr };
77
+ if (status === 2)
78
+ return { verdict: 'blocked', reason: stderr };
79
+ return {
80
+ verdict: 'unjudged',
81
+ reason: status === null
82
+ ? 'the judge was killed by a signal before it answered'
83
+ : `the judge exited with status ${status} instead of a verdict`,
84
+ };
85
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @polydeukes/sdk-ts — hand a covenant input to `pdks covenant check` from TypeScript.
3
+ *
4
+ * Alpha. One verb: it locates the `polydeukes` install of the project being judged, spawns
5
+ * its bin, and returns the verdict as a value. No judgment logic lives here.
6
+ * See https://github.com/huskyhoochu/polydeukes
7
+ */
8
+ export { type CheckCovenantSpawnSpec, type CheckCovenantSpec, type CheckCovenantVerdict, checkCovenant, } from './check-covenant.ts';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @polydeukes/sdk-ts — hand a covenant input to `pdks covenant check` from TypeScript.
3
+ *
4
+ * Alpha. One verb: it locates the `polydeukes` install of the project being judged, spawns
5
+ * its bin, and returns the verdict as a value. No judgment logic lives here.
6
+ * See https://github.com/huskyhoochu/polydeukes
7
+ */
8
+ export { checkCovenant, } from './check-covenant.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Locating the `polydeukes` install this SDK spawns the judge from.
3
+ *
4
+ * The anchor is the project root the caller named, never this module's own location:
5
+ * anchoring here would answer for the graph THIS package was installed into, which is a
6
+ * different tree from the one being judged.
7
+ */
8
+ /** The umbrella package the spawn looks for. */
9
+ export declare const UMBRELLA_PACKAGE = "polydeukes";
10
+ /**
11
+ * The absolute path of the umbrella's `pdks` bin as reachable from `projectRoot`, or
12
+ * `undefined` when there is nothing to spawn.
13
+ *
14
+ * `findPackageJSON` is experimental in Node 24, so its behaviour can still change.
15
+ *
16
+ * A manifest found but carrying no `bin.pdks` answers `undefined` too: spawning `node
17
+ * undefined` crashes with no verdict and no row, which reads to the caller as the same
18
+ * absence reached without saying so.
19
+ */
20
+ export declare function findUmbrellaBin(projectRoot: string): string | undefined;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Locating the `polydeukes` install this SDK spawns the judge from.
3
+ *
4
+ * The anchor is the project root the caller named, never this module's own location:
5
+ * anchoring here would answer for the graph THIS package was installed into, which is a
6
+ * different tree from the one being judged.
7
+ */
8
+ import { readFileSync } from 'node:fs';
9
+ import { findPackageJSON } from 'node:module';
10
+ import { join } from 'node:path';
11
+ import { isPlainObject } from '@polydeukes/core';
12
+ /** The umbrella package the spawn looks for. */
13
+ export const UMBRELLA_PACKAGE = 'polydeukes';
14
+ /**
15
+ * The absolute path of the umbrella's `pdks` bin as reachable from `projectRoot`, or
16
+ * `undefined` when there is nothing to spawn.
17
+ *
18
+ * `findPackageJSON` is experimental in Node 24, so its behaviour can still change.
19
+ *
20
+ * A manifest found but carrying no `bin.pdks` answers `undefined` too: spawning `node
21
+ * undefined` crashes with no verdict and no row, which reads to the caller as the same
22
+ * absence reached without saying so.
23
+ */
24
+ export function findUmbrellaBin(projectRoot) {
25
+ let manifestPath;
26
+ try {
27
+ // Absence throws here rather than returning undefined (Node 24.18); the branch below
28
+ // covers the documented `string | undefined` return.
29
+ manifestPath = findPackageJSON(UMBRELLA_PACKAGE, join(projectRoot, 'package.json'));
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ if (manifestPath === undefined)
35
+ return undefined;
36
+ let manifest;
37
+ try {
38
+ manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ const bin = isPlainObject(manifest) ? manifest.bin : undefined;
44
+ const pdks = isPlainObject(bin) ? bin.pdks : undefined;
45
+ if (typeof pdks !== 'string')
46
+ return undefined;
47
+ return join(manifestPath, '..', pdks);
48
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@polydeukes/sdk-ts",
3
+ "version": "0.7.1",
4
+ "description": "Polydeukes SDK for TypeScript — one verb that hands a covenant input IR to `pdks covenant check` and returns the verdict as a value. Alpha.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/huskyhoochu/polydeukes.git",
9
+ "directory": "packages/sdk-ts"
10
+ },
11
+ "type": "module",
12
+ "main": "./dist/index.js",
13
+ "module": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=24"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^24.0.0",
33
+ "typescript": "7.0.2",
34
+ "vitest": "^5.0.0",
35
+ "@polydeukes/core": "^0.7.1"
36
+ },
37
+ "peerDependencies": {
38
+ "@polydeukes/core": "^0.7.1",
39
+ "polydeukes": "^0.7.1"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.build.json",
43
+ "typecheck": "tsc --noEmit",
44
+ "test": "vitest run"
45
+ }
46
+ }