gh-inari 0.8.0 → 0.10.2

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 (56) hide show
  1. package/.codex-plugin/plugin.json +1 -1
  2. package/README.md +35 -11
  3. package/dist/artifact.d.ts +29 -2
  4. package/dist/artifact.js +266 -26
  5. package/dist/artifact.js.map +1 -1
  6. package/dist/cli-core.d.ts +24 -0
  7. package/dist/cli-core.js +1463 -0
  8. package/dist/cli-core.js.map +1 -0
  9. package/dist/cli.d.ts +9 -13
  10. package/dist/cli.js +123 -1365
  11. package/dist/cli.js.map +1 -1
  12. package/dist/command-contract.d.ts +128 -0
  13. package/dist/command-contract.js +343 -0
  14. package/dist/command-contract.js.map +1 -0
  15. package/dist/contract/index.d.ts +1 -0
  16. package/dist/contract/index.js +1 -0
  17. package/dist/contract/index.js.map +1 -1
  18. package/dist/contract/ir.d.ts +6 -0
  19. package/dist/contract/ir.js +24 -2
  20. package/dist/contract/ir.js.map +1 -1
  21. package/dist/contract/issue-form.js +2 -2
  22. package/dist/contract/issue-form.js.map +1 -1
  23. package/dist/contract/issue-reference.d.ts +58 -0
  24. package/dist/contract/issue-reference.js +157 -0
  25. package/dist/contract/issue-reference.js.map +1 -0
  26. package/dist/contract/schema.d.ts +4 -0
  27. package/dist/contract/schema.js +42 -1
  28. package/dist/contract/schema.js.map +1 -1
  29. package/dist/contract/validation.d.ts +1 -1
  30. package/dist/contract/validation.js +1 -1
  31. package/dist/contract/validation.js.map +1 -1
  32. package/dist/github/adapter.d.ts +2 -0
  33. package/dist/github/adapter.js +80 -32
  34. package/dist/github/adapter.js.map +1 -1
  35. package/dist/github/types.d.ts +7 -0
  36. package/dist/governance.d.ts +12 -7
  37. package/dist/governance.js +92 -20
  38. package/dist/governance.js.map +1 -1
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.js +2 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/reconciliation.d.ts +11 -8
  43. package/dist/reconciliation.js +170 -30
  44. package/dist/reconciliation.js.map +1 -1
  45. package/dist/semantic-template.js +23 -32
  46. package/dist/semantic-template.js.map +1 -1
  47. package/dist/skill.d.ts +8 -0
  48. package/dist/skill.js +68 -43
  49. package/dist/skill.js.map +1 -1
  50. package/dist/template-discovery.js +26 -50
  51. package/dist/template-discovery.js.map +1 -1
  52. package/dist/template-resolver.d.ts +76 -0
  53. package/dist/template-resolver.js +344 -0
  54. package/dist/template-resolver.js.map +1 -0
  55. package/package.json +7 -3
  56. package/skills/inari/SKILL.md +4 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inari",
3
- "version": "0.8.0",
3
+ "version": "0.10.2",
4
4
  "description": "Governed GitHub Issue/PR/template workflows via the inari CLI. Prefer inari over raw gh for operations it owns; use inari skill for operational playbooks.",
5
5
  "skills": "skills/inari"
6
6
  }
package/README.md CHANGED
@@ -90,8 +90,10 @@ recovery command. The current capability identifiers are
90
90
  `canonical-invocation`, `machine-readable-version`, `capability-diagnostics`,
91
91
  and `extension-bootstrap`.
92
92
 
93
- The `inari`, `gh-inari`, and `gh inari` invocation forms are behaviorally
94
- identical:
93
+ The `inari` executable is canonical. The direct package executable
94
+ `gh-inari` and the `gh inari` extension form are compatibility paths with the
95
+ same governed command semantics; prefer `inari` for agent and human-facing
96
+ commands:
95
97
 
96
98
  ```bash
97
99
  inari issue schema feature --json
@@ -166,8 +168,8 @@ inari issue get <number> [--template <template>] --json
166
168
  inari pr get <number> [--template <template>] --json
167
169
  inari issue check <number> [--template <template>]
168
170
  inari pr check <number> [--template <template>]
169
- inari issue edit <number> --from patch.json [--dry-run]
170
- inari pr edit <number> --from patch.json [--dry-run]
171
+ inari issue edit <number> [--from patch.json] [--field name=value] [--title title] [--dry-run]
172
+ inari pr edit <number> [--from patch.json] [--field name=value] [--title title] [--base branch] [--maintainer-can-modify] [--dry-run]
171
173
  inari issue normalize <number> [--dry-run]
172
174
  inari pr normalize <number> [--dry-run]
173
175
  inari issue sync <number> --from desired.json [--dry-run]
@@ -184,12 +186,14 @@ inari pr sync <number> --from desired.json [--dry-run]
184
186
  }
185
187
  ```
186
188
 
187
- The `fields` object is the semantic input contract shown by `schema`. Issue creation also accepts `assignees`; pull request creation accepts `head`, `base`, `draft`, and `maintainerCanModify`. `--title`, `--head`, and `--base` override envelope metadata.
189
+ The `fields` object is the semantic input contract shown by `schema`; the same schema output exposes the separate required create metadata schema. Issue creation also accepts `assignees`; pull request creation accepts `head`, `base`, `draft`, and `maintainerCanModify`. Caller-supplied `title` metadata is required for both create commands, and `--title`, `--head`, and `--base` override envelope metadata. Existing `edit` commands use the remote artifact as their patch base: `--title` is supported for Issues and pull requests, while pull requests also support `--base` and `--maintainer-can-modify`; `--draft` is rejected for edit because pull-request PATCH does not accept it. Omitted values are preserved and unsupported or immutable metadata is rejected.
188
190
 
189
191
  `pr sync --from` accepts a complete pull-request desired-state envelope. Use
190
192
  `inari pr sync --help` for the top-level contract, or
191
193
  `inari pr schema <template> --json` for its machine-readable `syncInput.schema`
192
194
  and a valid `syncInput.minimalExample`.
195
+ `issue sync --from` overlays supplied semantic fields and metadata onto the
196
+ current artifact, preserving values omitted from the input.
193
197
 
194
198
  Schema and validation output is JSON. `--json` makes render and create output JSON as well. Validation failures return exit status `2`; usage errors return `1`; GitHub/transport failures return `3`. Error objects contain stable `code`, `path` where applicable, and ordered `violations`.
195
199
 
@@ -211,18 +215,22 @@ boilerplate and raw Markdown are intentionally absent from successful output.
211
215
  Existing artifact remediation uses one semantic pipeline for both Issues and
212
216
  pull requests. `check` is read-only and classifies an artifact as
213
217
  `valid-current`, `non-canonical`, `semantically-invalid`, `unsupported`, or
214
- `ambiguous`. `edit` applies only an explicit semantic patch from JSON; it never
215
- accepts raw Markdown as the mutation contract. `normalize` re-renders a
218
+ `ambiguous`. `edit` applies an explicit semantic/metadata patch from JSON or
219
+ direct CLI options; it never accepts raw Markdown as the mutation contract. `normalize` re-renders a
216
220
  parseable, semantically valid artifact and fails closed when preservation is
217
- not proven. `sync` treats its input as the complete desired semantic state and
218
- reconciles the canonical projection deterministically. A successful no-op is
221
+ not proven. `issue sync` overlays its input onto the current canonical state,
222
+ while `pr sync` treats its input as the complete desired semantic state; both
223
+ reconcile the canonical projection deterministically. A successful no-op is
219
224
  reported explicitly, and `--dry-run` returns a bounded semantic/rendered diff
220
- without calling a GitHub mutation.
225
+ plus the validated resulting fields, metadata, and canonical body without
226
+ calling a GitHub mutation.
221
227
 
222
228
  ## Source of truth and supported semantics
223
229
 
224
230
  `.github/ISSUE_TEMPLATE/**` remains the Issue source of truth. For pull requests, Inari discovers GitHub's supported repository locations: `pull_request_template` files under the repository root, `docs/`, or `.github/`, plus `PULL_REQUEST_TEMPLATE/` directories under each location. Native PR template filenames and the `.md`/`.txt` extensions supported by Inari are matched case-insensitively. Other PR-template extension surfaces are intentionally unsupported in v1 and fail closed. Inari discovers and compiles those files; it does not replace them with a proprietary body schema. Supported Issue Form nodes are `input`, `textarea` (including native `render` code fences), single- and multi-select `dropdown`, `checkboxes`, and `markdown`. Browser-only or ambiguous behavior, such as uploads or unsupported textarea rendering modes, fails closed. Markdown nodes are retained for contract/schema explainability but never emitted into an Issue body.
225
231
 
232
+ Issue dependencies are a separate, template-independent semantic object. Structured Issue envelopes may provide `dependencies.blockedBy` and `dependencies.blocks`; each reference is the canonical `{ "repositoryHost": "github.com", "repositoryId": "<decimal REST repository database id>", "repository": "owner/name", "number": N }` identity. `repositoryHost` plus `repositoryId` is authoritative: the host/install boundary prevents IDs from different GH_HOST/GHES installations colliding. Within one host, rename/transfer preserves the repository identity tuple and changes only the optional locator; cross-host migration is a different identity. The GitHub adapter obtains this decimal ID from the REST repository endpoint through `gh api`, including for repository overrides through a read-only identity lookup; a context that cannot obtain it does not synthesize one from owner/name. Inari sorts and validates these references (including duplicate, self, malformed, and contradictory declarations) before rendering. Non-empty dependencies are preserved in a bounded reserved body marker and projected back as normalized JSON; ordinary Markdown and GitHub-native dependency metadata are never inferred as semantic input. GitHub-native dependency state is not currently mutated by Inari, so there is no competing authority or implicit drift reconciliation.
233
+
226
234
  Native PR Markdown expresses structure but not policy. A small versioned overlay may add constraints unavailable in Markdown without changing section order or content. The supported v1 form is:
227
235
 
228
236
  ```yaml
@@ -254,7 +262,23 @@ templates:
254
262
 
255
263
  Template and section bindings are deterministic. Stale, unknown, or ambiguous template/section references fail closed. `linkedIssue: true` means that the field contains a GitHub closing reference: `close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, or `resolved`, followed by `#ISSUE-NUMBER` or `OWNER/REPOSITORY#ISSUE-NUMBER`, with an optional colon and case-insensitive keyword. This validates syntax only; GitHub applies automatic linking/closure only under its own contextual rules, including the target default branch.
256
264
 
257
- Issue Form top-level `title` is the exact default title used when the caller omits a title; an explicit caller title replaces it without inferred prefix concatenation. Top-level `labels` are repository-governed defaults and are always retained; caller-supplied labels are appended in order with duplicates removed. Top-level `assignees`, `projects`, and `type`, and upload fields remain unsupported and fail closed rather than being approximated.
265
+ When `issue create` or `pr create` omits `--template`, the shared resolver uses
266
+ the repository's optional `.github/inari/template-resolution.yml` authority:
267
+
268
+ ```yaml
269
+ version: 1
270
+ defaults:
271
+ issue: feature
272
+ pr: default
273
+ ```
274
+
275
+ The precedence is explicit selector, configured default, sole candidate,
276
+ interactive TTY selection, then bounded failure. Non-interactive execution never
277
+ guesses among multiple candidates. An invalid or unavailable configured default
278
+ also fails closed. Candidate identifiers and an explicit `--template` recovery
279
+ action are included in structured ambiguity diagnostics.
280
+
281
+ Issue Form top-level `title` is a fixed native prefix for create validation, but it does not satisfy the caller's required title metadata by itself. An explicit caller title is used as supplied without inferred prefix concatenation or automatic generation. Top-level `labels` are repository-governed defaults and are always retained; caller-supplied labels are appended in order with duplicates removed. Top-level `assignees`, `projects`, and `type`, and upload fields remain unsupported and fail closed rather than being approximated.
258
282
 
259
283
  ## Scope
260
284
 
@@ -1,6 +1,7 @@
1
1
  import { type PartialSemanticValidationResult, type PartialSemanticRepairResult, type SemanticValidationResult, type SemanticViolation } from "./contract/validation.js";
2
2
  import { type ArtifactDiagnostic, type ArtifactDiagnosticReport } from "./diagnostics.js";
3
3
  import { type ArtifactKind, type CanonicalContract } from "./contract/ir.js";
4
+ import { type IssueDependencies, type IssueReference } from "./contract/issue-reference.js";
4
5
  import { type ValidatedRenderedIssueArtifact, type ValidatedRenderedPullRequestArtifact } from "./github/types.js";
5
6
  export interface ArtifactInputMetadata {
6
7
  readonly title?: string;
@@ -14,6 +15,8 @@ export interface ArtifactInputMetadata {
14
15
  export interface ArtifactInputDocument {
15
16
  readonly fields: Readonly<Record<string, unknown>>;
16
17
  readonly metadata: ArtifactInputMetadata;
18
+ /** Generic Issue relationships, independent of template-specific fields. */
19
+ readonly dependencies?: IssueDependencies;
17
20
  }
18
21
  /**
19
22
  * A representation-independent candidate entering the canonical contract.
@@ -26,6 +29,7 @@ export interface ArtifactCandidate {
26
29
  readonly fields: unknown;
27
30
  readonly metadata: ArtifactInputMetadata;
28
31
  readonly source: ArtifactCandidateSource;
32
+ readonly dependencies?: IssueDependencies;
29
33
  }
30
34
  export interface ArtifactCandidateAdapterResult {
31
35
  readonly parsed: boolean;
@@ -48,13 +52,14 @@ export interface CanonicalArtifactLoadResult {
48
52
  readonly invalidFields: PartialSemanticValidationResult["invalidFields"];
49
53
  readonly diagnostics: ArtifactDiagnosticReport;
50
54
  readonly violations: readonly SemanticViolation[];
55
+ readonly dependencies?: IssueDependencies;
51
56
  }
52
57
  export interface ArtifactMetadataViolation {
53
58
  readonly code: "INPUT_METADATA_INVALID";
54
59
  readonly path: string;
55
60
  readonly message: string;
56
61
  }
57
- export type ArtifactInputErrorCode = "INPUT_DOCUMENT_INVALID" | "INPUT_METADATA_INVALID";
62
+ export type ArtifactInputErrorCode = "INPUT_DOCUMENT_INVALID" | "INPUT_METADATA_INVALID" | "INPUT_DEPENDENCIES_INVALID";
58
63
  export declare class ArtifactInputError extends Error {
59
64
  readonly code: ArtifactInputErrorCode;
60
65
  readonly path: string;
@@ -92,6 +97,9 @@ export interface ExistingArtifactDiagnostic {
92
97
  export interface ExistingArtifactParseResult {
93
98
  readonly parsed: boolean;
94
99
  readonly values: Readonly<Record<string, unknown>>;
100
+ readonly dependencies?: IssueDependencies;
101
+ /** Raw dependency declaration retained for the semantic validation boundary. */
102
+ readonly dependencyInput?: unknown;
95
103
  readonly diagnostics: readonly ExistingArtifactDiagnostic[];
96
104
  }
97
105
  /**
@@ -101,6 +109,7 @@ export interface ExistingArtifactParseResult {
101
109
  */
102
110
  export interface RecoverableArtifactValues {
103
111
  readonly values: Readonly<Record<string, unknown>>;
112
+ readonly dependencies?: IssueDependencies;
104
113
  readonly diagnostics: readonly ExistingArtifactDiagnostic[];
105
114
  }
106
115
  export interface ExistingArtifactValidationResult {
@@ -115,6 +124,8 @@ export interface ExistingIssueReader {
115
124
  getIssue(issueNumber: number): Promise<{
116
125
  readonly body: string | null;
117
126
  readonly url: string;
127
+ readonly repositoryId?: string;
128
+ readonly repositoryHost?: string;
118
129
  }>;
119
130
  }
120
131
  export interface ExistingPullRequestReader {
@@ -132,6 +143,7 @@ export interface ExistingPullRequestReader {
132
143
  * repository governance/provenance that resolves the actual contract.
133
144
  */
134
145
  export declare const TEMPLATE_IDENTITY_MARKER_VERSION: "1";
146
+ export declare const ISSUE_DEPENDENCY_MARKER_VERSION: "1";
135
147
  export interface TemplateIdentityMarker {
136
148
  readonly version: string;
137
149
  readonly kind: ArtifactKind;
@@ -144,6 +156,18 @@ export interface TemplateIdentityMarkerExtraction {
144
156
  /** Body with a recognized trailing marker line removed; unchanged when none is present. */
145
157
  readonly body: string;
146
158
  }
159
+ export type IssueDependencyMarkerStatus = "absent" | "valid" | "malformed" | "unsupported-version";
160
+ export interface IssueDependencyMarkerExtraction {
161
+ readonly status: IssueDependencyMarkerStatus;
162
+ readonly dependencies?: IssueDependencies;
163
+ /** Body with a recognized trailing dependency marker removed. */
164
+ readonly body: string;
165
+ }
166
+ /**
167
+ * Read only the reserved trailing dependency marker emitted by Inari. No
168
+ * ordinary Markdown is interpreted as a relationship declaration.
169
+ */
170
+ export declare function extractIssueDependencyMarker(body: string): IssueDependencyMarkerExtraction;
147
171
  /**
148
172
  * Recognize and remove a trailing template identity marker line without
149
173
  * applying semantic parsing. Only a line starting with the exact reserved
@@ -212,7 +236,7 @@ export declare function parseExistingPullRequestArtifact(contractInput: unknown,
212
236
  * selected repair target.
213
237
  */
214
238
  export declare function recoverExistingArtifactValues(contractInput: unknown, body: string | null | undefined): RecoverableArtifactValues;
215
- export declare function validateExistingIssueArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactValidationResult;
239
+ export declare function validateExistingIssueArtifact(contractInput: unknown, body: string | null | undefined, subject?: IssueReference): ExistingArtifactValidationResult;
216
240
  export declare function validateExistingPullRequestArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactValidationResult;
217
241
  export interface ExistingArtifactCandidate {
218
242
  readonly contract: CanonicalContract;
@@ -227,6 +251,7 @@ export interface ExistingArtifactProjection {
227
251
  readonly projection: "canonical" | "unavailable";
228
252
  readonly classification: ExistingArtifactClassification;
229
253
  readonly fields?: Readonly<Record<string, unknown>>;
254
+ readonly dependencies?: IssueDependencies;
230
255
  readonly diagnostics: readonly ExistingArtifactDiagnostic[];
231
256
  readonly violations?: readonly SemanticViolation[];
232
257
  readonly attemptedTemplates?: readonly string[];
@@ -239,6 +264,8 @@ export declare function selectExistingArtifactCandidate(candidates: readonly Exi
239
264
  export declare function validateRequiredMetadataString(value: unknown, key: string): ArtifactMetadataViolation | undefined;
240
265
  export declare function validateExistingIssueFromAdapter(reader: ExistingIssueReader, contract: unknown, issueNumber: number): Promise<FetchedExistingArtifact>;
241
266
  export declare function validateExistingPullRequestFromAdapter(reader: ExistingPullRequestReader, contract: unknown, pullRequestNumber: number): Promise<FetchedExistingArtifact>;
267
+ /** @internal Validate the canonical Issue metadata handoff before mutation. */
268
+ export declare function verifyIssueMetadataRoundTrip(expected: Readonly<Record<string, unknown>>, artifact: ValidatedRenderedIssueArtifact): void;
242
269
  /** Escape only Markdown constructs that could change the canonical section structure. */
243
270
  export declare function escapeMarkdownValue(value: string): string;
244
271
  export declare function removeHtmlComments(value: string): string;