@kitman-sh/cli 0.7.1 → 0.7.3

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.
@@ -10,7 +10,7 @@ import {
10
10
  RunnerHeaders,
11
11
  RunnerRpcProtocolVersion,
12
12
  RunnerRpcs
13
- } from "./chunk-5rp5hvc0.js";
13
+ } from "./chunk-8g2twwm8.js";
14
14
  import {
15
15
  WebSocketConstructor,
16
16
  layerWebSocketConstructorGlobal,
@@ -19,7 +19,7 @@ import {
19
19
  make5,
20
20
  withHeaders,
21
21
  layerProtocolSocket
22
- } from "./chunk-9bje6bm0.js";
22
+ } from "./chunk-84t0s0s2.js";
23
23
 
24
24
  // packages/live/src/rpc/runner-client.ts
25
25
  var proxiedWebSocket = (proxy) => succeed4(WebSocketConstructor)((target) => {
@@ -107,7 +107,7 @@ import {
107
107
  interrupt3,
108
108
  makeUnsafe3,
109
109
  makeUnsafe4,
110
- make10,
110
+ make9,
111
111
  map4,
112
112
  filter,
113
113
  decodeText,
@@ -116,7 +116,7 @@ import {
116
116
  FileSystem,
117
117
  Path,
118
118
  ChildProcessSpawner,
119
- make12,
119
+ make11,
120
120
  Crypto,
121
121
  value,
122
122
  is,
@@ -161,7 +161,7 @@ import {
161
161
  herdrRunnerCapabilitiesForRepoBinding2,
162
162
  herdrRunnerCapabilities2,
163
163
  assertHerdrRunnerCapability2
164
- } from "./chunk-5rp5hvc0.js";
164
+ } from "./chunk-8g2twwm8.js";
165
165
  import {
166
166
  make4,
167
167
  readerString,
@@ -172,17 +172,17 @@ import {
172
172
  SocketUpgradeError,
173
173
  SocketCloseError,
174
174
  SocketError
175
- } from "./chunk-9bje6bm0.js";
176
- import"./chunk-tkk2synn.js";
175
+ } from "./chunk-84t0s0s2.js";
176
+ import"./chunk-0296rete.js";
177
177
  import {
178
178
  programLease,
179
179
  programHostsEnv,
180
180
  undeclaredHostsEnv
181
- } from "./chunk-2vcae278.js";
182
- import"./chunk-23ch5vdv.js";
181
+ } from "./chunk-1n61xbaq.js";
182
+ import"./chunk-c5nwtn11.js";
183
183
  import {
184
184
  nextInt
185
- } from "./chunk-1d2e4je3.js";
185
+ } from "./chunk-x6j2j12j.js";
186
186
 
187
187
  // packages/core/src/runner-trust.ts
188
188
  import { createHash } from "crypto";
@@ -195,6 +195,129 @@ function sha256(value) {
195
195
  return createHash("sha256").update(value, "utf8").digest("hex");
196
196
  }
197
197
 
198
+ // packages/core/src/engineering-resources.ts
199
+ var marketplace = {
200
+ source: "kitman-engineering-loop",
201
+ publisher: "Kitman Platform",
202
+ visibility: "public",
203
+ trusted: true,
204
+ reviewStatus: "approved",
205
+ installable: true,
206
+ rollout: { channel: "default", installDefault: true, preservesExistingInstallations: true }
207
+ };
208
+ var kit = (input) => ({
209
+ ...input,
210
+ metadata: { marketplace }
211
+ });
212
+ var MattPocockProcessKit = kit({
213
+ name: "Matt Pocock Process",
214
+ slug: "matt-pocock-process",
215
+ kind: "process",
216
+ description: "Adversarial, type-safe implementation and review practice for Kitman Engineering.",
217
+ contentMarkdown: [
218
+ "# Matt Pocock Process",
219
+ "",
220
+ "Use fresh-context review, TDD, Effect-native boundaries, and explicit type contracts.",
221
+ "",
222
+ "## Complete skill collection",
223
+ "",
224
+ "diagnosing-bugs, tdd, effect-ts, codebase-design, api-and-interface-design, improve-codebase-architecture, doubt-driven-development, documentation-and-adrs, postgres-drizzle, security-and-hardening, source-driven-development, test-driven-development, research."
225
+ ].join(`
226
+ `),
227
+ agentEnvironment: {
228
+ skills: [
229
+ {
230
+ name: "research",
231
+ policy: "required",
232
+ source: {
233
+ kind: "git",
234
+ url: "https://github.com/mattpocock/skills.git",
235
+ ref: "49dd158d1076134a641b33efb035946536778336",
236
+ path: "skills/engineering/research"
237
+ }
238
+ }
239
+ ]
240
+ },
241
+ agentInstructions: "Prefer the smallest correct change, preserve evidence, and stop on ambiguous public-interface decisions.",
242
+ doNotDo: "Do not execute user-controlled code in the API or Workflow worker."
243
+ });
244
+ var AddyOsmaniProcessKit = kit({
245
+ name: "Addy Osmani Process",
246
+ slug: "addy-osmani-process",
247
+ kind: "process",
248
+ description: "Incremental, accessible, observable, and production-focused delivery practice for Kitman Engineering.",
249
+ contentMarkdown: [
250
+ "# Addy Osmani Process",
251
+ "",
252
+ "Use incremental implementation, browser evidence, accessibility checks, CI discipline, and simplification.",
253
+ "",
254
+ "## Complete skill collection",
255
+ "",
256
+ "incremental-implementation, frontend-ui-engineering, browser-testing-with-devtools, web-design-guidelines, ci-cd-and-automation, code-review-and-quality, code-simplification, observability-and-instrumentation, performance-optimization, shipping-and-launch, git-workflow-and-versioning."
257
+ ].join(`
258
+ `),
259
+ agentInstructions: "Prefer verifiable slices, accessible interfaces, and evidence that survives handoff.",
260
+ doNotDo: "Do not hide failures behind broad fallbacks or claim production evidence from local fixtures."
261
+ });
262
+ var PonytailProcessKit = kit({
263
+ name: "Ponytail",
264
+ slug: "ponytail",
265
+ kind: "process",
266
+ description: "Removes unnecessary code and complexity before implementation and review complete.",
267
+ contentMarkdown: `# Ponytail
268
+
269
+ Prefer deletion, existing code, and the smallest correct change. Avoid speculative abstractions.`,
270
+ agentEnvironment: {
271
+ plugins: [
272
+ {
273
+ name: "ponytail",
274
+ policy: "required",
275
+ source: {
276
+ kind: "package",
277
+ packageName: "@dietrichgebert/ponytail",
278
+ version: "4.9.0"
279
+ }
280
+ }
281
+ ]
282
+ },
283
+ agentInstructions: "Use the required Ponytail skill before completing implementation or review.",
284
+ doNotDo: "Do not add an abstraction, dependency, or configuration without a present requirement."
285
+ });
286
+ var EngineeringTechnologyKitCatalog = [
287
+ kit({
288
+ name: "Backend",
289
+ slug: "backend",
290
+ kind: "technology",
291
+ description: "Effect, Elysia, PostgreSQL, Drizzle, Better Auth, and backend practices.",
292
+ contentMarkdown: `# Backend
293
+
294
+ Effect, Elysia, PostgreSQL, Drizzle, Better Auth, API contracts, security, and observability.`
295
+ }),
296
+ kit({
297
+ name: "Frontend",
298
+ slug: "frontend",
299
+ kind: "technology",
300
+ description: "Nuxt, Vue, Nuxt UI, TanStack Query, VueUse, accessibility, and browser testing.",
301
+ contentMarkdown: `# Frontend
302
+
303
+ Nuxt, Vue, Nuxt UI, TanStack Query, VueUse, accessibility, responsive UI, and browser testing.`
304
+ }),
305
+ kit({
306
+ name: "DevOps",
307
+ slug: "devops",
308
+ kind: "technology",
309
+ description: "Kubernetes, Kustomize, GitLab CI, and multi-stage container builds.",
310
+ contentMarkdown: `# DevOps
311
+
312
+ Kubernetes, Kustomize, GitLab CI, multi-stage container builds, and operational verification.`
313
+ })
314
+ ];
315
+ var EngineeringProcessKitCatalog = [
316
+ MattPocockProcessKit,
317
+ AddyOsmaniProcessKit,
318
+ PonytailProcessKit
319
+ ];
320
+
198
321
  // packages/agent-runtime/src/agent-environment.ts
199
322
  class AgentEnvironmentResolutionError extends TaggedError("AgentEnvironmentResolutionError") {
200
323
  get message() {
@@ -1596,6 +1719,9 @@ function repositoryUrl(repoBinding) {
1596
1719
  return repoBinding.repositoryUrl ?? `https://${repoBinding.host}/${repoBinding.owner}/${repoBinding.name}.git`;
1597
1720
  }
1598
1721
  // packages/agent-runtime/src/skill-materializer.ts
1722
+ var gitSourceKey = (source) => `${source.url}#${source.ref}:${source.path ?? ""}`;
1723
+ var reviewedGitSkillSources = new Set([...EngineeringProcessKitCatalog, ...EngineeringTechnologyKitCatalog].flatMap((kit) => kit.agentEnvironment?.skills ?? []).flatMap((skill) => skill.source.kind === "git" ? [gitSourceKey(skill.source)] : []));
1724
+
1599
1725
  class AgentEnvironmentMaterializationError extends TaggedError("AgentEnvironmentMaterializationError") {
1600
1726
  get message() {
1601
1727
  return `AgentEnvironment materialization failed: ${this.issues.map((issue) => issue.message).join("; ")}`;
@@ -1616,35 +1742,50 @@ var previewAgentEnvironmentMaterialization = fnUntraced(function* (input) {
1616
1742
  for (const skill of input.environment.skills) {
1617
1743
  const skillKey = skillIdentity(skill.item);
1618
1744
  const source = skill.item.source;
1619
- const skip = (message) => {
1620
- const issue = { message, skillName: skill.item.name, skillKey };
1745
+ const fromKit = skill.source.label === undefined ? "" : ` (from Kit ${skill.source.label})`;
1746
+ const problem = source.kind === "kitman" ? source.contentMarkdown === undefined ? `Kitman skill ${skill.item.name} has no contentMarkdown to materialize.` : undefined : input.noInstall === true ? `Skipping materialization for ${skill.item.name} because sync was run with noInstall.` : source.kind !== "git" ? `Skill ${skill.item.name}${fromKit} comes from ${source.kind}, which Kitman cannot install yet; only Kitman and git skills are installed. Remove it from the Kit or make it recommended.` : !source.url.startsWith("https://") ? `Git skill ${skill.item.name} must come from an https URL, not ${source.url}.` : !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u.test(source.ref) ? `Git skill ${skill.item.name} must be pinned to a commit, not ${source.ref}, so Kitman installs only the reviewed version.` : !reviewedGitSkillSources.has(gitSourceKey(source)) && !(input.approvedSkillSources ?? []).includes(gitSourceKey(source)) ? `Git skill ${skill.item.name}${fromKit} at ${gitSourceKey(source)} is not one Kitman reviewed; approve it by adding that source to approvedSkillSources in your Kitman user config, or remove it from the Kit.` : undefined;
1747
+ if (problem !== undefined) {
1748
+ const issue = { message: problem, skillName: skill.item.name, skillKey };
1621
1749
  if (skill.item.policy === "required")
1622
- return new AgentEnvironmentMaterializationError({ issues: [issue] });
1750
+ return yield* new AgentEnvironmentMaterializationError({ issues: [issue] });
1623
1751
  warnings.push(issue);
1624
- return;
1625
- };
1626
- const refusal = source.kind !== "kitman" ? skip(input.noInstall === true ? `Skipping materialization for ${skill.item.name} because sync was run with noInstall.` : `Materializing ${skill.item.name} from ${source.kind} is not supported; only Kitman skills are materialized.`) : source.contentMarkdown === undefined ? skip(`Kitman skill ${skill.item.name} has no contentMarkdown to materialize.`) : undefined;
1627
- if (refusal !== undefined)
1628
- return yield* refusal;
1629
- if (source.kind !== "kitman" || source.contentMarkdown === undefined)
1630
1752
  continue;
1631
- const skillPath = path.join(sourceRoot, "kitman", hashSegment(source.skillId ?? source.name ?? skill.item.name), skillSlug2(skill.item.name));
1632
- operations.push({
1633
- kind: "write_skill",
1634
- skillName: skill.item.name,
1635
- skillKey,
1636
- path: path.join(skillPath, "SKILL.md"),
1637
- content: skillMarkdown(skill.item.name, source.contentMarkdown)
1638
- }, {
1639
- kind: "symlink_skill",
1640
- skillName: skill.item.name,
1641
- skillKey,
1642
- sourcePath: skillPath,
1643
- targetPath: path.join(targetRoot, `${skillSlug2(skill.item.name)}-${hashSegment(skillKey)}`),
1644
- sourceRoot,
1645
- targetRoot
1646
- });
1647
- materializedSkills.push({ name: skill.item.name, key: skillKey, harness, source, skillPath, managed: true });
1753
+ }
1754
+ const expose = (skillPath) => {
1755
+ operations.push({
1756
+ kind: "symlink_skill",
1757
+ skillName: skill.item.name,
1758
+ skillKey,
1759
+ sourcePath: skillPath,
1760
+ targetPath: path.join(targetRoot, `${skillSlug2(skill.item.name)}-${hashSegment(skillKey)}`),
1761
+ sourceRoot,
1762
+ targetRoot
1763
+ });
1764
+ materializedSkills.push({ name: skill.item.name, key: skillKey, harness, source, skillPath, managed: true });
1765
+ };
1766
+ if (source.kind === "git") {
1767
+ const skillPath = path.join(sourceRoot, "git", hashSegment(skillKey), skillSlug2(skill.item.name));
1768
+ operations.push({
1769
+ kind: "fetch_git_skill",
1770
+ skillName: skill.item.name,
1771
+ skillKey,
1772
+ url: source.url,
1773
+ ref: source.ref,
1774
+ ...source.path === undefined ? undefined : { repositoryPath: source.path },
1775
+ path: skillPath
1776
+ });
1777
+ expose(skillPath);
1778
+ } else if (source.kind === "kitman" && source.contentMarkdown !== undefined) {
1779
+ const skillPath = path.join(sourceRoot, "kitman", hashSegment(source.skillId ?? source.name ?? skill.item.name), skillSlug2(skill.item.name));
1780
+ operations.push({
1781
+ kind: "write_skill",
1782
+ skillName: skill.item.name,
1783
+ skillKey,
1784
+ path: path.join(skillPath, "SKILL.md"),
1785
+ content: skillMarkdown(skill.item.name, source.contentMarkdown)
1786
+ });
1787
+ expose(skillPath);
1788
+ }
1648
1789
  }
1649
1790
  return { harness, operations, materializedSkills, warnings };
1650
1791
  });
@@ -1652,28 +1793,74 @@ var applyAgentEnvironmentMaterialization = fnUntraced(function* (plan) {
1652
1793
  for (const operation of plan.operations) {
1653
1794
  if (operation.kind === "write_skill")
1654
1795
  yield* writeSkill(plan, operation);
1796
+ else if (operation.kind === "fetch_git_skill")
1797
+ yield* fetchGitSkill(plan, operation);
1655
1798
  else
1656
1799
  yield* exposeSkill(operation);
1657
1800
  }
1658
1801
  return plan;
1659
1802
  });
1660
1803
  var refuse = (message) => fail4(new SkillMaterializationRefused({ message }));
1661
- var writeSkill = (plan, operation) => gen(function* () {
1662
- const fs = yield* FileSystem;
1804
+ var managedSkillPath = (plan, skillKey, file) => gen(function* () {
1663
1805
  const path = yield* Path;
1664
- const boundary = plan.operations.find((candidate) => candidate.kind === "symlink_skill" && candidate.skillKey === operation.skillKey);
1806
+ const boundary = plan.operations.find((candidate) => candidate.kind === "symlink_skill" && candidate.skillKey === skillKey);
1665
1807
  if (boundary === undefined)
1666
1808
  return yield* refuse("SKILL_SYMLINK_BOUNDARY_MISSING");
1667
1809
  const root = path.resolve(boundary.sourceRoot);
1668
- const target = path.resolve(operation.path);
1810
+ const target = path.resolve(file);
1669
1811
  if (!isPathWithin(path, root, target) || target === root)
1670
1812
  return yield* refuse("SKILL_WRITE_OUTSIDE_ROOT");
1671
1813
  yield* assertNoSymlinkComponents(path.dirname(target), "SKILL_WRITE_OUTSIDE_ROOT");
1814
+ return target;
1815
+ });
1816
+ var writeSkill = (plan, operation) => gen(function* () {
1817
+ const fs = yield* FileSystem;
1818
+ const path = yield* Path;
1819
+ const target = yield* managedSkillPath(plan, operation.skillKey, operation.path);
1672
1820
  if (yield* isSymbolicLink(target))
1673
1821
  return yield* refuse("SKILL_WRITE_SYMLINK");
1674
1822
  yield* fs.makeDirectory(path.dirname(target), { recursive: true });
1675
1823
  yield* fs.writeFileString(target, operation.content);
1676
1824
  });
1825
+ var fetchGitSkill = (plan, operation) => gen(function* () {
1826
+ const fs = yield* FileSystem;
1827
+ const path = yield* Path;
1828
+ const target = yield* managedSkillPath(plan, operation.skillKey, operation.path);
1829
+ const scratch = yield* fs.makeTempDirectoryScoped({ prefix: "kitman-git-skill-" });
1830
+ const repository = path.join(scratch, "repository");
1831
+ const tree = path.join(scratch, "tree");
1832
+ const git = (...args) => runCommand2("git", ["--git-dir", repository, ...args]);
1833
+ yield* fs.makeDirectory(tree);
1834
+ yield* runCommand2("git", ["init", "--quiet", "--bare", repository]);
1835
+ yield* git("fetch", "--quiet", "--depth", "1", "--", operation.url, operation.ref);
1836
+ yield* git("--work-tree", tree, "checkout", "FETCH_HEAD", "--", ".");
1837
+ const skillDirectory = path.resolve(tree, operation.repositoryPath ?? ".");
1838
+ if (!isPathWithin(path, tree, skillDirectory))
1839
+ return yield* refuse("SKILL_SOURCE_OUTSIDE_ROOT");
1840
+ if (yield* containsSymbolicLink(tree, skillDirectory))
1841
+ return yield* refuse(`Git skill ${operation.skillName} links to a file outside itself in ${operation.url}@${operation.ref}; Kitman does not install linked files.`);
1842
+ if (!(yield* fs.exists(path.join(skillDirectory, "SKILL.md"))))
1843
+ return yield* refuse(`Git skill ${operation.skillName} has no SKILL.md at ${operation.repositoryPath ?? "the repository root"} in ${operation.url}@${operation.ref}.`);
1844
+ const staged = `${target}.partial`;
1845
+ yield* fs.remove(staged, { recursive: true, force: true });
1846
+ yield* fs.makeDirectory(path.dirname(target), { recursive: true });
1847
+ yield* fs.copy(skillDirectory, staged);
1848
+ yield* fs.remove(target, { recursive: true, force: true });
1849
+ yield* fs.rename(staged, target);
1850
+ }).pipe(scoped);
1851
+ var containsSymbolicLink = (root, directory) => gen(function* () {
1852
+ const fs = yield* FileSystem;
1853
+ const path = yield* Path;
1854
+ for (let current = directory;current !== root; current = path.dirname(current))
1855
+ if (yield* isSymbolicLink(current))
1856
+ return true;
1857
+ if (!(yield* fs.exists(directory)))
1858
+ return false;
1859
+ for (const entry of yield* fs.readDirectory(directory, { recursive: true }))
1860
+ if (yield* isSymbolicLink(path.join(directory, entry)))
1861
+ return true;
1862
+ return false;
1863
+ });
1677
1864
  var exposeSkill = (operation) => gen(function* () {
1678
1865
  const fs = yield* FileSystem;
1679
1866
  const path = yield* Path;
@@ -2494,7 +2681,7 @@ var temporaryDirectory = map3(optionalEnv("TMPDIR"), (tmp) => tmp ?? "/tmp");
2494
2681
  var spawnServer = (input) => gen(function* () {
2495
2682
  const spawner = yield* ChildProcessSpawner;
2496
2683
  const scope = yield* make6();
2497
- const handle = yield* spawner.spawn(make12(input.command, [...input.args], {
2684
+ const handle = yield* spawner.spawn(make11(input.command, [...input.args], {
2498
2685
  env: { HERDR_SOCKET_PATH: input.socketPath },
2499
2686
  extendEnv: true,
2500
2687
  detached: true,
@@ -4368,7 +4555,8 @@ var previewAgentEnvironmentPlan = fnUntraced(function* (input) {
4368
4555
  ...input.rootDir === undefined ? undefined : { rootDir: input.rootDir },
4369
4556
  ...input.scope === undefined ? undefined : { scope: input.scope },
4370
4557
  ...input.cacheRoot === undefined ? undefined : { cacheRoot: input.cacheRoot },
4371
- ...input.noInstall === undefined ? undefined : { noInstall: input.noInstall }
4558
+ ...input.noInstall === undefined ? undefined : { noInstall: input.noInstall },
4559
+ ...input.approvedSkillSources === undefined ? undefined : { approvedSkillSources: input.approvedSkillSources }
4372
4560
  });
4373
4561
  const harnessConfig = yield* previewAgentEnvironmentConfigSync({
4374
4562
  environment: input.environment,
@@ -4451,10 +4639,10 @@ var handshake = [
4451
4639
  `).join("");
4452
4640
  var kitmanMcpPreflight = (mcp) => gen(function* () {
4453
4641
  const spawner = yield* ChildProcessSpawner;
4454
- const server = yield* spawner.spawn(make12(mcp.command, [...mcp.args], {
4642
+ const server = yield* spawner.spawn(make11(mcp.command, [...mcp.args], {
4455
4643
  extendEnv: true,
4456
4644
  env: mcp.env,
4457
- stdin: { stream: make10(new TextEncoder().encode(handshake)), endOnDone: false },
4645
+ stdin: { stream: make9(new TextEncoder().encode(handshake)), endOnDone: false },
4458
4646
  stderr: "ignore"
4459
4647
  }));
4460
4648
  const reply = yield* server.stdout.pipe(decodeText, splitLines, map4((line) => recordValue(getOrUndefined(decodeJsonStringOption(line)))), filter((message) => message?.id === 2), runHead);
@@ -4526,8 +4714,8 @@ var programProcessExecutor = (input) => (work) => gen(function* () {
4526
4714
  };
4527
4715
  const exitCode = sandbox === undefined ? yield* gen(function* () {
4528
4716
  const spawner = yield* ChildProcessSpawner;
4529
- const handle = yield* spawner.spawn(make12(input.bun ?? process.execPath, [file], {
4530
- stdin: make10(new TextEncoder().encode(stdin)),
4717
+ const handle = yield* spawner.spawn(make11(input.bun ?? process.execPath, [file], {
4718
+ stdin: make9(new TextEncoder().encode(stdin)),
4531
4719
  stdout: "inherit",
4532
4720
  stderr: "inherit",
4533
4721
  extendEnv: false,