Skip to content

feat: [kit] Add Forge for Freelance Services Agreement & Invoice Generation [mission-possible] .#152

Merged
akshatvirmani merged 24 commits into
Lamatic:mainfrom
cyber-turtle:feat/forge
Jun 5, 2026
Merged

feat: [kit] Add Forge for Freelance Services Agreement & Invoice Generation [mission-possible] .#152
akshatvirmani merged 24 commits into
Lamatic:mainfrom
cyber-turtle:feat/forge

Conversation

@cyber-turtle
Copy link
Copy Markdown
Contributor

@cyber-turtle cyber-turtle commented May 1, 2026

What This Kit Does

Forge is a 4-flow autonomous freelance documentation agent designed to protect freelancers by automating professional contracts and invoices in under 60 seconds.

Users submit project details, and the agent triggers a multi-step AI pipeline: first, it performs a Pricing Analysis to suggest market-calibrated rates; second, it runs a Governing Law Tradeoff to recommend the best jurisdiction based on the freelancer's specific concerns (IP, payment, or disputes); and finally, it synthesizes a full 13-section Services Agreement and a matching Professional Invoice.

Both documents are rendered in a premium glassmorphic interface, support digital signatures, and can be exported as perfectly paginated, multi-page PDFs.

Providers & Prerequisites

  • Lamatic.ai: Handles orchestration and the 4-step AI pipeline.
  • OpenRouter / LLM Providers: API keys configured in Lamatic's generic credentials panel (Forge is optimized for Gemini 1.5 Pro and GPT-4o).
  • Vercel: Recommended for deployment (configured for the /apps directory).

How to Run Locally

  1. cd kits/forge/apps
  2. npm install
  3. cp .env.example .env.local and fill in your 4 Lamatic Flow IDs and API Key
  4. npm run dev

Live Preview

image

Live App: https://forge-wheat-one.vercel.app/

Lamatic Flows

  • Pricing Flow ID: 8738b556-9a25-41e9-9233-03091e4e3759
  • Tradeoff Flow ID: 64a8c279-ef27-4632-a5ec-9ec3f752674e
  • Contract Flow ID: 6e5e8e78-c89b-449e-b9ef-6090e72f0564
  • Invoice Flow ID: 5788478d-6490-4107-afb9-470086c8a77d

PR Checklist

1. Select Contribution Type

  • Kit (kits/<category>/<kit-name>/)
  • Bundle (bundles/<bundle-name>/)
  • Template (templates/<template-name>/)

2. General Requirements

  • PR is for one project only (no unrelated changes)
  • No secrets, API keys, or real credentials are committed
  • Folder name uses kebab-case and matches the flow ID
  • All changes are documented in README.md (purpose, setup, usage)

3. File Structure (Check what applies)

  • config.json present with valid metadata (name, description, tags, steps, author, env keys)
  • All flows in flows/<flow-name>/ (where applicable) include:
    • config.json (Lamatic flow export)
    • inputs.json
    • meta.json
    • README.md
  • .env.example with placeholder values only (kits only)
  • No hand‑edited flow config.json node graphs (changes via Lamatic Studio export)

4. Validation

  • npm install && npm run dev works locally (kits: UI runs; bundles/templates: flows are valid)
  • PR title is clear (e.g., [kit] Add <name> for <use case>)
  • GitHub Actions workflows pass (all checks are green)
  • All CodeRabbit or other PR review comments are addressed and resolved
  • No unrelated files or projects are modified

Files Added - Complete Inventory

Configuration & Build Files (8 files)

  • lamatic.config.ts - Kit metadata, 4-step flow IDs, links to demo/GitHub/deployment
  • package.json - Next.js 14.2.5, Lamatic, Tailwind, react-signature-canvas, react-markdown
  • tsconfig.json - TypeScript strict mode, ESNext, @/* path alias
  • next.config.mjs - Unoptimized images, ignored TypeScript build errors
  • tailwind.config.js - Custom colors, aurora/galaxy animations, wizard/document sizing
  • postcss.config.js - Tailwind + Autoprefixer
  • .env.example - Lamatic endpoint, project ID, API key, 4 flow IDs
  • .gitignore - Node/Next.js/Vercel ignores

Documentation (2 files)

  • README.md (216 lines) - End-to-end setup, flow inputs/outputs, tech stack, deployment
  • agent.md (120 lines) - Agent purpose, flows, guardrails, environment variables, failure modes

Root Layout & Pages (2 files)

  • app/layout.tsx - Root layout with Nav, metadata, global styles
  • app/page.tsx - Homepage with hero, GalaxyButton CTA, 3 feature cards

Multi-Step Wizard (2 files)

  • app/new/page.tsx - 4-step wizard container with StepIndicator and AuroraBackground
  • components/wizard/StepIndicator.tsx - Progress tracker with checkmarks and animated connectors

Wizard Steps (4 components)

  • Step1ProjectDetails.tsx (405 lines) - Collects freelancer/client/project metadata, deliverables, timeline, payment config
  • Step2Pricing.tsx (273 lines) - Fetches AI pricing, editable line items, recalculates totals, displays justification
  • Step3GoverningLaw.tsx (220 lines) - Loads 3 governing law options, auto-selects recommended, shows pros/cons
  • Step4Generate.tsx (211 lines) - Generates contract & invoice in parallel, validates, persists, navigates to preview

Preview Pages (2 files)

  • app/preview/contract/page.tsx (130 lines) - Contract preview with sign/export/navigation controls
  • app/preview/invoice/page.tsx (130 lines) - Invoice preview with sign/export/back-to-contract controls

Document Renderers (2 components)

  • ContractDocument.tsx (98 lines) - Renders 13-section contract with formatted date, signature blocks
  • InvoiceDocument.tsx (186 lines) - Renders invoice header, freelancer/client info, line items table, totals, signature area

UI Components (6 files)

  • AuroraBackground.tsx (77 lines) - Mouse-tracking animated background with decorative orbs + noise overlay
  • GalaxyButton.tsx (117 lines) - CTA button with deterministic starfield animation, optional icon/arrow
  • Nav.tsx (21 lines) - Sticky header with Forge logo/text link to home
  • ErrorState.tsx (71 lines) - Error display with optional retry + home button
  • SignatureCanvas.tsx (129 lines) - Signature modal with baseline guide, clear/confirm controls, PNG export
  • ExportButton.tsx (63 lines) - Print-to-PDF trigger via window.print(), tracks loading/done state

Styling (1 file)

  • app/globals.css (520 lines) - Theme variables, glassmorphism components (liquid-glass, glass-panel), animations (aurora, galaxy, shimmer), comprehensive A4 print media rules

Utilities & Helpers (5 files)

  • lib/types.ts (102 lines) - TypeScript interfaces: ForgeSession, ProjectDetails, PricingResult, GoverningLawOption, InvoiceData
  • lib/storage.ts (18 lines) - Browser localStorage: getSession(), updateSession(patch), clearSession()
  • lib/utils.ts (6 lines) - cn() utility combining clsx + Tailwind merge
  • lib/lamatic-client.ts (27 lines) - Lazy singleton Lamatic client with error handling
  • lib/lamatic.ts (20 lines) - callFlow() helper for POST to /api/flow with error parsing

Backend (2 files)

  • app/api/flow/route.ts (101 lines) - POST handler: validates flowId, executes GraphQL to Lamatic, returns parsed response
  • actions/orchestrate.ts (49 lines) - Server action runForgeFlow() with error → user-facing message mapping

Configuration Object (1 file)

  • orchestrate.js (92 lines) - exports config with API endpoint/key, 4 flows with inputSchemas

Flow Definitions (4 files)

  • flows/forge-pricing.ts (205 lines) - Step 1→2: Market-intelligence engine
  • flows/forge-tradeoff.ts (210 lines) - Step 2→3: Legal-intelligence engine
  • flows/forge-contract.ts (211 lines) - Step 3→4: Document-generation engine (13-section contract)
  • flows/forge-invoice.ts (212 lines) - Step 3→4: Invoicing engine (professional invoice)

LLM Model Configs (4 files)

  • model-configs/forge-pricing_llmnode-pricing_generative-model-name.ts - Empty generativeModelName placeholder
  • model-configs/forge-tradeoff_llmnode-tradeoff_generative-model-name.ts - Empty generativeModelName placeholder
  • model-configs/forge-contract_llmnode-contract_generative-model-name.ts - Empty generativeModelName placeholder
  • model-configs/forge-invoice_llmnode-invoice_generative-model-name.ts - Empty generativeModelName placeholder

LLM System Prompts (4 files)

  • prompts/forge-pricing_llmnode-pricing_system_0.md (39 lines) - Pricing analyst: experience tiers, regional rules, market context, raw JSON output
  • prompts/forge-tradeoff_llmnode-tradeoff_system_0.md (10 lines) - Governing law advisor: 3 options with pros/cons array, recommended flag, raw JSON output
  • prompts/forge-contract_llmnode-contract_system_0.md (53 lines) - Contract drafter: 13-section structure (parties, scope, timeline, payment, IP, confidentiality, revisions, termination, governing law, disputes), raw JSON
  • prompts/forge-invoice_llmnode-invoice_system_0.md (48 lines) - Invoice formatter: header, parties, line items (unchanged), totals, payment instructions (rewritten), notes, raw JSON

LLM User Prompts (4 files)

  • prompts/forge-pricing_llmnode-pricing_user_1.md (9 lines) - Pricing context template: field, experience, deliverables, currency, countries
  • prompts/forge-tradeoff_llmnode-tradeoff_user_1.md (10 lines) - Tradeoff context template: parties, project, payment, concern
  • prompts/forge-contract_llmnode-contract_user_1.md (17 lines) - Contract context template: parties, scope, timeline, payment, governing law
  • prompts/forge-invoice_llmnode-invoice_user_1.md (17 lines) - Invoice context template: parties, dates, items, currency, totals

Constitution (1 file)

  • constitutions/default.md (16 lines) - Guardrails: legal disclaimer, market-based pricing, contract completeness, privacy/confidentiality, formatting

Lamatic Flow Architecture & Node Types

Unified Node Pattern (All 4 Flows)

Each flow uses 3 core node types wired in sequence:

  1. triggerNode (type: triggerNode)

    • Entry point: receives real-time API request payload
    • Defines advance_schema describing required input fields
    • Routes payload to next stage (LLM node)
  2. LLMNode (type: dynamicNode + generativeModel)

    • Processes trigger payload via configured LLM
    • Loads system prompt + user prompt template
    • Uses generativeModelName input to select model
    • Returns structured JSON-formatted output
  3. responseNode (type: responseNode)

    • Maps LLM output to final response field
    • Returns structured JSON to caller

4-Step Sequential Pipeline

Step 1 → Step 2: Pricing Flow

  • Input: Project details (field, experience, deliverables, currency, countries)
  • Process: LLM analyzes as market analyst → calculates experience-based tier rates + regional adjustments
  • Output: JSON-formatted pricing with per-item rates, quantities, amounts, total_amount, market context justification
  • Node Path: triggerNode → LLMNode_pricing (system/user prompts) → graphqlResponseNode_004 (pricing field)

Step 2 → Step 3: Tradeoff Flow

  • Input: Pricing context + parties info + freelancer's primary concern
  • Process: LLM acts as cross-border legal advisor → weighs 3 jurisdictions against freelancer priority
  • Output: JSON array of 3 governing law options with labels, rationale, pros/cons arrays, recommended flag
  • Node Path: triggerNode → LLMNode_tradeoff (system/user prompts) → graphqlResponseNode_001 (options field)

Step 3 → Step 4 (Parallel): Contract Flow

  • Input: All prior data + chosen governing law
  • Process: LLM drafts 13-section agreement with parties, scope, timeline, payment, IP, confidentiality, revisions, late payment, termination, governing law, disputes, signatures
  • Output: JSON object {section_id: {heading, body}, ...} for render-ready contract
  • Node Path: triggerNode → LLMNode_contract (system/user prompts) → graphqlResponseNode_002 (contract field)

Step 4 (Parallel): Invoice Flow

  • Input: All prior data (no governing law needed)
  • Process: LLM formats as invoice specialist → structures header, parties, line items (unchanged), totals, rewritten payment instructions, notes
  • Output: JSON-formatted InvoiceData with freelancer/client details, line_items array, totals, payment_instructions, notes
  • Node Path: triggerNode → LLMNode_invoice (system/user prompts) → graphqlResponseNode_003 (invoice field)

Flow Dependency Graph

ProjectDetails (Step 1)
    ↓
[Pricing Flow] → pricing JSON
    ↓
ProjectDetails + Pricing (Step 2)
    ↓
[Tradeoff Flow] → governing_law options JSON
    ↓
ProjectDetails + Pricing + GovernLaw (Step 3)
    ↓
[Contract Flow] ──┐ (parallel)
[Invoice Flow] ──┘
    ↓
contract JSON + invoice JSON (Step 4)
    ↓
Preview/Sign/Export → PDFs

Key Design Principles

  • JSON-in, JSON-out: All flows return LLM output as raw JSON strings; client-side parsing handles validation
  • Context accumulation: Each subsequent flow inherits all prior step outputs (project → pricing → governing law → documents)
  • Parallel generation: Contract + Invoice flows execute concurrently to reduce Step 4 latency
  • Prompt-driven templates: System/user prompt pairs are stored separately and composed at runtime via LLM node configuration
  • No fallback logic: Flows rely entirely on configured generative models; missing model config → empty response
  • Legal disclaimer: Constitution emphasizes LLM output is advisory only, not professional legal advice

Copilot AI review requested due to automatic review settings May 1, 2026 19:43
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 1, 2026

Review Change Stack

Walkthrough

The PR introduces Forge, a complete Next.js/Lamatic kit automating cross-border freelance contract and invoice generation. It spans a 4-step guided wizard (project details → AI pricing → governing-law tradeoff → document generation), session-based state persistence, API integration for flow execution, and document preview/signature/export functionality.

Changes

Forge Kit – AI-Powered Cross-Border Contract & Invoice Wizard

Layer / File(s) Summary
Kit Overview and Configuration
kits/forge/README.md, kits/forge/agent.md, kits/forge/lamatic.config.ts
Product documentation, flow descriptions, guardrails, tech stack, and kit metadata establishing the purpose and integration points.
Data Model and Session Storage
kits/forge/apps/lib/types.ts, kits/forge/apps/lib/storage.ts, kits/forge/apps/lib/utils.ts
TypeScript types for the multi-step workflow and localStorage utilities that persist form progress across wizard steps.
Lamatic Flow Configurations
kits/forge/flows/forge-pricing.ts, kits/forge/flows/forge-tradeoff.ts, kits/forge/flows/forge-contract.ts, kits/forge/flows/forge-invoice.ts
Four Lamatic workflows orchestrating AI-driven pricing analysis, governing-law recommendations, contract generation, and invoice formatting.
Prompt Templates and Model Configurations
kits/forge/prompts/*, kits/forge/model-configs/*, kits/forge/constitutions/default.md
System and user prompts guiding LLM behavior for each flow stage, plus model config placeholders and assistant constitution.
API Gateway and Flow Execution
kits/forge/apps/app/api/flow/route.ts, kits/forge/apps/lib/lamatic-client.ts, kits/forge/apps/lib/lamatic.ts, kits/forge/apps/actions/orchestrate.ts
Server-side integration: Next.js API route validating flow requests, Lamatic client singleton, client-side callFlow helper, and server action for orchestrated execution with error mapping.
Core App Layout and Pages
kits/forge/apps/app/layout.tsx, kits/forge/apps/app/page.tsx, kits/forge/apps/app/new/page.tsx
Root layout with metadata and navigation, homepage with hero/features/footer, and wizard page with step indicator and conditional rendering.
Four-Step Wizard Components
kits/forge/apps/components/wizard/Step*.tsx, kits/forge/apps/components/wizard/StepIndicator.tsx
Client components collecting project details, fetching/editing pricing, loading and selecting governing law, and generating contract/invoice concurrently with navigation to preview.
Document Preview, Signature, and Export
kits/forge/apps/components/preview/*, kits/forge/apps/app/preview/contract/page.tsx, kits/forge/apps/app/preview/invoice/page.tsx
Components rendering contract and invoice previews, capturing handwritten signatures via canvas, and exporting documents as PDF via browser print-to-PDF.
Visual Components and Styling System
kits/forge/apps/components/AuroraBackground.tsx, kits/forge/apps/components/GalaxyButton.tsx, kits/forge/apps/components/Nav.tsx, kits/forge/apps/app/globals.css, kits/forge/apps/tailwind.config.js
Animated background orbs, deterministic starfield button, sticky nav, global theme variables, glassmorphism effects, and comprehensive print CSS for A4 output.
Project Configuration and Build Setup
kits/forge/apps/package.json, kits/forge/apps/tsconfig.json, kits/forge/apps/postcss.config.js, kits/forge/apps/next.config.mjs, kits/forge/apps/orchestrate.js, kits/forge/apps/.env.example, kits/forge/apps/.gitignore
Full Next.js project scaffolding, dependency manifest, TypeScript and build configuration, Tailwind/PostCSS setup, environment variable templates, and gitignore rules.

Suggested reviewers

  • amanintech
  • d-pamneja
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description thoroughly documents the kit's purpose, provides setup instructions, includes live preview links, lists Lamatic Flow IDs, and confirms completion of all checklist requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The pull request title clearly identifies the main change: adding a Forge kit for freelance services agreement and invoice generation, with specific context tags included.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Forge kit to AgentKit: a 4-step Lamatic + Next.js wizard that collects project details, runs pricing + governing-law analysis, and generates a services agreement and invoice with signature + export UI.

Changes:

  • Added 4 Lamatic flow definitions (pricing, tradeoff, contract, invoice) with prompts, constitutions, and model-config placeholders.
  • Added a Next.js (App Router) frontend implementing the 4-step wizard, previews, signature capture, and export.
  • Added kit metadata/docs (lamatic.config.ts, agent.md, README.md) and local env scaffolding.

Reviewed changes

Copilot reviewed 54 out of 58 changed files in this pull request and generated 23 comments.

Show a summary per file
File Description
kits/forge/prompts/forge-tradeoff_llmnode-tradeoff_user_1.md Tradeoff flow user prompt template.
kits/forge/prompts/forge-tradeoff_llmnode-tradeoff_system_0.md Tradeoff flow system prompt + JSON output constraints.
kits/forge/prompts/forge-pricing_llmnode-pricing_user_1.md Pricing flow user prompt template.
kits/forge/prompts/forge-pricing_llmnode-pricing_system_0.md Pricing flow system prompt + JSON schema expectations.
kits/forge/prompts/forge-invoice_llmnode-invoice_user_1.md Invoice flow user prompt template.
kits/forge/prompts/forge-invoice_llmnode-invoice_system_0.md Invoice flow system prompt + strict JSON rules.
kits/forge/prompts/forge-contract_llmnode-contract_user_1.md Contract flow user prompt template.
kits/forge/prompts/forge-contract_llmnode-contract_system_0.md Contract flow system prompt describing 13-section contract JSON output.
kits/forge/model-configs/forge-tradeoff_llmnode-tradeoff_generative-model-name.ts Placeholder model-config for tradeoff LLM node.
kits/forge/model-configs/forge-pricing_llmnode-pricing_generative-model-name.ts Placeholder model-config for pricing LLM node.
kits/forge/model-configs/forge-invoice_llmnode-invoice_generative-model-name.ts Placeholder model-config for invoice LLM node.
kits/forge/model-configs/forge-contract_llmnode-contract_generative-model-name.ts Placeholder model-config for contract LLM node.
kits/forge/lamatic.config.ts Kit metadata (name/steps/env keys/links).
kits/forge/flows/forge-tradeoff.ts Lamatic flow graph for governing-law options.
kits/forge/flows/forge-pricing.ts Lamatic flow graph for pricing analysis.
kits/forge/flows/forge-invoice.ts Lamatic flow graph for invoice generation.
kits/forge/flows/forge-contract.ts Lamatic flow graph for contract generation.
kits/forge/constitutions/default.md Kit constitution/guardrails for agent behavior.
kits/forge/apps/tsconfig.json Next.js app TS config.
kits/forge/apps/tailwind.config.js Tailwind theme + content config.
kits/forge/apps/public/forge2.svg App logo asset.
kits/forge/apps/postcss.config.js PostCSS config for Tailwind.
kits/forge/apps/package.json App dependencies/scripts.
kits/forge/apps/orchestrate.js Centralized env-driven flow configuration.
kits/forge/apps/next.config.mjs Next.js config (TS/build/image settings).
kits/forge/apps/lib/utils.ts Tailwind/className helper (cn).
kits/forge/apps/lib/types.ts Shared session + document data types.
kits/forge/apps/lib/storage.ts localStorage session persistence helpers.
kits/forge/apps/lib/lamatic.ts Client helper to call /api/flow.
kits/forge/apps/lib/lamatic-client.ts Server-side Lamatic SDK client init.
kits/forge/apps/components/wizard/StepIndicator.tsx Wizard step indicator UI.
kits/forge/apps/components/wizard/Step4Generate.tsx Step 4: generate contract/invoice and store in session.
kits/forge/apps/components/wizard/Step3GoverningLaw.tsx Step 3: call tradeoff flow + select option.
kits/forge/apps/components/wizard/Step2Pricing.tsx Step 2: call pricing flow + edit line items.
kits/forge/apps/components/wizard/Step1ProjectDetails.tsx Step 1: project details form persisted to session.
kits/forge/apps/components/preview/SignatureCanvas.tsx Signature capture modal/canvas.
kits/forge/apps/components/preview/InvoiceDocument.tsx Invoice rendering component for preview/print.
kits/forge/apps/components/preview/ExportButton.tsx Export action UI (currently print-based).
kits/forge/apps/components/preview/ContractDocument.tsx Contract rendering component for preview/print.
kits/forge/apps/components/Nav.tsx Top navigation with logo.
kits/forge/apps/components/GalaxyButton.tsx Primary CTA button component.
kits/forge/apps/components/ErrorState.tsx Standard error + retry UI.
kits/forge/apps/components/AuroraBackground.tsx Animated background wrapper.
kits/forge/apps/app/preview/invoice/page.tsx Invoice preview page with signing/export actions.
kits/forge/apps/app/preview/contract/page.tsx Contract preview page with signing/export actions.
kits/forge/apps/app/page.tsx Landing page for the kit app.
kits/forge/apps/app/new/page.tsx Wizard container page.
kits/forge/apps/app/layout.tsx Root layout + metadata + nav.
kits/forge/apps/app/icon.svg App icon asset.
kits/forge/apps/app/globals.css Global styling, components, and print CSS.
kits/forge/apps/app/api/flow/route.ts Server proxy route that executes Lamatic workflows via GraphQL.
kits/forge/apps/actions/orchestrate.ts Server action wrapper around Lamatic SDK execution.
kits/forge/apps/.gitignore App-level ignore rules.
kits/forge/apps/.env.example Environment variable template.
kits/forge/agent.md Kit/agent overview and operational notes.
kits/forge/README.md Kit documentation: setup, flows, and usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread kits/forge/apps/app/api/flow/route.ts
Comment thread kits/forge/prompts/forge-invoice_llmnode-invoice_system_0.md
Comment thread kits/forge/flows/forge-contract.ts
Comment thread kits/forge/flows/forge-invoice.ts
Comment thread kits/forge/lamatic.config.ts
Comment thread kits/forge/apps/components/wizard/Step4Generate.tsx
Comment thread kits/forge/apps/lib/lamatic-client.ts
Comment thread kits/forge/apps/components/AuroraBackground.tsx
Comment thread kits/forge/apps/next.config.mjs
Comment thread kits/forge/apps/components/wizard/Step4Generate.tsx Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 54 out of 58 changed files in this pull request and generated 16 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread kits/forge/apps/app/api/flow/route.ts
Comment thread kits/forge/apps/app/api/flow/route.ts Outdated
Comment thread kits/forge/apps/components/wizard/Step4Generate.tsx Outdated
Comment thread kits/forge/apps/components/wizard/Step4Generate.tsx Outdated
Comment thread kits/forge/apps/components/wizard/Step4Generate.tsx
Comment thread kits/forge/flows/forge-tradeoff.ts
Comment thread kits/forge/flows/forge-contract.ts
Comment thread kits/forge/flows/forge-invoice.ts
Comment thread kits/forge/README.md Outdated
Comment thread kits/forge/flows/forge-pricing.ts Outdated
@akshatvirmani
Copy link
Copy Markdown
Contributor

/validate

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

📡 Running Studio validation — results will appear here shortly.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

Studio Runtime Validation (Phase 2)

Studio validation passed. The kit loaded successfully in Lamatic Studio.

This PR is ready for final review and merge.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@kits/forge/apps/app/api/flow/route.ts`:
- Around line 20-23: The route currently reads client-exposed env names
(endpoint/projectId) from NEXT_PUBLIC_LAMATIC_ENDPOINT and
NEXT_PUBLIC_LAMATIC_PROJECT_ID; change it to use the server-only names
LAMATIC_API_URL and LAMATIC_PROJECT_ID and keep LAMATIC_API_KEY for the secret
so the route uses server-only credentials (update the variables endpoint ->
LAMATIC_API_URL, projectId -> LAMATIC_PROJECT_ID, and keep apiKey ->
LAMATIC_API_KEY). Also ensure these names match the initialization in
kits/**/lib/lamatic-client.ts so the Lamatic client (init code in
lamatic-client.ts) and this route use the same env variable names.
- Around line 17-19: The POST handler's call to await req.json() is unguarded
and can throw before your error-path runs; move the body parsing into the
existing try/catch (or add one) so that the destructuring const { flowId,
payload } = await req.json() happens inside the guarded block, catch JSON parse
errors and return/throw the same structured error response you use downstream
(or call the existing error handler). Update the POST function to ensure any
parsing error for req.json is logged/handled consistently with the handler's
other failures.
- Around line 50-58: The outbound fetch call in route.ts that posts graphqlQuery
to endpoint (using endpoint, apiKey, projectId) needs an AbortController and a
timeout to avoid hangs; update the POST logic that calls fetch(...) to create an
AbortController, pass controller.signal to fetch, start a timer (e.g.,
setTimeout) that calls controller.abort() after a configurable timeout, and
clear the timer when the request finishes; also catch abort/errors from fetch
and map them to an appropriate response (timeout vs other error) so the route
doesn't remain pinned waiting on the upstream.

In `@kits/forge/apps/app/globals.css`:
- Around line 4-6: globals.css currently uses Tailwind v3 layer directives
(`@tailwind` base; `@tailwind` components; `@tailwind` utilities;) while package.json
pins tailwindcss to ^3.4.1; update to Tailwind v4+ by bumping the tailwindcss
dependency in package.json to a v4+ release and migrate globals.css to the v4
CSS entry format (replace the `@tailwind` layer directives with the new v4
imports/configured CSS pattern per Tailwind v4 docs), or if you intend to stay
on v3, update the kit guidance instead; ensure you update any Tailwind-related
build/postcss config to match the v4 migration steps so the project builds
correctly.

In `@kits/forge/apps/app/preview/invoice/page.tsx`:
- Around line 19-32: The invoice number is currently generated with
useState(`INV-${Date.now()}`) causing a new number on every mount; instead, on
mount read session.invoice_number from getSession() and if present initialize
invoiceNumber from that value, otherwise generate a new ID, set
session.invoice_number to it and persist the session (via your session
persistence helper—e.g., saveSession/setSession) so subsequent mounts use the
same number; update references to invoiceNumber and where you call
setInvoice/getSession so the component uses the persisted session.invoice_number
rather than always creating `INV-${Date.now()}`.

In `@kits/forge/apps/components/AuroraBackground.tsx`:
- Around line 22-38: The effect is being torn down and rebuilt on every
mousemove because useEffect depends on mousePos; stop deriving the animation
target from state and instead store the target in a ref (e.g., create
targetPosRef and update it inside your mousemove handler instead of via
setMousePos so children aren’t re-rendered), then change the rAF loop useEffect
(the one that defines updateOrb, uses currentPos and requestRef and queries
"interactive-orb") to run once on mount (empty dependency array) and read from
targetPosRef inside updateOrb; start the rAF once (requestRef.current =
requestAnimationFrame(updateOrb)) and cancel on unmount to prevent repeated
teardown/recreate of updateOrb.

In `@kits/forge/apps/components/GalaxyButton.tsx`:
- Around line 53-116: The current ButtonContent creates a real <button> and then
nests it inside <Link>, which is invalid; refactor so the interactive element
matches the presence of href: extract the visual inner spans (the backdrop,
stars, galaxy-text, etc.) into a pure presentational fragment (e.g.,
renderInnerContent or move the JSX out of the <button> wrapper) and then
conditionally render either a <button> (when href is falsy) or a <Link> anchor
(when href is truthy) as the outer interactive element; when rendering <Link>
pass the same classNames, handle clicks via onClick, set aria-disabled or
tabIndex and apply the disabled styling instead of nesting a <button>, and keep
the existing disabled prop logic for the <button> branch (reference
ButtonContent, Link, onClick, disabled, showArrow, staticStars, orbitingStars).

In `@kits/forge/apps/components/preview/SignatureCanvas.tsx`:
- Around line 41-60: The baseline-drawing logic is duplicated in handleClear and
the mount useEffect; extract it into a single helper drawBaseline(canvas) and
call that helper from both places. Implement drawBaseline to accept the
HTMLCanvasElement (from sigRef.current?.getCanvas()), obtain its 2D context, set
strokeStyle/lineWidth/setLineDash, and draw the same guide line, then replace
the inline blocks in handleClear and the useEffect with a call to
drawBaseline(canvas); ensure sigRef and getCanvas usage remains unchanged and
guard for null canvas/context before calling the helper.
- Around line 17-66: The dashed baseline is being drawn onto the signing canvas
(in useEffect and in handleClear via
sigRef.current?.getCanvas().getContext("2d")), so it ends up baked into the PNG
produced by handleConfirm (sigRef.current?.toDataURL). Remove the two blocks
that call getContext and draw the dashed line (the entire baseline draw in
useEffect and the redraw in handleClear) and instead render the dashed baseline
as a separate layer (e.g., a CSS background or an absolutely positioned div
overlay behind/above the <SignatureCanvas/> element) so the canvas (sigRef) only
contains user strokes and handleConfirm returns a clean signature image.

In `@kits/forge/apps/components/wizard/Step1ProjectDetails.tsx`:
- Around line 52-56: handleSubmit currently calls updateSession with the entire
form (including freelancer_payment_details and emails) which ends up persisted
in localStorage; change it to avoid storing sensitive payment info and use
ephemeral sessionStorage for the wizard lifetime: before calling updateSession,
create a sanitized copy of form that omits freelancer_payment_details and any
unnecessary raw payment/email fields (retain only IDs or masked values required
later), then call updateSession with that sanitized object and ensure
updateSession is using sessionStorage (or provide a new updateEphemeralSession
helper) rather than localStorage; update references to handleSubmit,
updateSession, and form to reflect the sanitized payload.
- Around line 38-65: Step1ProjectDetails currently uses local useState (INITIAL,
set, handleSubmit) and native required attrs; replace this with react-hook-form
+ zod schema for ProjectDetails (define validation rules: email format,
non-negative numeric fields like years, and date ordering) and wire the form via
useForm and zodResolver instead of set/useState; swap plain inputs/selects with
shadcn/ui (and Radix where appropriate) form primitives and controlled
register/Controller usage so validation and UI match repo standards; ensure
submit still calls updateSession({ projectDetails: values }) and onComplete()
after valid submit and restore defaults from getSession into form's
defaultValues.

In `@kits/forge/apps/components/wizard/Step2Pricing.tsx`:
- Around line 47-49: The code assigns parsed into state without validating its
shape, so before calling setPricing verify that parsed has a line_items property
that is an array (i.e. check parsed && Array.isArray(parsed.line_items)); if it
does, call setPricing(parsed), otherwise either set a safe fallback (e.g.
setPricing({...parsed, line_items: []})) or surface the existing error handling
path (set an error state) so the subsequent pricing.line_items.map(...) in the
render cannot crash; reference: pricingRaw, parsed, setPricing, PricingResult
and the usage pricing.line_items.map to locate where to add the guard.

In `@kits/forge/apps/components/wizard/Step3GoverningLaw.tsx`:
- Around line 57-63: The code currently assumes response.result.options parses
to an array; validate that parsed is an array before using it: after computing
optionsRaw and parsed (variables named optionsRaw and parsed in
Step3GoverningLaw), check Array.isArray(parsed) and if not, route to the
existing ErrorState (do not call setOptions, findIndex, setSelected, or rely on
options.map). If it is an array, then call setOptions(parsed) and run the
auto-selection logic (use parsed.findIndex and setSelected as before). Ensure
any JSON.parse errors are also caught and handled by showing ErrorState.

In `@kits/forge/apps/lib/lamatic-client.ts`:
- Around line 9-17: The client initialization in lib/lamatic-client.ts checks
for endpoint and apiKey but doesn't validate projectId; update the
initialization in the function that assigns _client (where new Lamatic({...}) is
called) to throw an Error when config.api.projectId (LAMATIC_PROJECT_ID) is
missing or null so the process fails fast; ensure the error message clearly
names the missing LAMATIC_PROJECT_ID variable and keep the rest of the Lamatic
constructor usage unchanged.

In `@kits/forge/apps/lib/storage.ts`:
- Around line 5-18: The session helpers (getSession, updateSession,
clearSession) must be hardened against non-browser contexts and malformed
localStorage data: in getSession wrap localStorage access and JSON.parse in a
try/catch and return {} on any error; ensure you only access localStorage when
typeof window !== 'undefined' (or wrap the access itself in try/catch) to avoid
server-side crashes; in updateSession wrap the localStorage.setItem call in
try/catch, merge safely with the result of getSession (ensuring it is an object)
before stringifying, and swallow/log errors instead of throwing; in clearSession
similarly guard/removeItem in a try/catch so calls are no-ops outside a browser
or when storage is corrupted.

In `@kits/forge/apps/orchestrate.js`:
- Around line 3-4: The orchestration identifiers (endpoint, projectId, flowId)
are currently read from NEXT_PUBLIC_* env vars and thus leak to the client;
change all references that set endpoint, projectId, and any flowId values in
orchestrate.js to use server-only env vars (e.g., process.env.LAMATIC_ENDPOINT,
process.env.LAMATIC_PROJECT_ID, process.env.LAMATIC_FLOW_ID) instead of
NEXT_PUBLIC_*; ensure these values are only used in server-side code paths
(remove any exports or client-side propagation) and add a fail-fast check in the
initialization logic that throws or logs an error if the required server-only
vars are missing.
- Around line 3-5: The current config silently defaults endpoint, projectId and
apiKey to empty strings (endpoint, projectId, apiKey) which delays errors;
instead validate process.env.NEXT_PUBLIC_LAMATIC_ENDPOINT,
process.env.NEXT_PUBLIC_LAMATIC_PROJECT_ID and process.env.LAMATIC_API_KEY at
module initialization and throw a clear Error (or assert) if any are missing so
the app fails fast on startup; update the code around where
endpoint/projectId/apiKey are set to remove the "?? ''" defaults and add a small
validation block that constructs a helpful message listing the missing env var
names before throwing.

In `@kits/forge/apps/package.json`:
- Around line 13-32: Update kits/forge/apps/package.json by replacing all
non-exact dependency specifiers with exact versions: change "react",
"react-dom", "lamatic", "clsx", "tailwind-merge", "lucide-react",
"react-markdown", and "react-signature-canvas" in the "dependencies" section and
"typescript", "`@types/node`", "`@types/react`", "`@types/react-dom`",
"`@types/react-signature-canvas`", "tailwindcss", "autoprefixer", "postcss", and
"eslint" in "devDependencies" from ranges or "latest" to concrete version
strings (no ^, ~, or "latest"); keep the existing exact entries such as
"eslint-config-next" unchanged. Ensure each entry is a fixed version literal
(e.g., "18.2.0" style) and run a quick install to verify no conflicts.

In `@kits/forge/prompts/forge-invoice_llmnode-invoice_system_0.md`:
- Around line 44-45: The prompt currently contains conflicting directives for
the output schema: one line mandates a fixed keyset including "line_items" while
another allows omitting or leaving "notes" empty, which can cause schema drift;
update the prompt in forge-invoice_llmnode-invoice_system_0.md to remove the
contradictory allowance so the keys are consistent — keep "line_items: use
exactly what is provided. Do not modify amounts." and change the "notes"
instruction to a single clear rule (either "notes: include the freelancer's
notes if provided, otherwise set as empty string" OR "notes: always include the
field, empty string if not provided") so downstream parsers receive a stable
schema.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: a9634588-dcbc-4619-bc77-abdef8867c1f

📥 Commits

Reviewing files that changed from the base of the PR and between 6e60d64 and c1404c5.

⛔ Files ignored due to path filters (4)
  • kits/forge/apps/app/icon.svg is excluded by !**/*.svg
  • kits/forge/apps/package-lock.json is excluded by !**/package-lock.json
  • kits/forge/apps/public/forge2.svg is excluded by !**/*.svg
  • kits/forge/apps/public/screenshot.png is excluded by !**/*.png
📒 Files selected for processing (54)
  • kits/forge/README.md
  • kits/forge/agent.md
  • kits/forge/apps/.env.example
  • kits/forge/apps/.gitignore
  • kits/forge/apps/actions/orchestrate.ts
  • kits/forge/apps/app/api/flow/route.ts
  • kits/forge/apps/app/globals.css
  • kits/forge/apps/app/layout.tsx
  • kits/forge/apps/app/new/page.tsx
  • kits/forge/apps/app/page.tsx
  • kits/forge/apps/app/preview/contract/page.tsx
  • kits/forge/apps/app/preview/invoice/page.tsx
  • kits/forge/apps/components/AuroraBackground.tsx
  • kits/forge/apps/components/ErrorState.tsx
  • kits/forge/apps/components/GalaxyButton.tsx
  • kits/forge/apps/components/Nav.tsx
  • kits/forge/apps/components/preview/ContractDocument.tsx
  • kits/forge/apps/components/preview/ExportButton.tsx
  • kits/forge/apps/components/preview/InvoiceDocument.tsx
  • kits/forge/apps/components/preview/SignatureCanvas.tsx
  • kits/forge/apps/components/wizard/Step1ProjectDetails.tsx
  • kits/forge/apps/components/wizard/Step2Pricing.tsx
  • kits/forge/apps/components/wizard/Step3GoverningLaw.tsx
  • kits/forge/apps/components/wizard/Step4Generate.tsx
  • kits/forge/apps/components/wizard/StepIndicator.tsx
  • kits/forge/apps/lib/lamatic-client.ts
  • kits/forge/apps/lib/lamatic.ts
  • kits/forge/apps/lib/storage.ts
  • kits/forge/apps/lib/types.ts
  • kits/forge/apps/lib/utils.ts
  • kits/forge/apps/next.config.mjs
  • kits/forge/apps/orchestrate.js
  • kits/forge/apps/package.json
  • kits/forge/apps/postcss.config.js
  • kits/forge/apps/tailwind.config.js
  • kits/forge/apps/tsconfig.json
  • kits/forge/constitutions/default.md
  • kits/forge/flows/forge-contract.ts
  • kits/forge/flows/forge-invoice.ts
  • kits/forge/flows/forge-pricing.ts
  • kits/forge/flows/forge-tradeoff.ts
  • kits/forge/lamatic.config.ts
  • kits/forge/model-configs/forge-contract_llmnode-contract_generative-model-name.ts
  • kits/forge/model-configs/forge-invoice_llmnode-invoice_generative-model-name.ts
  • kits/forge/model-configs/forge-pricing_llmnode-pricing_generative-model-name.ts
  • kits/forge/model-configs/forge-tradeoff_llmnode-tradeoff_generative-model-name.ts
  • kits/forge/prompts/forge-contract_llmnode-contract_system_0.md
  • kits/forge/prompts/forge-contract_llmnode-contract_user_1.md
  • kits/forge/prompts/forge-invoice_llmnode-invoice_system_0.md
  • kits/forge/prompts/forge-invoice_llmnode-invoice_user_1.md
  • kits/forge/prompts/forge-pricing_llmnode-pricing_system_0.md
  • kits/forge/prompts/forge-pricing_llmnode-pricing_user_1.md
  • kits/forge/prompts/forge-tradeoff_llmnode-tradeoff_system_0.md
  • kits/forge/prompts/forge-tradeoff_llmnode-tradeoff_user_1.md

Comment on lines +17 to +19
export async function POST(req: NextRequest) {
const { flowId, payload } = await req.json();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Mission safeguard: parse request JSON inside guarded error handling.

If body parsing fails at Line 18, it bypasses your downstream error-path logic and returns an unstructured failure.

Proposed patch
 export async function POST(req: NextRequest) {
-  const { flowId, payload } = await req.json();
+  let flowId: unknown;
+  let payload: unknown;
+  try {
+    ({ flowId, payload } = await req.json());
+  } catch {
+    return NextResponse.json({ error: 'Invalid JSON body.' }, { status: 400 });
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/app/api/flow/route.ts` around lines 17 - 19, The POST
handler's call to await req.json() is unguarded and can throw before your
error-path runs; move the body parsing into the existing try/catch (or add one)
so that the destructuring const { flowId, payload } = await req.json() happens
inside the guarded block, catch JSON parse errors and return/throw the same
structured error response you use downstream (or call the existing error
handler). Update the POST function to ensure any parsing error for req.json is
logged/handled consistently with the handler's other failures.

Comment on lines +20 to +23
const endpoint = process.env.NEXT_PUBLIC_LAMATIC_ENDPOINT;
const projectId = process.env.NEXT_PUBLIC_LAMATIC_PROJECT_ID;
const apiKey = process.env.LAMATIC_API_KEY;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mission directive: switch to server-only Lamatic env keys to avoid config drift.

This route depends on NEXT_PUBLIC_LAMATIC_ENDPOINT/NEXT_PUBLIC_LAMATIC_PROJECT_ID at Lines 20-21, which couples a server credential path to client-exposed naming and can break if only server-side keys are configured.

Proposed patch
-  const endpoint = process.env.NEXT_PUBLIC_LAMATIC_ENDPOINT;
-  const projectId = process.env.NEXT_PUBLIC_LAMATIC_PROJECT_ID;
+  const endpoint = process.env.LAMATIC_API_URL;
+  const projectId = process.env.LAMATIC_PROJECT_ID;
   const apiKey = process.env.LAMATIC_API_KEY;
As per coding guidelines, "`kits/**/lib/lamatic-client.ts` should initialize Lamatic with `LAMATIC_API_URL`, `LAMATIC_PROJECT_ID`, and `LAMATIC_API_KEY`."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/app/api/flow/route.ts` around lines 20 - 23, The route
currently reads client-exposed env names (endpoint/projectId) from
NEXT_PUBLIC_LAMATIC_ENDPOINT and NEXT_PUBLIC_LAMATIC_PROJECT_ID; change it to
use the server-only names LAMATIC_API_URL and LAMATIC_PROJECT_ID and keep
LAMATIC_API_KEY for the secret so the route uses server-only credentials (update
the variables endpoint -> LAMATIC_API_URL, projectId -> LAMATIC_PROJECT_ID, and
keep apiKey -> LAMATIC_API_KEY). Also ensure these names match the
initialization in kits/**/lib/lamatic-client.ts so the Lamatic client (init code
in lamatic-client.ts) and this route use the same env variable names.

Comment on lines +50 to +58
const res = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
'x-project-id': projectId,
},
body: JSON.stringify(graphqlQuery),
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mission resilience gap: outbound Lamatic fetch needs a timeout/abort controller.

The external call currently has no timeout. A hung upstream can pin this route until platform hard timeouts.

Proposed patch
   try {
+    const controller = new AbortController();
+    const timeout = setTimeout(() => controller.abort(), 15_000);
     const res = await fetch(endpoint, {
       method: 'POST',
       headers: {
         'Content-Type': 'application/json',
         'Authorization': `Bearer ${apiKey}`,
         'x-project-id': projectId,
       },
       body: JSON.stringify(graphqlQuery),
+      signal: controller.signal,
     });
+    clearTimeout(timeout);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const res = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
'x-project-id': projectId,
},
body: JSON.stringify(graphqlQuery),
});
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 15_000);
const res = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
'x-project-id': projectId,
},
body: JSON.stringify(graphqlQuery),
signal: controller.signal,
});
clearTimeout(timeout);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/app/api/flow/route.ts` around lines 50 - 58, The outbound
fetch call in route.ts that posts graphqlQuery to endpoint (using endpoint,
apiKey, projectId) needs an AbortController and a timeout to avoid hangs; update
the POST logic that calls fetch(...) to create an AbortController, pass
controller.signal to fetch, start a timer (e.g., setTimeout) that calls
controller.abort() after a configurable timeout, and clear the timer when the
request finishes; also catch abort/errors from fetch and map them to an
appropriate response (timeout vs other error) so the route doesn't remain pinned
waiting on the upstream.

Comment on lines +4 to +6
@tailwind base;
@tailwind components;
@tailwind utilities;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the Tailwind version pinned for the Forge kit
fd -t f 'package.json' kits/forge | while read -r f; do
  echo "== $f =="
  jq '{tailwind: (.dependencies.tailwindcss // .devDependencies.tailwindcss)}' "$f"
done

Repository: Lamatic/AgentKit

Length of output: 122


Mission: adjust Tailwind to match the v4+ CSS guideline

kits/forge/apps/app/globals.css uses Tailwind v3 layer directives (@tailwind base/components/utilities), and kits/forge/apps/package.json pins tailwindcss to ^3.4.1 (Tailwind v3), so the current state conflicts with the “kits/**/*.css: Use Tailwind CSS v4+” guideline. Upgrade tailwindcss to v4+ and migrate the directives to Tailwind v4’s CSS form (or, if v3 is intentional, update the guideline/intent for this kit).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/app/globals.css` around lines 4 - 6, globals.css currently
uses Tailwind v3 layer directives (`@tailwind` base; `@tailwind` components;
`@tailwind` utilities;) while package.json pins tailwindcss to ^3.4.1; update to
Tailwind v4+ by bumping the tailwindcss dependency in package.json to a v4+
release and migrate globals.css to the v4 CSS entry format (replace the
`@tailwind` layer directives with the new v4 imports/configured CSS pattern per
Tailwind v4 docs), or if you intend to stay on v3, update the kit guidance
instead; ensure you update any Tailwind-related build/postcss config to match
the v4 migration steps so the project builds correctly.

Comment on lines +19 to +32
const [invoiceNumber] = useState(`INV-${Date.now()}`);
const [showSigModal, setShowSigModal] = useState(false);

useEffect(() => {
const session = getSession();
if (!session.invoice || !session.projectDetails) {
router.push("/new");
return;
}
setInvoice(session.invoice);
setSignature(session.invoice_signature || null);
setProjectTitle(session.projectDetails.project_title);
setCurrency(session.pricing?.currency || session.projectDetails.payment_currency || "USD");
}, [router]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Agent, this invoice number self-destructs on every reload.

INV-${Date.now()} is minted fresh on each mount and never persisted, so a refresh — or bouncing to the contract page and back — re-issues a different number for the same invoice. Two exports won't agree.

Persist it once and reuse:

🧾 Proposed fix — pin the invoice number to the session
-  const [invoiceNumber] = useState(`INV-${Date.now()}`);
+  const [invoiceNumber, setInvoiceNumber] = useState("");
   const [showSigModal, setShowSigModal] = useState(false);

   useEffect(() => {
     const session = getSession();
     if (!session.invoice || !session.projectDetails) {
       router.push("/new");
       return;
     }
+    const existing = session.invoice_number || `INV-${Date.now()}`;
+    if (!session.invoice_number) updateSession({ invoice_number: existing });
+    setInvoiceNumber(existing);
     setInvoice(session.invoice);

Note: this assumes invoice_number is added to the session type — confirm against lib/types.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/app/preview/invoice/page.tsx` around lines 19 - 32, The
invoice number is currently generated with useState(`INV-${Date.now()}`) causing
a new number on every mount; instead, on mount read session.invoice_number from
getSession() and if present initialize invoiceNumber from that value, otherwise
generate a new ID, set session.invoice_number to it and persist the session (via
your session persistence helper—e.g., saveSession/setSession) so subsequent
mounts use the same number; update references to invoiceNumber and where you
call setInvoice/getSession so the component uses the persisted
session.invoice_number rather than always creating `INV-${Date.now()}`.

Comment on lines +5 to +18
export function getSession(): Partial<ForgeSession> {
if (typeof window === 'undefined') return {};
const raw = localStorage.getItem(KEY);
return raw ? JSON.parse(raw) : {};
}

export function updateSession(patch: Partial<ForgeSession>) {
const current = getSession();
localStorage.setItem(KEY, JSON.stringify({ ...current, ...patch }));
}

export function clearSession() {
localStorage.removeItem(KEY);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mission-critical hardening: prevent session utilities from crashing on malformed or server-side storage access.

JSON.parse at Line 8 can throw on corrupted storage, and localStorage access at Lines 13/17 can throw if these functions are called in a non-browser context. This can brick the wizard state path unexpectedly.

Proposed patch
 export function getSession(): Partial<ForgeSession> {
   if (typeof window === 'undefined') return {};
   const raw = localStorage.getItem(KEY);
-  return raw ? JSON.parse(raw) : {};
+  if (!raw) return {};
+  try {
+    return JSON.parse(raw);
+  } catch {
+    localStorage.removeItem(KEY);
+    return {};
+  }
 }
 
 export function updateSession(patch: Partial<ForgeSession>) {
+  if (typeof window === 'undefined') return;
   const current = getSession();
   localStorage.setItem(KEY, JSON.stringify({ ...current, ...patch }));
 }
 
 export function clearSession() {
+  if (typeof window === 'undefined') return;
   localStorage.removeItem(KEY);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function getSession(): Partial<ForgeSession> {
if (typeof window === 'undefined') return {};
const raw = localStorage.getItem(KEY);
return raw ? JSON.parse(raw) : {};
}
export function updateSession(patch: Partial<ForgeSession>) {
const current = getSession();
localStorage.setItem(KEY, JSON.stringify({ ...current, ...patch }));
}
export function clearSession() {
localStorage.removeItem(KEY);
}
export function getSession(): Partial<ForgeSession> {
if (typeof window === 'undefined') return {};
const raw = localStorage.getItem(KEY);
if (!raw) return {};
try {
return JSON.parse(raw);
} catch {
localStorage.removeItem(KEY);
return {};
}
}
export function updateSession(patch: Partial<ForgeSession>) {
if (typeof window === 'undefined') return;
const current = getSession();
localStorage.setItem(KEY, JSON.stringify({ ...current, ...patch }));
}
export function clearSession() {
if (typeof window === 'undefined') return;
localStorage.removeItem(KEY);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/lib/storage.ts` around lines 5 - 18, The session helpers
(getSession, updateSession, clearSession) must be hardened against non-browser
contexts and malformed localStorage data: in getSession wrap localStorage access
and JSON.parse in a try/catch and return {} on any error; ensure you only access
localStorage when typeof window !== 'undefined' (or wrap the access itself in
try/catch) to avoid server-side crashes; in updateSession wrap the
localStorage.setItem call in try/catch, merge safely with the result of
getSession (ensuring it is an object) before stringifying, and swallow/log
errors instead of throwing; in clearSession similarly guard/removeItem in a
try/catch so calls are no-ops outside a browser or when storage is corrupted.

Comment on lines +3 to +4
endpoint: process.env.NEXT_PUBLIC_LAMATIC_ENDPOINT ?? "",
projectId: process.env.NEXT_PUBLIC_LAMATIC_PROJECT_ID ?? "",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Mission-critical: keep orchestration identifiers server-only.

Line 3, Line 4, Line 10, Line 25, Line 47, and Line 70 use NEXT_PUBLIC_* for Lamatic endpoint/project/flow IDs. Those values should not be exposed client-side when they control server-side flow execution.

Suggested hardening diff
 export const config = {
   api: {
-    endpoint: process.env.NEXT_PUBLIC_LAMATIC_ENDPOINT ?? "",
-    projectId: process.env.NEXT_PUBLIC_LAMATIC_PROJECT_ID ?? "",
+    endpoint: process.env.LAMATIC_API_URL ?? "",
+    projectId: process.env.LAMATIC_PROJECT_ID ?? "",
     apiKey: process.env.LAMATIC_API_KEY ?? "",
   },
   flows: {
     pricing: {
       name: "Forge Pricing",
-      workflowId: process.env.NEXT_PUBLIC_FLOW_PRICING ?? "",
+      workflowId: process.env.FLOW_PRICING ?? "",
@@
     tradeoff: {
       name: "Forge Tradeoff",
-      workflowId: process.env.NEXT_PUBLIC_FLOW_TRADEOFF ?? "",
+      workflowId: process.env.FLOW_TRADEOFF ?? "",
@@
     contract: {
       name: "Forge Contract",
-      workflowId: process.env.NEXT_PUBLIC_FLOW_CONTRACT ?? "",
+      workflowId: process.env.FLOW_CONTRACT ?? "",
@@
     invoice: {
       name: "Forge Invoice",
-      workflowId: process.env.NEXT_PUBLIC_FLOW_INVOICE ?? "",
+      workflowId: process.env.FLOW_INVOICE ?? "",

Also applies to: 10-10, 25-25, 47-47, 70-70

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/orchestrate.js` around lines 3 - 4, The orchestration
identifiers (endpoint, projectId, flowId) are currently read from NEXT_PUBLIC_*
env vars and thus leak to the client; change all references that set endpoint,
projectId, and any flowId values in orchestrate.js to use server-only env vars
(e.g., process.env.LAMATIC_ENDPOINT, process.env.LAMATIC_PROJECT_ID,
process.env.LAMATIC_FLOW_ID) instead of NEXT_PUBLIC_*; ensure these values are
only used in server-side code paths (remove any exports or client-side
propagation) and add a fail-fast check in the initialization logic that throws
or logs an error if the required server-only vars are missing.

Comment on lines +3 to +5
endpoint: process.env.NEXT_PUBLIC_LAMATIC_ENDPOINT ?? "",
projectId: process.env.NEXT_PUBLIC_LAMATIC_PROJECT_ID ?? "",
apiKey: process.env.LAMATIC_API_KEY ?? "",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mission directive: fail fast on missing env vars.

Line 3, Line 4, Line 5, Line 10, Line 25, Line 47, and Line 70 default to empty strings. That defers config errors until runtime flow calls and makes failures harder to diagnose.

Suggested fail-fast diff
+const requireEnv = (key) => {
+  const value = process.env[key]
+  if (!value) throw new Error(`Missing required env var: ${key}`)
+  return value
+}
+
 export const config = {
   api: {
-    endpoint: process.env.NEXT_PUBLIC_LAMATIC_ENDPOINT ?? "",
-    projectId: process.env.NEXT_PUBLIC_LAMATIC_PROJECT_ID ?? "",
-    apiKey: process.env.LAMATIC_API_KEY ?? "",
+    endpoint: requireEnv("LAMATIC_API_URL"),
+    projectId: requireEnv("LAMATIC_PROJECT_ID"),
+    apiKey: requireEnv("LAMATIC_API_KEY"),
   },
   flows: {
     pricing: {
       name: "Forge Pricing",
-      workflowId: process.env.NEXT_PUBLIC_FLOW_PRICING ?? "",
+      workflowId: requireEnv("FLOW_PRICING"),
@@
     tradeoff: {
       name: "Forge Tradeoff",
-      workflowId: process.env.NEXT_PUBLIC_FLOW_TRADEOFF ?? "",
+      workflowId: requireEnv("FLOW_TRADEOFF"),
@@
     contract: {
       name: "Forge Contract",
-      workflowId: process.env.NEXT_PUBLIC_FLOW_CONTRACT ?? "",
+      workflowId: requireEnv("FLOW_CONTRACT"),
@@
     invoice: {
       name: "Forge Invoice",
-      workflowId: process.env.NEXT_PUBLIC_FLOW_INVOICE ?? "",
+      workflowId: requireEnv("FLOW_INVOICE"),

Also applies to: 10-10, 25-25, 47-47, 70-70

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/orchestrate.js` around lines 3 - 5, The current config
silently defaults endpoint, projectId and apiKey to empty strings (endpoint,
projectId, apiKey) which delays errors; instead validate
process.env.NEXT_PUBLIC_LAMATIC_ENDPOINT,
process.env.NEXT_PUBLIC_LAMATIC_PROJECT_ID and process.env.LAMATIC_API_KEY at
module initialization and throw a clear Error (or assert) if any are missing so
the app fails fast on startup; update the code around where
endpoint/projectId/apiKey are set to remove the "?? ''" defaults and add a small
validation block that constructs a helpful message listing the missing env var
names before throwing.

Comment on lines +13 to +32
"react": "^18",
"react-dom": "^18",
"lamatic": "latest",
"clsx": "^2.1.1",
"tailwind-merge": "^2.3.0",
"lucide-react": "^0.400.0",
"react-markdown": "^9.0.0",
"react-signature-canvas": "^1.0.7"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-signature-canvas": "^1.0.5",
"tailwindcss": "^3.4.1",
"autoprefixer": "^10.4.19",
"postcss": "^8.4.38",
"eslint": "^8",
"eslint-config-next": "14.2.5"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="kits/forge/apps/package.json"
echo "== $FILE =="
ls -l "$FILE"
echo
nl -ba "$FILE" | sed -n '1,120p'
echo
echo "== deps versions (prod + dev) =="
rg -n '"(react|react-dom|lamatic|clsx|tailwind-merge|lucide-react|react-markdown|react-signature-canvas|typescript|`@types/node`|`@types/react`|`@types/react-dom`|`@types/react-signature-canvas`|tailwindcss|autoprefixer|postcss|eslint|eslint-config-next)"\s*:\s*".*"' "$FILE" || true

Repository: Lamatic/AgentKit

Length of output: 234


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="kits/forge/apps/package.json"
echo "== $FILE =="
ls -l "$FILE"
echo

python3 - <<'PY'
import json, pathlib
p = pathlib.Path("kits/forge/apps/package.json")
txt = p.read_text()
print("---- raw snippet (first 160 lines) ----")
for i,line in enumerate(txt.splitlines(), start=1):
    if i>160: break
    print(f"{i:4d}: {line}")
print("---- parsed versions (deps+devDeps) ----")
data = json.loads(txt)
for section in ("dependencies","devDependencies"):
    d = data.get(section, {})
    for k in sorted(d.keys()):
        print(f"{section}.{k} = {d[k]}")
PY

echo
echo "== ranges/latest check (matches ^, ~, latest) =="
python3 - <<'PY'
import json, pathlib, re
p = pathlib.Path("kits/forge/apps/package.json")
data = json.loads(p.read_text())
keys = []
bad = []
for section in ("dependencies","devDependencies"):
    for k,v in data.get(section, {}).items():
        if v == "latest" or re.match(r'^[\^~]', v) or v=="*" :
            bad.append((section,k,v))
if not bad:
    print("No ranges/tilde/caret/latest/* found in the selected sections.")
else:
    for item in sorted(bad):
        print("BAD:", ".".join(item[:2]), "=", item[2])
PY

Repository: Lamatic/AgentKit

Length of output: 2648


Mission directive: pin every dependency version in kits/forge/apps/package.json (no ranges, no latest)

This kit’s package.json still uses caret ranges and latest, which breaks reproducible kit installs/builds:

  • dependencies: react ^18, react-dom ^18, lamatic latest, clsx ^2.1.1, tailwind-merge ^2.3.0, lucide-react ^0.400.0, react-markdown ^9.0.0, react-signature-canvas ^1.0.7
  • devDependencies: typescript ^5, @types/node ^20, @types/react ^18, @types/react-dom ^18, @types/react-signature-canvas ^1.0.5, tailwindcss ^3.4.1, autoprefixer ^10.4.19, postcss ^8.4.38, eslint ^8

Pin these to exact versions (keep already-exact next / eslint-config-next).
[kill-switch: no more ^/latest in the kit manifest]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/apps/package.json` around lines 13 - 32, Update
kits/forge/apps/package.json by replacing all non-exact dependency specifiers
with exact versions: change "react", "react-dom", "lamatic", "clsx",
"tailwind-merge", "lucide-react", "react-markdown", and "react-signature-canvas"
in the "dependencies" section and "typescript", "`@types/node`", "`@types/react`",
"`@types/react-dom`", "`@types/react-signature-canvas`", "tailwindcss",
"autoprefixer", "postcss", and "eslint" in "devDependencies" from ranges or
"latest" to concrete version strings (no ^, ~, or "latest"); keep the existing
exact entries such as "eslint-config-next" unchanged. Ensure each entry is a
fixed version literal (e.g., "18.2.0" style) and run a quick install to verify
no conflicts.

Comment on lines +44 to +45
- line_items: use exactly what is provided. Do not modify amounts. - notes: include the freelancer's notes if provided, otherwise omit
or leave as empty string.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mission directive: remove conflicting notes instructions to avoid malformed output.

You require a fixed key set, then allow omitting notes. That contradiction can produce schema drift and break downstream parsing.

Proposed prompt patch
-- line_items: use exactly what is provided. Do not modify amounts. - notes: include the freelancer's notes if provided, otherwise omit
-or leave as empty string.
+- line_items: use exactly what is provided. Do not modify amounts.
+- notes: always include the "notes" key; use freelancer notes when provided, otherwise set it to an empty string "".
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- line_items: use exactly what is provided. Do not modify amounts. - notes: include the freelancer's notes if provided, otherwise omit
or leave as empty string.
- line_items: use exactly what is provided. Do not modify amounts.
- notes: always include the "notes" key; use freelancer notes when provided, otherwise set it to an empty string "".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@kits/forge/prompts/forge-invoice_llmnode-invoice_system_0.md` around lines 44
- 45, The prompt currently contains conflicting directives for the output
schema: one line mandates a fixed keyset including "line_items" while another
allows omitting or leaving "notes" empty, which can cause schema drift; update
the prompt in forge-invoice_llmnode-invoice_system_0.md to remove the
contradictory allowance so the keys are consistent — keep "line_items: use
exactly what is provided. Do not modify amounts." and change the "notes"
instruction to a single clear rule (either "notes: include the freelancer's
notes if provided, otherwise set as empty string" OR "notes: always include the
field, empty string if not provided") so downstream parsers receive a stable
schema.

@akshatvirmani akshatvirmani changed the title [kit] Add Forge for Freelance Services Agreement & Invoice Generation [mission-possible] feat: [kit] Add Forge for Freelance Services Agreement & Invoice Generation [mission-possible] Jun 3, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

:robot_face: AgentKit Structural Validation

New Contributions Detected

  • Template: kits/forge

Check Results

Check Status
No edits to existing kits ✅ Pass
Required root files present ✅ Pass
Flow .ts files present ✅ Pass
lamatic.config.ts valid ✅ Pass
No changes outside kits/ ✅ Pass

⚠️ Warnings

  • kits/forge is missing .env.example — bundles and kits should include one

🎉 All checks passed! This contribution follows the AgentKit structure.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 3, 2026

Failure recorded at 2026-06-03T15:50:21Z UTC. If this PR is not fixed within 4 weeks it will be automatically closed.

@akshatvirmani
Copy link
Copy Markdown
Contributor

Hello @cyber-turtle

in the lamatic.config.ts file can you replace single quotes with double quotes in Kit?

Right now it is 'kit' instead of "kit".
I will update the github action to check both the symbols but for now if you can do it that would be great. We will be able to merge after that.

More context to what to what: Line 5 in the .ts file (type: 'kit' as const,) to (type: "kit" as const,)

Thanks!

@akshatvirmani akshatvirmani changed the title feat: [kit] Add Forge for Freelance Services Agreement & Invoice Generation [mission-possible] feat: [kit] Add Forge for Freelance Services Agreement & Invoice Generation [mission-possible] . Jun 4, 2026
@akshatvirmani
Copy link
Copy Markdown
Contributor

/validate

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

📡 Running Studio validation — results will appear here shortly.

@akshatvirmani akshatvirmani merged commit b31826a into Lamatic:main Jun 5, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants