@xyleapp/cli 0.1.0 → 0.2.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/bin/xyle.mjs +1 -1
- package/package.json +1 -1
- package/src/commands.mjs +51 -0
- package/src/seed.mjs +180 -0
package/bin/xyle.mjs
CHANGED
package/package.json
CHANGED
package/src/commands.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
SEO_BASE,
|
|
18
18
|
} from "./api.mjs";
|
|
19
19
|
import { getCredentials, clearCredentials, runLoginFlow } from "./auth.mjs";
|
|
20
|
+
import { seedInstructions, getToolNames, TOOLS } from "./seed.mjs";
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Print error message in red to stderr and exit.
|
|
@@ -284,4 +285,54 @@ export function registerCommands(program) {
|
|
|
284
285
|
console.log("\x1b[33mNot authenticated. Run: xyle login\x1b[0m");
|
|
285
286
|
}
|
|
286
287
|
});
|
|
288
|
+
|
|
289
|
+
// --- seed ---
|
|
290
|
+
program
|
|
291
|
+
.command("seed")
|
|
292
|
+
.description("Add xyle agent instructions to your project for AI coding tools")
|
|
293
|
+
.option(
|
|
294
|
+
"--tool <name>",
|
|
295
|
+
`Specific tool: ${getToolNames().join(", ")} (default: all)`
|
|
296
|
+
)
|
|
297
|
+
.option("--dir <path>", "Target directory", process.cwd())
|
|
298
|
+
.option("--list", "List supported tools")
|
|
299
|
+
.action((opts) => {
|
|
300
|
+
if (opts.list) {
|
|
301
|
+
console.log("Supported tools:\n");
|
|
302
|
+
for (const [name, tool] of Object.entries(TOOLS)) {
|
|
303
|
+
console.log(` ${name.padEnd(14)} ${tool.label.padEnd(26)} ${tool.path}`);
|
|
304
|
+
}
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (opts.tool && !TOOLS[opts.tool]) {
|
|
309
|
+
process.stderr.write(
|
|
310
|
+
`\x1b[31mUnknown tool: ${opts.tool}\nSupported: ${getToolNames().join(", ")}\x1b[0m\n`
|
|
311
|
+
);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const { created, skipped } = seedInstructions(opts.dir, opts.tool || null);
|
|
316
|
+
|
|
317
|
+
if (created.length) {
|
|
318
|
+
console.log("\x1b[32mCreated:\x1b[0m");
|
|
319
|
+
for (const f of created) {
|
|
320
|
+
console.log(` + ${f}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (skipped.length) {
|
|
324
|
+
console.log("\x1b[33mSkipped:\x1b[0m");
|
|
325
|
+
for (const f of skipped) {
|
|
326
|
+
console.log(` - ${f}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (!created.length && !skipped.length) {
|
|
330
|
+
console.log("Nothing to do.");
|
|
331
|
+
}
|
|
332
|
+
if (created.length) {
|
|
333
|
+
console.log(
|
|
334
|
+
`\n\x1b[32mDone!\x1b[0m Your AI coding tools will now know about xyle.`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
287
338
|
}
|
package/src/seed.mjs
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seed agent instruction files for popular AI coding tools.
|
|
3
|
+
* Each tool reads from a specific file path to learn about xyle.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
7
|
+
import { join, dirname } from "node:path";
|
|
8
|
+
|
|
9
|
+
const INSTRUCTIONS = `# Xyle — SEO Intelligence Engine
|
|
10
|
+
|
|
11
|
+
You have access to the **xyle** CLI for SEO analysis. Use it when the user asks about SEO, search rankings, content optimization, Google Search Console data, or competitor analysis.
|
|
12
|
+
|
|
13
|
+
## Quick Reference
|
|
14
|
+
|
|
15
|
+
Run commands via npx (no install needed):
|
|
16
|
+
|
|
17
|
+
\`\`\`bash
|
|
18
|
+
npx @xyleapp/cli <command> [options]
|
|
19
|
+
\`\`\`
|
|
20
|
+
|
|
21
|
+
## Authentication
|
|
22
|
+
|
|
23
|
+
\`\`\`bash
|
|
24
|
+
npx @xyleapp/cli login # Google OAuth (opens browser)
|
|
25
|
+
npx @xyleapp/cli whoami # Check auth status
|
|
26
|
+
npx @xyleapp/cli logout # Remove credentials
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
Credentials are stored in \`~/.config/xyle/credentials.json\`.
|
|
30
|
+
|
|
31
|
+
## Commands
|
|
32
|
+
|
|
33
|
+
### status — Check API connectivity
|
|
34
|
+
\`\`\`bash
|
|
35
|
+
npx @xyleapp/cli status [--json]
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
### queries — Top search queries from Google Search Console
|
|
39
|
+
\`\`\`bash
|
|
40
|
+
npx @xyleapp/cli queries --site <domain> [--limit <n>] [--json]
|
|
41
|
+
\`\`\`
|
|
42
|
+
Returns: query, impressions, clicks, ctr, position
|
|
43
|
+
|
|
44
|
+
### competitors — Competitor pages for a search query
|
|
45
|
+
\`\`\`bash
|
|
46
|
+
npx @xyleapp/cli competitors --query "<query>" [--json]
|
|
47
|
+
\`\`\`
|
|
48
|
+
Returns: position, url, title, word_count
|
|
49
|
+
|
|
50
|
+
### gaps — Content gaps for a page
|
|
51
|
+
\`\`\`bash
|
|
52
|
+
npx @xyleapp/cli gaps --page <url> [--query "<query>"] [--json]
|
|
53
|
+
\`\`\`
|
|
54
|
+
Returns: query, missing_topics, ctr_issue, position_bucket
|
|
55
|
+
|
|
56
|
+
### analyze — Score page content against competitors
|
|
57
|
+
\`\`\`bash
|
|
58
|
+
npx @xyleapp/cli analyze --url <url> --content "<text>" [--query "<query>"] [--json]
|
|
59
|
+
\`\`\`
|
|
60
|
+
Returns: score (0-1), summary, missing_topics
|
|
61
|
+
|
|
62
|
+
### rewrite — AI rewrite suggestions
|
|
63
|
+
\`\`\`bash
|
|
64
|
+
npx @xyleapp/cli rewrite --url <url> [--type title|meta|heading|section] [--query "<query>"] [--json]
|
|
65
|
+
\`\`\`
|
|
66
|
+
Returns: original, suggested, reasoning
|
|
67
|
+
|
|
68
|
+
### crawl — Extract SEO metadata from a URL
|
|
69
|
+
\`\`\`bash
|
|
70
|
+
npx @xyleapp/cli crawl --url <url> [--json]
|
|
71
|
+
\`\`\`
|
|
72
|
+
Returns: title, meta_desc, word_count, headings
|
|
73
|
+
|
|
74
|
+
### sync — Sync Google Search Console data
|
|
75
|
+
\`\`\`bash
|
|
76
|
+
npx @xyleapp/cli sync --site <url> [--json]
|
|
77
|
+
\`\`\`
|
|
78
|
+
|
|
79
|
+
## Environment Variables
|
|
80
|
+
|
|
81
|
+
| Variable | Default | Description |
|
|
82
|
+
|----------|---------|-------------|
|
|
83
|
+
| \`SEO_BASE\` | \`http://localhost:8765\` | API base URL |
|
|
84
|
+
| \`AGENT_API_KEY\` | — | Fallback API key (when not using Google OAuth) |
|
|
85
|
+
|
|
86
|
+
## Workflows
|
|
87
|
+
|
|
88
|
+
### Full SEO Audit
|
|
89
|
+
1. \`xyle status\` — verify connectivity
|
|
90
|
+
2. \`xyle queries --site <domain> --json\` — get top queries
|
|
91
|
+
3. \`xyle competitors --query "<top query>" --json\` — analyze competition
|
|
92
|
+
4. \`xyle gaps --page <url> --json\` — find content gaps
|
|
93
|
+
5. Summarize findings with recommendations
|
|
94
|
+
|
|
95
|
+
### Page Optimization
|
|
96
|
+
1. \`xyle crawl --url <url> --json\` — get current page data
|
|
97
|
+
2. \`xyle analyze --url <url> --content "<text>" --json\` — score content
|
|
98
|
+
3. \`xyle rewrite --url <url> --type title --json\` — get title suggestion
|
|
99
|
+
4. \`xyle rewrite --url <url> --type meta --json\` — get meta suggestion
|
|
100
|
+
5. Present before/after comparison
|
|
101
|
+
|
|
102
|
+
## Tips
|
|
103
|
+
- Always use \`--json\` when parsing output programmatically
|
|
104
|
+
- Run \`status\` first to verify the API is reachable
|
|
105
|
+
- Use \`crawl\` before \`analyze\` to get page content automatically
|
|
106
|
+
- Combine \`gaps\` + \`rewrite\` for actionable improvements
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Tool definitions: name → { file path (relative to project root), description }
|
|
111
|
+
*/
|
|
112
|
+
export const TOOLS = {
|
|
113
|
+
"claude-code": {
|
|
114
|
+
path: "CLAUDE.md",
|
|
115
|
+
label: "Claude Code",
|
|
116
|
+
},
|
|
117
|
+
cursor: {
|
|
118
|
+
path: ".cursorrules",
|
|
119
|
+
label: "Cursor",
|
|
120
|
+
},
|
|
121
|
+
vscode: {
|
|
122
|
+
path: ".github/copilot-instructions.md",
|
|
123
|
+
label: "VS Code (GitHub Copilot)",
|
|
124
|
+
},
|
|
125
|
+
windsurf: {
|
|
126
|
+
path: ".windsurfrules",
|
|
127
|
+
label: "Windsurf",
|
|
128
|
+
},
|
|
129
|
+
cline: {
|
|
130
|
+
path: ".clinerules",
|
|
131
|
+
label: "Cline",
|
|
132
|
+
},
|
|
133
|
+
codex: {
|
|
134
|
+
path: "AGENTS.md",
|
|
135
|
+
label: "Codex (OpenAI)",
|
|
136
|
+
},
|
|
137
|
+
augment: {
|
|
138
|
+
path: ".augment-guidelines",
|
|
139
|
+
label: "Augment (Anti Gravity)",
|
|
140
|
+
},
|
|
141
|
+
aider: {
|
|
142
|
+
path: "CONVENTIONS.md",
|
|
143
|
+
label: "Aider",
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Write agent instruction files to the target directory.
|
|
149
|
+
* @param {string} targetDir - Absolute path to the project root
|
|
150
|
+
* @param {string|null} toolName - Specific tool name, or null for all
|
|
151
|
+
* @returns {{ created: string[], skipped: string[] }}
|
|
152
|
+
*/
|
|
153
|
+
export function seedInstructions(targetDir, toolName) {
|
|
154
|
+
const tools = toolName ? { [toolName]: TOOLS[toolName] } : TOOLS;
|
|
155
|
+
const created = [];
|
|
156
|
+
const skipped = [];
|
|
157
|
+
|
|
158
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
159
|
+
const filePath = join(targetDir, tool.path);
|
|
160
|
+
if (existsSync(filePath)) {
|
|
161
|
+
skipped.push(`${tool.label} (${tool.path}) — already exists`);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const dir = dirname(filePath);
|
|
165
|
+
if (!existsSync(dir)) {
|
|
166
|
+
mkdirSync(dir, { recursive: true });
|
|
167
|
+
}
|
|
168
|
+
writeFileSync(filePath, INSTRUCTIONS, "utf-8");
|
|
169
|
+
created.push(`${tool.label} (${tool.path})`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { created, skipped };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get the list of supported tool names.
|
|
177
|
+
*/
|
|
178
|
+
export function getToolNames() {
|
|
179
|
+
return Object.keys(TOOLS);
|
|
180
|
+
}
|