@limrun/cli 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -1
- package/dist/commands/android/get.d.ts.map +1 -1
- package/dist/commands/android/get.js.map +1 -1
- package/dist/commands/skills/install.d.ts +17 -0
- package/dist/commands/skills/install.d.ts.map +1 -0
- package/dist/commands/skills/install.js +325 -0
- package/dist/commands/skills/install.js.map +1 -0
- package/dist/lib/skills.d.ts +20 -0
- package/dist/lib/skills.d.ts.map +1 -0
- package/dist/lib/skills.js +82 -0
- package/dist/lib/skills.js.map +1 -0
- package/package.json +9 -3
- package/skills/limrun-ios/SKILL.md +181 -0
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ The CLI stores configuration in `~/.lim/config.yaml`. This file is compatible wi
|
|
|
33
33
|
|
|
34
34
|
## Global Flags
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Most commands support these flags (exceptions: `lim skills install` does not take `--api-key` because it does not talk to the API):
|
|
37
37
|
|
|
38
38
|
| Flag | Description |
|
|
39
39
|
| ------------------- | ---------------------------------------------------------- |
|
|
@@ -86,6 +86,7 @@ This avoids relying on locally cached "last created" state and keeps the target
|
|
|
86
86
|
- [Assets](#assets) — Upload and download files (APKs, IPAs, etc.)
|
|
87
87
|
- [Sessions](#sessions) — Persistent connections for fast, interactive device control
|
|
88
88
|
- [Xcode Build Pipeline](#xcode-build-pipeline) — Sync code and run xcodebuild remotely
|
|
89
|
+
- [Skills](#skills) — Install Limrun skills for AI coding agents (Claude Code, Cursor, Codex)
|
|
89
90
|
|
|
90
91
|
---
|
|
91
92
|
|
|
@@ -613,6 +614,55 @@ Provide `--certificate-p12`, `--certificate-password`, and `--provisioning-profi
|
|
|
613
614
|
|
|
614
615
|
---
|
|
615
616
|
|
|
617
|
+
### Skills
|
|
618
|
+
|
|
619
|
+
Install Limrun skills into the native skills directory of AI coding agents (Claude Code, Cursor, Codex). After installation, the agent auto-discovers the skill and triggers it when you ask things like "build the iOS app" or "show me a screenshot."
|
|
620
|
+
|
|
621
|
+
```bash
|
|
622
|
+
# Interactive: prompts for agents (with detected ones pre-checked) and scope
|
|
623
|
+
lim skills install
|
|
624
|
+
|
|
625
|
+
# Non-interactive
|
|
626
|
+
lim skills install --agents claude --scope project
|
|
627
|
+
lim skills install --agents claude --agents cursor --scope project
|
|
628
|
+
lim skills install --agents codex --scope global
|
|
629
|
+
|
|
630
|
+
# Overwrite existing skill files (otherwise the command refuses on non-interactive runs)
|
|
631
|
+
lim skills install --agents claude --scope project --force
|
|
632
|
+
|
|
633
|
+
# Machine-readable output for scripts
|
|
634
|
+
lim skills install --agents claude --scope project --json
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**Flags:**
|
|
638
|
+
|
|
639
|
+
| Flag | Description |
|
|
640
|
+
| --------------------------- | -------------------------------------------------------------------------------------------- |
|
|
641
|
+
| `--agents <id>` | Target agent. Repeat to select multiple. One of: `claude`, `cursor`, `codex`. |
|
|
642
|
+
| `--scope <project\|global>` | `project` writes into the current directory; `global` writes into the user's home directory. |
|
|
643
|
+
| `--force` | Overwrite existing skill files without confirmation. |
|
|
644
|
+
| `--json` | Emit structured JSON instead of the human summary. |
|
|
645
|
+
| `--quiet` | Suppress non-result output. |
|
|
646
|
+
|
|
647
|
+
**Install paths:**
|
|
648
|
+
|
|
649
|
+
| Agent | Project | Global |
|
|
650
|
+
| ----------- | ------------------------- | -------------------------------------------------------------------------- |
|
|
651
|
+
| Claude Code | `.claude/skills/<skill>/` | `$CLAUDE_CONFIG_DIR/skills/<skill>/` (default `~/.claude/skills/<skill>/`) |
|
|
652
|
+
| Cursor | `.agents/skills/<skill>/` | `~/.agents/skills/<skill>/` |
|
|
653
|
+
| Codex | `.codex/skills/<skill>/` | `$CODEX_HOME/skills/<skill>/` (default `~/.codex/skills/<skill>/`) |
|
|
654
|
+
|
|
655
|
+
**Behavior:**
|
|
656
|
+
|
|
657
|
+
- The command compares bundled vs existing content byte-for-byte. Identical content is reported as `Unchanged` (no writes).
|
|
658
|
+
- Different content: in interactive mode you are asked to confirm each overwrite; in non-interactive mode the command refuses unless `--force` is passed.
|
|
659
|
+
- Non-interactive runs are all-or-nothing: if any selected target conflicts and `--force` is not set, no files are written for any target, and the command exits with status 1.
|
|
660
|
+
- Ctrl-C cancellation at any prompt exits cleanly without writing.
|
|
661
|
+
|
|
662
|
+
Cursor reads `.agents/skills/` natively, so we install there rather than `.cursor/skills/`. As a bonus, the same install reaches OpenCode and any other tool that follows the AGENTS.md convention - no extra menu options needed.
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
616
666
|
## Configuration
|
|
617
667
|
|
|
618
668
|
The CLI reads configuration from multiple sources (in order of precedence):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../src/commands/android/get.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,MAAM,CAAC,OAAO,SAAiD;IAC/D,MAAM,CAAC,WAAW,SACmI;IACrJ,MAAM,CAAC,QAAQ,WAAkF;IAEjG,MAAM,CAAC,IAAI;;MAKT;IAEF,MAAM,CAAC,KAAK;;;;;MAAgC;IAEtC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"get.d.ts","sourceRoot":"","sources":["../../../src/commands/android/get.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,MAAM,CAAC,OAAO,SAAiD;IAC/D,MAAM,CAAC,WAAW,SACmI;IACrJ,MAAM,CAAC,QAAQ,WAAkF;IAEjG,MAAM,CAAC,IAAI;;MAKT;IAEF,MAAM,CAAC,KAAK;;;;;MAAgC;IAEtC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAuB3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/commands/android/get.ts"],"names":[],"mappings":";;AAAA,sCAAmC;AACnC,qDAAiD;AAEjD,MAAqB,UAAW,SAAQ,0BAAW;IAejD,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACrD,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAE3B,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,gBAAgB,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;YAC7E,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;gBACf,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAC9D,IAAI,CAAC,MAAM,CAAC,OAAO,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC3C,IAAI,CAAC,MAAM,CAAC,SAAS,QAAQ,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC5D,IAAI,CAAC,MAAM,CAAC,WAAW,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC/C,IAAI,CAAC,MAAM,CAAC,UAAU,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC/C,IAAI,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC3E,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM;oBAAE,IAAI,CAAC,MAAM,CAAC,YAAY,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9E,IAAI,QAAQ,CAAC,MAAM,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"get.js","sourceRoot":"","sources":["../../../src/commands/android/get.ts"],"names":[],"mappings":";;AAAA,sCAAmC;AACnC,qDAAiD;AAEjD,MAAqB,UAAW,SAAQ,0BAAW;IAejD,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACrD,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAE3B,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,gBAAgB,GAAG,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;YAC7E,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;gBACf,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAC9D,IAAI,CAAC,MAAM,CAAC,OAAO,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC3C,IAAI,CAAC,MAAM,CAAC,SAAS,QAAQ,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC5D,IAAI,CAAC,MAAM,CAAC,WAAW,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC/C,IAAI,CAAC,MAAM,CAAC,UAAU,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC/C,IAAI,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC3E,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM;oBAAE,IAAI,CAAC,MAAM,CAAC,YAAY,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC9E,IAAI,QAAQ,CAAC,MAAM,CAAC,eAAe;oBACjC,IAAI,CAAC,MAAM,CAAC,sBAAsB,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;gBACvE,IAAI,eAAe;oBAAE,IAAI,CAAC,MAAM,CAAC,sBAAsB,eAAe,EAAE,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;;AApCM,kBAAO,GAAG,6CAA6C,CAAC;AACxD,sBAAW,GAChB,kJAAkJ,CAAC;AAC9I,mBAAQ,GAAG,CAAC,+BAA+B,EAAE,2CAA2C,CAAC,CAAC;AAE1F,eAAI,GAAG;IACZ,EAAE,EAAE,WAAI,CAAC,MAAM,CAAC;QACd,WAAW,EAAE,8EAA8E;QAC3F,QAAQ,EAAE,KAAK;KAChB,CAAC;CACH,CAAC;AAEK,gBAAK,GAAG,EAAE,GAAG,0BAAW,CAAC,SAAS,EAAE,CAAC;kBAbzB,UAAU"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class SkillsInstall extends Command {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static flags: {
|
|
7
|
+
agents: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
scope: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
quiet: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
private runInner;
|
|
15
|
+
private emitOutput;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/commands/skills/install.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AA4L7C,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,OAAO;IAChD,MAAM,CAAC,OAAO,SAAgD;IAC9D,MAAM,CAAC,WAAW,SAC6K;IAC/L,MAAM,CAAC,QAAQ,WAIb;IACF,MAAM,CAAC,KAAK;;;;;;MAsBV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YAWZ,QAAQ;IA2GtB,OAAO,CAAC,UAAU;CAiCnB"}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
|
+
const core_1 = require("@oclif/core");
|
|
10
|
+
const skills_1 = require("../../lib/skills");
|
|
11
|
+
const SKILL_NAME = 'limrun-ios';
|
|
12
|
+
const SKIPPED_REASON_CONFLICT = 'existing content differs; re-run with --force to overwrite';
|
|
13
|
+
const SKIPPED_REASON_BLOCKED = 'blocked: another target requires --force to proceed';
|
|
14
|
+
const SKIPPED_REASON_DECLINED = 'user declined overwrite confirmation';
|
|
15
|
+
class PromptCancelled extends Error {
|
|
16
|
+
constructor() {
|
|
17
|
+
super('cancelled');
|
|
18
|
+
this.name = 'PromptCancelled';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function detectAgentsForScope(scope) {
|
|
22
|
+
const detected = new Set();
|
|
23
|
+
for (const id of skills_1.AGENT_IDS) {
|
|
24
|
+
const agent = skills_1.AGENTS[id];
|
|
25
|
+
for (const p of agent.detectionPaths(scope)) {
|
|
26
|
+
if (fs_1.default.existsSync(p)) {
|
|
27
|
+
detected.add(id);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return detected;
|
|
33
|
+
}
|
|
34
|
+
async function promptAgents(preselected) {
|
|
35
|
+
// Loop to enforce "at least one agent" without crashing.
|
|
36
|
+
while (true) {
|
|
37
|
+
// `prompts` does not expose the multiselect cursor in onState - the state
|
|
38
|
+
// event only carries {value, aborted, exited}. Track cursor ourselves by
|
|
39
|
+
// listening to keypress events on stdin and mirror prompts' own dispatch
|
|
40
|
+
// (see prompts/lib/util/action.js and multiselect.js):
|
|
41
|
+
// - up / k : wrap (cursor === 0 ? last : cursor - 1)
|
|
42
|
+
// - down / j / tab : wrap (cursor === last ? 0 : cursor + 1)
|
|
43
|
+
// - ctrl+a : first
|
|
44
|
+
// - ctrl+e : last
|
|
45
|
+
// Anything else (page nav, home/end) is rare for a 3-row list and not
|
|
46
|
+
// tracked here; the worst case is the same as the previous clamp bug.
|
|
47
|
+
let cursor = 0;
|
|
48
|
+
const onKeypress = (_str, key) => {
|
|
49
|
+
if (!key || !key.name)
|
|
50
|
+
return;
|
|
51
|
+
if (key.meta && key.name !== 'escape')
|
|
52
|
+
return;
|
|
53
|
+
const last = skills_1.AGENT_IDS.length - 1;
|
|
54
|
+
if (key.ctrl) {
|
|
55
|
+
if (key.name === 'a')
|
|
56
|
+
cursor = 0;
|
|
57
|
+
else if (key.name === 'e')
|
|
58
|
+
cursor = last;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (key.name === 'up' || key.name === 'k') {
|
|
62
|
+
cursor = cursor === 0 ? last : cursor - 1;
|
|
63
|
+
}
|
|
64
|
+
else if (key.name === 'down' || key.name === 'j' || key.name === 'tab') {
|
|
65
|
+
cursor = cursor === last ? 0 : cursor + 1;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
process.stdin.on('keypress', onKeypress);
|
|
69
|
+
let response;
|
|
70
|
+
try {
|
|
71
|
+
response = await (0, prompts_1.default)({
|
|
72
|
+
type: 'multiselect',
|
|
73
|
+
name: 'agents',
|
|
74
|
+
message: 'Which agents do you want to set up?',
|
|
75
|
+
instructions: false,
|
|
76
|
+
choices: skills_1.AGENT_IDS.map((id) => ({
|
|
77
|
+
title: skills_1.AGENTS[id].displayName,
|
|
78
|
+
value: id,
|
|
79
|
+
selected: preselected.has(id),
|
|
80
|
+
})),
|
|
81
|
+
hint: 'Space to toggle, Enter to confirm (Enter alone picks the highlighted agent)',
|
|
82
|
+
}, {
|
|
83
|
+
onCancel: () => {
|
|
84
|
+
throw new PromptCancelled();
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
process.stdin.off('keypress', onKeypress);
|
|
90
|
+
}
|
|
91
|
+
let picked = (response.agents ?? []);
|
|
92
|
+
// If the user hit Enter without toggling anything, treat the highlighted
|
|
93
|
+
// row as their pick. Saves a Space keystroke for the common single-agent case.
|
|
94
|
+
if (picked.length === 0 && cursor >= 0 && cursor < skills_1.AGENT_IDS.length) {
|
|
95
|
+
picked = [skills_1.AGENT_IDS[cursor]];
|
|
96
|
+
}
|
|
97
|
+
if (picked.length > 0) {
|
|
98
|
+
process.stderr.write(` Selected: ${picked.map((id) => skills_1.AGENTS[id].displayName).join(', ')}\n`);
|
|
99
|
+
return picked;
|
|
100
|
+
}
|
|
101
|
+
// Re-prompt with a visible inline warning (should not be reachable now).
|
|
102
|
+
process.stderr.write('Select at least one agent.\n');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function promptScope() {
|
|
106
|
+
const response = await (0, prompts_1.default)({
|
|
107
|
+
type: 'select',
|
|
108
|
+
name: 'scope',
|
|
109
|
+
message: 'Install location?',
|
|
110
|
+
choices: [
|
|
111
|
+
{ title: 'Project', value: 'project', description: 'Install into the current directory' },
|
|
112
|
+
{ title: 'Global', value: 'global', description: 'Install into your home directory' },
|
|
113
|
+
],
|
|
114
|
+
initial: 0,
|
|
115
|
+
}, {
|
|
116
|
+
onCancel: () => {
|
|
117
|
+
throw new PromptCancelled();
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
return response.scope;
|
|
121
|
+
}
|
|
122
|
+
async function promptOverwrite(targetPath) {
|
|
123
|
+
const response = await (0, prompts_1.default)({
|
|
124
|
+
type: 'confirm',
|
|
125
|
+
name: 'ok',
|
|
126
|
+
message: `Overwrite existing ${targetPath}?`,
|
|
127
|
+
initial: false,
|
|
128
|
+
}, {
|
|
129
|
+
onCancel: () => {
|
|
130
|
+
throw new PromptCancelled();
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
return Boolean(response.ok);
|
|
134
|
+
}
|
|
135
|
+
function humanPath(absolutePath, scope) {
|
|
136
|
+
if (scope !== 'project') {
|
|
137
|
+
return absolutePath;
|
|
138
|
+
}
|
|
139
|
+
const relative = path_1.default.relative(process.cwd(), absolutePath);
|
|
140
|
+
if (relative.startsWith('..') || path_1.default.isAbsolute(relative)) {
|
|
141
|
+
return absolutePath;
|
|
142
|
+
}
|
|
143
|
+
return relative;
|
|
144
|
+
}
|
|
145
|
+
function statusLabel(status) {
|
|
146
|
+
switch (status) {
|
|
147
|
+
case 'installed':
|
|
148
|
+
return 'Installed';
|
|
149
|
+
case 'updated':
|
|
150
|
+
return 'Updated';
|
|
151
|
+
case 'unchanged':
|
|
152
|
+
return 'Unchanged';
|
|
153
|
+
case 'skipped':
|
|
154
|
+
return 'Skipped';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
class SkillsInstall extends core_1.Command {
|
|
158
|
+
async run() {
|
|
159
|
+
try {
|
|
160
|
+
await this.runInner();
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
if (err instanceof PromptCancelled) {
|
|
164
|
+
this.exit(130);
|
|
165
|
+
}
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async runInner() {
|
|
170
|
+
const { flags } = await this.parse(SkillsInstall);
|
|
171
|
+
const interactive = process.stdin.isTTY === true && !flags.json && !flags.quiet;
|
|
172
|
+
let agents;
|
|
173
|
+
let scope;
|
|
174
|
+
if (flags.agents && flags.agents.length > 0) {
|
|
175
|
+
agents = Array.from(new Set(flags.agents));
|
|
176
|
+
}
|
|
177
|
+
else if (interactive) {
|
|
178
|
+
// Pre-check based on project-local presence only. Global installs of
|
|
179
|
+
// agents (e.g. ~/.claude on a dev machine) are too weak a signal to
|
|
180
|
+
// auto-select them for this specific project's install.
|
|
181
|
+
agents = await promptAgents(detectAgentsForScope('project'));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
this.error('--agents requires at least one of: claude, cursor, codex.', { exit: 2 });
|
|
185
|
+
}
|
|
186
|
+
if (flags.scope) {
|
|
187
|
+
scope = flags.scope;
|
|
188
|
+
}
|
|
189
|
+
else if (interactive) {
|
|
190
|
+
scope = await promptScope();
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
this.error('Specify --agents and --scope in non-interactive mode.', { exit: 2 });
|
|
194
|
+
}
|
|
195
|
+
const source = (0, skills_1.sourceSkillMd)(this.config, SKILL_NAME);
|
|
196
|
+
if (!fs_1.default.existsSync(source)) {
|
|
197
|
+
this.error(`Bundled skill source missing at ${source}.`, { exit: 1 });
|
|
198
|
+
}
|
|
199
|
+
// Phase 1: Plan.
|
|
200
|
+
const planned = agents.map((id) => {
|
|
201
|
+
const agent = skills_1.AGENTS[id];
|
|
202
|
+
const target = (0, skills_1.targetSkillMd)(agent, scope, SKILL_NAME);
|
|
203
|
+
const { kind } = (0, skills_1.planSkillFileCopy)(source, target);
|
|
204
|
+
return { agent, scope, source, target, kind };
|
|
205
|
+
});
|
|
206
|
+
// Phase 2: Confirm.
|
|
207
|
+
const results = [];
|
|
208
|
+
const anyConflict = planned.some((t) => t.kind === 'conflict');
|
|
209
|
+
if (anyConflict && !flags.force && !interactive) {
|
|
210
|
+
// Non-interactive + conflict + no force: all-or-nothing. Skip all targets.
|
|
211
|
+
for (const t of planned) {
|
|
212
|
+
results.push({
|
|
213
|
+
agent: t.agent.id,
|
|
214
|
+
scope: t.scope,
|
|
215
|
+
path: t.target,
|
|
216
|
+
status: 'skipped',
|
|
217
|
+
reason: t.kind === 'conflict' ? SKIPPED_REASON_CONFLICT : SKIPPED_REASON_BLOCKED,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (!flags.json) {
|
|
221
|
+
// --quiet still suppresses the human summary, but a hard refusal needs to be visible.
|
|
222
|
+
process.stderr.write('Existing skill files would be overwritten. Re-run with --force, or run interactively to confirm per target.\n');
|
|
223
|
+
}
|
|
224
|
+
this.emitOutput(results, flags);
|
|
225
|
+
this.exit(1);
|
|
226
|
+
}
|
|
227
|
+
// Decide final status per target (no writes yet).
|
|
228
|
+
const finalDecisions = [];
|
|
229
|
+
for (const t of planned) {
|
|
230
|
+
if (t.kind === 'install') {
|
|
231
|
+
finalDecisions.push({ target: t, status: 'installed' });
|
|
232
|
+
}
|
|
233
|
+
else if (t.kind === 'unchanged') {
|
|
234
|
+
finalDecisions.push({ target: t, status: 'unchanged' });
|
|
235
|
+
}
|
|
236
|
+
else if (flags.force) {
|
|
237
|
+
finalDecisions.push({ target: t, status: 'updated' });
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// Interactive conflict without --force: prompt per target.
|
|
241
|
+
const displayPath = humanPath(t.target, t.scope);
|
|
242
|
+
const ok = await promptOverwrite(displayPath);
|
|
243
|
+
if (ok) {
|
|
244
|
+
finalDecisions.push({ target: t, status: 'updated' });
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
finalDecisions.push({
|
|
248
|
+
target: t,
|
|
249
|
+
status: 'skipped',
|
|
250
|
+
reason: SKIPPED_REASON_DECLINED,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Phase 3: Apply.
|
|
256
|
+
for (const decision of finalDecisions) {
|
|
257
|
+
if (decision.status === 'installed' || decision.status === 'updated') {
|
|
258
|
+
(0, skills_1.applySkillFileCopy)(decision.target.source, decision.target.target);
|
|
259
|
+
}
|
|
260
|
+
results.push({
|
|
261
|
+
agent: decision.target.agent.id,
|
|
262
|
+
scope: decision.target.scope,
|
|
263
|
+
path: decision.target.target,
|
|
264
|
+
status: decision.status,
|
|
265
|
+
...(decision.reason ? { reason: decision.reason } : {}),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
this.emitOutput(results, flags);
|
|
269
|
+
}
|
|
270
|
+
emitOutput(results, flags) {
|
|
271
|
+
if (flags.json) {
|
|
272
|
+
this.log(JSON.stringify({
|
|
273
|
+
skill: SKILL_NAME,
|
|
274
|
+
results,
|
|
275
|
+
}, null, 2));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (flags.quiet) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
// Human summary.
|
|
282
|
+
this.log('');
|
|
283
|
+
this.log(` ${SKILL_NAME}`);
|
|
284
|
+
const colWidth = Math.max(...results.map((r) => skills_1.AGENTS[r.agent].displayName.length), 'Claude Code'.length);
|
|
285
|
+
for (const r of results) {
|
|
286
|
+
const agentLabel = skills_1.AGENTS[r.agent].displayName.padEnd(colWidth);
|
|
287
|
+
const displayPath = humanPath(r.path, r.scope);
|
|
288
|
+
const reason = r.reason ? ` (${r.reason})` : '';
|
|
289
|
+
this.log(` ${agentLabel} -> ${displayPath} ${statusLabel(r.status)}${reason}`);
|
|
290
|
+
}
|
|
291
|
+
this.log('');
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
SkillsInstall.summary = 'Install Limrun skills for AI coding agents';
|
|
295
|
+
SkillsInstall.description = 'Copy the bundled Limrun skill into the native skills directory for each selected agent (Claude Code, Cursor, Codex). Pre-checks detected agents and lets you pick project or global scope.';
|
|
296
|
+
SkillsInstall.examples = [
|
|
297
|
+
'<%= config.bin %> skills install',
|
|
298
|
+
'<%= config.bin %> skills install --agents claude --agents cursor --scope project',
|
|
299
|
+
'<%= config.bin %> skills install --agents codex --scope global --force',
|
|
300
|
+
];
|
|
301
|
+
SkillsInstall.flags = {
|
|
302
|
+
agents: core_1.Flags.string({
|
|
303
|
+
description: 'Target agent. Repeat to pick multiple.',
|
|
304
|
+
multiple: true,
|
|
305
|
+
options: ['claude', 'cursor', 'codex'],
|
|
306
|
+
}),
|
|
307
|
+
scope: core_1.Flags.string({
|
|
308
|
+
description: 'Install scope.',
|
|
309
|
+
options: ['project', 'global'],
|
|
310
|
+
}),
|
|
311
|
+
force: core_1.Flags.boolean({
|
|
312
|
+
description: 'Overwrite existing skill files without confirmation.',
|
|
313
|
+
default: false,
|
|
314
|
+
}),
|
|
315
|
+
json: core_1.Flags.boolean({
|
|
316
|
+
description: 'Emit structured JSON output.',
|
|
317
|
+
default: false,
|
|
318
|
+
}),
|
|
319
|
+
quiet: core_1.Flags.boolean({
|
|
320
|
+
description: 'Suppress non-result output.',
|
|
321
|
+
default: false,
|
|
322
|
+
}),
|
|
323
|
+
};
|
|
324
|
+
exports.default = SkillsInstall;
|
|
325
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/commands/skills/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Config } from '@oclif/core';
|
|
2
|
+
export type AgentId = 'claude' | 'cursor' | 'codex';
|
|
3
|
+
export type Scope = 'project' | 'global';
|
|
4
|
+
export declare const AGENT_IDS: AgentId[];
|
|
5
|
+
export interface AgentSpec {
|
|
6
|
+
id: AgentId;
|
|
7
|
+
displayName: string;
|
|
8
|
+
skillsDir(scope: Scope): string;
|
|
9
|
+
detectionPaths(scope: Scope): string[];
|
|
10
|
+
}
|
|
11
|
+
export declare const AGENTS: Record<AgentId, AgentSpec>;
|
|
12
|
+
export declare function skillsRoot(config: Config): string;
|
|
13
|
+
export declare function sourceSkillMd(config: Config, skillName: string): string;
|
|
14
|
+
export declare function targetSkillMd(agent: AgentSpec, scope: Scope, skillName: string): string;
|
|
15
|
+
export type PlanKind = 'install' | 'unchanged' | 'conflict';
|
|
16
|
+
export declare function planSkillFileCopy(sourceFile: string, targetFile: string): {
|
|
17
|
+
kind: PlanKind;
|
|
18
|
+
};
|
|
19
|
+
export declare function applySkillFileCopy(sourceFile: string, targetFile: string): void;
|
|
20
|
+
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AACpD,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEzC,eAAO,MAAM,SAAS,EAAE,OAAO,EAAkC,CAAC;AAElE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;IAChC,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;CACxC;AAcD,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAsC7C,CAAC;AAEF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAE5D,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAO5F;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAG/E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AGENTS = exports.AGENT_IDS = void 0;
|
|
7
|
+
exports.skillsRoot = skillsRoot;
|
|
8
|
+
exports.sourceSkillMd = sourceSkillMd;
|
|
9
|
+
exports.targetSkillMd = targetSkillMd;
|
|
10
|
+
exports.planSkillFileCopy = planSkillFileCopy;
|
|
11
|
+
exports.applySkillFileCopy = applySkillFileCopy;
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const os_1 = __importDefault(require("os"));
|
|
15
|
+
exports.AGENT_IDS = ['claude', 'cursor', 'codex'];
|
|
16
|
+
function claudeGlobalRoot() {
|
|
17
|
+
const override = process.env.CLAUDE_CONFIG_DIR;
|
|
18
|
+
if (override)
|
|
19
|
+
return path_1.default.resolve(override);
|
|
20
|
+
return path_1.default.join(os_1.default.homedir(), '.claude');
|
|
21
|
+
}
|
|
22
|
+
function codexGlobalRoot() {
|
|
23
|
+
const override = process.env.CODEX_HOME;
|
|
24
|
+
if (override)
|
|
25
|
+
return path_1.default.resolve(override);
|
|
26
|
+
return path_1.default.join(os_1.default.homedir(), '.codex');
|
|
27
|
+
}
|
|
28
|
+
exports.AGENTS = {
|
|
29
|
+
claude: {
|
|
30
|
+
id: 'claude',
|
|
31
|
+
displayName: 'Claude Code',
|
|
32
|
+
skillsDir: (scope) => scope === 'project' ?
|
|
33
|
+
path_1.default.join(process.cwd(), '.claude', 'skills')
|
|
34
|
+
: path_1.default.join(claudeGlobalRoot(), 'skills'),
|
|
35
|
+
detectionPaths: (scope) => scope === 'project' ? [path_1.default.join(process.cwd(), '.claude')] : [claudeGlobalRoot()],
|
|
36
|
+
},
|
|
37
|
+
cursor: {
|
|
38
|
+
id: 'cursor',
|
|
39
|
+
displayName: 'Cursor',
|
|
40
|
+
// Cursor auto-discovers .agents/skills/ natively, same as .cursor/skills/.
|
|
41
|
+
// Installing into .agents/skills/ also reaches OpenCode and any other
|
|
42
|
+
// AGENTS.md-aware tool with a single copy, so prefer the broader path.
|
|
43
|
+
skillsDir: (scope) => scope === 'project' ?
|
|
44
|
+
path_1.default.join(process.cwd(), '.agents', 'skills')
|
|
45
|
+
: path_1.default.join(os_1.default.homedir(), '.agents', 'skills'),
|
|
46
|
+
// Detect either .cursor/ or .agents/: both are reliable signs the user
|
|
47
|
+
// is on a tool that auto-loads .agents/skills/.
|
|
48
|
+
detectionPaths: (scope) => scope === 'project' ?
|
|
49
|
+
[path_1.default.join(process.cwd(), '.cursor'), path_1.default.join(process.cwd(), '.agents')]
|
|
50
|
+
: [path_1.default.join(os_1.default.homedir(), '.cursor'), path_1.default.join(os_1.default.homedir(), '.agents')],
|
|
51
|
+
},
|
|
52
|
+
codex: {
|
|
53
|
+
id: 'codex',
|
|
54
|
+
displayName: 'Codex',
|
|
55
|
+
skillsDir: (scope) => scope === 'project' ?
|
|
56
|
+
path_1.default.join(process.cwd(), '.codex', 'skills')
|
|
57
|
+
: path_1.default.join(codexGlobalRoot(), 'skills'),
|
|
58
|
+
detectionPaths: (scope) => scope === 'project' ? [path_1.default.join(process.cwd(), '.codex')] : [codexGlobalRoot()],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
function skillsRoot(config) {
|
|
62
|
+
return path_1.default.join(config.root, 'skills');
|
|
63
|
+
}
|
|
64
|
+
function sourceSkillMd(config, skillName) {
|
|
65
|
+
return path_1.default.join(skillsRoot(config), skillName, 'SKILL.md');
|
|
66
|
+
}
|
|
67
|
+
function targetSkillMd(agent, scope, skillName) {
|
|
68
|
+
return path_1.default.join(agent.skillsDir(scope), skillName, 'SKILL.md');
|
|
69
|
+
}
|
|
70
|
+
function planSkillFileCopy(sourceFile, targetFile) {
|
|
71
|
+
if (!fs_1.default.existsSync(targetFile)) {
|
|
72
|
+
return { kind: 'install' };
|
|
73
|
+
}
|
|
74
|
+
const sourceBuf = fs_1.default.readFileSync(sourceFile);
|
|
75
|
+
const targetBuf = fs_1.default.readFileSync(targetFile);
|
|
76
|
+
return { kind: sourceBuf.equals(targetBuf) ? 'unchanged' : 'conflict' };
|
|
77
|
+
}
|
|
78
|
+
function applySkillFileCopy(sourceFile, targetFile) {
|
|
79
|
+
fs_1.default.mkdirSync(path_1.default.dirname(targetFile), { recursive: true });
|
|
80
|
+
fs_1.default.copyFileSync(sourceFile, targetFile);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@limrun/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Use remote XCode, iOS Simulator, Android Emulator and more to build and test apps from Linux, Windows or macOS.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lim": "./bin/run.js"
|
|
@@ -24,19 +24,21 @@
|
|
|
24
24
|
"test": "jest"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@limrun/api": "0.28.
|
|
27
|
+
"@limrun/api": "0.28.4",
|
|
28
28
|
"@oclif/core": "^4",
|
|
29
29
|
"cli-progress": "^3.12.0",
|
|
30
30
|
"cli-table3": "^0.6.5",
|
|
31
31
|
"dotenv": "^17.4.2",
|
|
32
32
|
"js-yaml": "^4.1.0",
|
|
33
|
-
"open": "^10.1.0"
|
|
33
|
+
"open": "^10.1.0",
|
|
34
|
+
"prompts": "^2.4.2"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@oclif/test": "^4",
|
|
37
38
|
"@types/cli-progress": "^3.11.6",
|
|
38
39
|
"@types/js-yaml": "^4.0.9",
|
|
39
40
|
"@types/node": "^22",
|
|
41
|
+
"@types/prompts": "^2.4.9",
|
|
40
42
|
"typescript": "^5.8.3"
|
|
41
43
|
},
|
|
42
44
|
"oclif": {
|
|
@@ -59,6 +61,9 @@
|
|
|
59
61
|
},
|
|
60
62
|
"session": {
|
|
61
63
|
"description": "Manage persistent background sessions for device interaction: start, status, stop. Sessions are used to execute commands on remote devices without the need to reconnect to the device after each command."
|
|
64
|
+
},
|
|
65
|
+
"skills": {
|
|
66
|
+
"description": "Install Limrun skills for AI coding agents (Claude Code, Cursor, Codex). Run `lim skills install` to set up."
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
},
|
|
@@ -68,6 +73,7 @@
|
|
|
68
73
|
"files": [
|
|
69
74
|
"dist",
|
|
70
75
|
"bin",
|
|
76
|
+
"skills",
|
|
71
77
|
"README.md"
|
|
72
78
|
]
|
|
73
79
|
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: limrun-ios
|
|
3
|
+
description: "Replaces xcodebuild with remote XCode and Simulators, plus full iOS UI control. Use when the user wants to build, run, or launch an iOS app, test iOS UI, interact with the simulator (tap, click, type into, scroll), inspect the screen, capture a screenshot, or record a video. Triggers on phrases like 'run it', 'build it', 'test it', 'show me a screenshot', 'launch on simulator', 'tap the button', or 'type into the field'."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Remote XCode & iOS Simulator
|
|
7
|
+
|
|
8
|
+
You are an iOS build-and-test operator. Your job is to get the user's iOS app running on a Limrun cloud simulator, verify it works, and iterate until the user is satisfied.
|
|
9
|
+
|
|
10
|
+
All builds and simulator operations run on Limrun and that's why you can build iOS
|
|
11
|
+
apps from any environments; linux, windows, macos, VM, container etc. Never try to
|
|
12
|
+
use local Xcode, local simulators, or local macOS build tools.
|
|
13
|
+
|
|
14
|
+
If `lim` CLI is not installed, you can install it with the following:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install --global @limrun/cli
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Check the CLI for current commands and flags
|
|
21
|
+
|
|
22
|
+
The CLI is the source of truth for command names, flags, and behavior. Before invoking any `lim` command you have not already used in this session, MUST run its `--help` first. The examples below are happy-path only; this skill intentionally does not embed the full reference because it would rot. Use:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
lim ios --help # list all iOS subcommands
|
|
26
|
+
lim ios <subcommand> --help # flags and examples for one iOS subcommand
|
|
27
|
+
lim xcode --help # list all xcode subcommands
|
|
28
|
+
lim xcode <subcommand> --help # flags and examples for one xcode subcommand
|
|
29
|
+
lim session --help # list all session subcommands
|
|
30
|
+
lim session <subcommand> --help # flags and examples for one session subcommand
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Build and Reload
|
|
34
|
+
|
|
35
|
+
First, create an XCode & iOS Simulator pair:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Add label selector depending on your identifiers. For example, Linear issue, repo name etc.
|
|
39
|
+
lim ios create --xcode \
|
|
40
|
+
--reuse-if-exists \
|
|
41
|
+
--label issue=<ISSUE ID> \
|
|
42
|
+
--label repo=<Repo Name> \
|
|
43
|
+
--label agent=<Your Agent Name>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The command output includes the instance ID (e.g. `ios_abc123`) and a `Signed Stream URL:`. Share that URL with the user so they can watch the simulator while you work. If you have a browser you can drive, open the URL in that browser and notify the user.
|
|
47
|
+
|
|
48
|
+
### Start a session for fast interaction
|
|
49
|
+
|
|
50
|
+
Right after creating the instance, start a session so subsequent interaction commands are fast:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# ID comes from the create output above. Always pass --id explicitly in agent workflows.
|
|
54
|
+
lim session start --id <instance-id>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Every `tap`, `type`, `element-tree`, `screenshot`, `record`, and `perform` command auto-routes through this session at ~50ms instead of ~2s. `lim ios delete` will auto-stop the session at cleanup; no separate `session stop` step is required.
|
|
58
|
+
|
|
59
|
+
### Build
|
|
60
|
+
|
|
61
|
+
Instead of `xcodebuild` command, you MUST use the following to build the iOS app.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
lim xcode build .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Use `--scheme` and `--workspace` flags if the project has multiple schemes or uses a workspace file. This makes sure the files are synced with the remote xcode and triggers
|
|
68
|
+
a build where the build logs are streamed through stdout and stderr.
|
|
69
|
+
|
|
70
|
+
Every successful build will automatically re-install the app in iOS Simulator and re-launch it.
|
|
71
|
+
|
|
72
|
+
## Interacting with the App
|
|
73
|
+
|
|
74
|
+
Prefer tapping by accessibility identifier, then by label, then by coordinates as a last resort:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
lim ios tap-element --ax-unique-id startButton
|
|
78
|
+
lim ios tap-element --ax-label "Save"
|
|
79
|
+
lim ios tap 201 450
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
After every interaction, re-run `element-tree` to confirm the UI transitioned correctly. No sleep is needed between a tap and element-tree.
|
|
83
|
+
|
|
84
|
+
For text input:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
lim ios type "hello world"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Testing Changes
|
|
91
|
+
|
|
92
|
+
After every build, test new or changed functionality by using interaction commands. Focus on what changed plus a quick smoke test of core flows.
|
|
93
|
+
|
|
94
|
+
Use element tree for functional assertions (element existence, labels, state changes). Use screenshots only for visual-only properties.
|
|
95
|
+
Use video recording for most accurate interaction tests such as animations, gameplay,
|
|
96
|
+
real experience etc.
|
|
97
|
+
|
|
98
|
+
Generally, start with getting an element tree:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
lim ios element-tree
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then if a single action will be taken, just call it. For example:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
lim ios tap-element --ax-label Continue
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If you will take multiple actions, you can create a chain of actions to be executed
|
|
111
|
+
with precise timing.
|
|
112
|
+
|
|
113
|
+
Some examples:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
lim ios perform --action type=tap,x=100,y=200 --action "type=typeText,text=Hello World"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
lim ios perform --action type=wait,durationMs=1000 --action type=pressKey,key=enter
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
You can write to a file and execute that too:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
lim ios perform --file ./actions.yaml
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use `lim ios perform --help` for more details on how to use it.
|
|
130
|
+
|
|
131
|
+
Video recording is available so you can review what the user sees while you are taking actions. For
|
|
132
|
+
any testing involving motion prefer video over screenshots for review.
|
|
133
|
+
|
|
134
|
+
Always include a demo video in the pull request so that user can see how it works.
|
|
135
|
+
|
|
136
|
+
Start recording (non-blocking):
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
lim ios record start
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Stop and save recording:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
lim ios record stop -o /tmp/recording.mp4
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Finalize
|
|
149
|
+
|
|
150
|
+
When you are wrapping up changes, produce a shareable build artifact so the user can test it themselves:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
ASSET_NAME="<bundle-id-or-pr-number-or-session-identifier>.zip"
|
|
154
|
+
lim xcode build . --upload ${ASSET_NAME}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Then share a preview link with the user (substitute the asset name above):
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
https://console.limrun.com/preview?asset=${ASSET_NAME}&platform=ios
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Always provide this in your last message.
|
|
164
|
+
|
|
165
|
+
## Cleanup
|
|
166
|
+
|
|
167
|
+
When the user is satisfied or the conversation is ending, always clean up:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
lim ios delete
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Gotchas
|
|
174
|
+
|
|
175
|
+
These are common failure points. Check here first when something goes wrong.
|
|
176
|
+
|
|
177
|
+
- **Instance ID is optional.** The CLI remembers the last created instance. You only need to pass an ID explicitly when controlling multiple instances.
|
|
178
|
+
- **No sleep needed between `tap-element` and `element-tree`.** The tap blocks until complete.
|
|
179
|
+
- **`element-tree` can be large.** Pipe through `grep` or `jq` to extract what you need rather than dumping the full tree into context.
|
|
180
|
+
- **Build errors are your job to fix.** If a build fails, read the error output, fix the code, and rebuild. Do not ask the user to fix build errors.
|
|
181
|
+
- **Bundle ID discovery.** If you don't know the bundle ID, check the Xcode project files or run `lim ios list-apps` after a successful build.
|