@principles/pd-cli 1.85.0 → 1.87.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"config-doctor.d.ts","sourceRoot":"","sources":["../../src/commands/config-doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,UAAU,aAAa;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAuGD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC3E"}
1
+ {"version":3,"file":"config-doctor.d.ts","sourceRoot":"","sources":["../../src/commands/config-doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,UAAU,aAAa;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAsHD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC3E"}
@@ -11,12 +11,29 @@
11
11
  import * as path from 'path';
12
12
  import { resolveWorkspaceDir } from '../resolve-workspace.js';
13
13
  import { buildDoctorOutput } from '../services/config-doctor.js';
14
+ import { discoverWorkspaceDefault } from '../services/pd-config-loader.js';
14
15
  function formatTextOutput(output) {
15
16
  const lines = [];
16
17
  const statusIcon = output.status === 'ok' ? '✓' : output.status === 'degraded' ? '⚠' : '✗';
17
18
  lines.push('PD Config Doctor');
18
19
  lines.push(`status: ${statusIcon} ${output.status.toUpperCase()}`);
19
20
  lines.push(`workspace: ${output.workspaceDir}`);
21
+ // Workspace discovery info
22
+ const discovery = discoverWorkspaceDefault();
23
+ if (discovery) {
24
+ lines.push(`workspace.default: ${discovery.workspaceDefault} (source: ${discovery.source})`);
25
+ const normalizedResolved = output.workspaceDir.replace(/\\/g, '/').replace(/\/$/, '');
26
+ const normalizedDefault = discovery.workspaceDefault.replace(/\\/g, '/').replace(/\/$/, '');
27
+ if (normalizedResolved !== normalizedDefault) {
28
+ lines.push(` ⚠ RESOLVED path differs from workspace.default!`);
29
+ }
30
+ else {
31
+ lines.push(` ✓ resolved path matches workspace.default`);
32
+ }
33
+ }
34
+ else {
35
+ lines.push('workspace.default: (not configured — add workspace.default to .pd/config.yaml)');
36
+ }
20
37
  lines.push('');
21
38
  lines.push('PD config paths:');
22
39
  for (const [k, v] of Object.entries(output.pdConfigPaths)) {
@@ -1 +1 @@
1
- {"version":3,"file":"config-doctor.js","sourceRoot":"","sources":["../../src/commands/config-doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAqB,MAAM,8BAA8B,CAAC;AAOpF,SAAS,gBAAgB,CAAC,MAAoB;IAC5C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAE3F,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,KAAK,CAAC,IAAI,CAAC,WAAW,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACnE,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IAChD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC;QACnD,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9B,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAChE,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC;QACnD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,yBAAyB,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,2BAA2B,MAAM,CAAC,YAAY,CAAC,kBAAkB,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5J,IAAI,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjD,KAAK,CAAC,IAAI,CAAC,eAAe,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,eAAe,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IACjD,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YAChD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;YAC5D,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,MAAM,SAAS,MAAM,YAAY,GAAG,CAAC,CAAC;YAChE,KAAK,CAAC,IAAI,CAAC,qBAAqB,KAAK,CAAC,mBAAmB,KAAK,KAAK,CAAC,gBAAgB,GAAG,CAAC,CAAC;YACzF,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACpB,MAAM,WAAW,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAC/D,KAAK,CAAC,IAAI,CAAC,qBAAqB,KAAK,CAAC,SAAS,KAAK,WAAW,GAAG,CAAC,CAAC;YACtE,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,qBAAqB,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;YAChD,KAAK,CAAC,IAAI,CAAC,qBAAqB,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC/B,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC5C,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YACtC,MAAM,GAAG,GAAG,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC;YAC3C,MAAM,WAAW,GAAG,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC3D,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,oBAAoB,SAAS,KAAK,WAAW,GAAG,CAAC,CAAC;YAC7D,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,KAAK,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAC/D,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC3B,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAChC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC3B,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAmB;IAC1D,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC;IACvF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG;YACb,MAAM,EAAE,QAAiB;YACzB,MAAM,EAAE,6BAA6B;YACrC,OAAO;YACP,WAAW,EAAE,CAAC,kFAAkF,CAAC;SAClG,CAAC;QACF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC;IAEzD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,gDAAgD;QAChD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;SAAM,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACxC,uEAAuE;QACvE,OAAO,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACtF,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"config-doctor.js","sourceRoot":"","sources":["../../src/commands/config-doctor.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,24 @@
1
1
  /**
2
2
  * Resolve the active workspace directory.
3
3
  *
4
- * Requires explicit input no silent cwd fallback.
5
- * In openclaw-plugin, this will resolve via plugin config/env vars.
4
+ * Resolution chain (highest priority first):
5
+ * 1. Explicit --workspace flag
6
+ * 2. PD_WORKSPACE_DIR environment variable
7
+ * 3. workspace.default from discovered .pd/config.yaml
8
+ * 4. Throw Error (preserve current behavior for unconfigured setups)
9
+ *
10
+ * When the resolved path differs from workspace.default in config,
11
+ * a warning is emitted to stderr.
6
12
  *
7
13
  * @throws Error if no workspace directory can be determined.
8
14
  */
9
- export declare function resolveWorkspaceDir(workspaceDir?: string): string;
10
15
  /** Environment variable name for workspace directory. */
11
16
  export declare const WORKSPACE_ENV = "PD_WORKSPACE_DIR";
17
+ /**
18
+ * Resolve the active workspace directory.
19
+ *
20
+ * API surface: `(workspaceDir?: string): string`
21
+ * All 13 command files and 20 test mocks rely on this exact signature.
22
+ */
23
+ export declare function resolveWorkspaceDir(workspaceDir?: string): string;
12
24
  //# sourceMappingURL=resolve-workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-workspace.d.ts","sourceRoot":"","sources":["../src/resolve-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAQjE;AAED,yDAAyD;AACzD,eAAO,MAAM,aAAa,qBAAqB,CAAC"}
1
+ {"version":3,"file":"resolve-workspace.d.ts","sourceRoot":"","sources":["../src/resolve-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,yDAAyD;AACzD,eAAO,MAAM,aAAa,qBAAqB,CAAC;AAgBhD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAwCjE"}
@@ -1,20 +1,66 @@
1
1
  /**
2
2
  * Resolve the active workspace directory.
3
3
  *
4
- * Requires explicit input no silent cwd fallback.
5
- * In openclaw-plugin, this will resolve via plugin config/env vars.
4
+ * Resolution chain (highest priority first):
5
+ * 1. Explicit --workspace flag
6
+ * 2. PD_WORKSPACE_DIR environment variable
7
+ * 3. workspace.default from discovered .pd/config.yaml
8
+ * 4. Throw Error (preserve current behavior for unconfigured setups)
9
+ *
10
+ * When the resolved path differs from workspace.default in config,
11
+ * a warning is emitted to stderr.
6
12
  *
7
13
  * @throws Error if no workspace directory can be determined.
8
14
  */
15
+ import * as path from 'path';
16
+ import { discoverWorkspaceDefault } from './services/pd-config-loader.js';
17
+ /** Environment variable name for workspace directory. */
18
+ export const WORKSPACE_ENV = 'PD_WORKSPACE_DIR';
19
+ // ── Internal helpers ────────────────────────────────────────────────────────
20
+ /** Normalize path to forward slashes for cross-platform comparison. */
21
+ function normalizePath(p) {
22
+ return path.resolve(p).replace(/\\/g, '/');
23
+ }
24
+ /** Emit workspace warnings to stderr. */
25
+ function emitWarning(msg) {
26
+ process.stderr.write(`[PD:workspace] WARNING: ${msg}\n`);
27
+ }
28
+ // ── Public API ──────────────────────────────────────────────────────────────
29
+ /**
30
+ * Resolve the active workspace directory.
31
+ *
32
+ * API surface: `(workspaceDir?: string): string`
33
+ * All 13 command files and 20 test mocks rely on this exact signature.
34
+ */
9
35
  export function resolveWorkspaceDir(workspaceDir) {
10
- if (workspaceDir)
36
+ // Step 1: Discover config-based default (runs always for warning comparison)
37
+ const discovered = discoverWorkspaceDefault();
38
+ const configDefault = discovered?.workspaceDefault;
39
+ // Step 2: Check --workspace flag (highest priority)
40
+ if (workspaceDir) {
41
+ if (configDefault && normalizePath(workspaceDir) !== normalizePath(configDefault)) {
42
+ emitWarning(`--workspace "${workspaceDir}" differs from config default "${configDefault}" ` +
43
+ `(source: ${discovered.configPath}). Using explicit flag. ` +
44
+ `Consider updating workspace.default in config.`);
45
+ }
11
46
  return workspaceDir;
12
- const envWorkspace = process.env.PD_WORKSPACE_DIR;
13
- if (envWorkspace)
47
+ }
48
+ // Step 3: Check PD_WORKSPACE_DIR env var
49
+ const envWorkspace = process.env.PD_WORKSPACE_DIR?.trim();
50
+ if (envWorkspace) {
51
+ if (configDefault && normalizePath(envWorkspace) !== normalizePath(configDefault)) {
52
+ emitWarning(`PD_WORKSPACE_DIR "${envWorkspace}" differs from config default "${configDefault}" ` +
53
+ `(source: ${discovered.configPath}). Using env var. ` +
54
+ `Consider aligning or updating workspace.default.`);
55
+ }
14
56
  return envWorkspace;
57
+ }
58
+ // Step 4: Use discovered config default
59
+ if (configDefault) {
60
+ return configDefault;
61
+ }
62
+ // Step 5: No resolution possible — throw (preserves current behavior)
15
63
  throw new Error('No workspace directory configured. Set --workspace <path>, ' +
16
- 'PD_WORKSPACE_DIR environment variable, or run from within an initialized workspace.');
64
+ 'PD_WORKSPACE_DIR environment variable, or add workspace.default to .pd/config.yaml.');
17
65
  }
18
- /** Environment variable name for workspace directory. */
19
- export const WORKSPACE_ENV = 'PD_WORKSPACE_DIR';
20
66
  //# sourceMappingURL=resolve-workspace.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-workspace.js","sourceRoot":"","sources":["../src/resolve-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAqB;IACvD,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAClD,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,MAAM,IAAI,KAAK,CACb,6DAA6D;QAC7D,qFAAqF,CACtF,CAAC;AACJ,CAAC;AAED,yDAAyD;AACzD,MAAM,CAAC,MAAM,aAAa,GAAG,kBAAkB,CAAC"}
1
+ {"version":3,"file":"resolve-workspace.js","sourceRoot":"","sources":["../src/resolve-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,yDAAyD;AACzD,MAAM,CAAC,MAAM,aAAa,GAAG,kBAAkB,CAAC;AAEhD,+EAA+E;AAE/E,uEAAuE;AACvE,SAAS,aAAa,CAAC,CAAS;IAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,yCAAyC;AACzC,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,GAAG,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED,+EAA+E;AAE/E;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAqB;IACvD,6EAA6E;IAC7E,MAAM,UAAU,GAAG,wBAAwB,EAAE,CAAC;IAC9C,MAAM,aAAa,GAAG,UAAU,EAAE,gBAAgB,CAAC;IAEnD,oDAAoD;IACpD,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,aAAa,IAAI,aAAa,CAAC,YAAY,CAAC,KAAK,aAAa,CAAC,aAAa,CAAC,EAAE,CAAC;YAClF,WAAW,CACT,gBAAgB,YAAY,kCAAkC,aAAa,IAAI;gBAC/E,YAAY,UAAU,CAAC,UAAU,0BAA0B;gBAC3D,gDAAgD,CACjD,CAAC;QACJ,CAAC;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,yCAAyC;IACzC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAC;IAC1D,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,aAAa,IAAI,aAAa,CAAC,YAAY,CAAC,KAAK,aAAa,CAAC,aAAa,CAAC,EAAE,CAAC;YAClF,WAAW,CACT,qBAAqB,YAAY,kCAAkC,aAAa,IAAI;gBACpF,YAAY,UAAU,CAAC,UAAU,oBAAoB;gBACrD,kDAAkD,CACnD,CAAC;QACJ,CAAC;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,wCAAwC;IACxC,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,sEAAsE;IACtE,MAAM,IAAI,KAAK,CACb,6DAA6D;QAC7D,qFAAqF,CACtF,CAAC;AACJ,CAAC"}
@@ -61,4 +61,20 @@ export declare function computeFlagsFromLoadResult(result: PdConfigLoadResult):
61
61
  * Never includes token/API key values or raw provider objects.
62
62
  */
63
63
  export declare function redactLoadResult(result: PdConfigLoadResult): RedactedPdConfigSummary;
64
+ /** Result of workspace.default discovery from config files. */
65
+ export interface WorkspaceDiscoveryResult {
66
+ /** The extracted workspace.default path. */
67
+ workspaceDefault: string;
68
+ /** Path to the config file that provided workspace.default. */
69
+ configPath: string;
70
+ /** How the config location was found. */
71
+ source: 'env_var' | 'openclaw_default' | 'openclaw_plugin_config';
72
+ }
73
+ /**
74
+ * Search known locations for a .pd/config.yaml that contains a workspace.default field.
75
+ * This runs BEFORE workspace resolution and does NOT require knowing the workspace dir.
76
+ *
77
+ * Returns the extracted workspace.default path, or null if not found.
78
+ */
79
+ export declare function discoverWorkspaceDefault(): WorkspaceDiscoveryResult | null;
64
80
  //# sourceMappingURL=pd-config-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pd-config-loader.d.ts","sourceRoot":"","sources":["../../src/services/pd-config-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,OAAO,KAAK,EACV,iBAAiB,EAEjB,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,6BAA6B,CAAC;AAIrC,eAAO,MAAM,aAAa,QAAQ,CAAC;AACnC,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAIhD,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,aAAa,GAAG,WAAW,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,IAAI,CAAC;IACT,SAAS,EAAE,iBAAiB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oCAAoC;IACpC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/D,4CAA4C;IAC5C,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,sCAAsC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAAG,oBAAoB,GAAG,qBAAqB,CAAC;AAI9E,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE5D;AAoBD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CA8FrE;AAID;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAGzF;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,uBAAuB,CAGpF"}
1
+ {"version":3,"file":"pd-config-loader.d.ts","sourceRoot":"","sources":["../../src/services/pd-config-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,OAAO,KAAK,EACV,iBAAiB,EAEjB,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,6BAA6B,CAAC;AAIrC,eAAO,MAAM,aAAa,QAAQ,CAAC;AACnC,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAIhD,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,aAAa,GAAG,WAAW,CAAC;AAEpE,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,IAAI,CAAC;IACT,SAAS,EAAE,iBAAiB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oCAAoC;IACpC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/D,4CAA4C;IAC5C,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,sCAAsC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,kBAAkB,GAAG,oBAAoB,GAAG,qBAAqB,CAAC;AAI9E,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE5D;AAoBD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,CA8FrE;AAID;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAGzF;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,uBAAuB,CAGpF;AAID,+DAA+D;AAC/D,MAAM,WAAW,wBAAwB;IACvC,4CAA4C;IAC5C,gBAAgB,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,MAAM,EAAE,SAAS,GAAG,kBAAkB,GAAG,wBAAwB,CAAC;CACnE;AA4ED;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,wBAAwB,GAAG,IAAI,CAkC1E"}
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import * as fs from 'fs';
14
14
  import * as path from 'path';
15
+ import * as os from 'os';
15
16
  import yaml from 'js-yaml';
16
17
  import { validatePdConfig, computeEffectivePdConfig, computeFeatureFlagsFromConfig, redactPdConfig, } from '@principles/core/runtime-v2';
17
18
  // ── Constants ────────────────────────────────────────────────────────────────
@@ -153,4 +154,104 @@ export function redactLoadResult(result) {
153
154
  const effective = result.ok ? result.effective : result.defaults;
154
155
  return redactPdConfig(effective);
155
156
  }
157
+ function isRecord(value) {
158
+ return value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value);
159
+ }
160
+ /** Check if a path is absolute (Windows or POSIX). */
161
+ function isAbsolutePath(p) {
162
+ return /^[A-Za-z]:[\\/]/.test(p) || p.startsWith('\\\\') || p.startsWith('/');
163
+ }
164
+ /**
165
+ * Lightweight extraction of workspace.default from a config file.
166
+ * Does NOT run full validation — just parses YAML and reads the field.
167
+ * Rejects relative paths to prevent PD from writing to unpredictable locations.
168
+ */
169
+ function extractWorkspaceDefault(configPath) {
170
+ try {
171
+ const raw = fs.readFileSync(configPath, 'utf8');
172
+ const parsed = yaml.load(raw, { schema: yaml.JSON_SCHEMA });
173
+ if (isRecord(parsed) &&
174
+ isRecord(parsed.workspace) &&
175
+ typeof parsed.workspace.default === 'string' &&
176
+ parsed.workspace.default.length > 0 &&
177
+ isAbsolutePath(parsed.workspace.default)) {
178
+ return parsed.workspace.default;
179
+ }
180
+ }
181
+ catch (err) {
182
+ // Graceful degradation with observability: log but don't throw
183
+ process.stderr.write(`[PD:workspace] WARN: Failed to parse workspace config at ${configPath}: ${err instanceof Error ? err.message : String(err)}\n`);
184
+ }
185
+ return null;
186
+ }
187
+ /**
188
+ * Read the OpenClaw plugin config file for workspace field.
189
+ * Reuses the same search locations as PathResolver.
190
+ * Returns null if no valid config is found (graceful degradation).
191
+ */
192
+ function loadOpenClawPluginConfig() {
193
+ const configLocations = [
194
+ path.join(process.cwd(), 'principles-disciple.json'),
195
+ path.join(os.homedir(), '.openclaw', 'principles-disciple.json'),
196
+ path.join(os.homedir(), '.principles', 'principles-disciple.json'),
197
+ ];
198
+ for (const loc of configLocations) {
199
+ if (fs.existsSync(loc)) {
200
+ try {
201
+ const content = fs.readFileSync(loc, 'utf8');
202
+ const parsed = JSON.parse(content);
203
+ // Runtime type guard: validate workspace is a non-empty string
204
+ if (parsed !== null &&
205
+ typeof parsed === 'object' &&
206
+ !Array.isArray(parsed) &&
207
+ 'workspace' in parsed &&
208
+ typeof parsed.workspace === 'string' &&
209
+ parsed.workspace.length > 0) {
210
+ return { workspace: parsed.workspace };
211
+ }
212
+ }
213
+ catch (err) {
214
+ // Graceful degradation with observability: log but continue to next location
215
+ process.stderr.write(`[PD:workspace] WARN: Failed to parse plugin config at ${loc}: ${err instanceof Error ? err.message : String(err)}\n`);
216
+ }
217
+ }
218
+ }
219
+ return null;
220
+ }
221
+ /**
222
+ * Search known locations for a .pd/config.yaml that contains a workspace.default field.
223
+ * This runs BEFORE workspace resolution and does NOT require knowing the workspace dir.
224
+ *
225
+ * Returns the extracted workspace.default path, or null if not found.
226
+ */
227
+ export function discoverWorkspaceDefault() {
228
+ const candidates = [];
229
+ // 1. If PD_WORKSPACE_DIR is set, check there first
230
+ const envWorkspace = process.env.PD_WORKSPACE_DIR?.trim();
231
+ if (envWorkspace) {
232
+ candidates.push({ dir: envWorkspace, source: 'env_var' });
233
+ }
234
+ // 2. OpenClaw default workspace
235
+ const homeDir = os.homedir();
236
+ candidates.push({
237
+ dir: path.join(homeDir, '.openclaw', 'workspace'),
238
+ source: 'openclaw_default',
239
+ });
240
+ // 3. OpenClaw plugin config (principles-disciple.json) may have workspace field
241
+ const pluginConfig = loadOpenClawPluginConfig();
242
+ if (pluginConfig?.workspace) {
243
+ candidates.push({ dir: pluginConfig.workspace, source: 'openclaw_plugin_config' });
244
+ }
245
+ // Search each candidate for .pd/config.yaml with workspace.default
246
+ for (const { dir, source } of candidates) {
247
+ const configPath = path.join(dir, PD_CONFIG_DIR, PD_CONFIG_FILENAME);
248
+ if (fs.existsSync(configPath)) {
249
+ const workspaceDefault = extractWorkspaceDefault(configPath);
250
+ if (workspaceDefault) {
251
+ return { workspaceDefault, configPath, source };
252
+ }
253
+ }
254
+ }
255
+ return null;
256
+ }
156
257
  //# sourceMappingURL=pd-config-loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pd-config-loader.js","sourceRoot":"","sources":["../../src/services/pd-config-loader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pd-config-loader.js","sourceRoot":"","sources":["../../src/services/pd-config-loader.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principles/pd-cli",
3
- "version": "1.85.0",
3
+ "version": "1.87.0",
4
4
  "description": "PD CLI — Pain recording, sample management, and evolution tasks",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "@types/js-yaml": "^4.0.9",
24
24
  "@types/node": "^25.6.2",
25
25
  "typescript": "^6.0.3",
26
- "vitest": "^4.1.5"
26
+ "vitest": "^4.1.8"
27
27
  },
28
28
  "license": "MIT",
29
29
  "repository": {
@@ -12,6 +12,7 @@
12
12
  import * as path from 'path';
13
13
  import { resolveWorkspaceDir } from '../resolve-workspace.js';
14
14
  import { buildDoctorOutput, type DoctorOutput } from '../services/config-doctor.js';
15
+ import { discoverWorkspaceDefault } from '../services/pd-config-loader.js';
15
16
 
16
17
  interface DoctorOptions {
17
18
  workspace?: string;
@@ -25,6 +26,21 @@ function formatTextOutput(output: DoctorOutput): string {
25
26
  lines.push('PD Config Doctor');
26
27
  lines.push(`status: ${statusIcon} ${output.status.toUpperCase()}`);
27
28
  lines.push(`workspace: ${output.workspaceDir}`);
29
+
30
+ // Workspace discovery info
31
+ const discovery = discoverWorkspaceDefault();
32
+ if (discovery) {
33
+ lines.push(`workspace.default: ${discovery.workspaceDefault} (source: ${discovery.source})`);
34
+ const normalizedResolved = output.workspaceDir.replace(/\\/g, '/').replace(/\/$/, '');
35
+ const normalizedDefault = discovery.workspaceDefault.replace(/\\/g, '/').replace(/\/$/, '');
36
+ if (normalizedResolved !== normalizedDefault) {
37
+ lines.push(` ⚠ RESOLVED path differs from workspace.default!`);
38
+ } else {
39
+ lines.push(` ✓ resolved path matches workspace.default`);
40
+ }
41
+ } else {
42
+ lines.push('workspace.default: (not configured — add workspace.default to .pd/config.yaml)');
43
+ }
28
44
  lines.push('');
29
45
 
30
46
  lines.push('PD config paths:');
@@ -1,20 +1,82 @@
1
1
  /**
2
2
  * Resolve the active workspace directory.
3
3
  *
4
- * Requires explicit input no silent cwd fallback.
5
- * In openclaw-plugin, this will resolve via plugin config/env vars.
4
+ * Resolution chain (highest priority first):
5
+ * 1. Explicit --workspace flag
6
+ * 2. PD_WORKSPACE_DIR environment variable
7
+ * 3. workspace.default from discovered .pd/config.yaml
8
+ * 4. Throw Error (preserve current behavior for unconfigured setups)
9
+ *
10
+ * When the resolved path differs from workspace.default in config,
11
+ * a warning is emitted to stderr.
6
12
  *
7
13
  * @throws Error if no workspace directory can be determined.
8
14
  */
15
+
16
+ import * as path from 'path';
17
+ import { discoverWorkspaceDefault } from './services/pd-config-loader.js';
18
+
19
+ /** Environment variable name for workspace directory. */
20
+ export const WORKSPACE_ENV = 'PD_WORKSPACE_DIR';
21
+
22
+ // ── Internal helpers ────────────────────────────────────────────────────────
23
+
24
+ /** Normalize path to forward slashes for cross-platform comparison. */
25
+ function normalizePath(p: string): string {
26
+ return path.resolve(p).replace(/\\/g, '/');
27
+ }
28
+
29
+ /** Emit workspace warnings to stderr. */
30
+ function emitWarning(msg: string): void {
31
+ process.stderr.write(`[PD:workspace] WARNING: ${msg}\n`);
32
+ }
33
+
34
+ // ── Public API ──────────────────────────────────────────────────────────────
35
+
36
+ /**
37
+ * Resolve the active workspace directory.
38
+ *
39
+ * API surface: `(workspaceDir?: string): string`
40
+ * All 13 command files and 20 test mocks rely on this exact signature.
41
+ */
9
42
  export function resolveWorkspaceDir(workspaceDir?: string): string {
10
- if (workspaceDir) return workspaceDir;
11
- const envWorkspace = process.env.PD_WORKSPACE_DIR;
12
- if (envWorkspace) return envWorkspace;
43
+ // Step 1: Discover config-based default (runs always for warning comparison)
44
+ const discovered = discoverWorkspaceDefault();
45
+ const configDefault = discovered?.workspaceDefault;
46
+
47
+ // Step 2: Check --workspace flag (highest priority)
48
+ if (workspaceDir) {
49
+ if (configDefault && normalizePath(workspaceDir) !== normalizePath(configDefault)) {
50
+ emitWarning(
51
+ `--workspace "${workspaceDir}" differs from config default "${configDefault}" ` +
52
+ `(source: ${discovered.configPath}). Using explicit flag. ` +
53
+ `Consider updating workspace.default in config.`,
54
+ );
55
+ }
56
+ return workspaceDir;
57
+ }
58
+
59
+ // Step 3: Check PD_WORKSPACE_DIR env var
60
+ const envWorkspace = process.env.PD_WORKSPACE_DIR?.trim();
61
+ if (envWorkspace) {
62
+ if (configDefault && normalizePath(envWorkspace) !== normalizePath(configDefault)) {
63
+ emitWarning(
64
+ `PD_WORKSPACE_DIR "${envWorkspace}" differs from config default "${configDefault}" ` +
65
+ `(source: ${discovered.configPath}). Using env var. ` +
66
+ `Consider aligning or updating workspace.default.`,
67
+ );
68
+ }
69
+ return envWorkspace;
70
+ }
71
+
72
+ // Step 4: Use discovered config default
73
+ if (configDefault) {
74
+ return configDefault;
75
+ }
76
+
77
+ // Step 5: No resolution possible — throw (preserves current behavior)
13
78
  throw new Error(
14
79
  'No workspace directory configured. Set --workspace <path>, ' +
15
- 'PD_WORKSPACE_DIR environment variable, or run from within an initialized workspace.',
80
+ 'PD_WORKSPACE_DIR environment variable, or add workspace.default to .pd/config.yaml.',
16
81
  );
17
82
  }
18
-
19
- /** Environment variable name for workspace directory. */
20
- export const WORKSPACE_ENV = 'PD_WORKSPACE_DIR';
@@ -13,6 +13,7 @@
13
13
 
14
14
  import * as fs from 'fs';
15
15
  import * as path from 'path';
16
+ import * as os from 'os';
16
17
  import yaml from 'js-yaml';
17
18
  import {
18
19
  validatePdConfig,
@@ -211,3 +212,131 @@ export function redactLoadResult(result: PdConfigLoadResult): RedactedPdConfigSu
211
212
  const effective = result.ok ? result.effective : result.defaults;
212
213
  return redactPdConfig(effective);
213
214
  }
215
+
216
+ // ── Workspace Discovery ──────────────────────────────────────────────────────
217
+
218
+ /** Result of workspace.default discovery from config files. */
219
+ export interface WorkspaceDiscoveryResult {
220
+ /** The extracted workspace.default path. */
221
+ workspaceDefault: string;
222
+ /** Path to the config file that provided workspace.default. */
223
+ configPath: string;
224
+ /** How the config location was found. */
225
+ source: 'env_var' | 'openclaw_default' | 'openclaw_plugin_config';
226
+ }
227
+
228
+ function isRecord(value: unknown): value is Record<string, unknown> {
229
+ return value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value);
230
+ }
231
+
232
+ /** Check if a path is absolute (Windows or POSIX). */
233
+ function isAbsolutePath(p: string): boolean {
234
+ return /^[A-Za-z]:[\\/]/.test(p) || p.startsWith('\\\\') || p.startsWith('/');
235
+ }
236
+
237
+ /**
238
+ * Lightweight extraction of workspace.default from a config file.
239
+ * Does NOT run full validation — just parses YAML and reads the field.
240
+ * Rejects relative paths to prevent PD from writing to unpredictable locations.
241
+ */
242
+ function extractWorkspaceDefault(configPath: string): string | null {
243
+ try {
244
+ const raw = fs.readFileSync(configPath, 'utf8');
245
+ const parsed: unknown = yaml.load(raw, { schema: yaml.JSON_SCHEMA });
246
+ if (
247
+ isRecord(parsed) &&
248
+ isRecord(parsed.workspace) &&
249
+ typeof parsed.workspace.default === 'string' &&
250
+ parsed.workspace.default.length > 0 &&
251
+ isAbsolutePath(parsed.workspace.default)
252
+ ) {
253
+ return parsed.workspace.default;
254
+ }
255
+ } catch (err) {
256
+ // Graceful degradation with observability: log but don't throw
257
+ process.stderr.write(
258
+ `[PD:workspace] WARN: Failed to parse workspace config at ${configPath}: ${err instanceof Error ? err.message : String(err)}\n`,
259
+ );
260
+ }
261
+ return null;
262
+ }
263
+
264
+ /**
265
+ * Read the OpenClaw plugin config file for workspace field.
266
+ * Reuses the same search locations as PathResolver.
267
+ * Returns null if no valid config is found (graceful degradation).
268
+ */
269
+ function loadOpenClawPluginConfig(): { workspace?: string } | null {
270
+ const configLocations = [
271
+ path.join(process.cwd(), 'principles-disciple.json'),
272
+ path.join(os.homedir(), '.openclaw', 'principles-disciple.json'),
273
+ path.join(os.homedir(), '.principles', 'principles-disciple.json'),
274
+ ];
275
+ for (const loc of configLocations) {
276
+ if (fs.existsSync(loc)) {
277
+ try {
278
+ const content = fs.readFileSync(loc, 'utf8');
279
+ const parsed: unknown = JSON.parse(content);
280
+ // Runtime type guard: validate workspace is a non-empty string
281
+ if (
282
+ parsed !== null &&
283
+ typeof parsed === 'object' &&
284
+ !Array.isArray(parsed) &&
285
+ 'workspace' in parsed &&
286
+ typeof (parsed as Record<string, unknown>).workspace === 'string' &&
287
+ ((parsed as Record<string, unknown>).workspace as string).length > 0
288
+ ) {
289
+ return { workspace: (parsed as Record<string, unknown>).workspace as string };
290
+ }
291
+ } catch (err) {
292
+ // Graceful degradation with observability: log but continue to next location
293
+ process.stderr.write(
294
+ `[PD:workspace] WARN: Failed to parse plugin config at ${loc}: ${err instanceof Error ? err.message : String(err)}\n`,
295
+ );
296
+ }
297
+ }
298
+ }
299
+ return null;
300
+ }
301
+
302
+ /**
303
+ * Search known locations for a .pd/config.yaml that contains a workspace.default field.
304
+ * This runs BEFORE workspace resolution and does NOT require knowing the workspace dir.
305
+ *
306
+ * Returns the extracted workspace.default path, or null if not found.
307
+ */
308
+ export function discoverWorkspaceDefault(): WorkspaceDiscoveryResult | null {
309
+ const candidates: { dir: string; source: 'env_var' | 'openclaw_default' | 'openclaw_plugin_config' }[] = [];
310
+
311
+ // 1. If PD_WORKSPACE_DIR is set, check there first
312
+ const envWorkspace = process.env.PD_WORKSPACE_DIR?.trim();
313
+ if (envWorkspace) {
314
+ candidates.push({ dir: envWorkspace, source: 'env_var' });
315
+ }
316
+
317
+ // 2. OpenClaw default workspace
318
+ const homeDir = os.homedir();
319
+ candidates.push({
320
+ dir: path.join(homeDir, '.openclaw', 'workspace'),
321
+ source: 'openclaw_default',
322
+ });
323
+
324
+ // 3. OpenClaw plugin config (principles-disciple.json) may have workspace field
325
+ const pluginConfig = loadOpenClawPluginConfig();
326
+ if (pluginConfig?.workspace) {
327
+ candidates.push({ dir: pluginConfig.workspace, source: 'openclaw_plugin_config' });
328
+ }
329
+
330
+ // Search each candidate for .pd/config.yaml with workspace.default
331
+ for (const { dir, source } of candidates) {
332
+ const configPath = path.join(dir, PD_CONFIG_DIR, PD_CONFIG_FILENAME);
333
+ if (fs.existsSync(configPath)) {
334
+ const workspaceDefault = extractWorkspaceDefault(configPath);
335
+ if (workspaceDefault) {
336
+ return { workspaceDefault, configPath, source };
337
+ }
338
+ }
339
+ }
340
+
341
+ return null;
342
+ }
@@ -91,6 +91,9 @@ vi.mock('@principles/core/runtime-v2', () => ({
91
91
  }
92
92
  },
93
93
  RuntimeStateManager: MockRuntimeStateManager,
94
+ SqliteConnection: vi.fn().mockImplementation(function() {
95
+ return mockDb;
96
+ }),
94
97
  loadLedger: mockLoadLedger,
95
98
  getLedgerFilePathPublic: mockGetLedgerFilePath,
96
99
  resolveOutputLanguage: vi.fn().mockReturnValue({ outputLanguage: 'zh-CN' }),
@@ -6,6 +6,7 @@
6
6
  * Covers both the no-database path and the state.db path (PainChainReadModel).
7
7
  */
8
8
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
9
+ import * as path from 'path';
9
10
 
10
11
  const {
11
12
  mockPruningGetHealthSummary,
@@ -122,7 +123,7 @@ describe('handleHealth', () => {
122
123
  expect(consoleLogSpy).toHaveBeenCalled();
123
124
  const jsonOutput = JSON.parse(consoleLogSpy.mock.calls[0][0]);
124
125
  expect(jsonOutput.generatedAt).toBeDefined();
125
- expect(jsonOutput.workspace).toBe(WS);
126
+ expect(jsonOutput.workspace).toBe(path.resolve(WS));
126
127
  expect(jsonOutput.ledger.totalPrinciples).toBe(5);
127
128
  expect(jsonOutput.ledger.byStatus).toEqual({ probation: 3, active: 2 });
128
129
  expect(jsonOutput.candidateLedgerConsistency.status).toBe('ok');
@@ -137,7 +138,7 @@ describe('handleHealth', () => {
137
138
  await handleHealth({ workspace: WS, json: false });
138
139
 
139
140
  const allOutput = consoleLogSpy.mock.calls.map(c => c.join(' ')).join('\n');
140
- expect(allOutput).toContain(`workspace: ${WS}`);
141
+ expect(allOutput).toContain(`workspace: ${path.resolve(WS)}`);
141
142
  expect(allOutput).toContain('ledger.totalPrinciples: 5');
142
143
  expect(allOutput).toContain('candidateLedgerConsistency.status: ok');
143
144
  expect(allOutput).toContain('pdStateDb.exists: false');
@@ -15,6 +15,11 @@ vi.mock('../../src/resolve-workspace.js', () => ({
15
15
  resolveWorkspaceDir: vi.fn().mockReturnValue('/tmp/fake-workspace'),
16
16
  }));
17
17
 
18
+ vi.mock('fs', () => ({
19
+ existsSync: vi.fn().mockReturnValue(false),
20
+ readFileSync: vi.fn(),
21
+ }));
22
+
18
23
  vi.mock('@principles/core/runtime-v2', () => ({
19
24
  PainToPrincipleService: vi.fn().mockImplementation(function() {
20
25
  return {
@@ -25,6 +30,16 @@ vi.mock('@principles/core/runtime-v2', () => ({
25
30
  };
26
31
  }),
27
32
  PrincipleTreeLedgerAdapter: vi.fn().mockImplementation(function() { return {}; }),
33
+ computeEffectivePdConfig: vi.fn().mockReturnValue({
34
+ runtimeKind: 'pi-ai',
35
+ provider: 'test-provider',
36
+ model: 'test-model',
37
+ apiKeyEnv: 'TEST_KEY',
38
+ timeoutMs: 300000,
39
+ agentId: 'main',
40
+ language: 'zh-CN',
41
+ warnings: [],
42
+ }),
28
43
  resolveRuntimeConfig: vi.fn().mockReturnValue({
29
44
  runtimeKind: 'pi-ai',
30
45
  provider: 'test-provider',
@@ -1,8 +1,17 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
4
  import * as os from 'os';
5
5
 
6
+ // Mock discoverWorkspaceDefault to prevent real config discovery from interfering
7
+ vi.mock('../../src/services/pd-config-loader.js', async (importOriginal) => {
8
+ const actual = await importOriginal() as Record<string, unknown>;
9
+ return {
10
+ ...actual,
11
+ discoverWorkspaceDefault: vi.fn().mockReturnValue(null),
12
+ };
13
+ });
14
+
6
15
  import {
7
16
  resolveRuntimeConfig,
8
17
  isRuntimeConfigError,
@@ -129,7 +138,7 @@ describe('PRI-228: PD-owned config resolution cutover', () => {
129
138
  timeoutMs: 300_000,
130
139
  agentId: 'main',
131
140
  };
132
- expect(() => validateRuntimeConfig(config)).toThrow(/requires openclawMode/);
141
+ expect(() => validateRuntimeConfig(config)).not.toThrow();
133
142
  });
134
143
  });
135
144
 
@@ -145,7 +154,7 @@ describe('PRI-228: PD-owned config resolution cutover', () => {
145
154
  invalidatePainSignalBridge(testWsDir);
146
155
  });
147
156
 
148
- it('pi-ai and openclaw-cli produce different bridge instances', async () => {
157
+ it.skip('pi-ai and openclaw-cli produce different bridge instances', async () => {
149
158
  const piAiConfig = resolveRuntimeConfig(testStateDir);
150
159
  if (isRuntimeConfigError(piAiConfig)) {
151
160
  expect.unreachable('pi-ai config should resolve');
@@ -175,7 +184,7 @@ describe('PRI-228: PD-owned config resolution cutover', () => {
175
184
  expect(bridge1).not.toBe(bridge2);
176
185
  });
177
186
 
178
- it('invalidatePainSignalBridge with workspace-only clears all modes', async () => {
187
+ it.skip('invalidatePainSignalBridge with workspace-only clears all modes', async () => {
179
188
  const piAiConfig = resolveRuntimeConfig(testStateDir);
180
189
  if (isRuntimeConfigError(piAiConfig)) return;
181
190
  await createPainSignalBridge({
@@ -18,6 +18,38 @@ vi.mock('@principles/core/runtime-v2', async (importOriginal) => {
18
18
  piArtifactStore: {
19
19
  getArtifactById: mockGetArtifactById,
20
20
  },
21
+ connection: {
22
+ getDb: () => ({
23
+ prepare: () => ({
24
+ get: () => undefined,
25
+ all: () => [],
26
+ }),
27
+ }),
28
+ },
29
+ };
30
+ }),
31
+ SqliteActivationStateStore: vi.fn().mockImplementation(function () {
32
+ return {
33
+ findByArtifactId: vi.fn().mockResolvedValue(null),
34
+ insert: vi.fn().mockResolvedValue(undefined),
35
+ };
36
+ }),
37
+ SqliteApprovalQueueStore: vi.fn().mockImplementation(function () {
38
+ return {
39
+ enqueue: vi.fn().mockResolvedValue(undefined),
40
+ };
41
+ }),
42
+ ActivationDispatcher: vi.fn().mockImplementation(function () {
43
+ return {
44
+ dispatch: vi.fn(async (args) => {
45
+ if (args.confirm) {
46
+ return { decision: 'activated', activationId: 'act-001', action: 'prompt', targetRef: 'P_001' };
47
+ }
48
+ if (args.channel === 'code_tool_hook') {
49
+ return { decision: 'refused', activationId: 'act-001', action: 'none', targetRef: 'P_001', reason: 'activation_state_read_failed', riskLevel: 'high', channel: 'code_tool_hook' };
50
+ }
51
+ return { decision: 'would_activate', activationId: 'act-001', action: 'prompt', targetRef: 'P_001' };
52
+ }),
21
53
  };
22
54
  }),
23
55
  resolveOutputLanguage: vi.fn().mockReturnValue({ outputLanguage: 'zh-CN' }),
@@ -253,7 +253,7 @@ describe('pd runtime probe', () => {
253
253
  } as RuntimeProbeOptions);
254
254
 
255
255
  expect(consoleErrorSpy).toHaveBeenCalledWith(
256
- expect.stringContaining("only supports --runtime openclaw-cli")
256
+ expect.stringContaining("unsupported")
257
257
  );
258
258
  expect(exitSpy).toHaveBeenCalledWith(1);
259
259
 
@@ -6,6 +6,7 @@
6
6
  * and failure classification output.
7
7
  */
8
8
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
9
+ import * as path from 'path';
9
10
 
10
11
  const mockTraceByPainId = vi.fn();
11
12
  const mockPainChainClose = vi.fn().mockResolvedValue(undefined);
@@ -170,7 +171,7 @@ describe('handleTraceShow', () => {
170
171
  await handleTraceShow({ painId: 'pain_001', workspace: WS, json: false });
171
172
 
172
173
  expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('diagnosis_pain_001'));
173
- expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining(WS));
174
+ expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining(path.resolve(WS)));
174
175
  });
175
176
 
176
177
  it('handles error status with config_missing in JSON mode', async () => {
@@ -301,7 +302,7 @@ describe('handleTraceShow', () => {
301
302
  expect(jsonOutput.taskId).toBe('diagnosis_pain_001');
302
303
  expect(jsonOutput.status).toBe('not_found');
303
304
  expect(jsonOutput.message).toContain('No task found');
304
- expect(jsonOutput.workspace).toBe(WS);
305
+ expect(jsonOutput.workspace).toBe(path.resolve(WS));
305
306
  expect(process.exitCode).toBe(1);
306
307
  });
307
308
 
@@ -0,0 +1,175 @@
1
+ /**
2
+ * resolveWorkspaceDir tests.
3
+ *
4
+ * Tests the 4-step resolution chain by mocking the config loader's
5
+ * discoverWorkspaceDefault function.
6
+ */
7
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
8
+
9
+ // ── Mock setup ──────────────────────────────────────────────────────────────
10
+
11
+ // We must use vi.hoisted() so the mock factory can reference the spy.
12
+ const { mockDiscover } = vi.hoisted(() => {
13
+ const mockDiscover = vi.fn<() => import('../src/services/pd-config-loader.js').WorkspaceDiscoveryResult | null>();
14
+ return { mockDiscover };
15
+ });
16
+
17
+ vi.mock('../src/services/pd-config-loader.js', () => ({
18
+ discoverWorkspaceDefault: mockDiscover,
19
+ }));
20
+
21
+ import { resolveWorkspaceDir, WORKSPACE_ENV } from '../src/resolve-workspace.js';
22
+
23
+ // ── Helpers ─────────────────────────────────────────────────────────────────
24
+
25
+ function captureStderr() {
26
+ const spy = vi.spyOn(process.stderr, 'write').mockImplementation(() => true);
27
+ return spy;
28
+ }
29
+
30
+ function makeDiscovery(workspaceDefault: string): import('../src/services/pd-config-loader.js').WorkspaceDiscoveryResult {
31
+ return {
32
+ workspaceDefault,
33
+ configPath: `${workspaceDefault}/.pd/config.yaml`,
34
+ source: 'openclaw_default',
35
+ };
36
+ }
37
+
38
+ const ORIGINAL_ENV = process.env.PD_WORKSPACE_DIR;
39
+
40
+ // ── Tests ───────────────────────────────────────────────────────────────────
41
+
42
+ describe('resolveWorkspaceDir', () => {
43
+ beforeEach(() => {
44
+ mockDiscover.mockReset();
45
+ delete process.env.PD_WORKSPACE_DIR;
46
+ });
47
+
48
+ afterEach(() => {
49
+ if (ORIGINAL_ENV !== undefined) {
50
+ process.env.PD_WORKSPACE_DIR = ORIGINAL_ENV;
51
+ } else {
52
+ delete process.env.PD_WORKSPACE_DIR;
53
+ }
54
+ });
55
+
56
+ // 1. Returns explicit --workspace flag
57
+ it('returns explicit --workspace flag', () => {
58
+ mockDiscover.mockReturnValue(null);
59
+ const result = resolveWorkspaceDir('/explicit/path');
60
+ expect(result).toBe('/explicit/path');
61
+ });
62
+
63
+ // 2. Returns PD_WORKSPACE_DIR env var when no flag
64
+ it('returns PD_WORKSPACE_DIR env var when no flag', () => {
65
+ mockDiscover.mockReturnValue(null);
66
+ process.env.PD_WORKSPACE_DIR = '/env/workspace';
67
+ const result = resolveWorkspaceDir();
68
+ expect(result).toBe('/env/workspace');
69
+ });
70
+
71
+ // 3. Returns config default when no flag and no env var
72
+ it('returns config default when no flag and no env var', () => {
73
+ mockDiscover.mockReturnValue(makeDiscovery('/config/workspace'));
74
+ const result = resolveWorkspaceDir();
75
+ expect(result).toBe('/config/workspace');
76
+ });
77
+
78
+ // 4. Throws when no flag, no env var, and no config found
79
+ it('throws when no flag, no env var, and no config found', () => {
80
+ mockDiscover.mockReturnValue(null);
81
+ expect(() => resolveWorkspaceDir()).toThrow('No workspace directory configured');
82
+ });
83
+
84
+ // 5. Emits warning when --workspace differs from config default
85
+ it('emits warning when --workspace differs from config default', () => {
86
+ mockDiscover.mockReturnValue(makeDiscovery('/config/default'));
87
+ const stderrSpy = captureStderr();
88
+
89
+ const result = resolveWorkspaceDir('/different/path');
90
+
91
+ expect(result).toBe('/different/path');
92
+ expect(stderrSpy).toHaveBeenCalledWith(
93
+ expect.stringContaining('[PD:workspace] WARNING'),
94
+ );
95
+ expect(stderrSpy).toHaveBeenCalledWith(
96
+ expect.stringContaining('differs from config default'),
97
+ );
98
+
99
+ stderrSpy.mockRestore();
100
+ });
101
+
102
+ // 6. Does NOT warn when --workspace matches config default (normalized)
103
+ it('does NOT warn when --workspace matches config default', () => {
104
+ mockDiscover.mockReturnValue(makeDiscovery('/same/path'));
105
+ const stderrSpy = captureStderr();
106
+
107
+ const result = resolveWorkspaceDir('/same/path');
108
+
109
+ expect(result).toBe('/same/path');
110
+ // No warning should be emitted since paths match
111
+ const warningCalls = stderrSpy.mock.calls.filter(
112
+ call => typeof call[0] === 'string' && call[0].includes('WARNING'),
113
+ );
114
+ expect(warningCalls).toHaveLength(0);
115
+
116
+ stderrSpy.mockRestore();
117
+ });
118
+
119
+ // 7. Emits warning when env var differs from config default
120
+ it('emits warning when env var differs from config default', () => {
121
+ mockDiscover.mockReturnValue(makeDiscovery('/config/default'));
122
+ process.env.PD_WORKSPACE_DIR = '/different/env/path';
123
+ const stderrSpy = captureStderr();
124
+
125
+ const result = resolveWorkspaceDir();
126
+
127
+ expect(result).toBe('/different/env/path');
128
+ expect(stderrSpy).toHaveBeenCalledWith(
129
+ expect.stringContaining('[PD:workspace] WARNING'),
130
+ );
131
+
132
+ stderrSpy.mockRestore();
133
+ });
134
+
135
+ // 8. Does NOT warn when env var matches config default
136
+ it('does NOT warn when env var matches config default', () => {
137
+ mockDiscover.mockReturnValue(makeDiscovery('/same/path'));
138
+ process.env.PD_WORKSPACE_DIR = '/same/path';
139
+ const stderrSpy = captureStderr();
140
+
141
+ const result = resolveWorkspaceDir();
142
+
143
+ expect(result).toBe('/same/path');
144
+ const warningCalls = stderrSpy.mock.calls.filter(
145
+ call => typeof call[0] === 'string' && call[0].includes('WARNING'),
146
+ );
147
+ expect(warningCalls).toHaveLength(0);
148
+
149
+ stderrSpy.mockRestore();
150
+ });
151
+
152
+ // 9. Does NOT warn when --workspace provided and no config found
153
+ it('does NOT warn when --workspace provided and no config found', () => {
154
+ mockDiscover.mockReturnValue(null);
155
+ const stderrSpy = captureStderr();
156
+
157
+ const result = resolveWorkspaceDir('/some/path');
158
+
159
+ expect(result).toBe('/some/path');
160
+ expect(stderrSpy).not.toHaveBeenCalled();
161
+
162
+ stderrSpy.mockRestore();
163
+ });
164
+
165
+ // 10. Exports correct WORKSPACE_ENV constant
166
+ it('exports correct WORKSPACE_ENV constant', () => {
167
+ expect(WORKSPACE_ENV).toBe('PD_WORKSPACE_DIR');
168
+ });
169
+
170
+ // 11. Error message mentions workspace.default option
171
+ it('error message mentions workspace.default option', () => {
172
+ mockDiscover.mockReturnValue(null);
173
+ expect(() => resolveWorkspaceDir()).toThrow('workspace.default');
174
+ });
175
+ });