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.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/artifact.d.ts +82 -0
- package/dist/artifact.js +472 -0
- package/dist/artifact.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +395 -0
- package/dist/cli.js.map +1 -0
- package/dist/contract/index.d.ts +4 -0
- package/dist/contract/index.js +5 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/ir.d.ts +178 -0
- package/dist/contract/ir.js +1071 -0
- package/dist/contract/ir.js.map +1 -0
- package/dist/contract/issue-form.d.ts +38 -0
- package/dist/contract/issue-form.js +662 -0
- package/dist/contract/issue-form.js.map +1 -0
- package/dist/contract/schema.d.ts +48 -0
- package/dist/contract/schema.js +162 -0
- package/dist/contract/schema.js.map +1 -0
- package/dist/contract/validation.d.ts +24 -0
- package/dist/contract/validation.js +212 -0
- package/dist/contract/validation.js.map +1 -0
- package/dist/github/adapter.d.ts +48 -0
- package/dist/github/adapter.js +463 -0
- package/dist/github/adapter.js.map +1 -0
- package/dist/github/errors.d.ts +43 -0
- package/dist/github/errors.js +65 -0
- package/dist/github/errors.js.map +1 -0
- package/dist/github/index.d.ts +4 -0
- package/dist/github/index.js +5 -0
- package/dist/github/index.js.map +1 -0
- package/dist/github/transport.d.ts +19 -0
- package/dist/github/transport.js +54 -0
- package/dist/github/transport.js.map +1 -0
- package/dist/github/types.d.ts +62 -0
- package/dist/github/types.js +16 -0
- package/dist/github/types.js.map +1 -0
- package/dist/github.d.ts +1 -0
- package/dist/github.js +2 -0
- package/dist/github.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/pr-policy.d.ts +39 -0
- package/dist/pr-policy.js +313 -0
- package/dist/pr-policy.js.map +1 -0
- package/dist/pull-request-template.d.ts +42 -0
- package/dist/pull-request-template.js +481 -0
- package/dist/pull-request-template.js.map +1 -0
- package/dist/template-discovery.d.ts +68 -0
- package/dist/template-discovery.js +431 -0
- package/dist/template-discovery.js.map +1 -0
- package/gh-inari +18 -0
- 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;
|