polydeukes 0.5.0 → 0.6.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.
Files changed (79) hide show
  1. package/README.ko.md +54 -74
  2. package/README.md +55 -86
  3. package/dist/baseline.d.ts +82 -0
  4. package/dist/baseline.js +166 -0
  5. package/dist/bin.d.ts +1 -1
  6. package/dist/bin.js +26 -6
  7. package/dist/claude-code-hook.d.ts +7 -5
  8. package/dist/claude-code-hook.js +69 -17
  9. package/dist/claude-code.d.ts +6 -0
  10. package/dist/claude-code.js +6 -0
  11. package/dist/covenant-check.d.ts +10 -17
  12. package/dist/covenant-check.js +44 -12
  13. package/dist/covenant-module.d.ts +2 -2
  14. package/dist/covenant-module.js +9 -1
  15. package/dist/docs/README.ko.md +60 -0
  16. package/dist/docs/README.md +64 -0
  17. package/dist/docs/catalog.json +464 -0
  18. package/dist/docs/concepts/judgment.ko.md +113 -0
  19. package/dist/docs/concepts/judgment.md +113 -0
  20. package/dist/docs/how-to/configure-project.ko.md +99 -0
  21. package/dist/docs/how-to/configure-project.md +95 -0
  22. package/dist/docs/how-to/connect-surfaces.ko.md +115 -0
  23. package/dist/docs/how-to/connect-surfaces.md +118 -0
  24. package/dist/docs/how-to/write-disciplines.ko.md +124 -0
  25. package/dist/docs/how-to/write-disciplines.md +125 -0
  26. package/dist/docs/index.json +2046 -0
  27. package/dist/docs/reference/cli/covenant-check.ko.md +101 -0
  28. package/dist/docs/reference/cli/covenant-check.md +98 -0
  29. package/dist/docs/reference/cli/docs.ko.md +97 -0
  30. package/dist/docs/reference/cli/docs.md +95 -0
  31. package/dist/docs/reference/cli/explain.ko.md +79 -0
  32. package/dist/docs/reference/cli/explain.md +84 -0
  33. package/dist/docs/reference/cli/init.ko.md +119 -0
  34. package/dist/docs/reference/cli/init.md +131 -0
  35. package/dist/docs/reference/configuration/index.ko.md +448 -0
  36. package/dist/docs/reference/configuration/index.md +474 -0
  37. package/dist/docs/reference/packages/adapter-claude-code.ko.md +83 -0
  38. package/dist/docs/reference/{adapter-claude-code.md → packages/adapter-claude-code.md} +16 -10
  39. package/dist/docs/reference/packages/adapter-git.ko.md +101 -0
  40. package/dist/docs/reference/{adapter-git.md → packages/adapter-git.md} +25 -15
  41. package/dist/docs/reference/packages/core.ko.md +128 -0
  42. package/dist/docs/reference/{core.md → packages/core.md} +32 -13
  43. package/dist/docs/reference/packages/covenant.ko.md +115 -0
  44. package/dist/docs/reference/{covenant.md → packages/covenant.md} +41 -26
  45. package/dist/docs/reference/packages/polydeukes.ko.md +134 -0
  46. package/dist/docs/reference/packages/polydeukes.md +139 -0
  47. package/dist/docs/troubleshooting.ko.md +142 -0
  48. package/dist/docs/troubleshooting.md +97 -122
  49. package/dist/docs/tutorials/first-judgment.ko.md +82 -0
  50. package/dist/docs/tutorials/first-judgment.md +81 -0
  51. package/dist/docs-catalog.d.ts +25 -0
  52. package/dist/docs-catalog.js +450 -0
  53. package/dist/docs-library.d.ts +23 -0
  54. package/dist/docs-library.js +347 -0
  55. package/dist/docs-markdown.d.ts +32 -0
  56. package/dist/docs-markdown.js +150 -0
  57. package/dist/docs-query.d.ts +11 -40
  58. package/dist/docs-query.js +28 -122
  59. package/dist/docs-types.d.ts +105 -0
  60. package/dist/docs-types.js +2 -0
  61. package/dist/explain.d.ts +3 -5
  62. package/dist/explain.js +48 -47
  63. package/dist/index.d.ts +2 -3
  64. package/dist/index.js +1 -2
  65. package/dist/init-claude-code.d.ts +5 -3
  66. package/dist/init-claude-code.js +226 -63
  67. package/dist/init-grok.d.ts +51 -0
  68. package/dist/init-grok.js +242 -0
  69. package/dist/load-config.d.ts +5 -1
  70. package/dist/load-config.js +2 -1
  71. package/dist/pre-state-reader.d.ts +22 -0
  72. package/dist/pre-state-reader.js +32 -0
  73. package/dist/scaffold-project.js +48 -8
  74. package/dist/schema/polydeukes.schema.json +38 -91
  75. package/package.json +7 -7
  76. package/dist/docs/configuration.md +0 -103
  77. package/dist/docs/installation.md +0 -212
  78. package/dist/docs/reference/configuration.md +0 -338
  79. package/dist/docs/reference/polydeukes.md +0 -287
@@ -26,11 +26,13 @@
26
26
  import { mkdirSync, readFileSync } from 'node:fs';
27
27
  import { homedir } from 'node:os';
28
28
  import { join, resolve } from 'node:path';
29
- import { COMMAND_ARGS, evaluatePrecedent, MUTATING_TOOLS, runAdapterPath, SHELL_TOOLS, transcriptFromJsonlFile, transcriptPathFromPayload, } from '@polydeukes/adapter-claude-code';
30
- import { appendRecordFailOpen, DEFAULT_TELEMETRY_LOG_PATH, normalizeProtectedPaths, readRecords, } from '@polydeukes/core';
31
- import { findUnattributed, readBaseline, snapshotBaseline, ttlWitness, writeBaseline, } from '@polydeukes/covenant';
29
+ import { COMMAND_ARGS, MUTATING_TOOLS, runAdapterPath, SHELL_TOOLS, sessionChannelReader, sessionSourceReader, transcriptFromJsonlFile, transcriptPathFromPayload, } from '@polydeukes/adapter-claude-code';
30
+ import { appendRecordFailOpen, DEFAULT_TELEMETRY_LOG_PATH, isPlainObject, normalizeProtectedPaths, readRecords, } from '@polydeukes/core';
31
+ import { ttlWitness } from '@polydeukes/covenant';
32
+ import { findUnattributed, readBaseline, snapshotBaseline, writeBaseline } from './baseline.js';
32
33
  import { loadCovenantModule, resolveCovenantDist } from './covenant-module.js';
33
34
  import { loadConfig } from './load-config.js';
35
+ import { sessionPreStateReader } from './pre-state-reader.js';
34
36
  /** The label every post-hoc state comparison row carries. */
35
37
  const BASELINE_LABEL = 'baseline';
36
38
  /**
@@ -109,7 +111,7 @@ function updateBaseline(spec) {
109
111
  function comparisonSpec(spec) {
110
112
  let config;
111
113
  try {
112
- config = loadConfig(spec.repoRoot).config;
114
+ config = loadConfig({ rootDir: spec.repoRoot }).config;
113
115
  }
114
116
  catch {
115
117
  return undefined;
@@ -129,9 +131,9 @@ function comparisonSpec(spec) {
129
131
  */
130
132
  export function assembleSessionRegistrations(spec) {
131
133
  const { config, rootDir, covenant, transcriptPath, transcript, witness } = spec;
132
- // The live transcript is the evidence channel the context family reads AND the one the
133
- // witness reads, so erasing or forging it disables every context discipline while opening
134
- // or shutting the human valve on the same file. It must NOT join this list: it lives deep
134
+ // The live transcript is the session a history declaration reads AND the one the witness
135
+ // reads, so erasing or forging it disables every history discipline while opening or
136
+ // shutting the human valve on the same file. It must NOT join this list: it lives deep
135
137
  // under HOME, and a path entry makes every ancestor protected — which measured as an
136
138
  // over-block refusing `cd /home/<user>`, `echo $HOME`, and every edit whose content
137
139
  // carried a bare `~`. The dedicated `transcript-mod` registration below covers that one
@@ -156,7 +158,7 @@ export function assembleSessionRegistrations(spec) {
156
158
  }),
157
159
  // Routing is the matches predicate, never path mention, so the home directory cannot
158
160
  // become a protected ancestor. No transcript in the payload means nothing to protect —
159
- // the valve and the context family already forfeited on the same absence.
161
+ // the valve and every history declaration already forfeited on the same absence.
160
162
  ...(transcriptPath === undefined
161
163
  ? []
162
164
  : [
@@ -179,17 +181,56 @@ export function assembleSessionRegistrations(spec) {
179
181
  rootDir,
180
182
  shellTools: SHELL_TOOLS,
181
183
  commandArgs: COMMAND_ARGS,
184
+ readPreState: sessionPreStateReader,
185
+ // One PreToolUse call is the whole observation, so the derived change set is a
186
+ // singleton and a change-set declaration cannot be judged here — it records `skipped`,
187
+ // the shape the commit surface gives a history declaration.
188
+ observesChangeSet: false,
182
189
  witness,
183
- // Context-family evidence is evaluated here, at assembly: a spawned body cannot hold
184
- // a transcript, and passing a path would leak JSONL knowledge into covenant. The
185
- // adapter brings the evaluator for its own `subagent`/`tool` vocabulary; core owns
186
- // `command`, which the compiler judges directly.
190
+ // The session itself, injected rather than its path: a declaration reading a
191
+ // `transcript` binding sees it flattened, and passing a path would leak JSONL
192
+ // knowledge into covenant.
187
193
  transcript,
188
- evaluatePrecedent,
189
194
  }),
190
195
  ];
191
196
  return registrations;
192
197
  }
198
+ /**
199
+ * This runtime's mutating+shell roster, rewritten onto the Claude vocabulary the adapter
200
+ * already judges. Claude names are not keys, so an existing Write/Edit/Bash envelope
201
+ * passes through. A name outside the table is left alone — that is a declared limit,
202
+ * recorded as the adapter's funnel pass, never a parse fault. The map lives here, not in
203
+ * the adapter, so the adapter stays Claude-vocabulary-only.
204
+ */
205
+ const GROK_TOOL_NAME_MAP = {
206
+ write: 'Write',
207
+ search_replace: 'Edit',
208
+ run_terminal_command: 'Bash',
209
+ };
210
+ /**
211
+ * Rewrite Grok tool names in a raw PreToolUse payload. Invalid JSON is left as the original
212
+ * string so the existing fail-closed path still runs — this function must not throw.
213
+ */
214
+ function rewriteGrokToolNames(rawPayload) {
215
+ let parsed;
216
+ try {
217
+ parsed = JSON.parse(rawPayload);
218
+ }
219
+ catch {
220
+ return rawPayload;
221
+ }
222
+ if (!isPlainObject(parsed))
223
+ return rawPayload;
224
+ for (const key of ['tool_name', 'toolName']) {
225
+ const value = parsed[key];
226
+ if (typeof value !== 'string')
227
+ continue;
228
+ const mapped = GROK_TOOL_NAME_MAP[value];
229
+ if (mapped !== undefined)
230
+ parsed[key] = mapped;
231
+ }
232
+ return JSON.stringify(parsed);
233
+ }
193
234
  /**
194
235
  * Judge one declared tool call before it runs. Async because the dispatcher spawns covenant
195
236
  * bodies — a synchronous runner would mean reimplementing the judge, which the
@@ -212,20 +253,20 @@ async function judgeHookCall(spec) {
212
253
  spec.telemetryPath ?? envTelemetryPath ?? join(spec.repoRoot, DEFAULT_TELEMETRY_LOG_PATH);
213
254
  // Discovery + parse + validation are the loader's job; a throw here (absent, ambiguous,
214
255
  // unparseable, or invalid config) falls into the fail-closed catch.
215
- const { config } = loadConfig(spec.repoRoot);
256
+ const { config } = loadConfig({ rootDir: spec.repoRoot });
216
257
  telemetryPath =
217
258
  spec.telemetryPath ?? envTelemetryPath ?? resolve(spec.repoRoot, config.telemetry.logPath);
218
259
  // Settled for the rest of the happy path. The `let` above exists so the catch can still
219
260
  // record when a failure lands before this point; a closure cannot narrow it, so the
220
261
  // dispatch seam below takes this const instead.
221
262
  const logPath = telemetryPath;
222
- const rawPayload = spec.rawPayload ?? readFileSync(0, 'utf-8');
263
+ const rawPayload = rewriteGrokToolNames(spec.rawPayload ?? readFileSync(0, 'utf-8'));
223
264
  // The transcript path travels in the raw payload only — up-translation drops it, so the
224
265
  // adapter reads it from the string. Every failure narrows to `undefined`, which leaves
225
266
  // the dispatcher on its `noopTranscript` default: lost evidence closes the valve rather
226
267
  // than opening it.
227
- const transcriptPath = transcriptPathFromPayload(rawPayload);
228
- const transcript = transcriptPath === undefined ? undefined : transcriptFromJsonlFile(transcriptPath);
268
+ const transcriptPath = transcriptPathFromPayload({ rawPayload });
269
+ const transcript = transcriptPath === undefined ? undefined : transcriptFromJsonlFile({ path: transcriptPath });
229
270
  // One witness predicate shared by every registration: a witness is a session-wide
230
271
  // permission the human granted, not a per-covenant one. Absent `witness` config leaves
231
272
  // this undefined, and no verdict can be witnessed open at all. The predicate receives
@@ -260,6 +301,16 @@ async function judgeHookCall(spec) {
260
301
  transcript,
261
302
  witness,
262
303
  });
304
+ // The world axis: files read from disk under the repository root, channels read beside
305
+ // the session's transcript. The disk is the pre-edit state on this surface; the rule
306
+ // that the judged change's own `post` overrides it belongs to the judge, so the root
307
+ // supplies what it read and nothing more. No `changes` list either — one PreToolUse call
308
+ // is the whole observation, and the judge derives that set from the input.
309
+ const { files, channels } = covenant.supplySources({
310
+ plan: covenant.planSources({ registrations }),
311
+ read: sessionSourceReader({ repoRoot: spec.repoRoot }),
312
+ readChannel: sessionChannelReader({ transcriptPath }),
313
+ });
263
314
  return await runAdapterPath({
264
315
  rawPayload,
265
316
  telemetryPath: logPath,
@@ -268,6 +319,7 @@ async function judgeHookCall(spec) {
268
319
  registrations,
269
320
  telemetryPath: logPath,
270
321
  transcript,
322
+ world: { files, channels },
271
323
  }),
272
324
  });
273
325
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `polydeukes/claude-code` — the session surface's entry point.
3
+ *
4
+ * The delegator a consumer project's PreToolUse hook imports: one verb and its spec type.
5
+ */
6
+ export { type ClaudeCodeHookOutcome, type ClaudeCodeHookSpec, runClaudeCodeHook, } from './claude-code-hook.ts';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `polydeukes/claude-code` — the session surface's entry point.
3
+ *
4
+ * The delegator a consumer project's PreToolUse hook imports: one verb and its spec type.
5
+ */
6
+ export { runClaudeCodeHook, } from './claude-code-hook.js';
@@ -10,26 +10,21 @@
10
10
  * fail-closed: a missing config, an unbuilt body, or a collector failure exits 2 with one
11
11
  * blocked record. An empty domain is an explicit pass with no records.
12
12
  */
13
+ import { type Observation } from '@polydeukes/adapter-git';
13
14
  import type { CovenantRegistration } from '@polydeukes/covenant';
14
- import { type CovenantModule } from './covenant-module.js';
15
- import { loadConfig } from './load-config.js';
15
+ import { type CovenantModule } from './covenant-module.ts';
16
+ import { loadConfig } from './load-config.ts';
16
17
  /**
17
18
  * Which observation of the commit surface a run judges. Only the collector differs between
18
19
  * them; the IR, the assembly, and the dispatcher are one path.
19
20
  *
20
- * `range` names its two refs. `ancestry: 'merge-base'` selects the `A...B` reading, whose
21
- * base is the two refs' common ancestor rather than `A` itself; the adapter that owns the
22
- * range grammar resolves it.
21
+ * The adapter that owns the git grammar owns the type: its supply body reads a path the way
22
+ * each observation sees the tree, and this root names the same fact for its callers.
23
23
  */
24
- export type CheckDomain = {
25
- kind: 'staged';
26
- } | {
27
- kind: 'worktree';
28
- } | {
29
- kind: 'range';
30
- base: string;
31
- head: string;
32
- ancestry?: 'merge-base';
24
+ export type CheckDomain = Observation;
25
+ /** {@link runCovenantCheck} result — the exit code the check process leaves with. */
26
+ export type CovenantCheckOutcome = {
27
+ exitCode: 0 | 2;
33
28
  };
34
29
  /** `runCovenantCheck` input. */
35
30
  export type CovenantCheckSpec = {
@@ -74,6 +69,4 @@ export declare function assembleCommitRegistrations(spec: CommitAssemblySpec): C
74
69
  * diff by default, the working tree or a ref range on request. Async because the dispatcher
75
70
  * spawns covenant bodies. An empty domain is an explicit pass: nothing to judge, no records.
76
71
  */
77
- export declare function runCovenantCheck(spec: CovenantCheckSpec): Promise<{
78
- exitCode: 0 | 2;
79
- }>;
72
+ export declare function runCovenantCheck(spec: CovenantCheckSpec): Promise<CovenantCheckOutcome>;
@@ -11,10 +11,11 @@
11
11
  * blocked record. An empty domain is an explicit pass with no records.
12
12
  */
13
13
  import { resolve } from 'node:path';
14
- import { collectRangeChanges, collectStagedChanges, collectWorktreeChanges, covenantInputFromStagedChanges, resolveGitAdapterSettings, STAGED_DELETE, STAGED_WRITE, } from '@polydeukes/adapter-git';
14
+ import { collectRangeChanges, collectStagedChanges, collectWorktreeChanges, covenantInputFromStagedChanges, observationSourceReader, resolveGitAdapterSettings, STAGED_DELETE, STAGED_WRITE, } from '@polydeukes/adapter-git';
15
15
  import { appendRecordFailOpen, DEFAULT_TELEMETRY_LOG_PATH, normalizeProtectedPaths, } from '@polydeukes/core';
16
16
  import { loadCovenantModule, resolveCovenantDist } from './covenant-module.js';
17
17
  import { loadConfig } from './load-config.js';
18
+ import { unobservedPreStateReader } from './pre-state-reader.js';
18
19
  /**
19
20
  * The TTY witness predicate, or undefined when no valve can exist (no witness configured
20
21
  * or no TTY seam). It fires on the first registration that broke, names it from the
@@ -61,7 +62,9 @@ function recordFailClosed(telemetryPath) {
61
62
  */
62
63
  export function assembleCommitRegistrations(spec) {
63
64
  const { config, rootDir, covenant, witness } = spec;
64
- const { protectedPaths: gitAdditivePaths } = resolveGitAdapterSettings(config.adapters?.git);
65
+ const { protectedPaths: gitAdditivePaths } = resolveGitAdapterSettings({
66
+ namespace: config.adapters?.git,
67
+ });
65
68
  // Union of the common list and the git-additive one, common first so first-occurrence
66
69
  // dedupe is deterministic. The session hook reads the common list alone.
67
70
  const protectedPaths = normalizeProtectedPaths({
@@ -74,14 +77,13 @@ export function assembleCommitRegistrations(spec) {
74
77
  mutatingToolNames: [STAGED_WRITE, STAGED_DELETE],
75
78
  witness,
76
79
  }),
77
- // No shell axis here, so command-family entries are left out. Context-family entries
78
- // stay in: with no transcript the compiler gives them skip registrations, which record
79
- // `skipped` on a match.
80
80
  ...covenant.compileDisciplineRegistrations({
81
- disciplines: disciplines.filter((entry) => entry.forbidCommand === undefined),
81
+ disciplines,
82
82
  rootDir,
83
83
  shellTools: [],
84
84
  commandArgs: [],
85
+ readPreState: unobservedPreStateReader,
86
+ observesChangeSet: true,
85
87
  witness,
86
88
  }),
87
89
  ];
@@ -104,7 +106,7 @@ function settleConfig(spec) {
104
106
  let telemetryPath;
105
107
  try {
106
108
  telemetryPath = spec.telemetryPath ?? resolve(spec.repoRoot, DEFAULT_TELEMETRY_LOG_PATH);
107
- const { config } = loadConfig(spec.repoRoot);
109
+ const { config } = loadConfig({ rootDir: spec.repoRoot });
108
110
  telemetryPath = spec.telemetryPath ?? resolve(spec.repoRoot, config.telemetry.logPath);
109
111
  return { settled: true, telemetryPath, config };
110
112
  }
@@ -118,12 +120,32 @@ function settleConfig(spec) {
118
120
  */
119
121
  function collectDomain(repoRoot, domain) {
120
122
  if (domain.kind === 'worktree')
121
- return collectWorktreeChanges(repoRoot);
123
+ return collectWorktreeChanges({ repoRoot });
122
124
  if (domain.kind === 'range') {
123
125
  const separator = domain.ancestry === 'merge-base' ? '...' : '..';
124
- return collectRangeChanges(repoRoot, `${domain.base}${separator}${domain.head}`);
126
+ return collectRangeChanges({
127
+ repoRoot,
128
+ range: `${domain.base}${separator}${domain.head}`,
129
+ });
130
+ }
131
+ return collectStagedChanges({ repoRoot });
132
+ }
133
+ /**
134
+ * The observation's change set: the paths of the collected changes that carry file-change
135
+ * evidence, in collection order.
136
+ *
137
+ * The same definition the judge derives its own set from, so both surfaces name the same
138
+ * changes. A deletion carries evidence and stays; a binary blob, which the collector gives
139
+ * a call with no evidence, produces no world of its own — listing it would hand the
140
+ * change-set relations a path no world can ever answer for.
141
+ */
142
+ function changedPaths(changes) {
143
+ const paths = [];
144
+ for (const call of covenantInputFromStagedChanges({ changes }).toolCalls) {
145
+ if (call.fileChange !== undefined)
146
+ paths.push(call.fileChange.path);
125
147
  }
126
- return collectStagedChanges(repoRoot);
148
+ return paths;
127
149
  }
128
150
  /**
129
151
  * Assemble the registrations and dispatch every collected change. Any throw here (an
@@ -132,7 +154,7 @@ function collectDomain(repoRoot, domain) {
132
154
  async function judgeChanges(spec, domain, telemetryPath, config, changes) {
133
155
  try {
134
156
  // Inside the try so an invalid adapter namespace fails closed.
135
- const { enforce } = resolveGitAdapterSettings(config.adapters?.git);
157
+ const { enforce } = resolveGitAdapterSettings({ namespace: config.adapters?.git });
136
158
  // Real Node resolution of the covenant package, so the commit surface runs the same
137
159
  // judges the session hook does; tests inject a directory instead. Awaited before any
138
160
  // registration is composed, so a dist the barrel cannot load fails the run closed here
@@ -155,14 +177,24 @@ async function judgeChanges(spec, domain, telemetryPath, config, changes) {
155
177
  covenant,
156
178
  witness,
157
179
  });
180
+ // One plan and one supply for the run: the per-change loop shares them, so the tree is
181
+ // read once per named file rather than once per change. `changes` carries the whole
182
+ // observation because this surface dispatches one change at a time to keep telemetry at
183
+ // one row per file — a set no judge could derive from the input it is handed.
184
+ const { files } = covenant.supplySources({
185
+ plan: covenant.planSources({ registrations }),
186
+ read: observationSourceReader({ repoRoot: spec.repoRoot, observation: domain }),
187
+ });
188
+ const world = { files, changes: changedPaths(changes) };
158
189
  for (const change of changes) {
159
- const input = covenantInputFromStagedChanges([change]);
190
+ const input = covenantInputFromStagedChanges({ changes: [change] });
160
191
  const { exitCode, results } = await covenant.dispatchCovenants({
161
192
  stdinPayload: JSON.stringify(input),
162
193
  registrations,
163
194
  telemetryPath,
164
195
  dispatcherLabel: 'covenant-check',
165
196
  enforce,
197
+ world,
166
198
  });
167
199
  if (exitCode === 2)
168
200
  blocked = true;
@@ -12,8 +12,8 @@
12
12
  * mirror where real Node resolution would always land on the healthy build.
13
13
  */
14
14
  import type * as covenant from '@polydeukes/covenant';
15
- /** The covenant surface both roots assemble against. */
16
- export type CovenantModule = typeof covenant;
15
+ /** The covenant surface both roots assemble against — the members they call, and no more. */
16
+ export type CovenantModule = Pick<typeof covenant, 'dispatchCovenants' | 'compileDisciplineRegistrations' | 'selfModRegistration' | 'shellModRegistration' | 'transcriptModRegistration' | 'planSources' | 'supplySources'>;
17
17
  /** Where real Node resolution puts the covenant package's built barrel. */
18
18
  export declare function resolveCovenantDist(): string;
19
19
  /**
@@ -11,6 +11,14 @@
11
11
  * The `covenantDist` seam selects WHICH dist is imported, so a fixture can inject a gutted
12
12
  * mirror where real Node resolution would always land on the healthy build.
13
13
  */
14
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
15
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
16
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
17
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
18
+ });
19
+ }
20
+ return path;
21
+ };
14
22
  import { createRequire } from 'node:module';
15
23
  import { join } from 'node:path';
16
24
  import { pathToFileURL } from 'node:url';
@@ -26,7 +34,7 @@ export function resolveCovenantDist() {
26
34
  */
27
35
  export async function loadCovenantModule(distDir) {
28
36
  try {
29
- return (await import(pathToFileURL(join(distDir, 'index.js')).href));
37
+ return (await import(__rewriteRelativeImportExtension(pathToFileURL(join(distDir, 'index.js')).href)));
30
38
  }
31
39
  catch (error) {
32
40
  throw new Error(`the covenant judges could not be loaded from ${distDir} — run 'pnpm build' to rebuild them: ${error instanceof Error ? error.message : String(error)}`);
@@ -0,0 +1,60 @@
1
+ # 폴리데우케스 문서
2
+
3
+ [English](./README.md) · **한국어**
4
+
5
+ > AI 코딩 파트너와 함께 개발하기 위한 규율 프레임워크입니다. 궁금한 곳부터 읽으십시오.
6
+
7
+ <a id="start-here"></a>
8
+ ## 여기서 시작하십시오
9
+
10
+ | 하려는 것 | 읽을 문서 |
11
+ |---|---|
12
+ | 첫 눈에 보이는 판정을 보고 싶다 | [첫 판정](./tutorials/first-judgment.ko.md) — 설치하고 Claude Code를 배선한 뒤 보호된 편집이 판정되는 모습을 봅니다 |
13
+ | Claude Code, Grok, git을 연결하고 싶다 | [표면 연결하기](./how-to/connect-surfaces.ko.md) — 세션과 커밋 배선, Grok 포함 |
14
+ | 프로젝트 설정을 다듬고 싶다 | [프로젝트 설정하기](./how-to/configure-project.ko.md) — 발견, IDE 지원, advise와 block의 선택 |
15
+ | 실제 규율을 쓰고 싶다 | [규율 작성하기](./how-to/write-disciplines.ko.md) — 실전 선언 예제, 특히 locale key pairing |
16
+ | 차단이나 건너뜀에서 회복하고 싶다 | [문제 해결](./troubleshooting.ko.md) — fail-closed 상태, 증인 밸브, 로그 |
17
+ | 이 프레임워크가 왜 있는지 알고 싶다 | [왜 폴리데우케스인가?](./why-polydeukes.ko.md) — 설계 백서 |
18
+ | 이 문서에 기여하고 싶다 | [문서에 기여하기](./contributing.ko.md) — 영한 쌍, 안정 ID, 카탈로그, 검사 |
19
+
20
+ <a id="reference"></a>
21
+ ## 레퍼런스
22
+
23
+ 설정 키, 하위 명령, 종료 코드를 설명합니다. 계획이 아니라 현재 동작을 기준으로 서술합니다.
24
+
25
+ | 문서 | 답하는 것 |
26
+ |---|---|
27
+ | [설정 레퍼런스](./reference/configuration/index.ko.md) | `polydeukes.config.yaml`에 무엇을 넣을 수 있고 각 키가 무엇을 하는지 |
28
+ | [`polydeukes` (`pdks` CLI)](./reference/packages/polydeukes.ko.md) | 패키지 계약. 서브커맨드는 [`reference/cli/`](./reference/cli/covenant-check.ko.md) |
29
+ | [`@polydeukes/core`](./reference/packages/core.ko.md) | 프로토콜, 입력 IR, 설정 스키마, 텔레메트리 |
30
+ | [`@polydeukes/covenant`](./reference/packages/covenant.ko.md) | 판정기입니다. 디스패처와 규율 라이브러리, 메타 약속, 밸브 |
31
+ | [`@polydeukes/adapter-claude-code`](./reference/packages/adapter-claude-code.ko.md) | 세션 표면입니다. 훅 페이로드에서 입력 IR로 |
32
+ | [`@polydeukes/adapter-git`](./reference/packages/adapter-git.ko.md) | 커밋 표면입니다. 스테이징·작업 트리·범위 diff에서 입력 IR로 |
33
+
34
+ <a id="shape-of-the-thing"></a>
35
+ ## 한 페이지로 보는 구조
36
+
37
+ 폴리데우케스는 개발자나 AI 에이전트가 하려는 일을 판정하고 결과를 기록합니다.
38
+ 기본값으로는 작업을 차단하지 않습니다. 설계의 바탕은 다음 세 가지입니다.
39
+
40
+ **약속은 가두기 위한 울타리가 아닙니다.** 여기서 확인하는 규율은 개발자가 이미 스스로 지켜 온 것들입니다. AI에게만큼 사람에게도 똑같이 적용되고, 프레임워크를 만든
41
+ 사람도 매일 그 판정을 받습니다.
42
+
43
+ **판정과 차단은 별개의 결정입니다.** 선언된 규율은 적용 범위에 해당하는 호출마다 판정합니다.
44
+ 위반했을 때 호출을 차단할지는 별도로 정합니다. 기본값으로는 사유를 기록하고 호출을 계속합니다.
45
+ 작성자는 `enforce: block`으로 차단을 선택할 수 있습니다. 별도 선택 없이 차단하는 것은
46
+ 프레임워크 자체를 보호하는 경우뿐입니다.
47
+
48
+ **모든 판정은 행 하나를 남깁니다.** `.polydeukes/roi.log`가 판정 결과마다 한 줄씩, 낱말 여섯 개짜리 어휘로 담습니다. 이 프로젝트가 자기 결함을 찾는
49
+ 방법이 그 기록입니다. 백서에 적힌 결함들도 전부 코드를 읽어서가 아니라 행을 세어서 나왔습니다.
50
+
51
+ <a id="two-surfaces"></a>
52
+ ## 두 표면
53
+
54
+ | 표면 | 판정 대상 | 배선 방법 | 대상 |
55
+ |---|---|---|---|
56
+ | **세션** | 도구 호출, 실행되기 전에 | `pdks init claude-code` 또는 `pdks init grok` | AI 파트너와 함께 개발하는 프로젝트 |
57
+ | **커밋** | diff — 스테이징 영역, 작업 트리, ref 범위 | pre-commit 훅, 또는 필요할 때 직접 실행 | 혼자 개발하는 사람, 그리고 CI |
58
+
59
+ 커밋 판정기는 필요할 때 직접 실행할 수도 있습니다. 작업 후에는 `pdks covenant check --worktree`,
60
+ PR 전에는 `--range`를 사용합니다. 같은 판정 기준으로 결과를 보고하며 증인 입력은 요청하지 않습니다.
@@ -0,0 +1,64 @@
1
+ # Polydeukes documentation
2
+
3
+ **English** · [한국어](./README.ko.md)
4
+
5
+ > A development discipline framework for building alongside an AI coding partner. Start where your
6
+ question is.
7
+
8
+ <a id="start-here"></a>
9
+ ## Start here
10
+
11
+ | If you want to | Read |
12
+ |---|---|
13
+ | Get to a first visible judgment | [First judgment](./tutorials/first-judgment.md) — install, wire Claude Code, and watch one protected edit get judged |
14
+ | Connect Claude Code, Grok, or git | [Connect the surfaces](./how-to/connect-surfaces.md) — session and commit setup, including Grok |
15
+ | Shape the project config | [Configure the project](./how-to/configure-project.md) — discovery, IDE support, and advise versus block |
16
+ | Write a real discipline | [Write disciplines](./how-to/write-disciplines.md) — worked declarations, especially locale key pairing |
17
+ | Recover from a block or skip | [Troubleshooting](./troubleshooting.md) — the fail-closed states, the witness valve, and the log |
18
+ | Understand why the framework exists | [Why Polydeukes?](./why-polydeukes.md) — the design whitepaper |
19
+ | Contribute to these docs | [Contributing](./contributing.md) — bilingual pairs, stable IDs, catalog, and checks |
20
+
21
+ <a id="reference"></a>
22
+ ## Reference
23
+
24
+ Every key, every subcommand, every exit code. These describe the present state only — nothing here
25
+ is aspirational.
26
+
27
+ | Document | Answers |
28
+ |---|---|
29
+ | [Configuration reference](./reference/configuration/index.md) | What may go in `polydeukes.config.yaml`, and what each key does |
30
+ | [`polydeukes` (the `pdks` CLI)](./reference/packages/polydeukes.md) | Package contract; subcommands live under [`reference/cli/`](./reference/cli/covenant-check.md) |
31
+ | [`@polydeukes/core`](./reference/packages/core.md) | The protocol, the input IR, the config schema, telemetry |
32
+ | [`@polydeukes/covenant`](./reference/packages/covenant.md) | The judge — dispatcher, discipline library, meta-covenants, the valve |
33
+ | [`@polydeukes/adapter-claude-code`](./reference/packages/adapter-claude-code.md) | Session surface — hook payloads become the input IR |
34
+ | [`@polydeukes/adapter-git`](./reference/packages/adapter-git.md) | Commit surface — staged, worktree, and range diffs become the input IR |
35
+
36
+ <a id="shape-of-the-thing"></a>
37
+ ## The shape of the thing, in one page
38
+
39
+ Polydeukes judges what a developer or an AI agent is about to do, records the verdict, and by
40
+ default lets the work proceed. Three ideas carry the rest.
41
+
42
+ **A covenant is a promise, not a fence.** The disciplines it enforces are the ones a good developer
43
+ already imposes on themselves. They bind the human exactly as much as the AI, and the framework's
44
+ own authors get judged by them daily.
45
+
46
+ **Judging and stopping are separate decisions.** Every declared discipline is judged on every
47
+ matching call. What a break then does is a second question: by default it is recorded with its
48
+ reason and the call continues. `enforce: block` is a promotion the author chooses. The only things
49
+ that stop a call unasked are the framework's own protections.
50
+
51
+ **Every judgment leaves a row.** `.polydeukes/roi.log` holds one line per verdict, in a vocabulary
52
+ of six words. That record is how this project finds its own defects — including the ones described
53
+ in the whitepaper, which were all found by counting rows rather than by reading code.
54
+
55
+ <a id="two-surfaces"></a>
56
+ ## Two surfaces
57
+
58
+ | Surface | Judges | Wired by | For |
59
+ |---|---|---|---|
60
+ | **Session** | A tool call, before it runs | `pdks init claude-code` or `pdks init grok` | A project developed with an AI partner |
61
+ | **Commit** | A diff — staged, the working tree, or a ref range | A pre-commit hook, or run on demand | A human developing alone, and CI |
62
+
63
+ The commit judge also answers on demand: `pdks covenant check --worktree` after a task, `--range`
64
+ before a PR. Same verdict a commit would receive, delivered as a report with no prompt and no gate.