@phyzess/pappra 0.0.0-stage → 0.1.0-beta.2

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 (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -2
  3. package/THIRD_PARTY_NOTICES.md +16 -0
  4. package/assets/integrations/skills/pappra/SKILL.md +68 -0
  5. package/assets/integrations/skills/pappra/references/hosts.md +30 -0
  6. package/assets/integrations/skills/pappra/references/lifecycle.md +37 -0
  7. package/assets/integrations/skills/pappra/references/operations.md +14 -0
  8. package/assets/manifest.json +4 -0
  9. package/dist/app/collaboration.d.ts +170 -0
  10. package/dist/app/collaboration.js +1650 -0
  11. package/dist/app/doctor.d.ts +9 -0
  12. package/dist/app/doctor.js +1030 -0
  13. package/dist/app/init.d.ts +143 -0
  14. package/dist/app/init.js +1111 -0
  15. package/dist/cli/commands/code.d.ts +16 -0
  16. package/dist/cli/commands/code.js +127 -0
  17. package/dist/cli/commands/collaboration.d.ts +13 -0
  18. package/dist/cli/commands/collaboration.js +186 -0
  19. package/dist/cli/commands/continuity.d.ts +29 -0
  20. package/dist/cli/commands/continuity.js +133 -0
  21. package/dist/cli/commands/docs.d.ts +11 -0
  22. package/dist/cli/commands/docs.js +133 -0
  23. package/dist/cli/commands/doctor.d.ts +14 -0
  24. package/dist/cli/commands/doctor.js +57 -0
  25. package/dist/cli/commands/host.d.ts +13 -0
  26. package/dist/cli/commands/host.js +125 -0
  27. package/dist/cli/commands/init.d.ts +51 -0
  28. package/dist/cli/commands/init.js +668 -0
  29. package/dist/cli/main.d.ts +2 -0
  30. package/dist/cli/main.js +10 -0
  31. package/dist/cli/output.d.ts +4 -0
  32. package/dist/cli/output.js +1 -0
  33. package/dist/cli/run.d.ts +2 -0
  34. package/dist/cli/run.js +597 -0
  35. package/dist/code/budget.d.ts +14 -0
  36. package/dist/code/budget.js +39 -0
  37. package/dist/code/exec.d.ts +23 -0
  38. package/dist/code/exec.js +133 -0
  39. package/dist/code/identity.d.ts +13 -0
  40. package/dist/code/identity.js +214 -0
  41. package/dist/code/index.d.ts +6 -0
  42. package/dist/code/index.js +3 -0
  43. package/dist/code/providers/codegraph.d.ts +59 -0
  44. package/dist/code/providers/codegraph.js +567 -0
  45. package/dist/code/registration.d.ts +9 -0
  46. package/dist/code/registration.js +293 -0
  47. package/dist/code/service.d.ts +53 -0
  48. package/dist/code/service.js +633 -0
  49. package/dist/code/text.d.ts +3 -0
  50. package/dist/code/text.js +202 -0
  51. package/dist/collab/admission.d.ts +450 -0
  52. package/dist/collab/admission.js +797 -0
  53. package/dist/collab/canonical.d.ts +26 -0
  54. package/dist/collab/canonical.js +136 -0
  55. package/dist/collab/contracts.d.ts +73 -0
  56. package/dist/collab/contracts.js +76 -0
  57. package/dist/collab/envelope.d.ts +52 -0
  58. package/dist/collab/envelope.js +241 -0
  59. package/dist/collab/fingerprint.d.ts +7 -0
  60. package/dist/collab/fingerprint.js +58 -0
  61. package/dist/collab/handoff.d.ts +350 -0
  62. package/dist/collab/handoff.js +513 -0
  63. package/dist/collab/index.d.ts +9 -0
  64. package/dist/collab/index.js +8 -0
  65. package/dist/collab/lifecycle.d.ts +449 -0
  66. package/dist/collab/lifecycle.js +534 -0
  67. package/dist/collab/messages.d.ts +148 -0
  68. package/dist/collab/messages.js +583 -0
  69. package/dist/collab/notify.d.ts +47 -0
  70. package/dist/collab/notify.js +140 -0
  71. package/dist/collab/preflight.d.ts +31 -0
  72. package/dist/collab/preflight.js +271 -0
  73. package/dist/collab/resources.d.ts +119 -0
  74. package/dist/collab/resources.js +451 -0
  75. package/dist/collab/store.d.ts +143 -0
  76. package/dist/collab/store.js +606 -0
  77. package/dist/collab/types.d.ts +354 -0
  78. package/dist/collab/types.js +1 -0
  79. package/dist/collab/validation.d.ts +10 -0
  80. package/dist/collab/validation.js +303 -0
  81. package/dist/continuity/context.d.ts +24 -0
  82. package/dist/continuity/context.js +179 -0
  83. package/dist/continuity/index.d.ts +3 -0
  84. package/dist/continuity/index.js +1 -0
  85. package/dist/continuity/paths.d.ts +37 -0
  86. package/dist/continuity/paths.js +258 -0
  87. package/dist/continuity/service.d.ts +59 -0
  88. package/dist/continuity/service.js +1258 -0
  89. package/dist/hosts/claude/native.d.ts +79 -0
  90. package/dist/hosts/claude/native.js +853 -0
  91. package/dist/hosts/codex/native.d.ts +345 -0
  92. package/dist/hosts/codex/native.js +1528 -0
  93. package/dist/hosts/codex/protocol.d.ts +51 -0
  94. package/dist/hosts/codex/protocol.js +42 -0
  95. package/dist/hosts/detect.d.ts +28 -0
  96. package/dist/hosts/detect.js +73 -0
  97. package/dist/hosts/index.d.ts +10 -0
  98. package/dist/hosts/index.js +10 -0
  99. package/dist/hosts/integration/bundle.d.ts +16 -0
  100. package/dist/hosts/integration/bundle.js +81 -0
  101. package/dist/hosts/integration/hook-entry.d.ts +1 -0
  102. package/dist/hosts/integration/hook-entry.js +68 -0
  103. package/dist/hosts/integration/hook.d.ts +32 -0
  104. package/dist/hosts/integration/hook.js +781 -0
  105. package/dist/hosts/integration/pi-extension-runtime.d.ts +18 -0
  106. package/dist/hosts/integration/pi-extension-runtime.js +89 -0
  107. package/dist/hosts/integration/pi-template.d.ts +3 -0
  108. package/dist/hosts/integration/pi-template.js +5 -0
  109. package/dist/hosts/integration/service.d.ts +41 -0
  110. package/dist/hosts/integration/service.js +1381 -0
  111. package/dist/hosts/integration/support.d.ts +36 -0
  112. package/dist/hosts/integration/support.js +41 -0
  113. package/dist/hosts/integration/templates.d.ts +22 -0
  114. package/dist/hosts/integration/templates.js +70 -0
  115. package/dist/hosts/pi/protocol.d.ts +22 -0
  116. package/dist/hosts/pi/protocol.js +28 -0
  117. package/dist/hosts/pi/query-result.d.ts +22 -0
  118. package/dist/hosts/pi/query-result.js +710 -0
  119. package/dist/hosts/pi/rpc.d.ts +937 -0
  120. package/dist/hosts/pi/rpc.js +1621 -0
  121. package/dist/hosts/pilot.d.ts +485 -0
  122. package/dist/hosts/pilot.js +593 -0
  123. package/dist/index.d.ts +5 -0
  124. package/dist/index.js +4 -0
  125. package/dist/infra/fs.d.ts +27 -0
  126. package/dist/infra/fs.js +130 -0
  127. package/dist/infra/git.d.ts +60 -0
  128. package/dist/infra/git.js +358 -0
  129. package/dist/infra/owned-process-group.d.ts +173 -0
  130. package/dist/infra/owned-process-group.js +571 -0
  131. package/dist/install/apply.d.ts +65 -0
  132. package/dist/install/apply.js +663 -0
  133. package/dist/install/index.d.ts +5 -0
  134. package/dist/install/index.js +5 -0
  135. package/dist/install/ownership.d.ts +85 -0
  136. package/dist/install/ownership.js +430 -0
  137. package/dist/install/plan.d.ts +39 -0
  138. package/dist/install/plan.js +97 -0
  139. package/dist/install/rollback.d.ts +18 -0
  140. package/dist/install/rollback.js +544 -0
  141. package/dist/install/runtime.d.ts +76 -0
  142. package/dist/install/runtime.js +1145 -0
  143. package/dist/knowledge/catalog.d.ts +47 -0
  144. package/dist/knowledge/catalog.js +308 -0
  145. package/dist/knowledge/context.d.ts +28 -0
  146. package/dist/knowledge/context.js +61 -0
  147. package/dist/knowledge/index.d.ts +3 -0
  148. package/dist/knowledge/index.js +1 -0
  149. package/dist/knowledge/journal.d.ts +16 -0
  150. package/dist/knowledge/journal.js +109 -0
  151. package/dist/knowledge/lifecycle.d.ts +8 -0
  152. package/dist/knowledge/lifecycle.js +1001 -0
  153. package/dist/knowledge/paths.d.ts +37 -0
  154. package/dist/knowledge/paths.js +236 -0
  155. package/dist/knowledge/refs.d.ts +36 -0
  156. package/dist/knowledge/refs.js +267 -0
  157. package/dist/knowledge/service.d.ts +21 -0
  158. package/dist/knowledge/service.js +393 -0
  159. package/dist/knowledge/types.d.ts +171 -0
  160. package/dist/knowledge/types.js +1 -0
  161. package/dist/knowledge/write.d.ts +8 -0
  162. package/dist/knowledge/write.js +1223 -0
  163. package/dist/package-info.d.ts +6 -0
  164. package/dist/package-info.js +19 -0
  165. package/dist/schemas/code.d.ts +377 -0
  166. package/dist/schemas/code.js +145 -0
  167. package/dist/schemas/collaboration.d.ts +115 -0
  168. package/dist/schemas/collaboration.js +106 -0
  169. package/dist/schemas/continuity.d.ts +315 -0
  170. package/dist/schemas/continuity.js +199 -0
  171. package/dist/schemas/diagnostics.d.ts +28 -0
  172. package/dist/schemas/diagnostics.js +27 -0
  173. package/dist/schemas/doctor.d.ts +157 -0
  174. package/dist/schemas/doctor.js +75 -0
  175. package/dist/schemas/document.d.ts +644 -0
  176. package/dist/schemas/document.js +296 -0
  177. package/dist/schemas/errors.d.ts +177 -0
  178. package/dist/schemas/errors.js +176 -0
  179. package/dist/schemas/evidence.d.ts +24 -0
  180. package/dist/schemas/evidence.js +16 -0
  181. package/dist/schemas/host.d.ts +436 -0
  182. package/dist/schemas/host.js +201 -0
  183. package/dist/schemas/index.d.ts +15 -0
  184. package/dist/schemas/index.js +15 -0
  185. package/dist/schemas/init.d.ts +121 -0
  186. package/dist/schemas/init.js +94 -0
  187. package/dist/schemas/install.d.ts +213 -0
  188. package/dist/schemas/install.js +139 -0
  189. package/dist/schemas/machine.d.ts +43 -0
  190. package/dist/schemas/machine.js +96 -0
  191. package/dist/schemas/outcome.d.ts +23 -0
  192. package/dist/schemas/outcome.js +17 -0
  193. package/dist/schemas/paths.d.ts +19 -0
  194. package/dist/schemas/paths.js +63 -0
  195. package/dist/schemas/workspace.d.ts +120 -0
  196. package/dist/schemas/workspace.js +201 -0
  197. package/dist/workspace/config-file.d.ts +36 -0
  198. package/dist/workspace/config-file.js +237 -0
  199. package/dist/workspace/config.d.ts +28 -0
  200. package/dist/workspace/config.js +79 -0
  201. package/dist/workspace/discover.d.ts +34 -0
  202. package/dist/workspace/discover.js +195 -0
  203. package/dist/workspace/documents.d.ts +21 -0
  204. package/dist/workspace/documents.js +395 -0
  205. package/dist/workspace/ignore.d.ts +10 -0
  206. package/dist/workspace/ignore.js +48 -0
  207. package/dist/workspace/index.d.ts +7 -0
  208. package/dist/workspace/index.js +3 -0
  209. package/dist/workspace/repositories.d.ts +24 -0
  210. package/dist/workspace/repositories.js +411 -0
  211. package/dist/workspace/resolve.d.ts +24 -0
  212. package/dist/workspace/resolve.js +268 -0
  213. package/dist/workspace/types.d.ts +71 -0
  214. package/dist/workspace/types.js +1 -0
  215. package/dist/workspace/validation.d.ts +18 -0
  216. package/dist/workspace/validation.js +197 -0
  217. package/package.json +105 -4
  218. package/skills/pappra/SKILL.md +68 -0
  219. package/skills/pappra/references/hosts.md +30 -0
  220. package/skills/pappra/references/lifecycle.md +37 -0
  221. package/skills/pappra/references/operations.md +14 -0
@@ -0,0 +1,51 @@
1
+ export interface NativeItem {
2
+ [key: string]: unknown;
3
+ type?: string;
4
+ text?: string;
5
+ status?: string;
6
+ exitCode?: number;
7
+ aggregatedOutput?: string;
8
+ }
9
+ export interface NativeEntry {
10
+ [key: string]: unknown;
11
+ id?: string;
12
+ status?: string;
13
+ item?: NativeItem;
14
+ }
15
+ export interface NativeThread {
16
+ [key: string]: unknown;
17
+ id?: string;
18
+ cwd?: string;
19
+ status?: {
20
+ type: string;
21
+ };
22
+ }
23
+ export interface NativeReply {
24
+ [key: string]: unknown;
25
+ thread?: NativeThread;
26
+ turn?: NativeEntry;
27
+ data?: NativeEntry[];
28
+ }
29
+ export interface NativeParams {
30
+ [key: string]: unknown;
31
+ threadId?: string;
32
+ turn?: NativeEntry;
33
+ }
34
+ export interface NativeRecord {
35
+ id?: string | number;
36
+ method?: string;
37
+ params?: NativeParams;
38
+ result?: NativeReply;
39
+ error?: {
40
+ message?: string;
41
+ };
42
+ }
43
+ export interface NativeEvent {
44
+ seq: number;
45
+ method: string;
46
+ params: NativeParams | null;
47
+ at: string;
48
+ error?: never;
49
+ }
50
+ export declare function isReply(value: unknown): value is NativeReply;
51
+ export declare function isRecord(value: unknown): value is NativeRecord;
@@ -0,0 +1,42 @@
1
+ import { object } from '../../collab/validation.js';
2
+ const optionalString = (value) => value === undefined || typeof value === 'string';
3
+ function isEntry(value) {
4
+ const item = object(value) ? value['item'] : undefined;
5
+ return (object(value) &&
6
+ optionalString(value['id']) &&
7
+ optionalString(value['status']) &&
8
+ (value['item'] === undefined ||
9
+ (object(value['item']) &&
10
+ object(item) &&
11
+ ['type', 'text', 'status', 'aggregatedOutput'].every((key) => optionalString(item[key])) &&
12
+ (value['item']['exitCode'] === undefined ||
13
+ typeof value['item']['exitCode'] === 'number'))));
14
+ }
15
+ export function isReply(value) {
16
+ return (object(value) &&
17
+ (value['thread'] === undefined ||
18
+ (object(value['thread']) &&
19
+ optionalString(value['thread']['id']) &&
20
+ optionalString(value['thread']['cwd']) &&
21
+ (value['thread']['status'] === undefined ||
22
+ (object(value['thread']['status']) &&
23
+ typeof value['thread']['status']['type'] === 'string')))) &&
24
+ (value['turn'] === undefined || isEntry(value['turn'])) &&
25
+ (value['data'] === undefined ||
26
+ (Array.isArray(value['data']) && value['data'].every(isEntry))));
27
+ }
28
+ export function isRecord(value) {
29
+ return (object(value) &&
30
+ (value['id'] === undefined ||
31
+ typeof value['id'] === 'string' ||
32
+ typeof value['id'] === 'number') &&
33
+ optionalString(value['method']) &&
34
+ (value['params'] === undefined ||
35
+ (object(value['params']) &&
36
+ optionalString(value['params']['threadId']) &&
37
+ (value['params']['turn'] === undefined ||
38
+ isEntry(value['params']['turn'])))) &&
39
+ (value['result'] === undefined || isReply(value['result'])) &&
40
+ (value['error'] === undefined ||
41
+ (object(value['error']) && optionalString(value['error']['message']))));
42
+ }
@@ -0,0 +1,28 @@
1
+ import type { EvidenceRef } from '../schemas/evidence.js';
2
+ export type HostName = 'pi' | 'codex' | 'claude';
3
+ export interface PassiveToolObservation {
4
+ readonly name: string;
5
+ readonly command: string;
6
+ readonly executablePath: string | null;
7
+ readonly discovered: boolean;
8
+ }
9
+ export interface PassiveHostObservation {
10
+ readonly host: HostName;
11
+ readonly selected: boolean;
12
+ readonly executablePath: string | null;
13
+ readonly discovered: boolean;
14
+ readonly configPaths: readonly string[];
15
+ readonly configured: boolean;
16
+ readonly callable: 'unknown';
17
+ readonly verification: 'unsupported';
18
+ readonly evidence: readonly EvidenceRef[];
19
+ }
20
+ export interface HostDetectionOptions {
21
+ readonly homeDir: string;
22
+ readonly pathEnv: string;
23
+ readonly selectedHosts: readonly string[];
24
+ readonly observedAt?: string;
25
+ }
26
+ export declare function detectPassiveTool(command: string, pathEnv: string): PassiveToolObservation;
27
+ /** Passive host discovery. It never starts a host or reads config contents. */
28
+ export declare function detectPassiveHosts(options: HostDetectionOptions): readonly PassiveHostObservation[];
@@ -0,0 +1,73 @@
1
+ import { accessSync, constants, existsSync, statSync } from 'node:fs';
2
+ import { delimiter, join } from 'node:path';
3
+ function evidence(path, observedAt) {
4
+ return {
5
+ kind: 'file',
6
+ ref: path,
7
+ digest: null,
8
+ observedAt,
9
+ scope: 'live-owned',
10
+ };
11
+ }
12
+ function executableOnPath(command, pathEnv) {
13
+ for (const directory of pathEnv.split(delimiter)) {
14
+ if (directory === '')
15
+ continue;
16
+ const candidate = join(directory, command);
17
+ try {
18
+ const stats = statSync(candidate);
19
+ if (!stats.isFile())
20
+ continue;
21
+ accessSync(candidate, constants.X_OK);
22
+ return candidate;
23
+ }
24
+ catch {
25
+ // Continue probing the remaining PATH entries.
26
+ }
27
+ }
28
+ return null;
29
+ }
30
+ export function detectPassiveTool(command, pathEnv) {
31
+ const executablePath = executableOnPath(command, pathEnv);
32
+ return {
33
+ name: command,
34
+ command,
35
+ executablePath,
36
+ discovered: executablePath !== null,
37
+ };
38
+ }
39
+ /** Passive host discovery. It never starts a host or reads config contents. */
40
+ export function detectPassiveHosts(options) {
41
+ const observedAt = options.observedAt ?? new Date().toISOString();
42
+ const configs = {
43
+ pi: [join(options.homeDir, '.pi')],
44
+ codex: [join(options.homeDir, '.codex', 'config.toml')],
45
+ claude: [join(options.homeDir, '.claude', 'settings.json')],
46
+ };
47
+ const commands = {
48
+ pi: 'pi',
49
+ codex: 'codex',
50
+ claude: 'claude',
51
+ };
52
+ const selected = new Set(options.selectedHosts);
53
+ return ['pi', 'codex', 'claude'].map((host) => {
54
+ const executablePath = executableOnPath(commands[host], options.pathEnv);
55
+ const configPaths = configs[host].filter((path) => existsSync(path));
56
+ const evidenceRefs = [];
57
+ if (executablePath !== null)
58
+ evidenceRefs.push(evidence(executablePath, observedAt));
59
+ for (const path of configPaths)
60
+ evidenceRefs.push(evidence(path, observedAt));
61
+ return {
62
+ host,
63
+ selected: selected.has(host),
64
+ executablePath,
65
+ discovered: executablePath !== null,
66
+ configPaths,
67
+ configured: configPaths.length > 0,
68
+ callable: 'unknown',
69
+ verification: 'unsupported',
70
+ evidence: evidenceRefs,
71
+ };
72
+ });
73
+ }
@@ -0,0 +1,10 @@
1
+ export * as detect from './detect.js';
2
+ export * as codex from './codex/native.js';
3
+ export * as claude from './claude/native.js';
4
+ export * as piRpc from './pi/rpc.js';
5
+ export * as piHistory from './pi/query-result.js';
6
+ export * as pilot from './pilot.js';
7
+ export { HostAdapter, HostIntegrationService, type HostIntegrationOptions, type HostTargetOptions, } from './integration/service.js';
8
+ export { mapNativeHookEvent, readHookInput, runHostHook, type HostHookOptions, type NativeHookMapping, } from './integration/hook.js';
9
+ export { createPiExtension, type PiExtensionLike, } from './integration/pi-extension-runtime.js';
10
+ export { HOST_SUPPORT, nativeEventFor, isSupportedVersion, } from './integration/support.js';
@@ -0,0 +1,10 @@
1
+ export * as detect from './detect.js';
2
+ export * as codex from './codex/native.js';
3
+ export * as claude from './claude/native.js';
4
+ export * as piRpc from './pi/rpc.js';
5
+ export * as piHistory from './pi/query-result.js';
6
+ export * as pilot from './pilot.js';
7
+ export { HostAdapter, HostIntegrationService, } from './integration/service.js';
8
+ export { mapNativeHookEvent, readHookInput, runHostHook, } from './integration/hook.js';
9
+ export { createPiExtension, } from './integration/pi-extension-runtime.js';
10
+ export { HOST_SUPPORT, nativeEventFor, isSupportedVersion, } from './integration/support.js';
@@ -0,0 +1,16 @@
1
+ import type { HostBundle, HostName } from '../../schemas/host.js';
2
+ export declare const SHARED_SKILL_ID = "pappra";
3
+ export interface HostBundlePayload {
4
+ readonly bundle: HostBundle;
5
+ readonly contents: ReadonlyMap<string, Buffer>;
6
+ }
7
+ export declare function installPathForBundleFile(host: HostName, sourcePath: string): string;
8
+ /**
9
+ * Loads the complete shared skill plus the generated host template. The
10
+ * digest covers every packaged support file and the exact native template.
11
+ */
12
+ export declare function loadHostBundle(host: HostName, packageVersion: string, options?: {
13
+ readonly assetsRoot?: string;
14
+ readonly runtimeEntry?: string;
15
+ readonly observedAt?: string;
16
+ }): Promise<HostBundlePayload>;
@@ -0,0 +1,81 @@
1
+ import { readFile, readdir } from 'node:fs/promises';
2
+ import { dirname, join, relative, sep } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { HOST_BUNDLE_SCHEMA } from '../../schemas/host.js';
5
+ import { sha256Digest } from '../../infra/fs.js';
6
+ import { renderHostTemplate } from './templates.js';
7
+ export const SHARED_SKILL_ID = 'pappra';
8
+ function packageRoot() {
9
+ return dirname(fileURLToPath(import.meta.resolve('@phyzess/pappra/package.json')));
10
+ }
11
+ async function listFiles(directory) {
12
+ const entries = await readdir(directory, { withFileTypes: true });
13
+ const files = [];
14
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
15
+ const path = join(directory, entry.name);
16
+ if (entry.isDirectory())
17
+ files.push(...(await listFiles(path)));
18
+ else if (entry.isFile())
19
+ files.push(path);
20
+ }
21
+ return files;
22
+ }
23
+ function portableRelative(root, path) {
24
+ return relative(root, path).split(sep).join('/');
25
+ }
26
+ export function installPathForBundleFile(host, sourcePath) {
27
+ if (host === 'pi' && sourcePath === 'templates/pi/entry.ts') {
28
+ return 'extensions/pappra/index.ts';
29
+ }
30
+ return sourcePath;
31
+ }
32
+ /**
33
+ * Loads the complete shared skill plus the generated host template. The
34
+ * digest covers every packaged support file and the exact native template.
35
+ */
36
+ export async function loadHostBundle(host, packageVersion, options = {}) {
37
+ const root = options.assetsRoot ?? join(packageRoot(), 'assets', 'integrations');
38
+ const skillRoot = join(root, 'skills', SHARED_SKILL_ID);
39
+ const paths = await listFiles(skillRoot);
40
+ if (paths.length === 0) {
41
+ throw new Error(`No packaged skill files found in ${skillRoot}`);
42
+ }
43
+ const contents = new Map();
44
+ const files = [];
45
+ for (const path of paths) {
46
+ const source = portableRelative(root, path);
47
+ if (source.includes('..') || source.startsWith('/')) {
48
+ throw new Error(`Unsafe packaged skill path: ${source}`);
49
+ }
50
+ const bytes = await readFile(path);
51
+ contents.set(source, bytes);
52
+ files.push({
53
+ path: source,
54
+ digest: sha256Digest(bytes),
55
+ kind: source.endsWith('/SKILL.md') ? 'skill' : 'reference',
56
+ });
57
+ }
58
+ const template = renderHostTemplate(host, options.runtimeEntry ??
59
+ join(packageRoot(), 'dist/hosts/integration/hook-entry.js'));
60
+ contents.set(template.path, template.bytes);
61
+ files.push({
62
+ path: template.path,
63
+ digest: sha256Digest(template.bytes),
64
+ kind: template.kind,
65
+ });
66
+ files.sort((left, right) => left.path.localeCompare(right.path));
67
+ const bundleDigest = sha256Digest(files.map((file) => `${file.path}\0${file.digest}`).join('\n'));
68
+ const observedAt = options.observedAt ?? new Date().toISOString();
69
+ return {
70
+ bundle: {
71
+ schema: HOST_BUNDLE_SCHEMA,
72
+ host,
73
+ skillId: SHARED_SKILL_ID,
74
+ packageVersion,
75
+ bundleDigest,
76
+ files,
77
+ observedAt,
78
+ },
79
+ contents,
80
+ };
81
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ import { mapNativeHookEvent, readHookInput, renderHostHookOutput, runHostHook, } from './hook.js';
2
+ import { hostNameSchema } from '../../schemas/host.js';
3
+ import { formatDiagnostic } from '../../schemas/diagnostics.js';
4
+ const EVENTS = [
5
+ 'startup',
6
+ 'resume',
7
+ 'compact',
8
+ 'checkpoint-request',
9
+ ];
10
+ function parseEvent(value) {
11
+ return EVENTS.find((event) => event === value) ?? 'startup';
12
+ }
13
+ function parseHost(value) {
14
+ const parsed = hostNameSchema.safeParse(value);
15
+ return parsed.success ? parsed.data : null;
16
+ }
17
+ function writeError(message) {
18
+ process.stderr.write(`pappra hook: ${message}\n`);
19
+ }
20
+ /**
21
+ * Command-hook entrypoint. It reads one bounded event, records only a local
22
+ * observation, and never writes a checkpoint or starts work.
23
+ */
24
+ async function main() {
25
+ const host = parseHost(process.argv[2]);
26
+ if (host === null) {
27
+ writeError('unknown host');
28
+ return 1;
29
+ }
30
+ const fallbackEvent = parseEvent(process.argv[3]);
31
+ const input = await readHookInput(process.stdin, 1024 * 1024, 1000);
32
+ if (!input.ok) {
33
+ if (typeof process.stdin.destroy === 'function')
34
+ process.stdin.destroy();
35
+ writeError(formatDiagnostic(input.diagnostic));
36
+ process.exit(1);
37
+ }
38
+ let raw;
39
+ try {
40
+ raw = JSON.parse(input.bytes.toString('utf8'));
41
+ }
42
+ catch {
43
+ writeError('native hook input is not JSON');
44
+ return 1;
45
+ }
46
+ const mapped = mapNativeHookEvent(host, raw, fallbackEvent, process.cwd());
47
+ if (!mapped.ok) {
48
+ writeError(formatDiagnostic(mapped.diagnostic));
49
+ return mapped.event === null ? 1 : 0;
50
+ }
51
+ const result = await runHostHook(mapped.input);
52
+ if (!result.ok) {
53
+ for (const entry of result.diagnostics) {
54
+ if (typeof entry === 'object' && entry !== null && 'message' in entry) {
55
+ const message = entry['message'];
56
+ if (typeof message === 'string')
57
+ writeError(message);
58
+ }
59
+ }
60
+ return result.status === 'invalid' || result.status === 'failed' ? 1 : 0;
61
+ }
62
+ const advisory = renderHostHookOutput(host, result);
63
+ if (advisory !== null)
64
+ process.stdout.write(advisory);
65
+ return 0;
66
+ }
67
+ const code = await main();
68
+ process.exitCode = code;
@@ -0,0 +1,32 @@
1
+ import type { Diagnostic } from '../../schemas/diagnostics.js';
2
+ import type { HostHookInput, HostHookResult, HostLifecycleEvent, HostName } from '../../schemas/host.js';
3
+ export declare function renderHostHookOutput(host: HostName, result: HostHookResult): string | null;
4
+ export type NativeHookMapping = {
5
+ readonly ok: true;
6
+ readonly input: HostHookInput;
7
+ } | {
8
+ readonly ok: false;
9
+ readonly diagnostic: Diagnostic;
10
+ readonly host: HostName;
11
+ readonly event: HostLifecycleEvent | null;
12
+ };
13
+ export declare function mapNativeHookEvent(host: HostName, raw: unknown, _fallbackEvent: HostLifecycleEvent, _fallbackCwd: string | null): NativeHookMapping;
14
+ export interface HostHookOptions {
15
+ readonly deadlineMs?: number;
16
+ readonly observedAt?: string;
17
+ /** Test seam; production uses the monotonic wall clock. */
18
+ readonly now?: () => number;
19
+ }
20
+ /**
21
+ * Records one bounded local lifecycle observation. It never reads transcripts,
22
+ * starts a host/model/network/index, writes a checkpoint, or continues a run.
23
+ */
24
+ export declare function runHostHook(input: HostHookInput, options?: HostHookOptions): Promise<HostHookResult>;
25
+ export declare function readHookInput(stream: AsyncIterable<Uint8Array | string>, maxBytes?: number, deadlineMs?: number): Promise<{
26
+ readonly ok: true;
27
+ readonly bytes: Buffer;
28
+ } | {
29
+ readonly ok: false;
30
+ readonly diagnostic: Diagnostic;
31
+ }>;
32
+ export declare function readBoundedHookFile(path: string): Promise<Buffer | null>;