opendoor-plugin 0.2.0 → 0.4.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.
package/dist/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "opendoor-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Opendoor — a pi-web browser plugin that surfaces Doorstop requirements stored in the workspace: the document tree, per-item state (reviewed/unreviewed changes, suspect links), validation findings, and one-click doorstop CLI actions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/kairibu/opendoor.git"
10
- },
11
7
  "keywords": [
12
8
  "pi",
9
+ "pi-package",
13
10
  "pi-web",
14
11
  "plugin",
12
+ "pi-web.dev-plugin",
13
+ "pi-web-plugin",
15
14
  "doorstop",
16
15
  "requirements"
17
16
  ],
17
+ "main": "browser/pi-web-plugin.js",
18
18
  "piWeb": {
19
19
  "plugins": [
20
20
  {
@@ -25,30 +25,7 @@
25
25
  }
26
26
  ]
27
27
  },
28
- "files": [
29
- "dist"
30
- ],
31
- "main": "dist/browser/pi-web-plugin.js",
32
- "scripts": {
33
- "build": "node scripts/build-plugin.mjs",
34
- "dev": "node scripts/build-plugin.mjs --watch",
35
- "test": "vitest run",
36
- "test:coverage": "vitest run --coverage",
37
- "typecheck": "tsc --noEmit"
38
- },
39
28
  "peerDependencies": {
40
- "@jmfederico/pi-web": ">=1.202608.1"
41
- },
42
- "devDependencies": {
43
- "@jmfederico/pi-web": "^1.202608.2",
44
- "@types/js-yaml": "^4.0.9",
45
- "@types/node": "^24.0.0",
46
- "@vitest/coverage-v8": "^4.1.11",
47
- "esbuild": "^0.27.7",
48
- "happy-dom": "^20.11.1",
49
- "js-yaml": "^4.3.2",
50
- "lit": "^3.3.3",
51
- "typescript": "^6.0.3",
52
- "vitest": "^4.1.10"
29
+ "@jmfederico/pi-web": ">=1.202609.1"
53
30
  }
54
31
  }
@@ -0,0 +1,136 @@
1
+ // Generated from src/pi-extension/opendoor-review.ts. Do not edit directly.
2
+
3
+ // src/doorstop-session-metadata.ts
4
+ var OPENDOOR_SESSION_METADATA_NAMESPACE = "opendoor";
5
+ var DOORSTOP_SESSION_METADATA_SCHEMA = 1;
6
+ var DOORSTOP_SESSION_METADATA_MAX_UIDS = 8;
7
+ var DOORSTOP_SESSION_METADATA_MAX_UID_LENGTH = 32;
8
+ var DOORSTOP_SESSION_METADATA_OPS = /* @__PURE__ */ new Set(["review"]);
9
+ var DOORSTOP_SESSION_METADATA_UID_PATTERN = /^[\w.-]+$/u;
10
+ function isMetadataUid(value) {
11
+ return typeof value === "string" && value !== "" && value.length <= DOORSTOP_SESSION_METADATA_MAX_UID_LENGTH && DOORSTOP_SESSION_METADATA_UID_PATTERN.test(value);
12
+ }
13
+ function buildDoorstopSessionMetadata(op, uids) {
14
+ if (!DOORSTOP_SESSION_METADATA_OPS.has(op)) {
15
+ throw new Error(`Unsupported doorstop session metadata op: ${JSON.stringify(op)}`);
16
+ }
17
+ if (uids.length === 0 || uids.length > DOORSTOP_SESSION_METADATA_MAX_UIDS) {
18
+ throw new Error(
19
+ `Doorstop session metadata requires 1..${String(DOORSTOP_SESSION_METADATA_MAX_UIDS)} UIDs`
20
+ );
21
+ }
22
+ const copied = [];
23
+ for (const uid of uids) {
24
+ if (!isMetadataUid(uid)) {
25
+ throw new Error(
26
+ `Invalid doorstop session metadata UID (expected a non-empty safe token of at most ${String(DOORSTOP_SESSION_METADATA_MAX_UID_LENGTH)} characters)`
27
+ );
28
+ }
29
+ copied.push(uid);
30
+ }
31
+ return {
32
+ schema: DOORSTOP_SESSION_METADATA_SCHEMA,
33
+ op,
34
+ uids: copied
35
+ };
36
+ }
37
+
38
+ // src/pi-extension/opendoor-review.ts
39
+ var PI_WEB_SESSION_DISPATCH_CHANNEL = "pi-web:session-dispatch:v1";
40
+ var OPENDOOR_REVIEW_COMMAND = "opendoor-review";
41
+ var OPENDOOR_REVIEW_MESSAGE_CUSTOM_TYPE = "opendoor-review-dispatch";
42
+ var OPENDOOR_REVIEW_SESSION_NAME_PREFIX = "Doorstop review ";
43
+ function defaultReviewInstruction(uid) {
44
+ return `Check doorstop requirement ${uid}: run \`doorstop\` in the workspace root, inspect ${uid}'s child links and references, and when it is ready run \`doorstop review ${uid}\`.`;
45
+ }
46
+ function parseReviewCommandArgs(args) {
47
+ const trimmed = args.trim();
48
+ if (trimmed === "") return void 0;
49
+ const match = /^(\S+)(?:\s+([\s\S]*))?$/u.exec(trimmed);
50
+ if (match === null) return void 0;
51
+ const uid = match[1] ?? "";
52
+ if (uid === "") return void 0;
53
+ return { uid, notes: (match[2] ?? "").trim() };
54
+ }
55
+ function opendoorReviewExtension(pi) {
56
+ pi.registerCommand(OPENDOOR_REVIEW_COMMAND, {
57
+ description: "Start an independent Doorstop review conversation for a requirement UID (e.g. /opendoor-review REQ0001)",
58
+ handler: async (args) => {
59
+ await runOpendoorReview(pi, args);
60
+ }
61
+ });
62
+ }
63
+ async function runOpendoorReview(pi, args) {
64
+ const parsed = parseReviewCommandArgs(args);
65
+ if (parsed === void 0) {
66
+ report(
67
+ pi,
68
+ `Usage: /${OPENDOOR_REVIEW_COMMAND} <UID> [instruction\u2026] \u2014 the UID is a Doorstop requirement UID.`
69
+ );
70
+ return;
71
+ }
72
+ let metadata;
73
+ try {
74
+ metadata = {
75
+ [OPENDOOR_SESSION_METADATA_NAMESPACE]: buildDoorstopSessionMetadata("review", [parsed.uid])
76
+ };
77
+ } catch (error) {
78
+ report(pi, `Cannot start a Doorstop review session: ${formatError(error)}`);
79
+ return;
80
+ }
81
+ const prompt = parsed.notes === "" ? defaultReviewInstruction(parsed.uid) : parsed.notes;
82
+ const name = `${OPENDOOR_REVIEW_SESSION_NAME_PREFIX}${parsed.uid}`;
83
+ let accepted;
84
+ pi.events.emit(PI_WEB_SESSION_DISPATCH_CHANNEL, {
85
+ input: { prompt, name, metadata },
86
+ accept: (result) => {
87
+ accepted = result instanceof Promise ? result : Promise.resolve(result);
88
+ }
89
+ });
90
+ if (accepted === void 0) {
91
+ report(
92
+ pi,
93
+ "Doorstop review dispatch requires PI WEB's session dispatch bridge. Update PI WEB and restart the session daemon when safe; no session was started."
94
+ );
95
+ return;
96
+ }
97
+ try {
98
+ const result = await accepted;
99
+ report(pi, `Started an independent Doorstop review session for ${parsed.uid}${spawnSuffix(result)}.`);
100
+ } catch (error) {
101
+ report(
102
+ pi,
103
+ `The Doorstop review session for ${parsed.uid} was not started: ${formatError(error)}`
104
+ );
105
+ }
106
+ }
107
+ function report(pi, text) {
108
+ try {
109
+ pi.sendMessage({
110
+ customType: OPENDOOR_REVIEW_MESSAGE_CUSTOM_TYPE,
111
+ content: text,
112
+ display: true
113
+ });
114
+ } catch {
115
+ }
116
+ }
117
+ function spawnSuffix(result) {
118
+ if (typeof result !== "object" || result === null) return "";
119
+ const sessionId = result["sessionId"];
120
+ return typeof sessionId === "string" && sessionId !== "" ? ` (session ${sessionId})` : "";
121
+ }
122
+ function formatError(error) {
123
+ if (error instanceof Error && error.message !== "") return error.message;
124
+ const text = String(error);
125
+ return text === "" ? "unknown error" : text;
126
+ }
127
+ export {
128
+ OPENDOOR_REVIEW_COMMAND,
129
+ OPENDOOR_REVIEW_MESSAGE_CUSTOM_TYPE,
130
+ OPENDOOR_REVIEW_SESSION_NAME_PREFIX,
131
+ PI_WEB_SESSION_DISPATCH_CHANNEL,
132
+ opendoorReviewExtension as default,
133
+ defaultReviewInstruction,
134
+ parseReviewCommandArgs,
135
+ runOpendoorReview
136
+ };
@@ -993,10 +993,20 @@ async function probeContainsDoorstopMarker(root, signal) {
993
993
  return walk(root, 0);
994
994
  }
995
995
  var plugin = {
996
- apiVersion: 1,
996
+ apiVersion: 3,
997
997
  name: "Opendoor",
998
+ // Permanently no `requires`/`start()` — see the module header: the review
999
+ // dispatch moved to the Pi extension, so this provider-contributing entry
1000
+ // never approaches the host's provider-cycle rule.
998
1001
  activate(context) {
999
- return { workspaceProvider: createDoorstopWorkspaceProvider(context) };
1002
+ return {
1003
+ workspaceProvider: createDoorstopWorkspaceProvider(context),
1004
+ // v3: the six-operation dispatch lives at the activation level, not on
1005
+ // the provider (`WorkspaceProvider.request` was removed).
1006
+ peer: {
1007
+ request: (request) => dispatchDoorstopOperation(context, request)
1008
+ }
1009
+ };
1000
1010
  }
1001
1011
  };
1002
1012
  var server_plugin_default = plugin;
@@ -1020,28 +1030,29 @@ function createDoorstopWorkspaceProvider(context) {
1020
1030
  publicMetadata: { doorstop: true }
1021
1031
  }
1022
1032
  ];
1023
- },
1024
- request: (request) => {
1025
- switch (request.operation) {
1026
- case DOORSTOP_RUN_OPERATION:
1027
- return requestDoorstopBackend(context, request);
1028
- case DOORSTOP_BASELINE_OPERATION:
1029
- return requestDoorstopBaseline(context, request);
1030
- case DOORSTOP_GIT_STATUS_OPERATION:
1031
- return requestDoorstopGitStatus(context, request);
1032
- case DOORSTOP_GIT_STAGE_OPERATION:
1033
- return requestDoorstopGitStage(context, request);
1034
- case DOORSTOP_GIT_UNSTAGE_OPERATION:
1035
- return requestDoorstopGitUnstage(context, request);
1036
- case DOORSTOP_GIT_COMMIT_OPERATION:
1037
- return requestDoorstopGitCommit(context, request);
1038
- default:
1039
- return Promise.reject(unsupportedBackendOperationError(request.operation));
1040
- }
1041
1033
  }
1042
1034
  });
1043
1035
  }
1036
+ function dispatchDoorstopOperation(context, request) {
1037
+ switch (request.operation) {
1038
+ case DOORSTOP_RUN_OPERATION:
1039
+ return requestDoorstopBackend(context, request);
1040
+ case DOORSTOP_BASELINE_OPERATION:
1041
+ return requestDoorstopBaseline(context, request);
1042
+ case DOORSTOP_GIT_STATUS_OPERATION:
1043
+ return requestDoorstopGitStatus(context, request);
1044
+ case DOORSTOP_GIT_STAGE_OPERATION:
1045
+ return requestDoorstopGitStage(context, request);
1046
+ case DOORSTOP_GIT_UNSTAGE_OPERATION:
1047
+ return requestDoorstopGitUnstage(context, request);
1048
+ case DOORSTOP_GIT_COMMIT_OPERATION:
1049
+ return requestDoorstopGitCommit(context, request);
1050
+ default:
1051
+ return Promise.reject(unsupportedBackendOperationError(request.operation));
1052
+ }
1053
+ }
1044
1054
  export {
1045
1055
  createDoorstopWorkspaceProvider,
1046
- server_plugin_default as default
1056
+ server_plugin_default as default,
1057
+ dispatchDoorstopOperation
1047
1058
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opendoor-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Opendoor — a pi-web browser plugin that surfaces Doorstop requirements stored in the workspace: the document tree, per-item state (reviewed/unreviewed changes, suspect links), validation findings, and one-click doorstop CLI actions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -10,18 +10,26 @@
10
10
  },
11
11
  "keywords": [
12
12
  "pi",
13
+ "pi-package",
13
14
  "pi-web",
14
15
  "plugin",
16
+ "pi-web.dev-plugin",
17
+ "pi-web-plugin",
15
18
  "doorstop",
16
19
  "requirements"
17
20
  ],
21
+ "pi": {
22
+ "extensions": [
23
+ "./dist/pi-extension/index.js"
24
+ ]
25
+ },
18
26
  "piWeb": {
19
27
  "plugins": [
20
28
  {
21
29
  "id": "opendoor",
22
- "browserRoot": "browser",
23
- "module": "browser/pi-web-plugin.js",
24
- "serverModule": "server-plugin.js"
30
+ "browserRoot": "dist/browser",
31
+ "module": "dist/browser/pi-web-plugin.js",
32
+ "serverModule": "dist/server-plugin.js"
25
33
  }
26
34
  ]
27
35
  },
@@ -37,18 +45,19 @@
37
45
  "typecheck": "tsc --noEmit"
38
46
  },
39
47
  "peerDependencies": {
40
- "@jmfederico/pi-web": ">=1.202608.1"
48
+ "@earendil-works/pi-coding-agent": "*",
49
+ "@jmfederico/pi-web": ">=1.202609.1"
41
50
  },
42
51
  "devDependencies": {
43
- "@jmfederico/pi-web": "^1.202608.2",
52
+ "@jmfederico/pi-web": "1.202610.8",
44
53
  "@types/js-yaml": "^4.0.9",
45
- "@types/node": "^24.0.0",
46
- "@vitest/coverage-v8": "^4.1.11",
54
+ "@types/node": "^24.19.2",
55
+ "@vitest/coverage-v8": "^4.1.12",
47
56
  "esbuild": "^0.27.7",
48
- "happy-dom": "^20.11.1",
57
+ "happy-dom": "^20.14.6",
49
58
  "js-yaml": "^4.3.2",
50
59
  "lit": "^3.3.3",
51
60
  "typescript": "^6.0.3",
52
- "vitest": "^4.1.10"
61
+ "vitest": "^4.1.12"
53
62
  }
54
63
  }