gh-inari 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/artifact.d.ts +82 -0
  4. package/dist/artifact.js +472 -0
  5. package/dist/artifact.js.map +1 -0
  6. package/dist/cli.d.ts +14 -0
  7. package/dist/cli.js +395 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/contract/index.d.ts +4 -0
  10. package/dist/contract/index.js +5 -0
  11. package/dist/contract/index.js.map +1 -0
  12. package/dist/contract/ir.d.ts +178 -0
  13. package/dist/contract/ir.js +1071 -0
  14. package/dist/contract/ir.js.map +1 -0
  15. package/dist/contract/issue-form.d.ts +38 -0
  16. package/dist/contract/issue-form.js +662 -0
  17. package/dist/contract/issue-form.js.map +1 -0
  18. package/dist/contract/schema.d.ts +48 -0
  19. package/dist/contract/schema.js +162 -0
  20. package/dist/contract/schema.js.map +1 -0
  21. package/dist/contract/validation.d.ts +24 -0
  22. package/dist/contract/validation.js +212 -0
  23. package/dist/contract/validation.js.map +1 -0
  24. package/dist/github/adapter.d.ts +48 -0
  25. package/dist/github/adapter.js +463 -0
  26. package/dist/github/adapter.js.map +1 -0
  27. package/dist/github/errors.d.ts +43 -0
  28. package/dist/github/errors.js +65 -0
  29. package/dist/github/errors.js.map +1 -0
  30. package/dist/github/index.d.ts +4 -0
  31. package/dist/github/index.js +5 -0
  32. package/dist/github/index.js.map +1 -0
  33. package/dist/github/transport.d.ts +19 -0
  34. package/dist/github/transport.js +54 -0
  35. package/dist/github/transport.js.map +1 -0
  36. package/dist/github/types.d.ts +62 -0
  37. package/dist/github/types.js +16 -0
  38. package/dist/github/types.js.map +1 -0
  39. package/dist/github.d.ts +1 -0
  40. package/dist/github.js +2 -0
  41. package/dist/github.js.map +1 -0
  42. package/dist/index.d.ts +10 -0
  43. package/dist/index.js +32 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/pr-policy.d.ts +39 -0
  46. package/dist/pr-policy.js +313 -0
  47. package/dist/pr-policy.js.map +1 -0
  48. package/dist/pull-request-template.d.ts +42 -0
  49. package/dist/pull-request-template.js +481 -0
  50. package/dist/pull-request-template.js.map +1 -0
  51. package/dist/template-discovery.d.ts +68 -0
  52. package/dist/template-discovery.js +431 -0
  53. package/dist/template-discovery.js.map +1 -0
  54. package/gh-inari +18 -0
  55. package/package.json +92 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yohn-jp
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # Inari
2
+
3
+ Inari (`gh-inari`) is a GitHub CLI extension focused on repository governance: it turns a repository's native Issue Forms and pull request templates into deterministic typed contracts. It validates structured JSON, renders canonical Markdown, and performs GitHub mutations only after the contract, input, and rendered artifact have all passed validation.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install --global gh-inari
9
+ ```
10
+
11
+ The standalone executable is named `gh-inari`:
12
+
13
+ ```bash
14
+ gh-inari --help
15
+ gh-inari --version
16
+ ```
17
+
18
+ To register the installed package as a GitHub CLI extension, pass its package directory to `gh extension install`:
19
+
20
+ ```bash
21
+ (cd "$(npm root --global)/gh-inari" && gh extension install .)
22
+ gh inari --help
23
+ ```
24
+
25
+ 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.
26
+
27
+ ## Commands
28
+
29
+ ```bash
30
+ gh inari template list
31
+ gh inari issue schema <template> --json
32
+ gh inari issue validate --template <template> --from issue.json
33
+ gh inari issue render --template <template> --from issue.json
34
+ gh inari issue create --template <template> --from issue.json
35
+ gh inari pr schema <template> --json
36
+ gh inari pr validate --template <template> --from pr.json
37
+ gh inari pr render --template <template> --from pr.json
38
+ gh inari pr create --template <template> --from pr.json
39
+ gh inari issue validate <number> --template <template> --json
40
+ gh inari pr validate <number> --template <template> --json
41
+ gh inari issue explain <number> --template <template> --json
42
+ gh inari pr explain <number> --template <template> --json
43
+ ```
44
+
45
+ `--from -` reads JSON from stdin. Create input uses an envelope when mutation metadata is needed:
46
+
47
+ ```json
48
+ {
49
+ "fields": { "summary": "A reproducible defect" },
50
+ "title": "fix: correct the parser",
51
+ "labels": ["bug"]
52
+ }
53
+ ```
54
+
55
+ 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.
56
+
57
+ 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`.
58
+
59
+ ## Source of truth and supported semantics
60
+
61
+ `.github/ISSUE_TEMPLATE/**` and `.github/PULL_REQUEST_TEMPLATE*` remain the structural source of truth. Inari discovers and compiles those files; it does not replace them with a proprietary body schema. Supported Issue Form nodes are `input`, `textarea`, single- and multi-select `dropdown`, `checkboxes`, and `markdown`. Browser-only or ambiguous behavior, such as uploads or unsupported textarea rendering modes, fails closed.
62
+
63
+ 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:
64
+
65
+ ```yaml
66
+ version: 1
67
+ template: default
68
+ sections:
69
+ - section: linked_issue
70
+ linkedIssue: true
71
+ - section: summary
72
+ required: true
73
+ minLength: 20
74
+ - section: acceptance
75
+ checklist:
76
+ minCompleted: 1
77
+ requireComplete: false
78
+ ```
79
+
80
+ References must match native section identities (`id`/`sourceId`); stale, unknown, or ambiguous references fail closed. No overlay rule means no inferred requirement.
81
+
82
+ Issue Form top-level `labels` are preserved as create defaults. Top-level `assignees`, `projects`, and `type`, upload fields, and textarea code-block rendering are explicitly unsupported in v1 and fail closed rather than being approximated.
83
+
84
+ ## Scope
85
+
86
+ Inari owns repository-governed GitHub mutations: it reads repository-native governance, exposes it as machine-readable contracts, validates structured input against that governance, renders the canonical artifact, and performs the corresponding `gh` operation — or rejects it with actionable feedback.
87
+
88
+ Inari is not a general GitHub CLI wrapper. Generic read/query operations such as `pr view`, `issue view`, diff/search summarization, or token-efficient GitHub inspection are out of scope.
89
+
90
+ ## Safety and existing artifacts
91
+
92
+ Every create path is:
93
+
94
+ ```text
95
+ resolve template -> compile contract -> validate semantic JSON
96
+ -> render canonical Markdown -> construct validated-rendered artifact -> call gh
97
+ ```
98
+
99
+ Schema, validate, and render never call a remote mutation. Invalid, ambiguous, unparseable, or unsupported pre-flight state cannot reach the mutation adapter. Existing Issue and PR validation fetches the artifact through the typed `gh` adapter, reconstructs semantic values, and calls the same compiler-owned validator. Diagnostics distinguish valid artifacts, ordinary semantic violations, wrong-template bodies, and unparseable structure.
100
+
101
+ The public compiler, contract, validation, rendering, and adapter boundaries are library APIs. Future Actions or App adapters can use them without invoking or scraping CLI output.
102
+
103
+ ## Development
104
+
105
+ ```bash
106
+ pnpm install --frozen-lockfile
107
+ pnpm run verify
108
+ ```
109
+
110
+ ## License
111
+
112
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,82 @@
1
+ import { type SemanticValidationResult, type SemanticViolation } from "./contract/validation.js";
2
+ import { type ValidatedRenderedIssueArtifact, type ValidatedRenderedPullRequestArtifact } from "./github/types.js";
3
+ export interface ArtifactInputMetadata {
4
+ readonly title?: string;
5
+ readonly labels?: readonly string[];
6
+ readonly assignees?: readonly string[];
7
+ readonly head?: string;
8
+ readonly base?: string;
9
+ readonly draft?: boolean;
10
+ readonly maintainerCanModify?: boolean;
11
+ }
12
+ export interface ArtifactInputDocument {
13
+ readonly fields: Readonly<Record<string, unknown>>;
14
+ readonly metadata: ArtifactInputMetadata;
15
+ }
16
+ export type ArtifactInputErrorCode = "INPUT_DOCUMENT_INVALID" | "INPUT_METADATA_INVALID";
17
+ export declare class ArtifactInputError extends Error {
18
+ readonly code: ArtifactInputErrorCode;
19
+ readonly path: string;
20
+ constructor(code: ArtifactInputErrorCode, message: string, path?: string);
21
+ }
22
+ export interface PreparedIssueArtifact {
23
+ readonly input: ArtifactInputDocument;
24
+ readonly validation: SemanticValidationResult;
25
+ readonly artifact: ValidatedRenderedIssueArtifact;
26
+ }
27
+ export interface PreparedPullRequestArtifact {
28
+ readonly input: ArtifactInputDocument;
29
+ readonly validation: SemanticValidationResult;
30
+ readonly artifact: ValidatedRenderedPullRequestArtifact;
31
+ }
32
+ export type ExistingArtifactClassification = "valid" | "semantic" | "wrong-template" | "unparseable";
33
+ export type ExistingArtifactDiagnosticCode = "EXISTING_WRONG_TEMPLATE" | "EXISTING_UNPARSEABLE" | "EXISTING_EXTRA_CONTENT" | "EXISTING_UNKNOWN_CHECKLIST_ITEM";
34
+ export interface ExistingArtifactDiagnostic {
35
+ readonly code: ExistingArtifactDiagnosticCode;
36
+ readonly path: string;
37
+ readonly message: string;
38
+ }
39
+ export interface ExistingArtifactParseResult {
40
+ readonly parsed: boolean;
41
+ readonly values: Readonly<Record<string, unknown>>;
42
+ readonly diagnostics: readonly ExistingArtifactDiagnostic[];
43
+ }
44
+ export interface ExistingArtifactValidationResult {
45
+ readonly valid: boolean;
46
+ readonly classification: ExistingArtifactClassification;
47
+ readonly parse: ExistingArtifactParseResult;
48
+ readonly violations: readonly ExistingArtifactDiagnostic[] | readonly SemanticViolation[];
49
+ }
50
+ export interface ExistingIssueReader {
51
+ getIssue(issueNumber: number): Promise<{
52
+ readonly body: string | null;
53
+ readonly url: string;
54
+ }>;
55
+ }
56
+ export interface ExistingPullRequestReader {
57
+ getPullRequest(pullRequestNumber: number): Promise<{
58
+ readonly body: string | null;
59
+ readonly url: string;
60
+ }>;
61
+ }
62
+ export interface FetchedExistingArtifact {
63
+ readonly number: number;
64
+ readonly url: string;
65
+ readonly result: ExistingArtifactValidationResult;
66
+ }
67
+ /** Parse the documented JSON input envelope while keeping field semantics adapter-independent. */
68
+ export declare function parseArtifactInputDocument(input: unknown): ArtifactInputDocument;
69
+ export declare function renderIssueArtifact(contractInput: unknown, input: unknown): string;
70
+ export declare function renderPullRequestArtifact(contractInput: unknown, input: unknown): string;
71
+ /** Construct the only values accepted by the GitHub mutation adapter. */
72
+ export declare function prepareIssueArtifact(contractInput: unknown, input: ArtifactInputDocument): PreparedIssueArtifact;
73
+ export declare function preparePullRequestArtifact(contractInput: unknown, input: ArtifactInputDocument): PreparedPullRequestArtifact;
74
+ export declare function parseExistingIssueArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactParseResult;
75
+ export declare function parseExistingPullRequestArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactParseResult;
76
+ export declare function validateExistingIssueArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactValidationResult;
77
+ export declare function validateExistingPullRequestArtifact(contractInput: unknown, body: string | null | undefined): ExistingArtifactValidationResult;
78
+ export declare function validateExistingIssueFromAdapter(reader: ExistingIssueReader, contract: unknown, issueNumber: number): Promise<FetchedExistingArtifact>;
79
+ export declare function validateExistingPullRequestFromAdapter(reader: ExistingPullRequestReader, contract: unknown, pullRequestNumber: number): Promise<FetchedExistingArtifact>;
80
+ /** Escape only Markdown constructs that could change the canonical section structure. */
81
+ export declare function escapeMarkdownValue(value: string): string;
82
+ export declare function removeHtmlComments(value: string): string;