openplanr 3.0.1 → 3.1.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/CHANGELOG.md +27 -0
- package/dist/cli/commands/init-deterministic.d.ts.map +1 -1
- package/dist/cli/commands/init-deterministic.js +49 -6
- package/dist/cli/commands/init-deterministic.js.map +1 -1
- package/dist/dashboard/.vite/manifest.json +2 -2
- package/dist/dashboard/assets/{index-Bk8DyO_2.css → index-BU5fzjyz.css} +1 -1
- package/dist/dashboard/assets/index-G7ygVDGg.js +572 -0
- package/dist/dashboard/assets/index-G7ygVDGg.js.map +1 -0
- package/dist/dashboard/dashboard-manifest.json +14 -14
- package/dist/dashboard/index.html +2 -2
- package/dist/models/types.d.ts +0 -46
- package/dist/models/types.d.ts.map +1 -1
- package/dist/services/capability-map-service.d.ts +4 -0
- package/dist/services/capability-map-service.d.ts.map +1 -1
- package/dist/services/capability-map-service.js +7 -1
- package/dist/services/capability-map-service.js.map +1 -1
- package/dist/services/claude-plugin-service.d.ts +6 -1
- package/dist/services/claude-plugin-service.d.ts.map +1 -1
- package/dist/services/claude-plugin-service.js +54 -15
- package/dist/services/claude-plugin-service.js.map +1 -1
- package/dist/services/config-service.d.ts +24 -1
- package/dist/services/config-service.d.ts.map +1 -1
- package/dist/services/config-service.js +69 -2
- package/dist/services/config-service.js.map +1 -1
- package/dist/services/git-service.d.ts +5 -0
- package/dist/services/git-service.d.ts.map +1 -1
- package/dist/services/git-service.js +27 -0
- package/dist/services/git-service.js.map +1 -1
- package/dist/services/linear-pull-service.d.ts.map +1 -1
- package/dist/services/linear-pull-service.js +1 -2
- package/dist/services/linear-pull-service.js.map +1 -1
- package/dist/services/pipeline-package-service.d.ts +2 -0
- package/dist/services/pipeline-package-service.d.ts.map +1 -1
- package/dist/services/pipeline-package-service.js +2 -1
- package/dist/services/pipeline-package-service.js.map +1 -1
- package/dist/services/runtime-change-summary.d.ts.map +1 -1
- package/dist/services/runtime-change-summary.js +4 -0
- package/dist/services/runtime-change-summary.js.map +1 -1
- package/dist/services/runtime-manager-service.d.ts.map +1 -1
- package/dist/services/runtime-manager-service.js +14 -3
- package/dist/services/runtime-manager-service.js.map +1 -1
- package/dist/services/spec-service.d.ts.map +1 -1
- package/dist/services/spec-service.js +11 -22
- package/dist/services/spec-service.js.map +1 -1
- package/dist/services/upgrade-service.d.ts.map +1 -1
- package/dist/services/upgrade-service.js +3 -1
- package/dist/services/upgrade-service.js.map +1 -1
- package/dist/templates/rules/shared/capability-map.md.hbs +2 -2
- package/lib/host-packages/adapter-registry.json +5 -5
- package/lib/host-packages/capability-map.json +89 -29
- package/lib/host-packages/claude/.claude-plugin/marketplace.json +2 -2
- package/lib/host-packages/claude/openplanr/.claude-plugin/plugin.json +1 -1
- package/lib/host-packages/claude/openplanr/.openplanr-content.json +69 -37
- package/lib/host-packages/claude/openplanr/CONNECTORS.md +62 -0
- package/lib/host-packages/claude/openplanr/README.md +1 -1
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-share.mjs +1 -1
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-shared-protocol-contracts-31a760fc.mjs +259 -36
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +54 -54
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +1 -1
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +259 -36
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +4 -4
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/package.json +3 -3
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +43 -24
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +279 -58
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +50 -42
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +282 -146
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +949 -316
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +143 -87
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +930 -183
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +17 -15
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-interface-primitives.js +375 -191
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-interface-primitives.js +140 -105
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +23 -23
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +4 -4
- package/lib/host-packages/claude/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +259 -36
- package/lib/host-packages/claude/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +4 -4
- package/lib/host-packages/claude/openplanr/skills/connect/SKILL.md +60 -0
- package/lib/host-packages/claude/openplanr/skills/connect/references/connectors.md +62 -0
- package/lib/host-packages/claude/openplanr/skills/openplanr/SKILL.md +4 -0
- package/lib/host-packages/claude/openplanr/skills/plan/SKILL.md +4 -1
- package/lib/host-packages/claude/openplanr/skills/plan/scripts/planning-ids.mjs +49 -3
- package/lib/host-packages/claude/openplanr/skills/share/SKILL.md +87 -0
- package/lib/host-packages/claude/openplanr/skills/share/references/connectors.md +62 -0
- package/lib/host-packages/claude/openplanr/skills/ship/SKILL.md +6 -3
- package/lib/host-packages/claude/openplanr/skills/ship/scripts/discover-verification.mjs +2 -1
- package/lib/host-packages/claude/openplanr/skills/sprint/SKILL.md +15 -6
- package/lib/host-packages/claude/openplanr/skills/sprint/references/connectors.md +62 -0
- package/lib/host-packages/claude/openplanr/skills/sprint/references/tracker-connections.md +9 -13
- package/lib/host-packages/claude/openplanr/skills/status/SKILL.md +14 -3
- package/lib/host-packages/claude/openplanr/skills/status/references/connectors.md +62 -0
- package/lib/host-packages/claude/openplanr/skills/status/references/tracker-connections.md +9 -13
- package/lib/host-packages/claude/openplanr/skills/sync/SKILL.md +25 -10
- package/lib/host-packages/claude/openplanr/skills/sync/references/connectors.md +62 -0
- package/lib/host-packages/claude/openplanr/skills/sync/references/tracker-connections.md +9 -13
- package/lib/host-packages/cursor/openplanr/.openplanr-content.json +68 -36
- package/lib/host-packages/cursor/openplanr/CONNECTORS.md +62 -0
- package/lib/host-packages/cursor/openplanr/manifest.json +4 -2
- package/lib/host-packages/cursor/openplanr/rules/planr-connect/references/connectors.md +62 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-connect.mdc +58 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-openplanr.mdc +4 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/planning-ids.mjs +49 -3
- package/lib/host-packages/cursor/openplanr/rules/planr-plan.mdc +4 -1
- package/lib/host-packages/cursor/openplanr/rules/planr-share/references/connectors.md +62 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-share.mdc +85 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-ship/scripts/discover-verification.mjs +2 -1
- package/lib/host-packages/cursor/openplanr/rules/planr-ship.mdc +6 -3
- package/lib/host-packages/cursor/openplanr/rules/planr-sprint/references/connectors.md +62 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-sprint/references/tracker-connections.md +9 -13
- package/lib/host-packages/cursor/openplanr/rules/planr-sprint.mdc +15 -6
- package/lib/host-packages/cursor/openplanr/rules/planr-status/references/connectors.md +62 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-status/references/tracker-connections.md +9 -13
- package/lib/host-packages/cursor/openplanr/rules/planr-status.mdc +14 -3
- package/lib/host-packages/cursor/openplanr/rules/planr-sync/references/connectors.md +62 -0
- package/lib/host-packages/cursor/openplanr/rules/planr-sync/references/tracker-connections.md +9 -13
- package/lib/host-packages/cursor/openplanr/rules/planr-sync.mdc +25 -10
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-share.mjs +1 -1
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-shared-protocol-contracts-31a760fc.mjs +259 -36
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +54 -54
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +1 -1
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +259 -36
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +4 -4
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/package.json +3 -3
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +43 -24
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +279 -58
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +50 -42
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +282 -146
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +949 -316
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +143 -87
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +930 -183
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +17 -15
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-interface-primitives.js +375 -191
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-interface-primitives.js +140 -105
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +23 -23
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +4 -4
- package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +259 -36
- package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +4 -4
- package/lib/host-packages/openai/.claude-plugin/marketplace.json +2 -2
- package/lib/host-packages/openai/openplanr/.codex-plugin/plugin.json +1 -1
- package/lib/host-packages/openai/openplanr/.openplanr-content.json +87 -39
- package/lib/host-packages/openai/openplanr/CONNECTORS.md +62 -0
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-share.mjs +1 -1
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-shared-protocol-contracts-31a760fc.mjs +259 -36
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +54 -54
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +1 -1
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +259 -36
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +4 -4
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/package.json +3 -3
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +43 -24
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +279 -58
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +50 -42
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +282 -146
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +949 -316
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +143 -87
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +930 -183
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +17 -15
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-interface-primitives.js +375 -191
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-interface-primitives.js +140 -105
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +23 -23
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +4 -4
- package/lib/host-packages/openai/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +259 -36
- package/lib/host-packages/openai/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +4 -4
- package/lib/host-packages/openai/openplanr/skills/connect/SKILL.md +59 -0
- package/lib/host-packages/openai/openplanr/skills/connect/agents/openai.yaml +6 -0
- package/lib/host-packages/openai/openplanr/skills/connect/openplanr.skill.json +37 -0
- package/lib/host-packages/openai/openplanr/skills/connect/references/connectors.md +62 -0
- package/lib/host-packages/openai/openplanr/skills/openplanr/SKILL.md +4 -0
- package/lib/host-packages/openai/openplanr/skills/plan/SKILL.md +4 -1
- package/lib/host-packages/openai/openplanr/skills/plan/scripts/planning-ids.mjs +49 -3
- package/lib/host-packages/openai/openplanr/skills/share/SKILL.md +86 -0
- package/lib/host-packages/openai/openplanr/skills/share/agents/openai.yaml +6 -0
- package/lib/host-packages/openai/openplanr/skills/share/openplanr.skill.json +37 -0
- package/lib/host-packages/openai/openplanr/skills/share/references/connectors.md +62 -0
- package/lib/host-packages/openai/openplanr/skills/ship/SKILL.md +6 -3
- package/lib/host-packages/openai/openplanr/skills/ship/scripts/discover-verification.mjs +2 -1
- package/lib/host-packages/openai/openplanr/skills/sprint/SKILL.md +15 -6
- package/lib/host-packages/openai/openplanr/skills/sprint/openplanr.skill.json +11 -0
- package/lib/host-packages/openai/openplanr/skills/sprint/references/connectors.md +62 -0
- package/lib/host-packages/openai/openplanr/skills/sprint/references/tracker-connections.md +9 -13
- package/lib/host-packages/openai/openplanr/skills/status/SKILL.md +14 -3
- package/lib/host-packages/openai/openplanr/skills/status/openplanr.skill.json +11 -0
- package/lib/host-packages/openai/openplanr/skills/status/references/connectors.md +62 -0
- package/lib/host-packages/openai/openplanr/skills/status/references/tracker-connections.md +9 -13
- package/lib/host-packages/openai/openplanr/skills/sync/SKILL.md +25 -10
- package/lib/host-packages/openai/openplanr/skills/sync/openplanr.skill.json +11 -0
- package/lib/host-packages/openai/openplanr/skills/sync/references/connectors.md +62 -0
- package/lib/host-packages/openai/openplanr/skills/sync/references/tracker-connections.md +9 -13
- package/lib/json-schema.mjs +313 -36
- package/lib/schemas/v1.0.0/spec.schema.json +120 -0
- package/lib/schemas/v1.0.0/story.schema.json +95 -0
- package/lib/schemas/v1.0.0/task.schema.json +171 -0
- package/lib/schemas/v1.7.0/spec.schema.json +117 -0
- package/lib/schemas/v1.7.0/story.schema.json +107 -0
- package/lib/schemas/v1.7.0/task.schema.json +217 -0
- package/package.json +31 -31
- package/dist/dashboard/assets/index-n08LJ6bK.js +0 -557
- package/dist/dashboard/assets/index-n08LJ6bK.js.map +0 -1
- package/dist/services/audit-log-service.d.ts +0 -47
- package/dist/services/audit-log-service.d.ts.map +0 -1
- package/dist/services/audit-log-service.js +0 -211
- package/dist/services/audit-log-service.js.map +0 -1
- package/dist/services/delta-apply-service.d.ts +0 -16
- package/dist/services/delta-apply-service.d.ts.map +0 -1
- package/dist/services/delta-apply-service.js +0 -74
- package/dist/services/delta-apply-service.js.map +0 -1
- package/dist/services/evidence-verifier.d.ts +0 -71
- package/dist/services/evidence-verifier.d.ts.map +0 -1
- package/dist/services/evidence-verifier.js +0 -174
- package/dist/services/evidence-verifier.js.map +0 -1
- package/dist/services/revise-apply-service.d.ts +0 -55
- package/dist/services/revise-apply-service.d.ts.map +0 -1
- package/dist/services/revise-apply-service.js +0 -265
- package/dist/services/revise-apply-service.js.map +0 -1
- package/dist/services/revise-cache-service.d.ts +0 -46
- package/dist/services/revise-cache-service.d.ts.map +0 -1
- package/dist/services/revise-cache-service.js +0 -89
- package/dist/services/revise-cache-service.js.map +0 -1
- package/dist/services/revise-plan-service.d.ts +0 -38
- package/dist/services/revise-plan-service.d.ts.map +0 -1
- package/dist/services/revise-plan-service.js +0 -153
- package/dist/services/revise-plan-service.js.map +0 -1
- /package/lib/host-packages/claude/openplanr/runtime/design/scripts/{design-runtime-support-336900df0d39.mjs → design-runtime-support-e34c388a8e59.mjs} +0 -0
- /package/lib/host-packages/cursor/openplanr/runtime/design/scripts/{design-runtime-support-336900df0d39.mjs → design-runtime-support-e34c388a8e59.mjs} +0 -0
- /package/lib/host-packages/openai/openplanr/runtime/design/scripts/{design-runtime-support-336900df0d39.mjs → design-runtime-support-e34c388a8e59.mjs} +0 -0
|
@@ -6,25 +6,40 @@ license: MIT
|
|
|
6
6
|
|
|
7
7
|
# OpenPlanr Sync
|
|
8
8
|
|
|
9
|
-
Reconcile local planning artifacts and, when requested,
|
|
10
|
-
Reason about conflicts in this active session. Local and
|
|
9
|
+
Reconcile local planning artifacts and, when requested, ~~project tracker state.
|
|
10
|
+
Reason about conflicts in this active session. Local and tracker work never require
|
|
11
11
|
the OpenPlanr CLI.
|
|
12
12
|
|
|
13
|
-
Remote steps run through the host's own
|
|
14
|
-
[tracker
|
|
15
|
-
mapping and what to tell the user when a
|
|
13
|
+
Remote steps run through the host's own ~~project tracker connection. The
|
|
14
|
+
[tracker sync reference](references/tracker-connections.md) holds which connection to
|
|
15
|
+
use per tracker, the status mapping, link shapes and what to tell the user when a
|
|
16
|
+
connection is missing; [connectors](references/connectors.md) lists the trackers
|
|
17
|
+
OpenPlanr syncs with and how to connect each in the current host.
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- Linear: the host's Linear connection.
|
|
19
|
+
When the GitHub CLI is signed in, the packaged [sync helper](scripts/sync.mjs) creates
|
|
20
|
+
and edits issues through `gh`; close or reopen an issue with `gh issue close` or
|
|
21
|
+
`gh issue reopen`. Other trackers go through the host's connection only.
|
|
22
|
+
`openplanr sync` only repairs local cross-references.
|
|
22
23
|
|
|
23
24
|
Audit is read-only by default. Apply local changes only when the request asks for
|
|
24
25
|
reconciliation, and push remote changes only when the request asks for external
|
|
25
26
|
synchronization. If a connection is unavailable, complete local reconciliation
|
|
26
27
|
and report only the external step that could not run.
|
|
27
28
|
|
|
29
|
+
## With nothing connected
|
|
30
|
+
|
|
31
|
+
Audit and local reconciliation run in full. Report the remote step as not run,
|
|
32
|
+
name ~~project tracker as the category it needs, and give the current host's step
|
|
33
|
+
from [connectors](references/connectors.md).
|
|
34
|
+
|
|
35
|
+
## If ~~project tracker is connected
|
|
36
|
+
|
|
37
|
+
Before any remote write, list every issue change: each issue to create, update,
|
|
38
|
+
close or reopen, with its title and the status it moves to. Write only when the
|
|
39
|
+
request explicitly asked for that push or the user approves the list. Afterwards,
|
|
40
|
+
report each item that was not updated and why, and name the category and product
|
|
41
|
+
the changes went to.
|
|
42
|
+
|
|
28
43
|
## Return
|
|
29
44
|
|
|
30
45
|
Lead with what is aligned, repaired or still blocked. Summarize aligned, locally
|
|
@@ -13,6 +13,17 @@
|
|
|
13
13
|
],
|
|
14
14
|
"execution": "host-agent",
|
|
15
15
|
"resources": [
|
|
16
|
+
{
|
|
17
|
+
"path": "references/connectors.md",
|
|
18
|
+
"kind": "reference",
|
|
19
|
+
"hosts": [
|
|
20
|
+
"claude-code",
|
|
21
|
+
"codex",
|
|
22
|
+
"chatgpt",
|
|
23
|
+
"cursor"
|
|
24
|
+
],
|
|
25
|
+
"executable": false
|
|
26
|
+
},
|
|
16
27
|
{
|
|
17
28
|
"path": "references/tracker-connections.md",
|
|
18
29
|
"kind": "reference",
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Connectors
|
|
2
|
+
|
|
3
|
+
OpenPlanr skills name a kind of service, such as `~~chat`, instead of a product. Connect
|
|
4
|
+
the product your team uses through your host: the vendor publishes the connector, and
|
|
5
|
+
you sign in through the host. OpenPlanr bundles no connectors; they and their credentials
|
|
6
|
+
live in your host. Two optional features need a token: Linear sync (`openplanr linear`) and
|
|
7
|
+
company sign-in (`openplanr company login`). Only when you use them does OpenPlanr store a
|
|
8
|
+
token, in your OS keychain, or in an encrypted file at `~/.planr/credentials.enc` if you set
|
|
9
|
+
`PLANR_CREDENTIAL_FILE_PASSPHRASE`. Tokens are sent only to Linear or to the company server
|
|
10
|
+
you sign in to.
|
|
11
|
+
`openplanr company logout` removes company tokens.
|
|
12
|
+
Every skill also works with nothing connected and reports any remote step it skipped.
|
|
13
|
+
|
|
14
|
+
| Category | Placeholder | Products |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| Chat | `~~chat` | Slack, Microsoft Teams |
|
|
17
|
+
| Project tracker | `~~project tracker` | Linear, GitHub Issues, Jira, Asana |
|
|
18
|
+
| Source control | `~~source control` | GitHub, GitLab, Bitbucket |
|
|
19
|
+
|
|
20
|
+
Never paste a token into a chat, file or command for OpenPlanr. After you connect a
|
|
21
|
+
service, start a new request so the host loads its tools.
|
|
22
|
+
|
|
23
|
+
## Chat (`~~chat`)
|
|
24
|
+
|
|
25
|
+
Share a status report, sprint note or release notes as a message draft that you review and send yourself.
|
|
26
|
+
|
|
27
|
+
| Product | Use | Claude Code | Codex | Cursor |
|
|
28
|
+
| --- | --- | --- | --- | --- |
|
|
29
|
+
| Slack | Message drafts | Run `/plugin install slack@claude-plugins-official`, then sign in to Slack from `/mcp`. A workspace admin may need to approve the app. | Install Slack from `/plugins` and sign in when asked. A workspace admin may need to approve the app. | Run `/add-plugin slack`, or install Slack from Customize, then sign in when asked. A workspace admin may need to approve the app. |
|
|
30
|
+
|
|
31
|
+
Message format for Slack, drafting through the connector: standard Markdown, which the connector converts on send ([Slack's messaging guidance](https://github.com/slackapi/slack-mcp-plugin/blob/main/skills/slack-messaging/SKILL.md)): `**bold**`, `_italic_`, `[link text](https://example.com)`, `-` lists, `>` quotes. Do not use mrkdwn's `*bold*` or `<url|text>` here.
|
|
32
|
+
|
|
33
|
+
Message format for Slack, text to paste: Slack mrkdwn ([formatting](https://docs.slack.dev/messaging/formatting-message-text)): `*bold*`, `_italic_`, `<https://example.com|link text>`, lines starting with `•` or `-` for lists, `>` for quotes; no headings or tables; escape `&`, `<` and `>` as `&`, `<` and `>`.
|
|
34
|
+
|
|
35
|
+
Also works with: [Microsoft Teams](https://learn.microsoft.com/en-us/microsoft-copilot-studio/mcp-teams-tools) (read for context). Add the vendor's connector in your host the same way.
|
|
36
|
+
|
|
37
|
+
Verified: [Slack](https://docs.slack.dev/ai/slack-mcp-server) on 2026-10-09, [Microsoft Teams](https://learn.microsoft.com/en-us/microsoft-copilot-studio/mcp-teams-tools) on 2026-10-09.
|
|
38
|
+
|
|
39
|
+
## Project tracker (`~~project tracker`)
|
|
40
|
+
|
|
41
|
+
Push planning status to linked issues after you approve each change, and read issue state back.
|
|
42
|
+
|
|
43
|
+
| Product | Use | Claude Code | Codex | Cursor |
|
|
44
|
+
| --- | --- | --- | --- | --- |
|
|
45
|
+
| Linear | OpenPlanr syncs with it | Run `/plugin install linear@claude-plugins-official`, then sign in to Linear from `/mcp`. | Install Linear from `/plugins` and sign in when asked. | Run `/add-plugin linear`, or install Linear from Customize, then sign in when asked. |
|
|
46
|
+
| GitHub Issues | OpenPlanr syncs with it | Install the GitHub CLI from cli.github.com and run `gh auth login`. | Install GitHub from `/plugins` and sign in when asked, or install the GitHub CLI and run `gh auth login`. | Install the GitHub CLI from cli.github.com and run `gh auth login`. |
|
|
47
|
+
|
|
48
|
+
Also works with: [Jira](https://support.atlassian.com/atlassian-ai-gateway/docs/get-started-with-the-atlassian-remote-mcp-server/) (read for context), [Asana](https://developers.asana.com/docs/using-asanas-mcp-server) (read for context). Add the vendor's connector in your host the same way.
|
|
49
|
+
|
|
50
|
+
Verified: [Linear](https://linear.app/docs/mcp) on 2026-10-09, [GitHub Issues](https://cli.github.com/manual/gh_auth_login) on 2026-10-09, [Jira](https://support.atlassian.com/atlassian-ai-gateway/docs/get-started-with-the-atlassian-remote-mcp-server/) on 2026-10-09, [Asana](https://developers.asana.com/docs/using-asanas-mcp-server) on 2026-10-09.
|
|
51
|
+
|
|
52
|
+
## Source control (`~~source control`)
|
|
53
|
+
|
|
54
|
+
Read pull request and branch state for status reports and sprint reviews.
|
|
55
|
+
|
|
56
|
+
| Product | Use | Claude Code | Codex | Cursor |
|
|
57
|
+
| --- | --- | --- | --- | --- |
|
|
58
|
+
| GitHub | Read for context | Install the GitHub CLI from cli.github.com and run `gh auth login`. | Install GitHub from `/plugins` and sign in when asked, or install the GitHub CLI and run `gh auth login`. | Install the GitHub CLI from cli.github.com and run `gh auth login`. |
|
|
59
|
+
|
|
60
|
+
Also works with: [GitLab](https://docs.gitlab.com/user/gitlab_duo/model_context_protocol/mcp_server/) (read for context), [Bitbucket](https://support.atlassian.com/atlassian-ai-gateway/docs/get-started-with-the-atlassian-remote-mcp-server/) (read for context). Add the vendor's connector in your host the same way.
|
|
61
|
+
|
|
62
|
+
Verified: [GitHub](https://cli.github.com/manual/gh_auth_login) on 2026-10-09, [GitLab](https://docs.gitlab.com/user/gitlab_duo/model_context_protocol/mcp_server/) on 2026-10-09, [Bitbucket](https://support.atlassian.com/atlassian-ai-gateway/docs/get-started-with-the-atlassian-remote-mcp-server/) on 2026-10-09.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Tracker sync reference
|
|
2
2
|
|
|
3
|
-
These skills store and read no
|
|
4
|
-
connection the host already has; the planning files stay the source of
|
|
3
|
+
These skills store and read no tracker or source control credentials. Remote steps
|
|
4
|
+
run through a connection the host already has; the planning files stay the source of
|
|
5
|
+
truth. OpenPlanr syncs with the trackers marked "OpenPlanr syncs with it" in
|
|
6
|
+
[connectors](connectors.md), and reads other products for context only.
|
|
5
7
|
|
|
6
8
|
## Use the host's connection
|
|
7
9
|
|
|
@@ -14,16 +16,10 @@ command, URL or file.
|
|
|
14
16
|
|
|
15
17
|
## When a connection is missing
|
|
16
18
|
|
|
17
|
-
Finish the local work first. Then report the remote step as not run
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
| --- | --- | --- |
|
|
22
|
-
| Claude Code | Run `/plugin install linear@claude-plugins-official`, then sign in to Linear from `/mcp`. | Install the GitHub CLI (cli.github.com) and run `gh auth login`. |
|
|
23
|
-
| Codex | Run `codex mcp add linear --url https://mcp.linear.app/mcp`, then `codex mcp login linear`. | Install the GitHub plugin from `/plugins`, or run `gh auth login`. |
|
|
24
|
-
| Cursor | Add Linear from the Cursor Marketplace and sign in to Linear. | Install the GitHub CLI (cli.github.com) and run `gh auth login`. |
|
|
25
|
-
|
|
26
|
-
Ask the user to try again once connected; never retry the remote step on your own.
|
|
19
|
+
Finish the local work first. Then report the remote step as not run, naming the
|
|
20
|
+
category, and give the one connect step for the current host from
|
|
21
|
+
[connectors](connectors.md). Ask the user to try again once connected; never retry the
|
|
22
|
+
remote step on your own.
|
|
27
23
|
|
|
28
24
|
## Status mapping
|
|
29
25
|
|
package/lib/json-schema.mjs
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Minimal JSON Schema (draft 2020-12) subset extracted from conformance/runner.mjs.
|
|
4
4
|
* Supports the constructs used by packaged Protocol schemas, including local
|
|
5
|
-
* JSON-pointer and caller-resolved external `$ref` references.
|
|
5
|
+
* JSON-pointer and caller-resolved external `$ref` references. Any other keyword or
|
|
6
|
+
* format is reported as an error rather than ignored.
|
|
6
7
|
*
|
|
7
8
|
* Zero third-party deps. Safe to import from node:test suites.
|
|
8
9
|
*/
|
|
@@ -30,6 +31,146 @@ const matchesType = (v, t) => {
|
|
|
30
31
|
|
|
31
32
|
const FORMAT_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
32
33
|
const FORMAT_DATETIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
34
|
+
const SUPPORTED_FORMATS = new Set(['date', 'date-time']);
|
|
35
|
+
|
|
36
|
+
const ANNOTATION_KEYWORDS = [
|
|
37
|
+
'$schema',
|
|
38
|
+
'$id',
|
|
39
|
+
'$defs',
|
|
40
|
+
'$comment',
|
|
41
|
+
'title',
|
|
42
|
+
'description',
|
|
43
|
+
'default',
|
|
44
|
+
'examples',
|
|
45
|
+
'deprecated',
|
|
46
|
+
'readOnly',
|
|
47
|
+
'writeOnly',
|
|
48
|
+
];
|
|
49
|
+
const VALIDATION_KEYWORDS = [
|
|
50
|
+
'$ref',
|
|
51
|
+
'type',
|
|
52
|
+
'const',
|
|
53
|
+
'enum',
|
|
54
|
+
'minLength',
|
|
55
|
+
'maxLength',
|
|
56
|
+
'pattern',
|
|
57
|
+
'format',
|
|
58
|
+
'minimum',
|
|
59
|
+
'maximum',
|
|
60
|
+
'exclusiveMinimum',
|
|
61
|
+
'minItems',
|
|
62
|
+
'maxItems',
|
|
63
|
+
'prefixItems',
|
|
64
|
+
'items',
|
|
65
|
+
'uniqueItems',
|
|
66
|
+
'contains',
|
|
67
|
+
'minContains',
|
|
68
|
+
'maxContains',
|
|
69
|
+
'required',
|
|
70
|
+
'properties',
|
|
71
|
+
'additionalProperties',
|
|
72
|
+
'maxProperties',
|
|
73
|
+
'dependentRequired',
|
|
74
|
+
'unevaluatedProperties',
|
|
75
|
+
'oneOf',
|
|
76
|
+
'anyOf',
|
|
77
|
+
'allOf',
|
|
78
|
+
'if',
|
|
79
|
+
'then',
|
|
80
|
+
'else',
|
|
81
|
+
'not',
|
|
82
|
+
];
|
|
83
|
+
const KNOWN_KEYWORDS = new Set([...ANNOTATION_KEYWORDS, ...VALIDATION_KEYWORDS]);
|
|
84
|
+
const TYPE_NAMES = new Set(['array', 'boolean', 'integer', 'null', 'number', 'object', 'string']);
|
|
85
|
+
|
|
86
|
+
const isSchema = (value) =>
|
|
87
|
+
typeof value === 'boolean' ||
|
|
88
|
+
(value !== null && typeof value === 'object' && !Array.isArray(value));
|
|
89
|
+
/** @type {(value: unknown) => value is Record<string, unknown>} */
|
|
90
|
+
const isPlainObject = (value) =>
|
|
91
|
+
value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
92
|
+
const isCount = (value) => Number.isSafeInteger(value) && value >= 0;
|
|
93
|
+
/** @type {(value: unknown) => value is string[]} */
|
|
94
|
+
const isUniqueStrings = (value) =>
|
|
95
|
+
Array.isArray(value) &&
|
|
96
|
+
value.every((item) => typeof item === 'string') &&
|
|
97
|
+
new Set(value).size === value.length;
|
|
98
|
+
const isSchemaArray = (value) => Array.isArray(value) && value.length > 0 && value.every(isSchema);
|
|
99
|
+
const isSchemaMap = (value) => isPlainObject(value) && Object.values(value).every(isSchema);
|
|
100
|
+
/** @type {Map<string, RegExp | null>} */
|
|
101
|
+
const compiledPatterns = new Map();
|
|
102
|
+
const MAX_COMPILED_PATTERNS = 1024;
|
|
103
|
+
/** @type {(source: string) => RegExp | null} */
|
|
104
|
+
const tryCompilePattern = (source) => {
|
|
105
|
+
try {
|
|
106
|
+
return new RegExp(source);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (error instanceof SyntaxError) return null;
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
/** Returns the compiled pattern, or null when it is not a valid regular expression. */
|
|
113
|
+
const compilePattern = (source) => {
|
|
114
|
+
if (!compiledPatterns.has(source)) {
|
|
115
|
+
if (compiledPatterns.size >= MAX_COMPILED_PATTERNS) compiledPatterns.clear();
|
|
116
|
+
compiledPatterns.set(source, tryCompilePattern(source));
|
|
117
|
+
}
|
|
118
|
+
return compiledPatterns.get(source) ?? null;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const describeValue = (value) => {
|
|
122
|
+
const text = String(JSON.stringify(value));
|
|
123
|
+
return text.length > 80 ? `${text.slice(0, 77)}...` : text;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Expected value shape of each supported keyword, per draft 2020-12.
|
|
128
|
+
* @type {Record<string, [string, (value: unknown) => boolean]>}
|
|
129
|
+
*/
|
|
130
|
+
const KEYWORD_SHAPES = {
|
|
131
|
+
$ref: ['a string', (value) => typeof value === 'string'],
|
|
132
|
+
$defs: ['an object of schemas', isSchemaMap],
|
|
133
|
+
type: [
|
|
134
|
+
'a type name or a non-empty array of unique type names',
|
|
135
|
+
(value) =>
|
|
136
|
+
(typeof value === 'string' && TYPE_NAMES.has(value)) ||
|
|
137
|
+
(isUniqueStrings(value) && value.length > 0 && value.every((name) => TYPE_NAMES.has(name))),
|
|
138
|
+
],
|
|
139
|
+
enum: ['an array', Array.isArray],
|
|
140
|
+
minLength: ['a non-negative integer', isCount],
|
|
141
|
+
maxLength: ['a non-negative integer', isCount],
|
|
142
|
+
pattern: [
|
|
143
|
+
'a valid regular expression string',
|
|
144
|
+
(value) => typeof value === 'string' && compilePattern(value) !== null,
|
|
145
|
+
],
|
|
146
|
+
minimum: ['a finite number', Number.isFinite],
|
|
147
|
+
maximum: ['a finite number', Number.isFinite],
|
|
148
|
+
exclusiveMinimum: ['a finite number', Number.isFinite],
|
|
149
|
+
minItems: ['a non-negative integer', isCount],
|
|
150
|
+
maxItems: ['a non-negative integer', isCount],
|
|
151
|
+
prefixItems: ['a non-empty array of schemas', isSchemaArray],
|
|
152
|
+
items: ['a schema', isSchema],
|
|
153
|
+
uniqueItems: ['a boolean', (value) => typeof value === 'boolean'],
|
|
154
|
+
contains: ['a schema', isSchema],
|
|
155
|
+
minContains: ['a non-negative integer', isCount],
|
|
156
|
+
maxContains: ['a non-negative integer', isCount],
|
|
157
|
+
required: ['an array of unique strings', isUniqueStrings],
|
|
158
|
+
properties: ['an object of schemas', isSchemaMap],
|
|
159
|
+
additionalProperties: ['a schema', isSchema],
|
|
160
|
+
maxProperties: ['a non-negative integer', isCount],
|
|
161
|
+
dependentRequired: [
|
|
162
|
+
'an object of unique string arrays',
|
|
163
|
+
(value) => isPlainObject(value) && Object.values(value).every(isUniqueStrings),
|
|
164
|
+
],
|
|
165
|
+
unevaluatedProperties: ['a schema', isSchema],
|
|
166
|
+
oneOf: ['a non-empty array of schemas', isSchemaArray],
|
|
167
|
+
anyOf: ['a non-empty array of schemas', isSchemaArray],
|
|
168
|
+
allOf: ['a non-empty array of schemas', isSchemaArray],
|
|
169
|
+
if: ['a schema', isSchema],
|
|
170
|
+
then: ['a schema', isSchema],
|
|
171
|
+
else: ['a schema', isSchema],
|
|
172
|
+
not: ['a schema', isSchema],
|
|
173
|
+
};
|
|
33
174
|
|
|
34
175
|
function resolveJsonPointer(root, reference) {
|
|
35
176
|
if (reference === '#') return root;
|
|
@@ -41,12 +182,73 @@ function resolveJsonPointer(root, reference) {
|
|
|
41
182
|
.reduce((value, part) => value?.[part], root);
|
|
42
183
|
}
|
|
43
184
|
|
|
185
|
+
/** Frozen schema nodes that passed every node check; a mutable node is re-checked on each call. */
|
|
186
|
+
const wellFormedSchemas = new WeakSet();
|
|
187
|
+
|
|
188
|
+
/** True when the node and every keyword value its checks read can no longer change. */
|
|
189
|
+
const isFrozenNode = (schema) =>
|
|
190
|
+
Object.isFrozen(schema) &&
|
|
191
|
+
Object.values(schema).every(Object.isFrozen) &&
|
|
192
|
+
(!isPlainObject(schema.dependentRequired) ||
|
|
193
|
+
Object.values(schema.dependentRequired).every(Object.isFrozen));
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Records unsupported keywords and formats and malformed keyword values, once per schema node.
|
|
197
|
+
* They go to a shared sink so `not`, `if` and combinator branches cannot swallow them.
|
|
198
|
+
*/
|
|
199
|
+
const checkSchemaNode = (schema, path, context) => {
|
|
200
|
+
if (wellFormedSchemas.has(schema) || context.checkedSchemas.has(schema)) return;
|
|
201
|
+
context.checkedSchemas.add(schema);
|
|
202
|
+
const errorCount = context.schemaErrors.length;
|
|
203
|
+
for (const keyword of Object.keys(schema)) {
|
|
204
|
+
if (!KNOWN_KEYWORDS.has(keyword) && !keyword.startsWith('x-')) {
|
|
205
|
+
context.schemaErrors.push({
|
|
206
|
+
path,
|
|
207
|
+
rule: 'unsupported-keyword',
|
|
208
|
+
detail: `schema keyword '${keyword}' is not supported by this validator`,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (Object.hasOwn(schema, 'format') && !SUPPORTED_FORMATS.has(schema.format)) {
|
|
213
|
+
context.schemaErrors.push({
|
|
214
|
+
path,
|
|
215
|
+
rule: 'unsupported-format',
|
|
216
|
+
detail: `schema format ${JSON.stringify(schema.format)} is not supported by this validator`,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
for (const [keyword, [expected, isValid]] of Object.entries(KEYWORD_SHAPES)) {
|
|
220
|
+
if (Object.hasOwn(schema, keyword) && !isValid(schema[keyword])) {
|
|
221
|
+
context.schemaErrors.push({
|
|
222
|
+
path,
|
|
223
|
+
rule: 'schema',
|
|
224
|
+
detail: `schema keyword '${keyword}' must be ${expected}, got ${describeValue(schema[keyword])}`,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (context.schemaErrors.length === errorCount && isFrozenNode(schema)) {
|
|
229
|
+
wellFormedSchemas.add(schema);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/** Records an unresolvable or circular `$ref` once per schema node. */
|
|
234
|
+
const reportReference = (schema, path, detail, context) => {
|
|
235
|
+
if (context.reportedReferences.has(schema)) return;
|
|
236
|
+
context.reportedReferences.add(schema);
|
|
237
|
+
context.schemaErrors.push({ path, rule: '$ref', detail });
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/** Validates one node and returns the object property names it evaluated. */
|
|
44
241
|
const validateNode = (value, schema, path, errs, context) => {
|
|
45
|
-
|
|
242
|
+
/** @type {Set<string>} */
|
|
243
|
+
const evaluated = new Set();
|
|
244
|
+
if (schema === true) return evaluated;
|
|
46
245
|
if (schema === false) {
|
|
47
246
|
errs.push({ path, rule: 'schema:false', detail: 'value not allowed' });
|
|
48
|
-
return;
|
|
247
|
+
return evaluated;
|
|
49
248
|
}
|
|
249
|
+
// A non-schema child is reported by its parent's keyword-shape check.
|
|
250
|
+
if (!isSchema(schema)) return evaluated;
|
|
251
|
+
checkSchemaNode(schema, path, context);
|
|
50
252
|
|
|
51
253
|
if (typeof schema?.$ref === 'string') {
|
|
52
254
|
const reference = schema.$ref;
|
|
@@ -61,21 +263,27 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
61
263
|
nextRoot = result?.rootSchema ?? resolved;
|
|
62
264
|
resolvedBase = result?.base ?? resolved?.$id ?? context.base;
|
|
63
265
|
}
|
|
64
|
-
if (!resolved) {
|
|
65
|
-
|
|
66
|
-
|
|
266
|
+
if (!isSchema(resolved)) {
|
|
267
|
+
const detail =
|
|
268
|
+
resolved === undefined || resolved === null
|
|
269
|
+
? `could not resolve schema reference ${reference}`
|
|
270
|
+
: `schema reference ${reference} resolved to ${typeOf(resolved)}, not a schema`;
|
|
271
|
+
reportReference(schema, path, detail, context);
|
|
272
|
+
return evaluated;
|
|
67
273
|
}
|
|
68
|
-
|
|
274
|
+
// Recursion that descends into the instance changes `path`; only a repeat at the same location loops.
|
|
275
|
+
const referenceKey = `${path} ${context.base ?? '<root>'}:${reference}`;
|
|
69
276
|
if (context.referenceStack.includes(referenceKey)) {
|
|
70
|
-
|
|
71
|
-
return;
|
|
277
|
+
reportReference(schema, path, `circular schema reference ${reference}`, context);
|
|
278
|
+
return evaluated;
|
|
72
279
|
}
|
|
73
|
-
validateNode(value, resolved, path, errs, {
|
|
280
|
+
const referenced = validateNode(value, resolved, path, errs, {
|
|
74
281
|
...context,
|
|
75
282
|
rootSchema: nextRoot,
|
|
76
283
|
base: resolvedBase,
|
|
77
284
|
referenceStack: [...context.referenceStack, referenceKey],
|
|
78
285
|
});
|
|
286
|
+
for (const key of referenced) evaluated.add(key);
|
|
79
287
|
}
|
|
80
288
|
|
|
81
289
|
if (schema.type !== undefined) {
|
|
@@ -86,7 +294,7 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
86
294
|
rule: 'type',
|
|
87
295
|
detail: `expected ${types.join('|')}, got ${typeOf(value)}`,
|
|
88
296
|
});
|
|
89
|
-
return;
|
|
297
|
+
return evaluated;
|
|
90
298
|
}
|
|
91
299
|
}
|
|
92
300
|
|
|
@@ -125,22 +333,13 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
125
333
|
detail: `length ${value.length} > ${schema.maxLength}`,
|
|
126
334
|
});
|
|
127
335
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
} catch (e) {
|
|
138
|
-
errs.push({
|
|
139
|
-
path,
|
|
140
|
-
rule: 'pattern',
|
|
141
|
-
detail: `invalid regex /${schema.pattern}/: ${e instanceof Error ? e.message : String(e)}`,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
336
|
+
const pattern = typeof schema.pattern === 'string' ? compilePattern(schema.pattern) : null;
|
|
337
|
+
if (pattern && !pattern.test(value)) {
|
|
338
|
+
errs.push({
|
|
339
|
+
path,
|
|
340
|
+
rule: 'pattern',
|
|
341
|
+
detail: `value ${JSON.stringify(value)} does not match /${schema.pattern}/`,
|
|
342
|
+
});
|
|
144
343
|
}
|
|
145
344
|
if (typeof schema.format === 'string') {
|
|
146
345
|
if (schema.format === 'date' && !FORMAT_DATE.test(value)) {
|
|
@@ -166,6 +365,13 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
166
365
|
if (typeof schema.maximum === 'number' && value > schema.maximum) {
|
|
167
366
|
errs.push({ path, rule: 'maximum', detail: `value ${value} > ${schema.maximum}` });
|
|
168
367
|
}
|
|
368
|
+
if (typeof schema.exclusiveMinimum === 'number' && value <= schema.exclusiveMinimum) {
|
|
369
|
+
errs.push({
|
|
370
|
+
path,
|
|
371
|
+
rule: 'exclusiveMinimum',
|
|
372
|
+
detail: `value ${value} <= ${schema.exclusiveMinimum}`,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
169
375
|
}
|
|
170
376
|
|
|
171
377
|
if (Array.isArray(value)) {
|
|
@@ -231,8 +437,34 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
231
437
|
}
|
|
232
438
|
}
|
|
233
439
|
}
|
|
440
|
+
if (
|
|
441
|
+
typeof schema.maxProperties === 'number' &&
|
|
442
|
+
Object.keys(value).length > schema.maxProperties
|
|
443
|
+
) {
|
|
444
|
+
errs.push({
|
|
445
|
+
path,
|
|
446
|
+
rule: 'maxProperties',
|
|
447
|
+
detail: `property count ${Object.keys(value).length} > ${schema.maxProperties}`,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
if (schema.dependentRequired !== null && typeof schema.dependentRequired === 'object') {
|
|
451
|
+
for (const [trigger, dependencies] of Object.entries(schema.dependentRequired)) {
|
|
452
|
+
if (!Object.hasOwn(value, trigger) || !Array.isArray(dependencies)) continue;
|
|
453
|
+
for (const dependency of dependencies) {
|
|
454
|
+
if (!Object.hasOwn(value, dependency)) {
|
|
455
|
+
errs.push({
|
|
456
|
+
path,
|
|
457
|
+
rule: 'dependentRequired',
|
|
458
|
+
detail: `property '${trigger}' requires property '${dependency}'`,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
234
464
|
const props = schema.properties || {};
|
|
235
465
|
for (const [k, v] of Object.entries(value)) {
|
|
466
|
+
if (Object.hasOwn(props, k)) evaluated.add(k);
|
|
467
|
+
else if (schema.additionalProperties !== undefined) evaluated.add(k);
|
|
236
468
|
if (!Object.hasOwn(props, k)) {
|
|
237
469
|
if (schema.additionalProperties === false) {
|
|
238
470
|
errs.push({ path, rule: 'additionalProperties', detail: `unknown property '${k}'` });
|
|
@@ -253,8 +485,11 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
253
485
|
let matched = 0;
|
|
254
486
|
for (const sub of schema.oneOf) {
|
|
255
487
|
const e = [];
|
|
256
|
-
validateNode(value, sub, path, e, context);
|
|
257
|
-
if (e.length === 0)
|
|
488
|
+
const branch = validateNode(value, sub, path, e, context);
|
|
489
|
+
if (e.length === 0) {
|
|
490
|
+
matched++;
|
|
491
|
+
for (const key of branch) evaluated.add(key);
|
|
492
|
+
}
|
|
258
493
|
}
|
|
259
494
|
if (matched !== 1) {
|
|
260
495
|
const titles = schema.oneOf.map((s) => s.title || '(unnamed)').join(' | ');
|
|
@@ -270,8 +505,11 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
270
505
|
let matched = 0;
|
|
271
506
|
for (const sub of schema.anyOf) {
|
|
272
507
|
const candidateErrors = [];
|
|
273
|
-
validateNode(value, sub, path, candidateErrors, context);
|
|
274
|
-
if (candidateErrors.length === 0)
|
|
508
|
+
const branch = validateNode(value, sub, path, candidateErrors, context);
|
|
509
|
+
if (candidateErrors.length === 0) {
|
|
510
|
+
matched += 1;
|
|
511
|
+
for (const key of branch) evaluated.add(key);
|
|
512
|
+
}
|
|
275
513
|
}
|
|
276
514
|
if (matched === 0) {
|
|
277
515
|
const titles = schema.anyOf.map((sub) => sub.title || '(unnamed)').join(' | ');
|
|
@@ -285,15 +523,22 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
285
523
|
|
|
286
524
|
if (Array.isArray(schema.allOf)) {
|
|
287
525
|
for (const sub of schema.allOf) {
|
|
288
|
-
validateNode(value, sub, path, errs, context);
|
|
526
|
+
for (const key of validateNode(value, sub, path, errs, context)) evaluated.add(key);
|
|
289
527
|
}
|
|
290
528
|
}
|
|
291
529
|
|
|
292
530
|
if (schema.if !== undefined) {
|
|
293
531
|
const conditionErrors = [];
|
|
294
|
-
validateNode(value, schema.if, path, conditionErrors, context);
|
|
295
|
-
const
|
|
296
|
-
if (
|
|
532
|
+
const condition = validateNode(value, schema.if, path, conditionErrors, context);
|
|
533
|
+
const passed = conditionErrors.length === 0;
|
|
534
|
+
if (passed) for (const key of condition) evaluated.add(key);
|
|
535
|
+
const branch = passed ? schema.then : schema.else;
|
|
536
|
+
if (branch !== undefined) {
|
|
537
|
+
const branchErrors = [];
|
|
538
|
+
const branchEvaluated = validateNode(value, branch, path, branchErrors, context);
|
|
539
|
+
errs.push(...branchErrors);
|
|
540
|
+
if (branchErrors.length === 0) for (const key of branchEvaluated) evaluated.add(key);
|
|
541
|
+
}
|
|
297
542
|
}
|
|
298
543
|
|
|
299
544
|
if (schema.not !== undefined) {
|
|
@@ -303,6 +548,24 @@ const validateNode = (value, schema, path, errs, context) => {
|
|
|
303
548
|
errs.push({ path, rule: 'not', detail: 'value matched a forbidden subschema' });
|
|
304
549
|
}
|
|
305
550
|
}
|
|
551
|
+
|
|
552
|
+
if (
|
|
553
|
+
schema.unevaluatedProperties !== undefined &&
|
|
554
|
+
value !== null &&
|
|
555
|
+
typeof value === 'object' &&
|
|
556
|
+
!Array.isArray(value)
|
|
557
|
+
) {
|
|
558
|
+
for (const [k, v] of Object.entries(value)) {
|
|
559
|
+
if (evaluated.has(k)) continue;
|
|
560
|
+
if (schema.unevaluatedProperties === false) {
|
|
561
|
+
errs.push({ path, rule: 'unevaluatedProperties', detail: `unevaluated property '${k}'` });
|
|
562
|
+
} else {
|
|
563
|
+
validateNode(v, schema.unevaluatedProperties, `${path}.${k}`, errs, context);
|
|
564
|
+
}
|
|
565
|
+
evaluated.add(k);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return evaluated;
|
|
306
569
|
};
|
|
307
570
|
|
|
308
571
|
/** @type {typeof import('./json-schema.d.mts').validateJson} */
|
|
@@ -315,13 +578,27 @@ export const validateJson = (
|
|
|
315
578
|
} = {},
|
|
316
579
|
) => {
|
|
317
580
|
const errs = [];
|
|
581
|
+
/** @type {{ path: string, rule: string, detail: string }[]} */
|
|
582
|
+
const schemaErrors = [];
|
|
583
|
+
if (!isSchema(schema)) {
|
|
584
|
+
return [
|
|
585
|
+
{
|
|
586
|
+
path: '$',
|
|
587
|
+
rule: 'schema',
|
|
588
|
+
detail: `expected a schema object or boolean, got ${typeOf(schema)}`,
|
|
589
|
+
},
|
|
590
|
+
];
|
|
591
|
+
}
|
|
318
592
|
validateNode(value, schema, '$', errs, {
|
|
319
593
|
rootSchema: schema,
|
|
320
594
|
resolveRef,
|
|
321
595
|
base,
|
|
322
596
|
referenceStack: [],
|
|
597
|
+
schemaErrors,
|
|
598
|
+
checkedSchemas: new WeakSet(),
|
|
599
|
+
reportedReferences: new WeakSet(),
|
|
323
600
|
});
|
|
324
|
-
return errs;
|
|
601
|
+
return [...schemaErrors, ...errs];
|
|
325
602
|
};
|
|
326
603
|
|
|
327
604
|
/**
|