@archora/forge-cli 1.2.1 → 1.3.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 (3) hide show
  1. package/LICENSE +121 -0
  2. package/dist/index.js +244 -6
  3. package/package.json +51 -51
package/LICENSE ADDED
@@ -0,0 +1,121 @@
1
+ Archora Forge Proprietary Source-Available License (Paid-Use-Only)
2
+
3
+ Copyright (c) 2026 Aleksandr Kotov (the "Licensor"). All rights reserved.
4
+
5
+ The source code and any other materials made available in this repository
6
+ (collectively, the "Software") are proprietary and confidential to the
7
+ Licensor. They are published in source-available form for the sole purpose
8
+ of allowing prospective licensees to inspect and evaluate the Software
9
+ before purchasing a commercial license.
10
+
11
+ This Software is NOT open source. The publication of this repository on a
12
+ public hosting platform does NOT constitute a grant of any open-source or
13
+ free-software license, and does NOT grant any rights beyond those expressly
14
+ stated below.
15
+
16
+ 1. Limited Permission Granted Without a Paid License
17
+
18
+ Without entering into a separate written commercial license with the
19
+ Licensor, you are permitted to do ONLY the following with the Software:
20
+
21
+ (a) view and read the source code via the hosting platform on which
22
+ it is published;
23
+ (b) clone or download a copy strictly for the purpose of personally
24
+ evaluating whether to obtain a paid commercial license, for a
25
+ reasonable evaluation period not exceeding thirty (30) days; and
26
+ (c) discuss the Software publicly (e.g. cite, link, or quote short
27
+ excerpts for commentary, criticism, news reporting, teaching,
28
+ scholarship, or research) consistent with applicable fair use
29
+ or fair dealing law.
30
+
31
+ No other rights are granted. In particular, and without limitation,
32
+ the following are NOT permitted without a separate paid commercial
33
+ license signed by the Licensor:
34
+
35
+ - any use of the Software, in whole or in part, in any project,
36
+ product, service, library, internal tool, script, pipeline,
37
+ build, CI job, or deliverable, whether commercial or
38
+ non-commercial, paid or unpaid, personal or organizational,
39
+ for-profit or not-for-profit, including hobby and educational
40
+ projects;
41
+ - execution of the Software for any purpose other than short-term
42
+ evaluation by you personally as described in clause 1(b);
43
+ - copying, modifying, merging, publishing, sublicensing,
44
+ distributing, selling, hosting, or offering the Software (or any
45
+ derivative work) as a service to any third party;
46
+ - using code generated by the Software in any project for which
47
+ you do not hold a valid commercial license to the Software;
48
+ - removing or altering any copyright, trademark, license, or
49
+ attribution notices contained in the Software or in code it
50
+ generates;
51
+ - using the Software, its source code, or its outputs to train,
52
+ fine-tune, evaluate, benchmark, or otherwise develop
53
+ machine-learning or artificial-intelligence models, datasets, or
54
+ systems;
55
+ - reverse-engineering the Software for any purpose other than
56
+ evaluation as described in clause 1(b), to the extent such
57
+ restriction is permitted by applicable law.
58
+
59
+ 2. No Implied License
60
+
61
+ No license is granted by implication, estoppel, exhaustion, course of
62
+ dealing, or otherwise. Public availability of this repository does
63
+ not change this.
64
+
65
+ 3. Commercial License Required
66
+
67
+ To use the Software for any purpose beyond the limited evaluation
68
+ described in Section 1, you must first obtain a paid commercial
69
+ license from the Licensor. Pricing and terms are available on
70
+ request — see COMMERCIAL-LICENSE.md.
71
+
72
+ 4. Trademarks
73
+
74
+ "Archora" and "Archora Forge" and any associated names, logos, and
75
+ brands are trademarks of the Licensor. No trademark rights are
76
+ granted by this license. Forks, modifications, or derivative works
77
+ (where permitted under a separate commercial license) may not use
78
+ these marks without the Licensor's prior written consent.
79
+
80
+ 5. Term and Termination
81
+
82
+ Your permission under Section 1 terminates automatically and
83
+ immediately upon any breach of this license, or upon written notice
84
+ from the Licensor. Upon termination you must delete all copies of
85
+ the Software in your possession or control, except for archival
86
+ copies you are required by law to retain.
87
+
88
+ 6. No Warranty
89
+
90
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
91
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
92
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
93
+ NON-INFRINGEMENT.
94
+
95
+ 7. Limitation of Liability
96
+
97
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL
98
+ THE LICENSOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
99
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
100
+ WITH THE SOFTWARE OR THIS LICENSE, EVEN IF ADVISED OF THE POSSIBILITY
101
+ OF SUCH DAMAGES.
102
+
103
+ 8. Governing Law
104
+
105
+ This license shall be governed by and construed in accordance with
106
+ the laws applicable at the Licensor's place of residence, without
107
+ regard to its conflict-of-laws principles.
108
+
109
+ 9. Prior Versions
110
+
111
+ Versions of the Software previously distributed under a different
112
+ license (including, without limitation, the MIT License) remain
113
+ governed by the license under which they were originally
114
+ distributed, with respect to the specific commits to which that
115
+ prior license applied. From the effective date of this license,
116
+ subsequent commits and releases are governed exclusively by these
117
+ terms.
118
+
119
+ For commercial licensing inquiries, contact:
120
+ Email: akotov@archora.dev
121
+ Telegram: @akotofff
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ var ForgeError = class extends Error {
30
30
  this.details = details;
31
31
  }
32
32
  };
33
- var forgeCoreVersion = "1.2.1";
33
+ var forgeCoreVersion = "1.3.0";
34
34
  var forgeGeneratedMarker = "// @archora-forge-generated";
35
35
  var forgeGeneratedMetadataMarker = "// @archora-forge-meta";
36
36
  async function writeGeneratedFiles(files, options) {
@@ -3350,6 +3350,9 @@ function renderReadiness(readiness) {
3350
3350
  <div class="card">
3351
3351
  <h2>Pilot Readiness</h2>
3352
3352
  <p>Status: <strong>${escapeHtml(readiness.status ?? "n/a")}</strong></p>
3353
+ ${readiness.gate ? `<p>Gate: <strong>${escapeHtml(readiness.gate.result ?? "n/a")}</strong></p>
3354
+ <p>Recommended CI mode: <strong>${escapeHtml(readiness.gate.recommendedCiMode ?? "n/a")}</strong></p>
3355
+ <p>${escapeHtml(readiness.gate.reason ?? "")}</p>` : ""}
3353
3356
  <p>${escapeHtml(readiness.decision ?? "")}</p>
3354
3357
  </div>
3355
3358
  <div class="card">
@@ -3364,6 +3367,10 @@ function renderReadiness(readiness) {
3364
3367
  <h2>Warnings</h2>
3365
3368
  ${renderSimpleList(readiness.warnings ?? [], "No warnings.")}
3366
3369
  </div>
3370
+ ${readiness.reviewerChecklist ? `<div class="card">
3371
+ <h2>Reviewer Checklist</h2>
3372
+ ${renderSimpleList(readiness.reviewerChecklist, "No reviewer checklist.")}
3373
+ </div>` : ""}
3367
3374
  </section>`;
3368
3375
  }
3369
3376
  function renderSimpleList(items, empty) {
@@ -4056,12 +4063,33 @@ function createAuditReadiness(input) {
4056
4063
  ...input.diagnostics.length > 0 ? ["Use fix suggestions to decide which OpenAPI changes are required before purchase."] : [],
4057
4064
  ...input.ok ? ["Use this audit package as the paid pilot evidence bundle."] : []
4058
4065
  ];
4066
+ const status = blockers.length > 0 ? "blocked" : warnings.length > 0 ? "needs-attention" : "ready";
4067
+ const gate = status === "ready" ? {
4068
+ result: "pass",
4069
+ recommendedCiMode: "block",
4070
+ reason: "Generated resource layer is ready for a blocking CI gate under the current policy."
4071
+ } : status === "needs-attention" ? {
4072
+ result: "warn",
4073
+ recommendedCiMode: "comment",
4074
+ reason: "Continue evaluation, but require explicit owner review before blocking merges."
4075
+ } : {
4076
+ result: "fail",
4077
+ recommendedCiMode: "block",
4078
+ reason: "Do not widen rollout until blockers are fixed or explicitly accepted."
4079
+ };
4059
4080
  return {
4060
- status: blockers.length > 0 ? "blocked" : warnings.length > 0 ? "needs-attention" : "ready",
4081
+ status,
4082
+ gate,
4061
4083
  decision: input.ok ? "Generated resource layer is ready for local adoption review." : "Generated resource layer needs fixes or explicit risk acceptance before purchase.",
4062
4084
  blockers,
4063
4085
  warnings,
4064
4086
  nextActions: nextActions.length > 0 ? nextActions : ["No action required."],
4087
+ reviewerChecklist: [
4088
+ "Open `index.html` and confirm detected resources match the frontend mental model.",
4089
+ "Open `report.md` and review blockers, warnings, scorecard and fix suggestions.",
4090
+ "Open `typecheck.md` and confirm generated TypeScript passed or every failure is triaged.",
4091
+ "Compare generated files with the existing API layer before committing rollout scope."
4092
+ ],
4065
4093
  summary: {
4066
4094
  healthScore: input.healthScore,
4067
4095
  resources: input.resources,
@@ -4093,6 +4121,16 @@ function createAuditMarkdown(payload) {
4093
4121
 
4094
4122
  Status: ${payload.ok ? "passed" : "failed"}
4095
4123
 
4124
+ ## Executive Review
4125
+
4126
+ Readiness: ${payload.readiness.status}
4127
+
4128
+ Gate: ${payload.readiness.gate.result}
4129
+
4130
+ Recommended CI mode: ${payload.readiness.gate.recommendedCiMode}
4131
+
4132
+ Gate reason: ${payload.readiness.gate.reason}
4133
+
4096
4134
  Decision: ${payload.readiness.decision}
4097
4135
 
4098
4136
  Health score: ${payload.healthScore}
@@ -4111,6 +4149,10 @@ ${Object.entries(payload.scorecard).map(([key, value]) => `- ${key}: ${value}`).
4111
4149
 
4112
4150
  ${payload.fixSuggestions.length > 0 ? payload.fixSuggestions.map((item) => `- ${item.code} (${item.count}): ${item.suggestion}`).join("\n") : "- No fix suggestions."}
4113
4151
 
4152
+ ## Reviewer Checklist
4153
+
4154
+ ${payload.readiness.reviewerChecklist.map((item) => `- ${item}`).join("\n")}
4155
+
4114
4156
  ## Artifacts
4115
4157
 
4116
4158
  ${payload.audit.artifacts.map((artifact) => `- ${artifact}`).join("\n")}
@@ -4345,6 +4387,13 @@ function createMarkdownReport(payload) {
4345
4387
 
4346
4388
  Readiness: ${payload.readiness.status}
4347
4389
 
4390
+ ${payload.readiness.gate ? `Gate: ${payload.readiness.gate.result}
4391
+
4392
+ Recommended CI mode: ${payload.readiness.gate.recommendedCiMode}
4393
+
4394
+ Gate reason: ${payload.readiness.gate.reason}
4395
+ ` : ""}
4396
+
4348
4397
  Decision: ${payload.readiness.decision}
4349
4398
 
4350
4399
  Blockers:
@@ -4430,6 +4479,19 @@ function createReadinessSummary(input) {
4430
4479
  ...input.summary.healthScore < 90 ? [`Health score is ${input.summary.healthScore}, below the recommended pilot threshold of 90.`] : []
4431
4480
  ];
4432
4481
  const status = blockers.length > 0 ? "blocked" : warnings.length > 0 ? "needs-attention" : "ready";
4482
+ const gate = status === "ready" ? {
4483
+ result: "pass",
4484
+ recommendedCiMode: "block",
4485
+ reason: "Keep the blocking check enabled; no report findings require human acceptance."
4486
+ } : status === "needs-attention" ? {
4487
+ result: "warn",
4488
+ recommendedCiMode: "comment",
4489
+ reason: "Use comment-only mode until warnings are triaged or accepted."
4490
+ } : {
4491
+ result: "fail",
4492
+ recommendedCiMode: "block",
4493
+ reason: "Block merge or require explicit acceptance before pilot handoff."
4494
+ };
4433
4495
  const nextActions = status === "ready" ? ["Use the report as the pilot readiness artifact and keep `archora-forge check` in CI."] : [
4434
4496
  ...input.drift.length > 0 ? ["Run `archora-forge generate` and commit the generated output, or review intentional drift before the pilot handoff."] : [],
4435
4497
  ...input.diagnostics.length > 0 ? ["Review diagnostics and decide which schema fixes are required for the pilot scope."] : [],
@@ -4437,6 +4499,7 @@ function createReadinessSummary(input) {
4437
4499
  ];
4438
4500
  return {
4439
4501
  status,
4502
+ gate,
4440
4503
  decision: status === "ready" ? "Schema is ready for a pilot readiness handoff under the current check policy." : status === "needs-attention" ? "Schema can continue through pilot review, but findings should be triaged first." : "Schema is not ready for pilot handoff until blockers are resolved or explicitly accepted.",
4441
4504
  blockers,
4442
4505
  warnings,
@@ -4462,17 +4525,18 @@ function evaluateFailedChecks(input) {
4462
4525
  // src/commands/ci.command.ts
4463
4526
  import { access as access3 } from "fs/promises";
4464
4527
  function registerCiCommand(cli) {
4465
- cli.command("ci <action> <provider>", "Create CI workflow templates for Forge impact review").option("--force", "Overwrite an existing workflow file").option("--output <path>", "Write workflow to a custom path").option("--mode <mode>", "Workflow mode: impact or pilot").option("--schema <path>", "OpenAPI schema path inside the repository").option("--base <ref>", "Git base ref for previous schema").action(async (action, provider, options) => {
4528
+ cli.command("ci <action> <provider>", "Create CI workflow templates for Forge impact review").option("--force", "Overwrite an existing workflow file").option("--output <path>", "Write workflow to a custom path").option("--mode <mode>", "Workflow mode: impact or pilot").option("--gate <mode>", "Merge gate mode: block or comment").option("--schema <path>", "OpenAPI schema path inside the repository").option("--base <ref>", "Git base ref for previous schema").action(async (action, provider, options) => {
4466
4529
  if (action !== "init") throw new Error(`Unknown CI action "${action}". Use init.`);
4467
4530
  if (provider !== "github") throw new Error(`Unsupported CI provider "${provider}". Use github.`);
4468
4531
  const mode = normalizeMode(options.mode);
4532
+ const gate = normalizeGate(options.gate);
4469
4533
  const path = options.output ?? ".github/workflows/archora-forge-impact.yml";
4470
4534
  if (await fileExists2(path) && !options.force) {
4471
4535
  logger.warn(`${path} already exists`);
4472
4536
  logger.line("Use --force to overwrite it.");
4473
4537
  return;
4474
4538
  }
4475
- await writeReportFile(path, createGithubImpactWorkflow({ mode, schema: options.schema ?? "openapi.yaml", base: options.base ?? "origin/main" }));
4539
+ await writeReportFile(path, createGithubImpactWorkflow({ mode, gate, schema: options.schema ?? "openapi.yaml", base: options.base ?? "origin/main" }));
4476
4540
  logger.success(`Created ${path}`);
4477
4541
  logger.line("Next: review OPENAPI_SCHEMA and OPENAPI_BASE_REF for your repository.");
4478
4542
  });
@@ -4485,6 +4549,11 @@ function normalizeMode(value) {
4485
4549
  if (value === "impact" || value === "pilot") return value;
4486
4550
  throw new Error("Invalid CI mode. Use impact or pilot.");
4487
4551
  }
4552
+ function normalizeGate(value) {
4553
+ if (!value) return "block";
4554
+ if (value === "block" || value === "comment") return value;
4555
+ throw new Error("Invalid CI gate mode. Use block or comment.");
4556
+ }
4488
4557
  function createGithubImpactWorkflow(options) {
4489
4558
  const runStep = options.mode === "pilot" ? ` pnpm exec archora-forge pilot "$OPENAPI_SCHEMA" --base "$OPENAPI_BASE_REF" \\
4490
4559
  --repo . \\
@@ -4496,6 +4565,12 @@ function createGithubImpactWorkflow(options) {
4496
4565
  --pr-comment-file forge-impact-pr.md`;
4497
4566
  const artifactPath = options.mode === "pilot" ? ` forge-pilot/**` : ` forge-impact.md
4498
4567
  forge-impact-pr.md`;
4568
+ const blockStep = options.gate === "block" ? `
4569
+ - name: Block merge on blocked API impact
4570
+ if: steps.forge.outcome == 'failure'
4571
+ run: |
4572
+ echo "Archora Forge reported blocked API impact. Review the PR comment and uploaded artifacts."
4573
+ exit 1` : "";
4499
4574
  return `name: archora-forge-impact
4500
4575
 
4501
4576
  on:
@@ -4512,6 +4587,7 @@ permissions:
4512
4587
  env:
4513
4588
  OPENAPI_SCHEMA: ${options.schema}
4514
4589
  OPENAPI_BASE_REF: ${options.base}
4590
+ FORGE_GATE_MODE: ${options.gate}
4515
4591
 
4516
4592
  jobs:
4517
4593
  ${options.mode}:
@@ -4533,6 +4609,8 @@ jobs:
4533
4609
  - run: pnpm install --frozen-lockfile
4534
4610
 
4535
4611
  - name: Run Forge impact
4612
+ id: forge
4613
+ continue-on-error: true
4536
4614
  run: |
4537
4615
  ${runStep}
4538
4616
 
@@ -4548,6 +4626,7 @@ ${artifactPath}
4548
4626
  with:
4549
4627
  file-path: forge-impact-pr.md
4550
4628
  comment-tag: archora-forge-impact
4629
+ ${blockStep}
4551
4630
  `;
4552
4631
  }
4553
4632
 
@@ -4640,14 +4719,48 @@ function formatPullRequestComment(payload) {
4640
4719
  return [
4641
4720
  "## Frontend API Impact",
4642
4721
  "",
4722
+ `Merge decision: ${formatMergeDecision(payload.decision.status)}`,
4723
+ `Merge risk: ${payload.decision.mergeRisk}`,
4724
+ `Reason: ${payload.decision.reason}`,
4725
+ "",
4643
4726
  payload.prSummary,
4644
4727
  "",
4728
+ "## Next Actions",
4729
+ "",
4730
+ ...formatNextActionLines(payload),
4731
+ "",
4645
4732
  "## Source Usage",
4646
4733
  "",
4647
4734
  ...formatSourceUsageLines(payload.sourceUsages ?? []),
4648
4735
  ""
4649
4736
  ].join("\n");
4650
4737
  }
4738
+ function formatMergeDecision(status) {
4739
+ if (status === "blocked") return "block";
4740
+ if (status === "review") return "review";
4741
+ return "pass";
4742
+ }
4743
+ function formatNextActionLines(payload) {
4744
+ if (payload.decision.status === "blocked") {
4745
+ return [
4746
+ "- Do not merge until the breaking frontend contract changes are handled.",
4747
+ "- Update impacted source usages before regenerating committed Forge output.",
4748
+ "- Re-run `archora-forge impact` after the OpenAPI or frontend changes are updated."
4749
+ ];
4750
+ }
4751
+ if (payload.decision.status === "review") {
4752
+ return [
4753
+ "- Review warnings with the frontend owner before merge.",
4754
+ "- Regenerate Forge output in the branch if the contract change is accepted.",
4755
+ "- Keep the PR comment attached to the API change for reviewer context."
4756
+ ];
4757
+ }
4758
+ return [
4759
+ "- Merge can continue from the API impact perspective.",
4760
+ "- Regenerate Forge output when the schema change is accepted.",
4761
+ "- Keep `archora-forge check` in CI to catch drift after regeneration."
4762
+ ];
4763
+ }
4651
4764
  function formatSourceUsageLines(usages) {
4652
4765
  if (usages.length === 0) return ["No impacted source usages found."];
4653
4766
  return usages.slice(0, 50).map((usage) => `- \`${usage.path}:${usage.lines.join(",")}\`: ${usage.matches.join(", ")}`);
@@ -4832,11 +4945,15 @@ function registerDemoCommand(cli) {
4832
4945
  skipTypecheck: true
4833
4946
  });
4834
4947
  await writeReportFile(join8(reportDir, "go-no-go.md"), createDemoGoNoGo(impact, audit.payload.ok));
4948
+ await Promise.all([
4949
+ writeReportFile(join8(reportDir, "check.html"), createDemoCheckHtml(impact, audit.payload.ok)),
4950
+ writeReportFile(join8(reportDir, "README.md"), createDemoHandoff())
4951
+ ]);
4835
4952
  logger.title();
4836
4953
  logger.line(`Demo package: ${outDir}`);
4837
4954
  logger.line(`Decision: ${impact.decision.status === "blocked" ? "no-go" : "review"}`);
4838
4955
  logger.line(`Impact report: ${join8(reportDir, "impact.md")}`);
4839
- process.exitCode = impact.decision.status === "blocked" ? 1 : 0;
4956
+ process.exitCode = 0;
4840
4957
  } catch (error) {
4841
4958
  logger.error(error instanceof Error ? error.message : String(error));
4842
4959
  process.exitCode = 2;
@@ -4867,6 +4984,69 @@ function createDemoGoNoGo(impact, auditOk) {
4867
4984
  ""
4868
4985
  ].join("\n");
4869
4986
  }
4987
+ function createDemoCheckHtml(impact, auditOk) {
4988
+ const failedChecks = [
4989
+ ...impact.decision.status === "blocked" ? ["api-impact-blocked"] : [],
4990
+ ...!auditOk ? ["audit-readiness-failed"] : []
4991
+ ];
4992
+ const status = failedChecks.length === 0 ? "ready" : "blocked";
4993
+ return createHtmlReport("Archora Forge Check", {
4994
+ ok: failedChecks.length === 0,
4995
+ schema: impact.newSchema,
4996
+ healthScore: auditOk ? 100 : 80,
4997
+ generatedFiles: impact.affectedFiles.length,
4998
+ failedChecks,
4999
+ readiness: {
5000
+ status,
5001
+ gate: status === "blocked" ? {
5002
+ result: "fail",
5003
+ recommendedCiMode: "block",
5004
+ reason: "Demo impact contains blocking frontend API changes."
5005
+ } : {
5006
+ result: "pass",
5007
+ recommendedCiMode: "block",
5008
+ reason: "Demo package has no blocking checks under the current policy."
5009
+ },
5010
+ decision: status === "blocked" ? "Do not merge until breaking frontend contract changes are handled." : "The demo package can continue through review.",
5011
+ blockers: failedChecks.length > 0 ? failedChecks.map((check) => `Failed check: ${check}.`) : [],
5012
+ warnings: impact.summary.warnings > 0 ? [`Impact report contains ${impact.summary.warnings} warnings.`] : [],
5013
+ nextActions: status === "blocked" ? ["Open `impact-pr.md` first.", "Review impacted source usages.", "Open `audit/index.html` for generated output readiness."] : ["Attach this report to the PR or pilot handoff."],
5014
+ reviewerChecklist: [
5015
+ "Confirm `impact-pr.md` gives a clear merge decision.",
5016
+ "Confirm affected generated files map to real frontend ownership.",
5017
+ "Confirm `audit/index.html` matches the expected resource model."
5018
+ ]
5019
+ },
5020
+ summary: impact.summary,
5021
+ decision: impact.decision,
5022
+ affectedResources: impact.affectedResources,
5023
+ affectedFiles: impact.affectedFiles,
5024
+ sourceUsages: impact.sourceUsages,
5025
+ migrationHints: impact.migrationHints
5026
+ });
5027
+ }
5028
+ function createDemoHandoff() {
5029
+ return [
5030
+ "# Forge Demo Package",
5031
+ "",
5032
+ "Open `impact-pr.md` first. It is the pull-request comment a reviewer should see before merge.",
5033
+ "",
5034
+ "Then review:",
5035
+ "",
5036
+ "- Open `impact.md` for the full frontend API impact report.",
5037
+ "- Open `check.html` for the reviewer-friendly readiness summary.",
5038
+ "- Open `audit/index.html` for generated output readiness and audit evidence.",
5039
+ "- Open `go-no-go.md` for the short adoption decision.",
5040
+ "",
5041
+ "To run the same package on a real repo:",
5042
+ "",
5043
+ "```sh",
5044
+ "archora-forge pilot ./openapi.yaml --base origin/main --repo . --out forge-pilot",
5045
+ "archora-forge ci init github --schema ./openapi.yaml --base origin/main --gate comment",
5046
+ "```",
5047
+ ""
5048
+ ].join("\n");
5049
+ }
4870
5050
  var demoOldSchema = `openapi: 3.0.3
4871
5051
  info: { title: Forge Demo API, version: 1.0.0 }
4872
5052
  paths:
@@ -5573,7 +5753,10 @@ function registerPilotCommand(cli) {
5573
5753
  schemaHeader: options.schemaHeader
5574
5754
  });
5575
5755
  const decision = createGoNoGo({ impact, auditOk: audit.payload.ok, auditDecision: audit.payload.readiness.decision });
5576
- await writeReportFile(join9(outDir, "go-no-go.md"), decision.markdown);
5756
+ await Promise.all([
5757
+ writeReportFile(join9(outDir, "go-no-go.md"), decision.markdown),
5758
+ writeReportFile(join9(outDir, "pilot-report.md"), createPilotReport({ impact, status: decision.status, auditOk: audit.payload.ok, auditDecision: audit.payload.readiness.decision }))
5759
+ ]);
5577
5760
  logger.title();
5578
5761
  logger.line(`Pilot package: ${outDir}`);
5579
5762
  logger.line(`Decision: ${decision.status}`);
@@ -5649,6 +5832,61 @@ function createGoNoGo(input) {
5649
5832
  ];
5650
5833
  return { status, markdown: lines.join("\n") };
5651
5834
  }
5835
+ function createPilotReport(input) {
5836
+ return [
5837
+ "# Archora Forge Pilot Report",
5838
+ "",
5839
+ "## Decision",
5840
+ "",
5841
+ `Decision: ${input.status}`,
5842
+ `Impact: ${input.impact.decision.status}`,
5843
+ `Merge risk: ${input.impact.decision.mergeRisk}`,
5844
+ `Audit: ${input.auditOk ? "passed" : "failed"}`,
5845
+ "",
5846
+ "## Artifact Links",
5847
+ "",
5848
+ "- `impact-pr.md` - PR comment for reviewers.",
5849
+ "- `impact.md` - full frontend API impact report.",
5850
+ "- `impact.json` - machine-readable impact payload.",
5851
+ "- `audit/index.html` - generated output audit and readiness report.",
5852
+ "- `audit/report.md` - Markdown audit handoff.",
5853
+ "- `go-no-go.md` - short adoption decision.",
5854
+ "",
5855
+ "## Impact Summary",
5856
+ "",
5857
+ `- Old schema: ${input.impact.oldSchema}`,
5858
+ `- New schema: ${input.impact.newSchema}`,
5859
+ ...input.impact.base ? [`- Base ref: ${input.impact.base}`] : [],
5860
+ `- Breaking changes: ${input.impact.summary.breaking}`,
5861
+ `- Warnings: ${input.impact.summary.warnings}`,
5862
+ `- Non-breaking changes: ${input.impact.summary.nonBreaking}`,
5863
+ `- Source usage matches: ${input.impact.sourceUsages?.length ?? 0}`,
5864
+ "",
5865
+ "## Affected Surface",
5866
+ "",
5867
+ `- Resources: ${formatInlineList(input.impact.affectedResources)}`,
5868
+ `- Generated files: ${formatInlineList(input.impact.affectedFiles)}`,
5869
+ `- Operation IDs: ${formatInlineList(input.impact.impactedSurface.operationIds)}`,
5870
+ `- Client methods: ${formatInlineList(input.impact.impactedSurface.clientMethods)}`,
5871
+ `- Query hooks: ${formatInlineList(input.impact.impactedSurface.queryHooks)}`,
5872
+ "",
5873
+ "## Reviewer Checklist",
5874
+ "",
5875
+ "- Confirm `impact-pr.md` gives a clear merge decision.",
5876
+ "- Confirm `impact.md` names the breaking changes and affected generated files.",
5877
+ "- Confirm `audit/index.html` matches the frontend resource model.",
5878
+ "- Confirm `audit/typecheck.md` passed or every failure is triaged.",
5879
+ "- Confirm `go-no-go.md` matches the team decision before purchase or rollout.",
5880
+ "",
5881
+ "## Next Review Step",
5882
+ "",
5883
+ input.status === "go" ? "Add the generated GitHub workflow, run the first PR with Forge comments enabled, then decide whether to commit generated output." : input.status === "conditional-go" ? "Review warnings with the frontend owner before committing generated output or widening scope." : "Do not roll out Forge on this schema until blockers are fixed or explicitly accepted by the frontend owner.",
5884
+ ""
5885
+ ].join("\n");
5886
+ }
5887
+ function formatInlineList(values) {
5888
+ return values && values.length > 0 ? values.map((value) => `\`${value}\``).join(", ") : "none";
5889
+ }
5652
5890
 
5653
5891
  // src/commands/inspect.command.ts
5654
5892
  function registerInspectCommand(cli) {
package/package.json CHANGED
@@ -1,51 +1,51 @@
1
- {
2
- "name": "@archora/forge-cli",
3
- "version": "1.2.1",
4
- "description": "CLI for generating typed frontend resource contracts from OpenAPI contracts.",
5
- "license": "SEE LICENSE IN LICENSE",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/archora-dev/archora-forge.git"
9
- },
10
- "bugs": {
11
- "url": "https://github.com/archora-dev/archora-forge/issues"
12
- },
13
- "homepage": "https://github.com/archora-dev/archora-forge#readme",
14
- "type": "module",
15
- "main": "./dist/index.js",
16
- "types": "./dist/index.d.ts",
17
- "bin": {
18
- "archora-forge": "dist/index.js"
19
- },
20
- "exports": {
21
- ".": {
22
- "types": "./dist/index.d.ts",
23
- "import": "./dist/index.js"
24
- }
25
- },
26
- "files": [
27
- "dist"
28
- ],
29
- "publishConfig": {
30
- "access": "public"
31
- },
32
- "scripts": {
33
- "build": "tsup",
34
- "dev": "tsup --watch",
35
- "typecheck": "tsc --noEmit"
36
- },
37
- "dependencies": {
38
- "cac": "^6.7.14",
39
- "jiti": "^2.7.0",
40
- "picocolors": "^1.1.1",
41
- "prettier": "^3.5.3",
42
- "yaml": "^2.8.4"
43
- },
44
- "devDependencies": {
45
- "@archora/forge-config": "workspace:*",
46
- "@archora/forge-core": "workspace:*",
47
- "@types/node": "^22.15.17",
48
- "tsup": "^8.4.0",
49
- "typescript": "^5.8.3"
50
- }
51
- }
1
+ {
2
+ "name": "@archora/forge-cli",
3
+ "version": "1.3.0",
4
+ "description": "CLI for generating typed frontend resource contracts from OpenAPI contracts.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/archora-dev/archora-forge.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/archora-dev/archora-forge/issues"
12
+ },
13
+ "homepage": "https://github.com/archora-dev/archora-forge#readme",
14
+ "type": "module",
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "bin": {
18
+ "archora-forge": "dist/index.js"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "dependencies": {
33
+ "cac": "^6.7.14",
34
+ "jiti": "^2.7.0",
35
+ "picocolors": "^1.1.1",
36
+ "prettier": "^3.5.3",
37
+ "yaml": "^2.8.4"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.15.17",
41
+ "tsup": "^8.4.0",
42
+ "typescript": "^5.8.3",
43
+ "@archora/forge-config": "1.3.0",
44
+ "@archora/forge-core": "1.3.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "dev": "tsup --watch",
49
+ "typecheck": "tsc --noEmit"
50
+ }
51
+ }