IRIS CLI v1.1.21 - #1
Merged
Merged
Conversation
- 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>
There was a problem hiding this comment.
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 expandiris scheduleswithcreate+delete. - Extend
iris workflows createto 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}`)) | ||
|
|
|
|
||
| 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 |
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>
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
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
iris schedule createsupports ALL job types (agent, heartbeat, competitor crawl, SEO check, Hive dispatch, custom scripts, code workflows)iris workflows create --type code --script ./file.js— write automation in JavaScript/Bash/Python, runs on Hive nodeiris onboard <url>— extract brand identity from any website + auto-generate branded Genesis pageiris-bridgerepo toiris-daemon, pushed all 60 files (was stale with 2 commits). Includes iris-sdk, schedule-registry, mesh networking, node-cronTest plan
iris schedule create --type seo_rank_check --agent 11 --frequency daily --params '{"domain":"heyiris.io","keywords":["AI"]}'iris onboard https://stripe.com --extract-onlyiris workflows create --type code --script ./test.js --runtime javascriptiris hive schedule list(verify schedule-registry works after fresh install)curl -fsSL https://heyiris.io/install-code | bashon clean machine🤖 Generated with Claude Code