@kody-ade/kody-engine-lite 0.1.134 → 0.1.136

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.
Files changed (2) hide show
  1. package/dist/bin/cli.js +185 -95
  2. package/package.json +1 -1
package/dist/bin/cli.js CHANGED
@@ -4932,7 +4932,10 @@ function executeShipStage(ctx, _def) {
4932
4932
  const head = getCurrentBranch(ctx.projectDir);
4933
4933
  const base = getDefaultBranch(ctx.projectDir);
4934
4934
  try {
4935
- execFileSync14("git", ["add", ctx.taskDir], {
4935
+ const memoryDir = path22.join(ctx.projectDir, ".kody", "memory");
4936
+ const addPaths = [ctx.taskDir];
4937
+ if (fs24.existsSync(memoryDir)) addPaths.push(memoryDir);
4938
+ execFileSync14("git", ["add", ...addPaths], {
4936
4939
  cwd: ctx.projectDir,
4937
4940
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
4938
4941
  stdio: "pipe"
@@ -5865,6 +5868,9 @@ async function runPipelineInner(ctx) {
5865
5868
  }
5866
5869
  continue;
5867
5870
  }
5871
+ if (def.name === "ship") {
5872
+ autoLearn(ctx);
5873
+ }
5868
5874
  ciGroup(`Stage: ${def.name}`);
5869
5875
  state.stages[def.name] = { state: "running", startedAt: (/* @__PURE__ */ new Date()).toISOString(), retries: 0 };
5870
5876
  writeState(state, ctx.taskDir);
@@ -5926,7 +5932,6 @@ async function runPipelineInner(ctx) {
5926
5932
  if (ctx.input.issueNumber && !ctx.input.local) {
5927
5933
  setLifecycleLabel(ctx.input.issueNumber, "done");
5928
5934
  }
5929
- autoLearn(ctx);
5930
5935
  }
5931
5936
  await runRetrospective(ctx, state, pipelineStartTime).catch((err) => {
5932
5937
  logger.warn(` Retrospective failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -6218,18 +6223,36 @@ async function runResolve(options) {
6218
6223
  logger.info(`Resolving PR #${prNumber} \u2014 merging ${defaultBranch}...`);
6219
6224
  const mergeResult = mergeDefault(projectDir);
6220
6225
  if (mergeResult === "error") {
6221
- return { outcome: "failed", error: "Failed to merge default branch" };
6226
+ const error = "Failed to merge default branch";
6227
+ if (!local) {
6228
+ try {
6229
+ postPRComment(prNumber, `\u274C **Resolve failed:** ${error}`);
6230
+ } catch {
6231
+ }
6232
+ }
6233
+ return { outcome: "failed", error };
6222
6234
  }
6223
6235
  if (mergeResult === "clean") {
6224
6236
  logger.info(" Clean merge \u2014 no conflicts");
6225
6237
  if (!local) {
6226
6238
  pushBranch(projectDir);
6239
+ try {
6240
+ postPRComment(prNumber, `\u2705 **Clean merge** \u2014 synced with \`${defaultBranch}\`, no conflicts.`);
6241
+ } catch {
6242
+ }
6227
6243
  }
6228
6244
  return { outcome: "merged" };
6229
6245
  }
6230
6246
  const conflictedFiles = getConflictedFiles(projectDir);
6231
6247
  if (conflictedFiles.length === 0) {
6232
- return { outcome: "failed", error: "Merge reported conflict but no conflicted files found" };
6248
+ const error = "Merge reported conflict but no conflicted files found";
6249
+ if (!local) {
6250
+ try {
6251
+ postPRComment(prNumber, `\u274C **Resolve failed:** ${error}`);
6252
+ } catch {
6253
+ }
6254
+ }
6255
+ return { outcome: "failed", error };
6233
6256
  }
6234
6257
  logger.info(` ${conflictedFiles.length} conflicted file(s): ${conflictedFiles.join(", ")}`);
6235
6258
  const conflictContext = getConflictContext(projectDir, conflictedFiles);
@@ -6238,7 +6261,14 @@ async function runResolve(options) {
6238
6261
  const runnerName = config.agent.defaultRunner ?? Object.keys(runners)[0] ?? "claude";
6239
6262
  const runner = runners[runnerName];
6240
6263
  if (!runner) {
6241
- return { outcome: "failed", error: `Runner "${runnerName}" not found` };
6264
+ const error = `Runner "${runnerName}" not found`;
6265
+ if (!local) {
6266
+ try {
6267
+ postPRComment(prNumber, `\u274C **Resolve failed:** ${error}`);
6268
+ } catch {
6269
+ }
6270
+ }
6271
+ return { outcome: "failed", error };
6242
6272
  }
6243
6273
  const model = resolveModel("mid");
6244
6274
  const extraEnv = {};
@@ -6251,7 +6281,14 @@ async function runResolve(options) {
6251
6281
  env: extraEnv
6252
6282
  });
6253
6283
  if (result2.outcome !== "completed") {
6254
- return { outcome: "failed", error: `Agent failed: ${result2.error}` };
6284
+ const error = `Agent failed: ${result2.error}`;
6285
+ if (!local) {
6286
+ try {
6287
+ postPRComment(prNumber, `\u274C **Resolve failed:** ${error}`);
6288
+ } catch {
6289
+ }
6290
+ }
6291
+ return { outcome: "failed", error };
6255
6292
  }
6256
6293
  logger.info(" Verifying resolution...");
6257
6294
  const verify = runQualityGates(projectDir, projectDir, { onlyFailOnFiles: conflictedFiles });
@@ -6259,8 +6296,15 @@ async function runResolve(options) {
6259
6296
  const errorSummary = verify.errors.slice(0, 5).join("\n");
6260
6297
  logger.error(` Verification failed:
6261
6298
  ${errorSummary}`);
6262
- return { outcome: "failed", error: `Conflict resolution failed verification:
6263
- ${errorSummary}` };
6299
+ const error = `Conflict resolution failed verification:
6300
+ ${errorSummary}`;
6301
+ if (!local) {
6302
+ try {
6303
+ postPRComment(prNumber, `\u274C **Resolve failed:** ${error}`);
6304
+ } catch {
6305
+ }
6306
+ }
6307
+ return { outcome: "failed", error };
6264
6308
  }
6265
6309
  logger.info(" Verification passed");
6266
6310
  commitAll(`chore: resolve merge conflicts with ${defaultBranch}`, projectDir);
@@ -7795,74 +7839,98 @@ function detectToolsForBootstrap(cwd) {
7795
7839
  (tool) => tool.detect.some((pattern) => fs8.existsSync(path7.join(cwd, pattern)))
7796
7840
  );
7797
7841
  }
7798
- var FRAMEWORK_SKILL_RULES = [
7799
- {
7800
- detect: (deps) => "next" in deps,
7801
- skills: [
7802
- { skill: "vercel-labs/agent-skills@vercel-react-best-practices", label: "React best practices" },
7803
- { skill: "wshobson/agents@nextjs-app-router-patterns", label: "Next.js App Router patterns" }
7804
- ]
7805
- },
7806
- {
7807
- detect: (deps) => "react" in deps && !("next" in deps),
7808
- skills: [
7809
- { skill: "vercel-labs/agent-skills@vercel-react-best-practices", label: "React best practices" }
7810
- ]
7811
- },
7812
- {
7813
- detect: (deps) => "vue" in deps,
7814
- skills: [
7815
- { skill: "antfu/skills@vue", label: "Vue best practices" }
7816
- ]
7817
- },
7818
- {
7819
- detect: (deps) => "svelte" in deps || "@sveltejs/kit" in deps,
7820
- skills: [
7821
- { skill: "ejirocodes/agent-skills@svelte5-best-practices", label: "Svelte best practices" }
7822
- ]
7823
- },
7824
- {
7825
- detect: (deps) => "@angular/core" in deps,
7826
- skills: [
7827
- { skill: "analogjs/angular-skills@angular-component", label: "Angular component patterns" }
7828
- ]
7829
- },
7830
- {
7831
- detect: (deps) => "payload" in deps,
7832
- skills: [
7833
- { skill: "payloadcms/skills@payload", label: "Payload CMS patterns" }
7834
- ]
7835
- },
7836
- {
7837
- detect: (deps) => "tailwindcss" in deps,
7838
- skills: [
7839
- { skill: "wshobson/agents@tailwind-design-system", label: "Tailwind design system" }
7840
- ]
7841
- }
7842
- ];
7843
- function detectFrameworkSkills(cwd) {
7842
+ var FRAMEWORK_KEYWORDS = {
7843
+ next: "nextjs",
7844
+ react: "react",
7845
+ vue: "vue",
7846
+ svelte: "svelte",
7847
+ "@angular/core": "angular",
7848
+ payload: "payload cms",
7849
+ tailwindcss: "tailwind",
7850
+ nuxt: "nuxt",
7851
+ astro: "astro",
7852
+ "solid-js": "solidjs",
7853
+ express: "express",
7854
+ fastify: "fastify",
7855
+ prisma: "prisma"
7856
+ };
7857
+ function parseSkillsSearchOutput(output) {
7858
+ const stripped = output.replace(/\x1b\[[0-9;]*m/g, "");
7859
+ const results = [];
7860
+ for (const line of stripped.split("\n")) {
7861
+ const match = line.match(/^([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+)\s+([\d.]+)([KM]?)\s+installs/);
7862
+ if (!match) continue;
7863
+ const ref = match[1];
7864
+ const name = ref.split("@").pop() ?? "";
7865
+ let installs = parseFloat(match[2]);
7866
+ if (match[3] === "K") installs *= 1e3;
7867
+ if (match[3] === "M") installs *= 1e6;
7868
+ results.push({ ref, name, installs });
7869
+ }
7870
+ return results;
7871
+ }
7872
+ function detectProjectKeywords(cwd) {
7844
7873
  const pkgPath = path7.join(cwd, "package.json");
7845
7874
  if (!fs8.existsSync(pkgPath)) return [];
7846
7875
  try {
7847
7876
  const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
7848
7877
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
7849
- const seen = /* @__PURE__ */ new Set();
7850
- const skills = [];
7851
- for (const rule of FRAMEWORK_SKILL_RULES) {
7852
- if (rule.detect(allDeps)) {
7853
- for (const skill of rule.skills) {
7854
- if (!seen.has(skill.skill)) {
7855
- seen.add(skill.skill);
7856
- skills.push(skill);
7857
- }
7858
- }
7859
- }
7878
+ const keywords = [];
7879
+ for (const [dep, keyword] of Object.entries(FRAMEWORK_KEYWORDS)) {
7880
+ if (dep in allDeps) keywords.push(keyword);
7860
7881
  }
7861
- return skills;
7882
+ return keywords;
7862
7883
  } catch {
7863
7884
  return [];
7864
7885
  }
7865
7886
  }
7887
+ function searchSkills(keywords, exclude, limit) {
7888
+ const seen = /* @__PURE__ */ new Set();
7889
+ const perKeyword = [];
7890
+ for (const keyword of keywords) {
7891
+ try {
7892
+ const output = execFileSync4("npx", ["skills", "find", keyword], {
7893
+ encoding: "utf-8",
7894
+ timeout: 15e3,
7895
+ stdio: ["pipe", "pipe", "pipe"]
7896
+ });
7897
+ const results = [];
7898
+ for (const skill of parseSkillsSearchOutput(output)) {
7899
+ if (!exclude.has(skill.name)) {
7900
+ results.push(skill);
7901
+ }
7902
+ }
7903
+ perKeyword.push(results);
7904
+ } catch {
7905
+ }
7906
+ }
7907
+ const selected = [];
7908
+ let round = 0;
7909
+ while (selected.length < limit) {
7910
+ let added = false;
7911
+ for (const results of perKeyword) {
7912
+ if (selected.length >= limit) break;
7913
+ if (round >= results.length) continue;
7914
+ const candidate = results[round];
7915
+ if (!seen.has(candidate.ref)) {
7916
+ seen.add(candidate.ref);
7917
+ selected.push(candidate);
7918
+ added = true;
7919
+ }
7920
+ }
7921
+ if (!added) break;
7922
+ round++;
7923
+ }
7924
+ return selected;
7925
+ }
7926
+ function collectSkillPaths(cwd, skillName, paths) {
7927
+ for (const dir of [".claude/skills", ".agents/skills"]) {
7928
+ const skillPath = path7.join(dir, skillName);
7929
+ if (fs8.existsSync(path7.join(cwd, skillPath))) {
7930
+ paths.push(skillPath);
7931
+ }
7932
+ }
7933
+ }
7866
7934
  function bootstrapCommand(opts, pkgRoot) {
7867
7935
  const cwd = process.cwd();
7868
7936
  setConfigDir(cwd);
@@ -8319,44 +8387,66 @@ ${entries}
8319
8387
  }
8320
8388
  console.log("\n\u2500\u2500 Skills \u2500\u2500");
8321
8389
  const installedSkillPaths = [];
8322
- const allSkills = [];
8323
- const seen = /* @__PURE__ */ new Set();
8390
+ const excludeSkills = /* @__PURE__ */ new Set();
8391
+ const claudeSkillsDir = path7.join(cwd, ".claude", "skills");
8392
+ if (fs8.existsSync(claudeSkillsDir)) {
8393
+ try {
8394
+ for (const entry of fs8.readdirSync(claudeSkillsDir, { withFileTypes: true })) {
8395
+ if (entry.isDirectory() || entry.isSymbolicLink()) excludeSkills.add(entry.name);
8396
+ }
8397
+ } catch {
8398
+ }
8399
+ }
8324
8400
  for (const tool of detectToolsForBootstrap(cwd)) {
8325
- if (tool.skill && !seen.has(tool.skill)) {
8326
- seen.add(tool.skill);
8327
- allSkills.push({ skill: tool.skill, label: `${tool.name} CLI` });
8401
+ if (tool.skill) {
8402
+ const toolSkillName = tool.skill.split("@").pop() ?? "";
8403
+ excludeSkills.add(toolSkillName);
8328
8404
  }
8329
8405
  }
8330
- for (const mapping of detectFrameworkSkills(cwd)) {
8331
- if (!seen.has(mapping.skill)) {
8332
- seen.add(mapping.skill);
8333
- allSkills.push(mapping);
8406
+ for (const tool of detectToolsForBootstrap(cwd)) {
8407
+ if (!tool.skill) continue;
8408
+ const skillName = tool.skill.split("@").pop() ?? "";
8409
+ if (excludeSkills.has(skillName) && fs8.existsSync(path7.join(claudeSkillsDir, skillName))) continue;
8410
+ try {
8411
+ console.log(` Installing: ${tool.name} CLI (${tool.skill})`);
8412
+ execFileSync4("npx", ["skills", "add", tool.skill, "--yes"], {
8413
+ cwd,
8414
+ encoding: "utf-8",
8415
+ timeout: 6e4,
8416
+ stdio: ["pipe", "pipe", "pipe"]
8417
+ });
8418
+ collectSkillPaths(cwd, skillName, installedSkillPaths);
8419
+ excludeSkills.add(skillName);
8420
+ console.log(` \u2713 ${tool.name} CLI`);
8421
+ } catch {
8422
+ console.log(` \u2717 ${tool.name} CLI \u2014 failed to install`);
8334
8423
  }
8335
8424
  }
8336
- if (allSkills.length > 0) {
8337
- for (const { skill, label } of allSkills) {
8338
- try {
8339
- console.log(` Installing: ${label} (${skill})`);
8340
- execFileSync4("npx", ["skills", "add", skill, "--yes"], {
8341
- cwd,
8342
- encoding: "utf-8",
8343
- timeout: 6e4,
8344
- stdio: ["pipe", "pipe", "pipe"]
8345
- });
8346
- const skillName = skill.split("@").pop() ?? "";
8347
- for (const dir of [".claude/skills", ".agents/skills"]) {
8348
- const skillPath = path7.join(dir, skillName);
8349
- if (fs8.existsSync(path7.join(cwd, skillPath))) {
8350
- installedSkillPaths.push(skillPath);
8351
- }
8425
+ const keywords = detectProjectKeywords(cwd);
8426
+ if (keywords.length > 0) {
8427
+ console.log(` Searching skills.sh for: ${keywords.join(", ")}`);
8428
+ const found = searchSkills(keywords, excludeSkills, 5);
8429
+ if (found.length > 0) {
8430
+ for (const skill of found) {
8431
+ try {
8432
+ console.log(` Installing: ${skill.name} (${skill.ref})`);
8433
+ execFileSync4("npx", ["skills", "add", skill.ref, "--yes"], {
8434
+ cwd,
8435
+ encoding: "utf-8",
8436
+ timeout: 6e4,
8437
+ stdio: ["pipe", "pipe", "pipe"]
8438
+ });
8439
+ collectSkillPaths(cwd, skill.name, installedSkillPaths);
8440
+ console.log(` \u2713 ${skill.name}`);
8441
+ } catch {
8442
+ console.log(` \u2717 ${skill.name} \u2014 failed to install`);
8352
8443
  }
8353
- console.log(` \u2713 ${label}`);
8354
- } catch {
8355
- console.log(` \u2717 ${label} \u2014 failed to install`);
8356
8444
  }
8445
+ } else {
8446
+ console.log(" \u25CB No matching skills found on skills.sh");
8357
8447
  }
8358
8448
  } else {
8359
- console.log(" \u25CB No skills to install (no frameworks detected)");
8449
+ console.log(" \u25CB No frameworks detected \u2014 skipping skill search");
8360
8450
  }
8361
8451
  if (fs8.existsSync(path7.join(cwd, "skills-lock.json"))) {
8362
8452
  installedSkillPaths.push("skills-lock.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.134",
3
+ "version": "0.1.136",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",