@davidorex/pi-context 0.26.0 → 0.28.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 (123) hide show
  1. package/CHANGELOG.md +71 -15
  2. package/README.md +71 -18
  3. package/dist/block-api.d.ts +165 -18
  4. package/dist/block-api.d.ts.map +1 -1
  5. package/dist/block-api.js +800 -57
  6. package/dist/block-api.js.map +1 -1
  7. package/dist/citation-rot-scanner.d.ts +80 -0
  8. package/dist/citation-rot-scanner.d.ts.map +1 -0
  9. package/dist/citation-rot-scanner.js +421 -0
  10. package/dist/citation-rot-scanner.js.map +1 -0
  11. package/dist/content-hash.d.ts +25 -0
  12. package/dist/content-hash.d.ts.map +1 -0
  13. package/dist/content-hash.js +62 -0
  14. package/dist/content-hash.js.map +1 -0
  15. package/dist/context-dir.d.ts +126 -1
  16. package/dist/context-dir.d.ts.map +1 -1
  17. package/dist/context-dir.js +208 -4
  18. package/dist/context-dir.js.map +1 -1
  19. package/dist/context-registry.d.ts +122 -0
  20. package/dist/context-registry.d.ts.map +1 -0
  21. package/dist/context-registry.js +189 -0
  22. package/dist/context-registry.js.map +1 -0
  23. package/dist/context-sdk.d.ts +179 -12
  24. package/dist/context-sdk.d.ts.map +1 -1
  25. package/dist/context-sdk.js +536 -101
  26. package/dist/context-sdk.js.map +1 -1
  27. package/dist/context.d.ts +222 -4
  28. package/dist/context.d.ts.map +1 -1
  29. package/dist/context.js +244 -56
  30. package/dist/context.js.map +1 -1
  31. package/dist/git-env.d.ts +2 -0
  32. package/dist/git-env.d.ts.map +1 -0
  33. package/dist/git-env.js +29 -0
  34. package/dist/git-env.js.map +1 -0
  35. package/dist/index.d.ts +124 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +440 -1125
  38. package/dist/index.js.map +1 -1
  39. package/dist/lens-view.d.ts.map +1 -1
  40. package/dist/lens-view.js +5 -3
  41. package/dist/lens-view.js.map +1 -1
  42. package/dist/migration-registry-loader.d.ts +121 -0
  43. package/dist/migration-registry-loader.d.ts.map +1 -0
  44. package/dist/migration-registry-loader.js +309 -0
  45. package/dist/migration-registry-loader.js.map +1 -0
  46. package/dist/migrations-store.d.ts +141 -0
  47. package/dist/migrations-store.d.ts.map +1 -0
  48. package/dist/migrations-store.js +206 -0
  49. package/dist/migrations-store.js.map +1 -0
  50. package/dist/object-store.d.ts +26 -0
  51. package/dist/object-store.d.ts.map +1 -0
  52. package/dist/object-store.js +104 -0
  53. package/dist/object-store.js.map +1 -0
  54. package/dist/ops-registry.d.ts +44 -0
  55. package/dist/ops-registry.d.ts.map +1 -0
  56. package/dist/ops-registry.js +1223 -0
  57. package/dist/ops-registry.js.map +1 -0
  58. package/dist/orientation.d.ts +16 -0
  59. package/dist/orientation.d.ts.map +1 -0
  60. package/dist/orientation.js +49 -0
  61. package/dist/orientation.js.map +1 -0
  62. package/dist/promote-item.d.ts +28 -0
  63. package/dist/promote-item.d.ts.map +1 -0
  64. package/dist/promote-item.js +257 -0
  65. package/dist/promote-item.js.map +1 -0
  66. package/dist/read-element.d.ts +129 -0
  67. package/dist/read-element.d.ts.map +1 -0
  68. package/dist/read-element.js +296 -0
  69. package/dist/read-element.js.map +1 -0
  70. package/dist/rename-canonical-id.d.ts.map +1 -1
  71. package/dist/rename-canonical-id.js +22 -13
  72. package/dist/rename-canonical-id.js.map +1 -1
  73. package/dist/roadmap-plan.d.ts.map +1 -1
  74. package/dist/roadmap-plan.js +23 -13
  75. package/dist/roadmap-plan.js.map +1 -1
  76. package/dist/schema-validator.d.ts +1 -0
  77. package/dist/schema-validator.d.ts.map +1 -1
  78. package/dist/schema-validator.js +7 -3
  79. package/dist/schema-validator.js.map +1 -1
  80. package/dist/schema-write.d.ts +78 -4
  81. package/dist/schema-write.d.ts.map +1 -1
  82. package/dist/schema-write.js +297 -34
  83. package/dist/schema-write.js.map +1 -1
  84. package/dist/status-vocab.d.ts +1 -0
  85. package/dist/status-vocab.d.ts.map +1 -1
  86. package/dist/status-vocab.js +4 -0
  87. package/dist/status-vocab.js.map +1 -1
  88. package/dist/truncate.d.ts +34 -0
  89. package/dist/truncate.d.ts.map +1 -0
  90. package/dist/truncate.js +85 -0
  91. package/dist/truncate.js.map +1 -0
  92. package/dist/write-schema-migration-tool.d.ts +46 -0
  93. package/dist/write-schema-migration-tool.d.ts.map +1 -0
  94. package/dist/write-schema-migration-tool.js +115 -0
  95. package/dist/write-schema-migration-tool.js.map +1 -0
  96. package/package.json +35 -2
  97. package/samples/blocks/work-orders.json +3 -0
  98. package/samples/conception.json +22 -3
  99. package/samples/migrations.json +133 -0
  100. package/samples/schemas/context-contracts.schema.json +19 -4
  101. package/samples/schemas/conventions.schema.json +68 -12
  102. package/samples/schemas/decisions.schema.json +125 -28
  103. package/samples/schemas/features.schema.json +140 -26
  104. package/samples/schemas/framework-gaps.schema.json +171 -26
  105. package/samples/schemas/issues.schema.json +86 -13
  106. package/samples/schemas/layer-plans.schema.json +195 -35
  107. package/samples/schemas/phase.schema.json +18 -3
  108. package/samples/schemas/rationale.schema.json +37 -7
  109. package/samples/schemas/requirements.schema.json +77 -10
  110. package/samples/schemas/research.schema.json +169 -36
  111. package/samples/schemas/spec-reviews.schema.json +88 -18
  112. package/samples/schemas/story.schema.json +18 -3
  113. package/samples/schemas/tasks.schema.json +66 -10
  114. package/samples/schemas/verification.schema.json +70 -12
  115. package/samples/schemas/work-orders.schema.json +195 -0
  116. package/schemas/bootstrap.schema.json +18 -6
  117. package/schemas/config.schema.json +58 -26
  118. package/schemas/context-registry.schema.json +46 -0
  119. package/schemas/migrations.schema.json +123 -0
  120. package/schemas/relations.schema.json +41 -10
  121. package/skill-narrative.md +42 -21
  122. package/skills/pi-context/SKILL.md +159 -65
  123. package/skills/pi-context/references/bundled-resources.md +7 -2
@@ -0,0 +1,206 @@
1
+ /**
2
+ * migrations-store — substrate-managed read / write surface for
3
+ * `<resolveContextDir(cwd)>/migrations.json`.
4
+ *
5
+ * Mirrors the config / relations precedent in context.ts: load + AJV-validate
6
+ * via `validateFromFile` against the bundled migrations.schema.json; whole-file
7
+ * writes delegate to block-api's atomic `writeTypedFile` (tmp + rename) so a
8
+ * failed write leaves the prior file byte-identical.
9
+ *
10
+ * Three op-correct mutation helpers — append / replace / remove — wrap a
11
+ * load → JSON deep-clone → mutate → write pattern. Identity of a MigrationDecl
12
+ * is the (schemaName, fromVersion) pair, mirroring the registry's
13
+ * one-outgoing-edge-per-(schemaName, fromVersion) discipline from
14
+ * `schema-migrations.ts`:
15
+ * - appendMigrationDecl requires the (schemaName, fromVersion) pair to be
16
+ * ABSENT; collision throws with a descriptive message so an unintended
17
+ * double-declaration surfaces at the write site rather than as silent
18
+ * registry duplicate-registration at load time.
19
+ * - replaceMigrationDecl requires the pair to be PRESENT; a missing target
20
+ * throws (use append to introduce a new declaration).
21
+ * - removeMigrationDecl requires the pair to be PRESENT; a missing target
22
+ * throws.
23
+ *
24
+ * After each successful mutation the helper invokes
25
+ * `invalidateMigrationRegistry(cwd)` (migration-registry-loader.ts) so the
26
+ * next `getProjectMigrationRegistry(cwd)` consumer reads the fresh
27
+ * declarations without process restart. The store↔loader edge is mutually
28
+ * cyclic (loader reads from store; store writes invalidate loader cache);
29
+ * ESM tolerates the cycle because both imports are function-level uses, not
30
+ * top-level evaluations.
31
+ *
32
+ * MigrationDecl + TransformSpec + TransformOp types mirror the on-disk schema
33
+ * shape one-for-one. They live here (rather than schema-migrations.ts) because
34
+ * schema-migrations.ts is the in-memory registry abstraction (knows nothing of
35
+ * persistence); the persisted-shape vocabulary belongs at the substrate-store
36
+ * boundary.
37
+ */
38
+ import fs from "node:fs";
39
+ import path from "node:path";
40
+ import { fileURLToPath } from "node:url";
41
+ import { writeTypedFile } from "./block-api.js";
42
+ import { migrationsPath, migrationsPathForDir, resolveContextDir } from "./context-dir.js";
43
+ import { invalidateMigrationRegistry, invalidateMigrationRegistryForDir } from "./migration-registry-loader.js";
44
+ import { validateFromFile } from "./schema-validator.js";
45
+ /**
46
+ * Resolve the bundled migrations schema file. Mirrors the private
47
+ * `bundledSchemaPath` helper in context.ts — schemas live one directory up
48
+ * from both `src/` (under tsx --test) and `dist/` (after tsc).
49
+ */
50
+ function bundledMigrationsSchemaPath() {
51
+ const here = path.dirname(fileURLToPath(import.meta.url));
52
+ return path.resolve(here, "..", "schemas", "migrations.schema.json");
53
+ }
54
+ /**
55
+ * Current schema_version emitted into newly-created migrations.json files.
56
+ * Tracks the `version` field of migrations.schema.json itself.
57
+ */
58
+ export const MIGRATIONS_FILE_VERSION = "1.0.0";
59
+ /**
60
+ * Load + AJV-validate migrations.json. Returns null when the file is absent
61
+ * (pre-write state is a normal condition, NOT an error — empty/missing yields
62
+ * an empty registry in the loader). Throws on read / parse / schema failure.
63
+ */
64
+ export function loadMigrationsFileForDir(substrateDir) {
65
+ const p = migrationsPathForDir(substrateDir);
66
+ if (!fs.existsSync(p))
67
+ return null;
68
+ let raw;
69
+ try {
70
+ raw = fs.readFileSync(p, "utf-8");
71
+ }
72
+ catch (err) {
73
+ throw new Error(`loadMigrationsFile: failed to read ${p}: ${err instanceof Error ? err.message : String(err)}`);
74
+ }
75
+ let data;
76
+ try {
77
+ data = JSON.parse(raw);
78
+ }
79
+ catch (err) {
80
+ throw new Error(`loadMigrationsFile: invalid JSON in ${p}: ${err instanceof Error ? err.message : String(err)}`);
81
+ }
82
+ validateFromFile(bundledMigrationsSchemaPath(), data, `migrations.json (${p})`);
83
+ return data;
84
+ }
85
+ export function loadMigrationsFile(cwd) {
86
+ return loadMigrationsFileForDir(resolveContextDir(cwd));
87
+ }
88
+ /**
89
+ * Atomic, AJV-validated whole-file write of migrations.json. Delegates to
90
+ * block-api's writeTypedFile against the bundled migrations schema. `ctx` is
91
+ * accepted for call-site parity with the rest of the substrate write surface;
92
+ * the migrations schema declares no envelope author fields so stamping is a
93
+ * structural no-op today (the loader-cache invalidation upstream of this call
94
+ * is the side-effect that matters for read-after-write parity).
95
+ */
96
+ export function writeMigrationsFile(cwd, file, ctx) {
97
+ writeMigrationsFileForDir(resolveContextDir(cwd), file, ctx);
98
+ }
99
+ /**
100
+ * Dir-targeted twin of {@link writeMigrationsFile} (Cycle-1 `*ForDir` pattern).
101
+ * Atomic, AJV-validated whole-file write of `<substrateDir>/migrations.json`
102
+ * against the bundled migrations schema — takes the ALREADY-RESOLVED substrate
103
+ * dir directly (no `.pi-context.json` pointer resolution). The cwd form is a
104
+ * thin wrapper resolving the active dir; behaviour is byte-identical when called
105
+ * via cwd. Same attestation-parity no-op semantics as the cwd form (the
106
+ * migrations schema declares no envelope author fields). Does NOT invalidate any
107
+ * loader cache — the cache-invalidation side-effect lives on the
108
+ * appendMigrationDecl mutation helpers, not the raw whole-file write.
109
+ */
110
+ export function writeMigrationsFileForDir(substrateDir, file, ctx) {
111
+ writeTypedFile(migrationsPathForDir(substrateDir), bundledMigrationsSchemaPath(), file, ctx, "migrations.json");
112
+ }
113
+ /**
114
+ * Compose an initial empty migrations file shape — used by the append helper
115
+ * when the on-disk file is absent. Centralised so the `schema_version` token
116
+ * is sourced from one place.
117
+ */
118
+ function emptyMigrationsFile() {
119
+ return { schema_version: MIGRATIONS_FILE_VERSION, migrations: [] };
120
+ }
121
+ /**
122
+ * Deep-clone via JSON round-trip. Sufficient for MigrationDecl shapes (no
123
+ * Date / Map / undefined values per the schema). Matches the amend-config
124
+ * load-clone-mutate-write precedent.
125
+ */
126
+ function clone(value) {
127
+ return JSON.parse(JSON.stringify(value));
128
+ }
129
+ /**
130
+ * Locate the index of a (schemaName, fromVersion) MigrationDecl within the
131
+ * file's `migrations[]` array; returns -1 when absent. Centralised so the
132
+ * three op-correct helpers cannot drift on identity-matching semantics.
133
+ */
134
+ function findMigrationIndex(file, schemaName, fromVersion) {
135
+ return file.migrations.findIndex((m) => m.schemaName === schemaName && m.fromVersion === fromVersion);
136
+ }
137
+ /**
138
+ * Append a new MigrationDecl to the substrate. Op-correctness: the
139
+ * (schemaName, fromVersion) pair must be ABSENT on-disk; collision throws.
140
+ * Invalidates the loader cache for `cwd` after a successful write so the
141
+ * next registry consumer reads the fresh declaration.
142
+ */
143
+ export function appendMigrationDecl(cwd, decl, ctx) {
144
+ appendMigrationDeclForDir(resolveContextDir(cwd), decl, ctx);
145
+ }
146
+ /**
147
+ * Dir-targeted twin of {@link appendMigrationDecl} (Cycle-1 `*ForDir` pattern).
148
+ * Append a new MigrationDecl to the substrate at `substrateDir`. Op-correctness:
149
+ * the (schemaName, fromVersion) pair must be ABSENT on-disk; collision throws.
150
+ * Invalidates the loader cache for `substrateDir` after a successful write so
151
+ * the next registry consumer reads the fresh declaration. The cwd form is a thin
152
+ * wrapper resolving the active dir; behaviour is byte-identical when called via
153
+ * cwd (resolveContextDir(cwd) → invalidateMigrationRegistry(cwd) routes through
154
+ * invalidateMigrationRegistryForDir on the same dir).
155
+ */
156
+ export function appendMigrationDeclForDir(substrateDir, decl, ctx) {
157
+ const current = loadMigrationsFileForDir(substrateDir) ?? emptyMigrationsFile();
158
+ const idx = findMigrationIndex(current, decl.schemaName, decl.fromVersion);
159
+ if (idx >= 0) {
160
+ throw new Error(`appendMigrationDecl: collision — migration for schema '${decl.schemaName}' fromVersion '${decl.fromVersion}' already exists; use replaceMigrationDecl to overwrite`);
161
+ }
162
+ const next = clone(current);
163
+ next.migrations.push(decl);
164
+ writeMigrationsFileForDir(substrateDir, next, ctx);
165
+ invalidateMigrationRegistryForDir(substrateDir);
166
+ }
167
+ /**
168
+ * Replace an existing MigrationDecl identified by (schemaName, fromVersion).
169
+ * Op-correctness: target must be PRESENT; missing target throws. The whole
170
+ * decl is replaced (no per-field merge). Invalidates the loader cache after a
171
+ * successful write.
172
+ */
173
+ export function replaceMigrationDecl(cwd, decl, ctx) {
174
+ const current = loadMigrationsFile(cwd);
175
+ if (current === null) {
176
+ throw new Error(`replaceMigrationDecl: migrations.json absent at ${migrationsPath(cwd)}; use appendMigrationDecl to create`);
177
+ }
178
+ const idx = findMigrationIndex(current, decl.schemaName, decl.fromVersion);
179
+ if (idx < 0) {
180
+ throw new Error(`replaceMigrationDecl: target missing — no migration declared for schema '${decl.schemaName}' fromVersion '${decl.fromVersion}'; use appendMigrationDecl to introduce`);
181
+ }
182
+ const next = clone(current);
183
+ next.migrations[idx] = decl;
184
+ writeMigrationsFile(cwd, next, ctx);
185
+ invalidateMigrationRegistry(cwd);
186
+ }
187
+ /**
188
+ * Remove an existing MigrationDecl identified by (schemaName, fromVersion).
189
+ * Op-correctness: target must be PRESENT; missing target throws. Invalidates
190
+ * the loader cache after a successful write.
191
+ */
192
+ export function removeMigrationDecl(cwd, schemaName, fromVersion, ctx) {
193
+ const current = loadMigrationsFile(cwd);
194
+ if (current === null) {
195
+ throw new Error(`removeMigrationDecl: migrations.json absent at ${migrationsPath(cwd)}; nothing to remove`);
196
+ }
197
+ const idx = findMigrationIndex(current, schemaName, fromVersion);
198
+ if (idx < 0) {
199
+ throw new Error(`removeMigrationDecl: target missing — no migration declared for schema '${schemaName}' fromVersion '${fromVersion}'`);
200
+ }
201
+ const next = clone(current);
202
+ next.migrations.splice(idx, 1);
203
+ writeMigrationsFile(cwd, next, ctx);
204
+ invalidateMigrationRegistry(cwd);
205
+ }
206
+ //# sourceMappingURL=migrations-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations-store.js","sourceRoot":"","sources":["../src/migrations-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * True when an object with `contentHash` already exists in the store.
3
+ * Asserts hash shape first (a malformed hash can never be present, but the
4
+ * guard keeps the surface uniform and rejects path-escape attempts at the
5
+ * boundary).
6
+ */
7
+ export declare function hasObject(substrateDir: string, contentHash: string): boolean;
8
+ /**
9
+ * Write `content` under `contentHash` if not already present.
10
+ *
11
+ * Idempotent: returns early when `hasObject` is true (content-addressed — the
12
+ * same hash implies the same bytes, so a re-put would rewrite identical
13
+ * content). On a fresh write, creates `<substrateDir>/objects/` if needed and
14
+ * writes `JSON.stringify(content, null, 2)` via atomic tmp+rename.
15
+ *
16
+ * `content` is expected to be the canonical content projection the caller
17
+ * hashed to obtain `contentHash`; this module does not re-derive or verify the
18
+ * hash (no dependency on content-hash.ts).
19
+ */
20
+ export declare function putObject(substrateDir: string, contentHash: string, content: Record<string, unknown>): void;
21
+ /**
22
+ * Parsed content stored under `contentHash`, or `null` when no such object
23
+ * exists. Asserts hash shape first.
24
+ */
25
+ export declare function getObject(substrateDir: string, contentHash: string): Record<string, unknown> | null;
26
+ //# sourceMappingURL=object-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-store.d.ts","sourceRoot":"","sources":["../src/object-store.ts"],"names":[],"mappings":"AAoDA;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAG5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAqB3G;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAOnG"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Content-addressed object store (Cycle 2, Phase B). A durable, append-only
3
+ * store keyed by a content hash: each distinct content projection is written
4
+ * once to `<substrateDir>/objects/<contentHash>.json` holding its JSON. This is
5
+ * the durable Merkle store that makes the OID/refname split actually
6
+ * content-addressed and gives later cycles real pinning + integrity.
7
+ *
8
+ * Dir-explicit (no pointer resolution), so it is independently testable and
9
+ * usable across substrate dirs. It takes a `contentHash` parameter rather than
10
+ * computing one — the caller supplies the hash from `computeContentHash` — so
11
+ * this module has ZERO dependency on `content-hash.ts`.
12
+ *
13
+ * No file lock: a write is keyed by its own content hash, so two writers of the
14
+ * same content produce byte-identical files at the same path (collision-free,
15
+ * idempotent). Atomic tmp+rename guards against a torn read of a partial file.
16
+ *
17
+ * `objects/` is tracked in git (it is the integrity store; gitignoring it would
18
+ * lose pinning). No `.gitignore` change accompanies this module.
19
+ *
20
+ * Dormant this cycle: nothing in any write path calls `putObject` yet (Cycle 3
21
+ * wiring / Cycle 10 backfill). Infrastructure module — not re-exported from
22
+ * `index.ts`.
23
+ */
24
+ import fs from "node:fs";
25
+ import path from "node:path";
26
+ /** Lowercase 64-char hex (a SHA-256 digest). */
27
+ const CONTENT_HASH_PATTERN = /^[0-9a-f]{64}$/;
28
+ /**
29
+ * Guard the hash before it becomes a path segment. A content hash is a fixed
30
+ * lowercase-hex SHA-256 digest; anything else (wrong length, uppercase, path
31
+ * separators, `..`) is rejected so a malformed value cannot escape the
32
+ * `objects/` directory or name a non-canonical file. Analogous to
33
+ * `assertSubstrateName` for block names.
34
+ */
35
+ function assertContentHash(contentHash) {
36
+ if (!CONTENT_HASH_PATTERN.test(contentHash)) {
37
+ throw new Error(`object-store: invalid contentHash ${JSON.stringify(contentHash)} — expected lowercase 64-char hex (SHA-256)`);
38
+ }
39
+ }
40
+ function objectsDirFor(substrateDir) {
41
+ return path.join(substrateDir, "objects");
42
+ }
43
+ function objectPathFor(substrateDir, contentHash) {
44
+ return path.join(objectsDirFor(substrateDir), `${contentHash}.json`);
45
+ }
46
+ /**
47
+ * True when an object with `contentHash` already exists in the store.
48
+ * Asserts hash shape first (a malformed hash can never be present, but the
49
+ * guard keeps the surface uniform and rejects path-escape attempts at the
50
+ * boundary).
51
+ */
52
+ export function hasObject(substrateDir, contentHash) {
53
+ assertContentHash(contentHash);
54
+ return fs.existsSync(objectPathFor(substrateDir, contentHash));
55
+ }
56
+ /**
57
+ * Write `content` under `contentHash` if not already present.
58
+ *
59
+ * Idempotent: returns early when `hasObject` is true (content-addressed — the
60
+ * same hash implies the same bytes, so a re-put would rewrite identical
61
+ * content). On a fresh write, creates `<substrateDir>/objects/` if needed and
62
+ * writes `JSON.stringify(content, null, 2)` via atomic tmp+rename.
63
+ *
64
+ * `content` is expected to be the canonical content projection the caller
65
+ * hashed to obtain `contentHash`; this module does not re-derive or verify the
66
+ * hash (no dependency on content-hash.ts).
67
+ */
68
+ export function putObject(substrateDir, contentHash, content) {
69
+ assertContentHash(contentHash);
70
+ if (hasObject(substrateDir, contentHash)) {
71
+ return;
72
+ }
73
+ const objectsDir = objectsDirFor(substrateDir);
74
+ fs.mkdirSync(objectsDir, { recursive: true });
75
+ const filePath = objectPathFor(substrateDir, contentHash);
76
+ const tmpPath = `${filePath}.object-store-${process.pid}.tmp`;
77
+ try {
78
+ fs.writeFileSync(tmpPath, JSON.stringify(content, null, 2), "utf-8");
79
+ fs.renameSync(tmpPath, filePath);
80
+ }
81
+ catch (err) {
82
+ try {
83
+ fs.unlinkSync(tmpPath);
84
+ }
85
+ catch {
86
+ /* ignore cleanup failure */
87
+ }
88
+ const msg = err instanceof Error ? err.message : String(err);
89
+ throw new Error(`object-store: failed to write object ${contentHash}: ${msg}`);
90
+ }
91
+ }
92
+ /**
93
+ * Parsed content stored under `contentHash`, or `null` when no such object
94
+ * exists. Asserts hash shape first.
95
+ */
96
+ export function getObject(substrateDir, contentHash) {
97
+ assertContentHash(contentHash);
98
+ const filePath = objectPathFor(substrateDir, contentHash);
99
+ if (!fs.existsSync(filePath)) {
100
+ return null;
101
+ }
102
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
103
+ }
104
+ //# sourceMappingURL=object-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-store.js","sourceRoot":"","sources":["../src/object-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,gDAAgD;AAChD,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAE9C;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAC,WAAmB;IAC7C,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACd,qCAAqC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,6CAA6C,CAC7G,CAAC;IACH,CAAC;AACF,CAAC;AAED,SAAS,aAAa,CAAC,YAAoB;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,aAAa,CAAC,YAAoB,EAAE,WAAmB;IAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,GAAG,WAAW,OAAO,CAAC,CAAC;AACtE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,YAAoB,EAAE,WAAmB;IAClE,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/B,OAAO,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,YAAoB,EAAE,WAAmB,EAAE,OAAgC;IACpG,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/B,IAAI,SAAS,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,CAAC;QAC1C,OAAO;IACR,CAAC;IACD,MAAM,UAAU,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;IAC/C,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,aAAa,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,GAAG,QAAQ,iBAAiB,OAAO,CAAC,GAAG,MAAM,CAAC;IAC9D,IAAI,CAAC;QACJ,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACrE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,CAAC;YACJ,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACR,4BAA4B;QAC7B,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,wCAAwC,WAAW,KAAK,GAAG,EAAE,CAAC,CAAC;IAChF,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,YAAoB,EAAE,WAAmB;IAClE,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,aAAa,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IAC1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAA4B,CAAC;AAClF,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { type TSchema } from "typebox";
3
+ /**
4
+ * One substrate operation. `run(cwd, params)` returns the text payload that
5
+ * registerAll places at `content[0].text`; everything else (param coercion,
6
+ * library calls, early returns, throws) lives in run verbatim. `parameters` is
7
+ * the typebox Type.Object schema published as the tool's parameter schema.
8
+ *
9
+ * `surface` partitions ops for the downstream CLI ("use" = read/write substrate
10
+ * ops; "process" reserved for lifecycle/dispatch ops). `authGated` flags ops
11
+ * the CLI should treat as requiring credentialed confirmation; it is carried
12
+ * for that consumer and is not the enforcement point (the pi-agent-dispatch
13
+ * auth-gate remains canonical).
14
+ */
15
+ export interface OpDefinition<P = any> {
16
+ name: string;
17
+ label: string;
18
+ description: string;
19
+ promptSnippet?: string;
20
+ parameters: TSchema;
21
+ run(cwd: string, params: P): string | Promise<string>;
22
+ authGated?: boolean;
23
+ surface: "use" | "process";
24
+ }
25
+ export declare const ops: OpDefinition[];
26
+ /**
27
+ * The pi-context-owned set of tool names that require human-authorization at
28
+ * the pi-agent-dispatch tool_call gate. Derived from the registry's authGated
29
+ * flags rather than hand-maintained, so a single source — the OpDefinition's
30
+ * authGated field — names both the CLI's credentialed-confirmation hint and the
31
+ * gate's aggregated allowlist. pi-agent-dispatch imports this via the `./ops`
32
+ * subpath and folds it into AUTH_REQUIRED_TOOLS alongside the other packages'
33
+ * owned sets. The gate at the pi-agent-dispatch layer remains the enforcement
34
+ * point; this list is the source of pi-context's contribution to it.
35
+ */
36
+ export declare const gatedTools: string[];
37
+ /**
38
+ * Register every op in `ops` as a pi tool. Each tool's execute body is the
39
+ * uniform wrapper around the op's run(): coerce params, await run, place the
40
+ * returned string at content[0].text. Behavior-identical to the prior inline
41
+ * registrations.
42
+ */
43
+ export declare function registerAll(pi: ExtensionAPI): void;
44
+ //# sourceMappingURL=ops-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ops-registry.d.ts","sourceRoot":"","sources":["../src/ops-registry.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAGX,YAAY,EAEZ,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,KAAK,OAAO,EAAQ,MAAM,SAAS,CAAC;AA4D7C;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,GAAG;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,KAAK,GAAG,SAAS,CAAC;CAC3B;AA6BD,eAAO,MAAM,GAAG,EAAE,YAAY,EAqzC7B,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,EAAsD,CAAC;AAYtF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAuBlD"}