@headways/cli 0.4.0 → 0.4.2

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.
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  apiRequest,
4
4
  rawRequest
5
- } from "./chunk-HYEL7L5Z.js";
6
- import "./chunk-T2H7EXOV.js";
5
+ } from "./chunk-2INXZHRG.js";
6
+ import "./chunk-UUFIIGTZ.js";
7
7
  export {
8
8
  apiRequest,
9
9
  rawRequest
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getApiUrl,
4
4
  requireAuth
5
- } from "./chunk-T2H7EXOV.js";
5
+ } from "./chunk-UUFIIGTZ.js";
6
6
 
7
7
  // src/lib/api.ts
8
8
  async function rawRequest(path, token, options = {}, apiUrl) {
@@ -8,6 +8,7 @@ var HEADWAYS_DIR = join(homedir(), ".headways");
8
8
  var CONFIG_FILE = join(HEADWAYS_DIR, "config.json");
9
9
  var CATALOG_FILE = join(HEADWAYS_DIR, "catalog.json");
10
10
  var INSTALLED_DIR = join(HEADWAYS_DIR, "installed");
11
+ var CLAUDE_SKILLS_DIR = join(homedir(), ".claude", "skills");
11
12
  function readConfig() {
12
13
  if (!existsSync(CONFIG_FILE)) return {};
13
14
  try {
@@ -42,6 +43,7 @@ export {
42
43
  CONFIG_FILE,
43
44
  CATALOG_FILE,
44
45
  INSTALLED_DIR,
46
+ CLAUDE_SKILLS_DIR,
45
47
  readConfig,
46
48
  writeConfig,
47
49
  getApiUrl,
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ CLAUDE_SKILLS_DIR,
3
4
  HEADWAYS_DIR,
4
5
  getApiUrl,
5
6
  readConfig
6
- } from "./chunk-T2H7EXOV.js";
7
+ } from "./chunk-UUFIIGTZ.js";
7
8
 
8
9
  // src/commands/sync/index.ts
9
10
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync, rmSync as rmSync2 } from "fs";
10
- import { homedir as homedir2 } from "os";
11
11
  import { join as join2 } from "path";
12
12
  import { createGunzip } from "zlib";
13
13
  import { Readable } from "stream";
@@ -53,9 +53,8 @@ function registerUninstallCommand(program) {
53
53
  removeHooks(CLAUDE_SETTINGS_GLOBAL);
54
54
  removeHooks(CLAUDE_SETTINGS_LOCAL);
55
55
  console.log("\u2713 Removed Claude Code hooks");
56
- const skillsDir = join(homedir(), ".claude", "skills");
57
- if (existsSync(skillsDir)) {
58
- rmSync(skillsDir, { recursive: true, force: true });
56
+ if (existsSync(CLAUDE_SKILLS_DIR)) {
57
+ rmSync(CLAUDE_SKILLS_DIR, { recursive: true, force: true });
59
58
  console.log("\u2713 Removed ~/.claude/skills/");
60
59
  }
61
60
  const headwaysDir = join(homedir(), ".headways");
@@ -232,9 +231,8 @@ async function downloadAndMaterialize(slug, version, state, apiUrl) {
232
231
  });
233
232
  if (!res.ok) throw new Error(`Bundle fetch failed: ${res.status}`);
234
233
  const buf = Buffer.from(await res.arrayBuffer());
235
- const skillsDir = join2(homedir2(), ".claude", "skills");
236
- const dest = join2(skillsDir, slug);
237
- const staging = join2(skillsDir, `.${slug}-staging`);
234
+ const dest = join2(CLAUDE_SKILLS_DIR, slug);
235
+ const staging = join2(CLAUDE_SKILLS_DIR, `.${slug}-staging`);
238
236
  mkdirSync2(staging, { recursive: true });
239
237
  await extractTarGz(buf, staging);
240
238
  if (existsSync2(dest)) rmSync2(dest, { recursive: true });
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CATALOG_FILE,
4
+ CLAUDE_SKILLS_DIR,
4
5
  CONFIG_FILE,
5
6
  HEADWAYS_DIR,
6
7
  INSTALLED_DIR,
@@ -9,9 +10,10 @@ import {
9
10
  readConfig,
10
11
  requireAuth,
11
12
  writeConfig
12
- } from "./chunk-T2H7EXOV.js";
13
+ } from "./chunk-UUFIIGTZ.js";
13
14
  export {
14
15
  CATALOG_FILE,
16
+ CLAUDE_SKILLS_DIR,
15
17
  CONFIG_FILE,
16
18
  HEADWAYS_DIR,
17
19
  INSTALLED_DIR,
package/dist/index.js CHANGED
@@ -2,25 +2,72 @@
2
2
  import {
3
3
  apiRequest,
4
4
  rawRequest
5
- } from "./chunk-HYEL7L5Z.js";
5
+ } from "./chunk-2INXZHRG.js";
6
6
  import {
7
7
  registerSetupCommand,
8
8
  registerSyncCommands,
9
9
  registerUninstallCommand
10
- } from "./chunk-OZULVVQC.js";
10
+ } from "./chunk-XTEQBKIN.js";
11
11
  import {
12
+ CLAUDE_SKILLS_DIR,
12
13
  INSTALLED_DIR,
13
14
  getApiUrl,
14
15
  getAppUrl,
15
16
  readConfig,
16
17
  requireAuth,
17
18
  writeConfig
18
- } from "./chunk-T2H7EXOV.js";
19
+ } from "./chunk-UUFIIGTZ.js";
19
20
 
20
21
  // src/index.ts
21
22
  import "dotenv/config";
22
23
  import { program } from "commander";
23
24
 
25
+ // package.json
26
+ var package_default = {
27
+ name: "@headways/cli",
28
+ version: "0.4.2",
29
+ type: "module",
30
+ description: "Headways CLI \u2014 authoring, sync, and runtime SDK",
31
+ license: "MIT",
32
+ files: [
33
+ "dist",
34
+ "LICENSE",
35
+ "README.md"
36
+ ],
37
+ bin: {
38
+ headways: "./dist/index.js"
39
+ },
40
+ publishConfig: {
41
+ access: "public"
42
+ },
43
+ scripts: {
44
+ build: "tsup",
45
+ dev: "tsx src/index.ts",
46
+ test: "vitest run",
47
+ "test:unit": "vitest run",
48
+ "type-check": "tsc -p tsconfig.json --noEmit",
49
+ prepublishOnly: "pnpm build"
50
+ },
51
+ dependencies: {
52
+ "@headways/db": "workspace:*",
53
+ "@modelcontextprotocol/sdk": "^1.15.0",
54
+ chalk: "^5.3.0",
55
+ commander: "^12.1.0",
56
+ dotenv: "^16.4.7",
57
+ "node-fetch": "^3.3.2",
58
+ yaml: "^2.5.1",
59
+ zod: "^3.25.28"
60
+ },
61
+ devDependencies: {
62
+ "@headways/config": "workspace:*",
63
+ "@types/node": "^22.16.5",
64
+ tsup: "^8.5.1",
65
+ tsx: "^4.21.0",
66
+ typescript: "^5.8.3",
67
+ vitest: "^3.2.4"
68
+ }
69
+ };
70
+
24
71
  // src/commands/auth.ts
25
72
  import "commander";
26
73
  import * as http from "http";
@@ -382,34 +429,35 @@ function registerImportCommand(program2) {
382
429
  import "commander";
383
430
  import * as fs3 from "fs/promises";
384
431
  import * as path3 from "path";
385
- import { watch } from "fs";
432
+ import { watch, existsSync } from "fs";
433
+ var catchMissing = (e) => {
434
+ if (e.code === "ENOENT") return null;
435
+ throw e;
436
+ };
386
437
  async function readSkillDir(dir) {
387
438
  const skillMdPath = path3.join(dir, "SKILL.md");
388
- const body = await fs3.readFile(skillMdPath, "utf-8").catch(() => "");
389
- let headline;
390
- const headwaysYamlPath = path3.join(dir, "headways.yaml");
439
+ let body;
391
440
  try {
392
- const yaml = await fs3.readFile(headwaysYamlPath, "utf-8");
393
- const match = yaml.match(/headline:\s*['"]?(.+?)['"]?\s*$/m);
394
- if (match) headline = match[1] ?? void 0;
441
+ body = await fs3.readFile(skillMdPath, "utf-8");
395
442
  } catch {
443
+ throw new Error(`SKILL.md not found in ${dir}`);
396
444
  }
397
- let capabilities;
398
- const capYamlPath = path3.join(dir, "capabilities.yaml");
399
- try {
400
- const capYaml = await fs3.readFile(capYamlPath, "utf-8");
401
- capabilities = { raw: capYaml };
402
- } catch {
445
+ const [headwaysYaml, capabilitiesYaml, connectionsYaml] = await Promise.all([
446
+ fs3.readFile(path3.join(dir, "headways.yaml"), "utf-8").catch(catchMissing),
447
+ fs3.readFile(path3.join(dir, "capabilities.yaml"), "utf-8").catch(catchMissing),
448
+ fs3.readFile(path3.join(dir, "connections.yaml"), "utf-8").catch(catchMissing)
449
+ ]);
450
+ let headline;
451
+ if (headwaysYaml) {
452
+ const match = headwaysYaml.match(/headline:\s*['"]?(.+?)['"]?\s*$/m);
453
+ if (match) headline = match[1] ?? void 0;
403
454
  }
404
455
  let connections;
405
- const connYamlPath = path3.join(dir, "connections.yaml");
406
- try {
407
- const connYaml = await fs3.readFile(connYamlPath, "utf-8");
408
- const items = parseConnectionsYaml(connYaml);
456
+ if (connectionsYaml) {
457
+ const items = parseConnectionsYaml(connectionsYaml);
409
458
  if (items.length > 0) connections = items;
410
- } catch {
411
459
  }
412
- return { body, headline, capabilities, connections };
460
+ return { body, headline, capabilities: capabilitiesYaml ?? void 0, connections };
413
461
  }
414
462
  function parseConnectionsYaml(yaml) {
415
463
  const items = [];
@@ -446,11 +494,17 @@ async function pushSkill(slug, dir) {
446
494
  });
447
495
  console.log(`Pushed '${slug}' draft`);
448
496
  }
497
+ function resolveSkillDir(slug) {
498
+ if (!slug) return process.cwd();
499
+ const installedPath = path3.join(CLAUDE_SKILLS_DIR, slug);
500
+ if (existsSync(installedPath)) return installedPath;
501
+ return path3.join(process.cwd(), slug);
502
+ }
449
503
  function registerPushCommand(program2) {
450
- program2.command("push [slug]").description("Push local skill files as a draft to Headways").option("--watch", "Watch for file changes and auto-push").option("--dir <dir>", "Skill directory (default: ./<slug> or cwd)").action(async (slug, opts) => {
504
+ program2.command("push [slug]").description("Push local skill files as a draft to Headways").option("--watch", "Watch for file changes and auto-push").option("--dir <dir>", "Skill directory (default: installed location, then ./<slug>)").action(async (slug, opts) => {
451
505
  requireAuth();
452
506
  const resolvedSlug = slug ?? path3.basename(process.cwd());
453
- const dir = opts.dir ?? (slug ? path3.join(process.cwd(), slug) : process.cwd());
507
+ const dir = opts.dir ?? resolveSkillDir(slug);
454
508
  await pushSkill(resolvedSlug, dir);
455
509
  if (opts.watch) {
456
510
  console.log(`Watching ${dir} for changes...`);
@@ -552,7 +606,7 @@ Declare every MCP connector the skill depends on. Users see this list on \`headw
552
606
  and the Headways app gates installation on the connectors being configured.
553
607
 
554
608
  \`\`\`yaml
555
- - connector: slack # connector identifier (e.g. slack, github, jira, linear, notion, google-drive)
609
+ - connector: slack # connector identifier (e.g. slack, github, atlassian, linear, notion, google-drive, stripe, asana, hubspot, datadog)
556
610
  purpose: Read channel messages and threads via Slack MCP tools
557
611
  - connector: github
558
612
  purpose: Read pull requests and issues
@@ -627,8 +681,8 @@ function registerSkillsCommands(program2) {
627
681
  console.log(SKILLS_GUIDE);
628
682
  });
629
683
  skills.command("list").description("List skills in the active org").action(async () => {
630
- const { requireAuth: requireAuth2 } = await import("./config-SHMIVRAP.js");
631
- const { apiRequest: apiRequest2 } = await import("./api-2BK6MGZB.js");
684
+ const { requireAuth: requireAuth2 } = await import("./config-XQHAXREA.js");
685
+ const { apiRequest: apiRequest2 } = await import("./api-5EKGGFQ6.js");
632
686
  requireAuth2();
633
687
  const result = await apiRequest2("/v1/skills");
634
688
  if (result.data.length === 0) {
@@ -640,10 +694,10 @@ function registerSkillsCommands(program2) {
640
694
  }
641
695
  });
642
696
  skills.command("accept <slug>").description("Accept a pending skill update and install it locally").action(async (slug) => {
643
- const { acceptSkill } = await import("./sync-6PKI35ZY.js");
697
+ const { acceptSkill } = await import("./sync-Q3OQUWOD.js");
644
698
  await acceptSkill(slug);
645
699
  try {
646
- const { apiRequest: apiRequest2 } = await import("./api-2BK6MGZB.js");
700
+ const { apiRequest: apiRequest2 } = await import("./api-5EKGGFQ6.js");
647
701
  const metadata = await apiRequest2(`/v1/skills/${slug}/bundle/metadata`);
648
702
  const reqs = metadata.connectionRequirements ?? [];
649
703
  if (reqs.length > 0) {
@@ -747,7 +801,7 @@ function registerEmitCommand(program2) {
747
801
  }
748
802
 
749
803
  // src/commands/prime.ts
750
- import { existsSync, readdirSync, readFileSync } from "fs";
804
+ import { existsSync as existsSync2, readdirSync, readFileSync } from "fs";
751
805
  import "commander";
752
806
  function registerPrimeCommand(program2) {
753
807
  program2.command("prime").description("Output Headways workflow context for AI coding assistants").action(() => {
@@ -823,7 +877,7 @@ function registerPrimeCommand(program2) {
823
877
  });
824
878
  }
825
879
  function getInstalledSkills() {
826
- if (!existsSync(INSTALLED_DIR)) return [];
880
+ if (!existsSync2(INSTALLED_DIR)) return [];
827
881
  try {
828
882
  return readdirSync(INSTALLED_DIR).filter((f) => f.endsWith(".json")).map((f) => {
829
883
  const slug = f.replace(/\.json$/, "");
@@ -846,7 +900,7 @@ function getInstalledSkills() {
846
900
  }
847
901
 
848
902
  // src/index.ts
849
- program.name("headways").description("Headways CLI \u2014 skill authoring, sync, and runtime SDK").version("0.2.1");
903
+ program.name("headways").description("Headways CLI \u2014 skill authoring, sync, and runtime SDK").version(package_default.version);
850
904
  registerAuthCommands(program);
851
905
  registerSkillsCommands(program);
852
906
  registerConnectionsCommands(program);
@@ -5,8 +5,8 @@ import {
5
5
  registerDevice,
6
6
  registerSyncCommands,
7
7
  writeSyncState
8
- } from "./chunk-OZULVVQC.js";
9
- import "./chunk-T2H7EXOV.js";
8
+ } from "./chunk-XTEQBKIN.js";
9
+ import "./chunk-UUFIIGTZ.js";
10
10
  export {
11
11
  acceptSkill,
12
12
  readSyncState,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@headways/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "description": "Headways CLI — authoring, sync, and runtime SDK",
6
6
  "license": "MIT",