@looplia/looplia-cli 0.6.7 → 0.6.9

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/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ var getDirname = () => path.dirname(getFilename());
13
13
  var __dirname = /* @__PURE__ */ getDirname();
14
14
 
15
15
  // src/commands/build.ts
16
- import { existsSync as existsSync4, mkdirSync as mkdirSync5 } from "fs";
16
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
17
17
  import { homedir as homedir5 } from "os";
18
18
  import { resolve as resolve2 } from "path";
19
19
 
@@ -243,6 +243,13 @@ async function getPluginPaths() {
243
243
  return await getProdPluginPaths();
244
244
  }
245
245
 
246
+ // ../../packages/provider/dist/chunk-5W3I2MT7.js
247
+ import { execSync } from "child_process";
248
+ import { existsSync as existsSync3 } from "fs";
249
+ import { createRequire } from "module";
250
+ import { homedir as homedir3 } from "os";
251
+ import { dirname as dirname3, join as join4 } from "path";
252
+
246
253
  // ../../node_modules/zod-to-json-schema/dist/esm/Options.js
247
254
  var ignoreOverride = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use");
248
255
  var defaultOptions = {
@@ -5568,7 +5575,7 @@ var zodToJsonSchema = (schema, options) => {
5568
5575
  return combined;
5569
5576
  };
5570
5577
 
5571
- // ../../node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
5578
+ // ../../packages/provider/node_modules/@anthropic-ai/claude-agent-sdk/sdk.mjs
5572
5579
  import { join as join5 } from "path";
5573
5580
  import { fileURLToPath as fileURLToPath23 } from "url";
5574
5581
  import { setMaxListeners } from "events";
@@ -27363,9 +27370,9 @@ function query({
27363
27370
  return queryInstance;
27364
27371
  }
27365
27372
 
27366
- // ../../packages/provider/dist/chunk-MM4XVM5O.js
27373
+ // ../../packages/provider/dist/chunk-5W3I2MT7.js
27367
27374
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
27368
- import { join as join4 } from "path";
27375
+ import { join as join23 } from "path";
27369
27376
  import {
27370
27377
  chmod,
27371
27378
  mkdir as mkdir2,
@@ -27374,11 +27381,74 @@ import {
27374
27381
  rm as rm2,
27375
27382
  writeFile as writeFile2
27376
27383
  } from "fs/promises";
27377
- import { homedir as homedir3 } from "os";
27378
- import { join as join23 } from "path";
27379
- import { cp as cp2, mkdir as mkdir22, readFile as readFile22, rm as rm22, stat as stat2, writeFile as writeFile22 } from "fs/promises";
27380
27384
  import { homedir as homedir22 } from "os";
27381
- import { isAbsolute, join as join32, normalize, resolve } from "path";
27385
+ import { join as join32 } from "path";
27386
+ import { cp as cp2, mkdir as mkdir22, readFile as readFile22, rm as rm22, stat as stat2, writeFile as writeFile22 } from "fs/promises";
27387
+ import { homedir as homedir32 } from "os";
27388
+ import { isAbsolute, join as join42, normalize, resolve } from "path";
27389
+ var LINE_SPLIT_REGEX = /\r?\n/;
27390
+ function findSdkBundledCliPath() {
27391
+ try {
27392
+ const require2 = createRequire(import.meta.url);
27393
+ const sdkPackagePath = require2.resolve(
27394
+ "@anthropic-ai/claude-agent-sdk/package.json"
27395
+ );
27396
+ const sdkDir = dirname3(sdkPackagePath);
27397
+ const cliPath = join4(sdkDir, "cli.js");
27398
+ if (existsSync3(cliPath)) {
27399
+ return cliPath;
27400
+ }
27401
+ } catch {
27402
+ }
27403
+ return;
27404
+ }
27405
+ var CLAUDE_CODE_PATHS = [
27406
+ // User's local bin (npm global install location)
27407
+ join4(homedir3(), ".local", "bin", "claude"),
27408
+ // System-wide installations
27409
+ "/usr/local/bin/claude",
27410
+ // macOS Homebrew
27411
+ "/opt/homebrew/bin/claude",
27412
+ // Windows (if applicable)
27413
+ join4(homedir3(), "AppData", "Local", "Programs", "claude", "claude.exe")
27414
+ ];
27415
+ var cachedClaudeCodePath;
27416
+ function findClaudeCodePath() {
27417
+ if (cachedClaudeCodePath !== void 0) {
27418
+ return cachedClaudeCodePath ?? void 0;
27419
+ }
27420
+ const envPath = process.env.CLAUDE_CODE_PATH;
27421
+ if (envPath && existsSync3(envPath)) {
27422
+ cachedClaudeCodePath = envPath;
27423
+ return cachedClaudeCodePath;
27424
+ }
27425
+ for (const path3 of CLAUDE_CODE_PATHS) {
27426
+ if (existsSync3(path3)) {
27427
+ cachedClaudeCodePath = path3;
27428
+ return cachedClaudeCodePath;
27429
+ }
27430
+ }
27431
+ try {
27432
+ const pathLookupCommand = process.platform === "win32" ? "where claude" : "which claude";
27433
+ const rawResult = execSync(pathLookupCommand, {
27434
+ encoding: "utf-8",
27435
+ stdio: ["pipe", "pipe", "pipe"]
27436
+ }).trim();
27437
+ const firstResult = rawResult.split(LINE_SPLIT_REGEX).find((line) => line.trim().length > 0) ?? "";
27438
+ if (firstResult && existsSync3(firstResult)) {
27439
+ cachedClaudeCodePath = firstResult;
27440
+ return cachedClaudeCodePath;
27441
+ }
27442
+ } catch {
27443
+ }
27444
+ const sdkCliPath = findSdkBundledCliPath();
27445
+ if (sdkCliPath) {
27446
+ cachedClaudeCodePath = sdkCliPath;
27447
+ return cachedClaudeCodePath;
27448
+ }
27449
+ cachedClaudeCodePath = null;
27450
+ return;
27451
+ }
27382
27452
  var DEFAULT_CONFIG = {
27383
27453
  model: "claude-haiku-4-5-20251001",
27384
27454
  workspace: "~/.looplia",
@@ -27404,10 +27474,10 @@ function createQueryLogger(workspace) {
27404
27474
  let logPath = null;
27405
27475
  return {
27406
27476
  init(sandboxId) {
27407
- const logsDir = join4(workspace, "sandbox", sandboxId, "logs");
27477
+ const logsDir = join23(workspace, "sandbox", sandboxId, "logs");
27408
27478
  mkdirSync3(logsDir, { recursive: true });
27409
27479
  const filename = generateLogFilename();
27410
- logPath = join4(logsDir, filename);
27480
+ logPath = join23(logsDir, filename);
27411
27481
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
27412
27482
  writeFileSync2(
27413
27483
  logPath,
@@ -27442,7 +27512,7 @@ var DEFAULT_SETTINGS = {
27442
27512
  },
27443
27513
  agents: {
27444
27514
  main: "claude-haiku-4-5-20251001",
27445
- executor: "haiku"
27515
+ executor: "claude-haiku-4-5-20251001"
27446
27516
  }
27447
27517
  };
27448
27518
  var PRESETS = {
@@ -27451,13 +27521,19 @@ var PRESETS = {
27451
27521
  name: "Anthropic Claude Haiku",
27452
27522
  apiProvider: "anthropic",
27453
27523
  mainModel: "claude-haiku-4-5-20251001",
27454
- executorModel: "haiku"
27524
+ executorModel: "claude-haiku-4-5-20251001",
27525
+ haikuModel: "claude-haiku-4-5-20251001",
27526
+ sonnetModel: "claude-haiku-4-5-20251001",
27527
+ opusModel: "claude-haiku-4-5-20251001"
27455
27528
  },
27456
27529
  ANTHROPIC_CLAUDE_SONNET: {
27457
27530
  name: "Anthropic Claude Sonnet",
27458
27531
  apiProvider: "anthropic",
27459
27532
  mainModel: "claude-sonnet-4-5-20250514",
27460
- executorModel: "haiku"
27533
+ executorModel: "claude-sonnet-4-5-20250514",
27534
+ haikuModel: "claude-sonnet-4-5-20250514",
27535
+ sonnetModel: "claude-sonnet-4-5-20250514",
27536
+ opusModel: "claude-sonnet-4-5-20250514"
27461
27537
  },
27462
27538
  // ZenMux Presets
27463
27539
  ZENMUX_ANTHROPIC_HAIKU45: {
@@ -27465,106 +27541,148 @@ var PRESETS = {
27465
27541
  apiProvider: "zenmux",
27466
27542
  baseUrl: "https://zenmux.ai/api/anthropic",
27467
27543
  mainModel: "anthropic/claude-haiku-4.5",
27468
- executorModel: "anthropic/claude-haiku-4.5"
27544
+ executorModel: "anthropic/claude-haiku-4.5",
27545
+ haikuModel: "anthropic/claude-haiku-4.5",
27546
+ sonnetModel: "anthropic/claude-haiku-4.5",
27547
+ opusModel: "anthropic/claude-haiku-4.5"
27469
27548
  },
27470
27549
  ZENMUX_ZAI_GLM47: {
27471
27550
  name: "ZenMux GLM-4.7",
27472
27551
  apiProvider: "zenmux",
27473
27552
  baseUrl: "https://zenmux.ai/api/anthropic",
27474
27553
  mainModel: "z-ai/glm-4.7",
27475
- executorModel: "z-ai/glm-4.7"
27554
+ executorModel: "z-ai/glm-4.7",
27555
+ haikuModel: "z-ai/glm-4.7",
27556
+ sonnetModel: "z-ai/glm-4.7",
27557
+ opusModel: "z-ai/glm-4.7"
27476
27558
  },
27477
27559
  ZENMUX_MINIMAX_M21: {
27478
27560
  name: "ZenMux MiniMax-M2.1",
27479
27561
  apiProvider: "zenmux",
27480
27562
  baseUrl: "https://zenmux.ai/api/anthropic",
27481
27563
  mainModel: "minimax/minimax-m2.1",
27482
- executorModel: "minimax/minimax-m2.1"
27564
+ executorModel: "minimax/minimax-m2.1",
27565
+ haikuModel: "minimax/minimax-m2.1",
27566
+ sonnetModel: "minimax/minimax-m2.1",
27567
+ opusModel: "minimax/minimax-m2.1"
27483
27568
  },
27484
27569
  ZENMUX_GOOGLE_GEMINI3FLASH: {
27485
27570
  name: "ZenMux Gemini-3-Flash",
27486
27571
  apiProvider: "zenmux",
27487
27572
  baseUrl: "https://zenmux.ai/api/anthropic",
27488
27573
  mainModel: "google/gemini-3-flash-preview",
27489
- executorModel: "google/gemini-3-flash-preview"
27574
+ executorModel: "google/gemini-3-flash-preview",
27575
+ haikuModel: "google/gemini-3-flash-preview",
27576
+ sonnetModel: "google/gemini-3-flash-preview",
27577
+ opusModel: "google/gemini-3-flash-preview"
27490
27578
  },
27491
27579
  ZENMUX_GOOGLE_GEMINI3FLASH_FREE: {
27492
27580
  name: "ZenMux Gemini-3-Flash (Free)",
27493
27581
  apiProvider: "zenmux",
27494
27582
  baseUrl: "https://zenmux.ai/api/anthropic",
27495
27583
  mainModel: "google/gemini-3-flash-preview-free",
27496
- executorModel: "google/gemini-3-flash-preview-free"
27584
+ executorModel: "google/gemini-3-flash-preview-free",
27585
+ haikuModel: "google/gemini-3-flash-preview-free",
27586
+ sonnetModel: "google/gemini-3-flash-preview-free",
27587
+ opusModel: "google/gemini-3-flash-preview-free"
27497
27588
  },
27498
27589
  ZENMUX_XIAOMI_MIMOV2FLASH: {
27499
27590
  name: "ZenMux MiMo-v2-Flash",
27500
27591
  apiProvider: "zenmux",
27501
27592
  baseUrl: "https://zenmux.ai/api/anthropic",
27502
27593
  mainModel: "xiaomi/mimo-v2-flash",
27503
- executorModel: "xiaomi/mimo-v2-flash"
27594
+ executorModel: "xiaomi/mimo-v2-flash",
27595
+ haikuModel: "xiaomi/mimo-v2-flash",
27596
+ sonnetModel: "xiaomi/mimo-v2-flash",
27597
+ opusModel: "xiaomi/mimo-v2-flash"
27504
27598
  },
27505
27599
  ZENMUX_XAI_GROK41FAST: {
27506
27600
  name: "ZenMux Grok-4.1-Fast",
27507
27601
  apiProvider: "zenmux",
27508
27602
  baseUrl: "https://zenmux.ai/api/anthropic",
27509
27603
  mainModel: "x-ai/grok-4.1-fast",
27510
- executorModel: "x-ai/grok-4.1-fast"
27604
+ executorModel: "x-ai/grok-4.1-fast",
27605
+ haikuModel: "x-ai/grok-4.1-fast",
27606
+ sonnetModel: "x-ai/grok-4.1-fast",
27607
+ opusModel: "x-ai/grok-4.1-fast"
27511
27608
  },
27512
27609
  ZENMUX_DEEPSEEK_V32: {
27513
27610
  name: "ZenMux DeepSeek-v3.2",
27514
27611
  apiProvider: "zenmux",
27515
27612
  baseUrl: "https://zenmux.ai/api/anthropic",
27516
27613
  mainModel: "deepseek/deepseek-v3.2",
27517
- executorModel: "deepseek/deepseek-v3.2"
27614
+ executorModel: "deepseek/deepseek-v3.2",
27615
+ haikuModel: "deepseek/deepseek-v3.2",
27616
+ sonnetModel: "deepseek/deepseek-v3.2",
27617
+ opusModel: "deepseek/deepseek-v3.2"
27518
27618
  },
27519
27619
  ZENMUX_DEEPSEEK_REASONER: {
27520
27620
  name: "ZenMux DeepSeek-Reasoner",
27521
27621
  apiProvider: "zenmux",
27522
27622
  baseUrl: "https://zenmux.ai/api/anthropic",
27523
27623
  mainModel: "deepseek/deepseek-reasoner",
27524
- executorModel: "deepseek/deepseek-reasoner"
27624
+ executorModel: "deepseek/deepseek-reasoner",
27625
+ haikuModel: "deepseek/deepseek-reasoner",
27626
+ sonnetModel: "deepseek/deepseek-reasoner",
27627
+ opusModel: "deepseek/deepseek-reasoner"
27525
27628
  },
27526
27629
  ZENMUX_VOLCENGINE_DOUBAO_SEED: {
27527
27630
  name: "ZenMux Doubao-Seed-1.8",
27528
27631
  apiProvider: "zenmux",
27529
27632
  baseUrl: "https://zenmux.ai/api/anthropic",
27530
27633
  mainModel: "volcengine/doubao-seed-1.8",
27531
- executorModel: "volcengine/doubao-seed-1.8"
27634
+ executorModel: "volcengine/doubao-seed-1.8",
27635
+ haikuModel: "volcengine/doubao-seed-1.8",
27636
+ sonnetModel: "volcengine/doubao-seed-1.8",
27637
+ opusModel: "volcengine/doubao-seed-1.8"
27532
27638
  },
27533
27639
  ZENMUX_MISTRAL_LARGE2512: {
27534
27640
  name: "ZenMux Mistral-Large-2512",
27535
27641
  apiProvider: "zenmux",
27536
27642
  baseUrl: "https://zenmux.ai/api/anthropic",
27537
27643
  mainModel: "mistralai/mistral-large-2512",
27538
- executorModel: "mistralai/mistral-large-2512"
27644
+ executorModel: "mistralai/mistral-large-2512",
27645
+ haikuModel: "mistralai/mistral-large-2512",
27646
+ sonnetModel: "mistralai/mistral-large-2512",
27647
+ opusModel: "mistralai/mistral-large-2512"
27539
27648
  },
27540
27649
  ZENMUX_ZAI_GLM46VFLASH: {
27541
27650
  name: "ZenMux GLM-4.6v-Flash",
27542
27651
  apiProvider: "zenmux",
27543
27652
  baseUrl: "https://zenmux.ai/api/anthropic",
27544
27653
  mainModel: "z-ai/glm-4.6v-flash",
27545
- executorModel: "z-ai/glm-4.6v-flash"
27654
+ executorModel: "z-ai/glm-4.6v-flash",
27655
+ haikuModel: "z-ai/glm-4.6v-flash",
27656
+ sonnetModel: "z-ai/glm-4.6v-flash",
27657
+ opusModel: "z-ai/glm-4.6v-flash"
27546
27658
  },
27547
27659
  ZENMUX_ZAI_GLM46V: {
27548
27660
  name: "ZenMux GLM-4.6v",
27549
27661
  apiProvider: "zenmux",
27550
27662
  baseUrl: "https://zenmux.ai/api/anthropic",
27551
27663
  mainModel: "z-ai/glm-4.6v",
27552
- executorModel: "z-ai/glm-4.6v"
27664
+ executorModel: "z-ai/glm-4.6v",
27665
+ haikuModel: "z-ai/glm-4.6v",
27666
+ sonnetModel: "z-ai/glm-4.6v",
27667
+ opusModel: "z-ai/glm-4.6v"
27553
27668
  },
27554
27669
  ZENMUX_OPENAI_GPT51CODEXMINI: {
27555
27670
  name: "ZenMux GPT-5.1 Codex Mini",
27556
27671
  apiProvider: "zenmux",
27557
27672
  baseUrl: "https://zenmux.ai/api/anthropic",
27558
27673
  mainModel: "openai/gpt-5.1-codex-mini",
27559
- executorModel: "openai/gpt-5.1-codex-mini"
27674
+ executorModel: "openai/gpt-5.1-codex-mini",
27675
+ haikuModel: "openai/gpt-5.1-codex-mini",
27676
+ sonnetModel: "openai/gpt-5.1-codex-mini",
27677
+ opusModel: "openai/gpt-5.1-codex-mini"
27560
27678
  }
27561
27679
  };
27562
27680
  var CONFIG_FILE = "looplia.setting.json";
27563
27681
  function getLoopliaHome() {
27564
- return join23(homedir3(), ".looplia");
27682
+ return join32(homedir22(), ".looplia");
27565
27683
  }
27566
27684
  function getConfigPath() {
27567
- return join23(getLoopliaHome(), CONFIG_FILE);
27685
+ return join32(getLoopliaHome(), CONFIG_FILE);
27568
27686
  }
27569
27687
  async function readLoopliaSettings() {
27570
27688
  const configPath = getConfigPath();
@@ -27596,23 +27714,33 @@ async function writeLoopliaSettings(settings) {
27596
27714
  async function removeLoopliaSettings() {
27597
27715
  await rm2(getConfigPath(), { force: true });
27598
27716
  }
27717
+ function setEnvIfNotSet(key, value) {
27718
+ if (!process.env[key]) {
27719
+ process.env[key] = value;
27720
+ }
27721
+ }
27722
+ function injectModelTierEnv(mainModel, executorModel) {
27723
+ setEnvIfNotSet("ANTHROPIC_DEFAULT_HAIKU_MODEL", mainModel);
27724
+ setEnvIfNotSet("ANTHROPIC_DEFAULT_SONNET_MODEL", mainModel);
27725
+ setEnvIfNotSet("ANTHROPIC_DEFAULT_OPUS_MODEL", mainModel);
27726
+ setEnvIfNotSet("LOOPLIA_AGENT_MODEL_MAIN", mainModel);
27727
+ setEnvIfNotSet("LOOPLIA_AGENT_MODEL_EXECUTOR", executorModel);
27728
+ }
27599
27729
  function injectLoopliaSettingsEnv(settings) {
27600
27730
  if (settings.apiProvider.type !== "anthropic") {
27601
27731
  if (settings.apiProvider.baseUrl && !process.env.ANTHROPIC_BASE_URL) {
27602
27732
  process.env.ANTHROPIC_BASE_URL = settings.apiProvider.baseUrl;
27603
27733
  }
27604
- if (settings.apiProvider.type === "zenmux" && process.env.ZENMUX_API_KEY) {
27605
- process.env.ANTHROPIC_API_KEY = process.env.ZENMUX_API_KEY;
27606
- } else if (!process.env.ANTHROPIC_API_KEY && settings.apiProvider.authToken) {
27734
+ if (settings.apiProvider.authToken) {
27607
27735
  process.env.ANTHROPIC_API_KEY = settings.apiProvider.authToken;
27736
+ } else {
27737
+ const isZenmuxEndpoint = settings.apiProvider.type === "zenmux" || settings.apiProvider.baseUrl?.includes("zenmux.ai");
27738
+ if (isZenmuxEndpoint && process.env.ZENMUX_API_KEY) {
27739
+ process.env.ANTHROPIC_API_KEY = process.env.ZENMUX_API_KEY;
27740
+ }
27608
27741
  }
27609
27742
  }
27610
- if (!process.env.LOOPLIA_AGENT_MODEL_MAIN) {
27611
- process.env.LOOPLIA_AGENT_MODEL_MAIN = settings.agents.main;
27612
- }
27613
- if (!process.env.LOOPLIA_AGENT_MODEL_EXECUTOR) {
27614
- process.env.LOOPLIA_AGENT_MODEL_EXECUTOR = settings.agents.executor;
27615
- }
27743
+ injectModelTierEnv(settings.agents.main, settings.agents.executor);
27616
27744
  }
27617
27745
  function getSettingsDisplayInfo(settings) {
27618
27746
  if (!settings) {
@@ -27762,11 +27890,11 @@ function extractSandboxIdFromPrompt(prompt) {
27762
27890
  var extractContentIdFromPrompt = extractSandboxIdFromPrompt;
27763
27891
  function expandPath(path3) {
27764
27892
  if (path3.startsWith("~/") || path3 === "~") {
27765
- const home = homedir22();
27893
+ const home = homedir32();
27766
27894
  if (!home) {
27767
27895
  throw new Error("Unable to determine home directory");
27768
27896
  }
27769
- const expanded = path3 === "~" ? home : join32(home, path3.slice(2));
27897
+ const expanded = path3 === "~" ? home : join42(home, path3.slice(2));
27770
27898
  return normalize(expanded);
27771
27899
  }
27772
27900
  if (isAbsolute(path3)) {
@@ -27783,23 +27911,23 @@ async function pathExists2(path3) {
27783
27911
  }
27784
27912
  }
27785
27913
  function getPluginPaths2() {
27786
- const base = join32(process.cwd(), "plugins");
27914
+ const base = join42(process.cwd(), "plugins");
27787
27915
  return {
27788
- core: join32(base, "looplia-core"),
27789
- writer: join32(base, "looplia-writer")
27916
+ core: join42(base, "looplia-core"),
27917
+ writer: join42(base, "looplia-writer")
27790
27918
  };
27791
27919
  }
27792
27920
  async function checkRequiredFiles(workspaceDir) {
27793
27921
  const requiredPaths = [
27794
27922
  // Core structure
27795
- join32(workspaceDir, "CLAUDE.md"),
27796
- join32(workspaceDir, ".claude", "agents"),
27797
- join32(workspaceDir, ".claude", "skills"),
27798
- join32(workspaceDir, "workflows"),
27923
+ join42(workspaceDir, "CLAUDE.md"),
27924
+ join42(workspaceDir, ".claude", "agents"),
27925
+ join42(workspaceDir, ".claude", "skills"),
27926
+ join42(workspaceDir, "workflows"),
27799
27927
  // From looplia-core plugin
27800
- join32(workspaceDir, ".claude", "commands"),
27801
- join32(workspaceDir, ".claude", "skills", "workflow-executor"),
27802
- join32(workspaceDir, ".claude", "skills", "workflow-validator")
27928
+ join42(workspaceDir, ".claude", "commands"),
27929
+ join42(workspaceDir, ".claude", "skills", "workflow-executor"),
27930
+ join42(workspaceDir, ".claude", "skills", "workflow-validator")
27803
27931
  ];
27804
27932
  for (const path3 of requiredPaths) {
27805
27933
  if (!await pathExists2(path3)) {
@@ -27828,33 +27956,33 @@ async function createTestWorkspace(workspaceDir, force) {
27828
27956
  await rm22(workspaceDir, { recursive: true, force: true });
27829
27957
  }
27830
27958
  await mkdir22(workspaceDir, { recursive: true });
27831
- await mkdir22(join32(workspaceDir, ".claude", "agents"), { recursive: true });
27832
- await mkdir22(join32(workspaceDir, ".claude", "skills"), { recursive: true });
27833
- await mkdir22(join32(workspaceDir, "sandbox"), { recursive: true });
27959
+ await mkdir22(join42(workspaceDir, ".claude", "agents"), { recursive: true });
27960
+ await mkdir22(join42(workspaceDir, ".claude", "skills"), { recursive: true });
27961
+ await mkdir22(join42(workspaceDir, "sandbox"), { recursive: true });
27834
27962
  const plugins = getPluginPaths2();
27835
- const writerWorkflowsDir = join32(plugins.writer, "workflows");
27963
+ const writerWorkflowsDir = join42(plugins.writer, "workflows");
27836
27964
  if (await pathExists2(writerWorkflowsDir)) {
27837
- await cp2(writerWorkflowsDir, join32(workspaceDir, "workflows"), {
27965
+ await cp2(writerWorkflowsDir, join42(workspaceDir, "workflows"), {
27838
27966
  recursive: true
27839
27967
  });
27840
27968
  } else {
27841
- await mkdir22(join32(workspaceDir, "workflows"), { recursive: true });
27969
+ await mkdir22(join42(workspaceDir, "workflows"), { recursive: true });
27842
27970
  }
27843
- const coreValidatorDir = join32(plugins.core, "skills", "workflow-validator");
27971
+ const coreValidatorDir = join42(plugins.core, "skills", "workflow-validator");
27844
27972
  if (await pathExists2(coreValidatorDir)) {
27845
27973
  await cp2(
27846
27974
  coreValidatorDir,
27847
- join32(workspaceDir, ".claude", "skills", "workflow-validator"),
27975
+ join42(workspaceDir, ".claude", "skills", "workflow-validator"),
27848
27976
  { recursive: true }
27849
27977
  );
27850
27978
  }
27851
27979
  await writeFile22(
27852
- join32(workspaceDir, "CLAUDE.md"),
27980
+ join42(workspaceDir, "CLAUDE.md"),
27853
27981
  "# Test Workspace\n",
27854
27982
  "utf-8"
27855
27983
  );
27856
27984
  await writeFile22(
27857
- join32(workspaceDir, "user-profile.json"),
27985
+ join42(workspaceDir, "user-profile.json"),
27858
27986
  JSON.stringify(createDefaultProfile2(), null, 2),
27859
27987
  "utf-8"
27860
27988
  );
@@ -27865,63 +27993,63 @@ async function bootstrapFromPlugins(workspaceDir, plugins) {
27865
27993
  await rm22(workspaceDir, { recursive: true, force: true });
27866
27994
  }
27867
27995
  await mkdir22(workspaceDir, { recursive: true });
27868
- await mkdir22(join32(workspaceDir, ".claude"), { recursive: true });
27869
- await mkdir22(join32(workspaceDir, "sandbox"), { recursive: true });
27870
- const coreCommandsDir = join32(plugins.core, "commands");
27996
+ await mkdir22(join42(workspaceDir, ".claude"), { recursive: true });
27997
+ await mkdir22(join42(workspaceDir, "sandbox"), { recursive: true });
27998
+ const coreCommandsDir = join42(plugins.core, "commands");
27871
27999
  if (await pathExists2(coreCommandsDir)) {
27872
- await cp2(coreCommandsDir, join32(workspaceDir, ".claude", "commands"), {
28000
+ await cp2(coreCommandsDir, join42(workspaceDir, ".claude", "commands"), {
27873
28001
  recursive: true
27874
28002
  });
27875
28003
  }
27876
- const coreSkillsDir = join32(plugins.core, "skills");
28004
+ const coreSkillsDir = join42(plugins.core, "skills");
27877
28005
  if (await pathExists2(coreSkillsDir)) {
27878
- await cp2(coreSkillsDir, join32(workspaceDir, ".claude", "skills"), {
28006
+ await cp2(coreSkillsDir, join42(workspaceDir, ".claude", "skills"), {
27879
28007
  recursive: true
27880
28008
  });
27881
28009
  }
27882
- const coreHooksDir = join32(plugins.core, "hooks");
28010
+ const coreHooksDir = join42(plugins.core, "hooks");
27883
28011
  if (await pathExists2(coreHooksDir)) {
27884
- await cp2(coreHooksDir, join32(workspaceDir, ".claude", "hooks"), {
28012
+ await cp2(coreHooksDir, join42(workspaceDir, ".claude", "hooks"), {
27885
28013
  recursive: true
27886
28014
  });
27887
28015
  }
27888
- const coreAgentsDir = join32(plugins.core, "agents");
28016
+ const coreAgentsDir = join42(plugins.core, "agents");
27889
28017
  if (await pathExists2(coreAgentsDir)) {
27890
- await mkdir22(join32(workspaceDir, ".claude", "agents"), { recursive: true });
27891
- await cp2(coreAgentsDir, join32(workspaceDir, ".claude", "agents"), {
28018
+ await mkdir22(join42(workspaceDir, ".claude", "agents"), { recursive: true });
28019
+ await cp2(coreAgentsDir, join42(workspaceDir, ".claude", "agents"), {
27892
28020
  recursive: true
27893
28021
  });
27894
28022
  }
27895
- const coreScriptsDir = join32(plugins.core, "scripts");
28023
+ const coreScriptsDir = join42(plugins.core, "scripts");
27896
28024
  if (await pathExists2(coreScriptsDir)) {
27897
- await cp2(coreScriptsDir, join32(workspaceDir, "scripts"), {
28025
+ await cp2(coreScriptsDir, join42(workspaceDir, "scripts"), {
27898
28026
  recursive: true
27899
28027
  });
27900
28028
  }
27901
- const coreClaudeMd = join32(plugins.core, "CLAUDE.md");
28029
+ const coreClaudeMd = join42(plugins.core, "CLAUDE.md");
27902
28030
  if (await pathExists2(coreClaudeMd)) {
27903
- await cp2(coreClaudeMd, join32(workspaceDir, "CLAUDE.md"));
28031
+ await cp2(coreClaudeMd, join42(workspaceDir, "CLAUDE.md"));
27904
28032
  }
27905
- const writerAgentsDir = join32(plugins.writer, "agents");
28033
+ const writerAgentsDir = join42(plugins.writer, "agents");
27906
28034
  if (await pathExists2(writerAgentsDir)) {
27907
- await cp2(writerAgentsDir, join32(workspaceDir, ".claude", "agents"), {
28035
+ await cp2(writerAgentsDir, join42(workspaceDir, ".claude", "agents"), {
27908
28036
  recursive: true
27909
28037
  });
27910
28038
  }
27911
- const writerSkillsDir = join32(plugins.writer, "skills");
28039
+ const writerSkillsDir = join42(plugins.writer, "skills");
27912
28040
  if (await pathExists2(writerSkillsDir)) {
27913
- await cp2(writerSkillsDir, join32(workspaceDir, ".claude", "skills"), {
28041
+ await cp2(writerSkillsDir, join42(workspaceDir, ".claude", "skills"), {
27914
28042
  recursive: true
27915
28043
  });
27916
28044
  }
27917
- const writerWorkflowsDir = join32(plugins.writer, "workflows");
28045
+ const writerWorkflowsDir = join42(plugins.writer, "workflows");
27918
28046
  if (await pathExists2(writerWorkflowsDir)) {
27919
- await cp2(writerWorkflowsDir, join32(workspaceDir, "workflows"), {
28047
+ await cp2(writerWorkflowsDir, join42(workspaceDir, "workflows"), {
27920
28048
  recursive: true
27921
28049
  });
27922
28050
  }
27923
28051
  await writeFile22(
27924
- join32(workspaceDir, "user-profile.json"),
28052
+ join42(workspaceDir, "user-profile.json"),
27925
28053
  JSON.stringify(createDefaultProfile2(), null, 2),
27926
28054
  "utf-8"
27927
28055
  );
@@ -27956,12 +28084,12 @@ async function ensureWorkspace(options) {
27956
28084
  return workspaceDir;
27957
28085
  }
27958
28086
  async function readUserProfile(workspaceDir) {
27959
- const profilePath = join32(workspaceDir, "user-profile.json");
28087
+ const profilePath = join42(workspaceDir, "user-profile.json");
27960
28088
  const content = await readFile22(profilePath, "utf-8");
27961
28089
  return JSON.parse(content);
27962
28090
  }
27963
28091
  async function writeUserProfile(workspaceDir, profile) {
27964
- const profilePath = join32(workspaceDir, "user-profile.json");
28092
+ const profilePath = join42(workspaceDir, "user-profile.json");
27965
28093
  await writeFile22(profilePath, JSON.stringify(profile, null, 2), "utf-8");
27966
28094
  }
27967
28095
  var workspaceCache = /* @__PURE__ */ new Map();
@@ -28448,14 +28576,10 @@ async function initializeAndValidateApiKey(config2) {
28448
28576
  "API key is required. Set ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN environment variable"
28449
28577
  );
28450
28578
  }
28451
- const isProxyProvider = settings !== null && settings.apiProvider.type !== "anthropic";
28452
- return { apiKey, isProxyProvider };
28579
+ return apiKey;
28453
28580
  }
28454
- function getAgentModels() {
28455
- return {
28456
- mainModel: process.env.LOOPLIA_AGENT_MODEL_MAIN ?? DEFAULT_SETTINGS.agents.main,
28457
- executorModel: process.env.LOOPLIA_AGENT_MODEL_EXECUTOR ?? DEFAULT_SETTINGS.agents.executor
28458
- };
28581
+ function getMainModel() {
28582
+ return process.env.LOOPLIA_AGENT_MODEL_MAIN ?? DEFAULT_SETTINGS.agents.main;
28459
28583
  }
28460
28584
  function* processEvent(event, progressTracker, context) {
28461
28585
  if (event.type === "tool_start" && event.tool === "Skill") {
@@ -28471,8 +28595,8 @@ function* processEvent(event, progressTracker, context) {
28471
28595
  }
28472
28596
  async function* executeAgenticQueryStreaming(prompt, jsonSchema, config2) {
28473
28597
  const resolvedConfig = resolveConfig(config2);
28474
- const { isProxyProvider } = await initializeAndValidateApiKey(config2);
28475
- const { mainModel, executorModel } = getAgentModels();
28598
+ await initializeAndValidateApiKey(config2);
28599
+ const mainModel = getMainModel();
28476
28600
  try {
28477
28601
  const workspace = config2?.workspace ?? await getOrInitWorkspace(
28478
28602
  resolvedConfig.workspace,
@@ -28500,51 +28624,24 @@ async function* executeAgenticQueryStreaming(prompt, jsonSchema, config2) {
28500
28624
  const context = createTransformContext(
28501
28625
  resolvedConfig.model
28502
28626
  );
28503
- const pluginPaths = await getPluginPaths();
28627
+ const pluginPaths = process.env.LOOPLIA_NO_PLUGINS === "true" ? [] : await getPluginPaths();
28628
+ if (process.env.LOOPLIA_DEBUG === "true") {
28629
+ console.error(
28630
+ "[LOOPLIA_DEBUG] Plugin paths:",
28631
+ JSON.stringify(pluginPaths)
28632
+ );
28633
+ }
28504
28634
  const userCwd = process.cwd();
28505
- const loopliaHome = getLoopliaPluginPath();
28506
- const workflowExecutionHint = isProxyProvider ? `
28507
-
28508
- ## Workflow Execution Mode: Inline (Proxy Provider)
28509
-
28510
- When executing looplia workflows (/run commands), use the "workflow-executor-inline" skill.
28511
- Execute all workflow steps INLINE without spawning Task subagents.
28512
- This ensures compatibility with your current API provider.` : "";
28513
- const agents = isProxyProvider ? void 0 : {
28514
- "skill-executor": {
28515
- description: "Universal skill orchestrator for looplia workflow steps. Executes a single workflow step by invoking skills.",
28516
- prompt: `You are the looplia skill-executor. Execute ONE workflow step by invoking the specified skill.
28517
-
28518
- ## Execution Protocol
28519
- 1. Read input files (if provided)
28520
- 2. Invoke the skill using the Skill tool
28521
- 3. Execute the mission with skill context
28522
- 4. Write JSON output to the specified path using Write tool
28523
-
28524
- ## CRITICAL: Output Writing is MANDATORY
28525
- YOU MUST CALL THE WRITE TOOL before completing. If you don't write the file, the workflow fails.
28526
-
28527
- ## Rules
28528
- - ALWAYS invoke the specified skill using Skill tool
28529
- - ALWAYS write output to the exact path using Write tool
28530
- - NEVER return results as text - always write JSON to output file
28531
- - NEVER spawn Task subagents - execute skills directly
28532
- - ALWAYS include contentId in JSON outputs`,
28533
- tools: [
28534
- "Read",
28535
- "Write",
28536
- "Skill",
28537
- "Glob",
28538
- "Grep",
28539
- "WebSearch",
28540
- "WebFetch"
28541
- ],
28542
- model: executorModel
28543
- }
28544
- };
28635
+ const loopliaHome = process.env.LOOPLIA_WORKSPACE_PATH ?? getLoopliaPluginPath();
28636
+ if (process.env.LOOPLIA_DEBUG === "true") {
28637
+ console.error("[LOOPLIA_DEBUG] loopliaHome:", loopliaHome);
28638
+ }
28639
+ const claudeCodePath = findClaudeCodePath();
28545
28640
  const result = query({
28546
28641
  prompt,
28547
28642
  options: {
28643
+ // v0.6.8: Use system-installed Claude Code if available, otherwise SDK uses built-in
28644
+ ...claudeCodePath && { pathToClaudeCodeExecutable: claudeCodePath },
28548
28645
  // v0.6.6: Use configured main model
28549
28646
  model: mainModel,
28550
28647
  // v0.6.5: SDK works relative to ~/.looplia (sandbox, workflows, etc.)
@@ -28554,7 +28651,7 @@ YOU MUST CALL THE WRITE TOOL before completing. If you don't write the file, the
28554
28651
  // v0.6.5: Load plugins from local paths instead of project settings
28555
28652
  plugins: pluginPaths,
28556
28653
  // v0.6.5: Append looplia system prompt + user context to claude_code preset
28557
- // v0.6.6: Add provider-aware workflow hint for proxy providers
28654
+ // v0.6.9: Removed provider-aware hint - unified subagent strategy for all providers
28558
28655
  systemPrompt: {
28559
28656
  type: "preset",
28560
28657
  preset: "claude_code",
@@ -28564,7 +28661,7 @@ YOU MUST CALL THE WRITE TOOL before completing. If you don't write the file, the
28564
28661
 
28565
28662
  User Working Directory: ${userCwd}
28566
28663
 
28567
- When processing --file arguments or user file paths, resolve them against the User Working Directory above.${workflowExecutionHint}`
28664
+ When processing --file arguments or user file paths, resolve them against the User Working Directory above.`
28568
28665
  },
28569
28666
  // v0.6.0: Enable Task for subagent spawning, Write/Glob for file operations
28570
28667
  // v0.6.3: Added WebSearch/WebFetch for input-less search skill
@@ -28577,9 +28674,8 @@ When processing --file arguments or user file paths, resolve them against the Us
28577
28674
  "WebSearch",
28578
28675
  "WebFetch"
28579
28676
  ],
28580
- outputFormat: { type: "json_schema", schema: jsonSchema },
28581
- // v0.6.6: Conditional agents - only for Anthropic Direct API
28582
- agents
28677
+ outputFormat: { type: "json_schema", schema: jsonSchema }
28678
+ // v0.6.9: No custom agents - using built-in general-purpose for workflow steps
28583
28679
  }
28584
28680
  });
28585
28681
  let finalResult;
@@ -28608,6 +28704,25 @@ When processing --file arguments or user file paths, resolve them against the Us
28608
28704
  error: { type: "unknown", message: "No result received" }
28609
28705
  };
28610
28706
  } catch (error2) {
28707
+ if (process.env.LOOPLIA_DEBUG === "true") {
28708
+ const debugInfo = {
28709
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
28710
+ errorType: error2 instanceof Error ? error2.constructor.name : typeof error2,
28711
+ errorMessage: error2 instanceof Error ? error2.message : String(error2),
28712
+ errorStack: error2 instanceof Error ? error2.stack : void 0,
28713
+ cwd: process.cwd(),
28714
+ loopliaHome: getLoopliaPluginPath(),
28715
+ env: {
28716
+ LOOPLIA_DEBUG: process.env.LOOPLIA_DEBUG,
28717
+ NODE_ENV: process.env.NODE_ENV,
28718
+ HOME: process.env.HOME
28719
+ }
28720
+ };
28721
+ console.error(
28722
+ "[LOOPLIA_DEBUG] SDK Error:",
28723
+ JSON.stringify(debugInfo, null, 2)
28724
+ );
28725
+ }
28611
28726
  return mapException(error2);
28612
28727
  }
28613
28728
  }
@@ -29833,14 +29948,14 @@ import { Box as Box20, Text as Text21, useApp, useInput as useInput6 } from "ink
29833
29948
  import { useCallback as useCallback2, useEffect as useEffect6, useMemo as useMemo2, useState as useState6 } from "react";
29834
29949
 
29835
29950
  // src/utils/agent-logger.ts
29836
- import { appendFileSync as appendFileSync4, existsSync as existsSync3, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
29951
+ import { appendFileSync as appendFileSync4, existsSync as existsSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
29837
29952
  import { homedir as homedir4 } from "os";
29838
29953
  import { join as join6 } from "path";
29839
29954
  var isDebugEnabled = () => process.env.LOOPLIA_DEBUG === "1";
29840
29955
  var getLogsDir = (context) => join6(homedir4(), ".looplia", "logs", context);
29841
29956
  function ensureLogsDir(context) {
29842
29957
  const logsDir = getLogsDir(context);
29843
- if (!existsSync3(logsDir)) {
29958
+ if (!existsSync4(logsDir)) {
29844
29959
  mkdirSync4(logsDir, { recursive: true });
29845
29960
  }
29846
29961
  return logsDir;
@@ -31338,14 +31453,14 @@ function getWorkspacePath2() {
31338
31453
  function ensureWorkspace2(mock) {
31339
31454
  const workspace = getWorkspacePath2();
31340
31455
  const workflowsDir = resolve2(workspace, "workflows");
31341
- if (!existsSync4(workspace)) {
31456
+ if (!existsSync5(workspace)) {
31342
31457
  if (mock) {
31343
31458
  mkdirSync5(workflowsDir, { recursive: true });
31344
31459
  } else {
31345
31460
  console.error("Workspace not initialized. Run: looplia init");
31346
31461
  process.exit(1);
31347
31462
  }
31348
- } else if (!existsSync4(workflowsDir)) {
31463
+ } else if (!existsSync5(workflowsDir)) {
31349
31464
  mkdirSync5(workflowsDir, { recursive: true });
31350
31465
  }
31351
31466
  return workspace;
@@ -32564,11 +32679,11 @@ async function runConfigCommand(args) {
32564
32679
  }
32565
32680
 
32566
32681
  // src/commands/init.ts
32567
- import { dirname as dirname3, join as join7 } from "path";
32682
+ import { dirname as dirname4, join as join7 } from "path";
32568
32683
  import { createInterface as createInterface2 } from "readline";
32569
32684
  import { fileURLToPath as fileURLToPath3 } from "url";
32570
32685
  function getCliBundledPluginsPath() {
32571
- const currentFile = typeof __dirname !== "undefined" ? __dirname : dirname3(fileURLToPath3(import.meta.url));
32686
+ const currentFile = typeof __dirname !== "undefined" ? __dirname : dirname4(fileURLToPath3(import.meta.url));
32572
32687
  return join7(currentFile, "..", "plugins");
32573
32688
  }
32574
32689
  function printInitHelp() {
@@ -32711,7 +32826,7 @@ async function runInitCommand(args) {
32711
32826
  import { randomBytes } from "crypto";
32712
32827
  import {
32713
32828
  copyFileSync as copyFileSync2,
32714
- existsSync as existsSync5,
32829
+ existsSync as existsSync6,
32715
32830
  mkdirSync as mkdirSync6,
32716
32831
  readFileSync as readFileSync2,
32717
32832
  writeFileSync as writeFileSync4
@@ -32790,7 +32905,7 @@ function createSandboxWithInputs(workspace, workflowId, inputs) {
32790
32905
  );
32791
32906
  } else {
32792
32907
  const absolutePath = resolve3(parsedInput.value);
32793
- if (!existsSync5(absolutePath)) {
32908
+ if (!existsSync6(absolutePath)) {
32794
32909
  throw new Error(`Input file not found: ${parsedInput.value}`);
32795
32910
  }
32796
32911
  copyFileSync2(absolutePath, join8(sandboxDir, "inputs", `${name}.md`));
@@ -32958,7 +33073,7 @@ function getWorkspacePath3() {
32958
33073
  }
32959
33074
  function ensureWorkspace3(mock) {
32960
33075
  const workspace = getWorkspacePath3();
32961
- if (!existsSync5(workspace)) {
33076
+ if (!existsSync6(workspace)) {
32962
33077
  if (mock) {
32963
33078
  mkdirSync6(join8(workspace, "sandbox"), { recursive: true });
32964
33079
  } else {
@@ -32986,7 +33101,7 @@ function buildRunPrompt(workflowId, sandboxId) {
32986
33101
  }
32987
33102
  function checkWorkflowInputless(workspace, workflowId) {
32988
33103
  const workflowPath = join8(workspace, "workflows", `${workflowId}.md`);
32989
- if (!existsSync5(workflowPath)) {
33104
+ if (!existsSync6(workflowPath)) {
32990
33105
  return false;
32991
33106
  }
32992
33107
  try {
@@ -33055,7 +33170,7 @@ async function executeBatch2(prompt, workspace, workflowId) {
33055
33170
  function resolveSandboxId(workspace, parsed, allowInputless) {
33056
33171
  if (parsed.sandboxId) {
33057
33172
  const sandboxDir = join8(workspace, "sandbox", parsed.sandboxId);
33058
- if (!existsSync5(sandboxDir)) {
33173
+ if (!existsSync6(sandboxDir)) {
33059
33174
  console.error(`Error: Sandbox not found: ${parsed.sandboxId}`);
33060
33175
  console.error(`Path: ${sandboxDir}`);
33061
33176
  process.exit(1);
@@ -33072,7 +33187,7 @@ function resolveSandboxId(workspace, parsed, allowInputless) {
33072
33187
  return sandboxId;
33073
33188
  }
33074
33189
  if (parsed.file) {
33075
- if (!existsSync5(parsed.file)) {
33190
+ if (!existsSync6(parsed.file)) {
33076
33191
  console.error(`Error: File not found: ${parsed.file}`);
33077
33192
  process.exit(1);
33078
33193
  }
@@ -33152,7 +33267,7 @@ async function runRunCommand(args) {
33152
33267
  }
33153
33268
 
33154
33269
  // src/index.ts
33155
- var VERSION = "0.6.7";
33270
+ var VERSION = "0.6.8";
33156
33271
  function printHelp3() {
33157
33272
  console.log(`
33158
33273
  looplia - Content intelligence CLI (v${VERSION})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@looplia/looplia-cli",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "Looplia CLI - AI-powered workflow automation tool",
5
5
  "type": "module",
6
6
  "license": "Elastic-2.0",
@@ -30,7 +30,7 @@
30
30
  "scripts": {
31
31
  "build": "tsup",
32
32
  "dev": "tsup --watch",
33
- "start": "node dist/index.js",
33
+ "start": "node dist/cli.js",
34
34
  "test": "bun run build && bun test",
35
35
  "test:watch": "bun test --watch",
36
36
  "test:unit": "bun test commands/",
@@ -1,11 +1,6 @@
1
1
  {
2
2
  "$schema": "https://code.claude.com/schemas/hooks.json",
3
3
  "hooks": [
4
- {
5
- "event": "SessionStart",
6
- "command": "echo '🚀 Looplia session started'",
7
- "description": "Log session start"
8
- },
9
4
  {
10
5
  "event": "PostToolUse",
11
6
  "matcher": "Write",
@@ -76,6 +76,6 @@ LOCK_FILE="${VALIDATION_JSON}.lock"
76
76
  # Update validation.json to mark step as validated (v0.6.0 uses "steps")
77
77
  jq --arg art "$ARTIFACT" '.steps[$art].validated = true' "$VALIDATION_JSON" > "${VALIDATION_JSON}.tmp"
78
78
  mv "${VALIDATION_JSON}.tmp" "$VALIDATION_JSON"
79
- echo "✓ Validated: $ARTIFACT.json"
79
+ echo "✓ Validated: $ARTIFACT.json" >&2
80
80
 
81
81
  ) 200>"$LOCK_FILE"
@@ -6,14 +6,14 @@ description: |
6
6
  "execute this looplia pipeline", "/run writing-kit", "start the looplia automation", or
7
7
  "process these workflow steps".
8
8
 
9
- Architecture: One workflow step triggers one skill-executor subagent call, which then
10
- invokes multiple skills to accomplish the step's mission. Handles sandbox management,
11
- per-step skill-executor orchestration, and validation state tracking per v0.6.3.
9
+ Architecture: One workflow step triggers one general-purpose subagent call, which then
10
+ invokes skills to accomplish the step's mission. Each step = separate context window.
11
+ Handles sandbox management, per-step orchestration, and validation state tracking.
12
12
 
13
- v0.6.3: Named inputs (${{ inputs.name }}), input-less workflow support.
13
+ v0.6.9: Unified general-purpose subagent strategy for all providers (context offload).
14
14
  ---
15
15
 
16
- # Workflow Executor Skill (v0.6.3)
16
+ # Workflow Executor Skill (v0.6.9)
17
17
 
18
18
  Execute looplia workflows defined in `workflows/*.md` files using the skills-first architecture.
19
19
 
@@ -26,17 +26,17 @@ Use this skill when:
26
26
 
27
27
  ---
28
28
 
29
- ## CRITICAL: Universal Skill-Executor Invocation
29
+ ## CRITICAL: Task Invocation with general-purpose Subagent
30
30
 
31
- **v0.6.1 BREAKING CHANGE:** ALL workflow steps use `skill-executor` subagent.
31
+ **v0.6.9:** Using built-in `general-purpose` subagent for ALL workflow steps (all providers).
32
32
 
33
33
  When executing a step with `skill: {name}` and `mission:`:
34
34
 
35
35
  ```json
36
36
  {
37
- "subagent_type": "skill-executor",
37
+ "subagent_type": "general-purpose",
38
38
  "description": "Execute step: {step.id}",
39
- "prompt": "Execute skill '{step.skill}' for step '{step.id}'.\n\nMission: {step.mission}\n\nInput: {resolved input}\nOutput: {step.output}\nValidation: {step.validate}"
39
+ "prompt": "Execute skill '{step.skill}' for step '{step.id}'.\n\n## Mission\n{step.mission}\n\n## Execution Protocol\n1. Read input files (if provided)\n2. Invoke the skill using Skill tool\n3. Execute the mission with skill context\n4. Write JSON output to the specified path using Write tool\n\n## CRITICAL: Output Writing is MANDATORY\nYOU MUST CALL THE WRITE TOOL before completing. If you don't write the file, the workflow fails.\n\n## Rules\n- ALWAYS invoke the specified skill using Skill tool\n- ALWAYS write output to the exact path using Write tool\n- NEVER return results as text - always write JSON to output file\n- NEVER spawn Task subagents - execute skills directly\n- ALWAYS include contentId in JSON outputs\n\nInput: {resolved input path}\nOutput: {step.output}\nValidation: {step.validate JSON}"
40
40
  }
41
41
  ```
42
42
 
@@ -54,23 +54,22 @@ When executing a step with `skill: {name}` and `mission:`:
54
54
  **Task tool call:**
55
55
  ```json
56
56
  {
57
- "subagent_type": "skill-executor",
57
+ "subagent_type": "general-purpose",
58
58
  "description": "Execute step: analyze-content",
59
- "prompt": "Execute skill 'media-reviewer' for step 'analyze-content'.\n\nMission: Deep analysis of video transcript. Extract key themes, important quotes, and narrative structure.\n\nInput: sandbox/video-2025-01-15-abc123/inputs/content.md\nOutput: sandbox/video-2025-01-15-abc123/outputs/analysis.json\nValidation: {\"required_fields\":[\"contentId\",\"headline\",\"keyThemes\"]}"
59
+ "prompt": "Execute skill 'media-reviewer' for step 'analyze-content'.\n\n## Mission\nDeep analysis of video transcript. Extract key themes, important quotes, and narrative structure.\n\n## Execution Protocol\n1. Read input files (if provided)\n2. Invoke the skill using Skill tool\n3. Execute the mission with skill context\n4. Write JSON output to the specified path using Write tool\n\n## CRITICAL: Output Writing is MANDATORY\nYOU MUST CALL THE WRITE TOOL before completing. If you don't write the file, the workflow fails.\n\n## Rules\n- ALWAYS invoke the specified skill using Skill tool\n- ALWAYS write output to the exact path using Write tool\n- NEVER return results as text - always write JSON to output file\n- NEVER spawn Task subagents - execute skills directly\n- ALWAYS include contentId in JSON outputs\n\nInput: sandbox/video-2025-01-15-abc123/inputs/content.md\nOutput: sandbox/video-2025-01-15-abc123/outputs/analysis.json\nValidation: {\"required_fields\":[\"contentId\",\"headline\",\"keyThemes\"]}"
60
60
  }
61
61
  ```
62
62
 
63
63
  ### Rules
64
64
 
65
- - **ALWAYS** use `subagent_type: "skill-executor"` for ALL workflow steps
65
+ - **ALWAYS** use `subagent_type: "general-purpose"` for ALL workflow steps
66
66
  - **NEVER** use custom subagent_type per step (removed in v0.6.1)
67
- - **NEVER** use `subagent_type: "general-purpose"` for workflow steps
68
67
  - **VALIDATE** that step has both `skill:` and `mission:` fields
69
68
  - **REJECT** steps using deprecated `run:` syntax
70
69
 
71
70
  ### Why Per-Step Task Calls (Context Isolation)
72
71
 
73
- Each `Task(skill-executor)` creates a **separate context window**:
72
+ Each `Task(general-purpose)` creates a **separate context window**:
74
73
  - Isolates step processing from main agent context
75
74
  - Prevents context pollution across steps
76
75
  - Enables focused execution with only relevant inputs
@@ -249,7 +248,7 @@ Computed order: [analyze-content, generate-ideas, build-writing-kit]
249
248
  **Execute steps ONE AT A TIME (context isolation):**
250
249
 
251
250
  1. Get first unvalidated step from dependency order
252
- 2. Make ONE `Task(skill-executor)` call for THIS step only
251
+ 2. Make ONE `Task(general-purpose)` call for THIS step only
253
252
  3. WAIT for Task completion before proceeding
254
253
  4. Validate output, update validation.json
255
254
  5. REPEAT for next unvalidated step
@@ -271,10 +270,10 @@ FOR EACH step in dependency order:
271
270
  ┌─────────┐ ┌─────────────────────────────┐
272
271
  │ SKIP │ │ 1. INVOKE Task tool: │
273
272
  │ (done) │ │ subagent_type: │
274
- └─────────┘ │ "skill-executor"
273
+ └─────────┘ │ "general-purpose"
275
274
  │ │
276
- │ 2. skill-executor invokes
277
- the specified skill
275
+ │ 2. Subagent invokes the
276
+ │ specified skill
278
277
  │ │
279
278
  │ 3. VALIDATE output │
280
279
  │ │
@@ -302,9 +301,9 @@ For step:
302
301
  Invoke Task tool:
303
302
  ```json
304
303
  {
305
- "subagent_type": "skill-executor",
304
+ "subagent_type": "general-purpose",
306
305
  "description": "Execute step: analyze-content",
307
- "prompt": "Execute skill 'media-reviewer' for step 'analyze-content'.\n\nMission: Deep analysis of video transcript. Extract key themes, important quotes with timestamps, and narrative structure.\n\nInput: sandbox/article-2025-12-18-xk7m/inputs/content.md\nOutput: sandbox/article-2025-12-18-xk7m/outputs/analysis.json\nValidation: {\"required_fields\":[\"contentId\",\"headline\",\"keyThemes\"]}"
306
+ "prompt": "Execute skill 'media-reviewer' for step 'analyze-content'.\n\n## Mission\nDeep analysis of video transcript. Extract key themes, important quotes with timestamps, and narrative structure.\n\n## Execution Protocol\n1. Read input files (if provided)\n2. Invoke the skill using Skill tool\n3. Execute the mission with skill context\n4. Write JSON output to the specified path using Write tool\n\n## CRITICAL: Output Writing is MANDATORY\nYOU MUST CALL THE WRITE TOOL before completing.\n\n## Rules\n- ALWAYS invoke the specified skill using Skill tool\n- ALWAYS write output to the exact path using Write tool\n- NEVER return results as text - always write JSON to output file\n- ALWAYS include contentId in JSON outputs\n\nInput: sandbox/article-2025-12-18-xk7m/inputs/content.md\nOutput: sandbox/article-2025-12-18-xk7m/outputs/analysis.json\nValidation: {\"required_fields\":[\"contentId\",\"headline\",\"keyThemes\"]}"
308
307
  }
309
308
  ```
310
309
 
@@ -350,9 +349,9 @@ When step with `final: true` passes validation:
350
349
 
351
350
  ---
352
351
 
353
- ## Variable Substitution (v0.6.3)
352
+ ## Variable Substitution (v0.6.9)
354
353
 
355
- Resolve variables before passing to skill-executor:
354
+ Resolve variables before passing to general-purpose subagent:
356
355
 
357
356
  | Variable | Resolution | Example |
358
357
  |----------|------------|---------|
@@ -435,21 +434,21 @@ input: ${{ steps.analyze-content.output }}
435
434
  4. [ORDER] Computed: [analyze-content, generate-ideas, build-writing-kit]
436
435
 
437
436
  5. [STEP] analyze-content
438
- - Task tool: subagent_type="skill-executor"
437
+ - Task tool: subagent_type="general-purpose"
439
438
  - Skill: media-reviewer
440
439
  - Output: outputs/analysis.json
441
440
  - Validate: PASSED
442
441
  - Update: validation.json (analyze-content.validated = true)
443
442
 
444
443
  6. [STEP] generate-ideas
445
- - Task tool: subagent_type="skill-executor"
444
+ - Task tool: subagent_type="general-purpose"
446
445
  - Skill: idea-synthesis
447
446
  - Output: outputs/ideas.json
448
447
  - Validate: PASSED
449
448
  - Update: validation.json (generate-ideas.validated = true)
450
449
 
451
450
  7. [STEP] build-writing-kit
452
- - Task tool: subagent_type="skill-executor"
451
+ - Task tool: subagent_type="general-purpose"
453
452
  - Skill: writing-kit-assembler
454
453
  - Output: outputs/writing-kit.json
455
454
  - Validate: PASSED
@@ -463,7 +462,7 @@ input: ${{ steps.analyze-content.output }}
463
462
  ## File References
464
463
 
465
464
  - Workflow definitions: `workflows/*.md`
466
- - Skill-executor: Inline subagent defined in CLI (see query-executor.ts)
465
+ - Subagent: Built-in `general-purpose` agent (v0.6.9)
467
466
  - Skill definitions: `plugins/*/skills/*/SKILL.md`
468
467
  - Sandbox storage: `sandbox/{sandbox-id}/`
469
468
  - Validator script: `.claude/skills/workflow-validator/scripts/validate.ts`