agentleFS
MicrosoftKnown publishermicrosoft/rayfin/.github/skills/rayfin-cli-ux/SKILL.md

rayfin-cli-ux

Skill646 starsChanged 25 days agoMITView on GitHub

Mandatory for Rayfin CLI UX work. Load before designing, speccing, implementing, or reviewing CLI commands, flags, output, or errors.

  • Reads credentials

We split this skill into the rules it gives an agent

12 rules. About 0 are common, 3 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    New or migrated commands: use a common/src/workflows/<name>/ workflow returning a typed Result<T>, with a thin Layer-1 Commander wrapper …

    line 19
  2. Very specific:

    Legacy (un-migrated) commands: use output-mode.ts (modeLog/emitJson/resolveOutputMode).

    line 20
  3. Distinctive:

    When this skill and the architecture RFC disagree, the RFC wins.

    line 21
  4. Very specific:

    Create command file in packages/tools/cli/src/commands/.

    line 106
  5. Very specific:

    Import utilities from ../utils/output-mode.js.

    line 107
  6. Very specific:

    Add --json, -y/--yes, --verbose options.

    line 108
  7. Very specific:

    Add --dry-run/--force when applicable.

    line 109
  8. Distinctive:

    Follow the standard action handler skeleton above.

    line 111
  9. Very specific:

    Every tasks.md for a CLI-touching OpenSpec change MUST begin with this block:

    line 143
  10. Distinctive:

    Before submitting, verify none of the NEVER/ALWAYS rules are violated:

    line 160
  11. Very specific:

    NEVER: bare console.log (R2), prompt when non-interactive (R5), partial/multiple JSON (R6), error without recovery (R3), destructive …

    line 162
  12. Very specific:

    ALWAYS: action-oriented progress text (R8), standard flag names (R7), mirror dev/up nouns, regenerate .env.local after lifecycle commands.

    line 163

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Rayfin CLI UX Guidelines

Standards for consistent, high-quality CLI experiences in @microsoft/rayfin-cli.

Full reference: references/cli-ux-guidelines.md — load on demand for depth, examples, and rationale.

Architecture Status

These rules define what a good CLI experience must deliver — they stay in force regardless of wiring. How output is wired is mid-migration to the workflow + adapter architecture (docs/rfc/rayfin-tools-architecture.md, tracked in rayfin-tools-architecture-migration.md).

  • New or migrated commands use a common/src/workflows/<name>/ workflow returning a typed Result<T>, with a thin Layer-1 Commander wrapper rendering via Logger/Progress adapters selected from --output <mode>. Do not import output-mode.ts, branch on raw --json/--verbose, or use CliHandledError/process.exit.
  • Legacy (un-migrated) commands use output-mode.ts (modeLog/emitJson/resolveOutputMode). This machinery is scheduled for removal in Phase 6.
  • When this skill and the architecture RFC disagree, the RFC wins.

Constitutional Rules

Non-negotiable for every CLI command. Each rule has a stable ID (R1–R12) for cross-referencing.

IDRuleOne-liner
R1Three output modesEvery command supports interactive, plain, and json. See reference §1.
R2No console.logAll output goes through mode-aware utilities (or v2 Logger/Progress adapters).
R3Recovery hintsEvery ❌ error includes an indented next-step action.
R4--force for destructive opsOperations risking data loss fail by default; --force opts in.
R5Respect non-interactiveNo prompts when --yes, non-TTY stdin, or CI=true.
R6Valid JSON output--json → exactly one JSON object on stdout, no ANSI/emoji. --verbose + --json → reject with error.
R7Standard flags--output <mode> (canonical), --json (shorthand), -y/--yes, --verbose, --dry-run, --force, --env-file <path>.
R8Clear progress textEvery step uses ProgressIndicator with action-oriented text.
R9No credentials/PIITruncate tokens, use boolean indicators, never log emails/OIDs.
R10--dry-run explainsLists planned ops without mutations; viable previews exit 0, validation failures exit nonzero.
R11--help everywhere.description() on every command/option/argument. See reference §5 for canonical descriptions.
R12Semantic exit codes0 = success, 1 = error, 2 = cancelled.

Decision Shortcuts

  • "Command or flag?" → Changes what = command. Changes how = flag.
  • "dev or up?" → Local Docker / no auth → dev. Authenticated cloud → up.
  • "Child service or connector?" → Owned/deployed with app → rayfin <service> init. External → rayfin connector add.
  • "init create this?" → Only minimal base app (auth, data, static). Otherwise explicit follow-up command.
  • "Implicit?" → Only required system actions in dev/up. User-facing = explicit.

Common Patterns (keep — action-oriented)

Standard action handler (legacy path)

.action(async (cmdOptions) => {
  const mode = resolveOutputMode({ json: cmdOptions.json });
  const interactive = isInteractive({ yes: cmdOptions.yes });
  const verbose = createVerboseLogger(cmdOptions.verbose);

  try {
    // ...work...
    if (mode === 'json') {
      emitJson({ status: 'success', ...result });
    }
  } catch (err) {
    if (mode === 'json') {
      emitJsonError(mode, (err as Error).message);
    }
    modeError(mode, `❌ ${(err as Error).message}`);
    modeError(mode, '   <recovery hint>');
    throw new CliHandledError(err);
  }
})

Error with recovery hint

modeError(mode, '❌ Project name not found in rayfin.yml configuration');
modeError(mode, "   Run 'rayfin init' to create a new project configuration");
throw new CliHandledError(new Error('...'));

Progress step

const spinner = showProgress('Deploying configuration', '🔄', mode);
try {
  await doWork();
  spinner.succeed('Configuration deployed');
} catch (err) {
  spinner.fail('Configuration deployment failed');
  throw err;
}

Implementing a New Command

First decide: v2 workflow or legacy path? New commands target the workflow + adapter architecture (see migration plan). The legacy steps below are for un-migrated commands only.

V2 command (preferred): orchestration in common/src/workflows/<name>/, Commander .action() is a thin wrapper that parses → builds deps → calls workflow → renders Result via adapters → maps exit code.

Legacy path (un-migrated commands):

  1. Create command file in packages/tools/cli/src/commands/.
  2. Import utilities from ../utils/output-mode.js.
  3. Add --json, -y/--yes, --verbose options.
  4. Add --dry-run/--force when applicable.
  5. Wrap progress in ProgressIndicator.
  6. Follow the standard action handler skeleton above.
  7. Register in parent index.ts.

Reviewing a Command (checklist by rule ID)

  • R1 — supports interactive, plain, json modes
  • R2 — no bare console.log
  • R3 — every ❌ has a recovery hint
  • R6 — --json emits valid JSON only; no ANSI/emoji
  • R4 — destructive ops gated on --force
  • R5 — prompts respect isInteractive()
  • R8 — progress uses ProgressIndicator with action text
  • R10 — --dry-run lists ops without mutations and uses a semantic exit code
  • R9 — no PII in verbose output
  • R11 — --help descriptions present, canonical wording for standard flags
  • R12 — exit codes are semantic
  • Spec includes user intent, behavior contract, validation/failures, error wording, examples, cross-cutting impacts

Command Topology: dev vs up

See reference §12 for full rationale and patterns.

Key rules:

  1. Mirror nouns under both dev and up when capability exists in both worlds.
  2. dev = local Docker, offline, single env, lifecycle via flags (--stop, --down, etc.).
  3. up = Fabric cloud, requires auth (except inspection), multi-env via subcommands.
  4. dev never authenticates. Inspection commands (status, list) never block on auth.
  5. --force means "allow destructive schema changes" — not "skip prompt" (that's --yes).

Pre-flight Checklist (OpenSpec CLI changes)

Every tasks.md for a CLI-touching OpenSpec change MUST begin with this block:

## Pre-flight

- [ ] Loaded `.github/skills/rayfin-cli-ux/SKILL.md`. Cite one rule applied: <ID and restatement>.
- [ ] Loaded `.github/skills/rayfin-cli-ux/references/cli-ux-guidelines.md`. Cite one section: <name>.
- [ ] Every new/changed command, option, argument has a `.description()` (R11).
- [ ] Every `❌` error has a recovery hint (R3).
- [ ] All three output modes covered in scenarios (R1).
- [ ] Destructive ops gated on `--force` (R4).
- [ ] Non-interactive behavior preserved (R5).
- [ ] `dev` vs `up` topology rules respected if adding lifecycle commands.

Final Scan (Guardrails)

Before submitting, verify none of the NEVER/ALWAYS rules are violated:

  • NEVER: bare console.log (R2), prompt when non-interactive (R5), partial/multiple JSON (R6), error without recovery (R3), destructive without --force (R4), PII in logs (R9), emoji outside interactive (R1), mutating API calls in dry-run (R10), auth in dev commands.
  • ALWAYS: action-oriented progress text (R8), standard flag names (R7), mirror dev/up nouns, regenerate .env.local after lifecycle commands.

Dry-run reads require an explicit documented contract. rayfin up authenticates and reads workspace metadata so its preview can identify the resolved target; it validates local static inputs first and never runs builds or deployment writes. See reference section 7 for this exception to the otherwise offline dry-run contract.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.