gh-inari 0.6.1 → 0.8.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.
Files changed (48) hide show
  1. package/.codex-plugin/plugin.json +6 -0
  2. package/README.md +37 -1
  3. package/dist/artifact.d.ts +129 -13
  4. package/dist/artifact.js +579 -63
  5. package/dist/artifact.js.map +1 -1
  6. package/dist/cli.js +366 -57
  7. package/dist/cli.js.map +1 -1
  8. package/dist/contract/index.d.ts +1 -0
  9. package/dist/contract/index.js +1 -0
  10. package/dist/contract/index.js.map +1 -1
  11. package/dist/contract/ir.d.ts +11 -0
  12. package/dist/contract/ir.js +23 -1
  13. package/dist/contract/ir.js.map +1 -1
  14. package/dist/contract/issue-form.js +15 -2
  15. package/dist/contract/issue-form.js.map +1 -1
  16. package/dist/contract/normalization.d.ts +31 -0
  17. package/dist/contract/normalization.js +73 -0
  18. package/dist/contract/normalization.js.map +1 -0
  19. package/dist/contract/validation.d.ts +120 -2
  20. package/dist/contract/validation.js +476 -3
  21. package/dist/contract/validation.js.map +1 -1
  22. package/dist/diagnostics.d.ts +133 -0
  23. package/dist/diagnostics.js +421 -0
  24. package/dist/diagnostics.js.map +1 -0
  25. package/dist/github/capability.js +1 -0
  26. package/dist/github/capability.js.map +1 -1
  27. package/dist/governance.d.ts +8 -2
  28. package/dist/governance.js +54 -4
  29. package/dist/governance.js.map +1 -1
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +2 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/pr-policy.d.ts +9 -0
  34. package/dist/pr-policy.js +36 -16
  35. package/dist/pr-policy.js.map +1 -1
  36. package/dist/pr-sync-input.d.ts +74 -0
  37. package/dist/pr-sync-input.js +231 -0
  38. package/dist/pr-sync-input.js.map +1 -0
  39. package/dist/reconciliation.d.ts +15 -2
  40. package/dist/reconciliation.js +362 -15
  41. package/dist/reconciliation.js.map +1 -1
  42. package/dist/semantic-template.js +25 -4
  43. package/dist/semantic-template.js.map +1 -1
  44. package/dist/skill.d.ts +42 -0
  45. package/dist/skill.js +138 -0
  46. package/dist/skill.js.map +1 -0
  47. package/package.json +5 -3
  48. package/skills/inari/SKILL.md +48 -0
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "inari",
3
+ "version": "0.8.0",
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
+ "skills": "skills/inari"
6
+ }
package/README.md CHANGED
@@ -119,6 +119,33 @@ npx --yes --package=./gh-inari-<version>.tgz gh-inari --version --json
119
119
 
120
120
  Inari uses the current `gh` authentication and repository context. It does not maintain a second credential store. Use `--repository owner/name` when the target repository is not the current checkout.
121
121
 
122
+ ## Codex Plugin
123
+
124
+ The same published `gh-inari` package is also a valid Codex Plugin — no
125
+ second install artifact is needed. Installing or unpacking `gh-inari` (any
126
+ of the paths above) ships `.codex-plugin/plugin.json` and
127
+ `skills/inari/SKILL.md` alongside the CLI. Codex requires explicit
128
+ marketplace registration and installation via Codex plugin commands to
129
+ activate the skill; standard `npm install` alone does not automatically
130
+ surface the plugin to Codex-aware agents.
131
+
132
+ To discover and install it from this repository's marketplace:
133
+
134
+ ```bash
135
+ codex plugin marketplace add .
136
+ ```
137
+
138
+ In Codex, run `/plugins`, select the `gh-inari` marketplace, and install
139
+ `inari`. Start a new Codex session after installation so the bundled Skill
140
+ is available.
141
+
142
+ The Skill is deliberately thin: it identifies governed GitHub Issue/PR/
143
+ template workflows as Inari-owned and routes agents to `inari skill` /
144
+ `inari skill <scenario>` for the actual operational playbooks, and to
145
+ `inari <domain> --help` for exact command syntax. It does not duplicate
146
+ scenario content, so it stays correct as `inari skill` evolves. Raw `gh`
147
+ remains available for anything outside Inari's governed surface.
148
+
122
149
  ## Commands
123
150
 
124
151
  ```bash
@@ -159,6 +186,11 @@ inari pr sync <number> --from desired.json [--dry-run]
159
186
 
160
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.
161
188
 
189
+ `pr sync --from` accepts a complete pull-request desired-state envelope. Use
190
+ `inari pr sync --help` for the top-level contract, or
191
+ `inari pr schema <template> --json` for its machine-readable `syncInput.schema`
192
+ and a valid `syncInput.minimalExample`.
193
+
162
194
  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`.
163
195
 
164
196
  `issue get` and `pr get` are canonical-only v1 reads. They resolve the target
@@ -168,7 +200,11 @@ parse the existing artifact with the same parser and semantic validator as
168
200
  metadata. Successful reads report `projection: "canonical"`; wrong-template,
169
201
  unparseable, ambiguous, and semantically invalid artifacts report structured
170
202
  diagnostics with `projection: "unavailable"` and never return guessed fields.
171
- When `--template` is omitted, all supported candidates are evaluated
203
+ When `--template` is omitted, Inari first looks for the bounded invisible
204
+ template identity marker every rendered artifact now carries. A valid marker
205
+ resolves the contract directly; an unknown, stale, or wrong-kind marker fails
206
+ closed with a diagnostic instead of guessing another template. Artifacts
207
+ without a marker fall back to evaluating all supported candidates
172
208
  deterministically; multiple structural matches fail closed. Native template
173
209
  boilerplate and raw Markdown are intentionally absent from successful output.
174
210
 
@@ -1,5 +1,6 @@
1
- import { type SemanticValidationResult, type SemanticViolation } from "./contract/validation.js";
2
- import { type CanonicalContract } from "./contract/ir.js";
1
+ import { type PartialSemanticValidationResult, type PartialSemanticRepairResult, type SemanticValidationResult, type SemanticViolation } from "./contract/validation.js";
2
+ import { type ArtifactDiagnostic, type ArtifactDiagnosticReport } from "./diagnostics.js";
3
+ import { type ArtifactKind, type CanonicalContract } from "./contract/ir.js";
3
4
  import { type ValidatedRenderedIssueArtifact, type ValidatedRenderedPullRequestArtifact } from "./github/types.js";
4
5
  export interface ArtifactInputMetadata {
5
6
  readonly title?: string;
@@ -14,6 +15,40 @@ export interface ArtifactInputDocument {
14
15
  readonly fields: Readonly<Record<string, unknown>>;
15
16
  readonly metadata: ArtifactInputMetadata;
16
17
  }
18
+ /**
19
+ * A representation-independent candidate entering the canonical contract.
20
+ * Adapters may decode JSON, native Markdown, an existing GitHub body, or
21
+ * internal field input, but they never validate or materialize contract
22
+ * semantics themselves.
23
+ */
24
+ export type ArtifactCandidateSource = "json" | "markdown" | "existing" | "fields";
25
+ export interface ArtifactCandidate {
26
+ readonly fields: unknown;
27
+ readonly metadata: ArtifactInputMetadata;
28
+ readonly source: ArtifactCandidateSource;
29
+ }
30
+ export interface ArtifactCandidateAdapterResult {
31
+ readonly parsed: boolean;
32
+ readonly candidate?: ArtifactCandidate;
33
+ readonly diagnostics: readonly ExistingArtifactDiagnostic[];
34
+ }
35
+ /** Result of the one candidate -> selected contract -> canonical JSON boundary. */
36
+ export interface CanonicalArtifactLoadResult {
37
+ readonly valid: boolean;
38
+ readonly complete: boolean;
39
+ /** Canonical contract-shaped semantic JSON. Never contains rejected fields. */
40
+ readonly canonical: Readonly<Record<string, unknown>>;
41
+ /** Explicit alias for callers that name the output canonical JSON. */
42
+ readonly canonicalJson: Readonly<Record<string, unknown>>;
43
+ /** Backward-compatible semantic value name used by renderer callers. */
44
+ readonly values: Readonly<Record<string, unknown>>;
45
+ readonly candidate: ArtifactCandidate;
46
+ readonly acceptedFields: readonly string[];
47
+ readonly missingFields: PartialSemanticValidationResult["missingFields"];
48
+ readonly invalidFields: PartialSemanticValidationResult["invalidFields"];
49
+ readonly diagnostics: ArtifactDiagnosticReport;
50
+ readonly violations: readonly SemanticViolation[];
51
+ }
17
52
  export interface ArtifactMetadataViolation {
18
53
  readonly code: "INPUT_METADATA_INVALID";
19
54
  readonly path: string;
@@ -23,21 +58,18 @@ export type ArtifactInputErrorCode = "INPUT_DOCUMENT_INVALID" | "INPUT_METADATA_
23
58
  export declare class ArtifactInputError extends Error {
24
59
  readonly code: ArtifactInputErrorCode;
25
60
  readonly path: string;
26
- constructor(code: ArtifactInputErrorCode, message: string, path?: string);
61
+ readonly details?: unknown;
62
+ constructor(code: ArtifactInputErrorCode, message: string, path?: string, details?: unknown);
27
63
  }
28
64
  export type ArtifactPreparationErrorCode = "ARTIFACT_PROVENANCE_MISSING" | "ARTIFACT_ROUND_TRIP_INVALID";
29
- export type ArtifactRoundTripDiagnosticCode = "ROUND_TRIP_PARSE" | "ROUND_TRIP_SEMANTIC" | "ROUND_TRIP_MISMATCH";
30
- export interface ArtifactRoundTripDiagnostic {
31
- readonly code: ArtifactRoundTripDiagnosticCode;
32
- readonly path: string;
33
- readonly message: string;
34
- readonly expected?: unknown;
35
- readonly actual?: unknown;
36
- }
65
+ /** @deprecated Round-trip diagnostics use the shared #118 diagnostic contract. */
66
+ export type ArtifactRoundTripDiagnostic = ArtifactDiagnostic;
67
+ /** @deprecated Use ArtifactDiagnosticCode/ArtifactDiagnosticDetailCode. */
68
+ export type ArtifactRoundTripDiagnosticCode = ArtifactDiagnostic["code"];
37
69
  /** Stable failures raised before a mutation-capable artifact is created. */
38
70
  export declare class ArtifactPreparationError extends Error {
39
71
  readonly code: ArtifactPreparationErrorCode;
40
- readonly diagnostics: readonly ArtifactRoundTripDiagnostic[];
72
+ readonly diagnostics: readonly ArtifactDiagnostic[];
41
73
  constructor(code: ArtifactPreparationErrorCode, message: string, diagnostics?: readonly ArtifactRoundTripDiagnostic[]);
42
74
  }
43
75
  export interface PreparedIssueArtifact {
@@ -51,7 +83,7 @@ export interface PreparedPullRequestArtifact {
51
83
  readonly artifact: ValidatedRenderedPullRequestArtifact;
52
84
  }
53
85
  export type ExistingArtifactClassification = "valid" | "semantic" | "wrong-template" | "unparseable" | "ambiguous";
54
- export type ExistingArtifactDiagnosticCode = "EXISTING_WRONG_TEMPLATE" | "EXISTING_UNPARSEABLE" | "EXISTING_EXTRA_CONTENT" | "EXISTING_UNKNOWN_CHECKLIST_ITEM" | "EXISTING_AMBIGUOUS_TEMPLATE" | "EXISTING_NON_CANONICAL" | "EXISTING_TEMPLATE_COMPILE_FAILED";
86
+ export type ExistingArtifactDiagnosticCode = "EXISTING_WRONG_TEMPLATE" | "EXISTING_UNPARSEABLE" | "EXISTING_EXTRA_CONTENT" | "EXISTING_UNKNOWN_CHECKLIST_ITEM" | "EXISTING_AMBIGUOUS_TEMPLATE" | "EXISTING_NON_CANONICAL" | "EXISTING_TEMPLATE_COMPILE_FAILED" | "EXISTING_TEMPLATE_MARKER_INVALID";
55
87
  export interface ExistingArtifactDiagnostic {
56
88
  readonly code: ExistingArtifactDiagnosticCode;
57
89
  readonly path: string;
@@ -62,6 +94,15 @@ export interface ExistingArtifactParseResult {
62
94
  readonly values: Readonly<Record<string, unknown>>;
63
95
  readonly diagnostics: readonly ExistingArtifactDiagnostic[];
64
96
  }
97
+ /**
98
+ * Semantic values recovered from an artifact that did not pass the strict
99
+ * structural parser. Values are extracted only from unambiguous contract
100
+ * sections and are still subject to the canonical semantic loader before use.
101
+ */
102
+ export interface RecoverableArtifactValues {
103
+ readonly values: Readonly<Record<string, unknown>>;
104
+ readonly diagnostics: readonly ExistingArtifactDiagnostic[];
105
+ }
65
106
  export interface ExistingArtifactValidationResult {
66
107
  readonly valid: boolean;
67
108
  readonly classification: ExistingArtifactClassification;
@@ -82,6 +123,38 @@ export interface ExistingPullRequestReader {
82
123
  readonly url: string;
83
124
  }>;
84
125
  }
126
+ /**
127
+ * Bounded invisible template identity marker embedded in newly rendered
128
+ * artifacts. It is the primary template-selection signal for governed
129
+ * read/repair/validation; legacy artifacts without a marker (or with one
130
+ * that cannot be trusted) fall back to deterministic structural matching.
131
+ * The marker is metadata only: it never substitutes for the authoritative
132
+ * repository governance/provenance that resolves the actual contract.
133
+ */
134
+ export declare const TEMPLATE_IDENTITY_MARKER_VERSION: "1";
135
+ export interface TemplateIdentityMarker {
136
+ readonly version: string;
137
+ readonly kind: ArtifactKind;
138
+ readonly path: string;
139
+ }
140
+ export type TemplateIdentityMarkerStatus = "absent" | "valid" | "malformed" | "unsupported-version";
141
+ export interface TemplateIdentityMarkerExtraction {
142
+ readonly status: TemplateIdentityMarkerStatus;
143
+ readonly marker?: TemplateIdentityMarker;
144
+ /** Body with a recognized trailing marker line removed; unchanged when none is present. */
145
+ readonly body: string;
146
+ }
147
+ /**
148
+ * Recognize and remove a trailing template identity marker line without
149
+ * applying semantic parsing. Only a line starting with the exact reserved
150
+ * marker prefix is treated as a marker attempt at all; ordinary trailing
151
+ * HTML comments (e.g. PR template scaffolding) are left untouched here and
152
+ * handled by the existing comment-stripping path. Once the reserved prefix
153
+ * is detected, the line is never silently ignored as "absent" again: an
154
+ * oversized, truncated, or otherwise broken marker attempt fails closed as
155
+ * "malformed" instead of falling through to structural matching.
156
+ */
157
+ export declare function extractTemplateIdentityMarker(body: string): TemplateIdentityMarkerExtraction;
85
158
  export interface FetchedExistingArtifact {
86
159
  readonly number: number;
87
160
  readonly url: string;
@@ -89,6 +162,41 @@ export interface FetchedExistingArtifact {
89
162
  }
90
163
  /** Parse the documented JSON input envelope while keeping field semantics adapter-independent. */
91
164
  export declare function parseArtifactInputDocument(input: unknown): ArtifactInputDocument;
165
+ /** Adapt a parsed JSON envelope without granting it canonical status. */
166
+ export declare function adaptJsonArtifactCandidate(input: unknown): ArtifactCandidate;
167
+ /** Adapt internal structured fields to the same candidate shape as JSON. */
168
+ export declare function adaptFieldArtifactCandidate(fields: unknown, metadata?: ArtifactInputMetadata): ArtifactCandidate;
169
+ /** Alias used by command adapters that call this input the CLI field path. */
170
+ export declare const adaptCliFieldCandidate: typeof adaptFieldArtifactCandidate;
171
+ /** Generic adapter spelling for callers that already hold structured fields. */
172
+ export declare const adaptArtifactCandidate: typeof adaptFieldArtifactCandidate;
173
+ /** Adapt an existing native artifact body through the repository parser. */
174
+ export declare function adaptMarkdownArtifactCandidate(contractInput: unknown, body: string | null | undefined): ArtifactCandidateAdapterResult;
175
+ /** Existing GitHub bodies use the same native Markdown adapter by design. */
176
+ export declare function adaptExistingArtifactCandidate(contractInput: unknown, body: string | null | undefined): ArtifactCandidateAdapterResult;
177
+ /**
178
+ * Reload a candidate against the selected canonical contract. Complete input
179
+ * takes the normal one-pass validator (and therefore may materialize contract
180
+ * defaults); incomplete/invalid input uses the bounded partial contract and
181
+ * exposes only accepted semantic values.
182
+ */
183
+ export declare function loadCanonicalArtifact(contractInput: unknown, candidateInput: unknown): CanonicalArtifactLoadResult;
184
+ /** Explicitly named alias for callers that pass a candidate object. */
185
+ export declare const loadCanonicalCandidate: typeof loadCanonicalArtifact;
186
+ /** Load a JSON representation through the canonical contract boundary. */
187
+ export declare function loadCanonicalJsonArtifact(contractInput: unknown, input: unknown): CanonicalArtifactLoadResult;
188
+ /** Load native Markdown through the same parser and canonical contract. */
189
+ export declare function loadCanonicalMarkdownArtifact(contractInput: unknown, body: string | null | undefined): CanonicalArtifactLoadResult;
190
+ /** Existing-body spelling retained so read/repair callers share one boundary. */
191
+ export declare function loadCanonicalExistingArtifact(contractInput: unknown, body: string | null | undefined): CanonicalArtifactLoadResult;
192
+ /** Classify an artifact input envelope without applying semantic defaults. */
193
+ export declare function validatePartialArtifactInput(contractInput: unknown, input: unknown): PartialSemanticValidationResult;
194
+ /** Terminology alias for callers that treat validation as classification. */
195
+ export declare const classifyPartialArtifactInput: typeof validatePartialArtifactInput;
196
+ /** Merge only a targeted field patch into a prior stateless partial result. */
197
+ export declare function repairPartialArtifactInput(contractInput: unknown, previous: unknown, patch?: unknown): PartialSemanticRepairResult;
198
+ /** Terminology alias for callers that describe targeted repair as a merge. */
199
+ export declare const mergePartialArtifactInput: typeof repairPartialArtifactInput;
92
200
  export declare function renderIssueArtifact(contractInput: unknown, input: unknown): string;
93
201
  export declare function renderPullRequestArtifact(contractInput: unknown, input: unknown): string;
94
202
  /** Construct the only values accepted by the GitHub mutation adapter. */
@@ -96,6 +204,14 @@ export declare function prepareIssueArtifact(contractInput: unknown, input: Arti
96
204
  export declare function preparePullRequestArtifact(contractInput: unknown, input: ArtifactInputDocument): PreparedPullRequestArtifact;
97
205
  export declare function parseExistingIssueArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactParseResult;
98
206
  export declare function parseExistingPullRequestArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactParseResult;
207
+ /**
208
+ * Recover field values from a malformed or wrong-template body without
209
+ * weakening the strict existing-artifact parser. The section boundaries and
210
+ * field decoding are the same parser primitives used by strict parsing; only
211
+ * the order/complete-structure requirement is relaxed for an explicitly
212
+ * selected repair target.
213
+ */
214
+ export declare function recoverExistingArtifactValues(contractInput: unknown, body: string | null | undefined): RecoverableArtifactValues;
99
215
  export declare function validateExistingIssueArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactValidationResult;
100
216
  export declare function validateExistingPullRequestArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactValidationResult;
101
217
  export interface ExistingArtifactCandidate {