@anaclumos/taal 1.1.2 → 1.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaclumos/taal",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "CLI tool to sync MCP server configs and Agent Skills across AI coding assistants",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,11 +36,12 @@
36
36
  "node": ">=18.0.0"
37
37
  },
38
38
  "dependencies": {
39
+ "@iarna/toml": "^2.2.5",
40
+ "chalk": "^5.3.0",
39
41
  "commander": "^12.0.0",
42
+ "jsonc-parser": "^3.3.1",
40
43
  "yaml": "^2.3.4",
41
- "chalk": "^5.3.0",
42
- "zod": "^3.22.4",
43
- "@iarna/toml": "^2.2.5"
44
+ "zod": "^3.22.4"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@biomejs/biome": "2.3.11",
@@ -1,6 +1,10 @@
1
+ import { existsSync } from "node:fs";
1
2
  import { homedir } from "node:os";
3
+ import { join } from "node:path";
2
4
  import type { McpServer } from "../config/schema.js";
3
5
  import { initializeProviders, registry } from "../providers/index.js";
6
+ import { copySkillsToProvider } from "../skills/copy.js";
7
+ import { discoverSkills } from "../skills/discovery.js";
4
8
 
5
9
  export interface CollectConflict {
6
10
  serverName: string;
@@ -11,6 +15,7 @@ export interface CollectConflict {
11
15
  export interface CollectResult {
12
16
  servers: Record<string, McpServer>;
13
17
  conflicts: CollectConflict[];
18
+ skillsCollected: number;
14
19
  summary: {
15
20
  totalServers: number;
16
21
  providersScanned: number;
@@ -103,9 +108,45 @@ export async function collect(baseDir?: string): Promise<CollectResult> {
103
108
  }
104
109
  }
105
110
 
111
+ // Collect skills from providers
112
+ const taalSkillsDir = join(home, ".taal", "skills");
113
+ let skillsCollected = 0;
114
+
115
+ for (const provider of allProviders) {
116
+ if (!provider.skillsPath) {
117
+ continue;
118
+ }
119
+
120
+ try {
121
+ const isInstalled = await provider.isInstalled(home);
122
+ if (!isInstalled) {
123
+ continue;
124
+ }
125
+
126
+ const skillsPath =
127
+ typeof provider.skillsPath === "function"
128
+ ? provider.skillsPath(home)
129
+ : provider.skillsPath;
130
+
131
+ if (existsSync(skillsPath)) {
132
+ const providerSkills = discoverSkills([skillsPath], home);
133
+ if (providerSkills.length > 0) {
134
+ await copySkillsToProvider(providerSkills, taalSkillsDir);
135
+ skillsCollected += providerSkills.length;
136
+ }
137
+ }
138
+ } catch (error) {
139
+ console.warn(
140
+ `Warning: Failed to collect skills from ${provider.name}:`,
141
+ error
142
+ );
143
+ }
144
+ }
145
+
106
146
  return {
107
147
  servers,
108
148
  conflicts,
149
+ skillsCollected,
109
150
  summary: {
110
151
  totalServers: Object.keys(servers).length,
111
152
  providersScanned,
@@ -1,6 +1,7 @@
1
1
  import { exists, mkdir, writeFile } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
+ import { collect } from "./collect.js";
4
5
 
5
6
  const SAMPLE_CONFIG = `# TAAL Configuration
6
7
  # https://github.com/user/taal
@@ -42,10 +43,16 @@ export interface InitOptions {
42
43
  force?: boolean;
43
44
  }
44
45
 
46
+ export interface InitResult {
47
+ configPath: string;
48
+ collected: boolean;
49
+ serversFound: number;
50
+ }
51
+
45
52
  export async function init(
46
53
  baseDir?: string,
47
54
  options: InitOptions = {}
48
- ): Promise<void> {
55
+ ): Promise<InitResult> {
49
56
  const taalDir = join(baseDir || homedir(), ".taal");
50
57
  const configPath = join(taalDir, "config.yaml");
51
58
  const skillsDir = join(taalDir, "skills");
@@ -63,4 +70,13 @@ export async function init(
63
70
 
64
71
  // Write sample config
65
72
  await writeFile(configPath, SAMPLE_CONFIG, "utf-8");
73
+
74
+ // Automatically collect existing MCP configs from installed providers
75
+ const collectResult = await collect(baseDir);
76
+
77
+ return {
78
+ configPath,
79
+ collected: collectResult.summary.totalServers > 0,
80
+ serversFound: collectResult.summary.totalServers,
81
+ };
66
82
  }
package/src/index.ts CHANGED
@@ -28,10 +28,23 @@ program
28
28
  .option("-f, --force", "Overwrite existing configuration")
29
29
  .action(async (options) => {
30
30
  try {
31
- await init(homedir(), options);
31
+ const result = await init(homedir(), options);
32
32
  console.log("✓ TAAL initialized successfully");
33
- console.log(` Config: ${homedir()}/.taal/config.yaml`);
33
+ console.log(` Config: ${result.configPath}`);
34
34
  console.log(` Skills: ${homedir()}/.taal/skills/`);
35
+
36
+ if (result.collected && result.serversFound > 0) {
37
+ console.log(
38
+ `\n✓ Collected ${result.serversFound} MCP server(s) from installed providers`
39
+ );
40
+ console.log(
41
+ "\nRun 'taal sync' to sync these servers to all enabled providers."
42
+ );
43
+ } else {
44
+ console.log(
45
+ "\nNo existing MCP servers found. Edit ~/.taal/config.yaml to add servers."
46
+ );
47
+ }
35
48
  } catch (error) {
36
49
  console.error("Error:", error instanceof Error ? error.message : error);
37
50
  process.exit(1);
@@ -47,9 +60,13 @@ program
47
60
  const result = await collect();
48
61
 
49
62
  console.log(
50
- `\n✓ Found ${result.summary.totalServers} servers from ${result.summary.providersWithConfigs} providers`
63
+ `\n✓ Found ${result.summary.totalServers} server(s) from ${result.summary.providersWithConfigs} provider(s)`
51
64
  );
52
65
 
66
+ if (result.skillsCollected > 0) {
67
+ console.log(`✓ Collected ${result.skillsCollected} skill(s)`);
68
+ }
69
+
53
70
  if (result.conflicts.length > 0) {
54
71
  console.log("\n⚠ Conflicts detected:");
55
72
  for (const conflict of result.conflicts) {
@@ -8,7 +8,7 @@ export class OpenCodeProvider extends BaseProvider {
8
8
  join(home, ".config", "opencode", "opencode.json");
9
9
  format = "json" as const;
10
10
  mcpKey = "mcp";
11
- skillsPath = (home: string) => join(home, ".opencode", "skills");
11
+ skillsPath = (home: string) => join(home, ".config", "opencode", "skill");
12
12
 
13
13
  transformMcpServers(
14
14
  servers: Record<string, McpServer>
@@ -1,13 +1,14 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import type { JsonMap } from "@iarna/toml";
3
3
  import { parse as parseToml, stringify as stringifyToml } from "@iarna/toml";
4
+ import { parse as parseJsonc } from "jsonc-parser";
4
5
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
5
6
  import { atomicWrite } from "../utils/atomic-write.js";
6
7
  import { backupConfig } from "../utils/backup.js";
7
8
  import type { ConfigFormat } from "./types.js";
8
9
 
9
10
  /**
10
- * Read and parse a JSON config file
11
+ * Read and parse a JSON config file (supports JSONC - JSON with Comments)
11
12
  */
12
13
  export function readJsonConfig(path: string): unknown {
13
14
  if (!existsSync(path)) {
@@ -16,7 +17,8 @@ export function readJsonConfig(path: string): unknown {
16
17
 
17
18
  try {
18
19
  const content = readFileSync(path, "utf-8");
19
- return JSON.parse(content);
20
+ // Use JSONC parser to support comments and trailing commas (used by Zed, VS Code, etc.)
21
+ return parseJsonc(content);
20
22
  } catch (error) {
21
23
  throw new Error(`Failed to read JSON config at ${path}: ${error}`);
22
24
  }