@headways/cli 0.3.0 → 0.4.0
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/index.js +104 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,10 +79,7 @@ function registerAuthCommands(program2) {
|
|
|
79
79
|
});
|
|
80
80
|
configure.command("clear").description("Clear credentials and reset setup state (keeps API and app URLs)").action(() => {
|
|
81
81
|
const cfg = readConfig();
|
|
82
|
-
|
|
83
|
-
if (cfg.apiUrl) cleared.apiUrl = cfg.apiUrl;
|
|
84
|
-
if (cfg.appUrl) cleared.appUrl = cfg.appUrl;
|
|
85
|
-
writeConfig(cleared);
|
|
82
|
+
writeConfig({ apiUrl: cfg.apiUrl, appUrl: cfg.appUrl });
|
|
86
83
|
console.log("Credentials cleared. The desktop app will show onboarding on next launch.");
|
|
87
84
|
});
|
|
88
85
|
configure.command("status").description("Show current API key and org").action(() => {
|
|
@@ -282,6 +279,13 @@ runtimes: [claude-code]
|
|
|
282
279
|
kind: outcome
|
|
283
280
|
description: 'Captures the main output artifact'
|
|
284
281
|
schema: {}
|
|
282
|
+
`
|
|
283
|
+
);
|
|
284
|
+
await fs.writeFile(
|
|
285
|
+
path.join(dir, "connections.yaml"),
|
|
286
|
+
`# connections:
|
|
287
|
+
# - connector: slack
|
|
288
|
+
# purpose: "Post skill output to a Slack channel"
|
|
285
289
|
`
|
|
286
290
|
);
|
|
287
291
|
await fs.mkdir(path.join(dir, "fixtures"), { recursive: true });
|
|
@@ -397,16 +401,47 @@ async function readSkillDir(dir) {
|
|
|
397
401
|
capabilities = { raw: capYaml };
|
|
398
402
|
} catch {
|
|
399
403
|
}
|
|
400
|
-
|
|
404
|
+
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);
|
|
409
|
+
if (items.length > 0) connections = items;
|
|
410
|
+
} catch {
|
|
411
|
+
}
|
|
412
|
+
return { body, headline, capabilities, connections };
|
|
413
|
+
}
|
|
414
|
+
function parseConnectionsYaml(yaml) {
|
|
415
|
+
const items = [];
|
|
416
|
+
const connectorRe = /^\s*-\s+connector:\s*(.+)$/;
|
|
417
|
+
const purposeRe = /^\s+purpose:\s*["']?(.+?)["']?\s*$/;
|
|
418
|
+
const lines = yaml.split("\n");
|
|
419
|
+
let current = null;
|
|
420
|
+
for (const line of lines) {
|
|
421
|
+
if (line.trimStart().startsWith("#")) continue;
|
|
422
|
+
const connMatch = connectorRe.exec(line);
|
|
423
|
+
if (connMatch) {
|
|
424
|
+
if (current?.connector && current.purpose) items.push(current);
|
|
425
|
+
current = { connector: (connMatch[1] ?? "").trim() };
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (current) {
|
|
429
|
+
const purposeMatch = purposeRe.exec(line);
|
|
430
|
+
if (purposeMatch) current.purpose = (purposeMatch[1] ?? "").trim();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (current?.connector && current.purpose) items.push(current);
|
|
434
|
+
return items;
|
|
401
435
|
}
|
|
402
436
|
async function pushSkill(slug, dir) {
|
|
403
|
-
const { body, headline, capabilities } = await readSkillDir(dir);
|
|
437
|
+
const { body, headline, capabilities, connections } = await readSkillDir(dir);
|
|
404
438
|
await apiRequest(`/v1/skills/${slug}/draft`, {
|
|
405
439
|
method: "PUT",
|
|
406
440
|
body: JSON.stringify({
|
|
407
441
|
body,
|
|
408
442
|
...headline ? { headline } : {},
|
|
409
|
-
...capabilities ? { capabilities } : {}
|
|
443
|
+
...capabilities ? { capabilities } : {},
|
|
444
|
+
...connections ? { connections } : {}
|
|
410
445
|
})
|
|
411
446
|
});
|
|
412
447
|
console.log(`Pushed '${slug}' draft`);
|
|
@@ -469,6 +504,7 @@ headways skills push <slug> # push local edits as a draft
|
|
|
469
504
|
SKILL.md # skill body \u2014 instructions for the AI agent
|
|
470
505
|
headways.yaml # metadata: slug, name, headline, channel, runtimes
|
|
471
506
|
capabilities.yaml # what the skill is allowed to do
|
|
507
|
+
connections.yaml # MCP connectors this skill requires (optional)
|
|
472
508
|
hooks.yaml # structured hooks the skill exposes (optional)
|
|
473
509
|
\`\`\`
|
|
474
510
|
|
|
@@ -510,6 +546,20 @@ data_classes: none # none | pii | phi | pci
|
|
|
510
546
|
auto_send: false # true = skill may act without user confirmation
|
|
511
547
|
\`\`\`
|
|
512
548
|
|
|
549
|
+
### connections.yaml (required for any skill that uses MCP connector tools)
|
|
550
|
+
|
|
551
|
+
Declare every MCP connector the skill depends on. Users see this list on \`headways skills accept\`
|
|
552
|
+
and the Headways app gates installation on the connectors being configured.
|
|
553
|
+
|
|
554
|
+
\`\`\`yaml
|
|
555
|
+
- connector: slack # connector identifier (e.g. slack, github, jira, linear, notion, google-drive)
|
|
556
|
+
purpose: Read channel messages and threads via Slack MCP tools
|
|
557
|
+
- connector: github
|
|
558
|
+
purpose: Read pull requests and issues
|
|
559
|
+
\`\`\`
|
|
560
|
+
|
|
561
|
+
Omit the file entirely if the skill has no connector dependencies.
|
|
562
|
+
|
|
513
563
|
### hooks.yaml (omit if unused)
|
|
514
564
|
|
|
515
565
|
\`\`\`yaml
|
|
@@ -565,6 +615,7 @@ local edits with \`headways skills push <slug>\`.
|
|
|
565
615
|
- Headline > 90 chars \u2192 submit blocked with 422. Shorten before pushing.
|
|
566
616
|
- Uppercase or special chars in slug \u2192 rejected at creation. Use \`a-z\`, \`0-9\`, \`-\` only.
|
|
567
617
|
- Missing \`capabilities.yaml\` entries \u2192 skill silently blocked at runtime.
|
|
618
|
+
- Missing \`connections.yaml\` for MCP-dependent skills \u2192 users install the skill but hit tool-not-found errors at runtime with no explanation. Always create this file when the skill calls MCP tools.
|
|
568
619
|
- Passive or noun-phrase headline \u2192 poor discoverability; rewrite as a verb phrase.
|
|
569
620
|
`.trim();
|
|
570
621
|
function registerSkillsCommands(program2) {
|
|
@@ -591,6 +642,23 @@ function registerSkillsCommands(program2) {
|
|
|
591
642
|
skills.command("accept <slug>").description("Accept a pending skill update and install it locally").action(async (slug) => {
|
|
592
643
|
const { acceptSkill } = await import("./sync-6PKI35ZY.js");
|
|
593
644
|
await acceptSkill(slug);
|
|
645
|
+
try {
|
|
646
|
+
const { apiRequest: apiRequest2 } = await import("./api-2BK6MGZB.js");
|
|
647
|
+
const metadata = await apiRequest2(`/v1/skills/${slug}/bundle/metadata`);
|
|
648
|
+
const reqs = metadata.connectionRequirements ?? [];
|
|
649
|
+
if (reqs.length > 0) {
|
|
650
|
+
console.log("");
|
|
651
|
+
console.log("This skill requires the following connectors:");
|
|
652
|
+
console.log("");
|
|
653
|
+
for (const req of reqs) {
|
|
654
|
+
console.log(` - ${req.connector.padEnd(20)} ${req.purpose}`);
|
|
655
|
+
}
|
|
656
|
+
console.log("");
|
|
657
|
+
console.log("To authorize these connectors, use the Headways desktop app");
|
|
658
|
+
console.log("or run: headways connections add <provider>");
|
|
659
|
+
}
|
|
660
|
+
} catch {
|
|
661
|
+
}
|
|
594
662
|
});
|
|
595
663
|
skills.command("feedback <slug>").description("Submit feedback about a skill").option(
|
|
596
664
|
"--reaction <type>",
|
|
@@ -616,6 +684,28 @@ function registerSkillsCommands(program2) {
|
|
|
616
684
|
});
|
|
617
685
|
}
|
|
618
686
|
|
|
687
|
+
// src/commands/connections/index.ts
|
|
688
|
+
import "commander";
|
|
689
|
+
function registerConnectionsCommands(program2) {
|
|
690
|
+
const connections = program2.command("connections").description("Manage connector authorizations");
|
|
691
|
+
connections.command("add [provider]").description("Authorize a connector (opens the desktop app)").action((provider) => {
|
|
692
|
+
const target = provider ? `the ${provider} connector` : "connectors";
|
|
693
|
+
console.log(`To authorize ${target}, open the Headways desktop app.`);
|
|
694
|
+
console.log("");
|
|
695
|
+
console.log(" 1. Open Headways");
|
|
696
|
+
console.log(" 2. Go to Settings \u2192 Connections");
|
|
697
|
+
console.log(' 3. Click "Connect" next to the connector you want to authorize');
|
|
698
|
+
console.log("");
|
|
699
|
+
console.log(
|
|
700
|
+
"Alternatively, install a skill that requires the connector and it will be set up automatically."
|
|
701
|
+
);
|
|
702
|
+
});
|
|
703
|
+
connections.command("list").description("List authorized connections (opens the desktop app)").action(() => {
|
|
704
|
+
console.log("To view your connections, open the Headways desktop app.");
|
|
705
|
+
console.log("Go to Settings \u2192 Connections to see and manage your authorized connectors.");
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
619
709
|
// src/sdk/emit.ts
|
|
620
710
|
import "commander";
|
|
621
711
|
function registerEmitCommand(program2) {
|
|
@@ -720,6 +810,10 @@ function registerPrimeCommand(program2) {
|
|
|
720
810
|
for (const skill of skills) {
|
|
721
811
|
const runLine = skill.lastRunAt ? `last run ${new Date(skill.lastRunAt).toLocaleDateString()}` : "never run";
|
|
722
812
|
lines.push(`- **${skill.slug}** v${skill.version} (${skill.runtime}, ${runLine})`);
|
|
813
|
+
if (skill.connectionRequirements.length > 0) {
|
|
814
|
+
const connectors = skill.connectionRequirements.map((r) => r.connector).join(", ");
|
|
815
|
+
lines.push(` - Requires connectors: ${connectors}`);
|
|
816
|
+
}
|
|
723
817
|
}
|
|
724
818
|
}
|
|
725
819
|
lines.push("", "## Skill Files", "");
|
|
@@ -739,7 +833,8 @@ function getInstalledSkills() {
|
|
|
739
833
|
slug,
|
|
740
834
|
version: String(raw.version ?? ""),
|
|
741
835
|
runtime: String(raw.runtime ?? "claude-code"),
|
|
742
|
-
lastRunAt: raw.last_run_at ?? null
|
|
836
|
+
lastRunAt: raw.last_run_at ?? null,
|
|
837
|
+
connectionRequirements: Array.isArray(raw.connection_requirements) ? raw.connection_requirements : []
|
|
743
838
|
};
|
|
744
839
|
} catch {
|
|
745
840
|
return null;
|
|
@@ -754,6 +849,7 @@ function getInstalledSkills() {
|
|
|
754
849
|
program.name("headways").description("Headways CLI \u2014 skill authoring, sync, and runtime SDK").version("0.2.1");
|
|
755
850
|
registerAuthCommands(program);
|
|
756
851
|
registerSkillsCommands(program);
|
|
852
|
+
registerConnectionsCommands(program);
|
|
757
853
|
registerSyncCommands(program);
|
|
758
854
|
registerEmitCommand(program);
|
|
759
855
|
registerPrimeCommand(program);
|