pm-github 2026.10.27 → 2026.10.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +52 -39
  2. package/README.md +75 -3
  3. package/dist/closeout.d.ts.map +1 -1
  4. package/dist/closeout.js +71 -30
  5. package/dist/closeout.js.map +1 -1
  6. package/dist/discussion-schema.d.ts +110 -0
  7. package/dist/discussion-schema.d.ts.map +1 -0
  8. package/dist/discussion-schema.js +41 -0
  9. package/dist/discussion-schema.js.map +1 -0
  10. package/dist/discussions.d.ts +26 -0
  11. package/dist/discussions.d.ts.map +1 -0
  12. package/dist/discussions.js +215 -0
  13. package/dist/discussions.js.map +1 -0
  14. package/dist/gate.d.ts.map +1 -1
  15. package/dist/gate.js +10 -4
  16. package/dist/gate.js.map +1 -1
  17. package/dist/identity.d.ts.map +1 -1
  18. package/dist/identity.js +2 -1
  19. package/dist/identity.js.map +1 -1
  20. package/dist/import-fidelity.d.ts +8 -3
  21. package/dist/import-fidelity.d.ts.map +1 -1
  22. package/dist/import-fidelity.js +39 -10
  23. package/dist/import-fidelity.js.map +1 -1
  24. package/dist/import-tags.d.ts +15 -0
  25. package/dist/import-tags.d.ts.map +1 -0
  26. package/dist/import-tags.js +35 -0
  27. package/dist/import-tags.js.map +1 -0
  28. package/dist/index.d.ts +52 -41
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +188 -89
  31. package/dist/index.js.map +1 -1
  32. package/dist/issue-forms.d.ts +1 -1
  33. package/dist/issue-forms.d.ts.map +1 -1
  34. package/dist/issue-forms.js +8 -5
  35. package/dist/issue-forms.js.map +1 -1
  36. package/dist/label-names.d.ts +6 -0
  37. package/dist/label-names.d.ts.map +1 -0
  38. package/dist/label-names.js +15 -0
  39. package/dist/label-names.js.map +1 -0
  40. package/dist/labels.d.ts +161 -0
  41. package/dist/labels.d.ts.map +1 -0
  42. package/dist/labels.js +235 -0
  43. package/dist/labels.js.map +1 -0
  44. package/dist/projects.d.ts +6 -4
  45. package/dist/projects.d.ts.map +1 -1
  46. package/dist/projects.js +25 -24
  47. package/dist/projects.js.map +1 -1
  48. package/dist/refresh.d.ts +2 -1
  49. package/dist/refresh.d.ts.map +1 -1
  50. package/dist/refresh.js +19 -9
  51. package/dist/refresh.js.map +1 -1
  52. package/dist/webhooks.d.ts.map +1 -1
  53. package/dist/webhooks.js +5 -2
  54. package/dist/webhooks.js.map +1 -1
  55. package/dist/workflow-diagnostic.d.ts +9 -0
  56. package/dist/workflow-diagnostic.d.ts.map +1 -0
  57. package/dist/workflow-diagnostic.js +62 -0
  58. package/dist/workflow-diagnostic.js.map +1 -0
  59. package/dist/workspace.d.ts +18 -0
  60. package/dist/workspace.d.ts.map +1 -0
  61. package/dist/workspace.js +39 -0
  62. package/dist/workspace.js.map +1 -0
  63. package/docs/discussion-capture-example.json +14 -0
  64. package/docs/discussion-capture.md +44 -0
  65. package/manifest.json +2 -2
  66. package/package.json +15 -10
@@ -0,0 +1,62 @@
1
+ /** Produce one workflow refusal while keeping raw command output private. */
2
+ import { readFileSync } from "node:fs";
3
+ import { ownValue } from "./own-record.js";
4
+ /** Decode a CLI report or the JSON diagnostic following stderr progress messages. */
5
+ export function decodeWorkflowDiagnostic(text, depth = 0) {
6
+ try {
7
+ const value = JSON.parse(text.slice(Math.max(0, text.indexOf("{"))));
8
+ if (value === null || typeof value !== "object" || Array.isArray(value))
9
+ return {};
10
+ const report = value;
11
+ if (typeof report.detail === "string" && depth < 2) {
12
+ const nested = decodeWorkflowDiagnostic(report.detail, depth + 1);
13
+ return Object.keys(nested).length ? nested : { reason: report.detail };
14
+ }
15
+ return report;
16
+ }
17
+ catch {
18
+ return {};
19
+ }
20
+ }
21
+ /** Read an optional receipt; missing or malformed files never expose their paths. */
22
+ function receipt(file) {
23
+ try {
24
+ return readFileSync(file, "utf8");
25
+ }
26
+ catch {
27
+ return "";
28
+ }
29
+ }
30
+ /** Pick one actionable failure; the workflow privacy-scans the resulting text before logging it. */
31
+ export function workflowFailure(report, diff = "") {
32
+ if (diff) {
33
+ const item = /\/(?:[^/\s]+\/)*([a-zA-Z0-9_-]+)\.toon\b/.exec(diff)?.[1];
34
+ const field = /^[+-]([a-z][a-z0-9_]*):/m.exec(diff)?.[1];
35
+ return `Repeat import changed ${item ?? "tracker state"}${field ? ` field ${field}` : ""}; retry with the current pm-github release and inspect concurrent source changes. No branch was published.`;
36
+ }
37
+ if (Array.isArray(report.findings) && report.findings.length) {
38
+ const finding = decodeWorkflowDiagnostic(JSON.stringify(report.findings[0]));
39
+ return `Import privacy gate refused item ${finding.item_id}, field ${finding.field}, rule ${finding.rule}; remove the source content or review an explicit gate allowlist entry. No branch was published.`;
40
+ }
41
+ if (Array.isArray(report.plan)) {
42
+ for (const value of report.plan) {
43
+ const row = decodeWorkflowDiagnostic(JSON.stringify(value));
44
+ const missing = Array.isArray(row.missing_evidence) ? row.missing_evidence.map(entry => decodeWorkflowDiagnostic(JSON.stringify(entry))) : [];
45
+ const failed = missing.find(entry => entry.code === "close_failed");
46
+ if (failed || row.action === "skip" && ["head_evidence_unavailable", "required_checks_unverified", "incomplete_merge"].includes(String(row.code))) {
47
+ return `Closeout refused item ${row.item}, PR #${row.pr}: ${failed ? failed.reason : row.reason} No branch was published.`;
48
+ }
49
+ }
50
+ }
51
+ const reason = ownValue(report, "reason");
52
+ return typeof reason === "string" ? `${reason} No branch was published.`
53
+ : "Command produced no usable failure receipt; rerun the failed command with --json using the same tracker and token permissions. No branch was published.";
54
+ }
55
+ /** Assemble a private candidate diagnostic from stdout, stderr and an optional repeat-import diff. */
56
+ export function readWorkflowFailure(reportFile, errorFile, diffFile) {
57
+ const output = decodeWorkflowDiagnostic(receipt(reportFile));
58
+ const error = decodeWorkflowDiagnostic(receipt(errorFile));
59
+ const fullFailure = output.command === "github closeout" && typeof output.failed === "number" && output.failed > 0;
60
+ return { reason: workflowFailure(fullFailure ? output : Object.keys(error).length ? error : output, receipt(diffFile)) };
61
+ }
62
+ //# sourceMappingURL=workflow-diagnostic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-diagnostic.js","sourceRoot":"","sources":["../workflow-diagnostic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /** Resolve tracker identity and schema through the same public SDK boundary as host commands. */
2
+ import { type RuntimeStatusRegistry } from "@unbrained/pm-cli/sdk";
3
+ /** Accept a workspace, tracker directory or relative CLI path before reading its schema. */
4
+ export declare function readGithubWorkspace(pmPath: string): Promise<{
5
+ pmRoot: string;
6
+ settings: import("@unbrained/pm-cli/sdk").PmSettings;
7
+ types: import("@unbrained/pm-cli/sdk").ItemTypeRegistry;
8
+ statuses: RuntimeStatusRegistry;
9
+ }>;
10
+ /** Canonicalize configured aliases using the host's own status input normalizer. */
11
+ export declare function canonicalGithubStatus(value: string | undefined, registry?: RuntimeStatusRegistry): string;
12
+ /** Classify native statuses by configured lifecycle roles for provider transitions. */
13
+ export declare function githubStatusKind(value: string | undefined, registry?: RuntimeStatusRegistry): string;
14
+ /** Include every configured terminal role when mapping native state to a provider. */
15
+ export declare function githubStatusIsTerminal(value: string | undefined, registry?: RuntimeStatusRegistry): boolean;
16
+ /** Public status registry accepted by pure planners and comparison helpers. */
17
+ export type GithubStatusRegistry = RuntimeStatusRegistry;
18
+ //# sourceMappingURL=workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../workspace.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,OAAO,EAAkL,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAKnP,4FAA4F;AAC5F,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM;;;;;GAKvD;AAED,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,wBAAkB,GAAG,MAAM,CAEnG;AAED,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,wBAAkB,GAAG,MAAM,CAU9F;AAED,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,wBAAkB,GAAG,OAAO,CAErG;AAED,+EAA+E;AAC/E,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /** Resolve tracker identity and schema through the same public SDK boundary as host commands. */
2
+ import { getActiveExtensionRegistrations, isTerminalStatus, normalizeStatusInput, readSettings, resolveItemTypeRegistry, resolvePmRoot, resolveRuntimeStatusRegistry, SETTINGS_DEFAULTS } from "@unbrained/pm-cli/sdk";
3
+ /** Built-in contracts preserve pure planner compatibility when no workspace is supplied. */
4
+ const defaultStatuses = resolveRuntimeStatusRegistry(SETTINGS_DEFAULTS.schema);
5
+ /** Accept a workspace, tracker directory or relative CLI path before reading its schema. */
6
+ export async function readGithubWorkspace(pmPath) {
7
+ const pmRoot = resolvePmRoot(process.cwd(), pmPath);
8
+ const settings = await readSettings(pmRoot);
9
+ return { pmRoot, settings, types: resolveItemTypeRegistry(settings, getActiveExtensionRegistrations()),
10
+ statuses: resolveRuntimeStatusRegistry(settings.schema) };
11
+ }
12
+ /** Canonicalize configured aliases using the host's own status input normalizer. */
13
+ export function canonicalGithubStatus(value, registry = defaultStatuses) {
14
+ return normalizeStatusInput(value, registry) ?? value ?? registry.open_status;
15
+ }
16
+ /** Classify native statuses by configured lifecycle roles for provider transitions. */
17
+ export function githubStatusKind(value, registry = defaultStatuses) {
18
+ const status = canonicalGithubStatus(value, registry);
19
+ if (registry.terminal_done_statuses.has(status))
20
+ return "closed";
21
+ if (registry.terminal_canceled_statuses.has(status))
22
+ return "canceled";
23
+ if (isTerminalStatus(status, registry))
24
+ return "closed";
25
+ if (registry.blocked_statuses.has(status))
26
+ return "blocked";
27
+ if (registry.draft_statuses.has(status))
28
+ return "draft";
29
+ if (status === registry.in_progress_status)
30
+ return "in_progress";
31
+ if (status === registry.open_status)
32
+ return "open";
33
+ return status;
34
+ }
35
+ /** Include every configured terminal role when mapping native state to a provider. */
36
+ export function githubStatusIsTerminal(value, registry = defaultStatuses) {
37
+ return isTerminalStatus(value ?? registry.open_status, registry);
38
+ }
39
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../workspace.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,OAAO,EAAE,+BAA+B,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,4BAA4B,EAAE,iBAAiB,EAA8B,MAAM,uBAAuB,CAAC;AAEnP,4FAA4F;AAC5F,MAAM,eAAe,GAAG,4BAA4B,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAE/E,4FAA4F;AAC5F,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAc;IACtD,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAC5C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,uBAAuB,CAAC,QAAQ,EAAE,+BAA+B,EAAE,CAAC;QACpG,QAAQ,EAAE,4BAA4B,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,qBAAqB,CAAC,KAAyB,EAAE,QAAQ,GAAG,eAAe;IACzF,OAAO,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,KAAK,IAAI,QAAQ,CAAC,WAAW,CAAC;AAChF,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,gBAAgB,CAAC,KAAyB,EAAE,QAAQ,GAAG,eAAe;IACpF,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACtD,IAAI,QAAQ,CAAC,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjE,IAAI,QAAQ,CAAC,0BAA0B,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,UAAU,CAAC;IACvE,IAAI,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IACxD,IAAI,QAAQ,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,OAAO,CAAC;IACxD,IAAI,MAAM,KAAK,QAAQ,CAAC,kBAAkB;QAAE,OAAO,aAAa,CAAC;IACjE,IAAI,MAAM,KAAK,QAAQ,CAAC,WAAW;QAAE,OAAO,MAAM,CAAC;IACnD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,sBAAsB,CAAC,KAAyB,EAAE,QAAQ,GAAG,eAAe;IAC1F,OAAO,gBAAgB,CAAC,KAAK,IAAI,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,14 @@
1
+ {
2
+ "repo": "example/project",
3
+ "issue": 42,
4
+ "item": "project-context",
5
+ "selections": [
6
+ {
7
+ "comment": 123456,
8
+ "start": 0,
9
+ "end": 19,
10
+ "quote": "Use the native SDK.",
11
+ "marked": true
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,44 @@
1
+ ## Selected GitHub discussion decisions
2
+
3
+ Copy `docs/discussion-capture-example.json`, select an existing native context item and edit the repository, issue, comment ID and exact span. `marked: true` records an explicit selection by the caller. The adapter never infers decisions from prose, labels, imperative language or an entire unselected thread. Empty selections create nothing. Issue body extraction and PR review findings are outside this feature.
4
+
5
+ ```sh
6
+ pm github discussion capture --input selection.json --json
7
+ pm github discussion capture --input selection.json --approve REVIEWED_DIGEST --json
8
+ ```
9
+
10
+ Review the complete first receipt before supplying its `approval`. Each row exposes the proposed native Decision ID, source author ID/login, canonical comment URL, timestamp, SHA-256 revision and exact quoted span. Routine output contains only selected passages. Default pm rendering is compact TOON; `--json` uses the same typed receipt. `created`, `unchanged`, `skipped` and `refused` count row states. A successful envelope can contain independent refused rows: inspect these counters and remedies. A refusal never permits a dependent write.
11
+
12
+ Offsets are zero-based, half-open UTF-16 code units, matching JavaScript `body.slice(start, end)`. CRLF, Unicode and whitespace are preserved exactly in the JSON evidence body; the SDK still owns native field normalization. Issue, comment and author IDs are positive safe integers; counters are nonnegative integers bounded by the ten-selection limit. Native item selectors and emitted identifiers are 1..512 code units without NUL, CRLF or path separators; the SDK resolves whitespace aliases and canonical item identifiers. Configured status IDs are 1..512 code units without NUL or CRLF and retain the SDK's own status semantics, including Unicode and slash-bearing names. Author logins accept bounded human and bot identities. Offsets are at most 65536, quotes are 1..4096 code units and at most ten dense selections are accepted. Comments are bounded to 65536 code units. No sparse arrays, unknown request keys, nonfinite numbers or implicit coercion are accepted. Output defaults to 65536 JSON bytes; `--max-output-bytes` accepts an integer 1024..131072. Planning reserves space for retry/refusal rows before any create. Split larger captures. Repeated spans in one request are skipped explicitly.
13
+
14
+ Source revisions hash the ordered tuple of comment ID, exact full body, API issue URL, comment URL, update timestamp, author ID and login. An edit anywhere in that selected comment invalidates the prior approval, even when the selected quote is unchanged. Each source must belong to the selected issue; the HTTP reader verifies the issue is not a pull request. A supplied SDK reader must provide current issue-comment snapshots under that same contract. GitHub access is read-only: only GET requests are issued.
15
+
16
+ Each capture creates one native `Decision`, parented explicitly to the selected context item, with empty dependencies and no sprint, release, goal or objective. Native linked docs point to the comment and source revision. Evidence is serialized JSON data, never executable instructions, a prompt, markdown expansion or a shell command. The provider author remains separate from the host's audit author. pm-evidence can cite this native Decision body for its own resolution/review workflows; this adapter does not maintain another citation store.
17
+
18
+ The SDK surface is `captureGithubDiscussion(pmRoot, input, hostSdk, options)`, exported with `DiscussionCaptureInput`, `DiscussionCaptureReport`, `DiscussionDecisionEvidence`, `DiscussionCaptureOptions`, `DiscussionCommentReader` and `DiscussionRefusalCode`. Pass the host-injected `ExtensionCommandSdk`; standalone callers may construct it through public `createExtensionCommandSdk` and `PmClient`. No executable/symlink traversal or eager SDK client acquisition occurs. Public SDK helpers resolve workspace schema and read native history. `validateDiscussionCaptureReport` validates JSON/TOON-decoded producer receipts through the same shared schema. No import/export/sync route bypasses the common capture path; the existing issue importer/exporter is unchanged.
19
+
20
+ ```ts
21
+ import { captureGithubDiscussion, type DiscussionCaptureInput } from "pm-github";
22
+ import { PmClient, createExtensionCommandSdk } from "@unbrained/pm-cli/sdk";
23
+
24
+ const pmRoot = "tracker";
25
+ const sdk = createExtensionCommandSdk(pmRoot, new PmClient({ pmRoot, author: "reviewer", noExtensions: true }));
26
+ const selection: DiscussionCaptureInput = {
27
+ repo: "example/project", issue: 42, item: "project-context",
28
+ selections: [{ comment: 123456, start: 0, end: 19, quote: "Use the native SDK.", marked: true }],
29
+ };
30
+ const plan = await captureGithubDiscussion(pmRoot, selection, sdk);
31
+ // Review plan.item, plan.status, plan.rows and plan.approval before a separate apply.
32
+ ```
33
+
34
+ Semantic planning invokes the SDK's `previewItemMutations` in disposable staging with the same native create options as apply. Configured native create requirements produce an individual `native_refused` row. As documented by that SDK, staging does not validate executable extension hooks, pending migrations or final commit state; those can still yield a retryable `write_failed` row. Planning creates no source tracker journal or items.
35
+
36
+ Approval binds tracker identity, parent history, schema, selected evidence and existing target revisions. Decision IDs bind canonical parent identity and the entire evidence revision; creation audit markers also bind the expected parent history revision. Retry reads current state and preserves existing or edited records. A normal native edit causes the existing Decision to be skipped with a reason, rather than overwritten or duplicated. A missing document with retained native history is a tombstone, not a fresh target: an old approval becomes stale and a new preview preserves that history without resurrecting the item. A source edit can be captured as a new Decision only after a fresh reviewed preview. Bad source records refuse individually and leave other independent selections reviewable. Completed creates survive later failures, and retry revalidates every remaining target; there is no frozen-plan replay, saved checkpoint, destructive compensation or second observation append.
37
+
38
+ Immediately before each create the adapter re-reads source, parent history, schema and deterministic target. History length plus exact stream digest detects parent ABA. Peer-created captures are reported as unchanged only when current SDK state and the immutable creation marker agree; foreign or edited targets are skipped. The final read-to-create window remains optimistic: ordinary SDK/CLI writers can change source or parent after the final read. `commitWorkspaceTransaction` does not exclude those writers, so this adapter makes no cross-writer isolation claim. The native SDK's exclusive deterministic create prevents duplicate IDs; it cannot atomically guard a remote comment and a separate parent. All rows are independent, and no dependent evidence is appended after a skipped create.
39
+
40
+ Typed fatal refusals carry `code` and a bounded `context.required` remedy: `invalid_selection` (fix the strict selection), `host_required` (use a supported injected host), `source_unavailable` (restore issue/comment read access), `source_changed` (reselect exact bytes), `target_unavailable` (restore native history), `stale_plan` (review a fresh preview), and `invalid_output` (fix the budget or split the request). Per-row `write_failed` remedies preserve prior successes and request a tracker health check. Foreign records are retained with `foreign_or_edited` rather than throwing.
41
+
42
+ ## Without git
43
+
44
+ Discussion preview and capture work in plain directory trackers and invoke no git executable. A GitHub repository identity is a remote source locator; it does not require a local git repository. Existing GitHub operations that inspect a proposed git diff still require git, or their documented explicit diff input.
package/manifest.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "pm-github",
3
- "version": "2026.10.27",
3
+ "version": "2026.10.29",
4
4
  "description": "GitHub Issues + Projects v2 integration. Imports issues as pm items (`pm github import`), exports pm items as GitHub issues, syncs issue state, and bidirectionally syncs pm items with a GitHub Projects v2 board (`pm github project import/sync/list/fields`) — mapping pm status to the board Status column with idempotent, no-data-loss provenance.",
5
5
  "author": "@unbraind",
6
6
  "entry": "./dist/index.js",
7
- "pm_min_version": "2026.10.5",
7
+ "pm_min_version": "2026.10.17",
8
8
  "priority": 50,
9
9
  "capabilities": [
10
10
  "commands",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pm-github",
3
- "version": "2026.10.27",
3
+ "version": "2026.10.29",
4
4
  "description": "GitHub Issues importer extension for pm-cli",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,12 +17,14 @@
17
17
  "dist/",
18
18
  "manifest.json",
19
19
  "README.md",
20
- "CHANGELOG.md"
20
+ "CHANGELOG.md",
21
+ "docs/discussion-capture.md",
22
+ "docs/discussion-capture-example.json"
21
23
  ],
22
24
  "scripts": {
23
25
  "build": "tsc",
24
26
  "build:test": "tsc -p tsconfig.test.json",
25
- "test": "npm run build && npm run build:test && node --test test/*.test.ts",
27
+ "test": "npm run build && npm run build:test && node --test --test-concurrency=2 test/*.test.ts",
26
28
  "prepack": "npm run build",
27
29
  "typecheck": "tsc --noEmit",
28
30
  "check": "npm run typecheck",
@@ -41,22 +43,23 @@
41
43
  "privacy": "node scripts/privacy-gate.ts",
42
44
  "lint": "eslint .",
43
45
  "duplication": "node scripts/duplication-gate.ts",
44
- "test:bun": "bun_status=0; for test_file in test/*.test.ts; do bun test --timeout 600000 \"$test_file\" || bun_status=1; done; exit \"$bun_status\""
46
+ "test:bun": "bun_status=0; for test_file in test/*.test.ts; do bun test --timeout 600000 \"$test_file\" || bun_status=1; done; exit \"$bun_status\"",
47
+ "test:minimum-sdk": "npm run build && node --test --test-concurrency=1 test/minimum-sdk-consumer.test.ts"
45
48
  },
46
49
  "peerDependencies": {
47
- "@unbrained/pm-cli": ">=2026.10.5"
50
+ "@unbrained/pm-cli": ">=2026.10.17"
48
51
  },
49
52
  "devDependencies": {
50
53
  "@babel/eslint-parser": "8.0.7",
51
54
  "@babel/plugin-syntax-typescript": "8.0.3",
52
55
  "@toon-format/toon": "4.4.0",
53
56
  "@types/node": "26.6.5",
54
- "@unbrained/pm-cli": "2026.10.20",
57
+ "@unbrained/pm-cli": ">=2026.10.17",
55
58
  "c8": "12.0.0",
56
59
  "eslint": "10.13.0",
57
60
  "jscpd": "5.4.2",
58
- "pm-changelog": "2026.10.20",
59
- "pm-ops": "2026.10.20",
61
+ "pm-changelog": "2026.10.24",
62
+ "pm-ops": "2026.10.24",
60
63
  "typescript": "7.0.2"
61
64
  },
62
65
  "keywords": [
@@ -100,10 +103,12 @@
100
103
  },
101
104
  "docs": [
102
105
  "README.md",
103
- "CHANGELOG.md"
106
+ "CHANGELOG.md",
107
+ "docs/discussion-capture.md"
104
108
  ],
105
109
  "examples": [
106
- "README.md"
110
+ "README.md",
111
+ "docs/discussion-capture-example.json"
107
112
  ]
108
113
  },
109
114
  "publishConfig": {