@ai-agent-tools/picgen 0.1.0-alpha.3 → 0.1.0-alpha.7

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 CHANGED
@@ -26,15 +26,21 @@ npm run build
26
26
  ## Install
27
27
 
28
28
  ```bash
29
+ node -v
30
+ npm -v
29
31
  npm install -g @ai-agent-tools/picgen
32
+ npx -y skills add ai-agent-tools/picgen --skill picgen -g -y --copy
33
+ picgen skill install codex
30
34
  picgen --help
31
35
  picgen quickstart
32
36
  ```
33
37
 
38
+ Use `npx -y skills add ...` for cross-agent skill installation when supported. `picgen skill install codex` is a Codex-only fallback that copies the bundled skill into `~/.codex/skills/picgen`.
39
+
34
40
  Agent trial prompt:
35
41
 
36
42
  ```text
37
- 请安装并体验 @ai-agent-tools/picgen:全局安装 npm install -g @ai-agent-tools/picgen,运行 picgen setup 配置,然后先 dry-run 预览,再确认生成一张测试图。如果我要用参考图,请使用 --reference <图片路径>。
43
+ 请帮我安装并配置 PicGen 生图工具。安装前请先检查这台电脑是否已经安装 Node.js 和 npm。如果没有安装,请先指导我安装 Node.js LTS 版本,并验证 node -v 和 npm -v 是否能正常显示版本号。然后执行 npm install -g @ai-agent-tools/picgen@latest 安装 CLI,再执行 npx -y skills add ai-agent-tools/picgen --skill picgen -g -y --copy 安装 PicGen skill。如果 skills 安装器不可用且当前是 Codex,请改用 picgen skill install codex。然后引导我配置 provider 和 API key,先预览生成方案,等我确认后再生成测试图。
38
44
  ```
39
45
 
40
46
  For first-user rollout, see [docs/release-alpha.md](./docs/release-alpha.md).
@@ -44,6 +50,8 @@ For first-user rollout, see [docs/release-alpha.md](./docs/release-alpha.md).
44
50
  ```bash
45
51
  picgen setup
46
52
  picgen quickstart
53
+ npx -y skills add ai-agent-tools/picgen --skill picgen -g -y --copy
54
+ picgen skill install codex
47
55
  picgen update check
48
56
  picgen doctor --json
49
57
  picgen create --dry-run "一张产品发布会主视觉"
@@ -52,6 +60,8 @@ picgen create --dry-run --provider gemini_official --reference ./reference.png "
52
60
  picgen create --yes --provider gemini_official --reference ./reference.png "基于参考图生成一张品牌海报"
53
61
  picgen provider list
54
62
  picgen provider add
63
+ picgen provider quick-add gemini-proxy --host https://www.pandai.vip --prefer
64
+ picgen key set PICGEN_GEMINI_PROXY_KEY --stdin
55
65
  picgen provider test openai_official --json
56
66
  picgen provider prefer gemini_official
57
67
  picgen provider disable gemini_proxy
@@ -91,6 +101,14 @@ OPENAI_API_KEY=...
91
101
  GEMINI_API_KEY=...
92
102
  ```
93
103
 
104
+ For non-technical users, `picgen setup` can save API keys for you in:
105
+
106
+ ```text
107
+ ~/.picgen/.env
108
+ ```
109
+
110
+ PicGen loads this managed env file automatically. Shell environment variables take priority, and a project `.env` can override the managed file for local testing.
111
+
94
112
  You can start from the included example:
95
113
 
96
114
  ```bash
package/dist/cli.js CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import "dotenv/config";
5
4
  import { Command } from "commander";
6
5
 
7
6
  // src/commands/create.ts
@@ -1055,7 +1054,7 @@ import { homedir as homedir2 } from "os";
1055
1054
 
1056
1055
  // src/version.ts
1057
1056
  var PACKAGE_NAME = "@ai-agent-tools/picgen";
1058
- var VERSION = "0.1.0-alpha.3";
1057
+ var VERSION = "0.1.0-alpha.7";
1059
1058
 
1060
1059
  // src/commands/update.ts
1061
1060
  var UPDATE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -1226,6 +1225,88 @@ async function runDoctor(options) {
1226
1225
  await maybePrintUpdateHint();
1227
1226
  }
1228
1227
 
1228
+ // src/commands/key.ts
1229
+ import { password } from "@inquirer/prompts";
1230
+
1231
+ // src/config/env.ts
1232
+ import { chmod, mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
1233
+ import { existsSync } from "fs";
1234
+ import { dirname as dirname3, join as join5, resolve as resolve2 } from "path";
1235
+ import { homedir as homedir3 } from "os";
1236
+ import { parse } from "dotenv";
1237
+ function getManagedEnvPath() {
1238
+ return process.env.PICGEN_ENV_PATH ?? join5(homedir3(), ".picgen", ".env");
1239
+ }
1240
+ async function loadPicgenEnv() {
1241
+ const shellEnv = new Set(Object.keys(process.env));
1242
+ await loadEnvFile(getManagedEnvPath(), shellEnv, false);
1243
+ await loadEnvFile(resolve2(process.cwd(), ".env"), shellEnv, true);
1244
+ }
1245
+ async function saveManagedEnvVar(name, value) {
1246
+ const path = getManagedEnvPath();
1247
+ const current = await readManagedEnvFile(path);
1248
+ const next = {
1249
+ ...current,
1250
+ [name]: value
1251
+ };
1252
+ await mkdir4(dirname3(path), { recursive: true });
1253
+ await writeFile4(path, stringifyEnv(next), "utf8");
1254
+ await chmod(path, 384);
1255
+ process.env[name] = value;
1256
+ return path;
1257
+ }
1258
+ async function loadEnvFile(path, shellEnv, overrideManagedValues) {
1259
+ if (!existsSync(path)) return;
1260
+ const parsed = parse(await readFile4(path, "utf8"));
1261
+ for (const [name, value] of Object.entries(parsed)) {
1262
+ if (shellEnv.has(name)) continue;
1263
+ if (!overrideManagedValues && process.env[name] !== void 0) continue;
1264
+ process.env[name] = value;
1265
+ }
1266
+ }
1267
+ async function readManagedEnvFile(path) {
1268
+ try {
1269
+ return parse(await readFile4(path, "utf8"));
1270
+ } catch (error) {
1271
+ if (error.code === "ENOENT") return {};
1272
+ throw error;
1273
+ }
1274
+ }
1275
+ function stringifyEnv(values) {
1276
+ return `${Object.entries(values).map(([name, value]) => `${name}=${quoteEnvValue(value)}`).join("\n")}
1277
+ `;
1278
+ }
1279
+ function quoteEnvValue(value) {
1280
+ if (/^[A-Za-z0-9_./:@+-]+$/.test(value)) return value;
1281
+ return JSON.stringify(value);
1282
+ }
1283
+
1284
+ // src/commands/key.ts
1285
+ async function setApiKey(name, options) {
1286
+ validateEnvName(name);
1287
+ const value = options.stdin ? await readStdin() : options.value ? options.value : await password({
1288
+ message: `Paste API key for ${name}`,
1289
+ mask: "*"
1290
+ });
1291
+ if (!value.trim()) {
1292
+ throw new Error("API key is empty.");
1293
+ }
1294
+ const path = await saveManagedEnvVar(name, value.trim());
1295
+ console.log(`Saved ${name} to ${path}`);
1296
+ }
1297
+ function validateEnvName(name) {
1298
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
1299
+ throw new Error(`Invalid environment variable name: ${name}`);
1300
+ }
1301
+ }
1302
+ async function readStdin() {
1303
+ const chunks = [];
1304
+ for await (const chunk of process.stdin) {
1305
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1306
+ }
1307
+ return Buffer.concat(chunks).toString("utf8");
1308
+ }
1309
+
1229
1310
  // src/commands/provider.ts
1230
1311
  import { input, select } from "@inquirer/prompts";
1231
1312
 
@@ -1371,6 +1452,31 @@ async function addProvider() {
1371
1452
  await saveConfig(config);
1372
1453
  console.log(`Added provider: ${provider2.name}`);
1373
1454
  }
1455
+ async function quickAddProvider(templateName, options) {
1456
+ const config = await loadConfig();
1457
+ const template = quickProviderTemplate(templateName);
1458
+ const name = options.name ?? nextAvailableProviderName(config, template.name);
1459
+ const provider2 = {
1460
+ enabled: true,
1461
+ protocol: template.protocol,
1462
+ channel: template.channel,
1463
+ base_url: normalizeProviderBaseUrl(options.host ?? template.base_url),
1464
+ api_key_env: options.keyEnv ?? template.api_key_env,
1465
+ models: parseModels(options.models ?? template.models.join(",")),
1466
+ capabilities: defaultCapabilitiesForProtocol2(template.protocol)
1467
+ };
1468
+ addProviderToConfig(config, name, provider2);
1469
+ if (options.prefer) {
1470
+ setPreferredProvider(config, name);
1471
+ }
1472
+ await saveConfig(config);
1473
+ console.log(`Added provider: ${name}`);
1474
+ console.log(`Protocol: ${provider2.protocol}`);
1475
+ console.log(`Host: ${provider2.base_url}`);
1476
+ console.log(`API key env: ${provider2.api_key_env}`);
1477
+ console.log(`Models: ${provider2.models.join(",")}`);
1478
+ if (options.prefer) console.log(`Preferred provider: ${name}`);
1479
+ }
1374
1480
  function addProviderToConfig(config, name, provider2) {
1375
1481
  config.providers[name] = provider2;
1376
1482
  const knownProviders = [config.routing.default_provider, ...config.routing.fallback_providers];
@@ -1497,6 +1603,53 @@ function nextAvailableProviderName(config, baseName, existingName) {
1497
1603
  function defaultCapabilitiesForProtocol2(protocol) {
1498
1604
  return protocol === "gemini" ? ["text-to-image", "reference-image"] : ["text-to-image"];
1499
1605
  }
1606
+ function quickProviderTemplate(templateName) {
1607
+ switch (templateName.replaceAll("_", "-")) {
1608
+ case "openai-proxy":
1609
+ return {
1610
+ name: "openai_proxy",
1611
+ protocol: "openai-images",
1612
+ channel: "third_party",
1613
+ base_url: "https://www.pandai.vip",
1614
+ api_key_env: "PICGEN_OPENAI_PROXY_KEY",
1615
+ models: ["gpt-image-2"]
1616
+ };
1617
+ case "gemini-proxy":
1618
+ return {
1619
+ name: "gemini_proxy",
1620
+ protocol: "gemini",
1621
+ channel: "third_party",
1622
+ base_url: "https://www.pandai.vip",
1623
+ api_key_env: "PICGEN_GEMINI_PROXY_KEY",
1624
+ models: ["gemini-3.1-flash-image-preview", "gemini-3-pro-image-preview"]
1625
+ };
1626
+ case "openai-official":
1627
+ return {
1628
+ name: "openai_official",
1629
+ protocol: "openai-images",
1630
+ channel: "official",
1631
+ base_url: defaultProviderBaseUrl("openai-images"),
1632
+ api_key_env: "OPENAI_API_KEY",
1633
+ models: ["gpt-image-2"]
1634
+ };
1635
+ case "gemini-official":
1636
+ return {
1637
+ name: "gemini_official",
1638
+ protocol: "gemini",
1639
+ channel: "official",
1640
+ base_url: defaultProviderBaseUrl("gemini"),
1641
+ api_key_env: "GEMINI_API_KEY",
1642
+ models: ["gemini-3.1-flash-image-preview", "gemini-3-pro-image-preview"]
1643
+ };
1644
+ default:
1645
+ throw new Error(
1646
+ `Unknown provider template: ${templateName}. Use openai-proxy, gemini-proxy, openai-official, or gemini-official.`
1647
+ );
1648
+ }
1649
+ }
1650
+ function parseModels(raw) {
1651
+ return raw.split(",").map((model) => model.trim()).filter(Boolean);
1652
+ }
1500
1653
 
1501
1654
  // src/commands/preferences.ts
1502
1655
  async function preferMode(name) {
@@ -1522,10 +1675,16 @@ function formatQuickstart() {
1522
1675
  "PicGen quickstart",
1523
1676
  "",
1524
1677
  "Install:",
1678
+ " node -v",
1679
+ " npm -v",
1525
1680
  " npm install -g @ai-agent-tools/picgen",
1681
+ " npx -y skills add ai-agent-tools/picgen --skill picgen -g -y --copy",
1682
+ " picgen skill install codex",
1526
1683
  "",
1527
1684
  "Configure:",
1528
- " picgen setup",
1685
+ " picgen setup # can save provider API keys for you",
1686
+ " picgen provider quick-add gemini-proxy --host https://www.pandai.vip --prefer",
1687
+ " picgen key set PICGEN_GEMINI_PROXY_KEY --stdin",
1529
1688
  " picgen doctor --json",
1530
1689
  "",
1531
1690
  "Preview before spending quota:",
@@ -1538,10 +1697,11 @@ function formatQuickstart() {
1538
1697
  ' picgen create --dry-run --reference ./reference.png "\u57FA\u4E8E\u53C2\u8003\u56FE\u751F\u6210\u4E00\u5F20\u54C1\u724C\u6D77\u62A5"',
1539
1698
  "",
1540
1699
  "Agent prompt:",
1541
- " \u8BF7\u5B89\u88C5\u5E76\u4F53\u9A8C @ai-agent-tools/picgen\uFF1A\u5168\u5C40\u5B89\u88C5 npm install -g @ai-agent-tools/picgen\uFF0C\u8FD0\u884C picgen setup \u914D\u7F6E\uFF0C\u7136\u540E\u5148 dry-run \u9884\u89C8\uFF0C\u518D\u786E\u8BA4\u751F\u6210\u4E00\u5F20\u6D4B\u8BD5\u56FE\u3002\u5982\u679C\u6211\u8981\u7528\u53C2\u8003\u56FE\uFF0C\u8BF7\u4F7F\u7528 --reference <\u56FE\u7247\u8DEF\u5F84>\u3002",
1700
+ " \u8BF7\u5E2E\u6211\u5B89\u88C5\u5E76\u914D\u7F6E PicGen \u751F\u56FE\u5DE5\u5177\u3002\u5B89\u88C5\u524D\u8BF7\u5148\u68C0\u67E5\u8FD9\u53F0\u7535\u8111\u662F\u5426\u5DF2\u7ECF\u5B89\u88C5 Node.js \u548C npm\u3002\u5982\u679C\u6CA1\u6709\u5B89\u88C5\uFF0C\u8BF7\u5148\u6307\u5BFC\u6211\u5B89\u88C5 Node.js LTS \u7248\u672C\uFF0C\u5E76\u9A8C\u8BC1 node -v \u548C npm -v \u662F\u5426\u80FD\u6B63\u5E38\u663E\u793A\u7248\u672C\u53F7\u3002\u7136\u540E\u6267\u884C npm install -g @ai-agent-tools/picgen@latest \u5B89\u88C5 CLI\uFF0C\u518D\u6267\u884C npx -y skills add ai-agent-tools/picgen --skill picgen -g -y --copy \u5B89\u88C5 PicGen skill\u3002\u5982\u679C skills \u5B89\u88C5\u5668\u4E0D\u53EF\u7528\u4E14\u5F53\u524D\u662F Codex\uFF0C\u8BF7\u6539\u7528 picgen skill install codex\u3002\u7136\u540E\u5F15\u5BFC\u6211\u914D\u7F6E provider \u548C API key\uFF0C\u5148\u9884\u89C8\u751F\u6210\u65B9\u6848\uFF0C\u7B49\u6211\u786E\u8BA4\u540E\u518D\u751F\u6210\u6D4B\u8BD5\u56FE\u3002",
1542
1701
  "",
1543
1702
  "Notes:",
1544
1703
  " - Provider host URLs should not include /v1 or /v1beta.",
1704
+ " - picgen setup can store API keys in ~/.picgen/.env.",
1545
1705
  " - Agent workflows should dry-run before real generation.",
1546
1706
  " - Generated images are saved locally; do not paste base64 into chat.",
1547
1707
  " - First-user rollout checklist: docs/release-alpha.md"
@@ -1549,7 +1709,7 @@ function formatQuickstart() {
1549
1709
  }
1550
1710
 
1551
1711
  // src/commands/setup.ts
1552
- import { confirm as confirm2, input as input2, select as select2 } from "@inquirer/prompts";
1712
+ import { confirm as confirm2, input as input2, password as password2, select as select2 } from "@inquirer/prompts";
1553
1713
  async function runSetup() {
1554
1714
  await ensureConfig();
1555
1715
  console.log(`PicGen config: ${getConfigPath()}`);
@@ -1564,17 +1724,20 @@ async function runSetup() {
1564
1724
  { name: "Quick add a common provider/channel", value: "quick-add" },
1565
1725
  { name: "Choose default provider/channel", value: "provider" },
1566
1726
  { name: "Choose generation preference", value: "mode" },
1727
+ { name: "Configure API key", value: "key" },
1567
1728
  { name: "Test a provider", value: "test" },
1568
1729
  { name: "Advanced: add a custom provider/channel", value: "add" },
1569
1730
  { name: "Finish setup", value: "done" }
1570
1731
  ]
1571
1732
  });
1572
1733
  if (action === "quick-add") {
1573
- await quickAddProvider();
1734
+ await quickAddProvider2();
1574
1735
  } else if (action === "provider") {
1575
1736
  await chooseDefaultProvider();
1576
1737
  } else if (action === "mode") {
1577
1738
  await chooseDefaultMode();
1739
+ } else if (action === "key") {
1740
+ await chooseProviderKeyToConfigure();
1578
1741
  } else if (action === "test") {
1579
1742
  await chooseProviderToTest();
1580
1743
  } else if (action === "add") {
@@ -1593,7 +1756,7 @@ async function printSetupSummary() {
1593
1756
  for (const [providerName, provider2] of Object.entries(config.providers)) {
1594
1757
  const preference = providerName === config.routing.default_provider ? "default" : config.routing.fallback_providers.includes(providerName) ? "fallback" : "manual";
1595
1758
  console.log(
1596
- `- ${providerName}: ${provider2.enabled ? "enabled" : "disabled"}, ${preference}, ${providerLabel(provider2)}, capabilities=${provider2.capabilities.join(",")}`
1759
+ `- ${providerName}: ${provider2.enabled ? "enabled" : "disabled"}, ${preference}, ${providerLabel(provider2)}, key=${provider2.api_key_env}${process.env[provider2.api_key_env] ? " set" : " missing"}, capabilities=${provider2.capabilities.join(",")}`
1597
1760
  );
1598
1761
  }
1599
1762
  }
@@ -1641,7 +1804,19 @@ async function chooseProviderToTest() {
1641
1804
  if (result.model) console.log(`Model: ${result.model}`);
1642
1805
  if (result.http_status) console.log(`HTTP status: ${result.http_status}`);
1643
1806
  }
1644
- async function quickAddProvider() {
1807
+ async function chooseProviderKeyToConfigure() {
1808
+ const config = await loadConfig();
1809
+ const name = await select2({
1810
+ message: "Choose the provider key to configure",
1811
+ default: config.routing.default_provider,
1812
+ choices: Object.entries(config.providers).map(([providerName, provider2]) => ({
1813
+ name: `${providerName} (${provider2.api_key_env}${process.env[provider2.api_key_env] ? ", currently set" : ", missing"})`,
1814
+ value: providerName
1815
+ }))
1816
+ });
1817
+ await configureProviderApiKey(config.providers[name]);
1818
+ }
1819
+ async function quickAddProvider2() {
1645
1820
  const config = await loadConfig();
1646
1821
  const template = await select2({
1647
1822
  message: "Choose the provider/channel you want to add",
@@ -1687,7 +1862,7 @@ async function quickAddProvider() {
1687
1862
  channel: defaults.channel,
1688
1863
  base_url: normalizeProviderBaseUrl(baseUrl),
1689
1864
  api_key_env: apiKeyEnv,
1690
- models: parseModels(modelsRaw),
1865
+ models: parseModels2(modelsRaw),
1691
1866
  capabilities: defaultCapabilitiesForProtocol2(defaults.protocol)
1692
1867
  };
1693
1868
  addProviderToConfig(config, name, provider2);
@@ -1700,7 +1875,27 @@ async function quickAddProvider() {
1700
1875
  }
1701
1876
  await saveConfig(config);
1702
1877
  console.log(`Added provider: ${name}`);
1703
- console.log(`Set ${apiKeyEnv} in your shell or .env before testing this provider.`);
1878
+ await configureProviderApiKey(provider2);
1879
+ }
1880
+ async function configureProviderApiKey(provider2) {
1881
+ if (process.env[provider2.api_key_env]) {
1882
+ const replace = await confirm2({
1883
+ message: `${provider2.api_key_env} is already available. Replace the saved PicGen key?`,
1884
+ default: false
1885
+ });
1886
+ if (!replace) return;
1887
+ }
1888
+ const value = await password2({
1889
+ message: `Paste API key for ${provider2.api_key_env} (leave empty to skip)`,
1890
+ mask: "*"
1891
+ });
1892
+ if (!value.trim()) {
1893
+ console.log(`Skipped API key. You can configure it later with picgen setup.`);
1894
+ return;
1895
+ }
1896
+ const path = await saveManagedEnvVar(provider2.api_key_env, value.trim());
1897
+ console.log(`Saved ${provider2.api_key_env} to ${path}`);
1898
+ console.log(`PicGen loads this file automatically. Advanced users can override it with shell env vars or a project .env.`);
1704
1899
  }
1705
1900
  function quickProviderDefaults(template) {
1706
1901
  switch (template) {
@@ -1742,7 +1937,7 @@ function quickProviderDefaults(template) {
1742
1937
  };
1743
1938
  }
1744
1939
  }
1745
- function parseModels(raw) {
1940
+ function parseModels2(raw) {
1746
1941
  return raw.split(",").map((model) => model.trim()).filter(Boolean);
1747
1942
  }
1748
1943
  function providerLabel(provider2) {
@@ -1764,7 +1959,49 @@ function modeLabel(modeName) {
1764
1959
  }
1765
1960
  }
1766
1961
 
1962
+ // src/commands/skill.ts
1963
+ import { access, cp, mkdir as mkdir5 } from "fs/promises";
1964
+ import { constants } from "fs";
1965
+ import { dirname as dirname4, join as join6, resolve as resolve3 } from "path";
1966
+ import { homedir as homedir4 } from "os";
1967
+ import { fileURLToPath } from "url";
1968
+ async function installSkill(target, options) {
1969
+ if (target !== "codex") {
1970
+ throw new Error(`Unsupported skill target: ${target}. Supported target: codex.`);
1971
+ }
1972
+ const source = await findBundledPicgenSkill();
1973
+ const destination = join6(getCodexHome(), "skills", "picgen");
1974
+ await mkdir5(dirname4(destination), { recursive: true });
1975
+ await cp(source, destination, {
1976
+ recursive: true,
1977
+ force: options.force ?? false,
1978
+ errorOnExist: !(options.force ?? false)
1979
+ });
1980
+ console.log(`Installed PicGen skill for Codex: ${destination}`);
1981
+ console.log("Restart Codex or start a new Codex session if the skill is not visible yet.");
1982
+ }
1983
+ function getCodexHome() {
1984
+ return process.env.CODEX_HOME ?? join6(homedir4(), ".codex");
1985
+ }
1986
+ async function findBundledPicgenSkill() {
1987
+ const here = dirname4(fileURLToPath(import.meta.url));
1988
+ const candidates = [
1989
+ resolve3(here, "../skills/picgen"),
1990
+ resolve3(here, "../../skills/picgen"),
1991
+ resolve3(process.cwd(), "skills/picgen")
1992
+ ];
1993
+ for (const candidate of candidates) {
1994
+ try {
1995
+ await access(join6(candidate, "SKILL.md"), constants.R_OK);
1996
+ return candidate;
1997
+ } catch {
1998
+ }
1999
+ }
2000
+ throw new Error("Bundled PicGen skill not found. Reinstall @ai-agent-tools/picgen and try again.");
2001
+ }
2002
+
1767
2003
  // src/cli.ts
2004
+ await loadPicgenEnv();
1768
2005
  var program = new Command();
1769
2006
  program.name("picgen").description("Lightweight image generation connector for AI agents.").version(VERSION);
1770
2007
  program.command("setup").description("Run the interactive PicGen setup wizard.").action(runSetup);
@@ -1779,12 +2016,15 @@ program.command("create").description("Create an image generation plan or genera
1779
2016
  var provider = program.command("provider").description("Manage providers/channels.");
1780
2017
  provider.command("list").description("List providers.").action(listProviders);
1781
2018
  provider.command("add").description("Add a provider.").action(addProvider);
2019
+ provider.command("quick-add").argument("<template>", "openai-proxy, gemini-proxy, openai-official, or gemini-official").description("Add a common provider/channel without interactive prompts.").option("--name <name>", "Provider name.").option("--host <url>", "Provider host URL. Do not include /v1 or /v1beta.").option("--key-env <name>", "API key environment variable.").option("--models <models>", "Comma-separated model list.").option("--prefer", "Use this provider as the default.").action(quickAddProvider);
1782
2020
  provider.command("edit").argument("<name>").description("Edit a provider.").action(editProvider);
1783
2021
  provider.command("test").argument("<name>").description("Test provider connectivity without generating an image.").option("--json", "Print machine-readable JSON.").action(runProviderTest);
1784
2022
  provider.command("prefer").argument("<name>").description("Set the default provider preference.").action(preferProvider);
1785
2023
  provider.command("enable").argument("<name>").description("Enable a provider.").action((name) => setProviderEnabled(name, true));
1786
2024
  provider.command("disable").argument("<name>").description("Disable a provider.").action((name) => setProviderEnabled(name, false));
1787
2025
  provider.command("remove").argument("<name>").description("Remove a provider.").action(removeProvider);
2026
+ program.command("key").description("Manage PicGen API keys.").command("set").argument("<env-name>", "Environment variable name, such as PICGEN_GEMINI_PROXY_KEY.").description("Save an API key to PicGen's managed env file.").option("--stdin", "Read the key value from stdin.").option("--value <value>", "Set the key value directly. Prefer --stdin for agent workflows.").action(setApiKey);
2027
+ program.command("skill").description("Install PicGen agent skills.").command("install").argument("<target>", "Skill target. Currently supported: codex.").description("Install the bundled PicGen skill into an agent skill directory.").option("--force", "Overwrite an existing installed skill.").action(installSkill);
1788
2028
  program.command("mode").description("Manage generation mode preferences.").command("prefer").argument("<name>").description("Set the default mode preference.").action(preferMode);
1789
2029
  program.command("preset").description("Manage generation preset preferences.").command("prefer").argument("<name>").description("Set the default preset preference.").action(preferPreset);
1790
2030
  program.command("update").description("Manage PicGen updates.").command("check").description("Check whether a newer PicGen version is available.").option("--json", "Print machine-readable JSON.").action(runUpdateCheck);
@@ -5,19 +5,25 @@ This checklist is for the first internal or friend-and-colleague trial of PicGen
5
5
  ## Install
6
6
 
7
7
  ```bash
8
+ node -v
9
+ npm -v
8
10
  npm install -g @ai-agent-tools/picgen
11
+ npx -y skills add ai-agent-tools/picgen --skill picgen -g -y --copy
12
+ picgen skill install codex
9
13
  picgen --help
10
14
  picgen quickstart
11
15
  ```
12
16
 
13
17
  Node.js 20 or newer is required.
14
18
 
19
+ `npx -y skills add ...` is the preferred cross-agent skill installation path when supported. `picgen skill install codex` installs the bundled PicGen skill into `~/.codex/skills/picgen` as a Codex-only fallback. Restart the agent or start a new session if the skill is not visible yet.
20
+
15
21
  ## Agent Prompt
16
22
 
17
23
  Send this to Codex, Trae, Claude Code, or a similar coding agent:
18
24
 
19
25
  ```text
20
- 请安装并体验 @ai-agent-tools/picgen:全局安装 npm install -g @ai-agent-tools/picgen,运行 picgen setup 配置,然后先 dry-run 预览,再确认生成一张测试图。如果我要用参考图,请使用 --reference <图片路径>。
26
+ 请帮我安装并配置 PicGen 生图工具。安装前请先检查这台电脑是否已经安装 Node.js 和 npm。如果没有安装,请先指导我安装 Node.js LTS 版本,并验证 node -v 和 npm -v 是否能正常显示版本号。然后执行 npm install -g @ai-agent-tools/picgen@latest 安装 CLI,再执行 npx -y skills add ai-agent-tools/picgen --skill picgen -g -y --copy 安装 PicGen skill。如果 skills 安装器不可用且当前是 Codex,请改用 picgen skill install codex。然后引导我配置 provider 和 API key,先预览生成方案,等我确认后再生成测试图。
21
27
  ```
22
28
 
23
29
  ## First Run
@@ -40,7 +46,25 @@ https://generativelanguage.googleapis.com
40
46
 
41
47
  Do not include `/v1` or `/v1beta`.
42
48
 
43
- 4. Set API keys in the shell or a local `.env` file:
49
+ 4. Configure API keys:
50
+
51
+ For non-technical users, prefer `picgen setup`. It can save provider API keys in PicGen's managed env file:
52
+
53
+ ```text
54
+ ~/.picgen/.env
55
+ ```
56
+
57
+ PicGen loads this file automatically.
58
+
59
+ In agent environments where interactive terminal prompts are not visible, ask the user for provider type, host, and API key in chat, then use non-interactive commands. Example for a Gemini-compatible third-party channel:
60
+
61
+ ```bash
62
+ picgen provider quick-add gemini-proxy --host https://www.pandai.vip --prefer
63
+ picgen key set PICGEN_GEMINI_PROXY_KEY --stdin
64
+ picgen provider test gemini_proxy --json
65
+ ```
66
+
67
+ Advanced users can still use shell environment variables or a local project `.env`:
44
68
 
45
69
  ```bash
46
70
  cp .env.example .env
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-agent-tools/picgen",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.7",
4
4
  "description": "A lightweight image generation connector for AI agents.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,10 +17,32 @@ Only suggest PicGen when the user discusses visual direction, mood, brand style,
17
17
 
18
18
  Never silently spend user quota. Do not send full conversation context to providers by default; summarize only the visual details needed for the final prompt.
19
19
 
20
+ ## Installation
21
+
22
+ If `picgen` is not available, install the CLI first:
23
+
24
+ ```bash
25
+ npm install -g @ai-agent-tools/picgen@latest
26
+ ```
27
+
28
+ To install or update this skill for supported agents, prefer the standard Skills installer:
29
+
30
+ ```bash
31
+ npx -y skills add ai-agent-tools/picgen --skill picgen -g -y --copy
32
+ ```
33
+
34
+ For Codex only, the CLI also provides a direct fallback:
35
+
36
+ ```bash
37
+ picgen skill install codex --force
38
+ ```
39
+
40
+ After installing or updating a skill, the user may need to restart the agent or open a new session before the skill is visible.
41
+
20
42
  ## Workflow
21
43
 
22
44
  1. Run `picgen doctor --json` to check configuration.
23
- 2. If no usable provider is configured, guide the user to run `picgen setup`.
45
+ 2. If no usable provider is configured, configure one before generation.
24
46
  3. Choose a preset from the user's intent, such as `poster`, `product-shot`, or `social-cover`.
25
47
  4. Run `picgen create --dry-run --preset <preset> "<prompt>"`.
26
48
  5. Present the dry-run as a user-facing generation preview. Do not expose `dry-run` as a technical term unless useful.
@@ -29,6 +51,36 @@ Never silently spend user quota. Do not send full conversation context to provid
29
51
 
30
52
  If the user explicitly says to generate directly or not ask for confirmation, you may skip the user-facing confirmation step. Still form a generation plan internally.
31
53
 
54
+ ## Provider Setup
55
+
56
+ When terminal prompts are visible to the user, `picgen setup` is acceptable.
57
+
58
+ When running inside an agent environment where interactive terminal prompts are not visible, do not run `picgen setup` as a blocking wizard. Ask the user for:
59
+
60
+ - Provider type: Gemini or OpenAI-compatible.
61
+ - Provider host: host only, such as `https://www.pandai.vip`; do not include `/v1` or `/v1beta`.
62
+ - API key.
63
+
64
+ Then use non-interactive commands.
65
+
66
+ Gemini-compatible third-party channel:
67
+
68
+ ```bash
69
+ picgen provider quick-add gemini-proxy --host https://www.pandai.vip --prefer
70
+ picgen key set PICGEN_GEMINI_PROXY_KEY --stdin
71
+ picgen provider test gemini_proxy --json
72
+ ```
73
+
74
+ OpenAI-compatible third-party channel:
75
+
76
+ ```bash
77
+ picgen provider quick-add openai-proxy --host https://www.pandai.vip --prefer
78
+ picgen key set PICGEN_OPENAI_PROXY_KEY --stdin
79
+ picgen provider test openai_proxy --json
80
+ ```
81
+
82
+ Pass the API key through stdin for `picgen key set`; do not put secrets directly in shell history unless the user explicitly accepts that tradeoff. If the agent runtime cannot pass stdin safely, ask the user to run `picgen key set <ENV_NAME>` in their terminal and paste the key into the hidden prompt.
83
+
32
84
  For reference-image generation, pass local images with repeated `--reference <path>` flags:
33
85
 
34
86
  ```bash
@@ -73,9 +125,9 @@ PicGen redacts generated image payloads and Gemini thought signatures from metad
73
125
 
74
126
  ## Error Handling
75
127
 
76
- If `doctor` reports no usable provider, ask the user to run `picgen setup`.
128
+ If `doctor` reports no usable provider, configure a provider. Prefer non-interactive setup in agent environments.
77
129
 
78
- If an API key is missing, name the required environment variable.
130
+ If an API key is missing, save it with `picgen key set <ENV_NAME> --stdin` or guide the user to run `picgen setup` when interactive prompts are visible. Name the required environment variable only when useful for debugging.
79
131
 
80
132
  If a provider is disabled, suggest enabling it or using a one-off provider override.
81
133