@open-agent-toolkit/cli 0.2.65 → 0.2.66

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 (142) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/configuration.md +19 -1
  3. package/assets/docs/cli-utilities/index.md +3 -0
  4. package/assets/docs/cli-utilities/remote-project-management.md +191 -0
  5. package/assets/docs/reference/cli-reference.md +12 -1
  6. package/assets/docs/reference/file-locations.md +22 -0
  7. package/assets/docs/reference/oat-directory-structure.md +30 -17
  8. package/assets/docs/reference/troubleshooting.md +44 -0
  9. package/assets/docs/workflows/skills/index.md +33 -0
  10. package/assets/public-package-versions.json +4 -4
  11. package/assets/skills/oat-doctor/SKILL.md +4 -4
  12. package/assets/skills/oat-pjm-remote/SKILL.md +101 -0
  13. package/assets/skills/oat-pjm-remote/references/external-action-protocol.md +43 -0
  14. package/dist/commands/backlog/new.d.ts +1 -0
  15. package/dist/commands/backlog/new.d.ts.map +1 -1
  16. package/dist/commands/backlog/new.js +3 -1
  17. package/dist/commands/config/index.d.ts +1 -0
  18. package/dist/commands/config/index.d.ts.map +1 -1
  19. package/dist/commands/config/index.js +232 -1
  20. package/dist/commands/pjm/doctor.d.ts +2 -0
  21. package/dist/commands/pjm/doctor.d.ts.map +1 -1
  22. package/dist/commands/pjm/doctor.js +71 -1
  23. package/dist/commands/pjm/index.d.ts +2 -0
  24. package/dist/commands/pjm/index.d.ts.map +1 -1
  25. package/dist/commands/pjm/index.js +3 -0
  26. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts +60 -0
  27. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts.map +1 -0
  28. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.js +151 -0
  29. package/dist/commands/pjm/remote/association.d.ts +27 -0
  30. package/dist/commands/pjm/remote/association.d.ts.map +1 -0
  31. package/dist/commands/pjm/remote/association.js +68 -0
  32. package/dist/commands/pjm/remote/authority.d.ts +210 -0
  33. package/dist/commands/pjm/remote/authority.d.ts.map +1 -0
  34. package/dist/commands/pjm/remote/authority.js +341 -0
  35. package/dist/commands/pjm/remote/batch.d.ts +15 -0
  36. package/dist/commands/pjm/remote/batch.d.ts.map +1 -0
  37. package/dist/commands/pjm/remote/batch.js +126 -0
  38. package/dist/commands/pjm/remote/closeout.d.ts +69 -0
  39. package/dist/commands/pjm/remote/closeout.d.ts.map +1 -0
  40. package/dist/commands/pjm/remote/closeout.js +208 -0
  41. package/dist/commands/pjm/remote/create-binding.d.ts +62 -0
  42. package/dist/commands/pjm/remote/create-binding.d.ts.map +1 -0
  43. package/dist/commands/pjm/remote/create-binding.js +126 -0
  44. package/dist/commands/pjm/remote/credential-safety.d.ts +7 -0
  45. package/dist/commands/pjm/remote/credential-safety.d.ts.map +1 -0
  46. package/dist/commands/pjm/remote/credential-safety.js +21 -0
  47. package/dist/commands/pjm/remote/discussion.d.ts +49 -0
  48. package/dist/commands/pjm/remote/discussion.d.ts.map +1 -0
  49. package/dist/commands/pjm/remote/discussion.js +124 -0
  50. package/dist/commands/pjm/remote/doctor.d.ts +22 -0
  51. package/dist/commands/pjm/remote/doctor.d.ts.map +1 -0
  52. package/dist/commands/pjm/remote/doctor.js +380 -0
  53. package/dist/commands/pjm/remote/external-action.d.ts +208 -0
  54. package/dist/commands/pjm/remote/external-action.d.ts.map +1 -0
  55. package/dist/commands/pjm/remote/external-action.js +367 -0
  56. package/dist/commands/pjm/remote/host-execution.d.ts +28 -0
  57. package/dist/commands/pjm/remote/host-execution.d.ts.map +1 -0
  58. package/dist/commands/pjm/remote/host-execution.js +63 -0
  59. package/dist/commands/pjm/remote/index.d.ts +41 -0
  60. package/dist/commands/pjm/remote/index.d.ts.map +1 -0
  61. package/dist/commands/pjm/remote/index.js +280 -0
  62. package/dist/commands/pjm/remote/jira-adf.d.ts +35 -0
  63. package/dist/commands/pjm/remote/jira-adf.d.ts.map +1 -0
  64. package/dist/commands/pjm/remote/jira-adf.js +169 -0
  65. package/dist/commands/pjm/remote/lifecycle.d.ts +163 -0
  66. package/dist/commands/pjm/remote/lifecycle.d.ts.map +1 -0
  67. package/dist/commands/pjm/remote/lifecycle.js +224 -0
  68. package/dist/commands/pjm/remote/local-projection.d.ts +30 -0
  69. package/dist/commands/pjm/remote/local-projection.d.ts.map +1 -0
  70. package/dist/commands/pjm/remote/local-projection.js +95 -0
  71. package/dist/commands/pjm/remote/managed-markdown.d.ts +30 -0
  72. package/dist/commands/pjm/remote/managed-markdown.d.ts.map +1 -0
  73. package/dist/commands/pjm/remote/managed-markdown.js +128 -0
  74. package/dist/commands/pjm/remote/migrate.d.ts +14 -0
  75. package/dist/commands/pjm/remote/migrate.d.ts.map +1 -0
  76. package/dist/commands/pjm/remote/migrate.js +32 -0
  77. package/dist/commands/pjm/remote/operation-state.d.ts +15 -0
  78. package/dist/commands/pjm/remote/operation-state.d.ts.map +1 -0
  79. package/dist/commands/pjm/remote/operation-state.js +143 -0
  80. package/dist/commands/pjm/remote/outbound-projection-safety.d.ts +25 -0
  81. package/dist/commands/pjm/remote/outbound-projection-safety.d.ts.map +1 -0
  82. package/dist/commands/pjm/remote/outbound-projection-safety.js +99 -0
  83. package/dist/commands/pjm/remote/output.d.ts +60 -0
  84. package/dist/commands/pjm/remote/output.d.ts.map +1 -0
  85. package/dist/commands/pjm/remote/output.js +48 -0
  86. package/dist/commands/pjm/remote/preview.d.ts +88 -0
  87. package/dist/commands/pjm/remote/preview.d.ts.map +1 -0
  88. package/dist/commands/pjm/remote/preview.js +154 -0
  89. package/dist/commands/pjm/remote/provider-conformance.d.ts +13 -0
  90. package/dist/commands/pjm/remote/provider-conformance.d.ts.map +1 -0
  91. package/dist/commands/pjm/remote/provider-conformance.js +37 -0
  92. package/dist/commands/pjm/remote/provider.d.ts +57 -0
  93. package/dist/commands/pjm/remote/provider.d.ts.map +1 -0
  94. package/dist/commands/pjm/remote/provider.js +28 -0
  95. package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts +46 -0
  96. package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts.map +1 -0
  97. package/dist/commands/pjm/remote/providers/github-publication-safety.js +126 -0
  98. package/dist/commands/pjm/remote/providers/github.d.ts +207 -0
  99. package/dist/commands/pjm/remote/providers/github.d.ts.map +1 -0
  100. package/dist/commands/pjm/remote/providers/github.js +1456 -0
  101. package/dist/commands/pjm/remote/providers/jira.d.ts +245 -0
  102. package/dist/commands/pjm/remote/providers/jira.d.ts.map +1 -0
  103. package/dist/commands/pjm/remote/providers/jira.js +1650 -0
  104. package/dist/commands/pjm/remote/providers/linear.d.ts +182 -0
  105. package/dist/commands/pjm/remote/providers/linear.d.ts.map +1 -0
  106. package/dist/commands/pjm/remote/providers/linear.js +1289 -0
  107. package/dist/commands/pjm/remote/purpose-policy.d.ts +29 -0
  108. package/dist/commands/pjm/remote/purpose-policy.d.ts.map +1 -0
  109. package/dist/commands/pjm/remote/purpose-policy.js +118 -0
  110. package/dist/commands/pjm/remote/reconcile.d.ts +42 -0
  111. package/dist/commands/pjm/remote/reconcile.d.ts.map +1 -0
  112. package/dist/commands/pjm/remote/reconcile.js +101 -0
  113. package/dist/commands/pjm/remote/resolution.d.ts +122 -0
  114. package/dist/commands/pjm/remote/resolution.d.ts.map +1 -0
  115. package/dist/commands/pjm/remote/resolution.js +243 -0
  116. package/dist/commands/pjm/remote/schema.d.ts +9309 -0
  117. package/dist/commands/pjm/remote/schema.d.ts.map +1 -0
  118. package/dist/commands/pjm/remote/schema.js +1503 -0
  119. package/dist/commands/pjm/remote/service.d.ts +20 -0
  120. package/dist/commands/pjm/remote/service.d.ts.map +1 -0
  121. package/dist/commands/pjm/remote/service.js +5121 -0
  122. package/dist/commands/pjm/remote/shared-storage.d.ts +36 -0
  123. package/dist/commands/pjm/remote/shared-storage.d.ts.map +1 -0
  124. package/dist/commands/pjm/remote/shared-storage.js +110 -0
  125. package/dist/commands/pjm/remote/snapshot.d.ts +32 -0
  126. package/dist/commands/pjm/remote/snapshot.d.ts.map +1 -0
  127. package/dist/commands/pjm/remote/snapshot.js +140 -0
  128. package/dist/commands/pjm/remote/storage-locator.d.ts +29 -0
  129. package/dist/commands/pjm/remote/storage-locator.d.ts.map +1 -0
  130. package/dist/commands/pjm/remote/storage-locator.js +62 -0
  131. package/dist/commands/pjm/remote/store.d.ts +85 -0
  132. package/dist/commands/pjm/remote/store.d.ts.map +1 -0
  133. package/dist/commands/pjm/remote/store.js +553 -0
  134. package/dist/commands/pjm/remote/verification.d.ts +40 -0
  135. package/dist/commands/pjm/remote/verification.d.ts.map +1 -0
  136. package/dist/commands/pjm/remote/verification.js +100 -0
  137. package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
  138. package/dist/commands/tools/shared/pack-manifest.js +1 -0
  139. package/dist/config/oat-config.d.ts +26 -0
  140. package/dist/config/oat-config.d.ts.map +1 -1
  141. package/dist/config/oat-config.js +186 -2
  142. package/package.json +2 -2
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: oat-pjm-remote
3
+ description: Use when explicitly intaking, publishing, refreshing, reconciling, or continuing an OAT PJM remote binding through a live host capability. Keeps policy, approval, state, and success verdicts in the OAT CLI while the host discovers and invokes currently granted execution capabilities.
4
+ disable-model-invocation: false
5
+ user-invocable: true
6
+ allowed-tools: Read, Bash, AskUserQuestion
7
+ metadata:
8
+ version: 1.0.2
9
+ ---
10
+
11
+ # Operate Remote Project Bindings
12
+
13
+ Use the OAT CLI as the authority for policy, approval, durable operation state,
14
+ and verified outcomes. Your role is limited to live capability discovery,
15
+ executing the exact semantic action emitted by the CLI, and returning one
16
+ bounded sanitized observation.
17
+
18
+ ## Progress Indicators (User-Facing)
19
+
20
+ Provide short updates at the durable boundaries: preflight, CLI preview or
21
+ handoff, live capability selection, observation submission, and terminal CLI
22
+ verdict. Never describe a host invocation result as success before the CLI has
23
+ verified authoritative read-back.
24
+
25
+ ```text
26
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
27
+ OAT ▸ REMOTE PROJECT MANAGEMENT
28
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
29
+ ```
30
+
31
+ - `[1/4] Verifying PJM adoption and local policy…`
32
+ - `[2/4] Discovering a matching live host capability…`
33
+ - `[3/4] Preparing the durable CLI action with bounded evidence…`
34
+ - `[4/4] Submitting evidence for CLI verification…`
35
+
36
+ ## Preflight
37
+
38
+ Run:
39
+
40
+ ```bash
41
+ oat pjm doctor --json
42
+ ```
43
+
44
+ Continue only when `adoption.state` is `declared` or `inferred-legacy`. For
45
+ `none` or `partial-initialization`, stop before any remote action and direct the
46
+ operator to `oat pjm init`.
47
+
48
+ ## Live Capability Discovery
49
+
50
+ Before the first provider-contacting remote command, inspect currently granted
51
+ MCP or connector tools and their live descriptions. Select a capability only
52
+ when its semantic operation, provider, and exact
53
+ account/workspace/site/repository context match the request.
54
+
55
+ If no capable granted connector exists, you may inspect an already configured
56
+ provider CLI and its live help before the first attempt. Do not install a tool,
57
+ request authentication material, encode or retain its command dialect, or
58
+ switch execution surfaces after an attempt begins.
59
+
60
+ Construct only the bounded provider-neutral capability evidence accepted by the
61
+ CLI: provider, exact context, surface kind, availability, semantic capabilities,
62
+ observation time, and evidence digest. Never include a native tool name, schema,
63
+ catalog, raw description, help text, request, or response.
64
+
65
+ ## Lifecycle
66
+
67
+ 1. Complete live capability discovery before the first provider-contacting
68
+ remote command.
69
+ 2. Run the requested `oat pjm remote` command in JSON mode and pass the bounded
70
+ evidence through `--capability-evidence-stdin`. For mutations, also supply
71
+ only the current caller-owned authority evidence requested by the command.
72
+ 3. Treat CLI policy, preview, approval, safety, and terminal verdicts as
73
+ authoritative. Never broaden a field mask or reconstruct publication
74
+ content from repository files.
75
+ 4. When the CLI returns `pending` with `externalAction`, follow
76
+ [the action protocol](references/external-action-protocol.md) and execute the
77
+ exact emitted semantic action at most once.
78
+ 5. Submit exactly one sanitized observation through `operation continue`.
79
+ 6. Continue only while the CLI emits another exact action for the same durable
80
+ operation. Stop on any terminal CLI envelope.
81
+
82
+ Never infer success from a connector result, process exit, or visible remote
83
+ change. Only a CLI `ok` envelope after authoritative read-back is success.
84
+
85
+ ## Discussion Reads
86
+
87
+ Discussion is read-only evidence. Honor the CLI-provided page/cursor and maximum
88
+ item limit. Return only the bounded sanitized observation. Do not persist a
89
+ thread, assignee list, activity history, or native response. A requested local
90
+ distillation is a separate local edit and never comment synchronization.
91
+
92
+ ## Safety Boundary
93
+
94
+ - Never read, request, print, or persist authentication headers or credential
95
+ values.
96
+ - Never scan the repository, worktree, Git history, or arbitrary files for
97
+ secrets.
98
+ - Never add fields to the normalized outbound projection.
99
+ - Never pass native requests, raw provider payloads, tool catalogs, or help
100
+ output into durable OAT evidence.
101
+ - Never declare remote success directly.
@@ -0,0 +1,43 @@
1
+ # External Action Protocol
2
+
3
+ The CLI emits a versioned provider-neutral action containing a durable
4
+ operation ID, step ID, action digest, provider context, semantic intent,
5
+ expected observation contract, and (for mutation) the exact outbound
6
+ projection and safety-result digests.
7
+
8
+ Initial live connector discovery (or configured-CLI-help fallback) and bounded
9
+ provider-neutral capability evidence construction happen before the first
10
+ provider-contacting remote command. The evidence is passed through
11
+ `--capability-evidence-stdin`; native tool names, schemas, catalogs,
12
+ descriptions, help text, requests, and responses are excluded.
13
+
14
+ Before execution:
15
+
16
+ 1. Confirm the action belongs to the current durable operation and step.
17
+ 2. Confirm the previously selected capability remains currently granted.
18
+ 3. Confirm semantic operation and exact provider context.
19
+ 4. For a mutation, confirm the projection and safety digests are present. Do
20
+ not reconstruct, enrich, or reinterpret the projection.
21
+
22
+ Execute at most once. Return only one observation with:
23
+
24
+ - the unchanged operation ID, step ID, and action digest;
25
+ - observation time and host-surface kind;
26
+ - bounded capability evidence digest and exact provider context;
27
+ - sanitized classification, durable identity/aliases when observed,
28
+ allowlisted fields, revision evidence, and a diagnostic code.
29
+
30
+ Do not return a native request, raw response, captured catalog, live help text,
31
+ process environment, authentication data, or provider-generated success claim.
32
+ Use:
33
+
34
+ ```bash
35
+ oat pjm remote operation continue \
36
+ --operation <operation-id> \
37
+ --observation-stdin \
38
+ --json
39
+ ```
40
+
41
+ A repeated, stale, mismatched, oversized, or unsanitized observation must be
42
+ rejected by the CLI. An ambiguous post-attempt outcome remains uncertain and
43
+ must be reconciled before any new execution choice or retry.
@@ -13,6 +13,7 @@ export interface CreateBacklogItemOptions {
13
13
  labels?: string[];
14
14
  description?: string;
15
15
  createdAt?: string;
16
+ associatedIssues?: unknown;
16
17
  }
17
18
  export interface CreateBacklogItemResult {
18
19
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/new.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EACL,sBAAsB,EACtB,KAAK,4BAA4B,EAClC,MAAM,oBAAoB,CAAC;AAS5B,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,eAAe,CAAC;IAC9B,KAAK,EAAE,4BAA4B,CAAC;CACrC;AAED,MAAM,WAAW,6BAA6B;IAC5C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AA2JD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,SAAS,GAAE,OAAO,CAAC,6BAA6B,CAAM,GACrD,OAAO,CAAC,uBAAuB,CAAC,CAwDlC"}
1
+ {"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/new.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EACL,sBAAsB,EACtB,KAAK,4BAA4B,EAClC,MAAM,oBAAoB,CAAC;AAS5B,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,eAAe,CAAC;IAC9B,KAAK,EAAE,4BAA4B,CAAC;CACrC;AAED,MAAM,WAAW,6BAA6B;IAC5C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AAgKD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,SAAS,GAAE,OAAO,CAAC,6BAA6B,CAAM,GACrD,OAAO,CAAC,uBAAuB,CAAC,CAwDlC"}
@@ -1,5 +1,6 @@
1
1
  import { access, readFile, rm, writeFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
+ import { parseAssociatedIssues, serializeAssociatedIssues, } from '../pjm/remote/association.js';
3
4
  import { resolvePjmTemplate, } from '../pjm/template-source.js';
4
5
  import { stripTemplateFrontmatter } from '../shared/strip-template-frontmatter.js';
5
6
  import YAML from 'yaml';
@@ -72,6 +73,7 @@ function normalizeInputs(options) {
72
73
  scopeEstimate,
73
74
  labels,
74
75
  description: options.description,
76
+ associatedIssues: serializeAssociatedIssues(parseAssociatedIssues(options.associatedIssues ?? [])),
75
77
  };
76
78
  }
77
79
  function renderBacklogItem(template, values) {
@@ -90,7 +92,7 @@ function renderBacklogItem(template, values) {
90
92
  assignee: null,
91
93
  created: values.timestamp,
92
94
  updated: values.timestamp,
93
- associated_issues: [],
95
+ associated_issues: values.associatedIssues,
94
96
  external_plans: [],
95
97
  }).trimEnd();
96
98
  return `---\n${frontmatter}\n---\n\n${body.trimEnd()}\n`;
@@ -21,6 +21,7 @@ interface ConfigCommandDependencies {
21
21
  resolveEffectiveConfig: (repoRoot: string, userConfigDir: string, env: NodeJS.ProcessEnv) => Promise<ResolvedConfig>;
22
22
  resolveAssetsRoot: () => Promise<string>;
23
23
  readFile: (path: string) => Promise<string>;
24
+ atomicWriteJson: (path: string, data: unknown) => Promise<void>;
24
25
  confirmAction: (message: string, ctx: PromptContext) => Promise<boolean>;
25
26
  validateMatrixCell: (provider: string, value: string, options: ValidateMatrixCellOptions) => Promise<MatrixCellAvailabilityResponse>;
26
27
  createDispatchValidationPassContext: typeof createDispatchValidationPassContext;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAIhF,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AAoBzC,OAAO,EAML,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAkBhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,mCAAmC,EACnC,0BAA0B,EAC3B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+GpC,UAAU,yBAAyB;IACjC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,kCAAkC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7E,2CAA2C,EAAE,CAC3C,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,SAAS,CAAC,CAAC;IACxB,gDAAgD,EAAE,CAChD,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,SAAS,CAAC,CAAC;IACxB,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzE,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,mCAAmC,EAAE,OAAO,mCAAmC,CAAC;IAChF,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAwnGD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CAkMT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ import { parseJsonConfig } from '../../config/json.js';
14
14
  import { VALID_DISPATCH_POLICY_MODES, VALID_MANAGED_DISPATCH_POLICIES, MAX_GATE_TIMEOUT_MS, MIN_GATE_TIMEOUT_MS, isValidGateTimeoutMs, normalizeWorkflowPostImplementSequence, readOatConfig, readOatConfigForDefaultScopeRepair, readOatConfigForDocumentationExcludesRepair, readOatConfigForInstructionPointerExcludesRepair, readOatLocalConfig, readUserConfig, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
15
15
  import { resolveEffectiveConfig, } from '../../config/resolve.js';
16
16
  import { resolveAssetsRoot } from '../../fs/assets.js';
17
+ import { atomicWriteJson } from '../../fs/io.js';
17
18
  import { resolveProjectRoot } from '../../fs/paths.js';
18
19
  import { normalizeMatrixCellAvailability, validateMatrixCell, } from '../../providers/identity/availability.js';
19
20
  import { createDispatchValidationPassContext, validateDispatchMatrixRefs, } from '../../providers/identity/dispatch-validation.js';
@@ -28,6 +29,31 @@ const DISPATCH_MATRIX_TIERS = [
28
29
  'high',
29
30
  'frontier',
30
31
  ];
32
+ const PJM_REMOTE_PROVIDERS = ['github', 'linear', 'jira'];
33
+ const PJM_REMOTE_OPERATIONS = [
34
+ 'create',
35
+ 'update-fields',
36
+ 'transition',
37
+ 'annotate',
38
+ 'delete',
39
+ 'relink',
40
+ 'detach',
41
+ 'recreate',
42
+ ];
43
+ const PJM_REMOTE_CONFIG_KEYS = [
44
+ 'pjm.remote',
45
+ 'pjm.remote.schemaVersion',
46
+ 'pjm.remote.storage.state',
47
+ 'pjm.remote.policy',
48
+ 'pjm.remote.policy.description',
49
+ 'pjm.remote.policy.authority.default',
50
+ ...PJM_REMOTE_OPERATIONS.map((operation) => `pjm.remote.policy.authority.operations.${operation}`),
51
+ ...PJM_REMOTE_PROVIDERS.flatMap((provider) => [
52
+ `pjm.remote.policy.providers.${provider}.description`,
53
+ `pjm.remote.policy.providers.${provider}.authority.default`,
54
+ ...PJM_REMOTE_OPERATIONS.map((operation) => `pjm.remote.policy.providers.${provider}.authority.operations.${operation}`),
55
+ ]),
56
+ ];
31
57
  const KEY_ORDER = [
32
58
  'activeIdea',
33
59
  'activeProject',
@@ -58,6 +84,7 @@ const KEY_ORDER = [
58
84
  'git.defaultBranch',
59
85
  'projects.root',
60
86
  'projects.defaultScope',
87
+ ...PJM_REMOTE_CONFIG_KEYS,
61
88
  'tools.brainstorm',
62
89
  'tools.core',
63
90
  'tools.docs',
@@ -94,7 +121,45 @@ const KEY_ORDER = [
94
121
  'workflow.gateTimeouts.artifact',
95
122
  'worktrees.root',
96
123
  ];
124
+ const PJM_REMOTE_CONFIG_CATALOG = PJM_REMOTE_CONFIG_KEYS.map((key) => {
125
+ const aggregate = key === 'pjm.remote' || key === 'pjm.remote.policy';
126
+ const schemaVersion = key === 'pjm.remote.schemaVersion';
127
+ const storage = key === 'pjm.remote.storage.state';
128
+ const authority = key.includes('.authority.');
129
+ const description = key.endsWith('.description');
130
+ const type = schemaVersion
131
+ ? 'literal 1'
132
+ : storage
133
+ ? 'local | shared'
134
+ : authority
135
+ ? 'read-only | user-approved | user-authorized | autonomous'
136
+ : description
137
+ ? 'none | managed-section | replace'
138
+ : 'object';
139
+ return {
140
+ key,
141
+ group: 'PJM Remote Shared Policy',
142
+ file: '.oat/config.json',
143
+ scope: 'shared repo',
144
+ type,
145
+ defaultValue: schemaVersion
146
+ ? '1'
147
+ : storage
148
+ ? 'local'
149
+ : authority
150
+ ? 'read-only'
151
+ : description
152
+ ? 'none'
153
+ : 'unset',
154
+ mutability: aggregate || schemaVersion ? 'read-only' : 'read/write',
155
+ owningCommand: aggregate || schemaVersion
156
+ ? 'individual child keys'
157
+ : `oat config set ${key} <value> --shared`,
158
+ description: 'Repository-owned remote storage or mutation policy; host execution is discovered live and cannot broaden it.',
159
+ };
160
+ });
97
161
  const CONFIG_CATALOG = [
162
+ ...PJM_REMOTE_CONFIG_CATALOG,
98
163
  {
99
164
  key: 'projects.root',
100
165
  group: 'Shared Repo (.oat/config.json)',
@@ -893,6 +958,7 @@ const DEFAULT_DEPENDENCIES = {
893
958
  resolveEffectiveConfig,
894
959
  resolveAssetsRoot,
895
960
  readFile: (path) => readFileDefault(path, 'utf8'),
961
+ atomicWriteJson,
896
962
  confirmAction,
897
963
  validateMatrixCell,
898
964
  createDispatchValidationPassContext,
@@ -905,6 +971,9 @@ function isConfigKey(value) {
905
971
  value === 'workflow.dispatchCeiling.providers' ||
906
972
  isDispatchCeilingProviderKey(value));
907
973
  }
974
+ function isPjmRemoteConfigKey(key) {
975
+ return key.startsWith('pjm.remote');
976
+ }
908
977
  function isDispatchCeilingProviderKey(value) {
909
978
  return (value.startsWith(DISPATCH_CEILING_PROVIDER_KEY_PREFIX) &&
910
979
  value.slice(DISPATCH_CEILING_PROVIDER_KEY_PREFIX.length).trim().length > 0);
@@ -1119,6 +1188,12 @@ function validateSurfaceForKey(key, surface) {
1119
1188
  }
1120
1189
  return;
1121
1190
  }
1191
+ if (isPjmRemoteConfigKey(key)) {
1192
+ if (surface !== 'shared') {
1193
+ throw new Error(`Cannot set '${key}' at '${surface}' scope. PJM remote policy and storage can only be set at shared scope.`);
1194
+ }
1195
+ return;
1196
+ }
1122
1197
  if (key.startsWith('explainers.')) {
1123
1198
  const allowed = key === 'explainers.defaults.style' ||
1124
1199
  key === 'explainers.defaults.palette' ||
@@ -1532,6 +1607,98 @@ function formatResolvedValue(value) {
1532
1607
  }
1533
1608
  return String(value);
1534
1609
  }
1610
+ function defaultPjmRemoteSharedConfig() {
1611
+ return {
1612
+ schemaVersion: 1,
1613
+ storage: { state: 'local' },
1614
+ policy: {
1615
+ description: 'none',
1616
+ authority: { default: 'read-only', operations: {} },
1617
+ providers: {},
1618
+ },
1619
+ };
1620
+ }
1621
+ function applyPjmRemoteSharedValue(current, key, rawValue) {
1622
+ if (key === 'pjm.remote' ||
1623
+ key === 'pjm.remote.policy' ||
1624
+ key === 'pjm.remote.schemaVersion') {
1625
+ throw new Error(`Config key '${key}' is read-only; set one of its documented child keys.`);
1626
+ }
1627
+ const remote = structuredClone(current ?? defaultPjmRemoteSharedConfig());
1628
+ if (key === 'pjm.remote.storage.state') {
1629
+ const state = rawValue.trim();
1630
+ if (state !== 'local' && state !== 'shared') {
1631
+ throw new Error(`Invalid value for ${key}: expected one of local | shared, got '${rawValue}'.`);
1632
+ }
1633
+ if (state === 'shared') {
1634
+ throw new Error("Shared operational storage requires 'oat pjm remote storage shared' preview and approval; direct config set is blocked.");
1635
+ }
1636
+ remote.storage = { state };
1637
+ return remote;
1638
+ }
1639
+ const descriptions = [
1640
+ 'none',
1641
+ 'managed-section',
1642
+ 'replace',
1643
+ ];
1644
+ const authorities = [
1645
+ 'read-only',
1646
+ 'user-approved',
1647
+ 'user-authorized',
1648
+ 'autonomous',
1649
+ ];
1650
+ const value = rawValue.trim();
1651
+ if (key === 'pjm.remote.policy.description') {
1652
+ if (!descriptions.includes(value)) {
1653
+ throw new Error(`Invalid value for ${key}: expected one of ${descriptions.join(' | ')}, got '${rawValue}'.`);
1654
+ }
1655
+ remote.policy.description = value;
1656
+ return remote;
1657
+ }
1658
+ if (!authorities.includes(value)) {
1659
+ if (key.endsWith('.description')) {
1660
+ if (!descriptions.includes(value)) {
1661
+ throw new Error(`Invalid value for ${key}: expected one of ${descriptions.join(' | ')}, got '${rawValue}'.`);
1662
+ }
1663
+ }
1664
+ else {
1665
+ throw new Error(`Invalid value for ${key}: expected one of ${authorities.join(' | ')}, got '${rawValue}'.`);
1666
+ }
1667
+ }
1668
+ if (key === 'pjm.remote.policy.authority.default') {
1669
+ remote.policy.authority.default = value;
1670
+ return remote;
1671
+ }
1672
+ const repositoryOperationPrefix = 'pjm.remote.policy.authority.operations.';
1673
+ if (key.startsWith(repositoryOperationPrefix)) {
1674
+ const operation = key.slice(repositoryOperationPrefix.length);
1675
+ remote.policy.authority.operations ??= {};
1676
+ remote.policy.authority.operations[operation] =
1677
+ value;
1678
+ return remote;
1679
+ }
1680
+ const parts = key.split('.');
1681
+ const provider = parts[4];
1682
+ remote.policy.providers ??= {};
1683
+ const providerPolicy = structuredClone(remote.policy.providers[provider] ?? {});
1684
+ if (parts[5] === 'description') {
1685
+ providerPolicy.description = value;
1686
+ }
1687
+ else {
1688
+ providerPolicy.authority ??= {};
1689
+ if (parts[6] === 'default') {
1690
+ providerPolicy.authority.default = value;
1691
+ }
1692
+ else {
1693
+ providerPolicy.authority.operations ??= {};
1694
+ const operation = parts[7];
1695
+ providerPolicy.authority.operations[operation] =
1696
+ value;
1697
+ }
1698
+ }
1699
+ remote.policy.providers[provider] = providerPolicy;
1700
+ return remote;
1701
+ }
1535
1702
  async function getConfigValue(repoRoot, userConfigDir, key, dependencies, preserveRawObject = false) {
1536
1703
  const resolved = await dependencies.resolveEffectiveConfig(repoRoot, userConfigDir, dependencies.processEnv);
1537
1704
  const entry = resolved.resolved[key];
@@ -1600,6 +1767,19 @@ async function listConfigKeys(repoRoot, userConfigDir, dependencies) {
1600
1767
  async function setConfigValue(repoRoot, userConfigDir, key, rawValue, surface, dependencies, warn) {
1601
1768
  validateSurfaceForKey(key, surface);
1602
1769
  const effectiveSurface = surface === 'auto' ? defaultSurfaceForKey(key) : surface;
1770
+ if (isPjmRemoteConfigKey(key)) {
1771
+ const config = await dependencies.readOatConfig(repoRoot);
1772
+ const remote = applyPjmRemoteSharedValue(config.pjm?.remote, key, rawValue);
1773
+ await dependencies.writeOatConfig(repoRoot, {
1774
+ ...config,
1775
+ pjm: { ...config.pjm, remote },
1776
+ });
1777
+ return {
1778
+ key,
1779
+ value: rawValue.trim(),
1780
+ source: 'shared',
1781
+ };
1782
+ }
1603
1783
  if (isWorkflowKey(key)) {
1604
1784
  const parsedValue = parseWorkflowValue(key, rawValue);
1605
1785
  const displayValue = formatResolvedValue(parsedValue);
@@ -1990,7 +2170,8 @@ function configPathForKey(key) {
1990
2170
  }
1991
2171
  if (key.startsWith('explainers.') ||
1992
2172
  key.startsWith('documentation.') ||
1993
- key.startsWith('archive.')) {
2173
+ key.startsWith('archive.') ||
2174
+ isPjmRemoteConfigKey(key)) {
1994
2175
  return key.split('.');
1995
2176
  }
1996
2177
  if (key === 'git.defaultBranch' ||
@@ -2024,6 +2205,15 @@ function configPathForKey(key) {
2024
2205
  */
2025
2206
  async function unsetConfigValue(repoRoot, userConfigDir, key, surface, dependencies, warn) {
2026
2207
  validateSurfaceForKey(key, surface);
2208
+ // These are structural views (and, for schemaVersion, the discriminator
2209
+ // that makes the remote policy readable), just as they are for `set`.
2210
+ // Refuse them before resolving config so an unset attempt can never be
2211
+ // mistaken for permission to remove the whole policy boundary.
2212
+ if (key === 'pjm.remote' ||
2213
+ key === 'pjm.remote.policy' ||
2214
+ key === 'pjm.remote.schemaVersion') {
2215
+ throw new Error(`Config key '${key}' is read-only; unset one of its documented child keys.`);
2216
+ }
2027
2217
  const resolved = await dependencies.resolveEffectiveConfig(repoRoot, userConfigDir, dependencies.processEnv);
2028
2218
  const envShadowed = resolved.resolved[key]?.source === 'env';
2029
2219
  if (key === 'activeProject' || key === 'lastPausedProject') {
@@ -2107,6 +2297,22 @@ async function removeFromSurface(repoRoot, userConfigDir, key, effectiveSurface,
2107
2297
  return false;
2108
2298
  }
2109
2299
  const configPath = join(repoRoot, '.oat', 'config.json');
2300
+ if (isPjmRemoteConfigKey(key)) {
2301
+ // PJM remote normalization materializes fail-closed defaults such as
2302
+ // policy.description=none and authority.default=read-only. Removing a
2303
+ // leaf from that normalized object and sending it through writeOatConfig
2304
+ // would therefore write the leaf straight back while reporting success.
2305
+ // The config has already passed the strict shared-policy reader in
2306
+ // resolveEffectiveConfig, so perform this removal against the validated raw
2307
+ // document and atomically persist only the requested structural change.
2308
+ const repaired = await removeConfigPathOnDisk(configPath, path);
2309
+ if (!repaired) {
2310
+ return false;
2311
+ }
2312
+ preservePjmRemotePolicyBoundary(repaired, path);
2313
+ await dependencies.atomicWriteJson(configPath, repaired);
2314
+ return true;
2315
+ }
2110
2316
  const sharedConfig = key === 'documentation.excludes'
2111
2317
  ? await dependencies.readOatConfigForDocumentationExcludesRepair(repoRoot)
2112
2318
  : key === 'documentation.instructionPointerExcludes'
@@ -2126,6 +2332,31 @@ async function removeFromSurface(repoRoot, userConfigDir, key, effectiveSurface,
2126
2332
  }
2127
2333
  return false;
2128
2334
  }
2335
+ /**
2336
+ * Keep the required PJM remote policy object when unsetting its final leaf.
2337
+ *
2338
+ * The recursive remover normally prunes empty parents. For this one schema,
2339
+ * however, `policy` is the structural boundary that keeps sibling remote state
2340
+ * (notably `storage.state=shared`) readable. An empty policy is valid and
2341
+ * normalizes to the fail-closed defaults; an absent policy makes the complete
2342
+ * remote object unreadable.
2343
+ */
2344
+ function preservePjmRemotePolicyBoundary(config, path) {
2345
+ if (path[0] !== 'pjm' || path[1] !== 'remote' || path[2] !== 'policy') {
2346
+ return;
2347
+ }
2348
+ const pjm = config.pjm;
2349
+ if (!isRecord(pjm)) {
2350
+ return;
2351
+ }
2352
+ const remote = pjm.remote;
2353
+ if (!isRecord(remote)) {
2354
+ return;
2355
+ }
2356
+ if (!Object.prototype.hasOwnProperty.call(remote, 'policy')) {
2357
+ remote.policy = {};
2358
+ }
2359
+ }
2129
2360
  /**
2130
2361
  * Remove `path` from the raw JSON on disk, ignoring normalization.
2131
2362
  *
@@ -1,8 +1,10 @@
1
1
  import type { DoctorCheck } from '../../ui/output.js';
2
2
  import { type PjmAdoption } from './adoption.js';
3
+ import { type RemoteDoctorInput } from './remote/doctor.js';
3
4
  export interface PjmDoctorOptions {
4
5
  projectRoot?: string;
5
6
  adoption?: PjmAdoption;
7
+ remote?: RemoteDoctorInput;
6
8
  }
7
9
  export declare function runPjmDoctorChecks(repoRoot: string, options?: PjmDoctorOptions): Promise<DoctorCheck[]>;
8
10
  //# sourceMappingURL=doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/commands/pjm/doctor.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AA4MlE,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,WAAW,EAAE,CAAC,CA2UxB"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/commands/pjm/doctor.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAElE,OAAO,EAAyB,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AA8MhF,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,WAAW,EAAE,CAAC,CAmVxB"}
@@ -1,9 +1,15 @@
1
+ import { execFile as execFileCallback } from 'node:child_process';
1
2
  import { access, readdir, readFile } from 'node:fs/promises';
2
- import { basename, dirname, join } from 'node:path';
3
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
4
+ import { promisify } from 'node:util';
3
5
  import { BACKLOG_ITEM_STATUSES, extractBacklogStatus, isTerminalBacklogStatus, isValidBacklogStatus, } from '../backlog/shared/item-status.js';
4
6
  import { getFrontmatterBlock } from '../shared/frontmatter.js';
7
+ import { parseJsonConfig } from '../../config/json.js';
5
8
  import { resolvePjmAdoption } from './adoption.js';
6
9
  import { CANONICAL_REPO_REFERENCE_PATHS } from './init.js';
10
+ import { runRemoteDoctorChecks } from './remote/doctor.js';
11
+ import { resolveRemoteStorageLocations } from './remote/storage-locator.js';
12
+ const execFileAsync = promisify(execFileCallback);
7
13
  const ALLOWED_TOP_LEVEL_DIRECTORIES = new Set([
8
14
  'pjm',
9
15
  'reference',
@@ -435,5 +441,69 @@ export async function runPjmDoctorChecks(repoRoot, options = {}) {
435
441
  ? undefined
436
442
  : 'Run `oat backlog archive <id>` to archive each completed item still under items/.',
437
443
  });
444
+ const defaultRemote = await resolveDefaultRemoteDoctorInput(projectRoot, repoRoot);
445
+ checks.push(...(await runRemoteDoctorChecks(options.remote ?? defaultRemote)));
438
446
  return checks;
439
447
  }
448
+ async function resolveDefaultRemoteDoctorInput(projectRoot, pjmRepoRoot) {
449
+ const rawConfig = await readRawRemoteConfig(projectRoot);
450
+ const stateStorage = isRecord(rawConfig) &&
451
+ isRecord(rawConfig.storage) &&
452
+ rawConfig.storage.state === 'shared'
453
+ ? 'shared'
454
+ : 'local';
455
+ try {
456
+ const [{ stdout: commonDirOutput }, { stdout: originOutput }] = await Promise.all([
457
+ execFileAsync('git', ['rev-parse', '--git-common-dir'], {
458
+ cwd: projectRoot,
459
+ }),
460
+ execFileAsync('git', ['config', '--get', 'remote.origin.url'], {
461
+ cwd: projectRoot,
462
+ }).catch(() => ({ stdout: '' })),
463
+ ]);
464
+ const commonDir = commonDirOutput.trim();
465
+ const gitCommonDir = isAbsolute(commonDir)
466
+ ? commonDir
467
+ : resolve(projectRoot, commonDir);
468
+ const repositoryIdentity = originOutput.trim() || `local-repository:${resolve(projectRoot)}`;
469
+ const locations = resolveRemoteStorageLocations({
470
+ repoRoot: projectRoot,
471
+ gitCommonDir,
472
+ repositoryIdentity,
473
+ stateStorage,
474
+ target: { kind: 'backlog', scope: 'shared', path: null },
475
+ });
476
+ return {
477
+ portableBindingsDir: locations.portable.bindingsDir,
478
+ operationalBindingsDir: locations.operational.bindingsDir,
479
+ operationsDir: locations.operational.operationsDir,
480
+ policy: rawConfig,
481
+ };
482
+ }
483
+ catch {
484
+ const fallbackRoot = join(pjmRepoRoot, 'pjm', 'remote');
485
+ return {
486
+ portableBindingsDir: join(fallbackRoot, 'bindings'),
487
+ operationalBindingsDir: join(fallbackRoot, 'state', 'bindings'),
488
+ operationsDir: join(fallbackRoot, 'state', 'operations'),
489
+ policy: rawConfig,
490
+ };
491
+ }
492
+ }
493
+ async function readRawRemoteConfig(projectRoot) {
494
+ const content = await readIfExists(join(projectRoot, '.oat', 'config.json'));
495
+ if (!content)
496
+ return undefined;
497
+ try {
498
+ const config = parseJsonConfig(content, join(projectRoot, '.oat', 'config.json'));
499
+ if (!isRecord(config) || !isRecord(config.pjm))
500
+ return undefined;
501
+ return config.pjm.remote;
502
+ }
503
+ catch {
504
+ return undefined;
505
+ }
506
+ }
507
+ function isRecord(value) {
508
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
509
+ }
@@ -6,6 +6,7 @@ import { resolvePjmAdoption } from './adoption.js';
6
6
  import { runPjmDoctorChecks } from './doctor.js';
7
7
  import { initializeRepoReference } from './init.js';
8
8
  import { migratePjmRepo, readPjmMigrationPrompt } from './migrate.js';
9
+ import { createPjmRemoteCommand } from './remote/index.js';
9
10
  interface PjmCommandDependencies {
10
11
  buildCommandContext: typeof buildCommandContext;
11
12
  resolveProjectRoot: typeof resolveProjectRoot;
@@ -15,6 +16,7 @@ interface PjmCommandDependencies {
15
16
  runPjmDoctorChecks: typeof runPjmDoctorChecks;
16
17
  migratePjmRepo: typeof migratePjmRepo;
17
18
  readPjmMigrationPrompt: typeof readPjmMigrationPrompt;
19
+ createPjmRemoteCommand: typeof createPjmRemoteCommand;
18
20
  }
19
21
  export declare function createPjmCommand(overrides?: Partial<PjmCommandDependencies>): Command;
20
22
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/pjm/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAA0B,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAiBnE,UAAU,sBAAsB;IAC9B,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,cAAc,EAAE,OAAO,cAAc,CAAC;IACtC,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AA6FD,wBAAgB,gBAAgB,CAC9B,SAAS,GAAE,OAAO,CAAC,sBAAsB,CAAM,GAC9C,OAAO,CAyLT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/pjm/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAA0B,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAiBxD,UAAU,sBAAsB;IAC9B,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,cAAc,EAAE,OAAO,cAAc,CAAC;IACtC,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AA8FD,wBAAgB,gBAAgB,CAC9B,SAAS,GAAE,OAAO,CAAC,sBAAsB,CAAM,GAC9C,OAAO,CA0LT"}