@kybernesis/create 0.7.8 → 0.7.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/arcana.d.ts CHANGED
@@ -1,8 +1,13 @@
1
+ /** The agent's own name, for proposing workspace names that match convention. */
2
+ export declare function agentName(dir: string, fallback: string): string;
1
3
  export interface ArcanaSetup {
2
4
  dir: string;
3
5
  /** Proposed workspace prefix — the agent's name. Only a suggestion. */
4
6
  suggest: string;
5
- /** Department subagents, each of which gets its own brain. */
7
+ /**
8
+ * Department subagents, each of which gets its own brain. Omitted when this
9
+ * runs as `kyb arcana`, where the repo itself is the source of truth.
10
+ */
6
11
  depts?: string[];
7
12
  }
8
13
  export declare function configureArcana({ dir, suggest, depts }: ArcanaSetup): Promise<void>;
package/dist/arcana.js CHANGED
@@ -1,5 +1,6 @@
1
- import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { upsertEnv } from "./envfile.js";
3
4
  import { ask, bold, dim, green, red, yellow } from "./util.js";
4
5
  /**
5
6
  * Collect the memory workspaces and their keys, and check them before moving on.
@@ -32,25 +33,44 @@ async function check(workspace, key) {
32
33
  return "unreachable";
33
34
  }
34
35
  }
35
- function upsertEnv(dir, values) {
36
- const p = join(dir, ".env.local");
37
- let text = existsSync(p) ? readFileSync(p, "utf8") : "";
38
- for (const [k, v] of Object.entries(values)) {
39
- if (!v)
40
- continue;
41
- const line = `${k}="${v}"`;
42
- const re = new RegExp(`^${k}=.*$`, "m");
43
- if (re.test(text))
44
- text = text.replace(re, line);
45
- else
46
- text += (text.endsWith("\n") || text === "" ? "" : "\n") + line + "\n";
36
+ /**
37
+ * The department subagents this repo actually has.
38
+ *
39
+ * Run standalone, this command used to ask only for the company and eval
40
+ * brains, because the dept list was something only `kyb init` knew. An agent
41
+ * with departments would then be set up "successfully" with every subagent
42
+ * still keyless — the exact silent-amnesia failure this command exists to
43
+ * prevent. The repo already knows; read it instead of asking init to remember.
44
+ */
45
+ function departments(dir) {
46
+ const root = join(dir, "agent", "subagents");
47
+ if (!existsSync(root))
48
+ return [];
49
+ try {
50
+ return readdirSync(root, { withFileTypes: true })
51
+ .filter((e) => e.isDirectory() && existsSync(join(root, e.name, "extensions", "arcana.ts")))
52
+ .map((e) => e.name)
53
+ .sort();
54
+ }
55
+ catch {
56
+ return [];
57
+ }
58
+ }
59
+ /** The agent's own name, for proposing workspace names that match convention. */
60
+ export function agentName(dir, fallback) {
61
+ try {
62
+ const env = readFileSync(join(dir, ".env.local"), "utf8");
63
+ const hit = /^KYBERNESIS_AGENT="?([^"\n]+)"?$/m.exec(env);
64
+ if (hit?.[1])
65
+ return hit[1];
66
+ }
67
+ catch {
68
+ /* no env yet — the directory name is the next best guess */
47
69
  }
48
- if (existsSync(p))
49
- writeFileSync(p, text);
50
- else
51
- appendFileSync(p, text);
70
+ return fallback;
52
71
  }
53
- export async function configureArcana({ dir, suggest, depts = [] }) {
72
+ export async function configureArcana({ dir, suggest, depts }) {
73
+ const subagents = depts ?? departments(dir);
54
74
  console.log(bold("\n Memory (Arcana) — workspaces and keys"));
55
75
  console.log(dim(" Create these at https://arcana.kybernesis.ai and mint a scoped kb_ key\n" +
56
76
  " for each. Names are yours — the suggestions are only a convention."));
@@ -84,7 +104,7 @@ export async function configureArcana({ dir, suggest, depts = [] }) {
84
104
  // The eval workspace name itself is derived by the npm script, so only the
85
105
  // key is stored; drop the placeholder we used to prompt with.
86
106
  delete values.ARCANA_EVAL_API_KEY_WORKSPACE;
87
- for (const dept of depts) {
107
+ for (const dept of subagents) {
88
108
  await pair(`Subagent "${dept}"`, `ARCANA_${dept.toUpperCase()}_WORKSPACE`, `ARCANA_${dept.toUpperCase()}_API_KEY`, `${suggest}-${dept}`);
89
109
  }
90
110
  // DM sessions default to the company brain unless the deployment splits them.
package/dist/cli.js CHANGED
@@ -12,14 +12,38 @@
12
12
  * kyb upgrade bump @kybernesis/* to latest, gated on the eval suite
13
13
  * --skip-eval skip the eval gate (not for production changes)
14
14
  */
15
- import { bold, dim } from "./util.js";
15
+ import { existsSync, readFileSync } from "node:fs";
16
+ import { basename, dirname, join } from "node:path";
17
+ import { fileURLToPath } from "node:url";
18
+ import { bold, dim, red } from "./util.js";
16
19
  import { init } from "./init.js";
17
20
  import { doctor } from "./doctor.js";
18
21
  import { upgrade } from "./upgrade.js";
19
22
  import { installSkills } from "./skills.js";
20
23
  import { deploy } from "./deploy.js";
21
24
  import { register } from "./register.js";
22
- import { configureArcana } from "./arcana.js";
25
+ import { agentName, configureArcana } from "./arcana.js";
26
+ /** This build's version, so a skew can name itself instead of being guessed at. */
27
+ const VERSION = (() => {
28
+ try {
29
+ const here = dirname(fileURLToPath(import.meta.url));
30
+ return JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8")).version;
31
+ }
32
+ catch {
33
+ return "unknown";
34
+ }
35
+ })();
36
+ /** Is the working directory already an eve agent, rather than a place to make one? */
37
+ function insideAgentProject() {
38
+ try {
39
+ const pkg = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf8"));
40
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
41
+ return "eve" in deps || existsSync(join(process.cwd(), "agent"));
42
+ }
43
+ catch {
44
+ return existsSync(join(process.cwd(), "agent"));
45
+ }
46
+ }
23
47
  function flag(rest, key) {
24
48
  const hit = rest.find((a) => a.startsWith(`--${key}=`));
25
49
  return hit ? hit.slice(key.length + 3) : undefined;
@@ -47,7 +71,13 @@ switch (command) {
47
71
  installSkills({ global: rest.includes("--global") });
48
72
  break;
49
73
  case "arcana":
50
- await configureArcana({ dir: process.cwd(), suggest: flag(rest, "name") ?? "agent" });
74
+ // Propose from what the repo says it is. Suggesting "agent-company" to
75
+ // someone standing in an agent called something else reads as a tool that
76
+ // has not looked at their project.
77
+ await configureArcana({
78
+ dir: process.cwd(),
79
+ suggest: flag(rest, "name") ?? agentName(process.cwd(), basename(process.cwd())),
80
+ });
51
81
  break;
52
82
  case "register":
53
83
  await register({ name: flag(rest, "name"), url: flag(rest, "url") });
@@ -63,7 +93,26 @@ switch (command) {
63
93
  break;
64
94
  default:
65
95
  if (!command.startsWith("-")) {
66
- // `npm create @kybernesis acme-agent` → argv[2] is the name.
96
+ // `npm create @kybernesis acme-agent` → argv[2] is the name. Inside an
97
+ // agent repo that reading is almost always wrong: it means a subcommand
98
+ // this build is too old to know, and scaffolding a project named after
99
+ // someone's command is a startling answer to a typo. It has already
100
+ // happened — `kyb arcana` on an older build started a whole new setup
101
+ // instead of asking for keys, so the version skew looked like a missing
102
+ // prompt and cost an afternoon.
103
+ if (insideAgentProject()) {
104
+ console.error(`
105
+ ${red(`kyb: no such command "${command}"`)}
106
+ ${dim(" (this looks like an agent project, so it was not read as a new project name)")}
107
+
108
+ This kyb is ${bold(VERSION)}. If you expected that command, it is newer:
109
+
110
+ ${bold("npm i -g @kybernesis/create@latest")}
111
+
112
+ Run ${bold("kyb")} with no arguments for the command list.
113
+ `);
114
+ process.exit(1);
115
+ }
67
116
  await init(command, initOptions(rest));
68
117
  break;
69
118
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/create",
3
- "version": "0.7.8",
3
+ "version": "0.7.9",
4
4
  "description": "The Kybernesis agent scaffolder and FDE toolkit: one command to a governed, remembering, multiplayer, self-testing eve agent — plus doctor and upgrade.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",