Skip to content

IRIS CLI v1.1.21 - #1

Merged
mayoalexander merged 35 commits into
mainfrom
dev
Apr 14, 2026
Merged

IRIS CLI v1.1.21#1
mayoalexander merged 35 commits into
mainfrom
dev

Conversation

@mayoalexander

Copy link
Copy Markdown

Summary

  • Unified Scheduleriris schedule create supports ALL job types (agent, heartbeat, competitor crawl, SEO check, Hive dispatch, custom scripts, code workflows)
  • Code Workflowsiris workflows create --type code --script ./file.js — write automation in JavaScript/Bash/Python, runs on Hive node
  • Site Onboardingiris onboard <url> — extract brand identity from any website + auto-generate branded Genesis page
  • iris-daemon consolidation — renamed iris-bridge repo to iris-daemon, pushed all 60 files (was stale with 2 commits). Includes iris-sdk, schedule-registry, mesh networking, node-cron

Test plan

  • iris schedule create --type seo_rank_check --agent 11 --frequency daily --params '{"domain":"heyiris.io","keywords":["AI"]}'
  • iris onboard https://stripe.com --extract-only
  • iris workflows create --type code --script ./test.js --runtime javascript
  • iris hive schedule list (verify schedule-registry works after fresh install)
  • Fresh install: curl -fsSL https://heyiris.io/install-code | bash on clean machine

🤖 Generated with Claude Code

mayoalexander and others added 10 commits April 12, 2026 18:44
- Added explicit rule: NEVER guess URLs, page URLs are heyiris.io/p/{slug}
- Added rule: READ CLI output carefully, don't make up values
- Changed Web Page flow: run `iris pages create` instead of just linking dashboard
- Includes publish command and correct URL format

Fixes: agent hallucinated https://hour-de-mayo.heyiris.io instead of
reading the correct URL from CLI output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion

publicUrl() now accepts a page object and reads public_url from the API
response (set by Page model's $appends accessor). Falls back to local
construction only when API doesn't provide it.

Prevents URL hallucination — the canonical URL comes from the server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The API requires json_content (array) but the CLI wasn't sending it,
causing "Validation failed" on every create. Now sends a minimal
landing template with HeroSection component and auto_publish=true
so pages are immediately live at their public URL.

Also uses API public_url in all page display outputs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SDK keys authenticate against iris-api, not fl-api. Added pagesFetch()
helper that routes all pages CRUD to IRIS_API (which proxies to fl-api
with its service token). Fixes 401 on pages list/create/update.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ions

1. scaffold/AGENTS.md: Added Critical Rules, Genesis component types,
   integration function table — loaded into EVERY agent session
2. iris pages component-registry: New CLI command listing all 24 valid
   component types with descriptions and required props
3. pages create: Fixed template — uses Hero (valid) not HeroSection,
   includes proper theme + SiteFooter, auto-publishes
4. install: Onboarding prompt includes component whitelist + rules

Fixes: agent hallucinating invalid component types (blank pages),
guessing URLs, and having no discoverability for page builder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d commands

1. scriptName("opencode") → scriptName("iris") — all help text now shows iris
2. scaffold/AGENTS.md: updated command table (iris pages, iris leads, etc.)
3. scaffold/how-to/pages.md: full Genesis recipe with valid component types
4. manifest.json: added pages.md entry
5. install: onboarding prompt includes component whitelist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- iris proposals create <lead-id> — generate proposal from lead data, attach contract, create Stripe checkout
- iris proposals status <lead-id> — check proposal/deal status
- iris proposals list — list all leads with active proposals
- iris contracts send <lead-id> — send contract for signing
- iris contracts status <lead-id> — check signing status
- iris contracts templates — list available contract templates

Both build on existing payment-gate API. Proposals wraps the full flow
(proposal + contract + payment), contracts focuses on the signing piece.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… consolidation

Scheduler:
- iris schedule create — single entry point for ALL job types
  (agent_task, heartbeat, competitor_intelligence, seo_rank_check,
   hive_task_dispatch, custom_script, code_workflow)
- iris schedule delete — remove scheduled jobs
- Added 'schedule' alias for 'schedules'

Code Workflows:
- iris workflows create --type code --script ./file.js --runtime javascript
- Stores script in workflow settings, runs on Hive node via sandbox_execute
- Supports javascript, bash, python runtimes

Site Onboarding:
- iris onboard <url> — extract brand identity + auto-generate Genesis page
- iris onboard <url> --extract-only — brand extraction without page creation
- Aliases: connect-site

Installer:
- Updated repo URLs from iris-bridge → iris-daemon
  (GitHub redirects old URL so no breakage)
- macOS, Windows, and scaffold docs all updated

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 13, 2026 03:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

IRIS CLI v1.1.21 expands the CLI’s “platform” surface area with new end-user workflows (schedules create, code workflows, site onboarding, proposals/contracts) and updates scaffold docs to match the Genesis page builder experience.

Changes:

  • Add new CLI commands: iris onboard, iris proposals, iris contracts, and expand iris schedules with create + delete.
  • Extend iris workflows create to support code workflows via --script/--runtime.
  • Improve Genesis pages UX: route Pages CRUD via IRIS_API, add a component registry command, and add new how-to docs.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
scaffold/manifest.json Registers new managed how-to doc for Genesis pages.
scaffold/how-to/README.md Adds recipe mapping entry for the new pages how-to.
scaffold/how-to/pages.md New Genesis pages CLI how-to + component type reference.
scaffold/how-to/hive-dispatch.md Updates daemon repo clone URL to iris-daemon.
scaffold/AGENTS.md Updates command map + adds stricter rules for pages/components/integrations.
packages/opencode/src/index.ts Renames CLI script name to iris and registers new platform commands.
packages/opencode/src/cli/cmd/platform-workflows.ts Adds code workflow creation (--script, --runtime) and improved output.
packages/opencode/src/cli/cmd/platform-schedules.ts Adds unified schedules create and schedules delete, plus alias schedule.
packages/opencode/src/cli/cmd/platform-proposals.ts New proposals command group (create/status/list).
packages/opencode/src/cli/cmd/platform-pages.ts Pages routed via IRIS_API, improved public URL handling, adds component registry + auto-publish create.
packages/opencode/src/cli/cmd/platform-onboard.ts New site onboarding command (brand extract + auto-create Genesis page).
packages/opencode/src/cli/cmd/platform-contracts.ts New contracts command group (send/status/templates).
packages/opencode/package.json Bumps version to 1.1.21.
install.ps1 Updates daemon repo clone URL to iris-daemon.
install Updates daemon repo clone URL + adds pages guidance/rules.

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

import { PlatformOutreachSendCommand } from "./cli/cmd/platform-outreach-send"
import { PlatformSomCommand } from "./cli/cmd/platform-som"
import { PlatformMonitorCommand } from "./cli/cmd/platform-monitor"
import { PlatformMonitorCommand, BriefingCommand } from "./cli/cmd/platform-monitor"
},
}

if (args["max-runs"]) {
}
console.log(` ${dim(`${COMPONENT_REGISTRY.length} components available`)}`)
console.log()
prompts.log.info(`Example: ${dim('iris pages set my-page "components.1" \'{"type":"Hero","props":{"title":"Hello"}}\'')}`)
Comment on lines +111 to +115

const brand = extractResult.brand ?? {}
const brandName = brand.brand_name ?? new URL(url).hostname
sp.stop(success(`Brand: ${brandName}`))

Comment thread install

RULES:
- NEVER use curl or call APIs directly. Only CLI commands.
- NEVER guess or hallucinate URLs. Page URLs follow the format: https://heyiris.io/p/{slug} (NOT subdomains). Always read the URL from CLI output.
Comment on lines +188 to +193
type: "TextBlock",
id: "about-1",
props: {
title: `About ${brandName}`,
content: extractResult.page_description ?? brand.tagline ?? `Welcome to ${brandName}.`,
themeMode,
Comment on lines +197 to +201
type: "ButtonCTA",
id: "cta-1",
props: {
text: ctaButton.text,
url: ctaButton.url,
Comment on lines +258 to +259
? `http://local.iris.freelabel.net:9300/p/${pageSlug}`
: `https://heyiris.io/p/${pageSlug}`
Comment on lines +88 to +90
spinner.stop("Proposal generated")

if (!(await handleApiError(res, "Create proposal"))) return
Comment on lines +70 to +72
spinner.stop("Contract created")

if (!(await handleApiError(res, "Send contract"))) return
mayoalexander and others added 17 commits April 12, 2026 22:52
New flags: --interval, --duration, --deposit, --brand-logo
Renamed: --no-contract → --skip-contract, --no-send → --skip-send
(yargs treats --no-X as boolean negation which caused silent failures)

Passes interval, duration_months, deposit_percent, brand_logo_url
to the /payment-gate API endpoint. Output shows billing summary
(amount/interval, duration, total, deposit).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…re upload

validateComponents() checks every component against VALID_COMPONENT_TYPES
set before sending to API. Catches hallucinated types like ImageShowcase,
YouTubeSection, HeroSection and shows the full valid type list + command
to run. Agent self-corrects from the error output.

Also adds pages.md how-to recipe and manifest entry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New CLI command: iris pages compose "build a streaming page for my channel"
- Sends description to iris-api compose endpoint
- Shows page URL, component count, self-heal attempts
- Supports --slug, --title, --theme, --style, --model flags

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Passes rev_share_percent to the payment-gate API. Output shows
"Rev Share: X% of net platform revenue" in the billing summary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… filter

- Table format: ID, Status, Frequency, Next Run (countdown), Name, Task Type
- --active flag: hides 670+ completed one-offs, shows only scheduled/running/paused
- Countdown: shows time until next run (7h, 45m, overdue)
- Task type labels from data.task_type (discover, heartbeat, etc.)
- Prompt preview on second line
- --json flag with time_until field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Infers from data.type and task_name:
- ⬡ hive: hive_task_dispatch, discover, som_batch, social_stats_sync
- ☁ cloud: heartbeats, agent tasks, everything else

Shows in table + JSON output (env field).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Installer:
- macOS LaunchAgent now installed by main installer (not just standalone)
- Daemon auto-starts on login — users never need to manually start it
- Wrapper script + plist copied and configured for ~/.iris/bridge path

Schedule CLI:
- Added countdown timer (⏱ 3h) showing time until next run
- Added task type label [competitor_intelligence] on each schedule
- Shows prompt/description on second line

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
⬡ hive  — runs on local machine via daemon (discover, SOM, social stats)
◉ iris  — runs on iris-api Railway (heartbeats)
☁ cloud — runs on fl-api Railway (agent tasks, workflows)
⟳ auto  — spawned by heartbeat agent during execution
⚡ hook  — event listener / webhook triggered

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Checks localhost:3200 health (500ms timeout). If daemon isn't running,
silently spawns iris-daemon start in background. Non-blocking, no output.
Users never need to manually start the daemon again.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
iris bridge start|stop|status|restart|logs|register
iris daemon start|stop|status|restart|logs|register (alias)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clean grouped display:
- ⬡ HIVE (local machine) — discover, SOM, social stats
- ◉ IRIS (cloud heartbeats) — agent heartbeat cycles
- ☁ CLOUD (agent tasks) — scheduled + heartbeat-spawned tasks
- ⟳ AUTO (heartbeat-spawned) — tasks created during heartbeat
- ⚡ HOOKS (event listeners) — webhook-triggered tasks

Shows agent name (not raw task_name), countdown timers inline,
deduplicates repeated prompt text, status badges (⏱ 14m, ⚠ overdue).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ⚠ stuck: one-time jobs running >1 hour (dead, needs cleanup)
- (via heartbeat): shows when task was spawned by heartbeat agent
- Clearer than "running once" which looks like it's working

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reads bloq name from eager-loaded relationship (s.bloq.name) with
fallback to batch bloq ID lookup. Shows as → BLOQ NAME on the
description line so you can see what knowledge base each job is
working against.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fetches latest execution in parallel for each active job. Shows:
- ✓/✗ status, time ago, model used, token count
- Response preview (first 70 chars)

Usage: iris schedules list --active --latest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ig view

history <id>: model, tokens, duration, tools, response preview
history <id> --full: complete response output
inspect <id>: agent config, system prompt, heartbeat mode, bloq context,
  task prompt, edit command. Everything you need to debug + tune.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…cing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mayoalexander and others added 8 commits April 13, 2026 11:49
iris proposals cancel <lead-id> — finds and completes all active
payment gate steps so a new proposal can be created.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Queries localhost:3200/daemon/schedules for local cron run data.
Shows: script name, cron expression, status, run count, last run time, duration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…details

-o  show last run stdout/stderr
-c  show script source code (checks ~/.iris/scripts, data/scripts, bridge/scripts)
-a  show everything (output + code + metadata)

Schedule registry now stores last_stdout, last_stderr, last_exit_code
for each run, exposed via /daemon/schedules API.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… architecture

Added to AGENTS.md (loaded every session):
- Schedules commands (list --active, inspect, history --full)
- Agent-first architecture docs (initial_prompt, system_prompt, heartbeat_tools)
- How to create specialized agents + debug with inspect

Updated iris-cli SKILL.md:
- Full TypeScript CLI command reference (schedules, pages, integrations, health)
- Agent-first heartbeat architecture section with examples
- 3-phase page compose documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests every link: fl-api, iris-api, daemon, Redis queue, Pusher.
For specific jobs: checks status, agent config, mission, tools,
bloq context, last execution, and error messages.

Usage:
  iris schedules diagnose        # system-wide check
  iris schedules diagnose 727    # specific job diagnosis

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- iris bug list: now fetches from API instead of just printing a URL
- iris boards list: fixed endpoint path (/user/{id}/bloqs/{id}/items)
- iris profile create: new command to create profiles via API
- iris profile reassign-articles: move articles between profiles by keyword

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mayoalexander
mayoalexander merged commit ca6967b into main Apr 14, 2026
0 of 3 checks passed
mayoalexander added a commit that referenced this pull request Aug 2, 2026
…, drill down, reconcile (#178595, #178599)

Written tests-first. 48 tests defining the contract, then the implementation,
then run against the real 149 payments in chat.db — which is where two defects
turned up that fixtures could never have caught.

WHY: `iris imessage read` requires a text body (lib/imessage.ts:227, and again
at :298 `if (!text) return null`). An Apple Cash transfer has no text — it is a
balloon payload — so 149 payments sat in the DB and the CLI reported none. That
is how a real $50 payout to Flo went missing.

Three constraints the API is built around, all verified against live data:

  1. THE AMOUNT IS NOT IN THE DATABASE. Confirmed by comparing a $50 screenshot
     against that payment's own row: not in text, not in message_summary_info
     (a bplist with only a state flag), not extractable from payload_data.
     So `amount` is optional and summarise() returns totalCents UNDEFINED when
     nothing is known — a total of 0 would be a lie. Pinned by test.
  2. MEANING LIVES IN A NEIGHBOURING MESSAGE. The only label a transfer carries
     is free text sent seconds around it. That convention is already in use by
     hand ("IRIS BUG BOUNTY #1 - FLO SMITH"), so it is parsed, not reinvented.
  3. ONE HUMAN, MANY CARDS. Matching is substring + case-insensitive across name
     AND handle, because searching "Flo" MUST reach the "Flozzel Smith" card.
     That exact miss is what hid the payment.

FOUND ONLY BY RUNNING AGAINST REAL DATA:
  - The labels are not in the `text` column either. Modern macOS stores message
    content in attributedBody, so the first real run attached 0 of 2 labels
    while both sat right there. The decoded string carries control-byte residue
    ("+!IRIS BUG BOUNTY #1 - FLO SMITH ") and must still parse — now a test.
  - An outbound message has handle_id 0, so the counterparty must come from the
    CHAT, not the handle. Also now a test, plus its inverse: an empty handle must
    never bucket, or a label could cross-attach to the wrong person.

reconcile() surfaces drift instead of smoothing it: recipient_mismatch,
duplicate_reference, sequence_gap, unlabelled-outbound. Against real data it
independently produced the finding that cost us this afternoon —
  [recipient_mismatch] label names "FLO SMITH" but the payment reached "Flozzel Smith"

Pure layer only; the chat.db reader lands next and now has a tested contract to
satisfy. attachLabels pre-filters to label-shaped messages before bucketing, so
5k payments x 20k messages completes in well under the 3s the test allows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mayoalexander added a commit that referenced this pull request Aug 2, 2026
Wires the TDD'd query layer to chat.db and ships the command. Apple Cash
transfers were invisible: lib/imessage.ts requires a text body (:227 in SQL,
:298 in the parser) and a payment has none, so 149 of them sat in the database
while the CLI reported nothing. That is how a real $50 payout went missing.

  iris imessage payments                    all payments, newest first
  iris imessage payments --contact Flo      finds the "Flozzel Smith" card
  iris imessage payments --check            reconciliation issues only
  --sent --received --since --until --reference --labelled --unlabelled
  --sort date|contact|reference --order --limit --offset --days --json

Live output, real data:

  2026-07-30 20:57:46  →  Rashad Bernard   IRIS BUG BOUNTY #2
  2026-07-29 14:01:50  →  Flozzel Smith    IRIS BUG BOUNTY #1  ⚠ label says "FLO SMITH"

That warning is the defect that cost us an afternoon, now produced automatically.

THREE THINGS THE FIRST REAL RUN BROKE ON, none of which fixtures could catch:

  - `.separator` does not survive imessage.query()'s shell escaping. Fields are
    now joined with char(31) INSIDE the SQL. Splitting on "|" — sqlite's default
    — would corrupt any row whose text contains a pipe, and text goes last so an
    overflow cannot shift earlier fields.
  - Selecting hex(attributedBody) across a year of messages overflowed the
    subprocess buffer outright (ENOBUFS). Now only the time windows AROUND
    payments are scanned, merged so a burst does not emit redundant clauses.
    Cost is proportional to PAYMENTS, not message history: 314 messages scanned
    in ~740ms cold and ~250ms warm, against 200,000 that could not complete.
  - The counterparty must come from the chat, not the handle: an outbound
    message has handle_id 0, so joining only `handle` drops every payment YOU
    sent — which, for a payer, is all of them.

The amount is never rendered and never totalled; the JSON carries
`amount_available: false` with a note, so no consumer can read a missing amount
as a zero. Apple does not store it — verified by comparing a $50 screenshot
against that payment's own row.

Does not touch `iris payments <lead-id>` (platform-payments.ts), which is Stripe.
48 payments tests green; full suite within the session's flaky band.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
mayoalexander added a commit that referenced this pull request Aug 27, 2026
… cause we never checked

`iris pages read n-46926bc2-…` built https://freelabel.net/p/n-<uuid>, got a 404,
and reported "/p/ serves PUBLISHED pages only. If this is a draft: iris pages
publish <slug>". Every word after the 404 was wrong: the note was live at
/n/<uuid> (200 on heyiris.io AND freelabel.net), it was never a draft, and
`pages publish` is not how notes are published — so the advice sent you to
republish something already published.

publicUrl() only ever built /p/${slug}, so a note ref had nowhere to go. It now
recognises `n-<uuid>` (and a bare uuid, the form `atlas use` already accepts) and
addresses the note viewer. The 404 text routes through notFoundHint(), which
names the cause it actually checked and points a note at `iris atlas use` — the
one verb that returns its text. Same fix reaches `pages screenshot`, which had
the identical message.

Addressing moves to a dependency-free page-ref.ts so it is unit testable without
platform-pages.ts's yargs/UI/API graph — the split hive-uptime.ts already uses.

`iris find` could not surface the answer either. "read a note", "note text",
"published note", "published note markdown" all returned ZERO hits for `atlas
use`, whose own describe ("pull a shared Atlas item's context") shares no word
with how anyone asks. That is how a five-step curl + HTML-scrape gets reinvented
for a one-command job. Adds the intent→noun entry and rewords the describe;
those queries now rank `atlas use` #1. Index regenerated additively: 1529 → 1533
entries, zero removals.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wF7NrsCHAhXRn3ZddtQv5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants