@phyzess/pappra 0.1.0-beta.6 → 0.1.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,7 +63,7 @@ requires an explicit controller. They are not needed for the core workflow above
63
63
  - **Knowledge:** workspace and private document catalogs with explicit preview/apply operations.
64
64
  - **Continuity:** private task checkpoints and bounded advisory resume packets. Resume never grants execution by itself.
65
65
  - **Code intelligence:** status, explicit preparation and bounded query for selected repositories. Text/structure fallback is available when the optional graph engine is absent.
66
- - **Host integrations:** optional Pi, Codex and Claude Code adapters that merge Pappra-owned entries without taking over user configuration. Hooks are copied as a dependency-free bundle into the host root; `host bundle --marketplace` exports a local marketplace that Claude Code and Codex accept, without registering it. `host install --mode plugin` fails closed with per-host evidence; see [host plugin validation](docs/host-plugin-validation.md).
66
+ - **Host integrations:** optional Pi, Codex and Claude Code adapters that merge Pappra-owned entries without taking over user configuration. Hooks are copied as a dependency-free bundle into the host root; `host bundle --marketplace` exports a local marketplace that Claude Code and Codex accept, without registering it. Plugin hooks are verified for Pi 1.3.0 and Claude Code 2.1.292 (startup); Codex 0.163.0 loads hooks only from user scope, so use `host install --host codex --scope user` (merges `$CODEX_HOME/hooks.json`). `host bundle` exports a plugin bundle for the other hosts and `host install --mode plugin` fails closed with the per-host commands. See [host plugin validation](docs/host-plugin-validation.md).
67
67
  - **Bounded collaboration:** an optional, explicitly controlled one-request workflow. It is not a scheduler or daemon.
68
68
 
69
69
  ## Optional integrations and support
@@ -87,7 +87,7 @@ The check runs the source policy, type checks, formatting, deterministic tests a
87
87
 
88
88
  ## Status and limits
89
89
 
90
- Pappra is development software (`0.1.0-beta.6`) published as `@phyzess/pappra` under the `beta` dist-tag. Releases are published from GitHub Actions with npm trusted publishing (OIDC), so no npm token is stored in this repository. Pin either the moving `beta` tag or an exact version such as `@phyzess/pappra@0.1.0-beta.6`, because pre-release versions are never republished.
90
+ Pappra is development software (`0.1.0-beta.8`) published as `@phyzess/pappra` under the `beta` dist-tag. Releases are published from GitHub Actions with npm trusted publishing (OIDC), so no npm token is stored in this repository. Pin either the moving `beta` tag or an exact version such as `@phyzess/pappra@0.1.0-beta.8`, because pre-release versions are never republished.
91
91
 
92
92
  The deterministic suite covers synthetic workspaces, fixtures and local process boundaries. It does not establish real-model behavior, native host callability on every platform, CodeGraph completeness, Windows/Linux parity or production performance. A host adapter version pin is an observed support claim, not proof that a particular native installation will load or run correctly.
93
93
 
@@ -12,5 +12,6 @@ export interface HostCommandOptions {
12
12
  readonly out: string | null;
13
13
  readonly mode: 'project-settings' | 'plugin' | null;
14
14
  readonly marketplace: boolean;
15
+ readonly scope: 'project' | 'user' | null;
15
16
  }
16
17
  export declare function runHostCommand(options: HostCommandOptions, output: CliOutput): Promise<number>;
@@ -1,5 +1,5 @@
1
1
  import { homedir } from 'node:os';
2
- import { resolve } from 'node:path';
2
+ import { join, resolve } from 'node:path';
3
3
  import { HostIntegrationService } from '../../hosts/integration/service.js';
4
4
  import { exportHostPluginBundle } from '../../hosts/integration/bundle-export.js';
5
5
  import { HOST_SUPPORT, isSupportedVersion, } from '../../hosts/integration/support.js';
@@ -10,6 +10,13 @@ export const hostUsage = 'pappra host <detect|describe|plan|install|verify|bundl
10
10
  '\n' +
11
11
  'bundle exports a host-native plugin/package directory and never registers it or\n' +
12
12
  'claims host callability.';
13
+ /** Codex only loads lifecycle hooks from its user configuration directory. */
14
+ function codexHome() {
15
+ const configured = process.env['CODEX_HOME'];
16
+ return configured === undefined || configured === ''
17
+ ? join(homedir(), '.codex')
18
+ : resolve(configured);
19
+ }
13
20
  function printDiagnostics(output, diagnostics) {
14
21
  for (const entry of diagnostics) {
15
22
  if (typeof entry === 'object' &&
@@ -46,9 +53,27 @@ export async function runHostCommand(options, output) {
46
53
  hostVersion: options.hostVersion,
47
54
  observedAt: new Date().toISOString(),
48
55
  });
49
- const hostRoot = options.hostRoot === null
50
- ? undefined
51
- : resolve(options.cwd, options.hostRoot);
56
+ if (options.scope === 'user') {
57
+ if (HOST_SUPPORT[options.host].hookScope !== 'user') {
58
+ output.stderr(`pappra host: ${options.host} loads hooks from ${HOST_SUPPORT[options.host].hookScope} scope; --scope user is only valid for a user-scoped host (codex).\n`);
59
+ return 2;
60
+ }
61
+ if (options.hostRoot !== null) {
62
+ output.stderr('pappra host: --scope user resolves CODEX_HOME and cannot be combined with --host-root.\n');
63
+ return 2;
64
+ }
65
+ if (options.action !== 'plan' &&
66
+ options.action !== 'install' &&
67
+ options.action !== 'verify') {
68
+ output.stderr('pappra host: --scope user is only valid for plan, install, or verify.\n');
69
+ return 2;
70
+ }
71
+ }
72
+ const hostRoot = options.scope === 'user'
73
+ ? codexHome()
74
+ : options.hostRoot === null
75
+ ? undefined
76
+ : resolve(options.cwd, options.hostRoot);
52
77
  const target = {
53
78
  ...(hostRoot === undefined ? {} : { hostRoot }),
54
79
  ...(options.hostVersion === null
package/dist/cli/run.js CHANGED
@@ -26,6 +26,7 @@ function parseOptions(args) {
26
26
  out: { type: 'string' },
27
27
  mode: { type: 'string' },
28
28
  marketplace: { type: 'boolean' },
29
+ scope: { type: 'string' },
29
30
  bind: { type: 'boolean' },
30
31
  force: { type: 'boolean' },
31
32
  'no-interactive': { type: 'boolean' },
@@ -251,6 +252,16 @@ export async function runCli(args, output) {
251
252
  failure(output, json, 'invalid_arguments', '--out is only valid for host.');
252
253
  return 2;
253
254
  }
255
+ if (values.scope !== undefined &&
256
+ values.scope !== 'project' &&
257
+ values.scope !== 'user') {
258
+ failure(output, json, 'invalid_arguments', '--scope accepts project or user.');
259
+ return 2;
260
+ }
261
+ if (command !== 'host' && values.scope !== undefined) {
262
+ failure(output, json, 'invalid_arguments', '--scope is only valid for host.');
263
+ return 2;
264
+ }
254
265
  if (command !== 'host' && values.marketplace === true) {
255
266
  failure(output, json, 'invalid_arguments', '--marketplace is only valid for host bundle.');
256
267
  return 2;
@@ -665,6 +676,10 @@ export async function runCli(args, output) {
665
676
  (values.apply === true && action !== 'install') ||
666
677
  (values.mode !== undefined && action !== 'install') ||
667
678
  (values.marketplace === true && action !== 'bundle') ||
679
+ (values.scope !== undefined &&
680
+ action !== 'plan' &&
681
+ action !== 'install' &&
682
+ action !== 'verify') ||
668
683
  (values['host-root'] !== undefined && action === null) ||
669
684
  (values['host-version'] !== undefined && action === null);
670
685
  if (values.help) {
@@ -693,6 +708,9 @@ export async function runCli(args, output) {
693
708
  ? values.mode
694
709
  : null,
695
710
  marketplace: values.marketplace === true,
711
+ scope: values.scope === 'project' || values.scope === 'user'
712
+ ? values.scope
713
+ : null,
696
714
  }, output);
697
715
  }
698
716
  if (command !== null) {
@@ -23933,6 +23933,7 @@ var HOST_SUPPORT = {
23933
23933
  pi: {
23934
23934
  surface: "pi-extension",
23935
23935
  version: "1.3.0",
23936
+ hookScope: "project",
23936
23937
  events: {
23937
23938
  startup: "session_start",
23938
23939
  resume: "session_start",
@@ -23943,6 +23944,9 @@ var HOST_SUPPORT = {
23943
23944
  codex: {
23944
23945
  surface: "codex-hooks",
23945
23946
  version: "0.163.0",
23947
+ // Codex 0.163.0 loads lifecycle hooks only from its user configuration
23948
+ // directory (CODEX_HOME); project `.codex/hooks.json` is not loaded.
23949
+ hookScope: "user",
23946
23950
  events: {
23947
23951
  startup: "SessionStart",
23948
23952
  resume: "SessionStart",
@@ -23953,6 +23957,7 @@ var HOST_SUPPORT = {
23953
23957
  claude: {
23954
23958
  surface: "claude-hooks",
23955
23959
  version: "2.1.292",
23960
+ hookScope: "project",
23956
23961
  events: {
23957
23962
  startup: "SessionStart",
23958
23963
  resume: "SessionStart",
@@ -23938,6 +23938,7 @@ var HOST_SUPPORT = {
23938
23938
  pi: {
23939
23939
  surface: "pi-extension",
23940
23940
  version: "1.3.0",
23941
+ hookScope: "project",
23941
23942
  events: {
23942
23943
  startup: "session_start",
23943
23944
  resume: "session_start",
@@ -23948,6 +23949,9 @@ var HOST_SUPPORT = {
23948
23949
  codex: {
23949
23950
  surface: "codex-hooks",
23950
23951
  version: "0.163.0",
23952
+ // Codex 0.163.0 loads lifecycle hooks only from its user configuration
23953
+ // directory (CODEX_HOME); project `.codex/hooks.json` is not loaded.
23954
+ hookScope: "user",
23951
23955
  events: {
23952
23956
  startup: "SessionStart",
23953
23957
  resume: "SessionStart",
@@ -23958,6 +23962,7 @@ var HOST_SUPPORT = {
23958
23962
  claude: {
23959
23963
  surface: "claude-hooks",
23960
23964
  version: "2.1.292",
23965
+ hookScope: "project",
23961
23966
  events: {
23962
23967
  startup: "SessionStart",
23963
23968
  resume: "SessionStart",
@@ -886,11 +886,13 @@ export class HostIntegrationService {
886
886
  async install(host, options) {
887
887
  if (options.mode === 'plugin') {
888
888
  const support = HOST_PLUGIN_HOOKS[host];
889
- const guidance = support.state === 'verified'
890
- ? 'Export the package with `pappra host bundle --host pi --out <dir> --json`, then run `pi install <dir> -l -a`; project-settings install is still available with --mode project-settings.'
891
- : support.state === 'unsupported'
892
- ? 'This host does not load plugin hooks at its pinned version; keep --mode project-settings. See docs/host-plugin-validation.md.'
893
- : 'Plugin hooks are not verified for this host yet; an authenticated lifecycle session is required. Use --mode project-settings meanwhile.';
889
+ const guidance = host === 'claude' && support.state === 'verified'
890
+ ? 'Export with `pappra host bundle --host claude --out <dir> --marketplace --json`, then run `claude plugin marketplace add <dir>` and `claude plugin install pappra@pappra-local`. Use --mode project-settings for automatic workspace wiring.'
891
+ : host === 'pi' && support.state === 'verified'
892
+ ? 'Export with `pappra host bundle --host pi --out <dir> --json`, then run `pi install <dir> -l -a`. Use --mode project-settings for automatic workspace wiring.'
893
+ : support.state === 'unsupported'
894
+ ? 'This host does not load plugin hooks at its pinned version; keep --mode project-settings. See docs/host-plugin-validation.md.'
895
+ : 'Plugin hooks are not verified for this host yet; an authenticated lifecycle session is required. Use --mode project-settings meanwhile.';
894
896
  return hostIntegrationResultSchema.parse({
895
897
  schema: 'pappra/host-integration/1',
896
898
  ok: false,
@@ -3,6 +3,7 @@ export declare const HOST_SUPPORT: {
3
3
  readonly pi: {
4
4
  readonly surface: 'pi-extension';
5
5
  readonly version: '1.3.0';
6
+ readonly hookScope: 'project';
6
7
  readonly events: {
7
8
  readonly startup: 'session_start';
8
9
  readonly resume: 'session_start';
@@ -13,6 +14,7 @@ export declare const HOST_SUPPORT: {
13
14
  readonly codex: {
14
15
  readonly surface: 'codex-hooks';
15
16
  readonly version: '0.163.0';
17
+ readonly hookScope: 'user';
16
18
  readonly events: {
17
19
  readonly startup: 'SessionStart';
18
20
  readonly resume: 'SessionStart';
@@ -23,6 +25,7 @@ export declare const HOST_SUPPORT: {
23
25
  readonly claude: {
24
26
  readonly surface: 'claude-hooks';
25
27
  readonly version: '2.1.292';
28
+ readonly hookScope: 'project';
26
29
  readonly events: {
27
30
  readonly startup: 'SessionStart';
28
31
  readonly resume: 'SessionStart';
@@ -50,7 +53,7 @@ export declare const HOST_PLUGIN_HOOKS: {
50
53
  readonly evidence: 'Codex 0.163.0 does not load plugin hooks: a control hook in CODEX_HOME fired during `codex exec --dangerously-bypass-hook-trust` while the bundled plugin hook never ran, including with a .codex-plugin/plugin.json copy.';
51
54
  };
52
55
  readonly claude: {
53
- readonly state: 'unverified';
54
- readonly evidence: 'Claude Code 2.1.292 validation passes, but no authenticated session was available to run a lifecycle event.';
56
+ readonly state: 'verified';
57
+ readonly evidence: 'Claude Code 2.1.292 passed `claude plugin validate`, then a project-scope marketplace install and `claude -p` wrote a SessionStart event from ${CLAUDE_PLUGIN_ROOT}/hooks/pappra-hook.mjs.';
55
58
  };
56
59
  };
@@ -2,6 +2,7 @@ export const HOST_SUPPORT = {
2
2
  pi: {
3
3
  surface: 'pi-extension',
4
4
  version: '1.3.0',
5
+ hookScope: 'project',
5
6
  events: {
6
7
  startup: 'session_start',
7
8
  resume: 'session_start',
@@ -12,6 +13,9 @@ export const HOST_SUPPORT = {
12
13
  codex: {
13
14
  surface: 'codex-hooks',
14
15
  version: '0.163.0',
16
+ // Codex 0.163.0 loads lifecycle hooks only from its user configuration
17
+ // directory (CODEX_HOME); project `.codex/hooks.json` is not loaded.
18
+ hookScope: 'user',
15
19
  events: {
16
20
  startup: 'SessionStart',
17
21
  resume: 'SessionStart',
@@ -22,6 +26,7 @@ export const HOST_SUPPORT = {
22
26
  claude: {
23
27
  surface: 'claude-hooks',
24
28
  version: '2.1.292',
29
+ hookScope: 'project',
25
30
  events: {
26
31
  startup: 'SessionStart',
27
32
  resume: 'SessionStart',
@@ -55,7 +60,7 @@ export const HOST_PLUGIN_HOOKS = {
55
60
  evidence: 'Codex 0.163.0 does not load plugin hooks: a control hook in CODEX_HOME fired during `codex exec --dangerously-bypass-hook-trust` while the bundled plugin hook never ran, including with a .codex-plugin/plugin.json copy.',
56
61
  },
57
62
  claude: {
58
- state: 'unverified',
59
- evidence: 'Claude Code 2.1.292 validation passes, but no authenticated session was available to run a lifecycle event.',
63
+ state: 'verified',
64
+ evidence: 'Claude Code 2.1.292 passed `claude plugin validate`, then a project-scope marketplace install and `claude -p` wrote a SessionStart event from ${CLAUDE_PLUGIN_ROOT}/hooks/pappra-hook.mjs.',
60
65
  },
61
66
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phyzess/pappra",
3
- "version": "0.1.0-beta.6",
3
+ "version": "0.1.0-beta.8",
4
4
  "description": "Portable workspace knowledge and continuity for coding agents.",
5
5
  "publishConfig": {
6
6
  "access": "public"